diff --git a/.gitignore b/.gitignore
index 6411252c..f5e1cac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -429,6 +429,7 @@
 /third_party/pywebsocket/src
 /third_party/re2/src
 /third_party/requests/src
+/third_party/retrolambda/*.jar
 /third_party/robolectric/lib/*.jar
 /third_party/robolectric/robolectric
 /third_party/scons-2.0.1
diff --git a/AUTHORS b/AUTHORS
index 3016155..a8dea032 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -272,6 +272,7 @@
 Hwanseung Lee <hs1217.lee@samsung.com>
 Hyunjune Kim <hyunjune.kim@samsung.com>
 Hyunki Baik <hyunki.baik@samsung.com>
+Hyungchan Kim <inlinechan@gmail.com>
 Hyungwook Lee <withlhw@gmail.com>
 Hyungwook Lee <hyungwook.lee@navercorp.com>
 Ian Cullinan <cullinan@amazon.com>
diff --git a/BUILD.gn b/BUILD.gn
index 6b0f6c18..6ebb48f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -642,10 +642,6 @@
       if (!is_debug && !is_component_build) {
         deps += [ "//chrome/tools/service_discovery_sniffer" ]
       }
-
-      if (is_clang) {
-        deps += [ "//build/sanitizers:copy_llvm_symbolizer" ]
-      }
     }
 
     if (use_x11) {
@@ -1044,9 +1040,6 @@
         "//v8:v8_shell($v8_snapshot_toolchain)",
       ]
     }
-    if (is_clang) {
-      deps += [ "//build/sanitizers:copy_llvm_symbolizer" ]
-    }
     if (is_win && symbol_level == 2 && target_cpu == "x86" && is_syzyasan) {
       deps += [
         "//chrome/tools/build/win/syzygy:chrome_dll_syzygy",
diff --git a/DEPS b/DEPS
index a1c13e7..4489316 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e7d34b1d2c4049a788d0bee4b93a7f11aab01ed2',
+  'skia_revision': '019be7b472a5a628e54822bddaa101fcda49da93',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '0f7cc8da63499813a36507be7a79d78ad4fb1478',
+  'v8_revision': '24a38ed5a84dc6414a938aee5cc7442e6b0faa7d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -52,11 +52,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'ced53ae26c312ac7f38536bf44353bad5794bd62',
+  'angle_revision': 'b5e997fbe7d9d4f337dbc738618e70e79dfe3726',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '64e38f0cebdde27aa0cfb405f330063582f9ac76',
+  'buildtools_revision': '55ad626b08ef971fd82a62b7abb325359542952b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '7341149c634e0ab9a619898826440f6e952cf0aa',
+  'pdfium_revision': 'cd5e12a9ea397b48056643a7b65126395eec3174',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '6f82f49c5a853b2b9836011bddafd8ce6a621d7d',
+  'catapult_revision': '11d3d44fb9d22dea38862472a620d4f0be67ae97',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -116,7 +116,7 @@
 
 deps = {
   'src/breakpad/src':
-    Var('chromium_git') + '/breakpad/breakpad/src.git' + '@' + '2dadd64db9965d8a621d52712abe95f96c4a1e0a',
+    Var('chromium_git') + '/breakpad/breakpad/src.git' + '@' + '18059dea400a3a0f212ef6edaebb5885098d1dbc',
 
   'src/buildtools':
     Var('chromium_git') + '/chromium/buildtools.git' + '@' +  Var('buildtools_revision'),
@@ -197,7 +197,7 @@
     Var('chromium_git') + '/webm/libvpx.git' + '@' +  'd7f1d60c51b47f6d22be919362caad09469a058b',
 
   'src/third_party/ffmpeg':
-    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '16cdcb08bb1cfb0e55a56e9bc2c32dffcf277953',
+    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '26be2ced90769f25f83b9a613fe3b3e47c1ce4c6',
 
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '7f9228152ab3d70e6848cc9c67389a0d4218740e',
@@ -224,7 +224,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '6fe85c08b1b3a622992ed6c264364b9d09b1db06', # commit position 15443
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '666ab2c1173e5411c3f81ebb0277644c978d136a', # commit position 15456
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -266,7 +266,7 @@
     Var('chromium_git') + '/external/github.com/CLD2Owners/cld2.git' + '@' + '84b58a5d7690ebf05a91406f371ce00c3daf31c0',
 
   'src/third_party/cld_3/src':
-    Var('chromium_git') + '/external/github.com/google/cld_3.git' + '@' + '79f017acfa55012ab8a553700e4b7fe78ba3709c',
+    Var('chromium_git') + '/external/github.com/google/cld_3.git' + '@' + 'c03368eff92acc56756df2d4cd74a01a674409ea',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + '9a235e0bc94319c5f7184bd69cbe5468a74a025c',
@@ -684,6 +684,16 @@
     ],
   },
   {
+    'name': 'retrolambda',
+    'pattern': '.',
+    'action': ['python',
+               'src/build/android/update_deps/update_third_party_deps.py',
+               'download',
+               '-b', 'chromium-android-tools/retrolambda',
+               '-l', 'third_party/retrolambda'
+    ],
+  },
+  {
     'name': 'icu4j',
     'pattern': '.',
     'action': ['python',
diff --git a/WATCHLISTS b/WATCHLISTS
index a278cd03..cb3ccd9 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -810,6 +810,11 @@
     'push_messaging': {
       'filepath': 'push_messaging'
     },
+    'reading_list': {
+      'filepath': 'components/reading_list|'\
+                  'ios/chrome/browser/reading_list|'\
+                  'ios/chrome/browser/ui/reading_list',
+    },
     'remoting': {
       'filepath': 'remoting/' \
                   '|testing/chromoting'
@@ -1974,6 +1979,7 @@
     'push_messaging': ['harkness+watch@chromium.org',
                        'johnme+watch@chromium.org',
                        'peter@chromium.org'],
+    'reading_list': ['stkhapugin@chromium.org'],
     'remoting': ['chromoting-reviews@chromium.org'],
     'rlz_id': ['alito+watch@chromium.org',
                'gab+watch@chromium.org',
diff --git a/android_webview/browser/aw_browser_policy_connector.cc b/android_webview/browser/aw_browser_policy_connector.cc
index 2baa24f..bcd497bc 100644
--- a/android_webview/browser/aw_browser_policy_connector.cc
+++ b/android_webview/browser/aw_browser_policy_connector.cc
@@ -39,21 +39,21 @@
   // URL Filtering
   handlers->AddHandler(base::MakeUnique<policy::SimplePolicyHandler>(
       policy::key::kURLWhitelist, policy::policy_prefs::kUrlWhitelist,
-      base::Value::TYPE_LIST));
+      base::Value::Type::LIST));
   handlers->AddHandler(base::MakeUnique<policy::URLBlacklistPolicyHandler>());
 
   // HTTP Negotiate authentication
   handlers->AddHandler(base::MakeUnique<policy::SimplePolicyHandler>(
       policy::key::kAuthServerWhitelist, prefs::kAuthServerWhitelist,
-      base::Value::TYPE_STRING));
+      base::Value::Type::STRING));
   handlers->AddHandler(base::MakeUnique<policy::SimplePolicyHandler>(
       policy::key::kAuthAndroidNegotiateAccountType,
-      prefs::kAuthAndroidNegotiateAccountType, base::Value::TYPE_STRING));
+      prefs::kAuthAndroidNegotiateAccountType, base::Value::Type::STRING));
 
   // Web restrictions
   handlers->AddHandler(base::WrapUnique(new policy::SimplePolicyHandler(
       policy::key::kWebRestrictionsAuthority, prefs::kWebRestrictionsAuthority,
-      base::Value::TYPE_STRING)));
+      base::Value::Type::STRING)));
 
   return handlers;
 }
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index 7882f809..24e22781 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -204,7 +204,11 @@
   bool offscreen_pre_raster_;
 
   // Must do a synchronous draw first to ensure GL bindings are initialized.
-  // TODO(boliu): Wait on render thread and remove this.
+  // TODO(boliu): Wait on render thread and remove this. When the
+  // first synchronous draw requirement is removed,
+  // RenderThreadManager::DeleteHardwareRendererOnUI will need to
+  // change, because it will no longer be true that having received a
+  // frame means that GL bindings have been initialized.
   bool allow_async_draw_;
 
   gfx::Vector2d last_on_draw_scroll_offset_;
diff --git a/android_webview/browser/render_thread_manager.cc b/android_webview/browser/render_thread_manager.cc
index 9a14dda..c8ee759 100644
--- a/android_webview/browser/render_thread_manager.cc
+++ b/android_webview/browser/render_thread_manager.cc
@@ -98,8 +98,8 @@
     : ui_loop_(ui_loop),
       client_(client),
       compositor_frame_producer_(nullptr),
+      has_received_frame_(false),
       renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()),
-      hardware_renderer_has_frame_(false),
       sync_on_draw_hardware_(base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kSyncOnDrawHardware)),
       inside_hardware_release_(false),
@@ -183,6 +183,9 @@
     std::unique_ptr<ChildFrame> new_frame) {
   DCHECK(new_frame);
   base::AutoLock lock(lock_);
+
+  has_received_frame_ = true;
+
   if (child_frames_.empty()) {
     child_frames_.emplace_back(std::move(new_frame));
     return nullptr;
@@ -212,8 +215,6 @@
 
 ChildFrameQueue RenderThreadManager::PassFramesOnRT() {
   base::AutoLock lock(lock_);
-  hardware_renderer_has_frame_ =
-      hardware_renderer_has_frame_ || !child_frames_.empty();
   ChildFrameQueue returned_frames;
   returned_frames.swap(child_frames_);
   return returned_frames;
@@ -334,7 +335,6 @@
   }
 
   if (IsInsideHardwareRelease()) {
-    hardware_renderer_has_frame_ = false;
     hardware_renderer_.reset();
     // Flush the idle queue in tear down.
     DeferredGpuCommandService::GetInstance()->PerformAllIdleWork();
@@ -358,14 +358,16 @@
 
   InsideHardwareReleaseReset auto_inside_hardware_release_reset(this);
 
+  client_->DetachFunctorFromView();
+
   // If the WebView gets onTrimMemory >= MODERATE twice in a row, the 2nd
   // onTrimMemory will result in an unnecessary Render Thread InvokeGL call.
-  bool hardware_initialized = HasFrameOnUI();
-  if (hardware_initialized) {
-    // The functor has only been attached to the view hierarchy if a compositor
-    // frame has been generated. Thus, it should only be detached in this case.
-    client_->DetachFunctorFromView();
-
+  // TODO(boliu): removing the requirement that the first frame be
+  // synchronous will require changing the following test.
+  if (has_received_frame_) {
+    // Receiving at least one frame is a precondition for
+    // initialization (such as looing up GL bindings and constructing
+    // hardware_renderer_).
     bool draw_functor_succeeded = client_->RequestInvokeGL(true);
     if (!draw_functor_succeeded) {
       LOG(ERROR) << "Unable to free GL resources. Has the Window leaked?";
@@ -386,10 +388,12 @@
     }
   }
 
-  if (hardware_initialized) {
+  if (has_received_frame_) {
     // Flush any invoke functors that's caused by ReleaseHardware.
     client_->RequestInvokeGL(true);
   }
+
+  has_received_frame_ = false;
 }
 
 void RenderThreadManager::SetCompositorFrameProducer(
@@ -402,7 +406,7 @@
 
 bool RenderThreadManager::HasFrameOnUI() const {
   base::AutoLock lock(lock_);
-  return hardware_renderer_has_frame_ || !child_frames_.empty();
+  return has_received_frame_;
 }
 
 bool RenderThreadManager::HasFrameForHardwareRendererOnRT() const {
diff --git a/android_webview/browser/render_thread_manager.h b/android_webview/browser/render_thread_manager.h
index 0badea3f..e93c4b8 100644
--- a/android_webview/browser/render_thread_manager.h
+++ b/android_webview/browser/render_thread_manager.h
@@ -100,6 +100,9 @@
   CompositorFrameProducer* compositor_frame_producer_;
   base::WeakPtr<RenderThreadManager> ui_thread_weak_ptr_;
   base::CancelableClosure request_draw_gl_cancelable_closure_;
+  // Whether any frame has been received on the UI thread by
+  // RenderThreadManager.
+  bool has_received_frame_;
 
   // Accessed by RT thread.
   std::unique_ptr<HardwareRenderer> hardware_renderer_;
@@ -109,7 +112,6 @@
 
   // Accessed by both UI and RT thread.
   mutable base::Lock lock_;
-  bool hardware_renderer_has_frame_;
   gfx::Vector2d scroll_offset_;
   ChildFrameQueue child_frames_;
   const bool sync_on_draw_hardware_;
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 9e69b6f..435ac16 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
@@ -18,7 +18,6 @@
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/web_restrictions/browser/web_restrictions_resource_throttle.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
 #include "content/public/browser/resource_request_info.h"
@@ -176,13 +175,13 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!MaybeBlockRequest()) {
-    controller()->Resume();
+    Resume();
   }
 }
 
 bool IoThreadClientThrottle::MaybeBlockRequest() {
   if (ShouldBlockRequest()) {
-    controller()->CancelWithError(net::ERR_ACCESS_DENIED);
+    CancelWithError(net::ERR_ACCESS_DENIED);
     return true;
   }
   return false;
diff --git a/android_webview/browser/surfaces_instance.cc b/android_webview/browser/surfaces_instance.cc
index 099858e9..f16fe5e 100644
--- a/android_webview/browser/surfaces_instance.cc
+++ b/android_webview/browser/surfaces_instance.cc
@@ -114,8 +114,7 @@
   // Create a frame with a single SurfaceDrawQuad referencing the child
   // Surface and transformed using the given transform.
   std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
-  render_pass->SetAll(cc::RenderPassId(1, 1), gfx::Rect(viewport), clip,
-                      gfx::Transform(), false);
+  render_pass->SetAll(1, gfx::Rect(viewport), clip, gfx::Transform(), false);
 
   cc::SharedQuadState* quad_state =
       render_pass->CreateAndAppendSharedQuadState();
diff --git a/android_webview/browser/test/rendering_test.cc b/android_webview/browser/test/rendering_test.cc
index 1157c8a..804fb2a 100644
--- a/android_webview/browser/test/rendering_test.cc
+++ b/android_webview/browser/test/rendering_test.cc
@@ -119,8 +119,7 @@
       new cc::CompositorFrame);
   std::unique_ptr<cc::RenderPass> root_pass(cc::RenderPass::Create());
   gfx::Rect viewport(browser_view_renderer_->size());
-  root_pass->SetNew(cc::RenderPassId(1, 1), viewport, viewport,
-                    gfx::Transform());
+  root_pass->SetNew(1, viewport, viewport, gfx::Transform());
   compositor_frame->render_pass_list.push_back(std::move(root_pass));
   return compositor_frame;
 }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
index 54d0aae..31a293d 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebMessagePortAdapter.java
@@ -10,6 +10,7 @@
 import android.webkit.WebMessage;
 import android.webkit.WebMessagePort;
 
+import org.chromium.content.browser.AppWebMessagePort;
 import org.chromium.content_public.browser.MessagePort;
 
 /**
@@ -61,7 +62,7 @@
 
     public static MessagePort[] toMessagePorts(WebMessagePort[] webMessagePorts) {
         if (webMessagePorts == null) return null;
-        MessagePort[] ports = new MessagePort[webMessagePorts.length];
+        MessagePort[] ports = new AppWebMessagePort[webMessagePorts.length];
         for (int i = 0; i < webMessagePorts.length; i++) {
             ports[i] = ((WebMessagePortAdapter) webMessagePorts[i]).getPort();
         }
diff --git a/android_webview/native/aw_autofill_client.cc b/android_webview/native/aw_autofill_client.cc
index a362e3d..11079829 100644
--- a/android_webview/native/aw_autofill_client.cc
+++ b/android_webview/native/aw_autofill_client.cc
@@ -79,7 +79,7 @@
   return nullptr;
 }
 
-rappor::RapporService* AwAutofillClient::GetRapporService() {
+rappor::RapporServiceImpl* AwAutofillClient::GetRapporServiceImpl() {
   return nullptr;
 }
 
diff --git a/android_webview/native/aw_autofill_client.h b/android_webview/native/aw_autofill_client.h
index 3b9cc1c0..f3ce561 100644
--- a/android_webview/native/aw_autofill_client.h
+++ b/android_webview/native/aw_autofill_client.h
@@ -65,7 +65,7 @@
   PrefService* GetPrefs() override;
   syncer::SyncService* GetSyncService() override;
   IdentityProvider* GetIdentityProvider() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   void ShowAutofillSettings() override;
   void ShowUnmaskPrompt(
       const autofill::CreditCard& card,
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
index 35e436ba..6ad410d 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -64,6 +64,7 @@
 
         private boolean mHaveSurface = false;
         private Runnable mReadyToRenderCallback = null;
+        private Runnable mReadyToDetachCallback = null;
 
         private long mDrawGL = 0;
         private long mViewContext = 0;
@@ -112,11 +113,14 @@
             mReadyToRenderCallback = runner;
         }
 
+        public void setReadyToDetachCallback(Runnable runner) {
+            mReadyToDetachCallback = runner;
+        }
+
         @Override
         public void surfaceCreated(SurfaceHolder holder) {
-            boolean didHaveSurface = mHaveSurface;
             mHaveSurface = true;
-            if (!didHaveSurface && mReadyToRenderCallback != null) {
+            if (mReadyToRenderCallback != null) {
                 mReadyToRenderCallback.run();
                 mReadyToRenderCallback = null;
             }
@@ -126,6 +130,10 @@
         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {
             mHaveSurface = false;
+            if (mReadyToDetachCallback != null) {
+                mReadyToDetachCallback.run();
+                mReadyToDetachCallback = null;
+            }
             super.surfaceDestroyed(holder);
         }
 
@@ -287,19 +295,37 @@
         mAwContents.onConfigurationChanged(newConfig);
     }
 
+    private void attachedContentsInternal() {
+        assert !mAttachedContents;
+        mAwContents.onAttachedToWindow();
+        mAttachedContents = true;
+    }
+
+    private void detachedContentsInternal() {
+        assert mAttachedContents;
+        mAwContents.onDetachedFromWindow();
+        mAttachedContents = false;
+    }
+
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (mHardwareView == null || mHardwareView.isReadyToRender()) {
-            mAwContents.onAttachedToWindow();
-            mAttachedContents = true;
+            attachedContentsInternal();
         } else {
             mHardwareView.setReadyToRenderCallback(new Runnable() {
                 @Override
                 public void run() {
-                    assert !mAttachedContents;
-                    mAwContents.onAttachedToWindow();
-                    mAttachedContents = true;
+                    attachedContentsInternal();
+                }
+            });
+        }
+
+        if (mHardwareView != null) {
+            mHardwareView.setReadyToDetachCallback(new Runnable() {
+                @Override
+                public void run() {
+                    detachedContentsInternal();
                 }
             });
         }
@@ -308,11 +334,14 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mAwContents.onDetachedFromWindow();
-        if (mHardwareView != null) {
-            mHardwareView.setReadyToRenderCallback(null);
+        if (mHardwareView == null || mHardwareView.isReadyToRender()) {
+            detachedContentsInternal();
+
+            if (mHardwareView != null) {
+                mHardwareView.setReadyToRenderCallback(null);
+                mHardwareView.setReadyToDetachCallback(null);
+            }
         }
-        mAttachedContents = false;
     }
 
     @Override
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 07570b3..1ade4e3 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -57,6 +57,11 @@
 interface HTMLInputElement : HTMLElement
     getter webkitEntries
 
+# crbug.com/671461
+interface MediaDevices : EventTarget
+    getter ondevicechange
+    setter ondevicechange
+
 # TODO(timvolodine): investigate in more detail
 [GLOBAL OBJECT]
     method openDatabase
diff --git a/apps/app_lifetime_monitor.h b/apps/app_lifetime_monitor.h
index dd2d7931..8af74c2 100644
--- a/apps/app_lifetime_monitor.h
+++ b/apps/app_lifetime_monitor.h
@@ -14,10 +14,6 @@
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 
-namespace extensions {
-class Extension;
-}
-
 class Profile;
 
 namespace apps {
diff --git a/apps/ui/views/app_window_frame_view.h b/apps/ui/views/app_window_frame_view.h
index 6800a12..f967f3e 100644
--- a/apps/ui/views/app_window_frame_view.h
+++ b/apps/ui/views/app_window_frame_view.h
@@ -14,8 +14,6 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/window/non_client_view.h"
 
-class SkRegion;
-
 namespace extensions {
 class NativeAppWindow;
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 81772c7..ccd4b2a 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -127,8 +127,8 @@
     "common/mojo_interface_factory.h",
     "common/multi_profile_uma.cc",
     "common/multi_profile_uma.h",
-    "common/new_window_client_proxy.cc",
-    "common/new_window_client_proxy.h",
+    "common/new_window_controller.cc",
+    "common/new_window_controller.h",
     "common/palette_delegate.h",
     "common/popup_message.cc",
     "common/popup_message.h",
@@ -977,6 +977,8 @@
       "laser/laser_pointer_points.h",
       "laser/laser_pointer_view.cc",
       "laser/laser_pointer_view.h",
+      "laser/laser_segment_utils.cc",
+      "laser/laser_segment_utils.h",
       "magnifier/partial_magnification_controller.cc",
       "magnifier/partial_magnification_controller.h",
     ]
@@ -1430,6 +1432,7 @@
     sources += [
       "first_run/first_run_helper_unittest.cc",
       "laser/laser_pointer_controller_unittest.cc",
+      "laser/laser_segment_utils_unittest.cc",
       "magnifier/partial_magnification_controller_unittest.cc",
     ]
     deps += [
diff --git a/ash/autoclick/mus/autoclick_application.cc b/ash/autoclick/mus/autoclick_application.cc
index 42c566e..b470da7c 100644
--- a/ash/autoclick/mus/autoclick_application.cc
+++ b/ash/autoclick/mus/autoclick_application.cc
@@ -108,7 +108,7 @@
                                       autoclick_controller_common_.get());
 
     std::map<std::string, std::vector<uint8_t>> properties;
-    properties[ui::mojom::WindowManager::kInitialContainerId_Property] =
+    properties[ui::mojom::WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(
             ash::kShellWindowId_OverlayContainer);
     properties[ui::mojom::WindowManager::kShowState_Property] =
diff --git a/ash/common/accelerators/accelerator_controller.cc b/ash/common/accelerators/accelerator_controller.cc
index 3a19e4f..d3993c2 100644
--- a/ash/common/accelerators/accelerator_controller.cc
+++ b/ash/common/accelerators/accelerator_controller.cc
@@ -4,6 +4,8 @@
 
 #include "ash/common/accelerators/accelerator_controller.h"
 
+#include <utility>
+
 #include "ash/common/accelerators/accelerator_commands.h"
 #include "ash/common/accelerators/accelerator_controller_delegate.h"
 #include "ash/common/accelerators/debug_commands.h"
@@ -13,6 +15,7 @@
 #include "ash/common/ime_control_delegate.h"
 #include "ash/common/media_delegate.h"
 #include "ash/common/multi_profile_uma.h"
+#include "ash/common/new_window_controller.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/shelf_widget.h"
 #include "ash/common/shelf/wm_shelf.h"
@@ -32,11 +35,8 @@
 #include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "content/public/common/service_names.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/accelerator_manager.h"
 #include "ui/keyboard/keyboard_controller.h"
@@ -150,18 +150,18 @@
 
 void HandleNewIncognitoWindow() {
   base::RecordAction(UserMetricsAction("Accel_New_Incognito_Window"));
-  WmShell::Get()->new_window_client()->NewWindow(true /* is_incognito */);
+  WmShell::Get()->new_window_controller()->NewWindow(true /* is_incognito */);
 }
 
 void HandleNewTab(const ui::Accelerator& accelerator) {
   if (accelerator.key_code() == ui::VKEY_T)
     base::RecordAction(UserMetricsAction("Accel_NewTab_T"));
-  WmShell::Get()->new_window_client()->NewTab();
+  WmShell::Get()->new_window_controller()->NewTab();
 }
 
 void HandleNewWindow() {
   base::RecordAction(UserMetricsAction("Accel_New_Window"));
-  WmShell::Get()->new_window_client()->NewWindow(false /* is_incognito */);
+  WmShell::Get()->new_window_controller()->NewWindow(false /* is_incognito */);
 }
 
 bool CanHandleNextIme(ImeControlDelegate* ime_control_delegate) {
@@ -204,7 +204,7 @@
 
 void HandleOpenFeedbackPage() {
   base::RecordAction(UserMetricsAction("Accel_Open_Feedback_Page"));
-  WmShell::Get()->new_window_client()->OpenFeedbackPage();
+  WmShell::Get()->new_window_controller()->OpenFeedbackPage();
 }
 
 bool CanHandlePreviousIme(ImeControlDelegate* ime_control_delegate) {
@@ -221,12 +221,12 @@
 
 void HandleRestoreTab() {
   base::RecordAction(UserMetricsAction("Accel_Restore_Tab"));
-  WmShell::Get()->new_window_client()->RestoreTab();
+  WmShell::Get()->new_window_controller()->RestoreTab();
 }
 
 void HandleShowKeyboardOverlay() {
   base::RecordAction(UserMetricsAction("Accel_Show_Keyboard_Overlay"));
-  WmShell::Get()->new_window_client()->ShowKeyboardOverlay();
+  WmShell::Get()->new_window_controller()->ShowKeyboardOverlay();
 }
 
 bool CanHandleShowMessageCenterBubble() {
@@ -252,7 +252,7 @@
 
 void HandleShowTaskManager() {
   base::RecordAction(UserMetricsAction("Accel_Show_Task_Manager"));
-  WmShell::Get()->new_window_client()->ShowTaskManager();
+  WmShell::Get()->new_window_controller()->ShowTaskManager();
 }
 
 bool CanHandleSwitchIme(ImeControlDelegate* ime_control_delegate,
@@ -371,7 +371,7 @@
 void HandleCrosh() {
   base::RecordAction(UserMetricsAction("Accel_Open_Crosh"));
 
-  WmShell::Get()->new_window_client()->OpenCrosh();
+  WmShell::Get()->new_window_controller()->OpenCrosh();
 }
 
 bool CanHandleDisableCapsLock(const ui::Accelerator& previous_accelerator) {
@@ -401,11 +401,11 @@
 void HandleFileManager() {
   base::RecordAction(UserMetricsAction("Accel_Open_File_Manager"));
 
-  WmShell::Get()->new_window_client()->OpenFileManager();
+  WmShell::Get()->new_window_controller()->OpenFileManager();
 }
 
 void HandleGetHelp() {
-  WmShell::Get()->new_window_client()->OpenGetHelp();
+  WmShell::Get()->new_window_controller()->OpenGetHelp();
 }
 
 bool CanHandleLock() {
@@ -684,6 +684,16 @@
   return true;
 }
 
+void AcceleratorController::BindRequest(
+    mojom::AcceleratorControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void AcceleratorController::SetVolumeController(
+    mojom::VolumeControllerPtr controller) {
+  volume_controller_ = std::move(controller);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // AcceleratorController, private:
 
@@ -1095,13 +1105,13 @@
       WmShell::Get()->system_tray_notifier()->NotifyRequestToggleWifi();
       break;
     case VOLUME_DOWN:
-      HandleVolumeDown(GetVolumeController(), accelerator);
+      HandleVolumeDown(volume_controller_.get(), accelerator);
       break;
     case VOLUME_MUTE:
-      HandleVolumeMute(GetVolumeController(), accelerator);
+      HandleVolumeMute(volume_controller_.get(), accelerator);
       break;
     case VOLUME_UP:
-      HandleVolumeUp(GetVolumeController(), accelerator);
+      HandleVolumeUp(volume_controller_.get(), accelerator);
       break;
 #else
     case DUMMY_FOR_RESERVED:
@@ -1164,19 +1174,4 @@
   return RESTRICTION_NONE;
 }
 
-mojom::VolumeController* AcceleratorController::GetVolumeController() {
-  if (!volume_controller_ && WmShell::Get()->delegate()->GetShellConnector()) {
-    WmShell::Get()->delegate()->GetShellConnector()->ConnectToInterface(
-        content::mojom::kBrowserServiceName, &volume_controller_);
-    volume_controller_.set_connection_error_handler(
-        base::Bind(&AcceleratorController::OnVolumeControllerConnectionError,
-                   base::Unretained(this)));
-  }
-  return volume_controller_.get();
-}
-
-void AcceleratorController::OnVolumeControllerConnectionError() {
-  volume_controller_.reset();
-}
-
 }  // namespace ash
diff --git a/ash/common/accelerators/accelerator_controller.h b/ash/common/accelerators/accelerator_controller.h
index 7a5ae515..38b9007e 100644
--- a/ash/common/accelerators/accelerator_controller.h
+++ b/ash/common/accelerators/accelerator_controller.h
@@ -14,10 +14,11 @@
 #include "ash/ash_export.h"
 #include "ash/common/accelerators/accelerator_table.h"
 #include "ash/common/accelerators/exit_warning_handler.h"
-#include "ash/public/interfaces/volume.mojom.h"
+#include "ash/public/interfaces/accelerator_controller.mojom.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/accelerator_history.h"
 
@@ -36,7 +37,9 @@
 // AcceleratorController provides functions for registering or unregistering
 // global keyboard accelerators, which are handled earlier than any windows. It
 // also implements several handlers as an accelerator target.
-class ASH_EXPORT AcceleratorController : public ui::AcceleratorTarget {
+class ASH_EXPORT AcceleratorController
+    : public ui::AcceleratorTarget,
+      NON_EXPORTED_BASE(public mojom::AcceleratorController) {
  public:
   AcceleratorController(AcceleratorControllerDelegate* delegate,
                         ui::AcceleratorManagerDelegate* manager_delegate);
@@ -121,6 +124,12 @@
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
   bool CanHandleAccelerators() const override;
 
+  // Binds the mojom::AcceleratorController interface to this object.
+  void BindRequest(mojom::AcceleratorControllerRequest request);
+
+  // mojom::AcceleratorController:
+  void SetVolumeController(mojom::VolumeControllerPtr controller) override;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(AcceleratorControllerTest, GlobalAccelerators);
   FRIEND_TEST_ALL_PREFIXES(AcceleratorControllerTest,
@@ -156,10 +165,6 @@
   AcceleratorProcessingRestriction GetAcceleratorProcessingRestriction(
       int action);
 
-  // Returns the volume controller interface raw pointer, may be null in tests.
-  mojom::VolumeController* GetVolumeController();
-  void OnVolumeControllerConnectionError();
-
   AcceleratorControllerDelegate* delegate_;
 
   std::unique_ptr<ui::AcceleratorManager> accelerator_manager_;
@@ -181,7 +186,11 @@
       actions_with_deprecations_;
   std::set<ui::Accelerator> deprecated_accelerators_;
 
-  // The cached volume controller interface pointer.
+  // Bindings for the mojom::AcceleratorController interface.
+  mojo::BindingSet<mojom::AcceleratorController> bindings_;
+
+  // Volume controller interface in chrome browser. May be null in tests. Exists
+  // because chrome owns the CrasAudioHandler dbus communication.
   mojom::VolumeControllerPtr volume_controller_;
 
   // Actions allowed when the user is not signed in.
diff --git a/ash/common/devtools/ash_devtools_css_agent.cc b/ash/common/devtools/ash_devtools_css_agent.cc
index aebfb217..1af835a 100644
--- a/ash/common/devtools/ash_devtools_css_agent.cc
+++ b/ash/common/devtools/ash_devtools_css_agent.cc
@@ -4,7 +4,10 @@
 
 #include "ash/common/devtools/ash_devtools_css_agent.h"
 
+#include "ash/common/wm_lookup.h"
 #include "ash/common/wm_window.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 
 namespace ash {
 namespace devtools {
@@ -12,9 +15,28 @@
 namespace {
 using namespace ui::devtools::protocol;
 
+const char kHeight[] = "height";
+const char kWidth[] = "width";
+const char kX[] = "x";
+const char kY[] = "y";
+
+std::unique_ptr<CSS::SourceRange> BuildDefaultSourceRange() {
+  // These tell the frontend where in the stylesheet a certain style
+  // is located. Since we don't have stylesheets, this is all 0.
+  // We need this because CSS fields are not editable unless
+  // the range is provided.
+  return CSS::SourceRange::create()
+      .setStartLine(0)
+      .setEndLine(0)
+      .setStartColumn(0)
+      .setEndColumn(0)
+      .build();
+}
+
 std::unique_ptr<CSS::CSSProperty> BuildCSSProperty(const std::string& name,
                                                    int value) {
   return CSS::CSSProperty::create()
+      .setRange(BuildDefaultSourceRange())
       .setName(name)
       .setValue(base::IntToString(value))
       .build();
@@ -23,21 +45,46 @@
 std::unique_ptr<Array<CSS::CSSProperty>> BuildBoundsCSSPropertyArray(
     const gfx::Rect& bounds) {
   auto cssProperties = Array<CSS::CSSProperty>::create();
-  cssProperties->addItem(BuildCSSProperty("height", bounds.height()));
-  cssProperties->addItem(BuildCSSProperty("width", bounds.width()));
-  cssProperties->addItem(BuildCSSProperty("x", bounds.x()));
-  cssProperties->addItem(BuildCSSProperty("y", bounds.y()));
+  cssProperties->addItem(BuildCSSProperty(kHeight, bounds.height()));
+  cssProperties->addItem(BuildCSSProperty(kWidth, bounds.width()));
+  cssProperties->addItem(BuildCSSProperty(kX, bounds.x()));
+  cssProperties->addItem(BuildCSSProperty(kY, bounds.y()));
   return cssProperties;
 }
 
 std::unique_ptr<CSS::CSSStyle> BuildCSSStyle(int node_id,
                                              const gfx::Rect& bounds) {
   return CSS::CSSStyle::create()
+      .setRange(BuildDefaultSourceRange())
+      .setStyleSheetId(base::IntToString(node_id))
       .setCssProperties(BuildBoundsCSSPropertyArray(bounds))
       .setShorthandEntries(Array<std::string>::create())
       .build();
 }
 
+Response ParseBounds(const std::string& style_text, gfx::Rect& bounds) {
+  std::vector<std::string> tokens = base::SplitString(
+      style_text, ":;", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (size_t i = 0; i < tokens.size() - 1; i += 2) {
+    const std::string& property = tokens.at(i);
+    int value;
+    if (!base::StringToInt(tokens.at(i + 1), &value))
+      return Response::Error("Unable to parse value for property=" + property);
+
+    if (property == kHeight)
+      bounds.set_height(std::max(0, value));
+    else if (property == kWidth)
+      bounds.set_width(std::max(0, value));
+    else if (property == kX)
+      bounds.set_x(value);
+    else if (property == kY)
+      bounds.set_y(value);
+    else
+      return Response::Error("Unsupported property=" + property);
+  }
+  return Response::OK();
+}
+
 }  // namespace
 
 AshDevToolsCSSAgent::AshDevToolsCSSAgent(AshDevToolsDOMAgent* dom_agent)
@@ -71,6 +118,44 @@
   return ui::devtools::protocol::Response::OK();
 }
 
+ui::devtools::protocol::Response AshDevToolsCSSAgent::setStyleTexts(
+    std::unique_ptr<ui::devtools::protocol::Array<
+        ui::devtools::protocol::CSS::StyleDeclarationEdit>> edits,
+    std::unique_ptr<
+        ui::devtools::protocol::Array<ui::devtools::protocol::CSS::CSSStyle>>*
+        result) {
+  std::unique_ptr<
+      ui::devtools::protocol::Array<ui::devtools::protocol::CSS::CSSStyle>>
+      updated_styles = ui::devtools::protocol::Array<
+          ui::devtools::protocol::CSS::CSSStyle>::create();
+  for (size_t i = 0; i < edits->length(); i++) {
+    const auto& edit = edits->get(i);
+    int node_id;
+    if (!base::StringToInt(edit->getStyleSheetId(), &node_id))
+      return ui::devtools::protocol::Response::Error("Invalid node id");
+
+    gfx::Rect updated_bounds;
+    if (!GetBoundsForNodeId(node_id, &updated_bounds)) {
+      return ui::devtools::protocol::Response::Error(
+          "No node found with that id");
+    }
+
+    ui::devtools::protocol::Response response(
+        ParseBounds(edit->getText(), updated_bounds));
+    if (!response.isSuccess())
+      return response;
+
+    updated_styles->addItem(BuildCSSStyle(node_id, updated_bounds));
+    if (!UpdateBounds(node_id, updated_bounds)) {
+      return ui::devtools::protocol::Response::Error(
+          "No node found with that id");
+    }
+  }
+
+  *result = std::move(updated_styles);
+  return ui::devtools::protocol::Response::OK();
+}
+
 void AshDevToolsCSSAgent::OnWindowBoundsChanged(WmWindow* window) {
   InvalidateStyleSheet(dom_agent_->GetNodeIdFromWindow(window));
 }
@@ -85,19 +170,9 @@
 
 std::unique_ptr<ui::devtools::protocol::CSS::CSSStyle>
 AshDevToolsCSSAgent::GetStylesForNode(int node_id) {
-  WmWindow* window = dom_agent_->GetWindowFromNodeId(node_id);
-  if (window)
-    return BuildCSSStyle(node_id, window->GetBounds());
-
-  views::Widget* widget = dom_agent_->GetWidgetFromNodeId(node_id);
-  if (widget)
-    return BuildCSSStyle(node_id, widget->GetWindowBoundsInScreen());
-
-  views::View* view = dom_agent_->GetViewFromNodeId(node_id);
-  if (view)
-    return BuildCSSStyle(node_id, view->bounds());
-
-  return nullptr;
+  gfx::Rect bounds;
+  return GetBoundsForNodeId(node_id, &bounds) ? BuildCSSStyle(node_id, bounds)
+                                              : nullptr;
 }
 
 void AshDevToolsCSSAgent::InvalidateStyleSheet(int node_id) {
@@ -105,5 +180,49 @@
   frontend()->styleSheetChanged(base::IntToString(node_id));
 }
 
+bool AshDevToolsCSSAgent::GetBoundsForNodeId(int node_id, gfx::Rect* bounds) {
+  WmWindow* window = dom_agent_->GetWindowFromNodeId(node_id);
+  if (window) {
+    *bounds = window->GetBounds();
+    return true;
+  }
+
+  views::Widget* widget = dom_agent_->GetWidgetFromNodeId(node_id);
+  if (widget) {
+    *bounds = widget->GetRestoredBounds();
+    return true;
+  }
+
+  views::View* view = dom_agent_->GetViewFromNodeId(node_id);
+  if (view) {
+    *bounds = view->bounds();
+    return true;
+  }
+
+  return false;
+}
+
+bool AshDevToolsCSSAgent::UpdateBounds(int node_id, const gfx::Rect& bounds) {
+  WmWindow* window = dom_agent_->GetWindowFromNodeId(node_id);
+  if (window) {
+    window->SetBounds(bounds);
+    return true;
+  }
+
+  views::Widget* widget = dom_agent_->GetWidgetFromNodeId(node_id);
+  if (widget) {
+    widget->SetBounds(bounds);
+    return true;
+  }
+
+  views::View* view = dom_agent_->GetViewFromNodeId(node_id);
+  if (view) {
+    view->SetBoundsRect(bounds);
+    return true;
+  }
+
+  return false;
+}
+
 }  // namespace devtools
 }  // namespace ash
diff --git a/ash/common/devtools/ash_devtools_css_agent.h b/ash/common/devtools/ash_devtools_css_agent.h
index 342f210..84f645d 100644
--- a/ash/common/devtools/ash_devtools_css_agent.h
+++ b/ash/common/devtools/ash_devtools_css_agent.h
@@ -27,6 +27,12 @@
       int node_id,
       ui::devtools::protocol::Maybe<ui::devtools::protocol::CSS::CSSStyle>*
           inline_style) override;
+  ui::devtools::protocol::Response setStyleTexts(
+      std::unique_ptr<ui::devtools::protocol::Array<
+          ui::devtools::protocol::CSS::StyleDeclarationEdit>> edits,
+      std::unique_ptr<
+          ui::devtools::protocol::Array<ui::devtools::protocol::CSS::CSSStyle>>*
+          result) override;
 
   // AshDevToolsDOMAgentObserver
   void OnWindowBoundsChanged(WmWindow* window) override;
@@ -37,6 +43,8 @@
   std::unique_ptr<ui::devtools::protocol::CSS::CSSStyle> GetStylesForNode(
       int node_id);
   void InvalidateStyleSheet(int node_id);
+  bool GetBoundsForNodeId(int node_id, gfx::Rect* bounds);
+  bool UpdateBounds(int node_id, const gfx::Rect& bounds);
 
   AshDevToolsDOMAgent* dom_agent_;
 
diff --git a/ash/common/devtools/ash_devtools_dom_agent.cc b/ash/common/devtools/ash_devtools_dom_agent.cc
index a41dd36..d68091a 100644
--- a/ash/common/devtools/ash_devtools_dom_agent.cc
+++ b/ash/common/devtools/ash_devtools_dom_agent.cc
@@ -5,8 +5,14 @@
 #include "ash/common/devtools/ash_devtools_dom_agent.h"
 
 #include "ash/common/wm_lookup.h"
+#include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_window.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "components/ui_devtools/devtools_server.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/display/display.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
 
 namespace ash {
 namespace devtools {
@@ -79,6 +85,20 @@
   return view_index == 0 ? nullptr : parent->child_at(view_index - 1);
 }
 
+int MaskColor(int value) {
+  return value & 0xff;
+}
+
+SkColor RGBAToSkColor(DOM::RGBA* rgba) {
+  if (!rgba)
+    return SkColorSetARGB(0, 0, 0, 0);
+  // Default alpha value is 0 (not visible) and need to convert alpha decimal
+  // percentage value to hex
+  return SkColorSetARGB(MaskColor(static_cast<int>(rgba->getA(0) * 255)),
+                        MaskColor(rgba->getR()), MaskColor(rgba->getG()),
+                        MaskColor(rgba->getB()));
+}
+
 }  // namespace
 
 AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) {
@@ -100,6 +120,19 @@
   return ui::devtools::protocol::Response::OK();
 }
 
+ui::devtools::protocol::Response AshDevToolsDOMAgent::highlightNode(
+    std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig>
+        highlight_config,
+    ui::devtools::protocol::Maybe<int> node_id) {
+  return HighlightNode(std::move(highlight_config), node_id.fromJust());
+}
+
+ui::devtools::protocol::Response AshDevToolsDOMAgent::hideHighlight() {
+  if (widget_for_highlighting_ && widget_for_highlighting_->IsVisible())
+    widget_for_highlighting_->Hide();
+  return ui::devtools::protocol::Response::OK();
+}
+
 // Handles removing windows.
 void AshDevToolsDOMAgent::OnWindowTreeChanging(WmWindow* window,
                                                const TreeChangeParams& params) {
@@ -223,8 +256,10 @@
   views::Widget* widget = window->GetInternalWidget();
   if (widget)
     children->addItem(BuildTreeForRootWidget(widget));
-  for (ash::WmWindow* child : window->GetChildren())
-    children->addItem(BuildTreeForWindow(child));
+  for (ash::WmWindow* child : window->GetChildren()) {
+    if (!IsHighlightingWindow(child))
+      children->addItem(BuildTreeForWindow(child));
+  }
 
   std::unique_ptr<ui::devtools::protocol::DOM::Node> node =
       BuildNode("Window", GetAttributes(window), std::move(children));
@@ -265,6 +300,9 @@
 }
 
 void AshDevToolsDOMAgent::AddWindowTree(WmWindow* window) {
+  if (IsHighlightingWindow(window))
+    return;
+
   DCHECK(window_to_node_id_map_.count(window->GetParent()));
   WmWindow* prev_sibling = FindPreviousSibling(window);
   frontend()->childNodeInserted(
@@ -276,6 +314,9 @@
 void AshDevToolsDOMAgent::RemoveWindowTree(WmWindow* window,
                                            bool remove_observer) {
   DCHECK(window);
+  if (IsHighlightingWindow(window))
+    return;
+
   if (window->GetInternalWidget())
     RemoveWidgetTree(window->GetInternalWidget(), remove_observer);
 
@@ -391,6 +432,7 @@
 
 void AshDevToolsDOMAgent::Reset() {
   RemoveObservers();
+  widget_for_highlighting_.reset();
   window_to_node_id_map_.clear();
   widget_to_node_id_map_.clear();
   view_to_node_id_map_.clear();
@@ -400,5 +442,90 @@
   node_ids = 1;
 }
 
+AshDevToolsDOMAgent::WindowAndBoundsPair
+AshDevToolsDOMAgent::GetNodeWindowAndBounds(int node_id) {
+  WmWindow* window = GetWindowFromNodeId(node_id);
+  if (window)
+    return std::make_pair(window, window->GetBoundsInScreen());
+
+  views::Widget* widget = GetWidgetFromNodeId(node_id);
+  if (widget) {
+    return std::make_pair(WmLookup::Get()->GetWindowForWidget(widget),
+                          widget->GetWindowBoundsInScreen());
+  }
+
+  views::View* view = GetViewFromNodeId(node_id);
+  if (view) {
+    gfx::Rect bounds = view->GetBoundsInScreen();
+    return std::make_pair(
+        WmLookup::Get()->GetWindowForWidget(view->GetWidget()), bounds);
+  }
+
+  return std::make_pair(nullptr, gfx::Rect());
+}
+
+void AshDevToolsDOMAgent::InitializeHighlightingWidget() {
+  DCHECK(!widget_for_highlighting_);
+  widget_for_highlighting_.reset(new views::Widget);
+  views::Widget::InitParams params;
+  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
+  params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.opacity = views::Widget::InitParams::WindowOpacity::TRANSLUCENT_WINDOW;
+  params.name = "HighlightingWidget";
+  shell_->GetPrimaryRootWindowController()
+      ->ConfigureWidgetInitParamsForContainer(widget_for_highlighting_.get(),
+                                              kShellWindowId_OverlayContainer,
+                                              &params);
+  params.keep_on_top = true;
+  params.accept_events = false;
+  widget_for_highlighting_->Init(params);
+}
+
+void AshDevToolsDOMAgent::UpdateHighlight(
+    const WindowAndBoundsPair& window_and_bounds,
+    SkColor background,
+    SkColor border) {
+  constexpr int kBorderThickness = 1;
+  views::View* root_view = widget_for_highlighting_->GetRootView();
+  root_view->SetBorder(views::CreateSolidBorder(kBorderThickness, border));
+  root_view->set_background(
+      views::Background::CreateSolidBackground(background));
+  WmLookup::Get()
+      ->GetWindowForWidget(widget_for_highlighting_.get())
+      ->SetBoundsInScreen(window_and_bounds.second,
+                          window_and_bounds.first->GetDisplayNearestWindow());
+}
+
+ui::devtools::protocol::Response AshDevToolsDOMAgent::HighlightNode(
+    std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig>
+        highlight_config,
+    int node_id) {
+  if (!widget_for_highlighting_)
+    InitializeHighlightingWidget();
+
+  WindowAndBoundsPair window_and_bounds(GetNodeWindowAndBounds(node_id));
+
+  if (!window_and_bounds.first)
+    return ui::devtools::protocol::Response::Error(
+        "No node found with that id");
+
+  SkColor border_color =
+      RGBAToSkColor(highlight_config->getBorderColor(nullptr));
+  SkColor content_color =
+      RGBAToSkColor(highlight_config->getContentColor(nullptr));
+  UpdateHighlight(window_and_bounds, content_color, border_color);
+
+  if (!widget_for_highlighting_->IsVisible())
+    widget_for_highlighting_->Show();
+
+  return ui::devtools::protocol::Response::OK();
+}
+
+bool AshDevToolsDOMAgent::IsHighlightingWindow(WmWindow* window) {
+  return widget_for_highlighting_ &&
+         window->GetInternalWidget() == widget_for_highlighting_.get();
+}
+
 }  // namespace devtools
 }  // namespace ash
diff --git a/ash/common/devtools/ash_devtools_dom_agent.h b/ash/common/devtools/ash_devtools_dom_agent.h
index f1f2a64..194dc66 100644
--- a/ash/common/devtools/ash_devtools_dom_agent.h
+++ b/ash/common/devtools/ash_devtools_dom_agent.h
@@ -35,13 +35,18 @@
       public views::WidgetRemovalsObserver,
       public views::ViewObserver {
  public:
-  explicit AshDevToolsDOMAgent(ash::WmShell* shell);
+  AshDevToolsDOMAgent(ash::WmShell* shell);
   ~AshDevToolsDOMAgent() override;
 
   // DOM::Backend
   ui::devtools::protocol::Response disable() override;
   ui::devtools::protocol::Response getDocument(
       std::unique_ptr<ui::devtools::protocol::DOM::Node>* out_root) override;
+  ui::devtools::protocol::Response highlightNode(
+      std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig>
+          highlight_config,
+      ui::devtools::protocol::Maybe<int> node_id) override;
+  ui::devtools::protocol::Response hideHighlight() override;
 
   // WindowObserver
   void OnWindowTreeChanging(WmWindow* window,
@@ -107,9 +112,23 @@
                       views::View* parent,
                       bool remove_observer);
 
+  void DestroyHighlightingWidget();
   void RemoveObservers();
   void Reset();
 
+  using WindowAndBoundsPair = std::pair<WmWindow*, gfx::Rect>;
+  WindowAndBoundsPair GetNodeWindowAndBounds(int node_id);
+  void InitializeHighlightingWidget();
+  void UpdateHighlight(const WindowAndBoundsPair& window_and_bounds,
+                       SkColor background,
+                       SkColor border);
+  ui::devtools::protocol::Response HighlightNode(
+      std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig>
+          highlight_config,
+      int node_id);
+  bool IsHighlightingWindow(WmWindow* window);
+
+  std::unique_ptr<views::Widget> widget_for_highlighting_;
   ash::WmShell* shell_;
 
   using WindowToNodeIdMap = std::unordered_map<WmWindow*, int>;
diff --git a/ash/common/devtools/ash_devtools_unittest.cc b/ash/common/devtools/ash_devtools_unittest.cc
index 2bdc8ccc..030ada5e 100644
--- a/ash/common/devtools/ash_devtools_unittest.cc
+++ b/ash/common/devtools/ash_devtools_unittest.cc
@@ -2,14 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/common/devtools/ash_devtools_css_agent.h"
 #include "ash/common/devtools/ash_devtools_dom_agent.h"
-
 #include "ash/common/test/ash_test.h"
 #include "ash/common/wm_lookup.h"
 #include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
 #include "base/strings/stringprintf.h"
+#include "ui/display/display.h"
+#include "ui/views/background.h"
 #include "ui/views/widget/native_widget_private.h"
 #include "ui/views/widget/widget.h"
 
@@ -17,6 +19,8 @@
 namespace {
 using namespace ui::devtools::protocol;
 const int kDefaultChildNodeCount = -1;
+const SkColor kBackgroundColor = SK_ColorRED;
+const SkColor kBorderColor = SK_ColorBLUE;
 
 class TestView : public views::View {
  public:
@@ -115,6 +119,62 @@
   return window_node;
 }
 
+int GetPropertyByName(const std::string& name,
+                      Array<CSS::CSSProperty>* properties) {
+  for (size_t i = 0; i < properties->length(); i++) {
+    CSS::CSSProperty* property = properties->get(i);
+    if (property->getName() == name) {
+      int value;
+      EXPECT_TRUE(base::StringToInt(property->getValue(), &value));
+      return value;
+    }
+  }
+  NOTREACHED();
+  return -1;
+}
+
+WmWindow* GetHighlightingWindow(int root_window_index) {
+  WmWindow::Windows overlay_windows =
+      WmShell::Get()
+          ->GetAllRootWindows()[root_window_index]
+          ->GetChildByShellWindowId(kShellWindowId_OverlayContainer)
+          ->GetChildren();
+  for (WmWindow* window : overlay_windows) {
+    if (window->GetName() == "HighlightingWidget")
+      return window;
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<DOM::RGBA> SkColorToRGBA(const SkColor& color) {
+  return DOM::RGBA::create()
+      .setA(SkColorGetA(color) / 255)
+      .setB(SkColorGetB(color))
+      .setG(SkColorGetG(color))
+      .setR(SkColorGetR(color))
+      .build();
+}
+
+std::unique_ptr<DOM::HighlightConfig> CreateHighlightConfig(
+    const SkColor& background_color,
+    const SkColor& border_color) {
+  return DOM::HighlightConfig::create()
+      .setContentColor(SkColorToRGBA(background_color))
+      .setBorderColor(SkColorToRGBA(border_color))
+      .build();
+}
+
+void ExpectHighlighted(const gfx::Rect& bounds, int root_window_index) {
+  WmWindow* highlighting_window = GetHighlightingWindow(root_window_index);
+  EXPECT_TRUE(highlighting_window->IsVisible());
+  EXPECT_EQ(bounds, highlighting_window->GetBoundsInScreen());
+  EXPECT_EQ(kBackgroundColor, highlighting_window->GetInternalWidget()
+                                  ->GetRootView()
+                                  ->background()
+                                  ->get_color());
+}
+
 }  // namespace
 
 class AshDevToolsTest : public AshTest {
@@ -127,8 +187,7 @@
     views::Widget::InitParams params;
     params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
     WmShell::Get()
-        ->GetPrimaryRootWindow()
-        ->GetRootWindowController()
+        ->GetPrimaryRootWindowController()
         ->ConfigureWidgetInitParamsForContainer(
             widget, kShellWindowId_DefaultContainer, &params);
     widget->Init(params);
@@ -143,9 +202,14 @@
     dom_agent_ =
         base::MakeUnique<devtools::AshDevToolsDOMAgent>(WmShell::Get());
     dom_agent_->Init(uber_dispatcher_.get());
+    css_agent_ =
+        base::MakeUnique<devtools::AshDevToolsCSSAgent>(dom_agent_.get());
+    css_agent_->Init(uber_dispatcher_.get());
+    css_agent_->enable();
   }
 
   void TearDown() override {
+    css_agent_.reset();
     dom_agent_.reset();
     uber_dispatcher_.reset();
     fake_frontend_channel_.reset();
@@ -168,16 +232,66 @@
                          parent_id, node_id)));
   }
 
+  void ExpectStyleSheetChanged(int node_id) {
+    EXPECT_EQ(1, frontend_channel()->CountProtocolNotificationMessage(
+                     base::StringPrintf(
+                         "{\"method\":\"CSS.styleSheetChanged\",\"params\":{"
+                         "\"styleSheetId\":\"%d\"}}",
+                         node_id)));
+  }
+
+  void CompareNodeBounds(DOM::Node* node, const gfx::Rect& bounds) {
+    Maybe<CSS::CSSStyle> styles;
+    css_agent_->getMatchedStylesForNode(node->getNodeId(), &styles);
+    ASSERT_TRUE(styles.isJust());
+    Array<CSS::CSSProperty>* properties = styles.fromJust()->getCssProperties();
+    EXPECT_EQ(bounds.height(), GetPropertyByName("height", properties));
+    EXPECT_EQ(bounds.width(), GetPropertyByName("width", properties));
+    EXPECT_EQ(bounds.x(), GetPropertyByName("x", properties));
+    EXPECT_EQ(bounds.y(), GetPropertyByName("y", properties));
+  }
+
+  void SetStyleTexts(DOM::Node* node,
+                     const std::string& style_text,
+                     bool success) {
+    auto edits = Array<CSS::StyleDeclarationEdit>::create();
+    auto edit = CSS::StyleDeclarationEdit::create()
+                    .setStyleSheetId(base::IntToString(node->getNodeId()))
+                    .setText(style_text)
+                    .build();
+    edits->addItem(std::move(edit));
+    std::unique_ptr<Array<CSS::CSSStyle>> output;
+    EXPECT_EQ(success,
+              css_agent_->setStyleTexts(std::move(edits), &output).isSuccess());
+
+    if (success)
+      ASSERT_TRUE(output);
+    else
+      ASSERT_FALSE(output);
+  }
+
+  void HighlightNode(int node_id) {
+    dom_agent_->highlightNode(
+        CreateHighlightConfig(kBackgroundColor, kBorderColor), node_id);
+  }
+
+  void HideHighlight(int root_window_index) {
+    dom_agent_->hideHighlight();
+    ASSERT_FALSE(GetHighlightingWindow(root_window_index)->IsVisible());
+  }
+
   FakeFrontendChannel* frontend_channel() {
     return fake_frontend_channel_.get();
   }
 
+  devtools::AshDevToolsCSSAgent* css_agent() { return css_agent_.get(); }
   devtools::AshDevToolsDOMAgent* dom_agent() { return dom_agent_.get(); }
 
  private:
   std::unique_ptr<UberDispatcher> uber_dispatcher_;
   std::unique_ptr<FakeFrontendChannel> fake_frontend_channel_;
   std::unique_ptr<devtools::AshDevToolsDOMAgent> dom_agent_;
+  std::unique_ptr<devtools::AshDevToolsCSSAgent> css_agent_;
 
   DISALLOW_COPY_AND_ASSIGN(AshDevToolsTest);
 };
@@ -474,4 +588,184 @@
   ExpectChildNodeInserted(target_view_node->getNodeId(), 0);
 }
 
+TEST_F(AshDevToolsTest, WindowWidgetViewHighlight) {
+  std::unique_ptr<views::Widget> widget(
+      CreateTestWidget(gfx::Rect(0, 0, 400, 400)));
+  WmWindow* parent_window = WmLookup::Get()->GetWindowForWidget(widget.get());
+  std::unique_ptr<WindowOwner> child_owner(CreateChildWindow(parent_window));
+  WmWindow* window = child_owner->window();
+  views::View* root_view = widget->GetRootView();
+
+  std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+  ASSERT_TRUE(parent_node);
+  Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+  ASSERT_TRUE(parent_children);
+  DOM::Node* window_node = parent_children->get(1);
+  DOM::Node* widget_node = parent_children->get(0);
+  DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+
+  HighlightNode(window_node->getNodeId());
+  ExpectHighlighted(window->GetBoundsInScreen(), 0);
+
+  HideHighlight(0);
+
+  HighlightNode(widget_node->getNodeId());
+  ExpectHighlighted(widget->GetWindowBoundsInScreen(), 0);
+
+  HideHighlight(0);
+
+  HighlightNode(root_view_node->getNodeId());
+  ExpectHighlighted(root_view->GetBoundsInScreen(), 0);
+
+  HideHighlight(0);
+
+  // Highlight non-existent node
+  HighlightNode(10000);
+  EXPECT_FALSE(GetHighlightingWindow(0)->IsVisible());
+}
+
+TEST_F(AshDevToolsTest, MultipleDisplayHighlight) {
+  if (!SupportsMultipleDisplays())
+    return;
+  UpdateDisplay("300x400,500x500");
+
+  WmWindow::Windows root_windows = WmShell::Get()->GetAllRootWindows();
+  std::unique_ptr<WindowOwner> window_owner(
+      CreateTestWindow(gfx::Rect(1, 2, 30, 40)));
+  WmWindow* window = window_owner->window();
+
+  std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  EXPECT_EQ(root_windows[0], window->GetRootWindow());
+  HighlightNode(dom_agent()->GetNodeIdFromWindow(window));
+  ExpectHighlighted(window->GetBoundsInScreen(), 0);
+
+  window->SetBoundsInScreen(gfx::Rect(500, 0, 50, 50), GetSecondaryDisplay());
+  EXPECT_EQ(root_windows[1], window->GetRootWindow());
+  HighlightNode(dom_agent()->GetNodeIdFromWindow(window));
+  ExpectHighlighted(window->GetBoundsInScreen(), 1);
+}
+
+TEST_F(AshDevToolsTest, WindowWidgetViewGetMatchedStylesForNode) {
+  std::unique_ptr<views::Widget> widget(
+      CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+  WmWindow* parent_window = WmLookup::Get()->GetWindowForWidget(widget.get());
+  std::unique_ptr<WindowOwner> child_owner(CreateChildWindow(parent_window));
+  WmWindow* window = child_owner->window();
+  gfx::Rect window_bounds(2, 2, 3, 3);
+  gfx::Rect widget_bounds(50, 50, 100, 75);
+  gfx::Rect view_bounds(4, 4, 3, 3);
+  window->SetBounds(window_bounds);
+  widget->SetBounds(widget_bounds);
+  widget->GetRootView()->SetBoundsRect(view_bounds);
+
+  std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+  ASSERT_TRUE(parent_node);
+  Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+  ASSERT_TRUE(parent_children);
+
+  CompareNodeBounds(parent_node, widget_bounds);
+  CompareNodeBounds(parent_children->get(1), window_bounds);
+  CompareNodeBounds(parent_children->get(0)->getChildren(nullptr)->get(0),
+                    view_bounds);
+}
+
+TEST_F(AshDevToolsTest, WindowWidgetViewStyleSheetChanged) {
+  std::unique_ptr<views::Widget> widget(
+      CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
+  WmWindow* parent_window = WmLookup::Get()->GetWindowForWidget(widget.get());
+  std::unique_ptr<WindowOwner> child_owner(CreateChildWindow(parent_window));
+  WmWindow* window = child_owner->window();
+
+  std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  gfx::Rect window_bounds(2, 2, 3, 3);
+  gfx::Rect widget_bounds(10, 10, 5, 6);
+  gfx::Rect view_bounds(4, 4, 3, 3);
+  window->SetBounds(window_bounds);
+  widget->SetBounds(widget_bounds);
+  widget->GetRootView()->SetBoundsRect(view_bounds);
+
+  DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+  ASSERT_TRUE(parent_node);
+  Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+  ASSERT_TRUE(parent_children);
+
+  ExpectStyleSheetChanged(parent_node->getNodeId());
+  ExpectStyleSheetChanged(parent_children->get(1)->getNodeId());
+  ExpectStyleSheetChanged(
+      parent_children->get(0)->getChildren(nullptr)->get(0)->getNodeId());
+}
+
+TEST_F(AshDevToolsTest, WindowWidgetViewSetStyleText) {
+  std::unique_ptr<views::Widget> widget(
+      CreateTestWidget(gfx::Rect(0, 0, 400, 400)));
+  WmWindow* parent_window = WmLookup::Get()->GetWindowForWidget(widget.get());
+  std::unique_ptr<WindowOwner> child_owner(CreateChildWindow(parent_window));
+  WmWindow* window = child_owner->window();
+  views::View* root_view = widget->GetRootView();
+
+  std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
+  dom_agent()->getDocument(&root);
+
+  DOM::Node* parent_node = FindInRoot(parent_window, root.get());
+  ASSERT_TRUE(parent_node);
+  Array<DOM::Node>* parent_children = parent_node->getChildren(nullptr);
+  ASSERT_TRUE(parent_children);
+
+  // Test different combinations on window node
+  DOM::Node* window_node = parent_children->get(1);
+
+  SetStyleTexts(window_node, "x: 25; y:35; width: 5; height: 20;", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 5, 20), window->GetBounds());
+
+  SetStyleTexts(window_node, "test_nothing_happens:1;", false);
+  EXPECT_EQ(gfx::Rect(25, 35, 5, 20), window->GetBounds());  // Not changed
+
+  SetStyleTexts(window_node, "\nheight: 10;\n  ", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 5, 10), window->GetBounds());
+
+  SetStyleTexts(window_node, "\nx: 10; y: 23; width: 52;\n  ", true);
+  EXPECT_EQ(gfx::Rect(10, 23, 52, 10), window->GetBounds());
+
+  // Test different combinations on widget node
+  DOM::Node* widget_node = parent_children->get(0);
+
+  SetStyleTexts(widget_node, "x: 25; y:35; width: 53; height: 64;", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 53, 64), widget->GetRestoredBounds());
+
+  SetStyleTexts(widget_node, "test_nothing_happens:1;", false);
+  EXPECT_EQ(gfx::Rect(25, 35, 53, 64),
+            widget->GetRestoredBounds());  // Not changed
+
+  SetStyleTexts(widget_node, "\nheight: 123;\n  ", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 53, 123), widget->GetRestoredBounds());
+
+  SetStyleTexts(widget_node, "\nx: 10; y: 23; width: 98;\n  ", true);
+  EXPECT_EQ(gfx::Rect(10, 23, 98, 123), widget->GetRestoredBounds());
+
+  // Test different combinations on view node
+  DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
+
+  SetStyleTexts(root_view_node, "x: 25; y:35; width: 45; height: 20;", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 45, 20), root_view->bounds());
+
+  SetStyleTexts(root_view_node, "test_nothing_happens:1;", false);
+  EXPECT_EQ(gfx::Rect(25, 35, 45, 20), root_view->bounds());  // Not changed
+
+  SetStyleTexts(root_view_node, "\nheight: 73;\n  ", true);
+  EXPECT_EQ(gfx::Rect(25, 35, 45, 73), root_view->bounds());
+
+  SetStyleTexts(root_view_node, "\nx: 10; y: 23; width: 52;\n  ", true);
+  EXPECT_EQ(gfx::Rect(10, 23, 52, 73), root_view->bounds());
+}
+
 }  // namespace ash
diff --git a/ash/common/frame/custom_frame_view_ash_unittest.cc b/ash/common/frame/custom_frame_view_ash_unittest.cc
index ffae7a8..f677e35 100644
--- a/ash/common/frame/custom_frame_view_ash_unittest.cc
+++ b/ash/common/frame/custom_frame_view_ash_unittest.cc
@@ -117,6 +117,13 @@
   DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshTest);
 };
 
+// Verifies the client view is not placed at a y location of 0.
+TEST_F(CustomFrameViewAshTest, ClientViewCorrectlyPlaced) {
+  std::unique_ptr<views::Widget> widget(CreateWidget(new TestWidgetDelegate));
+  widget->Show();
+  EXPECT_NE(0, widget->client_view()->bounds().y());
+}
+
 // Test that the height of the header is correct upon initially displaying
 // the widget.
 TEST_F(CustomFrameViewAshTest, HeaderHeight) {
diff --git a/ash/common/frame/default_header_painter.cc b/ash/common/frame/default_header_painter.cc
index e21030b..f333bf1 100644
--- a/ash/common/frame/default_header_painter.cc
+++ b/ash/common/frame/default_header_painter.cc
@@ -143,7 +143,7 @@
   int active_alpha = activation_animation_->CurrentValueBetween(0, 255);
   paint.setColor(color_utils::AlphaBlend(active_frame_color_,
                                          inactive_frame_color_, active_alpha));
-
+  paint.setAntiAlias(true);
   TileRoundRect(canvas, paint, GetLocalBounds(), corner_radius);
 
   if (!frame_->IsMaximized() && !frame_->IsFullscreen() &&
diff --git a/ash/common/frame/header_view.cc b/ash/common/frame/header_view.cc
index db7836c..d868bcf 100644
--- a/ash/common/frame/header_view.cc
+++ b/ash/common/frame/header_view.cc
@@ -44,7 +44,7 @@
   caption_button_container_->ResetWindowControls();
 }
 
-int HeaderView::GetPreferredOnScreenHeight() const {
+int HeaderView::GetPreferredOnScreenHeight() {
   if (is_immersive_delegate_ && target_widget_->IsFullscreen()) {
     return static_cast<int>(GetPreferredHeight() *
                             fullscreen_visible_fraction_);
@@ -52,7 +52,10 @@
   return GetPreferredHeight();
 }
 
-int HeaderView::GetPreferredHeight() const {
+int HeaderView::GetPreferredHeight() {
+  // Calculating the preferred height requires at least one Layout().
+  if (!did_layout_)
+    Layout();
   return header_painter_->GetHeaderHeightForPainting();
 }
 
@@ -105,6 +108,7 @@
 // HeaderView, views::View overrides:
 
 void HeaderView::Layout() {
+  did_layout_ = true;
   header_painter_->LayoutHeader();
 }
 
diff --git a/ash/common/frame/header_view.h b/ash/common/frame/header_view.h
index 6fa98b7..d96a62a3 100644
--- a/ash/common/frame/header_view.h
+++ b/ash/common/frame/header_view.h
@@ -47,10 +47,10 @@
   void ResetWindowControls();
 
   // Returns the amount of the view's pixels which should be on screen.
-  int GetPreferredOnScreenHeight() const;
+  int GetPreferredOnScreenHeight();
 
   // Returns the view's preferred height.
-  int GetPreferredHeight() const;
+  int GetPreferredHeight();
 
   // Returns the view's minimum width.
   int GetMinimumWidth() const;
@@ -106,6 +106,8 @@
   // Has this instance been set as the ImmersiveFullscreenControllerDelegate?
   bool is_immersive_delegate_ = true;
 
+  bool did_layout_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(HeaderView);
 };
 
diff --git a/ash/common/mojo_interface_factory.cc b/ash/common/mojo_interface_factory.cc
index e359a741..ef0b47f 100644
--- a/ash/common/mojo_interface_factory.cc
+++ b/ash/common/mojo_interface_factory.cc
@@ -6,7 +6,9 @@
 
 #include <utility>
 
+#include "ash/common/accelerators/accelerator_controller.h"
 #include "ash/common/cast_config_controller.h"
+#include "ash/common/new_window_controller.h"
 #include "ash/common/shelf/shelf_controller.h"
 #include "ash/common/shutdown_controller.h"
 #include "ash/common/system/locale/locale_notification_controller.h"
@@ -25,6 +27,11 @@
 
 namespace {
 
+void BindAcceleratorControllerRequestOnMainThread(
+    mojom::AcceleratorControllerRequest request) {
+  WmShell::Get()->accelerator_controller()->BindRequest(std::move(request));
+}
+
 void BindCastConfigOnMainThread(mojom::CastConfigRequest request) {
   WmShell::Get()->cast_config()->BindRequest(std::move(request));
 }
@@ -35,6 +42,11 @@
       std::move(request));
 }
 
+void BindNewWindowControllerRequestOnMainThread(
+    mojom::NewWindowControllerRequest request) {
+  WmShell::Get()->new_window_controller()->BindRequest(std::move(request));
+}
+
 void BindShelfRequestOnMainThread(mojom::ShelfControllerRequest request) {
   WmShell::Get()->shelf_controller()->BindRequest(std::move(request));
 }
@@ -70,11 +82,17 @@
 void RegisterInterfaces(
     service_manager::InterfaceRegistry* registry,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) {
+  registry->AddInterface(
+      base::Bind(&BindAcceleratorControllerRequestOnMainThread),
+      main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindCastConfigOnMainThread),
                          main_thread_task_runner);
   registry->AddInterface(
       base::Bind(&BindLocaleNotificationControllerOnMainThread),
       main_thread_task_runner);
+  registry->AddInterface(
+      base::Bind(&BindNewWindowControllerRequestOnMainThread),
+      main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShelfRequestOnMainThread),
                          main_thread_task_runner);
   registry->AddInterface(base::Bind(&BindShutdownControllerRequestOnMainThread),
diff --git a/ash/common/new_window_client_proxy.cc b/ash/common/new_window_client_proxy.cc
deleted file mode 100644
index 42f0d30..0000000
--- a/ash/common/new_window_client_proxy.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/common/new_window_client_proxy.h"
-
-#include "base/logging.h"
-#include "content/public/common/service_names.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace ash {
-
-NewWindowClientProxy::NewWindowClientProxy(
-    service_manager::Connector* connector)
-    : connector_(connector) {}
-
-NewWindowClientProxy::~NewWindowClientProxy() {}
-
-void NewWindowClientProxy::NewTab() {
-  EnsureInterface();
-  client_->NewTab();
-}
-
-void NewWindowClientProxy::NewWindow(bool incognito) {
-  EnsureInterface();
-  client_->NewWindow(incognito);
-}
-
-void NewWindowClientProxy::OpenFileManager() {
-  EnsureInterface();
-  client_->OpenFileManager();
-}
-
-void NewWindowClientProxy::OpenCrosh() {
-  EnsureInterface();
-  client_->OpenCrosh();
-}
-
-void NewWindowClientProxy::OpenGetHelp() {
-  EnsureInterface();
-  client_->OpenGetHelp();
-}
-
-void NewWindowClientProxy::RestoreTab() {
-  EnsureInterface();
-  client_->RestoreTab();
-}
-
-void NewWindowClientProxy::ShowKeyboardOverlay() {
-  EnsureInterface();
-  client_->ShowKeyboardOverlay();
-}
-
-void NewWindowClientProxy::ShowTaskManager() {
-  EnsureInterface();
-  client_->ShowTaskManager();
-}
-
-void NewWindowClientProxy::OpenFeedbackPage() {
-  EnsureInterface();
-  client_->OpenFeedbackPage();
-}
-
-void NewWindowClientProxy::EnsureInterface() {
-  // |connector_| can be null in unit tests. We check this at first usage
-  // instead of during construction because a NewWindowClientProxy is always
-  // created and is then replaced with a mock in the unit tests.
-  DCHECK(connector_);
-
-  if (client_)
-    return;
-  connector_->ConnectToInterface(content::mojom::kBrowserServiceName, &client_);
-  client_.set_connection_error_handler(base::Bind(
-      &NewWindowClientProxy::OnClientConnectionError, base::Unretained(this)));
-}
-
-void NewWindowClientProxy::OnClientConnectionError() {
-  client_.reset();
-}
-
-}  // namespace ash
diff --git a/ash/common/new_window_client_proxy.h b/ash/common/new_window_client_proxy.h
deleted file mode 100644
index fcefe0c..0000000
--- a/ash/common/new_window_client_proxy.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMMON_NEW_WINDOW_CLIENT_PROXY_H_
-#define ASH_COMMON_NEW_WINDOW_CLIENT_PROXY_H_
-
-#include "ash/public/interfaces/new_window.mojom.h"
-#include "base/macros.h"
-
-namespace service_manager {
-class Connector;
-}
-
-namespace ash {
-
-// A NewWindowClient which lazily connects to an exported mojom::NewWindowClient
-// in "content_browser" on first use.
-class NewWindowClientProxy : public mojom::NewWindowClient {
- public:
-  explicit NewWindowClientProxy(service_manager::Connector* connector);
-  ~NewWindowClientProxy() override;
-
-  // NewWindowClient:
-  void NewTab() override;
-  void NewWindow(bool incognito) override;
-  void OpenFileManager() override;
-  void OpenCrosh() override;
-  void OpenGetHelp() override;
-  void RestoreTab() override;
-  void ShowKeyboardOverlay() override;
-  void ShowTaskManager() override;
-  void OpenFeedbackPage() override;
-
- private:
-  // Ensures that we have a valid |client_| before we try to use it.
-  void EnsureInterface();
-
-  // Clears |client_| when |client_| has a connection error.
-  void OnClientConnectionError();
-
-  service_manager::Connector* connector_;
-  mojom::NewWindowClientPtr client_;
-
-  DISALLOW_COPY_AND_ASSIGN(NewWindowClientProxy);
-};
-
-}  // namespace ash
-
-#endif  // ASH_COMMON_NEW_WINDOW_CLIENT_PROXY_H_
diff --git a/ash/common/new_window_controller.cc b/ash/common/new_window_controller.cc
new file mode 100644
index 0000000..f316e9c7
--- /dev/null
+++ b/ash/common/new_window_controller.cc
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/common/new_window_controller.h"
+
+#include <utility>
+
+namespace ash {
+
+NewWindowController::NewWindowController() {}
+
+NewWindowController::~NewWindowController() {}
+
+void NewWindowController::BindRequest(
+    mojom::NewWindowControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void NewWindowController::NewTab() {
+  if (client_)
+    client_->NewTab();
+}
+
+void NewWindowController::NewWindow(bool incognito) {
+  if (client_)
+    client_->NewWindow(incognito);
+}
+
+void NewWindowController::OpenFileManager() {
+  if (client_)
+    client_->OpenFileManager();
+}
+
+void NewWindowController::OpenCrosh() {
+  if (client_)
+    client_->OpenCrosh();
+}
+
+void NewWindowController::OpenGetHelp() {
+  if (client_)
+    client_->OpenGetHelp();
+}
+
+void NewWindowController::RestoreTab() {
+  if (client_)
+    client_->RestoreTab();
+}
+
+void NewWindowController::ShowKeyboardOverlay() {
+  if (client_)
+    client_->ShowKeyboardOverlay();
+}
+
+void NewWindowController::ShowTaskManager() {
+  if (client_)
+    client_->ShowTaskManager();
+}
+
+void NewWindowController::OpenFeedbackPage() {
+  if (client_)
+    client_->OpenFeedbackPage();
+}
+
+void NewWindowController::SetClient(
+    mojom::NewWindowClientAssociatedPtrInfo client) {
+  client_.Bind(std::move(client));
+}
+
+}  // namespace ash
diff --git a/ash/common/new_window_controller.h b/ash/common/new_window_controller.h
new file mode 100644
index 0000000..32e262a
--- /dev/null
+++ b/ash/common/new_window_controller.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMMON_NEW_WINDOW_CONTROLLER_H_
+#define ASH_COMMON_NEW_WINDOW_CONTROLLER_H_
+
+#include "ash/public/interfaces/new_window.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace ash {
+
+// Provides the NewWindowController interface to the outside world. This lets a
+// consumer of ash provide a NewWindowClient, which we will dispatch to if one
+// has been provided to us.
+class NewWindowController : public mojom::NewWindowController,
+                            public mojom::NewWindowClient {
+ public:
+  NewWindowController();
+  ~NewWindowController() override;
+
+  void BindRequest(mojom::NewWindowControllerRequest request);
+
+  // NewWindowClient:
+  void NewTab() override;
+  void NewWindow(bool incognito) override;
+  void OpenFileManager() override;
+  void OpenCrosh() override;
+  void OpenGetHelp() override;
+  void RestoreTab() override;
+  void ShowKeyboardOverlay() override;
+  void ShowTaskManager() override;
+  void OpenFeedbackPage() override;
+
+ private:
+  // NewWindowController:
+  void SetClient(mojom::NewWindowClientAssociatedPtrInfo client) override;
+
+  mojo::BindingSet<mojom::NewWindowController> bindings_;
+
+  mojom::NewWindowClientAssociatedPtr client_;
+
+  DISALLOW_COPY_AND_ASSIGN(NewWindowController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_COMMON_NEW_WINDOW_CONTROLLER_H_
diff --git a/ash/common/shelf/shelf_model.cc b/ash/common/shelf/shelf_model.cc
index 04705e4..e5254194 100644
--- a/ash/common/shelf/shelf_model.cc
+++ b/ash/common/shelf/shelf_model.cc
@@ -90,7 +90,11 @@
 }
 
 void ShelfModel::Set(int index, const ShelfItem& item) {
-  DCHECK(index >= 0 && index < item_count());
+  if (index < 0 || index >= item_count()) {
+    NOTREACHED();
+    return;
+  }
+
   int new_index = item.type == items_[index].type
                       ? index
                       : ValidateInsertionIndex(item.type, index);
diff --git a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
index b10258c..8e6967e 100644
--- a/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
+++ b/ash/common/system/chromeos/ime_menu/ime_menu_tray.cc
@@ -55,6 +55,13 @@
   return gfx::Range(tray_item_height * min_items, tray_item_height * max_items);
 }
 
+// Returns the minimum with of IME menu.
+int GetMinimumMenuWidth() {
+  return MaterialDesignController::IsSystemTrayMenuMaterial()
+             ? kTrayMenuMinimumWidthMd
+             : kTrayMenuMinimumWidth;
+}
+
 // Shows language and input settings page.
 void ShowIMESettings() {
   WmShell::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_IME_SHOW_DETAILED);
@@ -293,6 +300,39 @@
   DISALLOW_COPY_AND_ASSIGN(ImeButtonsView);
 };
 
+// The list view that contains the selected IME and property items.
+class ImeMenuListView : public ImeListView {
+ public:
+  ImeMenuListView(SystemTrayItem* owner,
+                  bool show_keyboard_toggle,
+                  SingleImeBehavior single_ime_behavior)
+      : ImeListView(owner, show_keyboard_toggle, ImeListView::HIDE_SINGLE_IME) {
+  }
+
+  ~ImeMenuListView() override {}
+
+ protected:
+  void Layout() override {
+    gfx::Range height_range = GetImeListViewRange();
+    if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
+      scroller()->ClipHeightTo(height_range.start(), height_range.end());
+    } else {
+      uint32_t current_height = scroll_content()->height();
+      int minimum_menu_width = GetMinimumMenuWidth();
+      if (current_height > height_range.end()) {
+        scroller()->SetFixedSize(
+            gfx::Size(minimum_menu_width, height_range.end()));
+      } else if (current_height < height_range.start()) {
+        scroller()->SetFixedSize(
+            gfx::Size(minimum_menu_width, height_range.start()));
+      }
+    }
+    ImeListView::Layout();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ImeMenuListView);
+};
+
 }  // namespace
 
 ImeMenuTray::ImeMenuTray(WmShelf* wm_shelf)
@@ -324,9 +364,7 @@
 }
 
 void ImeMenuTray::ShowImeMenuBubble() {
-  int minimum_menu_width = MaterialDesignController::IsSystemTrayMenuMaterial()
-                               ? kTrayMenuMinimumWidthMd
-                               : kTrayMenuMinimumWidth;
+  int minimum_menu_width = GetMinimumMenuWidth();
   should_block_shelf_auto_hide_ = true;
   views::TrayBubbleView::InitParams init_params(
       GetAnchorAlignment(), minimum_menu_width, minimum_menu_width);
@@ -347,22 +385,8 @@
   }
 
   // Adds IME list to the bubble.
-  ime_list_view_ = new ImeListView(nullptr, ShouldShowKeyboardToggle(),
-                                   ImeListView::SHOW_SINGLE_IME);
-
-  uint32_t current_height = ime_list_view_->scroll_content()->height();
-  const gfx::Range height_range = GetImeListViewRange();
-
-  if (MaterialDesignController::IsSystemTrayMenuMaterial()) {
-    ime_list_view_->scroller()->ClipHeightTo(height_range.start(),
-                                             height_range.end());
-  } else if (current_height > height_range.end()) {
-    ime_list_view_->scroller()->SetFixedSize(
-        gfx::Size(minimum_menu_width, height_range.end()));
-  } else if (current_height < height_range.start()) {
-    ime_list_view_->scroller()->SetFixedSize(
-        gfx::Size(minimum_menu_width, height_range.start()));
-  }
+  ime_list_view_ = new ImeMenuListView(nullptr, ShouldShowKeyboardToggle(),
+                                       ImeListView::SHOW_SINGLE_IME);
   bubble_view->AddChildView(ime_list_view_);
 
   // The bottom view that contains buttons are not supported in login/lock
diff --git a/ash/common/system/user/user_card_view.cc b/ash/common/system/user/user_card_view.cc
index f222058c..157d07e 100644
--- a/ash/common/system/user/user_card_view.cc
+++ b/ash/common/system/user/user_card_view.cc
@@ -564,9 +564,8 @@
   auto user_email = new views::Label();
   base::string16 user_email_string;
   if (login_status != LoginStatus::GUEST) {
-    SystemTrayDelegate* tray_delegate = WmShell::Get()->system_tray_delegate();
     user_email_string =
-        tray_delegate->IsUserSupervised()
+        WmShell::Get()->system_tray_delegate()->IsUserSupervised()
             ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL)
             : base::UTF8ToUTF16(
                   delegate->GetUserInfo(user_index_)->GetDisplayEmail());
@@ -574,6 +573,8 @@
   user_email->SetText(user_email_string);
   user_email->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   user_email_style.SetupLabel(user_email);
+  user_email->SetVisible(!user_email_string.empty());
+  user_email->set_collapse_when_hidden(true);
 
   views::View* stack_of_labels = new views::View;
   AddChildView(stack_of_labels);
diff --git a/ash/common/test/BUILD.gn b/ash/common/test/BUILD.gn
index 9152cd1..cafc2c1 100644
--- a/ash/common/test/BUILD.gn
+++ b/ash/common/test/BUILD.gn
@@ -10,8 +10,6 @@
     "ash_test_impl.h",
     "material_design_controller_test_api.cc",
     "material_design_controller_test_api.h",
-    "test_new_window_client.cc",
-    "test_new_window_client.h",
     "test_palette_delegate.cc",
     "test_palette_delegate.h",
     "test_session_state_delegate.cc",
diff --git a/ash/common/test/test_new_window_client.cc b/ash/common/test/test_new_window_client.cc
deleted file mode 100644
index 8c67c2c0..0000000
--- a/ash/common/test/test_new_window_client.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/common/test/test_new_window_client.h"
-
-namespace ash {
-
-TestNewWindowClient::TestNewWindowClient() {}
-
-TestNewWindowClient::~TestNewWindowClient() {}
-
-void TestNewWindowClient::NewTab() {}
-
-void TestNewWindowClient::NewWindow(bool incognito) {}
-
-void TestNewWindowClient::OpenFileManager() {}
-
-void TestNewWindowClient::OpenCrosh() {}
-
-void TestNewWindowClient::OpenGetHelp() {}
-
-void TestNewWindowClient::RestoreTab() {}
-
-void TestNewWindowClient::ShowKeyboardOverlay() {}
-
-void TestNewWindowClient::ShowTaskManager() {}
-
-void TestNewWindowClient::OpenFeedbackPage() {}
-
-}  // namespace ash
diff --git a/ash/common/test/test_new_window_client.h b/ash/common/test/test_new_window_client.h
deleted file mode 100644
index 1fd5e58..0000000
--- a/ash/common/test/test_new_window_client.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_COMMON_TEST_TEST_NEW_WINDOW_CLIENT_H_
-#define ASH_COMMON_TEST_TEST_NEW_WINDOW_CLIENT_H_
-
-#include "ash/public/interfaces/new_window.mojom.h"
-
-namespace ash {
-
-// A mojom::NewWindowClient which doesn't try to access a remote mojo component
-// for testing purpose.
-class TestNewWindowClient : public mojom::NewWindowClient {
- public:
-  TestNewWindowClient();
-  ~TestNewWindowClient() override;
-
-  // NewWindowClient:
-  void NewTab() override;
-  void NewWindow(bool incognito) override;
-  void OpenFileManager() override;
-  void OpenCrosh() override;
-  void OpenGetHelp() override;
-  void RestoreTab() override;
-  void ShowKeyboardOverlay() override;
-  void ShowTaskManager() override;
-  void OpenFeedbackPage() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestNewWindowClient);
-};
-
-}  // namespace ash
-
-#endif  // ASH_COMMON_TEST_TEST_NEW_WINDOW_CLIENT_H_
diff --git a/ash/common/test/wm_shell_test_api.cc b/ash/common/test/wm_shell_test_api.cc
index fc37d56..6be4bab0 100644
--- a/ash/common/test/wm_shell_test_api.cc
+++ b/ash/common/test/wm_shell_test_api.cc
@@ -21,9 +21,4 @@
   WmShell::Get()->SetSystemTrayDelegate(std::move(delegate));
 }
 
-void WmShellTestApi::SetNewWindowClient(
-    std::unique_ptr<mojom::NewWindowClient> client) {
-  WmShell::Get()->new_window_client_ = std::move(client);
-}
-
 }  // namespace ash
diff --git a/ash/common/test/wm_shell_test_api.h b/ash/common/test/wm_shell_test_api.h
index 1e46ba94..07ab6fff 100644
--- a/ash/common/test/wm_shell_test_api.h
+++ b/ash/common/test/wm_shell_test_api.h
@@ -13,10 +13,6 @@
 
 class SystemTrayDelegate;
 
-namespace mojom {
-class NewWindowClient;
-}
-
 // Test API to access the internal state of the singleton WmShell.
 class WmShellTestApi {
  public:
@@ -25,8 +21,6 @@
 
   void SetSystemTrayDelegate(std::unique_ptr<SystemTrayDelegate> delegate);
 
-  void SetNewWindowClient(std::unique_ptr<mojom::NewWindowClient> client);
-
  private:
   DISALLOW_COPY_AND_ASSIGN(WmShellTestApi);
 };
diff --git a/ash/common/wallpaper/wallpaper_controller.cc b/ash/common/wallpaper/wallpaper_controller.cc
index e68e1a1..4da1034 100644
--- a/ash/common/wallpaper/wallpaper_controller.cc
+++ b/ash/common/wallpaper/wallpaper_controller.cc
@@ -4,7 +4,6 @@
 
 #include "ash/common/wallpaper/wallpaper_controller.h"
 
-#include "ash/common/shell_delegate.h"
 #include "ash/common/wallpaper/wallpaper_controller_observer.h"
 #include "ash/common/wallpaper/wallpaper_delegate.h"
 #include "ash/common/wallpaper/wallpaper_view.h"
@@ -17,8 +16,6 @@
 #include "base/logging.h"
 #include "base/task_runner.h"
 #include "components/wallpaper/wallpaper_resizer.h"
-#include "content/public/common/service_names.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/display/screen.h"
 #include "ui/views/widget/widget.h"
@@ -72,7 +69,7 @@
   return wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED;
 }
 
-bool WallpaperController::SetWallpaperImage(const gfx::ImageSkia& image,
+void WallpaperController::SetWallpaperImage(const gfx::ImageSkia& image,
                                             wallpaper::WallpaperLayout layout) {
   VLOG(1) << "SetWallpaper: image_id="
           << wallpaper::WallpaperResizer::GetImageId(image)
@@ -80,7 +77,7 @@
 
   if (WallpaperIsAlreadyLoaded(image, true /* compare_layouts */, layout)) {
     VLOG(1) << "Wallpaper is already loaded";
-    return false;
+    return;
   }
 
   current_wallpaper_.reset(new wallpaper::WallpaperResizer(
@@ -91,7 +88,6 @@
     observer.OnWallpaperDataChanged();
   wallpaper_mode_ = WALLPAPER_IMAGE;
   InstallDesktopControllerForAllWindows();
-  return true;
 }
 
 void WallpaperController::CreateEmptyWallpaper() {
@@ -191,16 +187,14 @@
 }
 
 void WallpaperController::OpenSetWallpaperPage() {
-  WmShell* shell = WmShell::Get();
-  service_manager::Connector* connector =
-      shell->delegate()->GetShellConnector();
-  if (!connector || !shell->wallpaper_delegate()->CanOpenSetWallpaperPage())
-    return;
+  if (wallpaper_picker_ &&
+      WmShell::Get()->wallpaper_delegate()->CanOpenSetWallpaperPage()) {
+    wallpaper_picker_->Open();
+  }
+}
 
-  mojom::WallpaperManagerPtr wallpaper_manager;
-  connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                &wallpaper_manager);
-  wallpaper_manager->Open();
+void WallpaperController::SetWallpaperPicker(mojom::WallpaperPickerPtr picker) {
+  wallpaper_picker_ = std::move(picker);
 }
 
 void WallpaperController::SetWallpaper(const SkBitmap& wallpaper,
diff --git a/ash/common/wallpaper/wallpaper_controller.h b/ash/common/wallpaper/wallpaper_controller.h
index 2d8ba39..ac2000f05 100644
--- a/ash/common/wallpaper/wallpaper_controller.h
+++ b/ash/common/wallpaper/wallpaper_controller.h
@@ -56,11 +56,8 @@
 
   wallpaper::WallpaperLayout GetWallpaperLayout() const;
 
-  // Sets wallpaper. This is mostly called by WallpaperManager to set
-  // the default or user selected custom wallpaper.
-  // Returns true if new image was actually set. And false when duplicate set
-  // request detected.
-  bool SetWallpaperImage(const gfx::ImageSkia& image,
+  // Sets the wallpaper and alerts observers of changes.
+  void SetWallpaperImage(const gfx::ImageSkia& image,
                          wallpaper::WallpaperLayout layout);
 
   // Creates an empty wallpaper. Some tests require a wallpaper widget is ready
@@ -106,6 +103,7 @@
   void OpenSetWallpaperPage();
 
   // mojom::WallpaperController overrides:
+  void SetWallpaperPicker(mojom::WallpaperPickerPtr picker) override;
   void SetWallpaper(const SkBitmap& wallpaper,
                     wallpaper::WallpaperLayout layout) override;
 
@@ -131,6 +129,9 @@
 
   WallpaperMode wallpaper_mode_;
 
+  // Wallpaper picker interface in chrome browser, used to open the picker.
+  mojom::WallpaperPickerPtr wallpaper_picker_;
+
   // Bindings for the WallpaperController interface.
   mojo::BindingSet<mojom::WallpaperController> bindings_;
 
diff --git a/ash/common/wm/window_state.h b/ash/common/wm/window_state.h
index 9d4eaac..a01d923 100644
--- a/ash/common/wm/window_state.h
+++ b/ash/common/wm/window_state.h
@@ -281,7 +281,7 @@
   // True if the window should not adjust the window's bounds when
   // virtual keyboard bounds changes.
   // TODO(oshima): This is hack. Replace this with proper
-  // implementation based on EnsureCaretInRect.
+  // implementation based on EnsureCaretNotInRect.
   bool ignore_keyboard_bounds_change() const {
     return ignore_keyboard_bounds_change_;
   }
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index 0ef8c86..e9d7568f 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -14,7 +14,7 @@
 #include "ash/common/devtools/ash_devtools_dom_agent.h"
 #include "ash/common/focus_cycler.h"
 #include "ash/common/keyboard/keyboard_ui.h"
-#include "ash/common/new_window_client_proxy.h"
+#include "ash/common/new_window_controller.h"
 #include "ash/common/palette_delegate.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
@@ -253,8 +253,7 @@
       immersive_context_(base::MakeUnique<ImmersiveContextAsh>()),
       locale_notification_controller_(
           base::MakeUnique<LocaleNotificationController>()),
-      new_window_client_(base::MakeUnique<NewWindowClientProxy>(
-          delegate_->GetShellConnector())),
+      new_window_controller_(base::MakeUnique<NewWindowController>()),
       shelf_controller_(base::MakeUnique<ShelfController>()),
       shutdown_controller_(base::MakeUnique<ShutdownController>()),
       system_tray_controller_(base::MakeUnique<SystemTrayController>()),
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index 1dff4ae..65ad4a22 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -54,6 +54,7 @@
 class LocaleNotificationController;
 class MaximizeModeController;
 class MruWindowTracker;
+class NewWindowController;
 class PaletteDelegate;
 class ScopedDisableInternalMouseAndKeyboard;
 class SessionStateDelegate;
@@ -83,10 +84,6 @@
 enum class LoginStatus;
 enum class TaskSwitchSource;
 
-namespace mojom {
-class NewWindowClient;
-}
-
 namespace wm {
 class MaximizeModeEventHandler;
 class WindowState;
@@ -145,8 +142,8 @@
 
   MediaDelegate* media_delegate() { return media_delegate_.get(); }
 
-  mojom::NewWindowClient* new_window_client() {
-    return new_window_client_.get();
+  NewWindowController* new_window_controller() {
+    return new_window_controller_.get();
   }
 
   // NOTE: Prefer ScopedRootWindowForNewWindows when setting temporarily.
@@ -492,7 +489,7 @@
   std::unique_ptr<MaximizeModeController> maximize_mode_controller_;
   std::unique_ptr<MediaDelegate> media_delegate_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
-  std::unique_ptr<mojom::NewWindowClient> new_window_client_;
+  std::unique_ptr<NewWindowController> new_window_controller_;
   std::unique_ptr<PaletteDelegate> palette_delegate_;
   std::unique_ptr<ShelfController> shelf_controller_;
   std::unique_ptr<ShelfDelegate> shelf_delegate_;
diff --git a/ash/display/display_util.cc b/ash/display/display_util.cc
index bcc9ca3..1b86878 100644
--- a/ash/display/display_util.cc
+++ b/ash/display/display_util.cc
@@ -7,13 +7,13 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/common/new_window_controller.h"
 #include "ash/common/system/system_notifier.h"
 #include "ash/common/wm_shell.h"
 #include "ash/display/extended_mouse_warp_controller.h"
 #include "ash/display/null_mouse_warp_controller.h"
 #include "ash/display/unified_mouse_warp_controller.h"
 #include "ash/host/ash_window_tree_host.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "ash/shell.h"
 #include "base/strings/string_number_conversions.h"
 #include "grit/ash_resources.h"
@@ -53,7 +53,7 @@
   bool HasClickedListener() override { return true; }
 
   void Click() override {
-    WmShell::Get()->new_window_client()->OpenFeedbackPage();
+    WmShell::Get()->new_window_controller()->OpenFeedbackPage();
   }
 
  private:
diff --git a/ash/laser/laser_pointer_points.h b/ash/laser/laser_pointer_points.h
index 69e7413d..a456595 100644
--- a/ash/laser/laser_pointer_points.h
+++ b/ash/laser/laser_pointer_points.h
@@ -26,7 +26,7 @@
     gfx::Point location;
     // age is a value between [0,1] where 0 means the point was just added and 1
     // means that the point is just about to be removed.
-    double age = 0.0;
+    float age = 0.0f;
   };
 
   // Constructor with a parameter to choose the fade out time of the points in
diff --git a/ash/laser/laser_pointer_view.cc b/ash/laser/laser_pointer_view.cc
index 4f3bbe6..ed3d405 100644
--- a/ash/laser/laser_pointer_view.cc
+++ b/ash/laser/laser_pointer_view.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/laser/laser_pointer_points.h"
+#include "ash/laser/laser_segment_utils.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "base/timer/timer.h"
@@ -21,10 +22,10 @@
 namespace {
 
 // Variables for rendering the laser. Radius in DIP.
-const double kPointInitialRadius = 5;
-const double kPointFinalRadius = 0.25;
+const float kPointInitialRadius = 5.0f;
+const float kPointFinalRadius = 0.25f;
 const int kPointInitialOpacity = 200;
-const int kPointFinalOpacity = 0;
+const int kPointFinalOpacity = 10;
 const SkColor kPointColor = SkColorSetRGB(255, 0, 0);
 
 float DistanceBetweenPoints(const gfx::Point& point1,
@@ -32,9 +33,9 @@
   return (point1 - point2).Length();
 }
 
-double LinearInterpolate(double initial_value,
-                         double final_value,
-                         double progress) {
+float LinearInterpolate(float initial_value,
+                        float final_value,
+                        float progress) {
   return initial_value + (final_value - initial_value) * progress;
 }
 
@@ -42,6 +43,121 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+// The laser segment calcuates the path needed to draw a laser segment. A laser
+// segment is used instead of just a regular line segments to avoid overlapping.
+// A laser segment looks as follows:
+//    _______         _________       _________        _________
+//   /       \        \       /      /         /      /         \       |
+//   |   A   |       2|.  B  .|1    2|.   C   .|1    2|.   D     \.1    |
+//   |       |        |       |      |         |      |          /      |
+//    \_____/         /_______\      \_________\      \_________/       |
+//
+//
+// Given a start and end point (represented by the periods in the above
+// diagrams), we create each segment by projecting each point along the normal
+// to the line segment formed by the start(1) and end(2) points. We then
+// create a path using arcs and lines. There are three types of laser segments:
+// head(B), regular(C) and tail(D). A typical laser is created by rendering one
+// tail(D), zero or more regular segments(C), one head(B) and a circle at the
+// end(A). They are meant to fit perfectly with the previous and next segments,
+// so that no whitespace/overlap is shown.
+// A more detailed version of this is located at https://goo.gl/qixdux.
+class LaserSegment {
+ public:
+  LaserSegment(const std::vector<gfx::PointF>& previous_points,
+               const gfx::PointF& start_point,
+               const gfx::PointF& end_point,
+               float start_radius,
+               float end_radius,
+               bool is_last_segment) {
+    DCHECK(previous_points.empty() || previous_points.size() == 2u);
+    bool is_first_segment = previous_points.empty();
+
+    // Calculate the variables for the equation of the lines which pass through
+    // the start and end points, and are perpendicular to the line segment
+    // between the start and end points.
+    float slope, start_y_intercept, end_y_intercept;
+    ComputeNormalLineVariables(start_point, end_point, &slope,
+                               &start_y_intercept, &end_y_intercept);
+
+    // Project the points along normal line by the given radius.
+    gfx::PointF end_first_projection, end_second_projection;
+    ComputeProjectedPoints(end_point, slope, end_y_intercept, end_radius,
+                           &end_first_projection, &end_second_projection);
+
+    // Create a collection of the points used to create the path and reorder
+    // them as needed.
+    std::vector<gfx::PointF> ordered_points;
+    ordered_points.reserve(4);
+    if (!is_first_segment) {
+      ordered_points.push_back(previous_points[1]);
+      ordered_points.push_back(previous_points[0]);
+    } else {
+      // We push two of the same point, so that for both cases we have 4 points,
+      // and we can use the same indexes when creating the path.
+      ordered_points.push_back(start_point);
+      ordered_points.push_back(start_point);
+    }
+    // Push the projected points so that the the smaller angle relative to the
+    // line segment between the two data points is first. This will ensure there
+    // is always a anticlockwise arc between the last two points, and always a
+    // clockwise arc for these two points if and when they are used in the next
+    // segment.
+    if (IsFirstPointSmallerAngle(start_point, end_point, end_first_projection,
+                                 end_second_projection)) {
+      ordered_points.push_back(end_first_projection);
+      ordered_points.push_back(end_second_projection);
+    } else {
+      ordered_points.push_back(end_second_projection);
+      ordered_points.push_back(end_first_projection);
+    }
+
+    // Create the path. The path always goes as follows:
+    // 1. Move to point 0.
+    // 2. Arc clockwise from point 0 to point 1. This step is skipped if it
+    //    is the tail segment.
+    // 3. Line from point 1 to point 2.
+    // 4. Arc anticlockwise from point 2 to point 3. Arc clockwise if this is
+    //    the head segment.
+    // 5. Line from point 3 to point 0.
+    //      2           1
+    //       *---------*                   |
+    //      /         /                    |
+    //      |         |                    |
+    //      |         |                    |
+    //      \         \                    |
+    //       *--------*
+    //      3          0
+    DCHECK_EQ(4u, ordered_points.size());
+    path_.moveTo(ordered_points[0].x(), ordered_points[0].y());
+    if (!is_first_segment) {
+      path_.arcTo(start_radius, start_radius, 180.0f, gfx::Path::kSmall_ArcSize,
+                  gfx::Path::kCW_Direction, ordered_points[1].x(),
+                  ordered_points[1].y());
+    }
+
+    path_.lineTo(ordered_points[2].x(), ordered_points[2].y());
+    path_.arcTo(
+        end_radius, end_radius, 180.0f, gfx::Path::kSmall_ArcSize,
+        is_last_segment ? gfx::Path::kCW_Direction : gfx::Path::kCCW_Direction,
+        ordered_points[3].x(), ordered_points[3].y());
+    path_.lineTo(ordered_points[0].x(), ordered_points[0].y());
+
+    // Store data to be used by the next segment.
+    path_points_.push_back(ordered_points[2]);
+    path_points_.push_back(ordered_points[3]);
+  }
+
+  SkPath path() const { return path_; }
+  std::vector<gfx::PointF> path_points() const { return path_points_; }
+
+ private:
+  SkPath path_;
+  std::vector<gfx::PointF> path_points_;
+
+  DISALLOW_COPY_AND_ASSIGN(LaserSegment);
+};
+
 // LaserPointerView
 LaserPointerView::LaserPointerView(base::TimeDelta life_duration,
                                    aura::Window* root_window)
@@ -104,52 +220,59 @@
     return;
 
   SkPaint paint;
-  paint.setStyle(SkPaint::kStroke_Style);
+  paint.setStyle(SkPaint::kFill_Style);
   paint.setAntiAlias(true);
-  paint.setStrokeJoin(SkPaint::kBevel_Join);
 
   // Compute the offset of the current widget.
-  gfx::Point widget_offset =
-      widget_->GetNativeView()->GetBoundsInRootWindow().origin();
+  gfx::Vector2d widget_offset(
+      widget_->GetNativeView()->GetBoundsInRootWindow().origin().x(),
+      widget_->GetNativeView()->GetBoundsInRootWindow().origin().y());
 
-  gfx::Point previous_point(
-      (laser_points_.GetOldest().location - widget_offset).x(),
-      (laser_points_.GetOldest().location - widget_offset).y());
-  gfx::Point current_point;
+  int num_points = laser_points_.GetNumberOfPoints();
+  DCHECK(num_points > 0);
+  LaserPointerPoints::LaserPoint previous_point = laser_points_.GetOldest();
+  previous_point.location -= widget_offset;
+  LaserPointerPoints::LaserPoint current_point;
+  std::vector<gfx::PointF> previous_segment_points;
+  float previous_radius;
+  int current_opacity;
 
-  int num_points_ = laser_points_.GetNumberOfPoints();
-  int point_count = 0;
-  int current_opacity = 0;
-  for (const LaserPointerPoints::LaserPoint& point :
-       laser_points_.laser_points()) {
+  for (int i = 0; i < num_points; ++i) {
+    current_point = laser_points_.laser_points()[i];
+    current_point.location -= widget_offset;
+
     // Set the radius and opacity based on the distance.
-    double current_radius =
-        LinearInterpolate(kPointInitialRadius, kPointFinalRadius, point.age);
+    float current_radius = LinearInterpolate(
+        kPointInitialRadius, kPointFinalRadius, current_point.age);
     current_opacity = int{LinearInterpolate(
-        double{kPointInitialOpacity}, double{kPointFinalOpacity}, point.age)};
-
-    gfx::Vector2d center = point.location - widget_offset;
-    current_point = gfx::Point(center.x(), center.y());
+        kPointInitialOpacity, kPointFinalOpacity, current_point.age)};
 
     // If we draw laser_points_ that are within a stroke width of each other,
     // the result will be very jagged, unless we are on the last point, then we
     // draw regardless.
-    point_count++;
-    float distance_threshold = float{current_radius * 2};
-    if (DistanceBetweenPoints(previous_point, current_point) <=
-            distance_threshold &&
-        point_count != num_points_) {
+    float distance_threshold = current_radius * 2.0f;
+    if (DistanceBetweenPoints(previous_point.location,
+                              current_point.location) <= distance_threshold &&
+        i != num_points - 1) {
       continue;
     }
 
+    LaserSegment current_segment(
+        previous_segment_points, gfx::PointF(previous_point.location),
+        gfx::PointF(current_point.location), previous_radius, current_radius,
+        i == num_points - 1);
+
+    SkPath path = current_segment.path();
     paint.setColor(SkColorSetA(kPointColor, current_opacity));
-    paint.setStrokeWidth(current_radius * 2);
-    canvas->DrawLine(previous_point, current_point, paint);
+    canvas->DrawPath(path, paint);
+
+    previous_segment_points = current_segment.path_points();
+    previous_radius = current_radius;
     previous_point = current_point;
   }
   // Draw the last point as a circle.
   paint.setColor(SkColorSetA(kPointColor, current_opacity));
   paint.setStyle(SkPaint::kFill_Style);
-  canvas->DrawCircle(current_point, kPointInitialRadius, paint);
+  canvas->DrawCircle(current_point.location, kPointInitialRadius, paint);
 }
 }  // namespace ash
diff --git a/ash/laser/laser_segment_utils.cc b/ash/laser/laser_segment_utils.cc
new file mode 100644
index 0000000..189f51b
--- /dev/null
+++ b/ash/laser/laser_segment_utils.cc
@@ -0,0 +1,128 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/laser/laser_segment_utils.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/transform.h"
+
+namespace ash {
+namespace {
+
+// Solves the equation x = (-b (+|-) sqrt(b^2 - 4ac)) / 2a. |use_plus|
+// determines whether + or - is used in the equation; if |use_plus| is true, +
+// is used. |a| cannot be 0 (linear equation). Note: This does not handle the
+// case where the roots are complex.
+float QuadraticEquation(bool use_plus, float a, float b, float c) {
+  DCHECK_NE(0.0f, a);
+  return (-1.0f * b + sqrt(b * b - 4.0f * a * c) * (use_plus ? 1.0f : -1.0f)) /
+         (2.0f * a);
+}
+}
+
+float AngleOfPointInNewCoordinates(const gfx::PointF& origin,
+                                   const gfx::Vector2dF& direction,
+                                   const gfx::PointF& point) {
+  double angle_degrees = atan2(direction.y(), direction.x()) * 180.0f / M_PI;
+  gfx::Transform transform;
+  transform.Rotate(-angle_degrees);
+  transform.Translate(-origin.x(), -origin.y());
+  gfx::Point3F point_to_transform(point.x(), point.y(), 0.0f);
+  transform.TransformPoint(&point_to_transform);
+  return atan2(point_to_transform.y(), point_to_transform.x());
+}
+
+void ComputeNormalLineVariables(const gfx::PointF& start_point,
+                                const gfx::PointF& end_point,
+                                float* normal_slope,
+                                float* start_y_intercept,
+                                float* end_y_intercept) {
+  float rise = end_point.y() - start_point.y();
+  float run = end_point.x() - start_point.x();
+  // If the rise of line between the two points is close to zero, the normal of
+  // the line is undefined.
+  if (fabs(rise) < 0.0001f) {
+    *normal_slope = std::numeric_limits<float>::quiet_NaN();
+    *start_y_intercept = std::numeric_limits<float>::quiet_NaN();
+    *end_y_intercept = std::numeric_limits<float>::quiet_NaN();
+    return;
+  }
+
+  *normal_slope = -1.0f * (run / rise);
+  *start_y_intercept = start_point.y() - *normal_slope * start_point.x();
+  *end_y_intercept = end_point.y() - *normal_slope * end_point.x();
+}
+
+void ComputeProjectedPoints(const gfx::PointF& point,
+                            float line_slope,
+                            float line_y_intercept,
+                            float projection_distance,
+                            gfx::PointF* first_projection,
+                            gfx::PointF* second_projection) {
+  // If the slope is NaN, the y-intercept should be NaN too. The line is thus
+  // vertical and projections will be projected straight up/down from |point|.
+  if (isnan(line_slope)) {
+    DCHECK(isnan(line_y_intercept));
+
+    *first_projection =
+        gfx::PointF(point.x(), point.y() + round(projection_distance));
+    *second_projection =
+        gfx::PointF(point.x(), point.y() - round(projection_distance));
+    return;
+  }
+
+  // |point| must be on the line defined by |line_slope| and |line_y_intercept|.
+  DCHECK_LE(fabs(point.y() - (line_slope * point.x() + line_y_intercept)), 2.f);
+
+  // We want the two points along the line given by |slope|(m) and
+  // |y_intercept|(b). If |original_point| is defined as (x,y) and
+  // |distance_from_old_point| is d, we want the two (dx,dy) which satisfys the
+  // two equations (1)dx^2+dy^2=d^2 and (2)y+dy=m(x+dx)+b. Since y,x,b and m are
+  // constants we form a new equation (3)dy=mdx + K, where K=mx+b-y. Plugging
+  // (3) into (1) we get dx^2+(mdx)^2+2Kmdx+K^2=d^2 ->
+  // (m^2+1)dx^2+(2Km)dx+(K^2-d^2)=0. We can then solve for dx using the
+  // quadratic equation with variables a=m^2+1, b=2Km, c=K^2-d^2. We plug
+  // dx into (3) to find dy. The new points will then be (x+dx,y+dy).
+  float constant = line_y_intercept + line_slope * point.x() - point.y();
+  float a = 1.0f + line_slope * line_slope;
+  float b = 2.0f * line_slope * constant;
+  float c = constant * constant - projection_distance * projection_distance;
+  float p1_delta_x = QuadraticEquation(true, a, b, c);
+  float p1_delta_y =
+      line_slope * (point.x() + p1_delta_x) + line_y_intercept - point.y();
+  float p2_delta_x = QuadraticEquation(false, a, b, c);
+  float p2_delta_y =
+      line_slope * (point.x() + p2_delta_x) + line_y_intercept - point.y();
+  *first_projection =
+      gfx::PointF(point.x() + round(p1_delta_x), point.y() + round(p1_delta_y));
+  *second_projection =
+      gfx::PointF(point.x() + round(p2_delta_x), point.y() + round(p2_delta_y));
+}
+
+bool IsFirstPointSmallerAngle(const gfx::PointF& start_point,
+                              const gfx::PointF& end_point,
+                              const gfx::PointF& first_point,
+                              const gfx::PointF& second_point) {
+  gfx::PointF new_origin(
+      start_point.x() + (end_point.x() - start_point.x()) / 2.0f,
+      start_point.y() + (end_point.y() - start_point.y()) / 2.0f);
+  gfx::Vector2dF direction = end_point - start_point;
+
+  // Compute the angles of the projections relative to the the new origin and
+  // direction.
+  float end_first_projection_angle =
+      AngleOfPointInNewCoordinates(new_origin, direction, first_point);
+  float end_second_projection_angle =
+      AngleOfPointInNewCoordinates(new_origin, direction, second_point);
+
+  // We want to always have the smaller angle come first.
+  return end_first_projection_angle < end_second_projection_angle;
+}
+
+}  // namespace ash
diff --git a/ash/laser/laser_segment_utils.h b/ash/laser/laser_segment_utils.h
new file mode 100644
index 0000000..58bb40a
--- /dev/null
+++ b/ash/laser/laser_segment_utils.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_LASER_LASER_SEGMENT_UTILS_H_
+#define ASH_LASER_LASER_SEGMENT_UTILS_H_
+
+#include <vector>
+
+#include "ash/ash_export.h"
+
+namespace gfx {
+class PointF;
+class Vector2dF;
+}
+
+namespace ash {
+
+// Compute the angle in radians of |point|, with |origin| as the origin and
+// |direction| as the x-axis. In other words, computes the angle in radians
+// between |direction| and |point| - |origin|.
+float ASH_EXPORT AngleOfPointInNewCoordinates(const gfx::PointF& origin,
+                                              const gfx::Vector2dF& direction,
+                                              const gfx::PointF& point);
+
+// Compute the variables for the equation of the lines normal to the line
+// segment formed by |start_point| and |end_point|, and which run through the
+// endpoints of that line segment. The outputs will be returned as NaN if the
+// line segment is parallel to the x-axis (undefined normal lines).
+void ASH_EXPORT ComputeNormalLineVariables(const gfx::PointF& start_point,
+                                           const gfx::PointF& end_point,
+                                           float* normal_slope,
+                                           float* start_y_intercept,
+                                           float* end_y_intercept);
+
+// Compute the two the projections of |point| along the line defined by
+// |line_slope| and |line_y_intercept|. The distance of each projection is
+// |projection_distance| from |point|.
+void ASH_EXPORT ComputeProjectedPoints(const gfx::PointF& point,
+                                       float line_slope,
+                                       float line_y_intercept,
+                                       float projection_distance,
+                                       gfx::PointF* first_projection,
+                                       gfx::PointF* second_projection);
+
+// Checks if the angle of |first_point| is smaller than the angle of
+// |second_point|. These angles are computed relative to the coordinate system
+// defined by the midpoint of |start_point| and |end_point|, with the x-axis
+// as |end_point| - |start_point|.
+bool ASH_EXPORT IsFirstPointSmallerAngle(const gfx::PointF& start_point,
+                                         const gfx::PointF& end_point,
+                                         const gfx::PointF& first_point,
+                                         const gfx::PointF& second_point);
+
+}  // namespace ash
+
+#endif  // ASH_LASER_LASER_SEGMENT_UTILS_H_
diff --git a/ash/laser/laser_segment_utils_unittest.cc b/ash/laser/laser_segment_utils_unittest.cc
new file mode 100644
index 0000000..33f4adc5
--- /dev/null
+++ b/ash/laser/laser_segment_utils_unittest.cc
@@ -0,0 +1,292 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <math.h>
+
+#include "ash/laser/laser_segment_utils.h"
+#include "ash/test/ash_test_base.h"
+#include "base/memory/ptr_util.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace ash {
+namespace {
+// |kEpsilon| is used to check that AngleOfPointInNewCoordinates produces to
+// expected angles. This function is only used after points are projected in
+// opposite directions along the normal, so they should never be to close to
+// each other, checking for equality up to 5 significant figures is more than
+// enough.
+const float kEpsilon = 0.0001f;
+}
+
+// Helper function to check if a given |point| has the |expected_angle_degree|
+// in the coordinate system formed by |origin| and |direction|.
+void CheckAngleOfPointInNewCoordinates(const gfx::PointF& origin,
+                                       const gfx::Vector2dF& direction,
+                                       const gfx::PointF& point,
+                                       float expected_angle_degree) {
+  float result = AngleOfPointInNewCoordinates(origin, direction, point);
+  EXPECT_NEAR(expected_angle_degree * M_PI / 180.0f, result, kEpsilon);
+}
+
+// Helper function to check if the computed variables match the expected ones.
+void CheckNormalLineVariables(const gfx::PointF& start_point,
+                              const gfx::PointF& end_point,
+                              float expected_slope,
+                              float expected_start_intercept,
+                              float expected_end_intercept) {
+  float calculated_slope;
+  float calculated_start_y_intercept;
+  float calculated_end_y_intercept;
+
+  ComputeNormalLineVariables(start_point, end_point, &calculated_slope,
+                             &calculated_start_y_intercept,
+                             &calculated_end_y_intercept);
+  EXPECT_NEAR(expected_slope, calculated_slope, kEpsilon);
+  EXPECT_NEAR(expected_start_intercept, calculated_start_y_intercept, kEpsilon);
+  EXPECT_NEAR(expected_end_intercept, calculated_end_y_intercept, kEpsilon);
+}
+
+// Helper function to check if the a given line segment has an expected
+// undefined normal line.
+void CheckUndefinedNormalLine(const gfx::PointF& start_point,
+                              const gfx::PointF& end_point) {
+  float calculated_slope;
+  float calculated_start_y_intercept;
+  float calculated_end_y_intercept;
+
+  ComputeNormalLineVariables(start_point, end_point, &calculated_slope,
+                             &calculated_start_y_intercept,
+                             &calculated_end_y_intercept);
+  EXPECT_TRUE(isnan(calculated_slope));
+  EXPECT_TRUE(isnan(calculated_start_y_intercept));
+  EXPECT_TRUE(isnan(calculated_end_y_intercept));
+}
+
+// Helper function to check that the projections from the given line variables
+// and |distance| match those expected in |expected_projections|.
+void CheckProjectedPoints(const gfx::PointF& start_point,
+                          float slope,
+                          float y_intercept,
+                          float distance,
+                          std::vector<gfx::PointF>& expected_projections) {
+  // There can only be two projections.
+  EXPECT_EQ(2u, expected_projections.size());
+
+  gfx::PointF calculated_first_projection;
+  gfx::PointF calculated_second_projection;
+
+  ComputeProjectedPoints(start_point, slope, y_intercept, distance,
+                         &calculated_first_projection,
+                         &calculated_second_projection);
+
+  std::vector<gfx::PointF> calculated_projections = {
+      calculated_first_projection, calculated_second_projection};
+
+  // Sort the points, so that we do not have to enter the projections in the
+  // right order.
+  std::sort(calculated_projections.begin(), calculated_projections.end());
+  std::sort(expected_projections.begin(), expected_projections.end());
+
+  EXPECT_EQ(expected_projections[0], calculated_projections[0]);
+  EXPECT_EQ(expected_projections[1], calculated_projections[1]);
+}
+
+// Helper function that checks that an IsFirstPointSmallerAngle will return
+// false if |larger_angle_point| is the first point argument and return true if
+// |larger_angle_point| is the second point argument.
+void CheckFirstPointSmaller(const gfx::PointF& start_point,
+                            const gfx::PointF& end_point,
+                            const gfx::PointF& larger_angle_point,
+                            const gfx::PointF& smaller_angle_point) {
+  EXPECT_FALSE(IsFirstPointSmallerAngle(
+      start_point, end_point, larger_angle_point, smaller_angle_point));
+  EXPECT_TRUE(IsFirstPointSmallerAngle(
+      start_point, end_point, smaller_angle_point, larger_angle_point));
+}
+
+using LaserSegmentUtilsTest = testing::Test;
+
+TEST_F(LaserSegmentUtilsTest, AngleOfPointInNewCoordinates) {
+  {
+    // Verify angles remain the same if the origin is at (0, 0) and the
+    // direction remains the same (1, 0).
+    const gfx::PointF origin(0.0f, 0.0f);
+    const gfx::Vector2dF direction(1.0f, 0.0f);
+
+    // The functions range is (-180.0, 180.0).
+    for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) {
+      float rad = angle * M_PI / 180.0f;
+      gfx::PointF new_point(cos(rad), sin(rad));
+      CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle);
+    }
+  }
+  {
+    // Verify angles are shifted by 45 degrees if the origin is at (0, 0) and
+    // the direction is (1, 1).
+    const gfx::PointF origin(0.0f, 0.0f);
+    const gfx::Vector2dF direction(1.0f, 1.0f);
+
+    // The functions range is (-180.0, 180.0).
+    for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) {
+      float rad = (angle + 45.0f) * M_PI / 180.0f;
+      gfx::PointF new_point(cos(rad), sin(rad));
+      CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle);
+    }
+  }
+  {
+    // Verify angles remain the same if the points are translated by (1, 1),
+    // if the origin is at (1, 1) and the direction remains the same (1, 0).
+    const gfx::PointF origin(1.0f, 1.0f);
+    const gfx::Vector2dF direction(1.0f, 0.0f);
+
+    // The functions range is (-180.0f, 180.0f).
+    for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) {
+      float rad = angle * M_PI / 180.0f;
+      gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y());
+      CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle);
+    }
+  }
+  {
+    // Verify angles are shifted by 45 degress if the points are translated by
+    // (1, 1), if the origin is at (1, 1) and the direction remains the same
+    // (1, 0).
+    const gfx::PointF origin(1.0f, 1.0f);
+    const gfx::Vector2dF direction(1.0f, 1.0f);
+
+    // The functions range is (-180.0, 180.0).
+    for (float angle = -179.0f; angle < 180.0f; angle += 10.0f) {
+      float rad = (angle + 45.0f) * M_PI / 180.0f;
+      gfx::PointF new_point(cos(rad) + origin.x(), sin(rad) + origin.y());
+      CheckAngleOfPointInNewCoordinates(origin, direction, new_point, angle);
+    }
+  }
+}
+
+TEST_F(LaserSegmentUtilsTest, ComputeNormalLineVariables) {
+  {
+    // Verify a line y=x should have a normal line y=-x+b. At point (0,0), b
+    // should equal y+x = 0. At point (1,1), b shoudl equal y+x = 2.
+    const gfx::PointF start(0.0f, 0.0f);
+    const gfx::PointF end(1.0f, 1.0f);
+    float slope = -1.0f;
+    float start_intercept = 0.0f;
+    float end_intercept = 2.0f;
+    CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept);
+  }
+  {
+    // Verify a line y=-x should have a normal line y=x+b. At point 0.0f), b
+    // should equal y-x =.0f. At point (1,-1), b should equal y-x = -2.
+    const gfx::PointF start(0.0f, 0.0f);
+    const gfx::PointF end(1.0f, -1.0f);
+    float slope = 1.0f;
+    float start_intercept = 0.0f;
+    float end_intercept = -2.0f;
+    CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept);
+  }
+  {
+    // Verify a line x=5 should have a normal line y.0f with intercepts at the
+    // previous y point.
+    const gfx::PointF start(5.0f, 0.0f);
+    const gfx::PointF end(5.0f, 5.0f);
+    float slope = 0.0f;
+    float start_intercept = 0.0f;
+    float end_intercept = 5.0f;
+    CheckNormalLineVariables(start, end, slope, start_intercept, end_intercept);
+  }
+  {
+    // Verify a line parallel to the x-axis should be undefined. The line values
+    // should not matter.
+    const gfx::PointF start(0.0f, 5.0f);
+    const gfx::PointF end(5.0f, 5.0f);
+    CheckUndefinedNormalLine(start, end);
+  }
+}
+
+TEST_F(LaserSegmentUtilsTest, ComputeProjectedPoints) {
+  {
+    // Verify projecting along y=x from (0, 0) by distance sqrt(2) shoudl result
+    // in two projections: (1, 1) and (-1, -1). We start from (0, 0) and
+    // translate by (1, 1) and (-1, -1) (vectors with slope 1) to get to (1, 1)
+    // and (-1, -1). The length of the distance from (1, 1) is sqrt(1*1 + 1*1) =
+    // sqrt(2).
+    const gfx::PointF start(0.0f, 0.0f);
+    const float slope = 1.0f;
+    const float y_intercept = 0.0f;
+    const float distance = sqrt(2.0f);
+    std::vector<gfx::PointF> expected_projections = {gfx::PointF(1.0f, 1.0f),
+                                                     gfx::PointF(-1.0f, -1.0f)};
+    CheckProjectedPoints(start, slope, y_intercept, distance,
+                         expected_projections);
+  }
+  {
+    // Verify projecting along y=-2x+2 from (2, -2) by distance 2*sqrt(5) should
+    // result in two projections: (0, 2) and (4, -6). We start from (2, -2) and
+    // translate by (-2, 4) and (2, -4) (vectors with slope -2) to get (0, 2)
+    // and (4, -6). The length of the distance from (2, -2) is sqrt(2*2 + 4*4) =
+    // sqrt(20) = 2*sqrt(5).
+    const gfx::PointF start(2.0f, -2.0f);
+    const float slope = -2.0f;
+    const float y_intercept = 2.0f;
+    const float distance = 2.0f * sqrt(5.0f);
+    std::vector<gfx::PointF> expected_projections = {gfx::PointF(0.0f, 2.0f),
+                                                     gfx::PointF(4.0f, -6.0f)};
+    CheckProjectedPoints(start, slope, y_intercept, distance,
+                         expected_projections);
+  }
+  {
+    // Verify projecting along y=5 from (5,5) by distance 2 should
+    // result in two projections: (5,7) and (5,3).
+    const gfx::PointF start(5.0f, 5.0f);
+    const float slope = 0.0f;
+    const float y_intercept = 5.0f;
+    const float distance = 2.0f;
+    std::vector<gfx::PointF> expected_projections = {gfx::PointF(7.0f, 5.0f),
+                                                     gfx::PointF(3.0f, 5.0f)};
+    CheckProjectedPoints(start, slope, y_intercept, distance,
+                         expected_projections);
+  }
+}
+
+TEST_F(LaserSegmentUtilsTest, IsFirstPointSmallerAngle) {
+  {
+    // Verify this function works in the case direction is unchanged.
+    const gfx::PointF start_point(0.0f, 0.0f);
+    const gfx::PointF end_point(1.0f, 0.0f);
+    const gfx::PointF positive_angle(1.0f, 1.0f);
+    const gfx::PointF negative_angle(-1.0f, -1.0f);
+    CheckFirstPointSmaller(start_point, end_point, positive_angle,
+                           negative_angle);
+  }
+  {
+    // Verify this function works in the case direction is 90 degrees.
+    const gfx::PointF start_point(0.0f, 0.0f);
+    const gfx::PointF end_point(0.0f, 1.0f);
+    const gfx::PointF positive_angle(-1.0f, 0.0f);
+    const gfx::PointF negative_angle(1.0f, 0.0f);
+    CheckFirstPointSmaller(start_point, end_point, positive_angle,
+                           negative_angle);
+  }
+  {
+    // Verify this function works in the case direction is 45 degrees.
+    const gfx::PointF start_point(0.0f, 0.0f);
+    const gfx::PointF end_point(1.0f, 1.0f);
+    const gfx::PointF positive_angle(0.0f, 1.0f);
+    const gfx::PointF negative_angle(1.0f, 0.0f);
+    CheckFirstPointSmaller(start_point, end_point, positive_angle,
+                           negative_angle);
+  }
+  {
+    // Verify this function works in the case direction is -135 degrees.
+    const gfx::PointF start_point(0.0f, 0.0f);
+    const gfx::PointF end_point(-1.0f, -1.0f);
+    const gfx::PointF positive_angle(0.0f, -1.0f);
+    const gfx::PointF negative_angle(-1.0f, 0.0f);
+    CheckFirstPointSmaller(start_point, end_point, positive_angle,
+                           negative_angle);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/mus/frame/README.md b/ash/mus/frame/README.md
index a756bc1..4524c30 100644
--- a/ash/mus/frame/README.md
+++ b/ash/mus/frame/README.md
@@ -15,11 +15,11 @@
 does nothing special here.
 
 2. The client takes control of it all (as happens in chrome). To
-enable this the client sets kDisableImmersive_Property on the window. In this
-mode the client creates a separate window for the reveal (similar to
+enable this the client sets kDisableImmersive_InitProperty on the window. In 
+this mode the client creates a separate window for the reveal (similar to
 1). The reveal window is a child of the window going into immersive
 mode. Mash knows to paint window decorations to the reveal window by way of
-the property kRendererParentTitleArea_Property set on the parent
+the property kRenderParentTitleArea_Property set on the parent
 window. The client runs all the immersive logic, including positioning
 of the reveal window.
 
diff --git a/ash/mus/frame/detached_title_area_renderer.h b/ash/mus/frame/detached_title_area_renderer.h
index 9c6e2128..b222df53 100644
--- a/ash/mus/frame/detached_title_area_renderer.h
+++ b/ash/mus/frame/detached_title_area_renderer.h
@@ -21,7 +21,7 @@
   // Used to indicate why this is being created.
   enum class Source {
     // This is being created at the request of a client, specifically because
-    // of kRendererParentTitleArea_Property set on a client owned window.
+    // of kRenderParentTitleArea_Property set on a client owned window.
     CLIENT,
 
     // Mash is creating this class to host an immersive reveal. Note that CLIENT
diff --git a/ash/mus/manifest.json b/ash/mus/manifest.json
index ab525538..b41f7ada 100644
--- a/ash/mus/manifest.json
+++ b/ash/mus/manifest.json
@@ -4,9 +4,13 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
+        // Modifications here should correspond with changes to
+        // chrome_content_browser_manifest_overlay.json.
         "ash": [
+          "ash::mojom::AcceleratorController",
           "ash::mojom::CastConfig",
           "ash::mojom::LocaleNotificationController",
+          "ash::mojom::NewWindowController",
           "ash::mojom::ShelfController",
           "ash::mojom::ShutdownController",
           "ash::mojom::SystemTray",
@@ -20,7 +24,7 @@
         "*": [ "accessibility", "app" ],
         "content_browser": [ "ash" ],
         "mash_session": [ "mash_session:session" ],
-        "ui": [ "ui:window_manager" ],
+        "ui": [ "window_manager" ],
         "touch_hud": [ "mash:launchable" ]
       }
     }
diff --git a/ash/mus/non_client_frame_controller.h b/ash/mus/non_client_frame_controller.h
index 1524b1d..dcacc1dc 100644
--- a/ash/mus/non_client_frame_controller.h
+++ b/ash/mus/non_client_frame_controller.h
@@ -108,7 +108,7 @@
   aura::Window* window_;
 
   // Used if a child window is added that has the
-  // kRendererParentTitleArea_Property set.
+  // kRenderParentTitleArea_Property set.
   DetachedTitleAreaRenderer* detached_title_area_renderer_ = nullptr;
 
   bool did_init_native_widget_ = false;
diff --git a/ash/mus/property_util.cc b/ash/mus/property_util.cc
index 4514d58..44e7121 100644
--- a/ash/mus/property_util.cc
+++ b/ash/mus/property_util.cc
@@ -15,7 +15,7 @@
 
 int64_t GetInitialDisplayId(const InitProperties& properties) {
   auto iter =
-      properties.find(ui::mojom::WindowManager::kInitialDisplayId_Property);
+      properties.find(ui::mojom::WindowManager::kDisplayId_InitProperty);
   return iter == properties.end() ? display::kInvalidDisplayId
                                   : mojo::ConvertTo<int64_t>(iter->second);
 }
@@ -23,7 +23,7 @@
 bool GetInitialContainerId(const InitProperties& properties,
                            int* container_id) {
   auto iter =
-      properties.find(ui::mojom::WindowManager::kInitialContainerId_Property);
+      properties.find(ui::mojom::WindowManager::kContainerId_InitProperty);
   if (iter == properties.end())
     return false;
 
@@ -32,8 +32,7 @@
 }
 
 bool GetInitialBounds(const InitProperties& properties, gfx::Rect* bounds) {
-  auto iter =
-      properties.find(ui::mojom::WindowManager::kInitialBounds_Property);
+  auto iter = properties.find(ui::mojom::WindowManager::kBounds_InitProperty);
   if (iter == properties.end())
     return false;
 
@@ -52,14 +51,14 @@
 }
 
 bool ShouldRemoveStandardFrame(const InitProperties& properties) {
-  auto iter =
-      properties.find(ui::mojom::WindowManager::kRemoveStandardFrame_Property);
+  auto iter = properties.find(
+      ui::mojom::WindowManager::kRemoveStandardFrame_InitProperty);
   return iter != properties.end() && mojo::ConvertTo<bool>(iter->second);
 }
 
 bool ShouldEnableImmersive(const InitProperties& properties) {
   auto iter =
-      properties.find(ui::mojom::WindowManager::kDisableImmersive_Property);
+      properties.find(ui::mojom::WindowManager::kDisableImmersive_InitProperty);
   return iter == properties.end() || !mojo::ConvertTo<bool>(iter->second);
 }
 
diff --git a/ash/mus/property_util.h b/ash/mus/property_util.h
index 810fc91dd..beb6886 100644
--- a/ash/mus/property_util.h
+++ b/ash/mus/property_util.h
@@ -31,12 +31,12 @@
 
 using InitProperties = std::map<std::string, std::vector<uint8_t>>;
 
-// Returns the kInitialDisplayId_Property if present, otherwise
+// Returns the kDisplayId_InitProperty if present, otherwise
 // kInvalidDisplayID.
 int64_t GetInitialDisplayId(const InitProperties& properties);
 
-// If |window| has the |kInitialContainerId_Property| set as a property, then
-// the value of |kInitialContainerId_Property| is set in |container_id| and true
+// If |window| has the |kContainerId_InitProperty| set as a property, then
+// the value of |kContainerId_InitProperty| is set in |container_id| and true
 // is returned. Otherwise false is returned.
 bool GetInitialContainerId(const InitProperties& properties, int* container_id);
 
diff --git a/ash/mus/test/ash_test_impl_mus.cc b/ash/mus/test/ash_test_impl_mus.cc
index 7849c3d..ef89b5a2 100644
--- a/ash/mus/test/ash_test_impl_mus.cc
+++ b/ash/mus/test/ash_test_impl_mus.cc
@@ -91,7 +91,7 @@
     views::Widget::InitParams* init_params) {
   init_params->context = WmWindowMus::GetAuraWindow(window);
   init_params
-      ->mus_properties[ui::mojom::WindowManager::kInitialDisplayId_Property] =
+      ->mus_properties[ui::mojom::WindowManager::kDisplayId_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           window->GetDisplayNearestWindow().id());
 }
diff --git a/ash/mus/test/wm_test_base.cc b/ash/mus/test/wm_test_base.cc
index 9d9804e..e3a05cb 100644
--- a/ash/mus/test/wm_test_base.cc
+++ b/ash/mus/test/wm_test_base.cc
@@ -105,7 +105,7 @@
                                            ui::wm::WindowType window_type) {
   std::map<std::string, std::vector<uint8_t>> properties;
   if (!bounds.IsEmpty()) {
-    properties[ui::mojom::WindowManager::kInitialBounds_Property] =
+    properties[ui::mojom::WindowManager::kBounds_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(bounds);
   }
 
@@ -132,7 +132,7 @@
           static_cast<aura::PropertyConverter::PrimitiveType>(
               ui::mojom::ShowState::FULLSCREEN));
   if (display_id != display::kInvalidDisplayId) {
-    properties[ui::mojom::WindowManager::kInitialDisplayId_Property] =
+    properties[ui::mojom::WindowManager::kDisplayId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(display_id);
   }
   aura::Window* window =
diff --git a/ash/mus/test/wm_test_helper.cc b/ash/mus/test/wm_test_helper.cc
index 338a20a0..be6a08f 100644
--- a/ash/mus/test/wm_test_helper.cc
+++ b/ash/mus/test/wm_test_helper.cc
@@ -6,7 +6,6 @@
 
 #include "ash/common/material_design/material_design_controller.h"
 #include "ash/common/test/material_design_controller_test_api.h"
-#include "ash/common/test/test_new_window_client.h"
 #include "ash/common/test/test_system_tray_delegate.h"
 #include "ash/common/test/wm_shell_test_api.h"
 #include "ash/common/wm_shell.h"
@@ -116,7 +115,6 @@
   // create the various test delegates.
   WmShellTestApi().SetSystemTrayDelegate(
       base::MakeUnique<test::TestSystemTrayDelegate>());
-  WmShellTestApi().SetNewWindowClient(base::MakeUnique<TestNewWindowClient>());
 
   aura::WindowTreeClient* window_tree_client =
       window_manager_app_->window_manager()->window_tree_client();
diff --git a/ash/mus/window_manager.cc b/ash/mus/window_manager.cc
index d65aad9..53f0f67 100644
--- a/ash/mus/window_manager.cc
+++ b/ash/mus/window_manager.cc
@@ -85,6 +85,9 @@
       screen_position_controller_(
           base::MakeUnique<ScreenPositionController>()) {
   property_converter_->RegisterProperty(
+      kRenderTitleAreaProperty,
+      ui::mojom::WindowManager::kRenderParentTitleArea_Property);
+  property_converter_->RegisterProperty(
       kShelfItemTypeKey, ui::mojom::WindowManager::kShelfItemType_Property);
 }
 
@@ -400,6 +403,11 @@
 aura::Window* WindowManager::OnWmCreateTopLevelWindow(
     ui::mojom::WindowType window_type,
     std::map<std::string, std::vector<uint8_t>>* properties) {
+  if (window_type == ui::mojom::WindowType::UNKNOWN) {
+    LOG(WARNING) << "Request to create top level of unknown type, failing";
+    return nullptr;
+  }
+
   return NewTopLevelWindow(window_type, properties);
 }
 
diff --git a/ash/mus/window_manager_unittest.cc b/ash/mus/window_manager_unittest.cc
index b2c689df..51dac6b 100644
--- a/ash/mus/window_manager_unittest.cc
+++ b/ash/mus/window_manager_unittest.cc
@@ -2,31 +2,62 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <map>
 #include <memory>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/macros.h"
+#include "base/run_loop.h"
 #include "services/service_manager/public/cpp/service_test.h"
-#include "services/ui/public/cpp/window.h"
-#include "services/ui/public/cpp/window_tree_client.h"
-#include "services/ui/public/cpp/window_tree_client_delegate.h"
+#include "services/ui/public/cpp/property_type_converters.h"
 #include "services/ui/public/interfaces/window_tree.mojom.h"
+#include "ui/aura/env.h"
+#include "ui/aura/mus/property_converter.h"
+#include "ui/aura/mus/window_tree_client.h"
+#include "ui/aura/mus/window_tree_client_delegate.h"
+#include "ui/aura/mus/window_tree_host_mus.h"
+#include "ui/aura/window.h"
+#include "ui/display/display.h"
+#include "ui/display/display_list.h"
+#include "ui/display/screen_base.h"
+#include "ui/wm/core/capture_controller.h"
+#include "ui/wm/core/wm_state.h"
 
 namespace ash {
 namespace mus {
 
-class WindowTreeClientDelegate : public ui::WindowTreeClientDelegate {
+class WindowTreeClientDelegate : public aura::WindowTreeClientDelegate {
  public:
   WindowTreeClientDelegate() {}
   ~WindowTreeClientDelegate() override {}
 
+  void WaitForEmbed() { run_loop_.Run(); }
+
+  void DestroyWindowTreeHost() { window_tree_host_.reset(); }
+
  private:
-  // ui::WindowTreeClientDelegate:
-  void OnEmbed(ui::Window* root) override {}
-  void OnEmbedRootDestroyed(ui::Window* root) override {}
-  void OnLostConnection(ui::WindowTreeClient* client) override {}
+  // aura::WindowTreeClientDelegate:
+  void OnEmbed(
+      std::unique_ptr<aura::WindowTreeHostMus> window_tree_host) override {
+    window_tree_host_ = std::move(window_tree_host);
+    run_loop_.Quit();
+  }
+  void OnEmbedRootDestroyed(aura::Window* root) override {}
+  void OnLostConnection(aura::WindowTreeClient* client) override {}
   void OnPointerEventObserved(const ui::PointerEvent& event,
-                              ui::Window* target) override {}
+                              aura::Window* target) override {}
+  aura::client::CaptureClient* GetCaptureClient() override {
+    return wm_state_.capture_controller();
+  }
+  aura::PropertyConverter* GetPropertyConverter() override {
+    return &property_converter_;
+  }
+
+  base::RunLoop run_loop_;
+  wm::WMState wm_state_;
+  aura::PropertyConverter property_converter_;
+  std::unique_ptr<aura::WindowTreeHostMus> window_tree_host_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowTreeClientDelegate);
 };
@@ -48,32 +79,41 @@
 // (ash::MaterialDesignController::IsShelfMaterial()). See
 // crbug.com/660194 and crbug.com/642879.
 // TODO(rockot): Reenable this test.
-// TODO(sky): convert to using aura.
 TEST_F(WindowManagerTest, DISABLED_OpenWindow) {
+  display::ScreenBase screen;
+  screen.display_list().AddDisplay(
+      display::Display(1, gfx::Rect(0, 0, 200, 200)),
+      display::DisplayList::Type::PRIMARY);
+  display::Screen::SetScreenInstance(&screen);
+
   WindowTreeClientDelegate window_tree_delegate;
 
   connector()->Connect("ash");
 
   // Connect to mus and create a new top level window. The request goes to
   // |ash|, but is async.
-  std::unique_ptr<ui::WindowTreeClient> client(
-      new ui::WindowTreeClient(&window_tree_delegate));
-  client->ConnectViaWindowTreeFactory(connector());
-  ui::Window* top_level_window = client->NewTopLevelWindow(nullptr);
-  ASSERT_TRUE(top_level_window);
-  ui::Window* child_window = client->NewWindow();
-  ASSERT_TRUE(child_window);
-  top_level_window->AddChild(child_window);
+  aura::WindowTreeClient client(connector(), &window_tree_delegate);
+  client.ConnectViaWindowTreeFactory();
+  aura::Env::GetInstance()->SetWindowTreeClient(&client);
+  std::map<std::string, std::vector<uint8_t>> properties;
+  properties[ui::mojom::WindowManager::kWindowType_InitProperty] =
+      mojo::ConvertTo<std::vector<uint8_t>>(
+          static_cast<int32_t>(ui::mojom::WindowType::WINDOW));
+  aura::WindowTreeHostMus window_tree_host_mus(&client, &properties);
+  aura::Window* child_window = new aura::Window(nullptr);
+  child_window->Init(ui::LAYER_NOT_DRAWN);
+  window_tree_host_mus.window()->AddChild(child_window);
 
   // Create another WindowTreeClient by way of embedding in
   // |child_window|. This blocks until it succeeds.
   ui::mojom::WindowTreeClientPtr tree_client;
   auto tree_client_request = GetProxy(&tree_client);
-  child_window->Embed(std::move(tree_client), base::Bind(&OnEmbed));
-  std::unique_ptr<ui::WindowTreeClient> child_client(new ui::WindowTreeClient(
-      &window_tree_delegate, nullptr, std::move(tree_client_request)));
-  child_client->WaitForEmbed();
-  ASSERT_TRUE(!child_client->GetRoots().empty());
+  client.Embed(child_window, std::move(tree_client), 0u, base::Bind(&OnEmbed));
+  aura::WindowTreeClient child_client(connector(), &window_tree_delegate,
+                                      nullptr, std::move(tree_client_request));
+  window_tree_delegate.WaitForEmbed();
+  ASSERT_TRUE(!child_client.GetRoots().empty());
+  window_tree_delegate.DestroyWindowTreeHost();
 }
 
 }  // namespace mus
diff --git a/ash/mus/window_properties.h b/ash/mus/window_properties.h
index b9126e3..e0916f4 100644
--- a/ash/mus/window_properties.h
+++ b/ash/mus/window_properties.h
@@ -10,7 +10,7 @@
 namespace ash {
 namespace mus {
 
-// Maps to ui::mojom::WindowManager::kRendererParentTitleArea_Property.
+// Maps to ui::mojom::WindowManager::kRenderParentTitleArea_Property.
 extern const aura::WindowProperty<bool>* const kRenderTitleAreaProperty;
 
 // Set to true if the window server tells us the window is janky (see
diff --git a/ash/public/cpp/shell_window_ids.cc b/ash/public/cpp/shell_window_ids.cc
index fcb5a78..5f4edcb4 100644
--- a/ash/public/cpp/shell_window_ids.cc
+++ b/ash/public/cpp/shell_window_ids.cc
@@ -11,7 +11,7 @@
 // NOTE: this list is ordered by activation order. That is, windows in
 // containers appearing earlier in the list are activated before windows in
 // containers appearing later in the list.
-const int kActivatableShellWindowIds[] = {
+const int32_t kActivatableShellWindowIds[] = {
     kShellWindowId_OverlayContainer, kShellWindowId_LockSystemModalContainer,
     kShellWindowId_SettingBubbleContainer, kShellWindowId_LockScreenContainer,
     kShellWindowId_SystemModalContainer, kShellWindowId_AlwaysOnTopContainer,
@@ -27,7 +27,7 @@
 const size_t kNumActivatableShellWindowIds =
     arraysize(kActivatableShellWindowIds);
 
-bool IsActivatableShellWindowId(int id) {
+bool IsActivatableShellWindowId(int32_t id) {
   for (size_t i = 0; i < kNumActivatableShellWindowIds; i++) {
     if (id == kActivatableShellWindowIds[i])
       return true;
diff --git a/ash/public/cpp/shell_window_ids.h b/ash/public/cpp/shell_window_ids.h
index dbba3f3..6e1b91d5d 100644
--- a/ash/public/cpp/shell_window_ids.h
+++ b/ash/public/cpp/shell_window_ids.h
@@ -6,115 +6,116 @@
 #define ASH_PUBLIC_CPP_SHELL_WINDOW_IDS_H_
 
 #include <stddef.h>
+#include <stdint.h>
 
 // Declarations of ids of special shell windows.
 
 namespace ash {
 
 // Used to indicate no shell window id.
-const int kShellWindowId_Invalid = -1;
+const int32_t kShellWindowId_Invalid = -1;
 
 // A higher-level container that holds all of the containers stacked below
 // kShellWindowId_LockScreenContainer.  Only used by PowerButtonController for
 // animating lower-level containers.
-const int kShellWindowId_NonLockScreenContainersContainer = 0;
+const int32_t kShellWindowId_NonLockScreenContainersContainer = 0;
 
 // A higher-level container that holds containers that hold lock-screen
 // windows.  Only used by PowerButtonController for animating lower-level
 // containers.
-const int kShellWindowId_LockScreenContainersContainer = 1;
+const int32_t kShellWindowId_LockScreenContainersContainer = 1;
 
 // A higher-level container that holds containers that hold lock-screen-related
 // windows (which we want to display while the screen is locked; effectively
 // containers stacked above kShellWindowId_LockSystemModalContainer).  Only used
 // by PowerButtonController for animating lower-level containers.
-const int kShellWindowId_LockScreenRelatedContainersContainer = 2;
+const int32_t kShellWindowId_LockScreenRelatedContainersContainer = 2;
 
 // A container used for windows of WINDOW_TYPE_CONTROL that have no parent.
 // This container is not visible.
-const int kShellWindowId_UnparentedControlContainer = 3;
+const int32_t kShellWindowId_UnparentedControlContainer = 3;
 
 // The wallpaper (desktop background) window.
-const int kShellWindowId_WallpaperContainer = 4;
+const int32_t kShellWindowId_WallpaperContainer = 4;
 
 // The virtual keyboard container.
 // NOTE: this is lazily created.
-const int kShellWindowId_VirtualKeyboardContainer = 5;
+const int32_t kShellWindowId_VirtualKeyboardContainer = 5;
 
 // The container for standard top-level windows.
-const int kShellWindowId_DefaultContainer = 6;
+const int32_t kShellWindowId_DefaultContainer = 6;
 
 // The container for top-level windows with the 'always-on-top' flag set.
-const int kShellWindowId_AlwaysOnTopContainer = 7;
+const int32_t kShellWindowId_AlwaysOnTopContainer = 7;
 
 // The container for windows docked to either side of the desktop.
-const int kShellWindowId_DockedContainer = 8;
+const int32_t kShellWindowId_DockedContainer = 8;
 
 // The container for the shelf.
-const int kShellWindowId_ShelfContainer = 9;
+const int32_t kShellWindowId_ShelfContainer = 9;
 
 // The container for bubbles which float over the shelf.
-const int kShellWindowId_ShelfBubbleContainer = 10;
+const int32_t kShellWindowId_ShelfBubbleContainer = 10;
 
 // The container for panel windows.
-const int kShellWindowId_PanelContainer = 11;
+const int32_t kShellWindowId_PanelContainer = 11;
 
 // The container for the app list.
-const int kShellWindowId_AppListContainer = 12;
+const int32_t kShellWindowId_AppListContainer = 12;
 
 // The container for user-specific modal windows.
-const int kShellWindowId_SystemModalContainer = 13;
+const int32_t kShellWindowId_SystemModalContainer = 13;
 
 // The container for the lock screen wallpaper (lock screen background).
-const int kShellWindowId_LockScreenWallpaperContainer = 14;
+const int32_t kShellWindowId_LockScreenWallpaperContainer = 14;
 
 // The container for the lock screen.
-const int kShellWindowId_LockScreenContainer = 15;
+const int32_t kShellWindowId_LockScreenContainer = 15;
 
 // The container for the lock screen modal windows.
-const int kShellWindowId_LockSystemModalContainer = 16;
+const int32_t kShellWindowId_LockSystemModalContainer = 16;
 
 // The container for the status area.
-const int kShellWindowId_StatusContainer = 17;
+const int32_t kShellWindowId_StatusContainer = 17;
 
 // A parent container that holds the virtual keyboard container and ime windows
 // if any. This is to ensure that the virtual keyboard or ime window is stacked
 // above most containers but below the mouse cursor and the power off animation.
-const int kShellWindowId_ImeWindowParentContainer = 18;
+const int32_t kShellWindowId_ImeWindowParentContainer = 18;
 
 // The container for menus.
-const int kShellWindowId_MenuContainer = 19;
+const int32_t kShellWindowId_MenuContainer = 19;
 
 // The container for drag/drop images and tooltips.
-const int kShellWindowId_DragImageAndTooltipContainer = 20;
+const int32_t kShellWindowId_DragImageAndTooltipContainer = 20;
 
 // The container for bubbles briefly overlaid onscreen to show settings changes
 // (volume, brightness, input method bubbles, etc.).
-const int kShellWindowId_SettingBubbleContainer = 21;
+const int32_t kShellWindowId_SettingBubbleContainer = 21;
 
 // The container for special components overlaid onscreen, such as the
 // region selector for partial screenshots.
-const int kShellWindowId_OverlayContainer = 22;
+const int32_t kShellWindowId_OverlayContainer = 22;
 
 // ID of the window created by PhantomWindowController or DragWindowController.
-const int kShellWindowId_PhantomWindow = 23;
+const int32_t kShellWindowId_PhantomWindow = 23;
 
 // The container for mouse cursor.
-const int kShellWindowId_MouseCursorContainer = 24;
+const int32_t kShellWindowId_MouseCursorContainer = 24;
 
 // The topmost container, used for power off animation.
-const int kShellWindowId_PowerButtonAnimationContainer = 25;
+const int32_t kShellWindowId_PowerButtonAnimationContainer = 25;
 
-const int kShellWindowId_Min = 0;
-const int kShellWindowId_Max = kShellWindowId_PowerButtonAnimationContainer;
+const int32_t kShellWindowId_Min = 0;
+const int32_t kShellWindowId_Max = kShellWindowId_PowerButtonAnimationContainer;
 
 // These are the list of container ids of containers which may contain windows
 // that need to be activated.
-extern const int kActivatableShellWindowIds[];
+extern const int32_t kActivatableShellWindowIds[];
 extern const size_t kNumActivatableShellWindowIds;
 
 // Returns true if |id| is in |kActivatableShellWindowIds|.
-bool IsActivatableShellWindowId(int id);
+bool IsActivatableShellWindowId(int32_t id);
 
 }  // namespace ash
 
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index b7e06121..876c651 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("interfaces") {
   sources = [
+    "accelerator_controller.mojom",
     "ash_window_type.mojom",
     "cast_config.mojom",
     "locale.mojom",
diff --git a/ash/public/interfaces/accelerator_controller.mojom b/ash/public/interfaces/accelerator_controller.mojom
new file mode 100644
index 0000000..0fe3475a
--- /dev/null
+++ b/ash/public/interfaces/accelerator_controller.mojom
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.mojom;
+
+import "ash/public/interfaces/volume.mojom";
+
+// Implemented by ash to delegate parts of keyboard accelerator handling back
+// to chrome.
+interface AcceleratorController {
+  // Sets the volume controller interface. This lives in chrome because chrome
+  // owns the D-Bus CrasAudioHandler implementation. Also the accessibility
+  // manager in chrome needs to know about volume changes.
+  SetVolumeController(VolumeController controller);
+};
diff --git a/ash/public/interfaces/new_window.mojom b/ash/public/interfaces/new_window.mojom
index 08a49f0..7009400 100644
--- a/ash/public/interfaces/new_window.mojom
+++ b/ash/public/interfaces/new_window.mojom
@@ -4,8 +4,13 @@
 
 module ash.mojom;
 
-// A delegate interface delegate to create or open windows that are not part of
-// ash.
+// An exported object in ash which lets an ash consumer set a client interface.
+interface NewWindowController {
+  SetClient(associated NewWindowClient client);
+};
+
+// A delegate interface that an ash user sends to ash to handle certain window
+// management responsibilities.
 interface NewWindowClient {
   // Invoked when the user uses Ctrl+T to open a new tab.
   NewTab();
diff --git a/ash/public/interfaces/volume.mojom b/ash/public/interfaces/volume.mojom
index 85bd939..3e5b1da7 100644
--- a/ash/public/interfaces/volume.mojom
+++ b/ash/public/interfaces/volume.mojom
@@ -6,7 +6,10 @@
 
 // Implemented by Chrome and used by ash to request audio volume changes.
 // Implemented by Chrome because it's currently the only client of audio dbus
-// interfaces (via CrasAudioHandler); this may be simplified if that changes.
+// interfaces (via CrasAudioHandler).
+// TODO(crbug.com/644336): Flip this pattern, ash should implement the volume
+// control interface or both ash and chrome should directly access the
+// CrasAudioHandler volume control functions.
 interface VolumeController {
   // Mute the audio volume.
   VolumeMute();
diff --git a/ash/public/interfaces/wallpaper.mojom b/ash/public/interfaces/wallpaper.mojom
index e03b28c..7215c31 100644
--- a/ash/public/interfaces/wallpaper.mojom
+++ b/ash/public/interfaces/wallpaper.mojom
@@ -11,6 +11,9 @@
 
 // Used by Chrome to set the wallpaper displayed by ash.
 interface WallpaperController {
+  // Set the wallpaper picker interface, to let ash trigger Chrome's picker.
+  SetWallpaperPicker(WallpaperPicker picker);
+
   // Set the wallpaper bitmap and layout used for the ash desktop background.
   // A null or empty |wallpaper| bitmap is treated as a no-op.
   // TODO(crbug.com/655875): Optimize ash wallpaper transport; avoid sending
@@ -19,7 +22,7 @@
 };
 
 // Used by ash to trigger Chrome's wallpaper picker functionality.
-interface WallpaperManager {
+interface WallpaperPicker {
   // Open the wallpaper picker window.
   Open();
 };
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index cf4e54c..43fd9ee 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -723,14 +723,14 @@
  public:
   MockTextInputClient() : ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {}
 
-  void EnsureCaretInRect(const gfx::Rect& rect) override {
-    visible_rect_ = rect;
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override {
+    caret_exclude_rect_ = rect;
   }
 
-  const gfx::Rect& visible_rect() const { return visible_rect_; }
+  const gfx::Rect& caret_exclude_rect() const { return caret_exclude_rect_; }
 
  private:
-  gfx::Rect visible_rect_;
+  gfx::Rect caret_exclude_rect_;
 
   DISALLOW_COPY_AND_ASSIGN(MockTextInputClient);
 };
@@ -1083,13 +1083,71 @@
 
   ui->EnsureCaretInWorkArea();
   ASSERT_EQ(root_window->bounds().width(),
-            text_input_client.visible_rect().width());
-  ASSERT_EQ(root_window->bounds().height() - keyboard_height,
-            text_input_client.visible_rect().height());
+            text_input_client.caret_exclude_rect().width());
+  ASSERT_EQ(keyboard_height, text_input_client.caret_exclude_rect().height());
 
   input_method->SetFocusedTextInputClient(NULL);
 }
 
+TEST_F(VirtualKeyboardRootWindowControllerTest,
+       EnsureCaretInWorkAreaWithMultipleDisplays) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  UpdateDisplay("500x500,600x600");
+  const int64_t primary_display_id =
+      display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  const int64_t secondary_display_id =
+      Shell::GetInstance()->display_manager()->GetSecondaryDisplay().id();
+  ASSERT_NE(primary_display_id, secondary_display_id);
+
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+  ASSERT_EQ(static_cast<size_t>(2), root_windows.size());
+  aura::Window* primary_root_window = root_windows[0];
+  aura::Window* secondary_root_window = root_windows[1];
+
+  keyboard::KeyboardController* keyboard_controller =
+      keyboard::KeyboardController::GetInstance();
+  keyboard::KeyboardUI* ui = keyboard_controller->ui();
+
+  MockTextInputClient text_input_client;
+  ui::InputMethod* input_method = ui->GetInputMethod();
+  ASSERT_TRUE(input_method);
+  input_method->SetFocusedTextInputClient(&text_input_client);
+
+  const int keyboard_height = 100;
+  // Check that the keyboard on the primary screen doesn't cover the window on
+  // the secondary screen.
+  aura::Window* keyboard_container = Shell::GetContainer(
+      primary_root_window, kShellWindowId_VirtualKeyboardContainer);
+  ASSERT_TRUE(keyboard_container);
+  keyboard_container->Show();
+  aura::Window* keyboard_window = ui->GetKeyboardWindow();
+  keyboard_container->AddChild(keyboard_window);
+  keyboard_window->set_owned_by_parent(false);
+  keyboard_window->SetBounds(keyboard::FullWidthKeyboardBoundsFromRootBounds(
+      primary_root_window->bounds(), keyboard_height));
+
+  EXPECT_TRUE(primary_root_window->GetBoundsInScreen().Contains(
+      text_input_client.caret_exclude_rect()));
+  EXPECT_FALSE(secondary_root_window->GetBoundsInScreen().Contains(
+      text_input_client.caret_exclude_rect()));
+
+  // Move the keyboard into the secondary display and check that the keyboard
+  // doesn't cover the window on the primary screen.
+  keyboard_controller->ShowKeyboardInDisplay(secondary_display_id);
+  keyboard_window->SetBounds(keyboard::FullWidthKeyboardBoundsFromRootBounds(
+      secondary_root_window->bounds(), keyboard_height));
+
+  ui->EnsureCaretInWorkArea();
+  EXPECT_FALSE(primary_root_window->GetBoundsInScreen().Contains(
+      text_input_client.caret_exclude_rect()));
+  EXPECT_TRUE(secondary_root_window->GetBoundsInScreen().Contains(
+      text_input_client.caret_exclude_rect()));
+
+  input_method->SetFocusedTextInputClient(nullptr);
+}
+
 // Tests that the virtual keyboard does not block context menus. The virtual
 // keyboard should appear in front of most content, but not context menus. See
 // crbug/377180.
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index e92ea0d..5764c4a 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -7,7 +7,6 @@
 #include "ash/accelerators/accelerator_controller_delegate_aura.h"
 #include "ash/common/material_design/material_design_controller.h"
 #include "ash/common/test/material_design_controller_test_api.h"
-#include "ash/common/test/test_new_window_client.h"
 #include "ash/common/test/test_session_state_delegate.h"
 #include "ash/common/test/test_system_tray_delegate.h"
 #include "ash/common/test/wm_shell_test_api.h"
@@ -156,8 +155,6 @@
   test_screenshot_delegate_ = new TestScreenshotDelegate();
   shell->accelerator_controller_delegate()->SetScreenshotDelegate(
       std::unique_ptr<ScreenshotDelegate>(test_screenshot_delegate_));
-
-  WmShellTestApi().SetNewWindowClient(base::MakeUnique<TestNewWindowClient>());
 }
 
 void AshTestHelper::TearDown() {
diff --git a/ash/touch_hud/mus/touch_hud_application.cc b/ash/touch_hud/mus/touch_hud_application.cc
index c8af21c..e508065 100644
--- a/ash/touch_hud/mus/touch_hud_application.cc
+++ b/ash/touch_hud/mus/touch_hud_application.cc
@@ -93,7 +93,7 @@
     params.delegate = new TouchHudUI(window_manager_connection_.get(), widget_);
 
     std::map<std::string, std::vector<uint8_t>> properties;
-    properties[ui::mojom::WindowManager::kInitialContainerId_Property] =
+    properties[ui::mojom::WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(
             ash::kShellWindowId_OverlayContainer);
     properties[ui::mojom::WindowManager::kShowState_Property] =
diff --git a/ash/wm/lock_state_controller.h b/ash/wm/lock_state_controller.h
index 9fc959c..00b49e81 100644
--- a/ash/wm/lock_state_controller.h
+++ b/ash/wm/lock_state_controller.h
@@ -52,13 +52,6 @@
 class ASH_EXPORT LockStateController : public aura::WindowTreeHostObserver,
                                        public ShellObserver {
  public:
-  // Amount of time that the power button needs to be held before we lock the
-  // screen.
-  static const int kLockTimeoutMs;
-
-  // Amount of time that the power button needs to be held before we shut down.
-  static const int kShutdownTimeoutMs;
-
   // Amount of time to wait for our lock requests to be honored before giving
   // up.
   static const int kLockFailTimeoutMs;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 91667d16..6ffa428 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -40,7 +40,7 @@
   # Turn on memory profiling in the task profiler when the heap shim is
   # available, except for official builds for now.
   enable_memory_task_profiler =
-      use_experimental_allocator_shim && !is_official_build
+      use_experimental_allocator_shim && (!is_official_build || is_syzyasan)
 }
 
 if (is_android) {
diff --git a/base/allocator/allocator_shim.cc b/base/allocator/allocator_shim.cc
index 95480ea..a9fc095b 100644
--- a/base/allocator/allocator_shim.cc
+++ b/base/allocator/allocator_shim.cc
@@ -39,18 +39,6 @@
 subtle::Atomic32 g_new_handler_lock = 0;
 #endif
 
-// In theory this should be just base::ThreadChecker. But we can't afford
-// the luxury of a LazyInstance<ThreadChecker> here as it would cause a new().
-bool CalledOnValidThread() {
-  using subtle::Atomic32;
-  const Atomic32 kInvalidTID = static_cast<Atomic32>(kInvalidThreadId);
-  static Atomic32 g_tid = kInvalidTID;
-  Atomic32 cur_tid = static_cast<Atomic32>(PlatformThread::CurrentId());
-  Atomic32 prev_tid =
-      subtle::NoBarrier_CompareAndSwap(&g_tid, kInvalidTID, cur_tid);
-  return prev_tid == kInvalidTID || prev_tid == cur_tid;
-}
-
 inline size_t GetCachedPageSize() {
   static size_t pagesize = 0;
   if (!pagesize)
@@ -112,25 +100,35 @@
 }
 
 void InsertAllocatorDispatch(AllocatorDispatch* dispatch) {
-  // Ensure this is always called on the same thread.
-  DCHECK(CalledOnValidThread());
+  // Loop in case of (an unlikely) race on setting the list head.
+  size_t kMaxRetries = 7;
+  for (size_t i = 0; i < kMaxRetries; ++i) {
+    const AllocatorDispatch* chain_head = GetChainHead();
+    dispatch->next = chain_head;
 
-  dispatch->next = GetChainHead();
+    // This function guarantees to be thread-safe w.r.t. concurrent
+    // insertions. It also has to guarantee that all the threads always
+    // see a consistent chain, hence the MemoryBarrier() below.
+    // InsertAllocatorDispatch() is NOT a fastpath, as opposite to malloc(), so
+    // we don't really want this to be a release-store with a corresponding
+    // acquire-load during malloc().
+    subtle::MemoryBarrier();
+    subtle::AtomicWord old_value =
+        reinterpret_cast<subtle::AtomicWord>(chain_head);
+    // Set the chain head to the new dispatch atomically. If we lose the race,
+    // the comparison will fail, and the new head of chain will be returned.
+    if (subtle::NoBarrier_CompareAndSwap(
+            &g_chain_head, old_value,
+            reinterpret_cast<subtle::AtomicWord>(dispatch)) == old_value) {
+      // Success.
+      return;
+    }
+  }
 
-  // This function does not guarantee to be thread-safe w.r.t. concurrent
-  // insertions, but still has to guarantee that all the threads always
-  // see a consistent chain, hence the MemoryBarrier() below.
-  // InsertAllocatorDispatch() is NOT a fastpath, as opposite to malloc(), so
-  // we don't really want this to be a release-store with a corresponding
-  // acquire-load during malloc().
-  subtle::MemoryBarrier();
-
-  subtle::NoBarrier_Store(&g_chain_head,
-                          reinterpret_cast<subtle::AtomicWord>(dispatch));
+  CHECK(false);  // Too many retries, this shouldn't happen.
 }
 
 void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch) {
-  DCHECK(CalledOnValidThread());
   DCHECK_EQ(GetChainHead(), dispatch);
   subtle::NoBarrier_Store(&g_chain_head,
                           reinterpret_cast<subtle::AtomicWord>(dispatch->next));
diff --git a/base/allocator/allocator_shim.h b/base/allocator/allocator_shim.h
index aca13d2b..8fd060fc 100644
--- a/base/allocator/allocator_shim.h
+++ b/base/allocator/allocator_shim.h
@@ -86,10 +86,10 @@
 // regardless of SetCallNewHandlerOnMallocFailure().
 BASE_EXPORT void* UncheckedAlloc(size_t size);
 
-// Inserts |dispatch| in front of the allocator chain. This method is NOT
+// Inserts |dispatch| in front of the allocator chain. This method is
 // thread-safe w.r.t concurrent invocations of InsertAllocatorDispatch().
-// The callers have the responsibility of linearizing the changes to the chain
-// (or more likely call these always on the same thread).
+// The callers have responsibility for inserting a single dispatch no more
+// than once.
 BASE_EXPORT void InsertAllocatorDispatch(AllocatorDispatch* dispatch);
 
 // Test-only. Rationale: (1) lack of use cases; (2) dealing safely with a
diff --git a/base/allocator/allocator_shim_override_ucrt_symbols_win.h b/base/allocator/allocator_shim_override_ucrt_symbols_win.h
index 2aa872f..b8d0d91f 100644
--- a/base/allocator/allocator_shim_override_ucrt_symbols_win.h
+++ b/base/allocator/allocator_shim_override_ucrt_symbols_win.h
@@ -63,6 +63,30 @@
   return ShimCalloc(n, size);
 }
 
+// The symbols
+//   * __acrt_heap
+//   * __acrt_initialize_heap
+//   * __acrt_uninitialize_heap
+//   * _get_heap_handle
+// must be overridden all or none, as they are otherwise supplied
+// by heap_handle.obj in the ucrt.lib file.
+HANDLE __acrt_heap = nullptr;
+
+bool __acrt_initialize_heap() {
+  __acrt_heap = ::HeapCreate(0, 0, 0);
+  return true;
+}
+
+bool __acrt_uninitialize_heap() {
+  ::HeapDestroy(__acrt_heap);
+  __acrt_heap = nullptr;
+  return true;
+}
+
+intptr_t _get_heap_handle(void) {
+  return reinterpret_cast<intptr_t>(__acrt_heap);
+}
+
 // The default dispatch translation unit has to define also the following
 // symbols (unless they are ultimately routed to the system symbols):
 //   void malloc_stats(void);
diff --git a/base/allocator/allocator_shim_unittest.cc b/base/allocator/allocator_shim_unittest.cc
index d67183ea..e45b03d 100644
--- a/base/allocator/allocator_shim_unittest.cc
+++ b/base/allocator/allocator_shim_unittest.cc
@@ -12,6 +12,7 @@
 #include <new>
 #include <vector>
 
+#include "base/allocator/features.h"
 #include "base/atomicops.h"
 #include "base/process/process_metrics.h"
 #include "base/synchronization/waitable_event.h"
@@ -20,7 +21,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if !defined(OS_WIN)
+#if defined(OS_WIN)
+#include <windows.h>
+#else
 #include <unistd.h>
 #endif
 
@@ -329,6 +332,12 @@
   ASSERT_EQ(kNumThreads, GetNumberOfNewHandlerCalls());
 }
 
+#if defined(OS_WIN) && BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+TEST_F(AllocatorShimTest, ShimReplacesCRTHeapWhenEnabled) {
+  ASSERT_NE(::GetProcessHeap(), reinterpret_cast<HANDLE>(_get_heap_handle()));
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+
 }  // namespace
 }  // namespace allocator
 }  // namespace base
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index cd427da..1850a1a 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -39,7 +39,7 @@
   DictionaryHiddenRootValue(std::unique_ptr<std::string> json,
                             std::unique_ptr<Value> root)
       : json_(std::move(json)) {
-    DCHECK(root->IsType(Value::TYPE_DICTIONARY));
+    DCHECK(root->IsType(Value::Type::DICTIONARY));
     DictionaryValue::Swap(static_cast<DictionaryValue*>(root.get()));
   }
 
@@ -91,7 +91,7 @@
   ListHiddenRootValue(std::unique_ptr<std::string> json,
                       std::unique_ptr<Value> root)
       : json_(std::move(json)) {
-    DCHECK(root->IsType(Value::TYPE_LIST));
+    DCHECK(root->IsType(Value::Type::LIST));
     ListValue::Swap(static_cast<ListValue*>(root.get()));
   }
 
@@ -140,7 +140,7 @@
 class JSONStringValue : public Value {
  public:
   explicit JSONStringValue(StringPiece piece)
-      : Value(TYPE_STRING), string_piece_(piece) {}
+      : Value(Type::STRING), string_piece_(piece) {}
 
   // Overridden from Value:
   bool GetAsString(std::string* out_value) const override {
@@ -154,8 +154,8 @@
   Value* DeepCopy() const override { return new StringValue(string_piece_); }
   bool Equals(const Value* other) const override {
     std::string other_string;
-    return other->IsType(TYPE_STRING) && other->GetAsString(&other_string) &&
-        StringPiece(other_string) == string_piece_;
+    return other->IsType(Type::STRING) && other->GetAsString(&other_string) &&
+           StringPiece(other_string) == string_piece_;
   }
 
  private:
@@ -255,15 +255,15 @@
   // Dictionaries and lists can contain JSONStringValues, so wrap them in a
   // hidden root.
   if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
-    if (root->IsType(Value::TYPE_DICTIONARY)) {
+    if (root->IsType(Value::Type::DICTIONARY)) {
       return MakeUnique<DictionaryHiddenRootValue>(std::move(input_copy),
                                                    std::move(root));
     }
-    if (root->IsType(Value::TYPE_LIST)) {
+    if (root->IsType(Value::Type::LIST)) {
       return MakeUnique<ListHiddenRootValue>(std::move(input_copy),
                                              std::move(root));
     }
-    if (root->IsType(Value::TYPE_STRING)) {
+    if (root->IsType(Value::Type::STRING)) {
       // A string type could be a JSONStringValue, but because there's no
       // corresponding HiddenRootValue, the memory will be lost. Deep copy to
       // preserve it.
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index 60232889..11d0de2 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -124,7 +124,7 @@
   TestLastThree(parser.get());
 
   ASSERT_TRUE(value.get());
-  EXPECT_TRUE(value->IsType(Value::TYPE_NULL));
+  EXPECT_TRUE(value->IsType(Value::Type::NONE));
 }
 
 TEST_F(JSONParserTest, ConsumeNumbers) {
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 2d6f8f3..1b00b1d9 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -26,7 +26,7 @@
     // some whitespace checking
     std::unique_ptr<Value> root = JSONReader().ReadToValue("   null   ");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+    EXPECT_TRUE(root->IsType(Value::Type::NONE));
   }
 
   {
@@ -38,23 +38,23 @@
     // Simple bool
     std::unique_ptr<Value> root = JSONReader().ReadToValue("true  ");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+    EXPECT_TRUE(root->IsType(Value::Type::BOOLEAN));
   }
 
   {
     // Embedded comment
     std::unique_ptr<Value> root = JSONReader().ReadToValue("/* comment */null");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+    EXPECT_TRUE(root->IsType(Value::Type::NONE));
     root = JSONReader().ReadToValue("40 /* comment */");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+    EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
     root = JSONReader().ReadToValue("true // comment");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+    EXPECT_TRUE(root->IsType(Value::Type::BOOLEAN));
     root = JSONReader().ReadToValue("/* comment */\"sample string\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string value;
     EXPECT_TRUE(root->GetAsString(&value));
     EXPECT_EQ("sample string", value);
@@ -72,7 +72,7 @@
     EXPECT_EQ(3u, list->GetSize());
     root = JSONReader().ReadToValue("/* comment **/42");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+    EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
     EXPECT_TRUE(root->GetAsInteger(&int_val));
     EXPECT_EQ(42, int_val);
     root = JSONReader().ReadToValue(
@@ -80,7 +80,7 @@
         "// */ 43\n"
         "44");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+    EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
     EXPECT_TRUE(root->GetAsInteger(&int_val));
     EXPECT_EQ(44, int_val);
   }
@@ -89,7 +89,7 @@
     // Test number formats
     std::unique_ptr<Value> root = JSONReader().ReadToValue("43");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+    EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
     int int_val = 0;
     EXPECT_TRUE(root->GetAsInteger(&int_val));
     EXPECT_EQ(43, int_val);
@@ -107,7 +107,7 @@
     // clause).
     std::unique_ptr<Value> root = JSONReader().ReadToValue("0");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+    EXPECT_TRUE(root->IsType(Value::Type::INTEGER));
     int int_val = 1;
     EXPECT_TRUE(root->GetAsInteger(&int_val));
     EXPECT_EQ(0, int_val);
@@ -119,13 +119,13 @@
     std::unique_ptr<Value> root = JSONReader().ReadToValue("2147483648");
     ASSERT_TRUE(root);
     double double_val;
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(2147483648.0, double_val);
     root = JSONReader().ReadToValue("-2147483649");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(-2147483649.0, double_val);
@@ -135,42 +135,42 @@
     // Parse a double
     std::unique_ptr<Value> root = JSONReader().ReadToValue("43.1");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(43.1, double_val);
 
     root = JSONReader().ReadToValue("4.3e-1");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(.43, double_val);
 
     root = JSONReader().ReadToValue("2.1e0");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(2.1, double_val);
 
     root = JSONReader().ReadToValue("2.1e+0001");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(21.0, double_val);
 
     root = JSONReader().ReadToValue("0.01");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(0.01, double_val);
 
     root = JSONReader().ReadToValue("1.00");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+    EXPECT_TRUE(root->IsType(Value::Type::DOUBLE));
     double_val = 0.0;
     EXPECT_TRUE(root->GetAsDouble(&double_val));
     EXPECT_DOUBLE_EQ(1.0, double_val);
@@ -210,7 +210,7 @@
     // Test string parser
     std::unique_ptr<Value> root = JSONReader().ReadToValue("\"hello world\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ("hello world", str_val);
@@ -220,7 +220,7 @@
     // Empty string
     std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ("", str_val);
@@ -231,7 +231,7 @@
     std::unique_ptr<Value> root =
         JSONReader().ReadToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
@@ -242,7 +242,7 @@
     std::unique_ptr<Value> root =
         JSONReader().ReadToValue("\"\\x41\\x00\\u1234\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ(std::wstring(L"A\0\x1234", 3), UTF8ToWide(str_val));
@@ -316,7 +316,7 @@
     EXPECT_EQ(1U, list->GetSize());
     Value* tmp_value = nullptr;
     ASSERT_TRUE(list->Get(0, &tmp_value));
-    EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN));
+    EXPECT_TRUE(tmp_value->IsType(Value::Type::BOOLEAN));
     bool bool_value = false;
     EXPECT_TRUE(tmp_value->GetAsBoolean(&bool_value));
     EXPECT_TRUE(bool_value);
@@ -345,7 +345,7 @@
     EXPECT_DOUBLE_EQ(9.87654321, double_val);
     Value* null_val = nullptr;
     ASSERT_TRUE(dict_val->Get("null", &null_val));
-    EXPECT_TRUE(null_val->IsType(Value::TYPE_NULL));
+    EXPECT_TRUE(null_val->IsType(Value::Type::NONE));
     std::string str_val;
     EXPECT_TRUE(dict_val->GetString("S", &str_val));
     EXPECT_EQ("str", str_val);
@@ -483,7 +483,7 @@
     std::unique_ptr<Value> root =
         JSONReader().ReadToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val));
@@ -507,7 +507,7 @@
     // Test utf16 encoded strings.
     std::unique_ptr<Value> root = JSONReader().ReadToValue("\"\\u20ac3,14\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     std::string str_val;
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ(
@@ -517,7 +517,7 @@
 
     root = JSONReader().ReadToValue("\"\\ud83d\\udca9\\ud83d\\udc6c\"");
     ASSERT_TRUE(root);
-    EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+    EXPECT_TRUE(root->IsType(Value::Type::STRING));
     str_val.clear();
     EXPECT_TRUE(root->GetAsString(&str_val));
     EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val);
@@ -545,7 +545,7 @@
   {
     // Test literal root objects.
     std::unique_ptr<Value> root = JSONReader::Read("null");
-    EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+    EXPECT_TRUE(root->IsType(Value::Type::NONE));
 
     root = JSONReader::Read("true");
     ASSERT_TRUE(root);
@@ -579,7 +579,7 @@
   JSONReader reader;
   std::unique_ptr<Value> root(reader.ReadToValue(input));
   ASSERT_TRUE(root) << reader.GetErrorMessage();
-  EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+  EXPECT_TRUE(root->IsType(Value::Type::DICTIONARY));
 }
 
 // Tests that the root of a JSON object can be deleted safely while its
diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc
index 43ddc9c..e835700a 100644
--- a/base/json/json_value_serializer_unittest.cc
+++ b/base/json/json_value_serializer_unittest.cc
@@ -224,7 +224,7 @@
   Value* null_value = nullptr;
   ASSERT_TRUE(root_dict->Get("null", &null_value));
   ASSERT_TRUE(null_value);
-  ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL));
+  ASSERT_TRUE(null_value->IsType(Value::Type::NONE));
 
   bool bool_value = false;
   ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value));
@@ -417,7 +417,7 @@
   Value* null_value = nullptr;
   ASSERT_TRUE(root_dict->Get("null", &null_value));
   ASSERT_TRUE(null_value);
-  ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL));
+  ASSERT_TRUE(null_value->IsType(Value::Type::NONE));
 
   bool bool_value = false;
   ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value));
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc
index 0b658eed..07b9d509 100644
--- a/base/json/json_writer.cc
+++ b/base/json/json_writer.cc
@@ -57,12 +57,12 @@
 
 bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
   switch (node.GetType()) {
-    case Value::TYPE_NULL: {
+    case Value::Type::NONE: {
       json_string_->append("null");
       return true;
     }
 
-    case Value::TYPE_BOOLEAN: {
+    case Value::Type::BOOLEAN: {
       bool value;
       bool result = node.GetAsBoolean(&value);
       DCHECK(result);
@@ -70,7 +70,7 @@
       return result;
     }
 
-    case Value::TYPE_INTEGER: {
+    case Value::Type::INTEGER: {
       int value;
       bool result = node.GetAsInteger(&value);
       DCHECK(result);
@@ -78,7 +78,7 @@
       return result;
     }
 
-    case Value::TYPE_DOUBLE: {
+    case Value::Type::DOUBLE: {
       double value;
       bool result = node.GetAsDouble(&value);
       DCHECK(result);
@@ -110,7 +110,7 @@
       return result;
     }
 
-    case Value::TYPE_STRING: {
+    case Value::Type::STRING: {
       std::string value;
       bool result = node.GetAsString(&value);
       DCHECK(result);
@@ -118,7 +118,7 @@
       return result;
     }
 
-    case Value::TYPE_LIST: {
+    case Value::Type::LIST: {
       json_string_->push_back('[');
       if (pretty_print_)
         json_string_->push_back(' ');
@@ -128,7 +128,7 @@
       bool result = node.GetAsList(&list);
       DCHECK(result);
       for (const auto& value : *list) {
-        if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY)
+        if (omit_binary_values_ && value->GetType() == Value::Type::BINARY)
           continue;
 
         if (first_value_has_been_output) {
@@ -149,7 +149,7 @@
       return result;
     }
 
-    case Value::TYPE_DICTIONARY: {
+    case Value::Type::DICTIONARY: {
       json_string_->push_back('{');
       if (pretty_print_)
         json_string_->append(kPrettyPrintLineEnding);
@@ -161,7 +161,7 @@
       for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
            itr.Advance()) {
         if (omit_binary_values_ &&
-            itr.value().GetType() == Value::TYPE_BINARY) {
+            itr.value().GetType() == Value::Type::BINARY) {
           continue;
         }
 
@@ -194,7 +194,7 @@
       return result;
     }
 
-    case Value::TYPE_BINARY:
+    case Value::Type::BINARY:
       // Successful only if we're allowed to omit it.
       DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
       return omit_binary_values_;
diff --git a/base/metrics/field_trial_param_associator.cc b/base/metrics/field_trial_param_associator.cc
index 0230b8a8..4a4e361 100644
--- a/base/metrics/field_trial_param_associator.cc
+++ b/base/metrics/field_trial_param_associator.cc
@@ -21,11 +21,10 @@
     const std::string& trial_name,
     const std::string& group_name,
     const FieldTrialParams& params) {
-  AutoLock scoped_lock(lock_);
-
   if (FieldTrialList::IsTrialActive(trial_name))
     return false;
 
+  AutoLock scoped_lock(lock_);
   const FieldTrialKey key(trial_name, group_name);
   if (ContainsKey(field_trial_params_, key))
     return false;
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 98086039..5aaba9b 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -106,10 +106,15 @@
 };
 
 namespace internal {
-// This wrapper is used for C++11 constexpr support by avoiding the declaration
-// of local variables in the saturated_cast template function.
-// TODO(jschuh): convert this back to a switch once we support C++14.
-template <typename Dst, class NaNHandler, typename Src>
+// These wrappers are used for C++11 constexpr support by avoiding both the
+// declaration of local variables and invalid evaluation resulting from the
+// lack of "constexpr if" support in the saturated_cast template function.
+// TODO(jschuh): Convert to single function with a switch once we support C++14.
+template <
+    typename Dst,
+    class NaNHandler,
+    typename Src,
+    typename std::enable_if<std::is_integral<Dst>::value>::type* = nullptr>
 constexpr Dst saturated_cast_impl(const Src value,
                                   const RangeConstraint constraint) {
   return constraint == RANGE_VALID
@@ -121,6 +126,22 @@
                            : NaNHandler::template HandleFailure<Dst>()));
 }
 
+template <typename Dst,
+          class NaNHandler,
+          typename Src,
+          typename std::enable_if<std::is_floating_point<Dst>::value>::type* =
+              nullptr>
+constexpr Dst saturated_cast_impl(const Src value,
+                                  const RangeConstraint constraint) {
+  return constraint == RANGE_VALID
+             ? static_cast<Dst>(value)
+             : (constraint == RANGE_UNDERFLOW
+                    ? -std::numeric_limits<Dst>::infinity()
+                    : (constraint == RANGE_OVERFLOW
+                           ? std::numeric_limits<Dst>::infinity()
+                           : std::numeric_limits<Dst>::quiet_NaN()));
+}
+
 // saturated_cast<> is analogous to static_cast<> for numeric types, except
 // that the specified numeric conversion will saturate rather than overflow or
 // underflow. NaN assignment to an integral will defer the behavior to a
@@ -130,12 +151,8 @@
           typename Src>
 constexpr Dst saturated_cast(Src value) {
   using SrcType = typename UnderlyingType<Src>::type;
-  return std::is_floating_point<Dst>::value
-             ? static_cast<Dst>(
-                   static_cast<SrcType>(value))  // Floating point optimization.
-             : internal::saturated_cast_impl<Dst, NaNHandler>(
-                   value,
-                   internal::DstRangeRelationToSrcRange<Dst, SrcType>(value));
+  return internal::saturated_cast_impl<Dst, NaNHandler>(
+      value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value));
 }
 
 // strict_cast<> is analogous to static_cast<> for numeric types, except that
diff --git a/base/numerics/safe_math_impl.h b/base/numerics/safe_math_impl.h
index 679cfa4..ae0a5b1 100644
--- a/base/numerics/safe_math_impl.h
+++ b/base/numerics/safe_math_impl.h
@@ -635,6 +635,16 @@
   bool is_valid_;
   T value_;
 
+  // Ensures that a type conversion does not trigger undefined behavior.
+  template <typename Src>
+  static constexpr T WellDefinedConversionOrZero(const Src value,
+                                                 const bool is_valid) {
+    using SrcType = typename internal::UnderlyingType<Src>::type;
+    return (std::is_integral<SrcType>::value || is_valid)
+               ? static_cast<T>(value)
+               : static_cast<T>(0);
+  }
+
  public:
   template <typename Src, NumericRepresentation type>
   friend class CheckedNumericState;
@@ -644,7 +654,7 @@
   template <typename Src>
   constexpr CheckedNumericState(Src value, bool is_valid)
       : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
-        value_(is_valid_ ? static_cast<T>(value) : 0) {
+        value_(WellDefinedConversionOrZero(value, is_valid_)) {
     static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
   }
 
@@ -652,12 +662,12 @@
   template <typename Src>
   constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
       : is_valid_(rhs.IsValid()),
-        value_(is_valid_ ? static_cast<T>(rhs.value()) : 0) {}
+        value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
 
   template <typename Src>
   constexpr explicit CheckedNumericState(Src value)
       : is_valid_(IsValueInRangeForNumericType<T>(value)),
-        value_(is_valid_ ? static_cast<T>(value) : 0) {}
+        value_(WellDefinedConversionOrZero(value, is_valid_)) {}
 
   constexpr bool is_valid() const { return is_valid_; }
   constexpr T value() const { return value_; }
@@ -669,6 +679,18 @@
  private:
   T value_;
 
+  // Ensures that a type conversion does not trigger undefined behavior.
+  template <typename Src>
+  static constexpr T WellDefinedConversionOrNaN(const Src value,
+                                                const bool is_valid) {
+    using SrcType = typename internal::UnderlyingType<Src>::type;
+    return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
+                NUMERIC_RANGE_CONTAINED ||
+            is_valid)
+               ? static_cast<T>(value)
+               : std::numeric_limits<T>::quiet_NaN();
+  }
+
  public:
   template <typename Src, NumericRepresentation type>
   friend class CheckedNumericState;
@@ -677,18 +699,20 @@
 
   template <typename Src>
   constexpr CheckedNumericState(Src value, bool is_valid)
-      : value_((is_valid && IsValueInRangeForNumericType<T>(value))
-                   ? static_cast<T>(value)
-                   : std::numeric_limits<T>::quiet_NaN()) {}
+      : value_(WellDefinedConversionOrNaN(value, is_valid)) {}
 
   template <typename Src>
   constexpr explicit CheckedNumericState(Src value)
-      : value_(static_cast<T>(value)) {}
+      : value_(WellDefinedConversionOrNaN(
+            value,
+            IsValueInRangeForNumericType<T>(value))) {}
 
   // Copy constructor.
   template <typename Src>
   constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
-      : value_(static_cast<T>(rhs.value())) {}
+      : value_(WellDefinedConversionOrNaN(
+            rhs.value(),
+            rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
 
   constexpr bool is_valid() const {
     // Written this way because std::isfinite is not reliably constexpr.
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h
index dc683d40..afb1010 100644
--- a/base/observer_list_threadsafe.h
+++ b/base/observer_list_threadsafe.h
@@ -55,11 +55,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace base {
-
-// Forward declaration for ObserverListThreadSafeTraits.
-template <class ObserverType>
-class ObserverListThreadSafe;
-
 namespace internal {
 
 template <typename ObserverType, typename Method>
@@ -75,27 +70,9 @@
 
 }  // namespace internal
 
-// This class is used to work around VS2005 not accepting:
-//
-// friend class
-//     base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>>;
-//
-// Instead of friending the class, we could friend the actual function
-// which calls delete.  However, this ends up being
-// RefCountedThreadSafe::DeleteInternal(), which is private.  So we
-// define our own templated traits class so we can friend it.
-template <class T>
-struct ObserverListThreadSafeTraits {
-  static void Destruct(const ObserverListThreadSafe<T>* x) {
-    delete x;
-  }
-};
-
 template <class ObserverType>
 class ObserverListThreadSafe
-    : public RefCountedThreadSafe<
-        ObserverListThreadSafe<ObserverType>,
-        ObserverListThreadSafeTraits<ObserverType>> {
+    : public RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>> {
  public:
   using NotificationType =
       typename ObserverList<ObserverType>::NotificationType;
@@ -180,8 +157,7 @@
   }
 
  private:
-  // See comment above ObserverListThreadSafeTraits' definition.
-  friend struct ObserverListThreadSafeTraits<ObserverType>;
+  friend class RefCountedThreadSafe<ObserverListThreadSafe<ObserverType>>;
 
   struct ObserverListContext {
     explicit ObserverListContext(NotificationType type)
diff --git a/base/strings/utf_string_conversions.cc b/base/strings/utf_string_conversions.cc
index 6b17eacd6..85450c65 100644
--- a/base/strings/utf_string_conversions.cc
+++ b/base/strings/utf_string_conversions.cc
@@ -180,10 +180,6 @@
 }
 
 std::string UTF16ToUTF8(StringPiece16 utf16) {
-  if (IsStringASCII(utf16)) {
-    return std::string(utf16.begin(), utf16.end());
-  }
-
   std::string ret;
   // Ignore the success flag of this call, it will do the best it can for
   // invalid input, which is what we want here.
diff --git a/base/syslog_logging.cc b/base/syslog_logging.cc
index 1cd5459..087b4fd 100644
--- a/base/syslog_logging.cc
+++ b/base/syslog_logging.cc
@@ -6,6 +6,8 @@
 #include "base/syslog_logging.h"
 
 #if defined(OS_WIN)
+#include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/win/eventlog_messages.h"
 
 #include <windows.h>
@@ -19,6 +21,18 @@
 
 namespace logging {
 
+#if defined(OS_WIN)
+
+namespace {
+std::string* g_event_source_name = nullptr;
+}
+
+void SetEventSourceName(const std::string& name) {
+  DCHECK_EQ(nullptr, g_event_source_name);
+  g_event_source_name = new std::string(name);
+}
+#endif  // defined(OS_WIN)
+
 EventLogMessage::EventLogMessage(const char* file,
                                  int line,
                                  LogSeverity severity)
@@ -27,13 +41,21 @@
 
 EventLogMessage::~EventLogMessage() {
 #if defined(OS_WIN)
-  const char kEventSource[] = "chrome";
-  HANDLE event_log_handle = RegisterEventSourceA(NULL, kEventSource);
+  // If g_event_source_name is nullptr (which it is per default) SYSLOG will
+  // degrade gracefully to regular LOG. If you see this happening most probably
+  // you are using SYSLOG before you called SetEventSourceName.
+  if (g_event_source_name == nullptr)
+    return;
+
+  HANDLE event_log_handle =
+      RegisterEventSourceA(NULL, g_event_source_name->c_str());
   if (event_log_handle == NULL) {
     stream() << " !!NOT ADDED TO EVENTLOG!!";
     return;
   }
 
+  base::ScopedClosureRunner auto_deregister(
+      base::Bind(base::IgnoreResult(&DeregisterEventSource), event_log_handle));
   std::string message(log_message_.str());
   WORD log_type = EVENTLOG_ERROR_TYPE;
   switch (log_message_.severity()) {
@@ -57,7 +79,6 @@
                     MSG_LOG_MESSAGE, NULL, 1, 0, strings, NULL)) {
     stream() << " !!NOT ADDED TO EVENTLOG!!";
   }
-  DeregisterEventSource(event_log_handle);
 #elif defined(OS_LINUX)
   const char kEventSource[] = "chrome";
   openlog(kEventSource, LOG_NOWAIT | LOG_PID, LOG_USER);
diff --git a/base/syslog_logging.h b/base/syslog_logging.h
index 0196ba7b..74f35be 100644
--- a/base/syslog_logging.h
+++ b/base/syslog_logging.h
@@ -17,6 +17,11 @@
 #define SYSLOG(severity) \
   SYSLOG_STREAM(severity)
 
+// Sets the name of the event source for logging to the Windows Event Log.
+// Call this function once before using the SYSLOG macro or otherwise it will
+// behave as a regular LOG macro.
+void BASE_EXPORT SetEventSourceName(const std::string& name);
+
 // Creates a formatted message on the system event log. That would be the
 // Application Event log on Windows and the messages log file on POSIX systems.
 class BASE_EXPORT EventLogMessage {
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index 64436dc..5e097686 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -34,8 +34,8 @@
 TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
 
 bool TraceEvent::SetFromJSON(const base::Value* event_value) {
-  if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
-    LOG(ERROR) << "Value must be TYPE_DICTIONARY";
+  if (event_value->GetType() != base::Value::Type::DICTIONARY) {
+    LOG(ERROR) << "Value must be Type::DICTIONARY";
     return false;
   }
   const base::DictionaryValue* dictionary =
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
index 086cfc9..caa35e2 100644
--- a/base/test/trace_event_analyzer_unittest.cc
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -123,7 +123,7 @@
 
   std::unique_ptr<base::Value> arg;
   EXPECT_TRUE(event.GetArgAsValue("dict", &arg));
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, arg->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, arg->GetType());
 }
 
 TEST_F(TraceEventAnalyzerTest, QueryEventMember) {
diff --git a/base/time/time.cc b/base/time/time.cc
index ba129954..df3942c 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -126,7 +126,7 @@
 }  // namespace time_internal
 
 std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
-  return os << time_delta.InSecondsF() << "s";
+  return os << time_delta.InSecondsF() << " s";
 }
 
 // Time -----------------------------------------------------------------------
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 2731a62..8906c3b 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -1116,17 +1116,17 @@
 
 TEST(TimeDeltaLogging, EmptyIsZero) {
   TimeDelta zero;
-  EXPECT_EQ("0s", AnyToString(zero));
+  EXPECT_EQ("0 s", AnyToString(zero));
 }
 
 TEST(TimeDeltaLogging, FiveHundredMs) {
   TimeDelta five_hundred_ms = TimeDelta::FromMilliseconds(500);
-  EXPECT_EQ("0.5s", AnyToString(five_hundred_ms));
+  EXPECT_EQ("0.5 s", AnyToString(five_hundred_ms));
 }
 
 TEST(TimeDeltaLogging, MinusTenSeconds) {
   TimeDelta minus_ten_seconds = TimeDelta::FromSeconds(-10);
-  EXPECT_EQ("-10s", AnyToString(minus_ten_seconds));
+  EXPECT_EQ("-10 s", AnyToString(minus_ten_seconds));
 }
 
 TEST(TimeDeltaLogging, DoesNotMessUpFormattingFlags) {
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 9f77e10..c09470be 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -105,26 +105,28 @@
 // NOTE: crbug.com/665516
 // Unfortunately, there is no safe way to collect information from secondary
 // heaps due to limitations and racy nature of this piece of WinAPI.
-void WinHeapMemoryDumpImpl(WinHeapInfo* main_heap_info) {
+void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) {
 #if defined(SYZYASAN)
   if (base::debug::IsBinaryInstrumented())
     return;
 #endif
-  HANDLE main_heap = ::GetProcessHeap();
-  ::HeapLock(main_heap);
+
+  // Iterate through whichever heap our CRT is using.
+  HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle());
+  ::HeapLock(crt_heap);
   PROCESS_HEAP_ENTRY heap_entry;
   heap_entry.lpData = nullptr;
   // Walk over all the entries in the main heap.
-  while (::HeapWalk(main_heap, &heap_entry) != FALSE) {
+  while (::HeapWalk(crt_heap, &heap_entry) != FALSE) {
     if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
-      main_heap_info->allocated_size += heap_entry.cbData;
-      main_heap_info->block_count++;
+      crt_heap_info->allocated_size += heap_entry.cbData;
+      crt_heap_info->block_count++;
     } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
-      main_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
-      main_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
+      crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
+      crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
     }
   }
-  CHECK(::HeapUnlock(main_heap) == TRUE);
+  CHECK(::HeapUnlock(crt_heap) == TRUE);
 }
 #endif  // defined(OS_WIN)
 }  // namespace
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
index 336d964..da33c6d 100644
--- a/base/trace_event/trace_event_argument.cc
+++ b/base/trace_event/trace_event_argument.cc
@@ -244,36 +244,36 @@
                                              const base::Value& value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
   switch (value.GetType()) {
-    case base::Value::TYPE_NULL:
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::NONE:
+    case base::Value::Type::BINARY:
       NOTREACHED();
       break;
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value;
       value.GetAsBoolean(&bool_value);
       SetBooleanWithCopiedName(name, bool_value);
     } break;
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value;
       value.GetAsInteger(&int_value);
       SetIntegerWithCopiedName(name, int_value);
     } break;
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       value.GetAsDouble(&double_value);
       SetDoubleWithCopiedName(name, double_value);
     } break;
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       const StringValue* string_value;
       value.GetAsString(&string_value);
       SetStringWithCopiedName(name, string_value->GetString());
     } break;
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const DictionaryValue* dict_value;
       value.GetAsDictionary(&dict_value);
       BeginDictionaryWithCopiedName(name);
@@ -284,7 +284,7 @@
       EndDictionary();
     } break;
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const ListValue* list_value;
       value.GetAsList(&list_value);
       BeginArrayWithCopiedName(name);
@@ -298,36 +298,36 @@
 void TracedValue::AppendBaseValue(const base::Value& value) {
   DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
   switch (value.GetType()) {
-    case base::Value::TYPE_NULL:
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::NONE:
+    case base::Value::Type::BINARY:
       NOTREACHED();
       break;
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value;
       value.GetAsBoolean(&bool_value);
       AppendBoolean(bool_value);
     } break;
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value;
       value.GetAsInteger(&int_value);
       AppendInteger(int_value);
     } break;
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       value.GetAsDouble(&double_value);
       AppendDouble(double_value);
     } break;
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       const StringValue* string_value;
       value.GetAsString(&string_value);
       AppendString(string_value->GetString());
     } break;
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const DictionaryValue* dict_value;
       value.GetAsDictionary(&dict_value);
       BeginDictionary();
@@ -338,7 +338,7 @@
       EndDictionary();
     } break;
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const ListValue* list_value;
       value.GetAsList(&list_value);
       BeginArray();
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
index 23579cb..48a0d29f 100644
--- a/base/trace_event/trace_event_memory_overhead.cc
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -69,27 +69,27 @@
 
 void TraceEventMemoryOverhead::AddValue(const Value& value) {
   switch (value.GetType()) {
-    case Value::TYPE_NULL:
-    case Value::TYPE_BOOLEAN:
-    case Value::TYPE_INTEGER:
-    case Value::TYPE_DOUBLE:
+    case Value::Type::NONE:
+    case Value::Type::BOOLEAN:
+    case Value::Type::INTEGER:
+    case Value::Type::DOUBLE:
       Add("FundamentalValue", sizeof(Value));
       break;
 
-    case Value::TYPE_STRING: {
+    case Value::Type::STRING: {
       const StringValue* string_value = nullptr;
       value.GetAsString(&string_value);
       Add("StringValue", sizeof(StringValue));
       AddString(string_value->GetString());
     } break;
 
-    case Value::TYPE_BINARY: {
+    case Value::Type::BINARY: {
       const BinaryValue* binary_value = nullptr;
       value.GetAsBinary(&binary_value);
       Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize());
     } break;
 
-    case Value::TYPE_DICTIONARY: {
+    case Value::Type::DICTIONARY: {
       const DictionaryValue* dictionary_value = nullptr;
       value.GetAsDictionary(&dictionary_value);
       Add("DictionaryValue", sizeof(DictionaryValue));
@@ -100,7 +100,7 @@
       }
     } break;
 
-    case Value::TYPE_LIST: {
+    case Value::Type::LIST: {
       const ListValue* list_value = nullptr;
       value.GetAsList(&list_value);
       Add("ListValue", sizeof(ListValue));
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 81d043ac3..2fae243 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -260,7 +260,7 @@
   for (size_t i = 0; i < trace_parsed_count; i++) {
     Value* value = NULL;
     trace_parsed_.Get(i, &value);
-    if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+    if (!value || value->GetType() != Value::Type::DICTIONARY)
       continue;
     DictionaryValue* dict = static_cast<DictionaryValue*>(value);
 
@@ -278,7 +278,7 @@
   for (size_t i = 0; i < old_trace_parsed_size; i++) {
     Value* value = nullptr;
     old_trace_parsed->Get(i, &value);
-    if (!value || value->GetType() != Value::TYPE_DICTIONARY) {
+    if (!value || value->GetType() != Value::Type::DICTIONARY) {
       trace_parsed_.Append(value->CreateDeepCopy());
       continue;
     }
@@ -367,7 +367,7 @@
          match_after_this_item = NULL;
       continue;
     }
-    if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+    if (!value || value->GetType() != Value::Type::DICTIONARY)
       continue;
     const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
 
@@ -385,7 +385,7 @@
   for (size_t i = 0; i < trace_parsed_count; i++) {
     const Value* value = NULL;
     trace_parsed.Get(i, &value);
-    if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+    if (!value || value->GetType() != Value::Type::DICTIONARY)
       continue;
     const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
 
@@ -1114,7 +1114,7 @@
   for (size_t i = 0; i < trace_parsed_count; i++) {
     const Value* value = NULL;
     trace_parsed.Get(i, &value);
-    if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+    if (!value || value->GetType() != Value::Type::DICTIONARY)
       continue;
     const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
     std::string name;
@@ -2320,7 +2320,7 @@
   dict->GetDictionary("args", &args_dict);
   ASSERT_TRUE(args_dict);
   EXPECT_TRUE(args_dict->Get("float_one", &value));
-  EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+  EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
   EXPECT_TRUE(value->GetAsDouble(&double_value));
   EXPECT_EQ(1, double_value);
 
@@ -2330,7 +2330,7 @@
   dict->GetDictionary("args", &args_dict);
   ASSERT_TRUE(args_dict);
   EXPECT_TRUE(args_dict->Get("float_half", &value));
-  EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+  EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
   EXPECT_TRUE(value->GetAsDouble(&double_value));
   EXPECT_EQ(0.5, double_value);
 
@@ -2340,7 +2340,7 @@
   dict->GetDictionary("args", &args_dict);
   ASSERT_TRUE(args_dict);
   EXPECT_TRUE(args_dict->Get("float_neghalf", &value));
-  EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+  EXPECT_TRUE(value->IsType(Value::Type::DOUBLE));
   EXPECT_TRUE(value->GetAsDouble(&double_value));
   EXPECT_EQ(-0.5, double_value);
 
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
index d32429d..158fb94 100644
--- a/base/tracked_objects.cc
+++ b/base/tracked_objects.cc
@@ -4,6 +4,7 @@
 
 #include "base/tracked_objects.h"
 
+#include <ctype.h>
 #include <limits.h>
 #include <stdlib.h>
 
@@ -13,10 +14,10 @@
 #include "base/compiler_specific.h"
 #include "base/debug/leak_annotations.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "base/process/process_handle.h"
-#include "base/strings/stringprintf.h"
 #include "base/third_party/valgrind/memcheck.h"
 #include "base/threading/worker_pool.h"
 #include "base/tracking_info.h"
@@ -31,6 +32,9 @@
 namespace tracked_objects {
 
 namespace {
+
+constexpr char kWorkerThreadSanitizedName[] = "WorkerThread-*";
+
 // When ThreadData is first initialized, should we start in an ACTIVE state to
 // record all of the startup-time tasks, or should we start up DEACTIVATED, so
 // that we only record after parsing the command line flag --enable-tracking.
@@ -76,6 +80,22 @@
   return current_timing_enabled == ENABLED_TIMING;
 }
 
+// Sanitize a thread name by replacing trailing sequence of digits with "*".
+// Examples:
+// 1. "BrowserBlockingWorker1/23857" => "BrowserBlockingWorker1/*"
+// 2. "Chrome_IOThread" => "Chrome_IOThread"
+std::string SanitizeThreadName(const std::string& thread_name) {
+  size_t i = thread_name.length();
+
+  while (i > 0 && isdigit(thread_name[i - 1]))
+    --i;
+
+  if (i == thread_name.length())
+    return thread_name;
+
+  return thread_name.substr(0, i) + '*';
+}
+
 }  // namespace
 
 //------------------------------------------------------------------------------
@@ -330,8 +350,7 @@
 
 BirthOnThreadSnapshot::BirthOnThreadSnapshot(const BirthOnThread& birth)
     : location(birth.location()),
-      thread_name(birth.birth_thread()->thread_name()) {
-}
+      sanitized_thread_name(birth.birth_thread()->sanitized_thread_name()) {}
 
 BirthOnThreadSnapshot::~BirthOnThreadSnapshot() {
 }
@@ -363,9 +382,6 @@
 base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER;
 
 // static
-int ThreadData::worker_thread_data_creation_count_ = 0;
-
-// static
 int ThreadData::cleanup_count_ = 0;
 
 // static
@@ -375,7 +391,7 @@
 ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
 
 // static
-ThreadData* ThreadData::first_retired_worker_ = NULL;
+ThreadData* ThreadData::first_retired_thread_data_ = NULL;
 
 // static
 base::LazyInstance<base::Lock>::Leaky
@@ -384,25 +400,14 @@
 // static
 base::subtle::Atomic32 ThreadData::status_ = ThreadData::UNINITIALIZED;
 
-ThreadData::ThreadData(const std::string& suggested_name)
+ThreadData::ThreadData(const std::string& sanitized_thread_name)
     : next_(NULL),
-      next_retired_worker_(NULL),
-      worker_thread_number_(0),
+      next_retired_thread_data_(NULL),
+      sanitized_thread_name_(sanitized_thread_name),
       incarnation_count_for_pool_(-1),
       current_stopwatch_(NULL) {
-  DCHECK_GE(suggested_name.size(), 0u);
-  thread_name_ = suggested_name;
-  PushToHeadOfList();  // Which sets real incarnation_count_for_pool_.
-}
-
-ThreadData::ThreadData(int thread_number)
-    : next_(NULL),
-      next_retired_worker_(NULL),
-      worker_thread_number_(thread_number),
-      incarnation_count_for_pool_(-1),
-      current_stopwatch_(NULL) {
-  CHECK_GT(thread_number, 0);
-  base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
+  DCHECK(sanitized_thread_name_.empty() ||
+         !isdigit(sanitized_thread_name_.back()));
   PushToHeadOfList();  // Which sets real incarnation_count_for_pool_.
 }
 
@@ -433,15 +438,17 @@
 ThreadData* ThreadData::next() const { return next_; }
 
 // static
-void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
+void ThreadData::InitializeThreadContext(const std::string& thread_name) {
   if (base::WorkerPool::RunsTasksOnCurrentThread())
     return;
+  DCHECK_NE(thread_name, kWorkerThreadSanitizedName);
   EnsureTlsInitialization();
   ThreadData* current_thread_data =
       reinterpret_cast<ThreadData*>(tls_index_.Get());
   if (current_thread_data)
     return;  // Browser tests instigate this.
-  current_thread_data = new ThreadData(suggested_name);
+  current_thread_data =
+      GetRetiredOrCreateThreadData(SanitizeThreadName(thread_name));
   tls_index_.Set(current_thread_data);
 }
 
@@ -454,26 +461,8 @@
     return registered;
 
   // We must be a worker thread, since we didn't pre-register.
-  ThreadData* worker_thread_data = NULL;
-  int worker_thread_number = 0;
-  {
-    base::AutoLock lock(*list_lock_.Pointer());
-    if (first_retired_worker_) {
-      worker_thread_data = first_retired_worker_;
-      first_retired_worker_ = first_retired_worker_->next_retired_worker_;
-      worker_thread_data->next_retired_worker_ = NULL;
-    } else {
-      worker_thread_number = ++worker_thread_data_creation_count_;
-    }
-  }
-
-  // If we can't find a previously used instance, then we have to create one.
-  if (!worker_thread_data) {
-    DCHECK_GT(worker_thread_number, 0);
-    worker_thread_data = new ThreadData(worker_thread_number);
-  }
-  DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
-
+  ThreadData* worker_thread_data =
+      GetRetiredOrCreateThreadData(kWorkerThreadSanitizedName);
   tls_index_.Set(worker_thread_data);
   return worker_thread_data;
 }
@@ -487,21 +476,23 @@
 }
 
 void ThreadData::OnThreadTerminationCleanup() {
+  // We must NOT do any allocations during this callback. There is a chance that
+  // the allocator is no longer active on this thread.
+
   // The list_lock_ was created when we registered the callback, so it won't be
   // allocated here despite the lazy reference.
   base::AutoLock lock(*list_lock_.Pointer());
   if (incarnation_counter_ != incarnation_count_for_pool_)
     return;  // ThreadData was constructed in an earlier unit test.
   ++cleanup_count_;
-  // Only worker threads need to be retired and reused.
-  if (!worker_thread_number_) {
-    return;
-  }
-  // We must NOT do any allocations during this callback.
-  // Using the simple linked lists avoids all allocations.
-  DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
-  this->next_retired_worker_ = first_retired_worker_;
-  first_retired_worker_ = this;
+
+  // Add this ThreadData to a retired list so that it can be reused by a thread
+  // with the same name sanitized name in the future.
+  // |next_retired_thread_data_| is expected to be nullptr for a ThreadData
+  // associated with an active thread.
+  DCHECK(!next_retired_thread_data_);
+  next_retired_thread_data_ = first_retired_thread_data_;
+  first_retired_thread_data_ = this;
 }
 
 // static
@@ -728,7 +719,7 @@
       if (death_data.count > 0) {
         (*phased_snapshots)[phase->profiling_phase].tasks.push_back(
             TaskSnapshot(BirthOnThreadSnapshot(*death.first), death_data,
-                         thread_name()));
+                         sanitized_thread_name()));
       }
     }
   }
@@ -847,8 +838,6 @@
 // static
 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
   base::AutoLock lock(*list_lock_.Pointer());
-  if (worker_thread_data_creation_count_ == 0)
-    return;  // We haven't really run much, and couldn't have leaked.
 
   // TODO(jar): until this is working on XP, don't run the real test.
 #if 0
@@ -873,16 +862,14 @@
     all_thread_data_list_head_ = NULL;
     ++incarnation_counter_;
     // To be clean, break apart the retired worker list (though we leak them).
-    while (first_retired_worker_) {
-      ThreadData* worker = first_retired_worker_;
-      CHECK_GT(worker->worker_thread_number_, 0);
-      first_retired_worker_ = worker->next_retired_worker_;
-      worker->next_retired_worker_ = NULL;
+    while (first_retired_thread_data_) {
+      ThreadData* thread_data = first_retired_thread_data_;
+      first_retired_thread_data_ = thread_data->next_retired_thread_data_;
+      thread_data->next_retired_thread_data_ = nullptr;
     }
   }
 
   // Put most global static back in pristine shape.
-  worker_thread_data_creation_count_ = 0;
   cleanup_count_ = 0;
   tls_index_.Set(NULL);
   // Almost UNINITIALIZED.
@@ -914,6 +901,39 @@
   }
 }
 
+// static
+ThreadData* ThreadData::GetRetiredOrCreateThreadData(
+    const std::string& sanitized_thread_name) {
+  SCOPED_UMA_HISTOGRAM_TIMER("TrackedObjects.GetRetiredOrCreateThreadData");
+
+  {
+    base::AutoLock lock(*list_lock_.Pointer());
+    ThreadData** pcursor = &first_retired_thread_data_;
+    ThreadData* cursor = first_retired_thread_data_;
+
+    // Assuming that there aren't more than a few tens of retired ThreadData
+    // instances, this lookup should be quick compared to the thread creation
+    // time. Retired ThreadData instances cannot be stored in a map because
+    // insertions are done from OnThreadTerminationCleanup() where allocations
+    // are not allowed.
+    //
+    // Note: Test processes may have more than a few tens of retired ThreadData
+    // instances.
+    while (cursor) {
+      if (cursor->sanitized_thread_name() == sanitized_thread_name) {
+        DCHECK_EQ(*pcursor, cursor);
+        *pcursor = cursor->next_retired_thread_data_;
+        cursor->next_retired_thread_data_ = nullptr;
+        return cursor;
+      }
+      pcursor = &cursor->next_retired_thread_data_;
+      cursor = cursor->next_retired_thread_data_;
+    }
+  }
+
+  return new ThreadData(sanitized_thread_name);
+}
+
 //------------------------------------------------------------------------------
 TaskStopwatch::TaskStopwatch()
     : wallclock_duration_ms_(0),
@@ -1038,11 +1058,10 @@
 
 TaskSnapshot::TaskSnapshot(const BirthOnThreadSnapshot& birth,
                            const DeathDataSnapshot& death_data,
-                           const std::string& death_thread_name)
+                           const std::string& death_sanitized_thread_name)
     : birth(birth),
       death_data(death_data),
-      death_thread_name(death_thread_name) {
-}
+      death_sanitized_thread_name(death_sanitized_thread_name) {}
 
 TaskSnapshot::~TaskSnapshot() {
 }
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
index ca51863..36caec3 100644
--- a/base/tracked_objects.h
+++ b/base/tracked_objects.h
@@ -62,71 +62,76 @@
 // with great efficiency (i.e., copying of strings is never needed, and
 // comparisons for equality can be based on pointer comparisons).
 //
-// Next, a Births instance is created for use ONLY on the thread where this
-// instance was created.  That Births instance records (in a base class
-// BirthOnThread) references to the static data provided in a Location instance,
-// as well as a pointer specifying the thread on which the birth takes place.
-// Hence there is at most one Births instance for each Location on each thread.
-// The derived Births class contains slots for recording statistics about all
-// instances born at the same location.  Statistics currently include only the
-// count of instances constructed.
+// Next, a Births instance is constructed or found. A Births instance records
+// (in a base class BirthOnThread) references to the static data provided in a
+// Location instance, as well as a pointer to the ThreadData bound to the thread
+// on which the birth takes place (see discussion on ThreadData below). There is
+// at most one Births instance for each Location / ThreadData pair. The derived
+// Births class contains slots for recording statistics about all instances born
+// at the same location. Statistics currently include only the count of
+// instances constructed.
 //
 // Since the base class BirthOnThread contains only constant data, it can be
-// freely accessed by any thread at any time (i.e., only the statistic needs to
-// be handled carefully, and stats are updated exclusively on the birth thread).
+// freely accessed by any thread at any time. The statistics must be handled
+// more carefully; they are updated exclusively by the single thread to which
+// the ThreadData is bound at a given time.
 //
 // For Tasks, having now either constructed or found the Births instance
 // described above, a pointer to the Births instance is then recorded into the
-// PendingTask structure in MessageLoop.  This fact alone is very useful in
-// debugging, when there is a question of where an instance came from.  In
-// addition, the birth time is also recorded and used to later evaluate the
-// lifetime duration of the whole Task.  As a result of the above embedding, we
-// can find out a Task's location of birth, and thread of birth, without using
-// any locks, as all that data is constant across the life of the process.
+// PendingTask structure.  This fact alone is very useful in debugging, when
+// there is a question of where an instance came from.  In addition, the birth
+// time is also recorded and used to later evaluate the lifetime duration of the
+// whole Task.  As a result of the above embedding, we can find out a Task's
+// location of birth, and name of birth thread, without using any locks, as all
+// that data is constant across the life of the process.
 //
 // The above work *could* also be done for any other object as well by calling
 // TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() as appropriate.
 //
-// The amount of memory used in the above data structures depends on how many
-// threads there are, and how many Locations of construction there are.
-// Fortunately, we don't use memory that is the product of those two counts, but
-// rather we only need one Births instance for each thread that constructs an
-// instance at a Location.  In many cases, instances are only created on one
-// thread, so the memory utilization is actually fairly restrained.
+// The upper bound for the amount of memory used in the above data structures is
+// the product of the number of ThreadData instances and the number of
+// Locations. Fortunately, Locations are often created on a single thread and
+// the memory utilization is actually fairly restrained.
 //
 // Lastly, when an instance is deleted, the final tallies of statistics are
 // carefully accumulated.  That tallying writes into slots (members) in a
-// collection of DeathData instances.  For each birth place Location that is
-// destroyed on a thread, there is a DeathData instance to record the additional
-// death count, as well as accumulate the run-time and queue-time durations for
-// the instance as it is destroyed (dies).  By maintaining a single place to
-// aggregate this running sum *only* for the given thread, we avoid the need to
-// lock such DeathData instances. (i.e., these accumulated stats in a DeathData
-// instance are exclusively updated by the singular owning thread).
+// collection of DeathData instances.  For each Births / death ThreadData pair,
+// there is a DeathData instance to record the additional death count, as well
+// as to accumulate the run-time and queue-time durations for the instance as it
+// is destroyed (dies). Since a ThreadData is bound to at most one thread at a
+// time, there is no need to lock such DeathData instances. (i.e., these
+// accumulated stats in a DeathData instance are exclusively updated by the
+// singular owning thread).
 //
-// With the above life cycle description complete, the major remaining detail
-// is explaining how each thread maintains a list of DeathData instances, and
-// of Births instances, and is able to avoid additional (redundant/unnecessary)
-// allocations.
+// With the above life cycle description complete, the major remaining detail is
+// explaining how existing Births and DeathData instances are found to avoid
+// redundant allocations.
 //
-// Each thread maintains a list of data items specific to that thread in a
-// ThreadData instance (for that specific thread only).  The two critical items
-// are lists of DeathData and Births instances.  These lists are maintained in
-// STL maps, which are indexed by Location.  As noted earlier, we can compare
-// locations very efficiently as we consider the underlying data (file,
-// function, line) to be atoms, and hence pointer comparison is used rather than
-// (slow) string comparisons.
+// A ThreadData instance maintains maps of Births and DeathData instances. The
+// Births map is indexed by Location and the DeathData map is indexed by
+// Births*. As noted earlier, we can compare Locations very efficiently as we
+// consider the underlying data (file, function, line) to be atoms, and hence
+// pointer comparison is used rather than (slow) string comparisons.
 //
-// To provide a mechanism for iterating over all "known threads," which means
-// threads that have recorded a birth or a death, we create a singly linked list
-// of ThreadData instances.  Each such instance maintains a pointer to the next
-// one.  A static member of ThreadData provides a pointer to the first item on
-// this global list, and access via that all_thread_data_list_head_ item
-// requires the use of the list_lock_.
-// When new ThreadData instances is added to the global list, it is pre-pended,
-// which ensures that any prior acquisition of the list is valid (i.e., the
-// holder can iterate over it without fear of it changing, or the necessity of
-// using an additional lock.  Iterations are actually pretty rare (used
+// The first time that a thread calls ThreadData::InitializeThreadContext() or
+// ThreadData::Get(), a ThreadData instance is bound to it and stored in TLS. If
+// a ThreadData bound to a terminated thread with the same sanitized name (i.e.
+// name without trailing digits) as the current thread is available, it is
+// reused. Otherwise, a new ThreadData instance is instantiated. Since a
+// ThreadData is bound to at most one thread at a time, there is no need to
+// acquire a lock to access its maps. Over time, a ThreadData may be bound to
+// different threads that share the same sanitized name.
+//
+// We maintain a list of all ThreadData instances for the current process. Each
+// ThreadData instance has a pointer to the next one. A static member of
+// ThreadData provides a pointer to the first item on this global list, and
+// access via that all_thread_data_list_head_ item requires the use of the
+// list_lock_.
+//
+// When new ThreadData instances are added to the global list, they are pre-
+// pended, which ensures that any prior acquisition of the list is valid (i.e.,
+// the holder can iterate over it without fear of it changing, or the necessity
+// of using an additional lock.  Iterations are actually pretty rare (used
 // primarily for cleanup, or snapshotting data for display), so this lock has
 // very little global performance impact.
 //
@@ -173,12 +178,13 @@
 // memory reference).
 //
 // TODO(jar): We can implement a Snapshot system that *tries* to grab the
-// snapshots on the source threads *when* they have MessageLoops available
-// (worker threads don't have message loops generally, and hence gathering from
-// them will continue to be asynchronous).  We had an implementation of this in
-// the past, but the difficulty is dealing with message loops being terminated.
-// We can *try* to spam the available threads via some task runner to
-// achieve this feat, and it *might* be valuable when we are collecting data
+// snapshots on the source threads *when* they have SingleThreadTaskRunners
+// available (worker threads don't have SingleThreadTaskRunners, and hence
+// gathering from them will continue to be asynchronous).  We had an
+// implementation of this in the past, but the difficulty is dealing with
+// threads being terminated. We can *try* to post a task to threads that have a
+// SingleThreadTaskRunner and check if that succeeds (will fail if the thread
+// has been terminated). This *might* be valuable when we are collecting data
 // for upload via UMA (where correctness of data may be more significant than
 // for a single screen of about:profiler).
 //
@@ -229,7 +235,7 @@
   ~BirthOnThreadSnapshot();
 
   LocationSnapshot location;
-  std::string thread_name;
+  std::string sanitized_thread_name;
 };
 
 //------------------------------------------------------------------------------
@@ -478,14 +484,14 @@
   TaskSnapshot();
   TaskSnapshot(const BirthOnThreadSnapshot& birth,
                const DeathDataSnapshot& death_data,
-               const std::string& death_thread_name);
+               const std::string& death_sanitized_thread_name);
   ~TaskSnapshot();
 
   BirthOnThreadSnapshot birth;
   // Delta between death data for a thread for a certain profiling phase and the
   // snapshot for the pervious phase, if any.  Otherwise, just a snapshot.
   DeathDataSnapshot death_data;
-  std::string death_thread_name;
+  std::string death_sanitized_thread_name;
 };
 
 //------------------------------------------------------------------------------
@@ -521,9 +527,8 @@
 
   // Initialize the current thread context with a new instance of ThreadData.
   // This is used by all threads that have names, and should be explicitly
-  // set *before* any births on the threads have taken place.  It is generally
-  // only used by the message loop, which has a well defined thread name.
-  static void InitializeThreadContext(const std::string& suggested_name);
+  // set *before* any births on the threads have taken place.
+  static void InitializeThreadContext(const std::string& thread_name);
 
   // Using Thread Local Store, find the current instance for collecting data.
   // If an instance does not exist, construct one (and remember it for use on
@@ -581,7 +586,9 @@
   static void TallyRunInAScopedRegionIfTracking(const Births* births,
                                                 const TaskStopwatch& stopwatch);
 
-  const std::string& thread_name() const { return thread_name_; }
+  const std::string& sanitized_thread_name() const {
+    return sanitized_thread_name_;
+  }
 
   // Initializes all statics if needed (this initialization call should be made
   // while we are single threaded).
@@ -630,12 +637,7 @@
   typedef std::vector<std::pair<const Births*, DeathDataPhaseSnapshot>>
       DeathsSnapshot;
 
-  // Worker thread construction creates a name since there is none.
-  explicit ThreadData(int thread_number);
-
-  // Message loop based construction should provide a name.
-  explicit ThreadData(const std::string& suggested_name);
-
+  explicit ThreadData(const std::string& sanitized_thread_name);
   ~ThreadData();
 
   // Push this instance to the head of all_thread_data_list_head_, linking it to
@@ -699,6 +701,12 @@
   // ThreadData instances.
   static void ShutdownSingleThreadedCleanup(bool leak);
 
+  // Returns a ThreadData instance for a thread whose sanitized name is
+  // |sanitized_thread_name|. The returned instance may have been extracted from
+  // the list of retired ThreadData instances or newly allocated.
+  static ThreadData* GetRetiredOrCreateThreadData(
+      const std::string& sanitized_thread_name);
+
   // When non-null, this specifies an external function that supplies monotone
   // increasing time functcion.
   static NowFunction* now_function_for_testing_;
@@ -706,22 +714,16 @@
   // We use thread local store to identify which ThreadData to interact with.
   static base::ThreadLocalStorage::StaticSlot tls_index_;
 
-  // List of ThreadData instances for use with worker threads.  When a worker
-  // thread is done (terminated), we push it onto this list.  When a new worker
-  // thread is created, we first try to re-use a ThreadData instance from the
-  // list, and if none are available, construct a new one.
-  // This is only accessed while list_lock_ is held.
-  static ThreadData* first_retired_worker_;
+  // Linked list of ThreadData instances that were associated with threads that
+  // have been terminated and that have not been associated with a new thread
+  // since then. This is only accessed while |list_lock_| is held.
+  static ThreadData* first_retired_thread_data_;
 
   // Link to the most recently created instance (starts a null terminated list).
   // The list is traversed by about:profiler when it needs to snapshot data.
   // This is only accessed while list_lock_ is held.
   static ThreadData* all_thread_data_list_head_;
 
-  // The next available worker thread number.  This should only be accessed when
-  // the list_lock_ is held.
-  static int worker_thread_data_creation_count_;
-
   // The number of times TLS has called us back to cleanup a ThreadData
   // instance.  This is only accessed while list_lock_ is held.
   static int cleanup_count_;
@@ -742,23 +744,16 @@
 
   // Link to next instance (null terminated list).  Used to globally track all
   // registered instances (corresponds to all registered threads where we keep
-  // data).
+  // data). Only modified in the constructor.
   ThreadData* next_;
 
-  // Pointer to another ThreadData instance for a Worker-Thread that has been
-  // retired (its thread was terminated).  This value is non-NULL only for a
-  // retired ThreadData associated with a Worker-Thread.
-  ThreadData* next_retired_worker_;
+  // Pointer to another retired ThreadData instance. This value is nullptr if
+  // this is associated with an active thread.
+  ThreadData* next_retired_thread_data_;
 
-  // The name of the thread that is being recorded.  If this thread has no
-  // message_loop, then this is a worker thread, with a sequence number postfix.
-  std::string thread_name_;
-
-  // Indicate if this is a worker thread, and the ThreadData contexts should be
-  // stored in the unregistered_thread_data_pool_ when not in use.
-  // Value is zero when it is not a worker thread.  Value is a positive integer
-  // corresponding to the created thread name if it is a worker thread.
-  int worker_thread_number_;
+  // The name of the thread that is being recorded, with all trailing digits
+  // replaced with a single "*" character.
+  const std::string sanitized_thread_name_;
 
   // A map used on each thread to keep track of Births on this thread.
   // This map should only be accessed on the thread it was constructed on.
diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc
index b64e5fd..f208e3c 100644
--- a/base/tracked_objects_unittest.cc
+++ b/base/tracked_objects_unittest.cc
@@ -11,14 +11,17 @@
 
 #include <memory>
 
+#include "base/macros.h"
 #include "base/process/process_handle.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "base/tracking_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 const int kLineNumber = 1776;
 const char kFile[] = "FixedUnitTestFileName";
-const char kWorkerThreadName[] = "WorkerThread-1";
+const char kWorkerThreadName[] = "WorkerThread-*";
 const char kMainThreadName[] = "SomeMainThreadName";
 const char kStillAlive[] = "Still_Alive";
 
@@ -92,7 +95,8 @@
     EXPECT_EQ(kLineNumber,
               process_data_phase.tasks[0].birth.location.line_number);
 
-    EXPECT_EQ(birth_thread, process_data_phase.tasks[0].birth.thread_name);
+    EXPECT_EQ(birth_thread,
+              process_data_phase.tasks[0].birth.sanitized_thread_name);
 
     EXPECT_EQ(count, process_data_phase.tasks[0].death_data.count);
     EXPECT_EQ(count * run_ms,
@@ -107,7 +111,8 @@
     EXPECT_EQ(queue_ms,
               process_data_phase.tasks[0].death_data.queue_duration_sample);
 
-    EXPECT_EQ(death_thread, process_data_phase.tasks[0].death_thread_name);
+    EXPECT_EQ(death_thread,
+              process_data_phase.tasks[0].death_sanitized_thread_name);
 
     EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
   }
@@ -115,6 +120,16 @@
   // Sets time that will be returned by ThreadData::Now().
   static void SetTestTime(unsigned int test_time) { test_time_ = test_time; }
 
+  int GetNumThreadData() {
+    int num_thread_data = 0;
+    ThreadData* current = ThreadData::first();
+    while (current) {
+      ++num_thread_data;
+      current = current->next();
+    }
+    return num_thread_data;
+  }
+
  private:
   // Returns test time in milliseconds.
   static unsigned int GetTestTime() { return test_time_; }
@@ -230,7 +245,8 @@
             process_data_phase.tasks[0].birth.location.function_name);
   EXPECT_EQ(kLineNumber,
             process_data_phase.tasks[0].birth.location.line_number);
-  EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].birth.thread_name);
+  EXPECT_EQ(kWorkerThreadName,
+            process_data_phase.tasks[0].birth.sanitized_thread_name);
   EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count);
   EXPECT_EQ(time_elapsed,
             process_data_phase.tasks[0].death_data.run_duration_sum);
@@ -241,7 +257,8 @@
   EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sum);
   EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sample);
-  EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].death_thread_name);
+  EXPECT_EQ(kWorkerThreadName,
+            process_data_phase.tasks[0].death_sanitized_thread_name);
 }
 
 TEST_F(TrackedObjectsTest, DeathDataTestRecordDurations) {
@@ -659,7 +676,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase0.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
   EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -669,7 +687,8 @@
   EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].death_sanitized_thread_name);
 
   auto it1 = process_data.phased_snapshots.find(1);
   ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -683,7 +702,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase1.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
   EXPECT_EQ(10, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -693,7 +713,8 @@
   EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].death_sanitized_thread_name);
 
   EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
 }
@@ -776,7 +797,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase0.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
   EXPECT_EQ(6, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -786,7 +808,8 @@
   EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].death_sanitized_thread_name);
 
   auto it1 = process_data.phased_snapshots.find(1);
   ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -800,7 +823,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase1.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
   EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -810,7 +834,8 @@
   EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].death_sanitized_thread_name);
 
   auto it2 = process_data.phased_snapshots.find(2);
   ASSERT_TRUE(it2 != process_data.phased_snapshots.end());
@@ -824,7 +849,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase2.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase2.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase2.tasks[0].death_data.count);
   EXPECT_EQ(2, process_data_phase2.tasks[0].death_data.run_duration_sum);
@@ -834,7 +860,8 @@
   EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase2.tasks[0].death_sanitized_thread_name);
 
   EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
 }
@@ -881,7 +908,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase0.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count);
   EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum);
@@ -891,7 +919,8 @@
   EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase0.tasks[0].death_sanitized_thread_name);
 
   auto it1 = process_data.phased_snapshots.find(1);
   ASSERT_TRUE(it1 != process_data.phased_snapshots.end());
@@ -944,7 +973,8 @@
   EXPECT_EQ(kLineNumber,
             process_data_phase1.tasks[0].birth.location.line_number);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].birth.sanitized_thread_name);
 
   EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count);
   EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.run_duration_sum);
@@ -954,7 +984,8 @@
   EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_sample);
 
-  EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase1.tasks[0].death_sanitized_thread_name);
 
   EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
 }
@@ -1121,7 +1152,8 @@
             process_data_phase.tasks[0].birth.location.function_name);
   EXPECT_EQ(kLineNumber,
             process_data_phase.tasks[0].birth.location.line_number);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[0].birth.sanitized_thread_name);
   EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count);
   EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_sum);
   EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_max);
@@ -1129,13 +1161,15 @@
   EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sum);
   EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_max);
   EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sample);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[0].death_sanitized_thread_name);
   EXPECT_EQ(kFile, process_data_phase.tasks[1].birth.location.file_name);
   EXPECT_EQ(kFunction,
             process_data_phase.tasks[1].birth.location.function_name);
   EXPECT_EQ(kSecondFakeLineNumber,
             process_data_phase.tasks[1].birth.location.line_number);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[1].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[1].birth.sanitized_thread_name);
   EXPECT_EQ(1, process_data_phase.tasks[1].death_data.count);
   EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_sum);
   EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_max);
@@ -1143,7 +1177,8 @@
   EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sum);
   EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_max);
   EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sample);
-  EXPECT_EQ(kStillAlive, process_data_phase.tasks[1].death_thread_name);
+  EXPECT_EQ(kStillAlive,
+            process_data_phase.tasks[1].death_sanitized_thread_name);
   EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
 }
 
@@ -1286,7 +1321,8 @@
             process_data_phase.tasks[t0].birth.location.function_name);
   EXPECT_EQ(kLineNumber,
             process_data_phase.tasks[t0].birth.location.line_number);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[t0].birth.sanitized_thread_name);
   EXPECT_EQ(1, process_data_phase.tasks[t0].death_data.count);
   EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_sum);
   EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_max);
@@ -1294,13 +1330,15 @@
   EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sum);
   EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_max);
   EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sample);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[t0].death_sanitized_thread_name);
   EXPECT_EQ(kFile, process_data_phase.tasks[t1].birth.location.file_name);
   EXPECT_EQ(kFunction,
             process_data_phase.tasks[t1].birth.location.function_name);
   EXPECT_EQ(kSecondFakeLineNumber,
             process_data_phase.tasks[t1].birth.location.line_number);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].birth.thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[t1].birth.sanitized_thread_name);
   EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.count);
   EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_sum);
   EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_max);
@@ -1308,8 +1346,30 @@
   EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sum);
   EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_max);
   EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sample);
-  EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].death_thread_name);
+  EXPECT_EQ(kMainThreadName,
+            process_data_phase.tasks[t1].death_sanitized_thread_name);
   EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
 }
 
+// Repetitively create and stop named threads. Verify that the number of
+// instantiated ThreadData instance is equal to the number of different
+// sanitized thread names used in the test.
+TEST_F(TrackedObjectsTest, ReuseRetiredThreadData) {
+  const char* const kThreadNames[] = {"Foo%d", "Bar%d", "123Dummy%d",
+                                      "456Dummy%d", "%d"};
+  constexpr int kNumIterations = 10;
+  EXPECT_EQ(0, GetNumThreadData());
+
+  for (int i = 0; i < kNumIterations; ++i) {
+    for (const char* thread_name : kThreadNames) {
+      base::Thread thread(base::StringPrintf(thread_name, i));
+      EXPECT_TRUE(thread.Start());
+    }
+  }
+
+  // Expect one ThreadData instance for each element in |kThreadNames| and one
+  // ThreadData instance for the main thread.
+  EXPECT_EQ(static_cast<int>(arraysize(kThreadNames) + 1), GetNumThreadData());
+}
+
 }  // namespace tracked_objects
diff --git a/base/values.cc b/base/values.cc
index f00a03f..fd0bbe17 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -23,7 +23,8 @@
 
 const char* const kTypeNames[] = {"null",   "boolean", "integer",    "double",
                                   "string", "binary",  "dictionary", "list"};
-static_assert(arraysize(kTypeNames) == Value::TYPE_LIST + 1,
+static_assert(arraysize(kTypeNames) ==
+                  static_cast<size_t>(Value::Type::LIST) + 1,
               "kTypeNames Has Wrong Size");
 
 std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node);
@@ -60,10 +61,10 @@
 
 std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
   switch (node.GetType()) {
-    case Value::TYPE_LIST:
+    case Value::Type::LIST:
       return CopyListWithoutEmptyChildren(static_cast<const ListValue&>(node));
 
-    case Value::TYPE_DICTIONARY:
+    case Value::Type::DICTIONARY:
       return CopyDictionaryWithoutEmptyChildren(
           static_cast<const DictionaryValue&>(node));
 
@@ -79,14 +80,14 @@
 
 // static
 std::unique_ptr<Value> Value::CreateNullValue() {
-  return WrapUnique(new Value(TYPE_NULL));
+  return WrapUnique(new Value(Type::NONE));
 }
 
 // static
 const char* Value::GetTypeName(Value::Type type) {
-  DCHECK_GE(type, 0);
+  DCHECK_GE(static_cast<int>(type), 0);
   DCHECK_LT(static_cast<size_t>(type), arraysize(kTypeNames));
-  return kTypeNames[type];
+  return kTypeNames[static_cast<size_t>(type)];
 }
 
 bool Value::GetAsBinary(const BinaryValue** out_value) const {
@@ -136,7 +137,7 @@
 Value* Value::DeepCopy() const {
   // This method should only be getting called for null Values--all subclasses
   // need to provide their own implementation;.
-  DCHECK(IsType(TYPE_NULL));
+  DCHECK(IsType(Type::NONE));
   return CreateNullValue().release();
 }
 
@@ -147,8 +148,8 @@
 bool Value::Equals(const Value* other) const {
   // This method should only be getting called for null Values--all subclasses
   // need to provide their own implementation;.
-  DCHECK(IsType(TYPE_NULL));
-  return other->IsType(TYPE_NULL);
+  DCHECK(IsType(Type::NONE));
+  return other->IsType(Type::NONE);
 }
 
 // static
@@ -170,15 +171,13 @@
 ///////////////////// FundamentalValue ////////////////////
 
 FundamentalValue::FundamentalValue(bool in_value)
-    : Value(TYPE_BOOLEAN), boolean_value_(in_value) {
-}
+    : Value(Type::BOOLEAN), boolean_value_(in_value) {}
 
 FundamentalValue::FundamentalValue(int in_value)
-    : Value(TYPE_INTEGER), integer_value_(in_value) {
-}
+    : Value(Type::INTEGER), integer_value_(in_value) {}
 
 FundamentalValue::FundamentalValue(double in_value)
-    : Value(TYPE_DOUBLE), double_value_(in_value) {
+    : Value(Type::DOUBLE), double_value_(in_value) {
   if (!std::isfinite(double_value_)) {
     NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
                  << "values cannot be represented in JSON";
@@ -190,34 +189,34 @@
 }
 
 bool FundamentalValue::GetAsBoolean(bool* out_value) const {
-  if (out_value && IsType(TYPE_BOOLEAN))
+  if (out_value && IsType(Type::BOOLEAN))
     *out_value = boolean_value_;
-  return (IsType(TYPE_BOOLEAN));
+  return (IsType(Type::BOOLEAN));
 }
 
 bool FundamentalValue::GetAsInteger(int* out_value) const {
-  if (out_value && IsType(TYPE_INTEGER))
+  if (out_value && IsType(Type::INTEGER))
     *out_value = integer_value_;
-  return (IsType(TYPE_INTEGER));
+  return (IsType(Type::INTEGER));
 }
 
 bool FundamentalValue::GetAsDouble(double* out_value) const {
-  if (out_value && IsType(TYPE_DOUBLE))
+  if (out_value && IsType(Type::DOUBLE))
     *out_value = double_value_;
-  else if (out_value && IsType(TYPE_INTEGER))
+  else if (out_value && IsType(Type::INTEGER))
     *out_value = integer_value_;
-  return (IsType(TYPE_DOUBLE) || IsType(TYPE_INTEGER));
+  return (IsType(Type::DOUBLE) || IsType(Type::INTEGER));
 }
 
 FundamentalValue* FundamentalValue::DeepCopy() const {
   switch (GetType()) {
-    case TYPE_BOOLEAN:
+    case Type::BOOLEAN:
       return new FundamentalValue(boolean_value_);
 
-    case TYPE_INTEGER:
+    case Type::INTEGER:
       return new FundamentalValue(integer_value_);
 
-    case TYPE_DOUBLE:
+    case Type::DOUBLE:
       return new FundamentalValue(double_value_);
 
     default:
@@ -231,15 +230,15 @@
     return false;
 
   switch (GetType()) {
-    case TYPE_BOOLEAN: {
+    case Type::BOOLEAN: {
       bool lhs, rhs;
       return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs;
     }
-    case TYPE_INTEGER: {
+    case Type::INTEGER: {
       int lhs, rhs;
       return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs;
     }
-    case TYPE_DOUBLE: {
+    case Type::DOUBLE: {
       double lhs, rhs;
       return GetAsDouble(&lhs) && other->GetAsDouble(&rhs) && lhs == rhs;
     }
@@ -252,14 +251,12 @@
 ///////////////////// StringValue ////////////////////
 
 StringValue::StringValue(StringPiece in_value)
-    : Value(TYPE_STRING), value_(in_value.as_string()) {
+    : Value(Type::STRING), value_(in_value.as_string()) {
   DCHECK(IsStringUTF8(in_value));
 }
 
 StringValue::StringValue(const string16& in_value)
-    : Value(TYPE_STRING),
-      value_(UTF16ToUTF8(in_value)) {
-}
+    : Value(Type::STRING), value_(UTF16ToUTF8(in_value)) {}
 
 StringValue::~StringValue() {
 }
@@ -303,13 +300,10 @@
 
 ///////////////////// BinaryValue ////////////////////
 
-BinaryValue::BinaryValue()
-    : Value(TYPE_BINARY),
-      size_(0) {
-}
+BinaryValue::BinaryValue() : Value(Type::BINARY), size_(0) {}
 
 BinaryValue::BinaryValue(std::unique_ptr<char[]> buffer, size_t size)
-    : Value(TYPE_BINARY), buffer_(std::move(buffer)), size_(size) {}
+    : Value(Type::BINARY), buffer_(std::move(buffer)), size_(size) {}
 
 BinaryValue::~BinaryValue() {
 }
@@ -355,9 +349,7 @@
   return nullptr;
 }
 
-DictionaryValue::DictionaryValue()
-    : Value(TYPE_DICTIONARY) {
-}
+DictionaryValue::DictionaryValue() : Value(Type::DICTIONARY) {}
 
 DictionaryValue::~DictionaryValue() {
   Clear();
@@ -561,7 +553,7 @@
                                 const BinaryValue** out_value) const {
   const Value* value;
   bool result = Get(path, &value);
-  if (!result || !value->IsType(TYPE_BINARY))
+  if (!result || !value->IsType(Type::BINARY))
     return false;
 
   if (out_value)
@@ -580,7 +572,7 @@
                                     const DictionaryValue** out_value) const {
   const Value* value;
   bool result = Get(path, &value);
-  if (!result || !value->IsType(TYPE_DICTIONARY))
+  if (!result || !value->IsType(Type::DICTIONARY))
     return false;
 
   if (out_value)
@@ -600,7 +592,7 @@
                               const ListValue** out_value) const {
   const Value* value;
   bool result = Get(path, &value);
-  if (!result || !value->IsType(TYPE_LIST))
+  if (!result || !value->IsType(Type::LIST))
     return false;
 
   if (out_value)
@@ -685,7 +677,7 @@
     const DictionaryValue** out_value) const {
   const Value* value;
   bool result = GetWithoutPathExpansion(key, &value);
-  if (!result || !value->IsType(TYPE_DICTIONARY))
+  if (!result || !value->IsType(Type::DICTIONARY))
     return false;
 
   if (out_value)
@@ -709,7 +701,7 @@
     const ListValue** out_value) const {
   const Value* value;
   bool result = GetWithoutPathExpansion(key, &value);
-  if (!result || !value->IsType(TYPE_LIST))
+  if (!result || !value->IsType(Type::LIST))
     return false;
 
   if (out_value)
@@ -790,7 +782,7 @@
   for (DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); it.Advance()) {
     const Value* merge_value = &it.value();
     // Check whether we have to merge dictionaries.
-    if (merge_value->IsType(Value::TYPE_DICTIONARY)) {
+    if (merge_value->IsType(Value::Type::DICTIONARY)) {
       DictionaryValue* sub_dict;
       if (GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
         sub_dict->MergeDictionary(
@@ -865,8 +857,7 @@
   return nullptr;
 }
 
-ListValue::ListValue() : Value(TYPE_LIST) {
-}
+ListValue::ListValue() : Value(Type::LIST) {}
 
 ListValue::~ListValue() {
   Clear();
@@ -956,7 +947,7 @@
 bool ListValue::GetBinary(size_t index, const BinaryValue** out_value) const {
   const Value* value;
   bool result = Get(index, &value);
-  if (!result || !value->IsType(TYPE_BINARY))
+  if (!result || !value->IsType(Type::BINARY))
     return false;
 
   if (out_value)
@@ -975,7 +966,7 @@
                               const DictionaryValue** out_value) const {
   const Value* value;
   bool result = Get(index, &value);
-  if (!result || !value->IsType(TYPE_DICTIONARY))
+  if (!result || !value->IsType(Type::DICTIONARY))
     return false;
 
   if (out_value)
@@ -993,7 +984,7 @@
 bool ListValue::GetList(size_t index, const ListValue** out_value) const {
   const Value* value;
   bool result = Get(index, &value);
-  if (!result || !value->IsType(TYPE_LIST))
+  if (!result || !value->IsType(Type::LIST))
     return false;
 
   if (out_value)
@@ -1173,4 +1164,11 @@
   return out << json;
 }
 
+std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
+  if (static_cast<int>(type) < 0 ||
+      static_cast<size_t>(type) >= arraysize(kTypeNames))
+    return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
+  return out << Value::GetTypeName(type);
+}
+
 }  // namespace base
diff --git a/base/values.h b/base/values.h
index 21c3f5f..14a02e4 100644
--- a/base/values.h
+++ b/base/values.h
@@ -49,15 +49,15 @@
 // See the file-level comment above for more information.
 class BASE_EXPORT Value {
  public:
-  enum Type {
-    TYPE_NULL = 0,
-    TYPE_BOOLEAN,
-    TYPE_INTEGER,
-    TYPE_DOUBLE,
-    TYPE_STRING,
-    TYPE_BINARY,
-    TYPE_DICTIONARY,
-    TYPE_LIST
+  enum class Type {
+    NONE = 0,
+    BOOLEAN,
+    INTEGER,
+    DOUBLE,
+    STRING,
+    BINARY,
+    DICTIONARY,
+    LIST
     // Note: Do not add more types. See the file-level comment above for why.
   };
 
@@ -134,7 +134,7 @@
   // Overridden from Value:
   bool GetAsBoolean(bool* out_value) const override;
   bool GetAsInteger(int* out_value) const override;
-  // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+  // Values of both type Type::INTEGER and Type::DOUBLE can be obtained as
   // doubles.
   bool GetAsDouble(double* out_value) const override;
   FundamentalValue* DeepCopy() const override;
@@ -287,7 +287,7 @@
   // |out_value| is optional and will only be set if non-NULL.
   bool GetBoolean(StringPiece path, bool* out_value) const;
   bool GetInteger(StringPiece path, int* out_value) const;
-  // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+  // Values of both type Type::INTEGER and Type::DOUBLE can be obtained as
   // doubles.
   bool GetDouble(StringPiece path, double* out_value) const;
   bool GetString(StringPiece path, std::string* out_value) const;
@@ -427,7 +427,7 @@
   // |out_value| is optional and will only be set if non-NULL.
   bool GetBoolean(size_t index, bool* out_value) const;
   bool GetInteger(size_t index, int* out_value) const;
-  // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+  // Values of both type Type::INTEGER and Type::DOUBLE can be obtained as
   // doubles.
   bool GetDouble(size_t index, double* out_value) const;
   bool GetString(size_t index, std::string* out_value) const;
@@ -562,6 +562,10 @@
   return out << static_cast<const Value&>(value);
 }
 
+// Stream operator so that enum class Types can be used in log statements.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+                                     const Value::Type& type);
+
 }  // namespace base
 
 #endif  // BASE_VALUES_H_
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 61e754e..873b7b7 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -145,10 +145,10 @@
   // Test overloaded StringValue constructor.
   std::unique_ptr<Value> narrow_value(new StringValue("narrow"));
   ASSERT_TRUE(narrow_value.get());
-  ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
+  ASSERT_TRUE(narrow_value->IsType(Value::Type::STRING));
   std::unique_ptr<Value> utf16_value(new StringValue(ASCIIToUTF16("utf16")));
   ASSERT_TRUE(utf16_value.get());
-  ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
+  ASSERT_TRUE(utf16_value->IsType(Value::Type::STRING));
 
   // Test overloaded GetAsString.
   std::string narrow = "http://google.com";
@@ -179,7 +179,7 @@
 // properly deleted by modifying the value of external flag on destruction.
 class DeletionTestValue : public Value {
  public:
-  explicit DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+  explicit DeletionTestValue(bool* deletion_flag) : Value(Type::NONE) {
     Init(deletion_flag);  // Separate function so that we can use ASSERT_*
   }
 
@@ -343,7 +343,7 @@
   EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
   Value* value4;
   ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
-  EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
+  EXPECT_EQ(Value::Type::NONE, value4->GetType());
 }
 
 // Tests the deprecated version of SetWithoutPathExpansion.
@@ -367,7 +367,7 @@
   EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
   Value* value4;
   ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
-  EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
+  EXPECT_EQ(Value::Type::NONE, value4->GetType());
 }
 
 TEST(ValuesTest, DictionaryRemovePath) {
@@ -378,7 +378,7 @@
   std::unique_ptr<Value> removed_item;
   EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item));
   ASSERT_TRUE(removed_item);
-  EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_INTEGER));
+  EXPECT_TRUE(removed_item->IsType(base::Value::Type::INTEGER));
   EXPECT_FALSE(dict.HasKey("a.long.way.down"));
   EXPECT_FALSE(dict.HasKey("a.long.way"));
   EXPECT_TRUE(dict.Get("a.long.key.path", NULL));
@@ -391,7 +391,7 @@
   removed_item.reset();
   EXPECT_TRUE(dict.RemovePath("a.long.key.path", &removed_item));
   ASSERT_TRUE(removed_item);
-  EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_BOOLEAN));
+  EXPECT_TRUE(removed_item->IsType(base::Value::Type::BOOLEAN));
   EXPECT_TRUE(dict.empty());
 }
 
@@ -450,13 +450,13 @@
   ASSERT_TRUE(copy_dict->Get("null", &copy_null));
   ASSERT_TRUE(copy_null);
   ASSERT_NE(copy_null, original_null);
-  ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
+  ASSERT_TRUE(copy_null->IsType(Value::Type::NONE));
 
   Value* copy_bool = NULL;
   ASSERT_TRUE(copy_dict->Get("bool", &copy_bool));
   ASSERT_TRUE(copy_bool);
   ASSERT_NE(copy_bool, original_bool);
-  ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
+  ASSERT_TRUE(copy_bool->IsType(Value::Type::BOOLEAN));
   bool copy_bool_value = false;
   ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
   ASSERT_TRUE(copy_bool_value);
@@ -465,7 +465,7 @@
   ASSERT_TRUE(copy_dict->Get("int", &copy_int));
   ASSERT_TRUE(copy_int);
   ASSERT_NE(copy_int, original_int);
-  ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
+  ASSERT_TRUE(copy_int->IsType(Value::Type::INTEGER));
   int copy_int_value = 0;
   ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
   ASSERT_EQ(42, copy_int_value);
@@ -474,7 +474,7 @@
   ASSERT_TRUE(copy_dict->Get("double", &copy_double));
   ASSERT_TRUE(copy_double);
   ASSERT_NE(copy_double, original_double);
-  ASSERT_TRUE(copy_double->IsType(Value::TYPE_DOUBLE));
+  ASSERT_TRUE(copy_double->IsType(Value::Type::DOUBLE));
   double copy_double_value = 0;
   ASSERT_TRUE(copy_double->GetAsDouble(&copy_double_value));
   ASSERT_EQ(3.14, copy_double_value);
@@ -483,7 +483,7 @@
   ASSERT_TRUE(copy_dict->Get("string", &copy_string));
   ASSERT_TRUE(copy_string);
   ASSERT_NE(copy_string, original_string);
-  ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
+  ASSERT_TRUE(copy_string->IsType(Value::Type::STRING));
   std::string copy_string_value;
   string16 copy_string16_value;
   ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
@@ -495,7 +495,7 @@
   ASSERT_TRUE(copy_dict->Get("string16", &copy_string16));
   ASSERT_TRUE(copy_string16);
   ASSERT_NE(copy_string16, original_string16);
-  ASSERT_TRUE(copy_string16->IsType(Value::TYPE_STRING));
+  ASSERT_TRUE(copy_string16->IsType(Value::Type::STRING));
   ASSERT_TRUE(copy_string16->GetAsString(&copy_string_value));
   ASSERT_TRUE(copy_string16->GetAsString(&copy_string16_value));
   ASSERT_EQ(std::string("hello16"), copy_string_value);
@@ -505,7 +505,7 @@
   ASSERT_TRUE(copy_dict->Get("binary", &copy_binary));
   ASSERT_TRUE(copy_binary);
   ASSERT_NE(copy_binary, original_binary);
-  ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+  ASSERT_TRUE(copy_binary->IsType(Value::Type::BINARY));
   ASSERT_NE(original_binary->GetBuffer(),
     static_cast<BinaryValue*>(copy_binary)->GetBuffer());
   ASSERT_EQ(original_binary->GetSize(),
@@ -518,7 +518,7 @@
   ASSERT_TRUE(copy_dict->Get("list", &copy_value));
   ASSERT_TRUE(copy_value);
   ASSERT_NE(copy_value, original_list);
-  ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
+  ASSERT_TRUE(copy_value->IsType(Value::Type::LIST));
   ListValue* copy_list = NULL;
   ASSERT_TRUE(copy_value->GetAsList(&copy_list));
   ASSERT_TRUE(copy_list);
@@ -544,7 +544,7 @@
   ASSERT_TRUE(copy_dict->Get("dictionary", &copy_value));
   ASSERT_TRUE(copy_value);
   ASSERT_NE(copy_value, original_nested_dictionary);
-  ASSERT_TRUE(copy_value->IsType(Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(copy_value->IsType(Value::Type::DICTIONARY));
   DictionaryValue* copy_nested_dictionary = NULL;
   ASSERT_TRUE(copy_value->GetAsDictionary(&copy_nested_dictionary));
   ASSERT_TRUE(copy_nested_dictionary);
diff --git a/blimp/BUILD.gn b/blimp/BUILD.gn
index 03ca1ae..a5d02d7 100644
--- a/blimp/BUILD.gn
+++ b/blimp/BUILD.gn
@@ -70,7 +70,6 @@
   test("blimp_browsertests") {
     deps = [
       "//blimp/engine:browser_tests",
-      "//tools/xdisplaycheck",
     ]
     data = [
       "test/data/",
diff --git a/blimp/engine/app/blimp_engine_crash_keys.cc b/blimp/engine/app/blimp_engine_crash_keys.cc
index d678788..881ce4b 100644
--- a/blimp/engine/app/blimp_engine_crash_keys.cc
+++ b/blimp/engine/app/blimp_engine_crash_keys.cc
@@ -27,6 +27,7 @@
       // browser/:
       { "third-party-modules-loaded", crash_keys::kSmallSize },
       { "third-party-modules-not-loaded", crash_keys::kSmallSize },
+      { "enrolled-to-domain", crash_keys::kSmallSize },
 
       // //content crash keys
       { "bad_message_reason", crash_keys::kSmallSize },
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 67b1e06..1d0b33d 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -267,12 +267,8 @@
   <issue id="TypographyDashes" severity="Error">
     <ignore regexp="chrome/app/policy/android/values-v21/restriction_values.xml"/>
   </issue>
-  <issue id="Typos" severity="Error">
-    <ignore regexp="gen/chrome/android/chrome_strings_grd.resources.zip/values-pt-rBR/android_chrome_strings.xml"/>
-    <ignore regexp="gen/chrome/android/chrome_strings_grd.resources.zip/values-pt-rPT/android_chrome_strings.xml"/>
-    <ignore regexp="gen/chrome/android/chrome_strings_grd.resources.zip/values-tr/android_chrome_strings.xml"/>
-    <ignore regexp="gen/chrome/android/chrome_strings_grd.resources.zip/values/android_chrome_strings.xml"/>
-  </issue>
+  <!-- Typos check disabled due to lint bug: http://crbug.com/671170 -->
+  <issue id="Typos" severity="ignore" />
   <issue id="UnusedAttribute" severity="ignore"/>
   <issue id="UnusedIds" severity="ignore"/>
   <issue id="UnusedQuantity" severity="Error">
diff --git a/build/android/play_services/config.json b/build/android/play_services/config.json
index 2fdd8d2..d359f9f 100644
--- a/build/android/play_services/config.json
+++ b/build/android/play_services/config.json
@@ -11,6 +11,6 @@
     "play-services-nearby",
     "play-services-vision"
   ],
-  "version_number": "9.8.0",
+  "version_number": "10.0.1",
   "version_xml_path": "res/values/version.xml"
 }
diff --git a/build/android/play_services/google_play_services_library.zip.sha1 b/build/android/play_services/google_play_services_library.zip.sha1
index e89214f..850a4fd 100644
--- a/build/android/play_services/google_play_services_library.zip.sha1
+++ b/build/android/play_services/google_play_services_library.zip.sha1
@@ -1 +1 @@
-55ffb3367ee79437dd190b88cdeaf1c62e663870
\ No newline at end of file
+3e54555b12aa48593a31a89c6239ec35520e4477
\ No newline at end of file
diff --git a/build/android/pylib/local/device/local_device_perf_test_run.py b/build/android/pylib/local/device/local_device_perf_test_run.py
index 3398a0b..efb0001 100644
--- a/build/android/pylib/local/device/local_device_perf_test_run.py
+++ b/build/android/pylib/local/device/local_device_perf_test_run.py
@@ -241,7 +241,7 @@
             except device_errors.CommandTimeoutError:
               logging.exception(
                   'Device failed to recover after failing %s.', test)
-          tries_left = tries_left - 1
+          tries_left -= 1
 
       results.AddResult(base_test_result.BaseTestResult(test, result_type))
     return results
@@ -310,6 +310,7 @@
           result_type = self._RunSingleTest(test)
         finally:
           self._TestTearDown()
+          tries_left -= 1
       results.AddResult(base_test_result.BaseTestResult(test, result_type))
     return results
 
@@ -323,7 +324,7 @@
 
 class LocalDevicePerfTestRun(local_device_test_run.LocalDeviceTestRun):
 
-  _DEFAULT_TIMEOUT = 3 * 60 * 60  # 3 hours.
+  _DEFAULT_TIMEOUT = 5 * 60 * 60  # 5 hours.
   _CONFIG_VERSION = 1
 
   def __init__(self, env, test_instance):
diff --git a/build/android/stacktrace/stackwalker.py b/build/android/stacktrace/stackwalker.py
index ff1921b..db54354 100755
--- a/build/android/stacktrace/stackwalker.py
+++ b/build/android/stacktrace/stackwalker.py
@@ -49,12 +49,14 @@
   for line in data.splitlines():
     if current_dump is not None:
       if _MICRODUMP_END.match(line):
+        current_dump.append(line)
         all_dumps.append(current_dump)
         current_dump = None
       else:
         current_dump.append(line)
     elif _MICRODUMP_BEGIN.match(line):
       current_dump = []
+      current_dump.append(line)
   return all_dumps
 
 
@@ -107,7 +109,7 @@
     return 0
 
   symbolized_dumps = []
-  for index, micro_dump in micro_dumps:
+  for micro_dump in micro_dumps:
     symbolized_dumps.append(SymbolizeMicroDump(
         args.stackwalker_binary_path, micro_dump, args.symbols_path))
 
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index d2053e3..aacf15b 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -58,9 +58,6 @@
 config("feature_flags") {
   # Don't use deprecated V8 APIs anywhere.
   defines = [ "V8_DEPRECATION_WARNINGS" ]
-  if (enable_notifications) {
-    defines += [ "ENABLE_NOTIFICATIONS" ]
-  }
   if (enable_pdf) {
     defines += [ "ENABLE_PDF=1" ]
   }
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 27b736f3..48fcbc4e 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2245,6 +2245,11 @@
       _accumulated_deps = invoker.deps
     }
 
+    # Caller overriding build config must have valid java sources file if it has
+    # java files.
+    assert(!defined(invoker.override_build_config) ||
+           !defined(invoker.java_files) || defined(invoker.java_sources_file))
+
     assert(defined(invoker.java_files) || defined(invoker.srcjars) ||
            defined(invoker.srcjar_deps))
     _base_path = "$target_gen_dir/$target_name"
@@ -2326,7 +2331,11 @@
       _java_files += invoker.java_files
     }
     if (_java_files != []) {
-      _java_sources_file = "$_base_path.sources"
+      if (defined(invoker.java_sources_file)) {
+        _java_sources_file = invoker.java_sources_file
+      } else {
+        _java_sources_file = "$_base_path.sources"
+      }
       write_file(_java_sources_file, rebase_path(_java_files, root_build_dir))
     }
 
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 0f18914..91d84cc 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1433,6 +1433,9 @@
     _lib_dex_path = "$base_path.dex.jar"
     _rebased_lib_dex_path = rebase_path(_lib_dex_path, root_build_dir)
     _template_name = target_name
+    if (defined(invoker.java_files)) {
+      _java_sources_file = "$base_path.sources"
+    }
 
     enable_multidex =
         defined(invoker.enable_multidex) && invoker.enable_multidex
@@ -1616,6 +1619,10 @@
       build_config = _build_config
       android_manifest = _android_manifest
 
+      if (defined(_java_sources_file)) {
+        java_sources_file = _java_sources_file
+      }
+
       deps = _android_manifest_deps
 
       if (defined(invoker.deps)) {
@@ -1782,6 +1789,9 @@
       jar_path = _jar_path
       dex_path = _lib_dex_path
       emma_never_instrument = _emma_never_instrument
+      if (defined(_java_sources_file)) {
+        java_sources_file = _java_sources_file
+      }
 
       if (defined(invoker.deps)) {
         deps += invoker.deps
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 9426ef18..fae310a 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -441,13 +441,16 @@
   if (!is_debug && (allow_posix_link_time_opt || is_cfi) && !is_nacl) {
     if (use_thin_lto) {
       cflags += [ "-flto=thin" ]
-      ldflags += [
-        "-flto=thin",
+      ldflags += [ "-flto=thin" ]
 
-        # Limit the parallelism to the sweet spot on most of the machines.
-        # As of now, ThinLTO does not scale beyond 16 cores anyway.
-        "-Wl,-plugin-opt,jobs=16",
-      ]
+      # Limit the parallelism to avoid too agressive competition between
+      # linker jobs. This is still suboptimal to a potential dynamic
+      # resource allocation scheme, but should be good enough.
+      if (use_lld) {
+        ldflags += [ "-Wl,--thinlto-jobs=8" ]
+      } else {
+        ldflags += [ "-Wl,-plugin-opt,jobs=8" ]
+      }
     } else {
       # Note: ThinLTO does not currently have this feature implemented
       # For Full LTO, it provides a measurable runtime speedup of Chrome.
@@ -1278,12 +1281,12 @@
       } else {
         common_optimize_on_ldflags += [ "/LTCG" ]  # Link-time code generation.
       }
-    }
-    if (full_wpo_on_official) {
-      if (use_incremental_wpo) {
-        arflags = [ "/LTCG:INCREMENTAL" ]
-      } else {
-        arflags = [ "/LTCG" ]
+      if (full_wpo_on_official) {
+        if (use_incremental_wpo) {
+          arflags = [ "/LTCG:INCREMENTAL" ]
+        } else {
+          arflags = [ "/LTCG" ]
+        }
       }
     }
   }
@@ -1545,7 +1548,6 @@
 # Full symbols.
 config("symbols") {
   if (is_win) {
-    import("//build/toolchain/goma.gni")
     cflags = [ "/Zi" ]  # Produce PDB file, no edit and continue.
 
     if (is_win_fastlink) {
diff --git a/build/config/features.gni b/build/config/features.gni
index 9866c77..9f663a57 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -79,20 +79,6 @@
 
 # Additional dependent variables -----------------------------------------------
 
-# The seccomp-bpf sandbox is only supported on five architectures
-# currently.
-# Do not disable seccomp_bpf anywhere without talking to
-# security@chromium.org!
-use_seccomp_bpf =
-    (is_linux || is_android) &&
-    (current_cpu == "x86" || current_cpu == "x64" || current_cpu == "arm" ||
-     current_cpu == "arm64" || current_cpu == "mipsel")
-
-# Enable notifications everywhere except iOS.
-enable_notifications = !is_ios
-
-enable_web_speech = !is_android && !is_ios
-
 enable_task_manager = !is_ios && !is_android
 
 enable_themes = !is_android && !is_ios
diff --git a/build/get_landmines.py b/build/get_landmines.py
index bb87459..9fe984e8 100755
--- a/build/get_landmines.py
+++ b/build/get_landmines.py
@@ -45,7 +45,7 @@
     print ('Improper dependency for create_nmf.py broke in r240802, '
            'fixed in r240860.')
   if (platform() == 'win' and gyp_msvs_version().startswith('2015')):
-    print 'Switch to VS2015 Update 3'
+    print 'Switch to VS2015 Update 3, 14393 SDK'
   print 'Need to clobber everything due to an IDL change in r154579 (blink)'
   print 'Need to clobber everything due to gen file moves in r175513 (Blink)'
   if (platform() != 'ios'):
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index cb37eb6d..dcc4108 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -26,10 +26,49 @@
   exit 1
 }
 
+# Waits for the user to press 'Y' or 'N'. Either uppercase of lowercase is
+# accepted. Returns 0 for 'Y' and 1 for 'N'. If an optional parameter has
+# been provided to yes_no(), the function also accepts RETURN as a user input.
+# The parameter specifies the exit code that should be returned in that case.
+# The function will echo the user's selection followed by a newline character.
+# Users can abort the function by pressing CTRL-C. This will call "exit 1".
+yes_no() {
+  if [ 0 -ne "${do_default-0}" ] ; then
+    [ $1 -eq 0 ] && echo "Y" || echo "N"
+    return $1
+  fi
+  local c
+  while :; do
+    c="$(trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT
+         stty -echo iuclc -icanon 2>/dev/null
+         dd count=1 bs=1 2>/dev/null | od -An -tx1)"
+    case "$c" in
+      " 0a") if [ -n "$1" ]; then
+               [ $1 -eq 0 ] && echo "Y" || echo "N"
+               return $1
+             fi
+             ;;
+      " 79") echo "Y"
+             return 0
+             ;;
+      " 6e") echo "N"
+             return 1
+             ;;
+      "")    echo "Aborted" >&2
+             exit 1
+             ;;
+      *)     # The user pressed an unrecognized key. As we are not echoing
+             # any incorrect user input, alert the user by ringing the bell.
+             (tput bel) 2>/dev/null
+             ;;
+    esac
+  done
+}
+
 # Checks whether a particular package is available in the repos.
 # USAGE: $ package_exists <package name>
 package_exists() {
-  apt-cache pkgnames | grep -x "$1" > /dev/null 2>&1
+  [ ! -z "`apt-cache search --names-only "$1"`" ]
 }
 
 # These default to on because (some) bots need them and it keeps things
@@ -72,12 +111,12 @@
 fi
 
 lsb_release=$(lsb_release --codename --short)
-ubuntu_codenames="(precise|trusty|utopic|vivid|wily|xenial)"
+supported_releases="(precise|trusty|utopic|vivid|wily|xenial|jessie)"
 if [ 0 -eq "${do_unsupported-0}" ] && [ 0 -eq "${do_quick_check-0}" ] ; then
-  if [[ ! $lsb_release =~ $ubuntu_codenames ]]; then
+  if [[ ! $lsb_release =~ $supported_releases ]]; then
     echo "ERROR: Only Ubuntu 12.04 (precise), 14.04 (trusty), " \
-      "14.10 (utopic), 15.04 (vivid), 15.10 (wily) and 16.04 (xenial) " \
-      "are currently supported" >&2
+      "14.10 (utopic), 15.04 (vivid), 15.10 (wily) and 16.04 (xenial), " \
+      "and Debian 8 (jessie) are currently supported" >&2
     exit 1
   fi
 
@@ -99,17 +138,16 @@
 # Packages needed for development
 dev_list="bison cdbs curl dpkg-dev elfutils devscripts fakeroot
           flex fonts-ipafont fonts-thai-tlwg g++ git-core git-svn gperf
-          language-pack-da language-pack-fr language-pack-he
-          language-pack-zh-hant libasound2-dev libbrlapi-dev libav-tools
-          libbz2-dev libcairo2-dev libcap-dev libcups2-dev libcurl4-gnutls-dev
-          libdrm-dev libelf-dev libffi-dev libgconf2-dev libglib2.0-dev
-          libglu1-mesa-dev libgnome-keyring-dev libgtk2.0-dev libkrb5-dev
-          libnspr4-dev libnss3-dev libpam0g-dev libpci-dev libpulse-dev
-          libsctp-dev libspeechd-dev libsqlite3-dev libssl-dev libudev-dev
-          libwww-perl libxslt1-dev libxss-dev libxt-dev libxtst-dev openbox
-          patch perl pkg-config python python-cherrypy3 python-crypto
-          python-dev python-numpy python-opencv python-openssl python-psutil
-          python-yaml rpm ruby subversion ttf-dejavu-core wdiff xcompmgr zip
+          libasound2-dev libbrlapi-dev libav-tools libbz2-dev libcairo2-dev
+          libcap-dev libcups2-dev libcurl4-gnutls-dev libdrm-dev libelf-dev
+          libffi-dev libgconf2-dev libglib2.0-dev libglu1-mesa-dev
+          libgnome-keyring-dev libgtk2.0-dev libkrb5-dev libnspr4-dev
+          libnss3-dev libpam0g-dev libpci-dev libpulse-dev libsctp-dev
+          libspeechd-dev libsqlite3-dev libssl-dev libudev-dev libwww-perl
+          libxslt1-dev libxss-dev libxt-dev libxtst-dev openbox patch perl
+          pkg-config python python-cherrypy3 python-crypto python-dev
+          python-numpy python-opencv python-openssl python-psutil python-yaml
+          rpm ruby subversion ttf-dejavu-core wdiff xcompmgr zip
           $chromeos_dev_list"
 
 # 64-bit systems need a minimum set of 32-bit compat packages for the pre-built
@@ -152,9 +190,45 @@
 lib32_list="linux-libc-dev:i386"
 
 # arm cross toolchain packages needed to build chrome on armhf
-arm_list="libc6-dev-armhf-cross
-          linux-libc-dev-armhf-cross
-          g++-arm-linux-gnueabihf"
+EM_REPO="deb http://emdebian.org/tools/debian/ jessie main"
+EM_SOURCE=$(cat <<EOF
+# Repo added by Chromium $0
+${EM_REPO}
+# deb-src http://emdebian.org/tools/debian/ jessie main
+EOF
+)
+EM_ARCHIVE_KEY_FINGER="084C6C6F39159EDB67969AA87DE089671804772E"
+GPP_ARM_PACKAGE="g++-arm-linux-gnueabihf"
+case $lsb_release in
+  "jessie")
+    eval $(apt-config shell APT_SOURCESDIR 'Dir::Etc::sourceparts/d')
+    CROSSTOOLS_LIST="${APT_SOURCESDIR}/crosstools.list"
+    arm_list="libc6-dev:armhf
+              linux-libc-dev:armhf"
+    if test "$do_inst_arm" = "1"; then
+      if $(dpkg-query -W ${GPP_ARM_PACKAGE} &>/dev/null); then
+        arm_list+=" ${GPP_ARM_PACKAGE}"
+      else
+        echo "The Debian Cross-toolchains repository is necessary to"
+        echo "cross-compile Chromium for arm."
+        echo -n "Do you want me to add it for you (y/N) "
+        if yes_no 1; then
+          gpg --keyserver pgp.mit.edu --recv-keys ${EM_ARCHIVE_KEY_FINGER}
+          gpg -a --export ${EM_ARCHIVE_KEY_FINGER} | sudo apt-key add -
+          if ! grep "^${EM_REPO}" "${CROSSTOOLS_LIST}" &>/dev/null; then
+            echo "${EM_SOURCE}" | sudo tee -a "${CROSSTOOLS_LIST}" >/dev/null
+          fi
+          arm_list+=" ${GPP_ARM_PACKAGE}"
+        fi
+      fi
+    fi
+    ;;
+  *)
+    arm_list="libc6-dev-armhf-cross
+              linux-libc-dev-armhf-cross
+              ${GPP_ARM_PACKAGE}"
+    ;;
+esac
 
 # Work around for dependency issue Ubuntu/Trusty: http://crbug.com/435056
 case $lsb_release in
@@ -210,11 +284,6 @@
 nacl_list="${nacl_list} libgl1-mesa-glx${mesa_variant}:i386"
 
 # Some package names have changed over time
-if package_exists ttf-mscorefonts-installer; then
-  dev_list="${dev_list} ttf-mscorefonts-installer"
-else
-  dev_list="${dev_list} msttcorefonts"
-fi
 if package_exists libnspr4-dbg; then
   dbg_list="${dbg_list} libnspr4-dbg libnss3-dbg"
   lib_list="${lib_list} libnspr4 libnss3"
@@ -257,6 +326,14 @@
 else
   dev_list="${dev_list} php5-cgi libapache2-mod-php5"
 fi
+# ttf-mscorefonts-installer is in the Debian contrib repo, which has
+# dependencies on non-free software.  Install it only if the user has already
+# enabled contrib.
+if package_exists ttf-mscorefonts-installer; then
+  dev_list="${dev_list} ttf-mscorefonts-installer"
+elif package_exists msttcorefonts; then
+  dev_list="${dev_list} msttcorefonts"
+fi
 # Ubuntu 16.04 has this package deleted.
 if package_exists ttf-kochi-gothic; then
   dev_list="${dev_list} ttf-kochi-gothic"
@@ -285,45 +362,6 @@
   lib32_list="$lib32_list $multilib_package"
 fi
 
-# Waits for the user to press 'Y' or 'N'. Either uppercase of lowercase is
-# accepted. Returns 0 for 'Y' and 1 for 'N'. If an optional parameter has
-# been provided to yes_no(), the function also accepts RETURN as a user input.
-# The parameter specifies the exit code that should be returned in that case.
-# The function will echo the user's selection followed by a newline character.
-# Users can abort the function by pressing CTRL-C. This will call "exit 1".
-yes_no() {
-  if [ 0 -ne "${do_default-0}" ] ; then
-    [ $1 -eq 0 ] && echo "Y" || echo "N"
-    return $1
-  fi
-  local c
-  while :; do
-    c="$(trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT
-         stty -echo iuclc -icanon 2>/dev/null
-         dd count=1 bs=1 2>/dev/null | od -An -tx1)"
-    case "$c" in
-      " 0a") if [ -n "$1" ]; then
-               [ $1 -eq 0 ] && echo "Y" || echo "N"
-               return $1
-             fi
-             ;;
-      " 79") echo "Y"
-             return 0
-             ;;
-      " 6e") echo "N"
-             return 1
-             ;;
-      "")    echo "Aborted" >&2
-             exit 1
-             ;;
-      *)     # The user pressed an unrecognized key. As we are not echoing
-             # any incorrect user input, alert the user by ringing the bell.
-             (tput bel) 2>/dev/null
-             ;;
-    esac
-  done
-}
-
 if test "$do_inst_syms" = "" && test 0 -eq ${do_quick_check-0}
 then
   echo "This script installs all tools and libraries needed to build Chromium."
@@ -404,6 +442,9 @@
   if [[ ! $lsb_release =~ (precise) ]]; then
     sudo dpkg --add-architecture i386
   fi
+  if [[ $lsb_release = "jessie" ]]; then
+    sudo dpkg --add-architecture armhf
+  fi
 fi
 sudo apt-get update
 
@@ -498,3 +539,23 @@
 else
   echo "Skipping symbolic links for NaCl."
 fi
+
+echo "Installing locales."
+CHROMIUM_LOCALES="da_DK.UTF-8 fr_FR.UTF-8 he_IL.UTF-8 zh_TW.UTF-8"
+LOCALE_GEN=/etc/locale.gen
+if [ -e ${LOCALE_GEN} ]; then
+  OLD_LOCALE_GEN="$(cat /etc/locale.gen)"
+  for CHROMIUM_LOCALE in ${CHROMIUM_LOCALES}; do
+    sudo sed -i "s/^# ${CHROMIUM_LOCALE}/${CHROMIUM_LOCALE}/" ${LOCALE_GEN}
+  done
+  # Regenerating locales can take a while, so only do it if we need to.
+  if (echo "${OLD_LOCALE_GEN}" | cmp -s ${LOCALE_GEN}); then
+    echo "Locales already up-to-date."
+  else
+    sudo locale-gen
+  fi
+else
+  for CHROMIUM_LOCALE in ${CHROMIUM_LOCALES}; do
+    sudo locale-gen ${CHROMIUM_LOCALE}
+  done
+fi
diff --git a/build/linux/sysroot_scripts/install-sysroot.py b/build/linux/sysroot_scripts/install-sysroot.py
index 109d553..13b0891 100755
--- a/build/linux/sysroot_scripts/install-sysroot.py
+++ b/build/linux/sysroot_scripts/install-sysroot.py
@@ -25,7 +25,7 @@
 import shutil
 import subprocess
 import sys
-import urllib
+import urllib2
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 sys.path.append(os.path.dirname(os.path.dirname(SCRIPT_DIR)))
@@ -248,7 +248,9 @@
   sys.stderr.flush()
   for _ in range(3):
     try:
-      urllib.urlretrieve(url, tarball)
+      response = urllib2.urlopen(url)
+      with open(tarball, "wb") as f:
+        f.write(response.read())
       break
     except:
       pass
diff --git a/build/sanitizers/BUILD.gn b/build/sanitizers/BUILD.gn
deleted file mode 100644
index 473200f..0000000
--- a/build/sanitizers/BUILD.gn
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/clang/clang.gni")
-
-if (is_clang) {
-  copy("copy_llvm_symbolizer") {
-    if (is_win) {
-      sources = [
-        "$clang_base_path/bin/llvm-symbolizer.exe",
-      ]
-      outputs = [
-        "$root_out_dir/llvm-symbolizer.exe",
-      ]
-    } else {
-      sources = [
-        "$clang_base_path/bin/llvm-symbolizer",
-      ]
-      outputs = [
-        "$root_out_dir/llvm-symbolizer",
-      ]
-    }
-  }
-}
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index efccc86..dbccde90 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -245,9 +245,6 @@
 // https://crbug.com/430533
 "race:TileTaskGraphRunner::Run\n"
 
-// https://crbug.com/448203
-"race:blink::RemoteFrame::detach\n"
-
 // Lock inversion in third party code, won't fix.
 // https://crbug.com/455638
 "deadlock:dbus::Bus::ShutdownAndBlock\n"
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn
index f35977d2..ce631b6c 100644
--- a/build/secondary/third_party/android_tools/BUILD.gn
+++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -177,7 +177,7 @@
 
 # TODO(dgn): Use the POM files instead of hardcoding the dependencies.
 gms_path = "$default_extras_android_sdk_root/extras/google/m2repository/com/google/android/gms"
-gms_version = "9.8.0"
+gms_version = "10.0.1"
 
 android_aar_prebuilt("google_play_services_basement_java") {
   deps = [
diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py
index 281298c..edc6256c 100644
--- a/build/toolchain/win/tool_wrapper.py
+++ b/build/toolchain/win/tool_wrapper.py
@@ -133,12 +133,13 @@
     # non-Windows don't do that there.
     link = subprocess.Popen(args, shell=sys.platform == 'win32', env=env,
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    out, _ = link.communicate()
-    for line in out.splitlines():
+    # Read output one line at a time as it shows up to avoid OOM failures when
+    # GBs of output is produced.
+    for line in link.stdout:
       if (not line.startswith('   Creating library ') and
           not line.startswith('Generating code') and
           not line.startswith('Finished generating code')):
-        print line
+        print line,
     return link.returncode
 
   def ExecLinkWithManifests(self, arch, embed_manifest, out, ldcmd, resname,
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index c768a8a7..3edf1ff 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -305,12 +305,8 @@
   """Load a list of SHA1s corresponding to the toolchains that we want installed
   to build with."""
   if GetVisualStudioVersion() == '2015':
-    if bool(int(os.environ.get('DEPOT_TOOLS_WIN_SDK_PRERELEASE', '0'))):
-      # Update 3 final with patches with 10.0.14393 SDK.
-      return ['d3cb0e37bdd120ad0ac4650b674b09e81be45616']
-    else:
-      # Update 3 final with patches with 10.0.10586.0 SDK.
-      return ['d5dc33b15d1b2c086f2f6632e2fd15882f80dbd3']
+    # Update 3 final with patches with 10.0.14393.0 SDK.
+    return ['d3cb0e37bdd120ad0ac4650b674b09e81be45616']
   else:
     return ['03a4e939cd325d6bc5216af41b92d02dda1366a6']
 
diff --git a/build_overrides/v8.gni b/build_overrides/v8.gni
index 08eb3c1..59132e3c 100644
--- a/build_overrides/v8.gni
+++ b/build_overrides/v8.gni
@@ -5,11 +5,20 @@
 # V8 extras
 # Adding V8 extras files requires API owners review
 
-v8_extra_library_files = [
+# This list is for files that export symbols that are used in other extras
+# files. Putting them here causes them to be executed first during snapshot
+# creation.
+_v8_extras_dependencies =
+    [ "//third_party/WebKit/Source/core/streams/CommonStrings.js" ]
+
+_v8_extras = [
   "//third_party/WebKit/Source/core/streams/ByteLengthQueuingStrategy.js",
   "//third_party/WebKit/Source/core/streams/CountQueuingStrategy.js",
   "//third_party/WebKit/Source/core/streams/ReadableStream.js",
 ]
+
+v8_extra_library_files = _v8_extras_dependencies + _v8_extras
+
 v8_experimental_extra_library_files =
     [ "//third_party/WebKit/Source/core/streams/WritableStream.js" ]
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 7485f094..8e6b7e5 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -320,8 +320,6 @@
     "quads/render_pass.h",
     "quads/render_pass_draw_quad.cc",
     "quads/render_pass_draw_quad.h",
-    "quads/render_pass_id.cc",
-    "quads/render_pass_id.h",
     "quads/shared_quad_state.cc",
     "quads/shared_quad_state.h",
     "quads/solid_color_draw_quad.cc",
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index cc68c34..5d30516a 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -157,7 +157,7 @@
     element_animations->SetAnimationHost(nullptr);
   }
 
-  DeactivateAnimationPlayer(player);
+  RemoveFromTicking(player);
 }
 
 void AnimationHost::SetMutatorHostClient(MutatorHostClient* client) {
@@ -270,44 +270,44 @@
   return supports_scroll_animations_;
 }
 
-bool AnimationHost::NeedsAnimateLayers() const {
-  return !active_players_.empty();
+bool AnimationHost::NeedsTickAnimations() const {
+  return !ticking_players_.empty();
 }
 
 bool AnimationHost::ActivateAnimations() {
-  if (!NeedsAnimateLayers())
+  if (!NeedsTickAnimations())
     return false;
 
   TRACE_EVENT0("cc", "AnimationHost::ActivateAnimations");
-  PlayersList active_active_players_copy = active_players_;
-  for (auto& it : active_active_players_copy)
+  PlayersList ticking_players_copy = ticking_players_;
+  for (auto& it : ticking_players_copy)
     it->ActivateAnimations();
 
   return true;
 }
 
-bool AnimationHost::AnimateLayers(base::TimeTicks monotonic_time) {
-  if (!NeedsAnimateLayers())
+bool AnimationHost::TickAnimations(base::TimeTicks monotonic_time) {
+  if (!NeedsTickAnimations())
     return false;
 
-  TRACE_EVENT0("cc", "AnimationHost::AnimateLayers");
-  PlayersList active_active_players_copy = active_players_;
-  for (auto& it : active_active_players_copy)
-    it->Animate(monotonic_time);
+  TRACE_EVENT0("cc", "AnimationHost::TickAnimations");
+  PlayersList ticking_players_copy = ticking_players_;
+  for (auto& it : ticking_players_copy)
+    it->Tick(monotonic_time);
 
   return true;
 }
 
 bool AnimationHost::UpdateAnimationState(bool start_ready_animations,
                                          MutatorEvents* mutator_events) {
-  if (!NeedsAnimateLayers())
+  if (!NeedsTickAnimations())
     return false;
 
   auto animation_events = static_cast<AnimationEvents*>(mutator_events);
 
   TRACE_EVENT0("cc", "AnimationHost::UpdateAnimationState");
-  PlayersList active_active_players_copy = active_players_;
-  for (auto& it : active_active_players_copy)
+  PlayersList ticking_players_copy = ticking_players_;
+  for (auto& it : ticking_players_copy)
     it->UpdateState(start_ready_animations, animation_events);
 
   return true;
@@ -326,9 +326,8 @@
        ++event_index) {
     ElementId element_id = events->events_[event_index].element_id;
 
-    // Use the map of all ElementAnimations, not just active ones, since
-    // non-active ElementAnimations may still receive events for impl-only
-    // animations.
+    // Use the map of all ElementAnimations, not just ticking players, since
+    // non-ticking Players may still receive events for impl-only animations.
     const ElementToAnimationsMap& all_element_animations =
         element_to_animations_map_;
     auto iter = all_element_animations.find(element_id);
@@ -520,9 +519,9 @@
   return element_animations ? element_animations->HasAnyAnimation() : false;
 }
 
-bool AnimationHost::HasActiveAnimationForTesting(ElementId element_id) const {
+bool AnimationHost::HasTickingAnimationForTesting(ElementId element_id) const {
   auto element_animations = GetElementAnimationsForElementId(element_id);
-  return element_animations ? element_animations->HasActiveAnimation() : false;
+  return element_animations ? element_animations->HasTickingAnimation() : false;
 }
 
 void AnimationHost::ImplOnlyScrollAnimationCreate(
@@ -557,28 +556,26 @@
   return scroll_offset_animations_impl_->ScrollAnimationAbort(needs_completion);
 }
 
-void AnimationHost::ActivateAnimationPlayer(
-    scoped_refptr<AnimationPlayer> player) {
-  DCHECK(std::find(active_players_.begin(), active_players_.end(), player) ==
-         active_players_.end());
-  active_players_.push_back(player);
+void AnimationHost::AddToTicking(scoped_refptr<AnimationPlayer> player) {
+  DCHECK(std::find(ticking_players_.begin(), ticking_players_.end(), player) ==
+         ticking_players_.end());
+  ticking_players_.push_back(player);
 }
 
-void AnimationHost::DeactivateAnimationPlayer(
-    scoped_refptr<AnimationPlayer> player) {
+void AnimationHost::RemoveFromTicking(scoped_refptr<AnimationPlayer> player) {
   auto to_erase =
-      std::find(active_players_.begin(), active_players_.end(), player);
-  if (to_erase != active_players_.end())
-    active_players_.erase(to_erase);
+      std::find(ticking_players_.begin(), ticking_players_.end(), player);
+  if (to_erase != ticking_players_.end())
+    ticking_players_.erase(to_erase);
 }
 
-const AnimationHost::PlayersList& AnimationHost::active_players_for_testing()
+const AnimationHost::PlayersList& AnimationHost::ticking_players_for_testing()
     const {
-  return active_players_;
+  return ticking_players_;
 }
 
 const AnimationHost::ElementToAnimationsMap&
-AnimationHost::all_element_animations_for_testing() const {
+AnimationHost::element_animations_for_testing() const {
   return element_to_animations_map_;
 }
 
diff --git a/cc/animation/animation_host.h b/cc/animation/animation_host.h
index e91b55e..f1ee294 100644
--- a/cc/animation/animation_host.h
+++ b/cc/animation/animation_host.h
@@ -95,10 +95,10 @@
   void PushPropertiesTo(MutatorHost* host_impl) override;
 
   void SetSupportsScrollAnimations(bool supports_scroll_animations) override;
-  bool NeedsAnimateLayers() const override;
+  bool NeedsTickAnimations() const override;
 
   bool ActivateAnimations() override;
-  bool AnimateLayers(base::TimeTicks monotonic_time) override;
+  bool TickAnimations(base::TimeTicks monotonic_time) override;
   bool UpdateAnimationState(bool start_ready_animations,
                             MutatorEvents* events) override;
 
@@ -153,7 +153,7 @@
                            float* start_scale) const override;
 
   bool HasAnyAnimation(ElementId element_id) const override;
-  bool HasActiveAnimationForTesting(ElementId element_id) const override;
+  bool HasTickingAnimationForTesting(ElementId element_id) const override;
 
   void ImplOnlyScrollAnimationCreate(ElementId element_id,
                                      const gfx::ScrollOffset& target_offset,
@@ -171,16 +171,16 @@
   // This should only be called from the main thread.
   ScrollOffsetAnimations& scroll_offset_animations() const;
 
-  // Registers the given animation player as active. An active animation
-  // player is one that has a running animation that needs to be ticked.
-  void ActivateAnimationPlayer(scoped_refptr<AnimationPlayer> player);
+  // Registers the given animation player as ticking. A ticking animation
+  // player is one that has a running animation.
+  void AddToTicking(scoped_refptr<AnimationPlayer> player);
 
   // Unregisters the given animation player. When this happens, the
-  // animation player will no longer be ticked (since it's not active).
-  void DeactivateAnimationPlayer(scoped_refptr<AnimationPlayer> player);
+  // animation player will no longer be ticked.
+  void RemoveFromTicking(scoped_refptr<AnimationPlayer> player);
 
-  const PlayersList& active_players_for_testing() const;
-  const ElementToAnimationsMap& all_element_animations_for_testing() const;
+  const PlayersList& ticking_players_for_testing() const;
+  const ElementToAnimationsMap& element_animations_for_testing() const;
 
  private:
   explicit AnimationHost(ThreadInstance thread_instance);
@@ -192,7 +192,7 @@
   void EraseTimeline(scoped_refptr<AnimationTimeline> timeline);
 
   ElementToAnimationsMap element_to_animations_map_;
-  PlayersList active_players_;
+  PlayersList ticking_players_;
 
   // A list of all timelines which this host owns.
   using IdToTimelineMap =
diff --git a/cc/animation/animation_player.cc b/cc/animation/animation_player.cc
index a4a1c6b1..af11dfc 100644
--- a/cc/animation/animation_player.cc
+++ b/cc/animation/animation_player.cc
@@ -27,7 +27,7 @@
       id_(id),
       needs_push_properties_(false),
       needs_to_start_animations_(false),
-      is_active_(false),
+      is_ticking_(false),
       scroll_offset_animation_was_interrupted_(false) {
   DCHECK(id_);
 }
@@ -139,7 +139,7 @@
   SetNeedsCommit();
   needs_to_start_animations_ = true;
 
-  UpdateActivation(ActivationType::NORMAL);
+  UpdateTickingState(UpdateTickingType::NORMAL);
   element_animations_->UpdateClientAnimationState();
 }
 
@@ -183,7 +183,7 @@
   animations_.erase(animations_to_remove, animations_.end());
 
   if (element_animations_) {
-    UpdateActivation(ActivationType::NORMAL);
+    UpdateTickingState(UpdateTickingType::NORMAL);
     if (animation_removed)
       element_animations_->UpdateClientAnimationState();
     SetNeedsCommit();
@@ -262,10 +262,10 @@
 
   PushPropertiesToImplThread(player_impl);
 
-  player_impl->UpdateActivation(ActivationType::NORMAL);
+  player_impl->UpdateTickingState(UpdateTickingType::NORMAL);
 }
 
-void AnimationPlayer::Animate(base::TimeTicks monotonic_time) {
+void AnimationPlayer::Tick(base::TimeTicks monotonic_time) {
   DCHECK(!monotonic_time.is_null());
   DCHECK(element_animations_);
 
@@ -305,32 +305,32 @@
     }
   }
 
-  UpdateActivation(ActivationType::NORMAL);
+  UpdateTickingState(UpdateTickingType::NORMAL);
 }
 
-void AnimationPlayer::UpdateActivation(ActivationType type) {
-  bool force = type == ActivationType::FORCE;
+void AnimationPlayer::UpdateTickingState(UpdateTickingType type) {
+  bool force = type == UpdateTickingType::FORCE;
   if (animation_host_) {
-    bool was_active = is_active_;
-    is_active_ = HasNonDeletedAnimation();
+    bool was_ticking = is_ticking_;
+    is_ticking_ = HasNonDeletedAnimation();
 
     bool has_element_in_any_list =
         element_animations_->has_element_in_any_list();
 
-    if (is_active_ && ((!was_active && has_element_in_any_list) || force)) {
-      animation_host_->ActivateAnimationPlayer(this);
-    } else if (!is_active_ && (was_active || force)) {
-      Deactivate();
+    if (is_ticking_ && ((!was_ticking && has_element_in_any_list) || force)) {
+      animation_host_->AddToTicking(this);
+    } else if (!is_ticking_ && (was_ticking || force)) {
+      RemoveFromTicking();
     }
   }
 }
 
-void AnimationPlayer::Deactivate() {
+void AnimationPlayer::RemoveFromTicking() {
   DCHECK(animation_host_);
   // Resetting last_tick_time_ here ensures that calling ::UpdateState
   // before ::Animate doesn't start an animation.
   last_tick_time_ = base::TimeTicks();
-  animation_host_->DeactivateAnimationPlayer(this);
+  animation_host_->RemoveFromTicking(this);
 }
 
 bool AnimationPlayer::NotifyAnimationStarted(const AnimationEvent& event) {
@@ -432,7 +432,7 @@
   element_animations_->SetNeedsPushProperties();
 }
 
-bool AnimationPlayer::HasActiveAnimation() const {
+bool AnimationPlayer::HasTickingAnimation() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (!animations_[i]->is_finished())
       return true;
@@ -823,7 +823,7 @@
     element_animations_->UpdateClientAnimationState();
 
   scroll_offset_animation_was_interrupted_ = false;
-  UpdateActivation(ActivationType::NORMAL);
+  UpdateTickingState(UpdateTickingType::NORMAL);
 }
 
 bool AnimationPlayer::HasFilterAnimationThatInflatesBounds() const {
diff --git a/cc/animation/animation_player.h b/cc/animation/animation_player.h
index 62c9d54..174c6bc 100644
--- a/cc/animation/animation_player.h
+++ b/cc/animation/animation_player.h
@@ -76,11 +76,11 @@
 
   void PushPropertiesTo(AnimationPlayer* player_impl);
 
-  void Animate(base::TimeTicks monotonic_time);
+  void Tick(base::TimeTicks monotonic_time);
   void UpdateState(bool start_ready_animations, AnimationEvents* events);
 
-  void UpdateActivation(ActivationType type);
-  void Deactivate();
+  void UpdateTickingState(UpdateTickingType type);
+  void RemoveFromTicking();
 
   // AnimationDelegate routing.
   bool NotifyAnimationStarted(const AnimationEvent& event);
@@ -92,7 +92,7 @@
 
   // Returns true if there are any animations that have neither finished nor
   // aborted.
-  bool HasActiveAnimation() const;
+  bool HasTickingAnimation() const;
 
   // Returns true if there are any animations at all to process.
   bool has_any_animation() const { return !animations_.empty(); }
@@ -149,11 +149,11 @@
   bool HasElementInActiveList() const;
   gfx::ScrollOffset ScrollOffsetForAnimation() const;
 
-  // Returns the active animation animating the given property that is either
+  // Returns the animation animating the given property that is either
   // running, or is next to run, if such an animation exists.
   Animation* GetAnimation(TargetProperty::Type target_property) const;
 
-  // Returns the active animation for the given unique animation id.
+  // Returns animation for the given unique animation id.
   Animation* GetAnimationById(int animation_id) const;
 
   void GetPropertyAnimationState(PropertyAnimationState* pending_state,
@@ -214,7 +214,7 @@
   bool needs_to_start_animations_;
 
   // This is used to ensure that we don't spam the animation host.
-  bool is_active_;
+  bool is_ticking_;
 
   bool scroll_offset_animation_was_interrupted_;
 
diff --git a/cc/animation/animation_player_unittest.cc b/cc/animation/animation_player_unittest.cc
index c6d1a431..4f234a0 100644
--- a/cc/animation/animation_player_unittest.cc
+++ b/cc/animation/animation_player_unittest.cc
@@ -167,11 +167,11 @@
 
   base::TimeTicks time;
   time += base::TimeDelta::FromSecondsD(0.1);
-  AnimateLayersTransferEvents(time, 3u);
+  TickAnimationsTransferEvents(time, 3u);
   EXPECT_TRUE(CheckPlayerTimelineNeedsPushProperties(false));
 
   time += base::TimeDelta::FromSecondsD(duration);
-  AnimateLayersTransferEvents(time, 3u);
+  TickAnimationsTransferEvents(time, 3u);
   EXPECT_TRUE(CheckPlayerTimelineNeedsPushProperties(false));
 
   client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
@@ -248,7 +248,7 @@
 
   base::TimeTicks time;
   time += base::TimeDelta::FromSecondsD(0.1);
-  AnimateLayersTransferEvents(time, 2u);
+  TickAnimationsTransferEvents(time, 2u);
 
   EXPECT_TRUE(delegate1.started());
   EXPECT_FALSE(delegate1.finished());
@@ -260,7 +260,7 @@
   EXPECT_FALSE(player2->needs_push_properties());
 
   time += base::TimeDelta::FromSecondsD(duration);
-  AnimateLayersTransferEvents(time, 2u);
+  TickAnimationsTransferEvents(time, 2u);
 
   EXPECT_TRUE(delegate1.finished());
   EXPECT_TRUE(delegate2.finished());
@@ -333,10 +333,10 @@
 
   base::TimeTicks time;
   time += base::TimeDelta::FromSecondsD(0.1);
-  AnimateLayersTransferEvents(time, 1u);
+  TickAnimationsTransferEvents(time, 1u);
 
   time += base::TimeDelta::FromSecondsD(duration);
-  AnimateLayersTransferEvents(time, 1u);
+  TickAnimationsTransferEvents(time, 1u);
 
   client_.ExpectOpacityPropertyMutated(element_id_, ElementListType::ACTIVE,
                                        end_opacity);
diff --git a/cc/animation/element_animations.cc b/cc/animation/element_animations.cc
index c9705c7..9365c4f1 100644
--- a/cc/animation/element_animations.cc
+++ b/cc/animation/element_animations.cc
@@ -47,7 +47,7 @@
   DCHECK(element_id_);
   DCHECK(animation_host_);
 
-  UpdateActivation(ActivationType::FORCE);
+  UpdatePlayersTickingState(UpdateTickingType::FORCE);
 
   DCHECK(animation_host_->mutator_host_client());
   if (animation_host_->mutator_host_client()->IsElementInList(
@@ -90,7 +90,7 @@
   }
   set_has_element_in_pending_list(false);
 
-  Deactivate();
+  RemovePlayersFromTicking();
 }
 
 void ElementAnimations::ElementRegistered(ElementId element_id,
@@ -98,7 +98,7 @@
   DCHECK_EQ(element_id_, element_id);
 
   if (!has_element_in_any_list())
-    UpdateActivation(ActivationType::FORCE);
+    UpdatePlayersTickingState(UpdateTickingType::FORCE);
 
   if (list_type == ElementListType::ACTIVE)
     set_has_element_in_active_list(true);
@@ -115,7 +115,7 @@
     set_has_element_in_pending_list(false);
 
   if (!has_element_in_any_list())
-    Deactivate();
+    RemovePlayersFromTicking();
 }
 
 void ElementAnimations::AddPlayer(AnimationPlayer* player) {
@@ -148,14 +148,15 @@
   needs_update_impl_client_state_ = false;
 }
 
-void ElementAnimations::UpdateActivation(ActivationType activation_type) const {
+void ElementAnimations::UpdatePlayersTickingState(
+    UpdateTickingType update_ticking_type) const {
   for (auto& player : players_list_)
-    player.UpdateActivation(activation_type);
+    player.UpdateTickingState(update_ticking_type);
 }
 
-void ElementAnimations::Deactivate() const {
+void ElementAnimations::RemovePlayersFromTicking() const {
   for (auto& player : players_list_)
-    player.Deactivate();
+    player.RemoveFromTicking();
 }
 
 void ElementAnimations::NotifyAnimationStarted(const AnimationEvent& event) {
@@ -395,9 +396,9 @@
   }
 }
 
-bool ElementAnimations::HasActiveAnimation() const {
+bool ElementAnimations::HasTickingAnimation() const {
   for (auto& player : players_list_) {
-    if (player.HasActiveAnimation())
+    if (player.HasTickingAnimation())
       return true;
   }
 
diff --git a/cc/animation/element_animations.h b/cc/animation/element_animations.h
index 8aa851f6..81ce7af 100644
--- a/cc/animation/element_animations.h
+++ b/cc/animation/element_animations.h
@@ -30,7 +30,7 @@
 enum class ElementListType;
 struct AnimationEvent;
 
-enum class ActivationType { NORMAL, FORCE };
+enum class UpdateTickingType { NORMAL, FORCE };
 
 // An ElementAnimations owns a list of all AnimationPlayers, attached to
 // the element.
@@ -70,7 +70,7 @@
 
   // Returns true if there are any animations that have neither finished nor
   // aborted.
-  bool HasActiveAnimation() const;
+  bool HasTickingAnimation() const;
 
   // Returns true if there are any animations at all to process.
   bool HasAnyAnimation() const;
@@ -175,8 +175,8 @@
 
   static TargetProperties GetPropertiesMaskForAnimationState();
 
-  void UpdateActivation(ActivationType activation_type) const;
-  void Deactivate() const;
+  void UpdatePlayersTickingState(UpdateTickingType update_ticking_type) const;
+  void RemovePlayersFromTicking() const;
 
   PlayersList players_list_;
   AnimationHost* animation_host_;
diff --git a/cc/animation/element_animations_unittest.cc b/cc/animation/element_animations_unittest.cc
index c1ebd4d6..9aec920 100644
--- a/cc/animation/element_animations_unittest.cc
+++ b/cc/animation/element_animations_unittest.cc
@@ -332,7 +332,7 @@
   player2_impl->ActivateAnimations();
   EXPECT_TRUE(player2_impl->GetAnimationById(animation_id));
 
-  player2_impl->Animate(kInitialTickTime);
+  player2_impl->Tick(kInitialTickTime);
 
   auto events = CreateEventsForTesting();
   player2_impl->UpdateState(true, events.get());
@@ -364,7 +364,7 @@
             player_impl_->GetAnimationById(animation_id)->run_state());
 
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Synchronize the start times.
@@ -374,7 +374,7 @@
             player_impl_->GetAnimationById(animation_id)->start_time());
 
   // Start the animation on the main thread. Should not affect the start time.
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_impl_->UpdateState(true, nullptr);
   EXPECT_EQ(player_->GetAnimationById(animation_id)->start_time(),
             player_impl_->GetAnimationById(animation_id)->start_time());
@@ -399,7 +399,7 @@
             player_impl_->GetAnimationById(animation_id)->run_state());
 
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Synchronize the start times.
@@ -411,7 +411,7 @@
             player_impl_->GetAnimationById(animation_id)->start_time());
 
   // Start the animation on the main thread. Should not affect the start time.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, nullptr);
   EXPECT_EQ(start_time, player_->GetAnimationById(animation_id)->start_time());
   EXPECT_EQ(player_->GetAnimationById(animation_id)->start_time(),
@@ -429,67 +429,67 @@
 
   auto events = CreateEventsForTesting();
 
-  EXPECT_EQ(1u, host->all_element_animations_for_testing().size());
-  EXPECT_EQ(1u, host_impl->all_element_animations_for_testing().size());
+  EXPECT_EQ(1u, host->element_animations_for_testing().size());
+  EXPECT_EQ(1u, host_impl->element_animations_for_testing().size());
 
   // Initially, both animationss should be inactive.
-  EXPECT_EQ(0u, host->active_players_for_testing().size());
-  EXPECT_EQ(0u, host_impl->active_players_for_testing().size());
+  EXPECT_EQ(0u, host->ticking_players_for_testing().size());
+  EXPECT_EQ(0u, host_impl->ticking_players_for_testing().size());
 
   AddOpacityTransitionToPlayer(player_.get(), 1, 0, 1, false);
   // The main thread animations should now be active.
-  EXPECT_EQ(1u, host->active_players_for_testing().size());
+  EXPECT_EQ(1u, host->ticking_players_for_testing().size());
 
   PushProperties();
   player_impl_->ActivateAnimations();
   // Both animationss should now be active.
-  EXPECT_EQ(1u, host->active_players_for_testing().size());
-  EXPECT_EQ(1u, host_impl->active_players_for_testing().size());
+  EXPECT_EQ(1u, host->ticking_players_for_testing().size());
+  EXPECT_EQ(1u, host_impl->ticking_players_for_testing().size());
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->events_.size());
   player_->NotifyAnimationStarted(events->events_[0]);
 
-  EXPECT_EQ(1u, host->active_players_for_testing().size());
-  EXPECT_EQ(1u, host_impl->active_players_for_testing().size());
+  EXPECT_EQ(1u, host->ticking_players_for_testing().size());
+  EXPECT_EQ(1u, host_impl->ticking_players_for_testing().size());
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, nullptr);
-  EXPECT_EQ(1u, host->active_players_for_testing().size());
+  EXPECT_EQ(1u, host->ticking_players_for_testing().size());
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
   EXPECT_EQ(Animation::FINISHED,
             player_->GetAnimation(TargetProperty::OPACITY)->run_state());
-  EXPECT_EQ(1u, host->active_players_for_testing().size());
+  EXPECT_EQ(1u, host->ticking_players_for_testing().size());
 
   events = CreateEventsForTesting();
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   player_impl_->UpdateState(true, events.get());
 
   EXPECT_EQ(Animation::WAITING_FOR_DELETION,
             player_impl_->GetAnimation(TargetProperty::OPACITY)->run_state());
   // The impl thread animations should have de-activated.
-  EXPECT_EQ(0u, host_impl->active_players_for_testing().size());
+  EXPECT_EQ(0u, host_impl->ticking_players_for_testing().size());
 
   EXPECT_EQ(1u, events->events_.size());
   player_->NotifyAnimationFinished(events->events_[0]);
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   player_->UpdateState(true, nullptr);
 
   EXPECT_EQ(Animation::WAITING_FOR_DELETION,
             player_->GetAnimation(TargetProperty::OPACITY)->run_state());
   // The main thread animations should have de-activated.
-  EXPECT_EQ(0u, host->active_players_for_testing().size());
+  EXPECT_EQ(0u, host->ticking_players_for_testing().size());
 
   PushProperties();
   player_impl_->ActivateAnimations();
   EXPECT_FALSE(player_->has_any_animation());
   EXPECT_FALSE(player_impl_->has_any_animation());
-  EXPECT_EQ(0u, host->active_players_for_testing().size());
-  EXPECT_EQ(0u, host_impl->active_players_for_testing().size());
+  EXPECT_EQ(0u, host->ticking_players_for_testing().size());
+  EXPECT_EQ(0u, host_impl->ticking_players_for_testing().size());
 }
 
 TEST_F(ElementAnimationsTest, SyncPause) {
@@ -519,11 +519,11 @@
 
   // Start the animations on each animations.
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(time);
+  player_impl_->Tick(time);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->events_.size());
 
-  player_->Animate(time);
+  player_->Tick(time);
   player_->UpdateState(true, nullptr);
   player_->NotifyAnimationStarted(events->events_[0]);
 
@@ -553,8 +553,8 @@
 
   // Advance time so it stays within the first range.
   time += TimeDelta::FromMilliseconds(10);
-  player_->Animate(time);
-  player_impl_->Animate(time);
+  player_->Tick(time);
+  player_impl_->Tick(time);
 
   EXPECT_EQ(Animation::PAUSED,
             player_impl_->GetAnimationById(animation_id)->run_state());
@@ -585,7 +585,7 @@
             player_impl_->GetAnimationById(animation_id)->run_state());
 
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->events_.size());
   EXPECT_EQ(AnimationEvent::STARTED, events->events_[0].type);
@@ -595,14 +595,14 @@
 
   // Complete animation on impl thread.
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromSeconds(1));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromSeconds(1));
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->events_.size());
   EXPECT_EQ(AnimationEvent::FINISHED, events->events_[0].type);
 
   player_->NotifyAnimationFinished(events->events_[0]);
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromSeconds(2));
+  player_->Tick(kInitialTickTime + TimeDelta::FromSeconds(2));
   player_->UpdateState(true, nullptr);
 
   PushProperties();
@@ -621,7 +621,7 @@
   auto events = CreateEventsForTesting();
 
   AddOpacityTransitionToPlayer(player_.get(), 1.0, 0.0f, 1.0f, false);
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, nullptr);
   EXPECT_TRUE(player_->needs_push_properties());
 
@@ -633,7 +633,7 @@
 
   player_impl_->ActivateAnimations();
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_impl_->UpdateState(true, events.get());
 
   // There should be a STARTED event for the animation.
@@ -641,14 +641,14 @@
   EXPECT_EQ(AnimationEvent::STARTED, events->events_[0].type);
   player_->NotifyAnimationStarted(events->events_[0]);
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
 
   EXPECT_FALSE(host_->needs_push_properties());
   EXPECT_FALSE(host_impl_->needs_push_properties());
 
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   EXPECT_TRUE(host_impl_->needs_push_properties());
@@ -663,7 +663,7 @@
 
   player_->NotifyAnimationFinished(events->events_[0]);
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   player_->UpdateState(true, nullptr);
   EXPECT_TRUE(host_->needs_push_properties());
 
@@ -709,18 +709,18 @@
   EXPECT_FALSE(player_->needs_to_start_animations());
   player_->AddAnimation(std::move(to_add));
   EXPECT_TRUE(player_->needs_to_start_animations());
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   EXPECT_FALSE(player_->needs_to_start_animations());
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
   // A non-impl-only animation should not generate property updates.
   const AnimationEvent* event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 }
@@ -747,16 +747,16 @@
       Animation::Create(std::move(curve), 1, 0, TargetProperty::FILTER));
   player_->AddAnimation(std::move(animation));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(start_filters,
             client_.GetFilters(element_id_, ElementListType::ACTIVE));
   // A non-impl-only animation should not generate property updates.
   const AnimationEvent* event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(1u,
             client_.GetFilters(element_id_, ElementListType::ACTIVE).size());
@@ -765,11 +765,11 @@
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(end_filters,
             client_.GetFilters(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 }
@@ -804,15 +804,15 @@
                           ->curve()
                           ->Duration());
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, nullptr);
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(initial_value,
             client_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_impl_->HasActiveAnimation());
+  EXPECT_TRUE(player_impl_->HasTickingAnimation());
   EXPECT_EQ(initial_value,
             client_impl_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
   // Scroll offset animations should not generate property updates.
@@ -820,14 +820,14 @@
   EXPECT_FALSE(event);
 
   player_->NotifyAnimationStarted(events->events_[0]);
-  player_->Animate(kInitialTickTime + duration / 2);
+  player_->Tick(kInitialTickTime + duration / 2);
   player_->UpdateState(true, nullptr);
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_VECTOR2DF_EQ(
       gfx::Vector2dF(200.f, 250.f),
       client_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + duration / 2);
+  player_impl_->Tick(kInitialTickTime + duration / 2);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(
       gfx::Vector2dF(200.f, 250.f),
@@ -835,19 +835,19 @@
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_impl_->Animate(kInitialTickTime + duration);
+  player_impl_->Tick(kInitialTickTime + duration);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(target_value, client_impl_.GetScrollOffset(
                                         element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_impl_->HasActiveAnimation());
+  EXPECT_FALSE(player_impl_->HasTickingAnimation());
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_->Animate(kInitialTickTime + duration);
+  player_->Tick(kInitialTickTime + duration);
   player_->UpdateState(true, nullptr);
   EXPECT_VECTOR2DF_EQ(target_value, client_.GetScrollOffset(
                                         element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 TEST_F(ElementAnimationsTest, ScrollOffsetTransitionOnImplOnly) {
@@ -871,9 +871,9 @@
   animation->set_is_impl_only(true);
   player_impl_->AddAnimation(std::move(animation));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_impl_->HasActiveAnimation());
+  EXPECT_TRUE(player_impl_->HasTickingAnimation());
   EXPECT_EQ(initial_value,
             client_impl_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
   // Scroll offset animations should not generate property updates.
@@ -883,7 +883,7 @@
   TimeDelta duration = TimeDelta::FromMicroseconds(
       duration_in_seconds * base::Time::kMicrosecondsPerSecond);
 
-  player_impl_->Animate(kInitialTickTime + duration / 2);
+  player_impl_->Tick(kInitialTickTime + duration / 2);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(
       gfx::Vector2dF(200.f, 250.f),
@@ -891,11 +891,11 @@
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_impl_->Animate(kInitialTickTime + duration);
+  player_impl_->Tick(kInitialTickTime + duration);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(target_value, client_impl_.GetScrollOffset(
                                         element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_impl_->HasActiveAnimation());
+  EXPECT_FALSE(player_impl_->HasTickingAnimation());
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 }
@@ -918,13 +918,13 @@
 
   // Calling UpdateState after Animate should promote the animation to running
   // state.
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(
       Animation::RUNNING,
       player_impl_->GetAnimation(TargetProperty::SCROLL_OFFSET)->run_state());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(
       Animation::WAITING_FOR_DELETION,
@@ -942,7 +942,7 @@
       Animation::WAITING_FOR_TARGET_AVAILABILITY,
       player_impl_->GetAnimation(TargetProperty::SCROLL_OFFSET)->run_state());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   EXPECT_EQ(
@@ -990,18 +990,18 @@
                           ->curve()
                           ->Duration());
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, nullptr);
 
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(initial_value,
             client_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
   EXPECT_EQ(gfx::ScrollOffset(), client_impl_.GetScrollOffset(
                                      element_id_, ElementListType::PENDING));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
 
-  EXPECT_TRUE(player_impl_->HasActiveAnimation());
+  EXPECT_TRUE(player_impl_->HasTickingAnimation());
   EXPECT_EQ(initial_value, client_impl_.GetScrollOffset(
                                element_id_, ElementListType::PENDING));
 
@@ -1015,14 +1015,14 @@
   EXPECT_FALSE(event);
 
   player_->NotifyAnimationStarted(events->events_[0]);
-  player_->Animate(kInitialTickTime + duration / 2);
+  player_->Tick(kInitialTickTime + duration / 2);
   player_->UpdateState(true, nullptr);
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_VECTOR2DF_EQ(
       gfx::Vector2dF(400.f, 150.f),
       client_.GetScrollOffset(element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + duration / 2);
+  player_impl_->Tick(kInitialTickTime + duration / 2);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(
       gfx::Vector2dF(400.f, 150.f),
@@ -1030,19 +1030,19 @@
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_impl_->Animate(kInitialTickTime + duration);
+  player_impl_->Tick(kInitialTickTime + duration);
   player_impl_->UpdateState(true, events.get());
   EXPECT_VECTOR2DF_EQ(target_value, client_impl_.GetScrollOffset(
                                         element_id_, ElementListType::PENDING));
-  EXPECT_FALSE(player_impl_->HasActiveAnimation());
+  EXPECT_FALSE(player_impl_->HasTickingAnimation());
   event = GetMostRecentPropertyUpdateEvent(events.get());
   EXPECT_FALSE(event);
 
-  player_->Animate(kInitialTickTime + duration);
+  player_->Tick(kInitialTickTime + duration);
   player_->UpdateState(true, nullptr);
   EXPECT_VECTOR2DF_EQ(target_value, client_.GetScrollOffset(
                                         element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 TEST_F(ElementAnimationsTest, ScrollOffsetRemovalClearsScrollDelta) {
@@ -1166,14 +1166,14 @@
   EXPECT_FALSE(delegate.started());
   EXPECT_FALSE(delegate.finished());
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   EXPECT_TRUE(delegate.started());
   EXPECT_FALSE(delegate.finished());
 
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime + duration);
+  player_impl_->Tick(kInitialTickTime + duration);
   EXPECT_EQ(duration, player_impl_->GetAnimation(TargetProperty::SCROLL_OFFSET)
                           ->curve()
                           ->Duration());
@@ -1206,7 +1206,7 @@
             player_impl_->GetAnimationById(animation_id)->run_state());
 
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Synchronize the start times.
@@ -1234,27 +1234,27 @@
   // We should pause at the first keyframe indefinitely waiting for that
   // animation to start.
   player_->AddAnimation(std::move(to_add));
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // Send the synchronized start time.
   player_->NotifyAnimationStarted(AnimationEvent(
       AnimationEvent::STARTED, ElementId(), 1, TargetProperty::OPACITY,
       kInitialTickTime + TimeDelta::FromMilliseconds(2000)));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(5000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(5000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Tests that two queued animations affecting the same property run in sequence.
@@ -1275,26 +1275,26 @@
 
   EXPECT_TRUE(player_->needs_to_start_animations());
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
 
   // The second animation still needs to be started.
   EXPECT_TRUE(player_->needs_to_start_animations());
 
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   EXPECT_TRUE(player_->needs_to_start_animations());
   player_->UpdateState(true, events.get());
   EXPECT_FALSE(player_->needs_to_start_animations());
 
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Tests interrupting a transition with another transition.
@@ -1307,9 +1307,9 @@
   player_->AddAnimation(CreateAnimation(
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)),
       1, TargetProperty::OPACITY));
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   std::unique_ptr<Animation> to_add(CreateAnimation(
@@ -1320,14 +1320,14 @@
 
   // Since the previous animation was aborted, the new animation should start
   // right in this call to animate.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Tests scheduling two animations to run together when only one property is
@@ -1348,20 +1348,20 @@
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)),
       2, TargetProperty::OPACITY));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_TRUE(player_->HasActiveAnimation());
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  EXPECT_TRUE(player_->HasTickingAnimation());
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
   // Should not have started the float transition yet.
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
   // The float animation should have started at time 1 and should be done.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Tests scheduling two animations to run together with different lengths and
@@ -1384,25 +1384,25 @@
       2, TargetProperty::OPACITY));
 
   // Animations with id 1 should both start now.
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
   // The opacity animation should have finished at time 1, but the group
   // of animations with id 1 don't finish until time 2 because of the length
   // of the transform animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, events.get());
   // Should not have started the float transition yet.
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // The second opacity animation should start at time 2 and should be done by
   // time 3.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Test that a looping animation loops and for the correct number of iterations.
@@ -1418,33 +1418,33 @@
   to_add->set_iterations(3);
   player_->AddAnimation(std::move(to_add));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1250));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1250));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.25f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1750));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1750));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2250));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2250));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.25f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2750));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2750));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   player_->UpdateState(true, events.get());
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // Just be extra sure.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
   player_->UpdateState(true, events.get());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
@@ -1462,35 +1462,33 @@
   to_add->set_iterations(-1);
   player_->AddAnimation(std::move(to_add));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1250));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1250));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.25f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1750));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1750));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
-  player_->Animate(kInitialTickTime +
-                   TimeDelta::FromMilliseconds(1073741824250));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1073741824250));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.25f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime +
-                   TimeDelta::FromMilliseconds(1073741824750));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1073741824750));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   EXPECT_TRUE(player_->GetAnimation(TargetProperty::OPACITY));
   player_->GetAnimation(TargetProperty::OPACITY)
       ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(750));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
 
@@ -1505,13 +1503,13 @@
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)),
       1, TargetProperty::OPACITY));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   EXPECT_TRUE(player_->GetAnimation(TargetProperty::OPACITY));
@@ -1519,23 +1517,23 @@
       ->SetRunState(Animation::PAUSED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(500));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   EXPECT_TRUE(player_->GetAnimation(TargetProperty::OPACITY));
   player_->GetAnimation(TargetProperty::OPACITY)
       ->SetRunState(Animation::RUNNING,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024250));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1024250));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1024500));
   player_->UpdateState(true, events.get());
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
 
@@ -1556,26 +1554,26 @@
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.75f)),
       3, 2, TargetProperty::OPACITY));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.5f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   EXPECT_TRUE(player_->GetAnimationById(animation_id));
   player_->GetAnimationById(animation_id)
       ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1000));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(!player_->HasActiveAnimation());
+  EXPECT_TRUE(!player_->HasTickingAnimation());
   EXPECT_EQ(0.75f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
 
@@ -1592,9 +1590,9 @@
   to_add->set_needs_synchronized_start_time(true);
   player_->AddAnimation(std::move(to_add));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   Animation* active_animation = player_->GetAnimation(TargetProperty::OPACITY);
   EXPECT_TRUE(active_animation);
   EXPECT_TRUE(active_animation->needs_synchronized_start_time());
@@ -1622,7 +1620,7 @@
   first_animation->set_is_controlling_instance_for_test(true);
   player_->AddAnimation(std::move(first_animation));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, events.get());
 
   std::unique_ptr<Animation> second_animation(CreateAnimation(
@@ -1632,9 +1630,9 @@
   player_->AddAnimation(std::move(second_animation));
 
   // Animate but don't UpdateState.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   events = CreateEventsForTesting();
   player_->UpdateState(true, events.get());
 
@@ -1643,15 +1641,15 @@
   EXPECT_NE(events->events_[0].type, events->events_[1].type);
 
   // The float transition should still be at its starting point.
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   player_->UpdateState(true, events.get());
 
   // The float tranisition should now be done.
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
-  EXPECT_FALSE(player_->HasActiveAnimation());
+  EXPECT_FALSE(player_->HasTickingAnimation());
 }
 
 // Tests that an animation animations with only a pending observer gets ticked
@@ -1669,7 +1667,7 @@
 
   // Without an observer, the animation shouldn't progress to the STARTING
   // state.
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->events_.size());
   EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
@@ -1680,7 +1678,7 @@
   // With only a pending observer, the animation should progress to the
   // STARTING state and get ticked at its starting point, but should not
   // progress to RUNNING.
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->events_.size());
   EXPECT_EQ(Animation::STARTING,
@@ -1690,7 +1688,7 @@
 
   // Even when already in the STARTING state, the animation should stay
   // there, and shouldn't be ticked past its starting point.
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->events_.size());
   EXPECT_EQ(Animation::STARTING,
@@ -1702,7 +1700,7 @@
 
   // Now that an active observer has been added, the animation should still
   // initially tick at its starting point, but should now progress to RUNNING.
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->events_.size());
   EXPECT_EQ(Animation::RUNNING,
@@ -1713,7 +1711,7 @@
             client_impl_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // The animation should now tick past its starting point.
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(3500));
   EXPECT_NE(0.5f,
             client_impl_.GetOpacity(element_id_, ElementListType::PENDING));
   EXPECT_NE(0.5f,
@@ -1814,9 +1812,9 @@
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)),
       5, 5, TargetProperty::OPACITY));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, nullptr);
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
 
   EXPECT_EQ(Animation::FINISHED, player_->GetAnimationById(1)->run_state());
@@ -1858,7 +1856,7 @@
             player_->GetAnimation(TargetProperty::OPACITY)->run_state());
   EXPECT_TRUE(host_->needs_push_properties());
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   player_->UpdateState(true, nullptr);
   EXPECT_EQ(Animation::ABORTED,
             player_->GetAnimation(TargetProperty::OPACITY)->run_state());
@@ -1900,7 +1898,7 @@
   EXPECT_TRUE(player_impl_->needs_push_properties());
 
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_TRUE(host_impl_->needs_push_properties());
   EXPECT_EQ(1u, events->events_.size());
@@ -1913,7 +1911,7 @@
             player_->GetAnimation(TargetProperty::OPACITY)->run_state());
   EXPECT_TRUE(delegate.aborted());
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_->UpdateState(true, nullptr);
   EXPECT_TRUE(host_->needs_push_properties());
   EXPECT_EQ(Animation::WAITING_FOR_DELETION,
@@ -1966,7 +1964,7 @@
       player_impl_->GetAnimation(TargetProperty::SCROLL_OFFSET)->run_state());
 
   auto events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_TRUE(delegate_impl.finished());
   EXPECT_TRUE(host_impl_->needs_push_properties());
@@ -2021,7 +2019,7 @@
   second_animation->set_is_controlling_instance_for_test(true);
   player_impl_->AddAnimation(std::move(second_animation));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Both animations should have started.
@@ -2030,7 +2028,7 @@
   EXPECT_EQ(AnimationEvent::STARTED, events->events_[1].type);
 
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
 
   // The opacity animation should be finished, but should not have generated
@@ -2040,7 +2038,7 @@
             player_impl_->GetAnimationById(2)->run_state());
   EXPECT_EQ(Animation::RUNNING, player_impl_->GetAnimationById(1)->run_state());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   // Both animations should have generated FINISHED events.
@@ -2072,7 +2070,7 @@
   second_animation->set_is_controlling_instance_for_test(true);
   player_impl_->AddAnimation(std::move(second_animation));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Both animations should have started.
@@ -2083,7 +2081,7 @@
   player_impl_->AbortAnimations(TargetProperty::OPACITY, false);
 
   events = CreateEventsForTesting();
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
 
   // We should have exactly 2 events: a FINISHED event for the tranform
@@ -2517,7 +2515,7 @@
   EXPECT_FALSE(
       player_impl_->GetAnimationById(animation_id)->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   EXPECT_FALSE(player_impl_->needs_to_start_animations());
   player_impl_->UpdateState(true, events.get());
 
@@ -2538,7 +2536,7 @@
   EXPECT_TRUE(
       player_impl_->GetAnimationById(animation_id)->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
 
   // Since the animation has been activated, it should have reached the
@@ -2571,7 +2569,7 @@
   EXPECT_FALSE(
       player_impl_->GetAnimationById(animation_id)->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
 
   // Since the animation hasn't been activated, only the pending observer
   // should have been ticked.
@@ -2592,7 +2590,7 @@
   EXPECT_EQ(Animation::RUNNING,
             player_impl_->GetAnimationById(animation_id)->run_state());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
 
   // Both elements should have been ticked.
   EXPECT_EQ(0.75f,
@@ -2648,14 +2646,14 @@
   EXPECT_TRUE(client_impl_.GetTransformIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
   events->events_.clear();
 
   // Finish the animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
   EXPECT_FALSE(client_.GetHasPotentialTransformAnimation(
       element_id_, ElementListType::ACTIVE));
@@ -2674,7 +2672,7 @@
   EXPECT_TRUE(client_impl_.GetTransformIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_FALSE(client_impl_.GetHasPotentialTransformAnimation(
       element_id_, ElementListType::PENDING));
@@ -2716,7 +2714,7 @@
   EXPECT_TRUE(client_impl_.GetTransformIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -2768,7 +2766,7 @@
   EXPECT_TRUE(client_impl_.GetTransformIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -2784,7 +2782,7 @@
   EXPECT_FALSE(client_impl_.GetTransformIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
   player_impl_->UpdateState(true, events.get());
 
   element_animations_->NotifyAnimationAborted(events->events_[0]);
@@ -2865,14 +2863,14 @@
   EXPECT_TRUE(client_impl_.GetOpacityIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
   events->events_.clear();
 
   // Finish the animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
   EXPECT_FALSE(client_.GetHasPotentialOpacityAnimation(
       element_id_, ElementListType::ACTIVE));
@@ -2891,7 +2889,7 @@
   EXPECT_TRUE(client_impl_.GetOpacityIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_FALSE(client_impl_.GetHasPotentialOpacityAnimation(
       element_id_, ElementListType::PENDING));
@@ -2926,7 +2924,7 @@
   EXPECT_TRUE(client_impl_.GetOpacityIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -2978,7 +2976,7 @@
   EXPECT_TRUE(client_impl_.GetOpacityIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -2994,7 +2992,7 @@
   EXPECT_FALSE(client_impl_.GetOpacityIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
   player_impl_->UpdateState(true, events.get());
 
   element_animations_->NotifyAnimationAborted(events->events_[0]);
@@ -3075,14 +3073,14 @@
   EXPECT_TRUE(client_impl_.GetFilterIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
   events->events_.clear();
 
   // Finish the animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_->UpdateState(true, nullptr);
   EXPECT_FALSE(client_.GetHasPotentialFilterAnimation(element_id_,
                                                       ElementListType::ACTIVE));
@@ -3101,7 +3099,7 @@
   EXPECT_TRUE(client_impl_.GetFilterIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
   EXPECT_FALSE(client_impl_.GetHasPotentialFilterAnimation(
       element_id_, ElementListType::PENDING));
@@ -3135,7 +3133,7 @@
   EXPECT_TRUE(client_impl_.GetFilterIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -3186,7 +3184,7 @@
   EXPECT_TRUE(client_impl_.GetFilterIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_impl_->UpdateState(true, events.get());
 
   player_->NotifyAnimationStarted(events->events_[0]);
@@ -3202,7 +3200,7 @@
   EXPECT_FALSE(client_impl_.GetFilterIsCurrentlyAnimating(
       element_id_, ElementListType::ACTIVE));
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
   player_impl_->UpdateState(true, events.get());
 
   element_animations_->NotifyAnimationAborted(events->events_[0]);
@@ -3241,11 +3239,11 @@
 
   AddOpacityTransitionToPlayer(player_.get(), 1, 1.f, 2.f, true);
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // Opacity values are clipped [0,1]
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   EXPECT_EQ(1.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
 
@@ -3255,11 +3253,11 @@
 
   AddOpacityTransitionToPlayer(player_.get(), 1, 0.f, -2.f, true);
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // Opacity values are clipped [0,1]
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 }
 
@@ -3275,7 +3273,7 @@
 
   PushProperties();
   player_impl_->ActivateAnimations();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
   EXPECT_EQ(Animation::RUNNING,
             player_impl_->GetAnimationById(animation_id)->run_state());
@@ -3300,7 +3298,7 @@
   EXPECT_TRUE(
       player_impl_->GetAnimationById(animation_id)->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_impl_->UpdateState(true, events.get());
 
   // Only the active observer should have been ticked.
@@ -3329,7 +3327,7 @@
 
   PushProperties();
   player_impl_->ActivateAnimations();
-  player_impl_->Animate(kInitialTickTime);
+  player_impl_->Tick(kInitialTickTime);
   player_impl_->UpdateState(true, events.get());
 
   // Remove the first animation from the main-thread animations, and add a
@@ -3351,7 +3349,7 @@
   EXPECT_FALSE(player_impl_->GetAnimationById(second_animation_id)
                    ->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   player_impl_->UpdateState(true, events.get());
 
   // The original animation should still be running, and the new animation
@@ -3378,7 +3376,7 @@
   EXPECT_TRUE(player_impl_->GetAnimationById(second_animation_id)
                   ->affects_active_elements());
 
-  player_impl_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
+  player_impl_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   player_impl_->UpdateState(true, events.get());
 
   // The new animation should be running, and the active observer should have
@@ -3401,13 +3399,13 @@
   animation->set_affects_active_elements(false);
 
   player_->AddAnimation(std::move(animation));
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
   EXPECT_TRUE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
                                                     ElementListType::PENDING));
   EXPECT_FALSE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
                                                      ElementListType::ACTIVE));
   player_->UpdateState(true, nullptr);
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
 
   EXPECT_TRUE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
                                                     ElementListType::PENDING));
@@ -3429,7 +3427,7 @@
   EXPECT_FALSE(player_->IsCurrentlyAnimatingProperty(TargetProperty::FILTER,
                                                      ElementListType::ACTIVE));
 
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(10));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(10));
   player_->UpdateState(true, nullptr);
 
   EXPECT_TRUE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
@@ -3444,7 +3442,7 @@
   EXPECT_EQ(0.f, client_.GetOpacity(element_id_, ElementListType::ACTIVE));
 
   // Tick past the end of the animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1100));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(1100));
   player_->UpdateState(true, nullptr);
 
   EXPECT_FALSE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
@@ -3474,7 +3472,7 @@
 
   player_->AddAnimation(std::move(animation));
 
-  player_->Animate(kInitialTickTime);
+  player_->Tick(kInitialTickTime);
 
   // Since the animation has a start delay, the elements it affects have a
   // potentially running transform animation but aren't currently animating
@@ -3487,7 +3485,7 @@
                                                      ElementListType::PENDING));
   EXPECT_FALSE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
                                                      ElementListType::ACTIVE));
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_FALSE(player_->IsPotentiallyAnimatingProperty(
       TargetProperty::FILTER, ElementListType::PENDING));
   EXPECT_FALSE(player_->IsPotentiallyAnimatingProperty(
@@ -3503,7 +3501,7 @@
                                                      ElementListType::PENDING));
   EXPECT_FALSE(player_->IsCurrentlyAnimatingProperty(TargetProperty::OPACITY,
                                                      ElementListType::ACTIVE));
-  EXPECT_TRUE(player_->HasActiveAnimation());
+  EXPECT_TRUE(player_->HasTickingAnimation());
   EXPECT_FALSE(player_->IsPotentiallyAnimatingProperty(
       TargetProperty::FILTER, ElementListType::PENDING));
   EXPECT_FALSE(player_->IsPotentiallyAnimatingProperty(
@@ -3512,7 +3510,7 @@
   player_->UpdateState(true, nullptr);
 
   // Tick past the start delay.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   player_->UpdateState(true, nullptr);
   EXPECT_TRUE(player_->IsPotentiallyAnimatingProperty(
       TargetProperty::OPACITY, ElementListType::PENDING));
@@ -3526,7 +3524,7 @@
   // After the animaton finishes, the elements it affects have neither a
   // potentially running transform animation nor a currently running transform
   // animation.
-  player_->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
+  player_->Tick(kInitialTickTime + TimeDelta::FromMilliseconds(4000));
   player_->UpdateState(true, nullptr);
   EXPECT_FALSE(player_->IsPotentiallyAnimatingProperty(
       TargetProperty::OPACITY, ElementListType::PENDING));
@@ -3541,19 +3539,19 @@
 TEST_F(ElementAnimationsTest, DestroyTestMainLayerBeforePushProperties) {
   CreateTestLayer(false, false);
   AttachTimelinePlayerLayer();
-  EXPECT_EQ(0u, host_->active_players_for_testing().size());
+  EXPECT_EQ(0u, host_->ticking_players_for_testing().size());
 
   player_->AddAnimation(CreateAnimation(
       std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)),
       2, TargetProperty::OPACITY));
-  EXPECT_EQ(1u, host_->active_players_for_testing().size());
+  EXPECT_EQ(1u, host_->ticking_players_for_testing().size());
 
   DestroyTestMainLayer();
-  EXPECT_EQ(0u, host_->active_players_for_testing().size());
+  EXPECT_EQ(0u, host_->ticking_players_for_testing().size());
 
   PushProperties();
-  EXPECT_EQ(0u, host_->active_players_for_testing().size());
-  EXPECT_EQ(0u, host_impl_->active_players_for_testing().size());
+  EXPECT_EQ(0u, host_->ticking_players_for_testing().size());
+  EXPECT_EQ(0u, host_impl_->ticking_players_for_testing().size());
 }
 
 }  // namespace
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index d4e6c9f3..73e62b3 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -213,8 +213,8 @@
   layer_->SetBackgroundFilters(filters);
 }
 
-bool WebLayerImpl::hasActiveAnimationForTesting() {
-  return layer_->HasActiveAnimationForTesting();
+bool WebLayerImpl::hasTickingAnimationForTesting() {
+  return layer_->HasTickingAnimationForTesting();
 }
 
 void WebLayerImpl::setScrollPositionDouble(blink::WebDoublePoint position) {
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h
index 42eb99c..1d8c2a09 100644
--- a/cc/blink/web_layer_impl.h
+++ b/cc/blink/web_layer_impl.h
@@ -88,7 +88,7 @@
   void setFilters(const cc::FilterOperations& filters) override;
   void setFiltersOrigin(const blink::WebFloatPoint& origin) override;
   void setBackgroundFilters(const cc::FilterOperations& filters) override;
-  bool hasActiveAnimationForTesting() override;
+  bool hasTickingAnimationForTesting() override;
   void setScrollPositionDouble(blink::WebDoublePoint position) override;
   blink::WebDoublePoint scrollPositionDouble() const override;
   void setScrollClipLayer(blink::WebLayer* clip_layer) override;
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn
index fab163da..b193c9f 100644
--- a/cc/ipc/BUILD.gn
+++ b/cc/ipc/BUILD.gn
@@ -49,7 +49,6 @@
     "mojo_compositor_frame_sink.mojom",
     "quads.mojom",
     "render_pass.mojom",
-    "render_pass_id.mojom",
     "returned_resource.mojom",
     "selection.mojom",
     "shared_quad_state.mojom",
@@ -106,7 +105,6 @@
     "local_frame_id_struct_traits.h",
     "quads_struct_traits.cc",
     "quads_struct_traits.h",
-    "render_pass_id_struct_traits.h",
     "render_pass_struct_traits.cc",
     "render_pass_struct_traits.h",
     "selection_struct_traits.h",
diff --git a/cc/ipc/begin_frame_args.mojom b/cc/ipc/begin_frame_args.mojom
index b0b784a..8b16c62a 100644
--- a/cc/ipc/begin_frame_args.mojom
+++ b/cc/ipc/begin_frame_args.mojom
@@ -4,7 +4,7 @@
 
 module cc.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 
 enum BeginFrameArgsType {
   INVALID,
diff --git a/cc/ipc/cc_param_traits.cc b/cc/ipc/cc_param_traits.cc
index e3c0100..75d2fee8 100644
--- a/cc/ipc/cc_param_traits.cc
+++ b/cc/ipc/cc_param_traits.cc
@@ -423,7 +423,7 @@
 bool ParamTraits<cc::RenderPass>::Read(const base::Pickle* m,
                                        base::PickleIterator* iter,
                                        param_type* p) {
-  cc::RenderPassId id;
+  int id;
   gfx::Rect output_rect;
   gfx::Rect damage_rect;
   gfx::Transform transform_to_root_target;
@@ -703,7 +703,7 @@
   const size_t kMaxSharedQuadStateListSize = 100000;
   const size_t kMaxQuadListSize = 1000000;
 
-  std::set<cc::RenderPassId> pass_set;
+  std::set<int> pass_id_set;
 
   uint32_t num_render_passes;
   if (!ReadParam(m, iter, &p->resource_list) ||
@@ -730,10 +730,10 @@
         continue;
       const cc::RenderPassDrawQuad* rpdq =
           cc::RenderPassDrawQuad::MaterialCast(quad);
-      if (!pass_set.count(rpdq->render_pass_id))
+      if (!pass_id_set.count(rpdq->render_pass_id))
         return false;
     }
-    pass_set.insert(render_pass->id);
+    pass_id_set.insert(render_pass->id);
     p->render_pass_list.push_back(std::move(render_pass));
   }
 
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index 2df89c3..831a37e1 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -11,7 +11,6 @@
 #include "cc/quads/debug_border_draw_quad.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/quads/render_pass.h"
-#include "cc/quads/render_pass_id.h"
 #include "cc/quads/shared_quad_state.h"
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/stream_video_draw_quad.h"
@@ -42,11 +41,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(cc::YUVVideoDrawQuad::ColorSpace,
                           cc::YUVVideoDrawQuad::COLOR_SPACE_LAST)
 
-IPC_STRUCT_TRAITS_BEGIN(cc::RenderPassId)
-  IPC_STRUCT_TRAITS_MEMBER(layer_id)
-  IPC_STRUCT_TRAITS_MEMBER(index)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(cc::SurfaceSequence)
   IPC_STRUCT_TRAITS_MEMBER(frame_sink_id)
   IPC_STRUCT_TRAITS_MEMBER(sequence)
diff --git a/cc/ipc/cc_param_traits_unittest.cc b/cc/ipc/cc_param_traits_unittest.cc
index 1b663fd..5ed70d0f 100644
--- a/cc/ipc/cc_param_traits_unittest.cc
+++ b/cc/ipc/cc_param_traits_unittest.cc
@@ -28,7 +28,6 @@
 using cc::FilterOperations;
 using cc::PictureDrawQuad;
 using cc::RenderPass;
-using cc::RenderPassId;
 using cc::RenderPassDrawQuad;
 using cc::ResourceId;
 using cc::ResourceProvider;
@@ -268,8 +267,8 @@
       YUVVideoDrawQuad::REC_601;
   gfx::ColorSpace arbitrary_video_color_space = gfx::ColorSpace::CreateREC601();
 
-  RenderPassId child_id(30, 5);
-  RenderPassId root_id(10, 14);
+  int child_id = 30;
+  int root_id = 14;
 
   FilterOperations arbitrary_filters1;
   arbitrary_filters1.Append(
@@ -477,8 +476,7 @@
 
 TEST_F(CCParamTraitsTest, UnusedSharedQuadStates) {
   std::unique_ptr<RenderPass> pass_in = RenderPass::Create();
-  pass_in->SetAll(RenderPassId(1, 1), gfx::Rect(100, 100), gfx::Rect(),
-                  gfx::Transform(), false);
+  pass_in->SetAll(1, gfx::Rect(100, 100), gfx::Rect(), gfx::Transform(), false);
 
   // The first SharedQuadState is used.
   SharedQuadState* shared_state1_in = pass_in->CreateAndAppendSharedQuadState();
@@ -583,8 +581,7 @@
   arbitrary_resource2.is_overlay_candidate = false;
 
   std::unique_ptr<RenderPass> renderpass_in = RenderPass::Create();
-  renderpass_in->SetNew(RenderPassId(1, 1), gfx::Rect(), gfx::Rect(),
-                        gfx::Transform());
+  renderpass_in->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   CompositorFrame frame_in;
   frame_in.resource_list.push_back(arbitrary_resource1);
diff --git a/cc/ipc/cc_serialization_perftest.cc b/cc/ipc/cc_serialization_perftest.cc
index e903fa3..a93e27e 100644
--- a/cc/ipc/cc_serialization_perftest.cc
+++ b/cc/ipc/cc_serialization_perftest.cc
@@ -201,8 +201,7 @@
 
     for (uint32_t i = 0; i < num_passes; ++i) {
       std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
-      render_pass->SetNew(RenderPassId(1, 1), gfx::Rect(), gfx::Rect(),
-                          gfx::Transform());
+      render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
       for (uint32_t j = 0; j < num_quads; ++j) {
         if (j == 0 || single_sqs == UseSingleSharedQuadState::NO)
           render_pass->CreateAndAppendSharedQuadState();
diff --git a/cc/ipc/compositor_frame_for_blink.typemap b/cc/ipc/compositor_frame_for_blink.typemap
index 2350610..046b359 100644
--- a/cc/ipc/compositor_frame_for_blink.typemap
+++ b/cc/ipc/compositor_frame_for_blink.typemap
@@ -9,7 +9,6 @@
   "//cc/ipc/compositor_frame_struct_traits.h",
   "//cc/ipc/filter_operation_struct_traits.h",
   "//cc/ipc/filter_operations_struct_traits.h",
-  "//cc/ipc/render_pass_id_struct_traits.h",
   "//cc/ipc/render_pass_struct_traits.h",
   "//cc/ipc/selection_struct_traits.h",
   "//cc/ipc/shared_quad_state_struct_traits.h",
diff --git a/cc/ipc/display_compositor.mojom b/cc/ipc/display_compositor.mojom
index d8fab7a..2df2c2a 100644
--- a/cc/ipc/display_compositor.mojom
+++ b/cc/ipc/display_compositor.mojom
@@ -29,15 +29,6 @@
       cc.mojom.MojoCompositorFrameSink& compositor_frame_sink,
       cc.mojom.MojoCompositorFrameSinkPrivate& compositor_frame_sink_private,
       cc.mojom.MojoCompositorFrameSinkClient compositor_frame_sink_client);
-
-  // TODO(fsamuel): This surface reference API is temporary. Surface references
-  // should be bundled with CompositorFrames.
-  AddRootSurfaceReference(cc.mojom.SurfaceId child_id);
-  AddSurfaceReference(cc.mojom.SurfaceId parent_id,
-                      cc.mojom.SurfaceId child_id);
-  RemoveRootSurfaceReference(cc.mojom.SurfaceId child_id);
-  RemoveSurfaceReference(cc.mojom.SurfaceId parent_id,
-                         cc.mojom.SurfaceId child_id);
 };
 
 // The DisplayCompositorClient interface is implemented by the Display
@@ -45,6 +36,11 @@
 // compositor. The display compositor host is either the browser process in
 // Chrome or the window server process.
 interface DisplayCompositorClient {
+  // Called by the display compostor when it is created. Provides the top level
+  // root surface id that should reference display roots. This will always be
+  // the first message sent.
+  OnDisplayCompositorCreated(cc.mojom.SurfaceId root_surface_id);
+
   // Called by the display compositor immediately upon receiving a
   // CompositorFrame with a new SurfaceId for the first time.
   OnSurfaceCreated(cc.mojom.SurfaceId surface_id,
diff --git a/cc/ipc/local_frame_id.mojom b/cc/ipc/local_frame_id.mojom
index 6b25b6b..1ede094e 100644
--- a/cc/ipc/local_frame_id.mojom
+++ b/cc/ipc/local_frame_id.mojom
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 module cc.mojom;
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/unguessable_token.mojom";
 
 struct LocalFrameId {
   // An identifier allocated by the client uniquely identifying a surface within
diff --git a/cc/ipc/mojo_compositor_frame_sink.mojom b/cc/ipc/mojo_compositor_frame_sink.mojom
index 5c6d77e3..87ddd3a 100644
--- a/cc/ipc/mojo_compositor_frame_sink.mojom
+++ b/cc/ipc/mojo_compositor_frame_sink.mojom
@@ -8,7 +8,9 @@
 import "cc/ipc/compositor_frame.mojom";
 import "cc/ipc/frame_sink_id.mojom";
 import "cc/ipc/local_frame_id.mojom";
+import "cc/ipc/surface_reference.mojom";
 import "cc/ipc/returned_resource.mojom";
+import "cc/ipc/surface_sequence.mojom";
 
 // A MojoCompositorFrameSink is an interface for receiving CompositorFrame
 // structs. A CompositorFrame contains the complete output meant for display.
@@ -31,10 +33,26 @@
   SubmitCompositorFrame(cc.mojom.LocalFrameId local_frame_id,
                         cc.mojom.CompositorFrame frame);
 
+  // Adds surface references.
+  AddSurfaceReferences(array<SurfaceReference> references);
+
+  // Removes surface references.
+  RemoveSurfaceReferences(array<SurfaceReference> references);
+
   // Notify that the surface is no longer in use (and is okay to be evicted) so
   // that its resources gets returned in time.
   EvictFrame();
 
+  // TODO(staraz): Delete Require() and Satisfy() once surface references
+  // (CL 2541683004) are ready.
+  // Add the provided |sequence| as a destruction dependency of the
+  // surface associated with the provided |local_frame_id|.
+  Require(cc.mojom.LocalFrameId local_frame_id,
+          cc.mojom.SurfaceSequence sequence);
+
+  // Mark the sequence as satisfied and garbage collect surfaces.
+  Satisfy(cc.mojom.SurfaceSequence sequence);
+
   // TODO(fsamuel): ReadbackBitmap API would be useful here.
 };
 
diff --git a/cc/ipc/quads.mojom b/cc/ipc/quads.mojom
index 82f2542..111e0fc 100644
--- a/cc/ipc/quads.mojom
+++ b/cc/ipc/quads.mojom
@@ -5,7 +5,6 @@
 module cc.mojom;
 
 import "cc/ipc/filter_operations.mojom";
-import "cc/ipc/render_pass_id.mojom";
 import "cc/ipc/shared_quad_state.mojom";
 import "cc/ipc/surface_id.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
@@ -20,7 +19,7 @@
 };
 
 struct RenderPassQuadState {
-  cc.mojom.RenderPassId render_pass_id;
+  int32 render_pass_id;
 
   // If nonzero, resource id of mask to use when drawing this pass.
   uint32 mask_resource_id;
diff --git a/cc/ipc/quads_struct_traits.cc b/cc/ipc/quads_struct_traits.cc
index 6d649d5..7cc9ef84 100644
--- a/cc/ipc/quads_struct_traits.cc
+++ b/cc/ipc/quads_struct_traits.cc
@@ -67,8 +67,11 @@
   quad->resources.ids[cc::RenderPassDrawQuad::kMaskResourceIdIndex] =
       data.mask_resource_id();
   quad->resources.count = data.mask_resource_id() ? 1 : 0;
-  return data.ReadRenderPassId(&quad->render_pass_id) &&
-         data.ReadMaskUvScale(&quad->mask_uv_scale) &&
+  quad->render_pass_id = data.render_pass_id();
+  // RenderPass ids are never zero.
+  if (!quad->render_pass_id)
+    return false;
+  return data.ReadMaskUvScale(&quad->mask_uv_scale) &&
          data.ReadMaskTextureSize(&quad->mask_texture_size) &&
          data.ReadFilters(&quad->filters) &&
          data.ReadFiltersScale(&quad->filters_scale) &&
diff --git a/cc/ipc/quads_struct_traits.h b/cc/ipc/quads_struct_traits.h
index dc6aed9..4c2f60c 100644
--- a/cc/ipc/quads_struct_traits.h
+++ b/cc/ipc/quads_struct_traits.h
@@ -5,10 +5,10 @@
 #ifndef CC_IPC_QUADS_STRUCT_TRAITS_H_
 #define CC_IPC_QUADS_STRUCT_TRAITS_H_
 
+#include "base/logging.h"
 #include "cc/ipc/filter_operation_struct_traits.h"
 #include "cc/ipc/filter_operations_struct_traits.h"
 #include "cc/ipc/quads.mojom-shared.h"
-#include "cc/ipc/render_pass_id_struct_traits.h"
 #include "cc/ipc/shared_quad_state_struct_traits.h"
 #include "cc/ipc/surface_id_struct_traits.h"
 #include "cc/quads/debug_border_draw_quad.h"
@@ -134,9 +134,10 @@
 
 template <>
 struct StructTraits<cc::mojom::RenderPassQuadStateDataView, cc::DrawQuad> {
-  static const cc::RenderPassId& render_pass_id(const cc::DrawQuad& input) {
+  static int32_t render_pass_id(const cc::DrawQuad& input) {
     const cc::RenderPassDrawQuad* quad =
         cc::RenderPassDrawQuad::MaterialCast(&input);
+    DCHECK(quad->render_pass_id);
     return quad->render_pass_id;
   }
 
@@ -468,12 +469,12 @@
     return ConstIterator(input.begin());
   }
 
-  static void AdvanceIterator(ConstIterator& iterator) {
+  static void AdvanceIterator(ConstIterator& iterator) {  // NOLINT
     iterator.last_shared_quad_state = (*iterator.it)->shared_quad_state;
     ++iterator.it;
   }
 
-  static Element GetValue(ConstIterator& iterator) {
+  static Element GetValue(ConstIterator& iterator) {  // NOLINT
     DrawQuadWithSharedQuadState dq = {*iterator.it, nullptr};
     // Only serialize the SharedQuadState if we haven't seen it before and
     // therefore have not already serialized it.
diff --git a/cc/ipc/render_pass.mojom b/cc/ipc/render_pass.mojom
index a45a2ce..2747e2a 100644
--- a/cc/ipc/render_pass.mojom
+++ b/cc/ipc/render_pass.mojom
@@ -4,14 +4,13 @@
 
 module cc.mojom;
 
-import "cc/ipc/render_pass_id.mojom";
 import "cc/ipc/quads.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/transform.mojom";
 
 // See cc/quads/render_pass.h.
 struct RenderPass {
-  RenderPassId id;
+  int32 id;
   gfx.mojom.Rect output_rect;
   gfx.mojom.Rect damage_rect;
   gfx.mojom.Transform transform_to_root_target;
diff --git a/cc/ipc/render_pass_id.mojom b/cc/ipc/render_pass_id.mojom
deleted file mode 100644
index 90eee18..0000000
--- a/cc/ipc/render_pass_id.mojom
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module cc.mojom;
-
-struct RenderPassId {
-  int32 layer_id;
-  uint32 index;
-};
diff --git a/cc/ipc/render_pass_id.typemap b/cc/ipc/render_pass_id.typemap
deleted file mode 100644
index cc3d1112..0000000
--- a/cc/ipc/render_pass_id.typemap
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//cc/ipc/render_pass_id.mojom"
-public_headers = [ "//cc/quads/render_pass_id.h" ]
-traits_headers = [ "//cc/ipc/render_pass_id_struct_traits.h" ]
-deps = [
-  "//cc/ipc:struct_traits",
-]
-type_mappings = [ "cc.mojom.RenderPassId=cc::RenderPassId" ]
diff --git a/cc/ipc/render_pass_id_struct_traits.h b/cc/ipc/render_pass_id_struct_traits.h
deleted file mode 100644
index dd900105..0000000
--- a/cc/ipc/render_pass_id_struct_traits.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_IPC_RENDER_PASS_ID_STRUCT_TRAITS_H_
-#define CC_IPC_RENDER_PASS_ID_STRUCT_TRAITS_H_
-
-#include "cc/ipc/render_pass_id.mojom-shared.h"
-#include "cc/quads/render_pass_id.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<cc::mojom::RenderPassIdDataView, cc::RenderPassId> {
-  static int layer_id(const cc::RenderPassId& id) { return id.layer_id; }
-
-  static uint32_t index(const cc::RenderPassId& id) { return id.index; }
-
-  static bool Read(cc::mojom::RenderPassIdDataView data,
-                   cc::RenderPassId* out) {
-    out->layer_id = data.layer_id();
-    out->index = data.index();
-    return true;
-  }
-};
-
-}  // namespace mojo
-
-#endif  // CC_IPC_RENDER_PASS_ID_STRUCT_TRAITS_H_
diff --git a/cc/ipc/render_pass_struct_traits.cc b/cc/ipc/render_pass_struct_traits.cc
index 954f182b..895d516 100644
--- a/cc/ipc/render_pass_struct_traits.cc
+++ b/cc/ipc/render_pass_struct_traits.cc
@@ -15,11 +15,15 @@
     Read(cc::mojom::RenderPassDataView data,
          std::unique_ptr<cc::RenderPass>* out) {
   *out = cc::RenderPass::Create();
-  if (!data.ReadId(&(*out)->id) || !data.ReadOutputRect(&(*out)->output_rect) ||
+  if (!data.ReadOutputRect(&(*out)->output_rect) ||
       !data.ReadDamageRect(&(*out)->damage_rect) ||
       !data.ReadTransformToRootTarget(&(*out)->transform_to_root_target)) {
     return false;
   }
+  (*out)->id = data.id();
+  // RenderPass ids are never zero.
+  if (!(*out)->id)
+    return false;
   (*out)->has_transparent_background = data.has_transparent_background();
 
   mojo::ArrayDataView<cc::mojom::DrawQuadDataView> quads;
diff --git a/cc/ipc/render_pass_struct_traits.h b/cc/ipc/render_pass_struct_traits.h
index e89dd84..95a31cf 100644
--- a/cc/ipc/render_pass_struct_traits.h
+++ b/cc/ipc/render_pass_struct_traits.h
@@ -5,9 +5,9 @@
 #ifndef CC_IPC_RENDER_PASS_STRUCT_TRAITS_H_
 #define CC_IPC_RENDER_PASS_STRUCT_TRAITS_H_
 
+#include "base/logging.h"
 #include "cc/ipc/quads_struct_traits.h"
 #include "cc/ipc/render_pass.mojom-shared.h"
-#include "cc/ipc/render_pass_id_struct_traits.h"
 #include "cc/quads/render_pass.h"
 #include "ui/gfx/mojo/transform_struct_traits.h"
 
@@ -16,8 +16,8 @@
 template <>
 struct StructTraits<cc::mojom::RenderPassDataView,
                     std::unique_ptr<cc::RenderPass>> {
-  static const cc::RenderPassId& id(
-      const std::unique_ptr<cc::RenderPass>& input) {
+  static int32_t id(const std::unique_ptr<cc::RenderPass>& input) {
+    DCHECK(input->id);
     return input->id;
   }
 
diff --git a/cc/ipc/struct_traits_unittest.cc b/cc/ipc/struct_traits_unittest.cc
index b7fa588..437898b 100644
--- a/cc/ipc/struct_traits_unittest.cc
+++ b/cc/ipc/struct_traits_unittest.cc
@@ -8,7 +8,6 @@
 #include "cc/quads/debug_border_draw_quad.h"
 #include "cc/quads/render_pass.h"
 #include "cc/quads/render_pass_draw_quad.h"
-#include "cc/quads/render_pass_id.h"
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/stream_video_draw_quad.h"
 #include "cc/quads/surface_draw_quad.h"
@@ -68,11 +67,6 @@
     callback.Run(std::move(r));
   }
 
-  void EchoRenderPassId(const RenderPassId& r,
-                        const EchoRenderPassIdCallback& callback) override {
-    callback.Run(r);
-  }
-
   void EchoReturnedResource(
       const ReturnedResource& r,
       const EchoReturnedResourceCallback& callback) override {
@@ -146,6 +140,7 @@
 // RenderPass, and QuadListBasic unit tests.
 TEST_F(StructTraitsTest, CompositorFrame) {
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
+  render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(2, 3), gfx::Transform());
 
   // SharedQuadState.
   const gfx::Transform sqs_quad_to_target_transform(
@@ -420,8 +415,7 @@
 
 TEST_F(StructTraitsTest, QuadListBasic) {
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
-  render_pass->SetNew(RenderPassId(1, 1), gfx::Rect(), gfx::Rect(),
-                      gfx::Transform());
+  render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   SharedQuadState* sqs = render_pass->CreateAndAppendSharedQuadState();
 
@@ -449,7 +443,7 @@
 
   const gfx::Rect rect4(1234, 5678, 9101112, 13141516);
   const ResourceId resource_id4(1337);
-  const RenderPassId render_pass_id(1234, 5678);
+  const int render_pass_id = 1234;
   const gfx::Vector2dF mask_uv_scale(1337.1f, 1234.2f);
   const gfx::Size mask_texture_size(1234, 5678);
   FilterOperations filters;
@@ -576,7 +570,7 @@
 }
 
 TEST_F(StructTraitsTest, RenderPass) {
-  const RenderPassId id(3, 2);
+  const int id = 3;
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
@@ -685,7 +679,7 @@
 }
 
 TEST_F(StructTraitsTest, RenderPassWithEmptySharedQuadStateList) {
-  const RenderPassId id(3, 2);
+  const int id = 3;
   const gfx::Rect output_rect(45, 22, 120, 13);
   const gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
@@ -711,17 +705,6 @@
   EXPECT_EQ(has_transparent_background, output->has_transparent_background);
 }
 
-TEST_F(StructTraitsTest, RenderPassId) {
-  const int layer_id = 1337;
-  const uint32_t index = 0xdeadbeef;
-  RenderPassId input(layer_id, index);
-  mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
-  RenderPassId output;
-  proxy->EchoRenderPassId(input, &output);
-  EXPECT_EQ(layer_id, output.layer_id);
-  EXPECT_EQ(index, output.index);
-}
-
 TEST_F(StructTraitsTest, ReturnedResource) {
   const uint32_t id = 1337;
   const gpu::CommandBufferNamespace command_buffer_namespace = gpu::IN_PROCESS;
@@ -890,8 +873,7 @@
 
 TEST_F(StructTraitsTest, YUVDrawQuad) {
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
-  render_pass->SetNew(RenderPassId(1, 1), gfx::Rect(), gfx::Rect(),
-                      gfx::Transform());
+  render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   const DrawQuad::Material material = DrawQuad::YUV_VIDEO_CONTENT;
   const gfx::Rect rect(1234, 4321, 1357, 7531);
diff --git a/cc/ipc/traits_test_service.mojom b/cc/ipc/traits_test_service.mojom
index 7948f12..8891949 100644
--- a/cc/ipc/traits_test_service.mojom
+++ b/cc/ipc/traits_test_service.mojom
@@ -11,7 +11,6 @@
 import "cc/ipc/filter_operations.mojom";
 import "cc/ipc/quads.mojom";
 import "cc/ipc/render_pass.mojom";
-import "cc/ipc/render_pass_id.mojom";
 import "cc/ipc/returned_resource.mojom";
 import "cc/ipc/selection.mojom";
 import "cc/ipc/shared_quad_state.mojom";
@@ -43,9 +42,6 @@
   EchoRenderPass(RenderPass r) => (RenderPass pass);
 
   [Sync]
-  EchoRenderPassId(RenderPassId r) => (RenderPassId pass);
-
-  [Sync]
   EchoReturnedResource(ReturnedResource r) => (ReturnedResource pass);
 
   [Sync]
diff --git a/cc/ipc/typemaps.gni b/cc/ipc/typemaps.gni
index d64c203..c0e8c47 100644
--- a/cc/ipc/typemaps.gni
+++ b/cc/ipc/typemaps.gni
@@ -11,7 +11,6 @@
   "//cc/ipc/frame_sink_id.typemap",
   "//cc/ipc/local_frame_id.typemap",
   "//cc/ipc/render_pass.typemap",
-  "//cc/ipc/render_pass_id.typemap",
   "//cc/ipc/returned_resource.typemap",
   "//cc/ipc/selection.typemap",
   "//cc/ipc/shared_quad_state.typemap",
diff --git a/cc/layers/append_quads_data.h b/cc/layers/append_quads_data.h
index 831bf816..cf17b5b28 100644
--- a/cc/layers/append_quads_data.h
+++ b/cc/layers/append_quads_data.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include "cc/quads/render_pass_id.h"
-
 namespace cc {
 
 // Set by the layer appending quads.
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 7362df6..f25894d2 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1531,9 +1531,9 @@
   }
 }
 
-bool Layer::HasActiveAnimationForTesting() const {
+bool Layer::HasTickingAnimationForTesting() const {
   return layer_tree_host_
-             ? GetMutatorHost()->HasActiveAnimationForTesting(element_id())
+             ? GetMutatorHost()->HasTickingAnimationForTesting(element_id())
              : false;
 }
 
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 97b698f..08c72af 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -440,7 +440,7 @@
   void SetMutableProperties(uint32_t properties);
   uint32_t mutable_properties() const { return inputs_.mutable_properties; }
 
-  bool HasActiveAnimationForTesting() const;
+  bool HasTickingAnimationForTesting() const;
 
   void SetHasWillChangeTransformHint(bool has_will_change);
   bool has_will_change_transform_hint() const {
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index be233f6..0c34b95 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -968,7 +968,7 @@
     base::JSONReader json_reader;
     std::unique_ptr<base::Value> debug_info_value(json_reader.ReadToValue(str));
 
-    if (debug_info_value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (debug_info_value->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* dictionary_value = nullptr;
       bool converted_to_dictionary =
           debug_info_value->GetAsDictionary(&dictionary_value);
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 8497191..61771910 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1312,8 +1312,8 @@
 
   state->BeginArray("coverage_tiles");
   for (PictureLayerTilingSet::CoverageIterator iter(
-           tilings_.get(), 1.f, gfx::Rect(raster_source_->GetSize()),
-           ideal_contents_scale_);
+           tilings_.get(), MaximumTilingContentsScale(),
+           gfx::Rect(raster_source_->GetSize()), ideal_contents_scale_);
        iter; ++iter) {
     state->BeginDictionary();
 
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index b1bc688..3623133 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -328,16 +328,13 @@
   layer_list_.clear();
 }
 
-RenderPassId RenderSurfaceImpl::GetRenderPassId() {
-  int layer_id = owning_layer_->id();
-  int sub_id = 0;
-  DCHECK_GT(layer_id, 0);
-  return RenderPassId(layer_id, sub_id);
+int RenderSurfaceImpl::GetRenderPassId() {
+  return owning_layer_->id();
 }
 
 void RenderSurfaceImpl::AppendRenderPasses(RenderPassSink* pass_sink) {
   std::unique_ptr<RenderPass> pass = RenderPass::Create(layer_list_.size());
-  pass->SetNew(GetRenderPassId(), content_rect(),
+  pass->SetNew(owning_layer_->id(), content_rect(),
                gfx::IntersectRects(content_rect(),
                                    damage_tracker_->current_damage_rect()),
                draw_properties_.screen_space_transform);
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 8cd9523f..80c2c15 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -27,7 +27,6 @@
 class DamageTracker;
 class FilterOperations;
 class Occlusion;
-class RenderPassId;
 class RenderPassSink;
 class LayerImpl;
 class LayerIterator;
@@ -145,7 +144,7 @@
 
   DamageTracker* damage_tracker() const { return damage_tracker_.get(); }
 
-  RenderPassId GetRenderPassId();
+  int GetRenderPassId();
 
   void AppendRenderPasses(RenderPassSink* pass_sink);
   void AppendQuads(RenderPass* render_pass, AppendQuadsData* append_quads_data);
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
index ac9dd0f..8e9aba2 100644
--- a/cc/layers/render_surface_unittest.cc
+++ b/cc/layers/render_surface_unittest.cc
@@ -184,7 +184,7 @@
   ASSERT_EQ(1u, pass_sink.RenderPasses().size());
   RenderPass* pass = pass_sink.RenderPasses()[0].get();
 
-  EXPECT_EQ(RenderPassId(2, 0), pass->id);
+  EXPECT_EQ(2, pass->id);
   EXPECT_EQ(content_rect, pass->output_rect);
   EXPECT_EQ(origin, pass->transform_to_root_target);
 }
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index cc7fd12..d8af8b6 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -167,8 +167,7 @@
     const RenderPassList& render_passes_in_draw_order) {
   render_pass_bypass_quads_.clear();
 
-  std::unordered_map<RenderPassId, gfx::Size, RenderPassIdHash>
-      render_passes_in_frame;
+  std::unordered_map<int, gfx::Size> render_passes_in_frame;
   RenderPass* root_render_pass = render_passes_in_draw_order.back().get();
   for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) {
     RenderPass* pass = render_passes_in_draw_order[i].get();
@@ -178,11 +177,11 @@
         continue;
       }
     }
-    render_passes_in_frame.insert(std::pair<RenderPassId, gfx::Size>(
-        pass->id, RenderPassTextureSize(pass)));
+    render_passes_in_frame.insert(
+        std::pair<int, gfx::Size>(pass->id, RenderPassTextureSize(pass)));
   }
 
-  std::vector<RenderPassId> passes_to_delete;
+  std::vector<int> passes_to_delete;
   for (auto pass_iter = render_pass_textures_.begin();
        pass_iter != render_pass_textures_.end(); ++pass_iter) {
     auto it = render_passes_in_frame.find(pass_iter->first);
@@ -568,8 +567,8 @@
   return false;
 }
 
-bool DirectRenderer::HasAllocatedResourcesForTesting(RenderPassId id) const {
-  auto iter = render_pass_textures_.find(id);
+bool DirectRenderer::HasAllocatedResourcesForTesting(int render_pass_id) const {
+  auto iter = render_pass_textures_.find(render_pass_id);
   return iter != render_pass_textures_.end() && iter->second->id();
 }
 
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index af732778..2bf0c9c3 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -28,7 +28,6 @@
 class DrawPolygon;
 class OutputSurface;
 class RenderPass;
-class RenderPassId;
 class RendererSettings;
 class ResourceProvider;
 class ScopedResource;
@@ -51,7 +50,7 @@
   void SetVisible(bool visible);
   void DecideRenderPassAllocationsForFrame(
       const RenderPassList& render_passes_in_draw_order);
-  bool HasAllocatedResourcesForTesting(RenderPassId id) const;
+  bool HasAllocatedResourcesForTesting(int render_pass_id) const;
   void DrawFrame(RenderPassList* render_passes_in_draw_order,
                  float device_scale_factor,
                  const gfx::ColorSpace& device_color_space,
@@ -186,12 +185,9 @@
   bool use_partial_swap_;
 
   // TODO(danakj): Just use a vector of pairs here? Hash map is way overkill.
-  std::unordered_map<RenderPassId,
-                     std::unique_ptr<ScopedResource>,
-                     RenderPassIdHash>
+  std::unordered_map<int, std::unique_ptr<ScopedResource>>
       render_pass_textures_;
-  std::unordered_map<RenderPassId, TileDrawQuad, RenderPassIdHash>
-      render_pass_bypass_quads_;
+  std::unordered_map<int, TileDrawQuad> render_pass_bypass_quads_;
 
   bool visible_ = false;
 
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 4dbe7c10..73e52032 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -492,8 +492,8 @@
   output_surface_->set_has_external_stencil_test(true);
 
   RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                    gfx::Rect(viewport_size), gfx::Transform());
+      AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                    gfx::Transform());
   root_pass->has_transparent_background = false;
 
   DrawFrame(renderer_.get(), viewport_size);
@@ -691,8 +691,8 @@
 
   gfx::Size viewport_size(1, 1);
   RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                    gfx::Rect(viewport_size), gfx::Transform());
+      AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                    gfx::Transform());
   root_pass->has_transparent_background = false;
 
   // On DEBUG builds, render passes with opaque background clear to blue to
@@ -735,8 +735,8 @@
 
   gfx::Size viewport_size(1, 1);
   RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                    gfx::Rect(viewport_size), gfx::Transform());
+      AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                    gfx::Transform());
   root_pass->has_transparent_background = true;
 
   EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, 1, _)).Times(1);
@@ -771,8 +771,8 @@
   renderer.SetVisible(true);
 
   gfx::Size viewport_size(1, 1);
-  AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                gfx::Rect(viewport_size), gfx::Transform());
+  AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                gfx::Transform());
 
   EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _))
       .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0)))
@@ -832,11 +832,10 @@
   // During initialization we are allowed to set any texture parameters.
   EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
 
-  RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 1),
-                    gfx::Rect(100, 100), gfx::Transform());
+  RenderPass* root_pass = AddRenderPass(&render_passes_in_draw_order_, 1,
+                                        gfx::Rect(100, 100), gfx::Transform());
   gpu::SyncToken mailbox_sync_token;
-  AddOneOfEveryQuadType(root_pass, resource_provider.get(), RenderPassId(0, 0),
+  AddOneOfEveryQuadType(root_pass, resource_provider.get(), 0,
                         &mailbox_sync_token);
 
   renderer.DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
@@ -917,13 +916,13 @@
 
   gfx::Size viewport_size(10, 10);
 
-  RenderPassId child_pass_id(2, 0);
+  int child_pass_id = 2;
   RenderPass* child_pass =
       AddRenderPass(&render_passes_in_draw_order_, child_pass_id,
                     gfx::Rect(viewport_size), gfx::Transform());
   AddQuad(child_pass, gfx::Rect(viewport_size), SK_ColorBLUE);
 
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass =
       AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                     gfx::Rect(viewport_size), gfx::Transform());
@@ -1005,20 +1004,20 @@
   gfx::Size viewport_size(1, 1);
 
   gfx::Rect grand_child_rect(25, 25);
-  RenderPassId grand_child_pass_id(3, 0);
+  int grand_child_pass_id = 3;
   RenderPass* grand_child_pass =
       AddRenderPass(&render_passes_in_draw_order_, grand_child_pass_id,
                     grand_child_rect, gfx::Transform());
   AddClippedQuad(grand_child_pass, grand_child_rect, SK_ColorYELLOW);
 
   gfx::Rect child_rect(50, 50);
-  RenderPassId child_pass_id(2, 0);
+  int child_pass_id = 2;
   RenderPass* child_pass =
       AddRenderPass(&render_passes_in_draw_order_, child_pass_id, child_rect,
                     gfx::Transform());
   AddQuad(child_pass, child_rect, SK_ColorBLUE);
 
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass =
       AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                     gfx::Rect(viewport_size), gfx::Transform());
@@ -1082,7 +1081,7 @@
 
   {
     // Partial frame, should not discard.
-    RenderPassId root_pass_id(1, 0);
+    int root_pass_id = 1;
     RenderPass* root_pass =
         AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                       gfx::Rect(viewport_size), gfx::Transform());
@@ -1096,7 +1095,7 @@
   }
   {
     // Full frame, should discard.
-    RenderPassId root_pass_id(1, 0);
+    int root_pass_id = 1;
     RenderPass* root_pass =
         AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                       gfx::Rect(viewport_size), gfx::Transform());
@@ -1111,7 +1110,7 @@
   {
     // Full frame, external scissor is set, should not discard.
     output_surface->set_has_external_stencil_test(true);
-    RenderPassId root_pass_id(1, 0);
+    int root_pass_id = 1;
     RenderPass* root_pass =
         AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                       gfx::Rect(viewport_size), gfx::Transform());
@@ -1160,7 +1159,7 @@
   gfx::Size viewport_size(100, 100);
   gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);
 
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass =
       AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                     gfx::Rect(viewport_size), gfx::Transform());
@@ -1184,10 +1183,10 @@
   gfx::Size viewport_size(1, 1);
 
   gfx::Rect child_rect(50, 50);
-  RenderPassId child_pass_id(2, 0);
+  int child_pass_id = 2;
   RenderPass* child_pass;
 
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass;
 
   ResourceId mask = resource_provider_->CreateResource(
@@ -1400,11 +1399,11 @@
 // project incorrectly by the given transform, because of w<0 clipping.
 TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
   gfx::Rect child_rect(50, 50);
-  RenderPassId child_pass_id(2, 0);
+  int child_pass_id = 2;
   RenderPass* child_pass;
 
   gfx::Size viewport_size(1, 1);
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass;
 
   gfx::Transform transform_preventing_aa;
@@ -1440,7 +1439,7 @@
 
 TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
   gfx::Size viewport_size(1, 1);
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass;
 
   gfx::Transform pixel_aligned_transform_causing_aa;
@@ -1533,7 +1532,7 @@
   void DrawFrame(float device_scale_factor,
                  const gfx::Size& viewport_size,
                  bool transparent) {
-    RenderPassId render_pass_id(1, 0);
+    int render_pass_id = 1;
     RenderPass* render_pass =
         AddRenderPass(&render_passes_in_draw_order_, render_pass_id,
                       gfx::Rect(viewport_size), gfx::Transform());
@@ -1652,8 +1651,8 @@
 
   gfx::Size viewport_size(1, 1);
   RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                    gfx::Rect(viewport_size), gfx::Transform());
+      AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                    gfx::Transform());
   root_pass->has_transparent_background = false;
   root_pass->copy_requests.push_back(
       CopyOutputRequest::CreateRequest(base::Bind(&IgnoreCopyResult)));
@@ -1690,7 +1689,7 @@
   Mock::VerifyAndClearExpectations(validator.get());
 
   // Without a copy request Attempt() should be called once.
-  root_pass = AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
+  root_pass = AddRenderPass(&render_passes_in_draw_order_, 1,
                             gfx::Rect(viewport_size), gfx::Transform());
   root_pass->has_transparent_background = false;
 
@@ -1708,7 +1707,7 @@
 
   // If the CALayerOverlay path is taken, then the ordinary overlay path should
   // not be called.
-  root_pass = AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
+  root_pass = AddRenderPass(&render_passes_in_draw_order_, 1,
                             gfx::Rect(viewport_size), gfx::Transform());
   root_pass->has_transparent_background = false;
 
@@ -1807,8 +1806,8 @@
 
   gfx::Size viewport_size(1, 1);
   RenderPass* root_pass =
-      AddRenderPass(&render_passes_in_draw_order_, RenderPassId(1, 0),
-                    gfx::Rect(viewport_size), gfx::Transform());
+      AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
+                    gfx::Transform());
   root_pass->has_transparent_background = false;
 
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
@@ -1891,7 +1890,7 @@
     gfx::Size viewport_size(100, 100);
 
     {
-      RenderPassId root_pass_id(1, 0);
+      int root_pass_id = 1;
       RenderPass* root_pass =
           AddRenderPass(&render_passes_in_draw_order_, root_pass_id,
                         gfx::Rect(viewport_size), gfx::Transform());
diff --git a/cc/output/in_process_context_provider.h b/cc/output/in_process_context_provider.h
index 1be3eef..d85f194 100644
--- a/cc/output/in_process_context_provider.h
+++ b/cc/output/in_process_context_provider.h
@@ -25,10 +25,6 @@
 class GpuMemoryBufferManager;
 class ImageFactory;
 struct SharedMemoryLimits;
-class SyncPointManager;
-namespace gles2 {
-class MailboxManager;
-}
 }
 
 namespace skia_bindings {
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc
index 141878f4..4472c9e 100644
--- a/cc/output/overlay_unittest.cc
+++ b/cc/output/overlay_unittest.cc
@@ -196,15 +196,12 @@
 };
 
 std::unique_ptr<RenderPass> CreateRenderPass() {
-  RenderPassId id(1, 0);
+  int render_pass_id = 1;
   gfx::Rect output_rect(0, 0, 256, 256);
   bool has_transparent_background = true;
 
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
-  pass->SetAll(id,
-               output_rect,
-               output_rect,
-               gfx::Transform(),
+  pass->SetAll(render_pass_id, output_rect, output_rect, gfx::Transform(),
                has_transparent_background);
 
   SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
@@ -1771,8 +1768,7 @@
     CALayerOverlayTest::SetUp();
     pass_ = CreateRenderPass();
     quad_ = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
-    render_pass_id_.layer_id = 3;
-    render_pass_id_.index = 17;
+    render_pass_id_ = 3;
   }
 
   void ProcessForOverlays() {
@@ -1784,7 +1780,7 @@
   }
   std::unique_ptr<RenderPass> pass_;
   RenderPassDrawQuad* quad_;
-  RenderPassId render_pass_id_;
+  int render_pass_id_;
   FilterOperations filters_;
   FilterOperations background_filters_;
   CALayerOverlayList ca_layer_list_;
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index ffd204a9..56a3d0d 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -33,7 +33,7 @@
 namespace {
 
 #if !defined(OS_ANDROID)
-std::unique_ptr<RenderPass> CreateTestRootRenderPass(RenderPassId id,
+std::unique_ptr<RenderPass> CreateTestRootRenderPass(int id,
                                                      const gfx::Rect& rect) {
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
   const gfx::Rect output_rect = rect;
@@ -44,7 +44,7 @@
 }
 
 std::unique_ptr<RenderPass> CreateTestRenderPass(
-    RenderPassId id,
+    int id,
     const gfx::Rect& rect,
     const gfx::Transform& transform_to_root_target) {
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
@@ -92,7 +92,7 @@
 
 void CreateTestRenderPassDrawQuad(const SharedQuadState* shared_state,
                                   const gfx::Rect& rect,
-                                  RenderPassId pass_id,
+                                  int pass_id,
                                   RenderPass* render_pass) {
   RenderPassDrawQuad* quad =
       render_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
@@ -675,7 +675,7 @@
 TYPED_TEST(RendererPixelTest, SimpleGreenRect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -698,7 +698,7 @@
   gfx::Rect rect(this->device_viewport_size_);
   gfx::Rect small_rect(100, 100);
 
-  RenderPassId child_id(2, 1);
+  int child_id = 2;
   std::unique_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_id, small_rect, gfx::Transform());
 
@@ -709,7 +709,7 @@
       child_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   color_quad->SetNew(child_shared_state, rect, rect, SK_ColorGREEN, false);
 
-  RenderPassId root_id(1, 1);
+  int root_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRenderPass(root_id, rect, gfx::Transform());
 
@@ -735,7 +735,7 @@
 TYPED_TEST(RendererPixelTest, PremultipliedTextureWithoutBackground) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -765,7 +765,7 @@
 TYPED_TEST(RendererPixelTest, PremultipliedTextureWithBackground) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* texture_quad_state =
@@ -810,7 +810,7 @@
     quad_rect_ = gfx::Rect(0, 0, this->device_viewport_size_.width(),
                            this->device_viewport_size_.height() / 2.0);
 
-    RenderPassId id(1, 1);
+    int id = 1;
     render_pass_ = CreateTestRootRenderPass(id, viewport_rect_);
 
     // Create the front quad rotated on the Z and Y axis.
@@ -996,8 +996,8 @@
 
 TYPED_TEST(IntersectingQuadPixelTest, RenderPassQuads) {
   this->SetupQuadStateAndRenderPass();
-  RenderPassId child_pass_id1(2, 2);
-  RenderPassId child_pass_id2(2, 3);
+  int child_pass_id1 = 2;
+  int child_pass_id2 = 3;
   std::unique_ptr<RenderPass> child_pass1 =
       CreateTestRenderPass(child_pass_id1, this->quad_rect_, gfx::Transform());
   SharedQuadState* child1_quad_state = CreateTestSharedQuadState(
@@ -1089,7 +1089,7 @@
 TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithoutBackground) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1120,7 +1120,7 @@
 TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* texture_quad_state =
@@ -1157,7 +1157,7 @@
                            RenderPassList* pass_list) {
     gfx::Rect rect(200, 200);
 
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
     // Scale the video up so that bilinear filtering kicks in to sample more
@@ -1205,7 +1205,7 @@
 TEST_P(VideoGLRendererPixelHiLoTest, SimpleYUVRect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1231,7 +1231,7 @@
   gfx::Rect draw_rect(this->device_viewport_size_.width() * 1.5,
                       this->device_viewport_size_.height() * 1.5);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, viewport);
 
   SharedQuadState* shared_state =
@@ -1254,7 +1254,7 @@
 TEST_F(VideoGLRendererPixelHiLoTest, OffsetYUVRect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1277,7 +1277,7 @@
 TEST_F(VideoGLRendererPixelTest, SimpleYUVRectBlack) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1305,7 +1305,7 @@
 TEST_F(VideoGLRendererPixelTest, SimpleYUVJRect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1328,7 +1328,7 @@
 TEST_F(VideoGLRendererPixelTest, SimpleNV12JRect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1371,7 +1371,7 @@
 TEST_F(VideoGLRendererPixelTest, SimpleYUVJRectGrey) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1395,7 +1395,7 @@
 TEST_F(VideoGLRendererPixelHiLoTest, SimpleYUVARect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1422,7 +1422,7 @@
 TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1449,7 +1449,7 @@
 TEST_F(VideoGLRendererPixelTest, TwoColorY16Rect) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
@@ -1473,11 +1473,11 @@
 TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1557,11 +1557,11 @@
 TYPED_TEST(RendererPixelTest, FastPassSaturateFilter) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1622,11 +1622,11 @@
 TYPED_TEST(RendererPixelTest, FastPassFilterChain) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1689,11 +1689,11 @@
 TYPED_TEST(RendererPixelTest, FastPassColorFilterAlphaTranslation) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1777,11 +1777,11 @@
 TYPED_TEST(RendererPixelTest, EnlargedRenderPassTexture) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1826,11 +1826,11 @@
 TYPED_TEST(RendererPixelTest, EnlargedRenderPassTextureWithAntiAliasing) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -1890,13 +1890,13 @@
 TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
   SharedQuadState* root_pass_shared_state = CreateTestSharedQuadState(
       gfx::Transform(), viewport_rect, root_pass.get());
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, viewport_rect, transform_to_root);
@@ -1985,13 +1985,13 @@
 TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad2) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
   SharedQuadState* root_pass_shared_state = CreateTestSharedQuadState(
       gfx::Transform(), viewport_rect, root_pass.get());
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
       CreateTestRenderPass(child_pass_id, viewport_rect, transform_to_root);
@@ -2078,14 +2078,14 @@
   void SetUpRenderPassList() {
     gfx::Rect device_viewport_rect(this->device_viewport_size_);
 
-    RenderPassId root_id(1, 1);
+    int root_id = 1;
     std::unique_ptr<RenderPass> root_pass =
         CreateTestRootRenderPass(root_id, device_viewport_rect);
     root_pass->has_transparent_background = false;
 
     gfx::Transform identity_quad_to_target_transform;
 
-    RenderPassId filter_pass_id(2, 1);
+    int filter_pass_id = 2;
     gfx::Transform transform_to_root;
     std::unique_ptr<RenderPass> filter_pass = CreateTestRenderPass(
         filter_pass_id, filter_pass_layer_rect_, transform_to_root);
@@ -2238,7 +2238,7 @@
   // Draw a blue quad that covers the entire device viewport. It should be
   // clipped to the bottom left and top right corners by the external stencil.
   gfx::Rect rect(this->device_viewport_size_);
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
   SharedQuadState* blue_shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
@@ -2261,7 +2261,7 @@
   // Draw a green quad that covers the entire device viewport. The stencil
   // buffer should be ignored.
   gfx::Rect rect(this->device_viewport_size_);
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
   SharedQuadState* green_shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
@@ -2285,12 +2285,12 @@
 
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
   root_pass->has_transparent_background = false;
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -2326,7 +2326,7 @@
 TEST_F(GLRendererPixelTest, AntiAliasing) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   gfx::Transform red_quad_to_target_transform;
@@ -2368,7 +2368,7 @@
 TEST_F(GLRendererPixelTest, AxisAligned) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, rect, transform_to_root);
@@ -2415,7 +2415,7 @@
 TEST_F(GLRendererPixelTest, ForceAntiAliasingOff) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, rect, transform_to_root);
@@ -2452,8 +2452,7 @@
 TEST_F(GLRendererPixelTest, AntiAliasingPerspective) {
   gfx::Rect rect(this->device_viewport_size_);
 
-  std::unique_ptr<RenderPass> pass =
-      CreateTestRootRenderPass(RenderPassId(1, 1), rect);
+  std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(1, rect);
 
   gfx::Rect red_rect(0, 0, 180, 500);
   gfx::Transform red_quad_to_target_transform(
@@ -2493,7 +2492,7 @@
   ResourceFormat texture_format = RGBA_8888;
   bool nearest_neighbor = false;
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2569,7 +2568,7 @@
   ResourceFormat texture_format = RGBA_8888;
   bool nearest_neighbor = false;
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2651,7 +2650,7 @@
   ResourceFormat texture_format = RGBA_8888;
   bool nearest_neighbor = false;
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2700,7 +2699,7 @@
   ResourceFormat texture_format = RGBA_8888;
   bool nearest_neighbor = true;
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2769,7 +2768,7 @@
         resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size);
   }
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2820,7 +2819,7 @@
         resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size);
   }
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2872,7 +2871,7 @@
         resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size);
   }
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -2904,7 +2903,7 @@
   ResourceFormat texture_format = RGBA_8888;
   bool nearest_neighbor = false;
 
-  RenderPassId id(1, 1);
+  int id = 1;
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> pass =
       CreateTestRenderPass(id, viewport, transform_to_root);
@@ -3047,11 +3046,11 @@
   // This draws a blue rect above a yellow rect with an inverted output surface.
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -3095,11 +3094,11 @@
   // This draws a blue rect above a yellow rect with an inverted output surface.
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -3143,11 +3142,11 @@
 TEST_F(GLRendererPixelTest, CheckReadbackSubset) {
   gfx::Rect viewport_rect(this->device_viewport_size_);
 
-  RenderPassId root_pass_id(1, 1);
+  int root_pass_id = 1;
   std::unique_ptr<RenderPass> root_pass =
       CreateTestRootRenderPass(root_pass_id, viewport_rect);
 
-  RenderPassId child_pass_id(2, 2);
+  int child_pass_id = 2;
   gfx::Rect pass_rect(this->device_viewport_size_);
   gfx::Transform transform_to_root;
   std::unique_ptr<RenderPass> child_pass =
@@ -3200,7 +3199,7 @@
 
   gfx::Rect rect(this->device_viewport_size_);
 
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
 
   SharedQuadState* shared_state =
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
index 89382ec..dcab4404 100644
--- a/cc/output/software_renderer_unittest.cc
+++ b/cc/output/software_renderer_unittest.cc
@@ -99,7 +99,7 @@
 
   InitializeRenderer(base::WrapUnique(new SoftwareOutputDevice));
 
-  RenderPassId root_render_pass_id = RenderPassId(1, 1);
+  int root_render_pass_id = 1;
   std::unique_ptr<RenderPass> root_render_pass = RenderPass::Create();
   root_render_pass->SetNew(
       root_render_pass_id, outer_rect, outer_rect, gfx::Transform());
@@ -165,7 +165,7 @@
 
   gfx::Rect root_rect = outer_rect;
 
-  RenderPassId root_render_pass_id = RenderPassId(1, 1);
+  int root_render_pass_id = 1;
   std::unique_ptr<RenderPass> root_render_pass = RenderPass::Create();
   root_render_pass->SetNew(
       root_render_pass_id, root_rect, root_rect, gfx::Transform());
@@ -225,7 +225,7 @@
 
   gfx::Rect root_rect(tile_size);
 
-  RenderPassId root_render_pass_id = RenderPassId(1, 1);
+  int root_render_pass_id = 1;
   std::unique_ptr<RenderPass> root_render_pass = RenderPass::Create();
   root_render_pass->SetNew(
       root_render_pass_id, root_rect, root_rect, gfx::Transform());
@@ -279,7 +279,7 @@
   RenderPassList list;
 
   // Draw a fullscreen green quad in a first frame.
-  RenderPassId root_clear_pass_id(1, 0);
+  int root_clear_pass_id = 1;
   RenderPass* root_clear_pass = AddRenderPass(
       &list, root_clear_pass_id, gfx::Rect(viewport_size), gfx::Transform());
   AddQuad(root_clear_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
@@ -301,7 +301,7 @@
   // frame.
   gfx::Rect smaller_rect(20, 20, 60, 60);
 
-  RenderPassId root_smaller_pass_id(2, 0);
+  int root_smaller_pass_id = 2;
   RenderPass* root_smaller_pass = AddRenderPass(
       &list, root_smaller_pass_id, gfx::Rect(viewport_size), gfx::Transform());
   AddQuad(root_smaller_pass, smaller_rect, SK_ColorMAGENTA);
@@ -333,13 +333,13 @@
 
   // Pass drawn as inner quad is magenta.
   gfx::Rect smaller_rect(20, 20, 60, 60);
-  RenderPassId smaller_pass_id(2, 1);
+  int smaller_pass_id = 2;
   RenderPass* smaller_pass =
       AddRenderPass(&list, smaller_pass_id, smaller_rect, gfx::Transform());
   AddQuad(smaller_pass, smaller_rect, SK_ColorMAGENTA);
 
   // Root pass is green.
-  RenderPassId root_clear_pass_id(1, 0);
+  int root_clear_pass_id = 1;
   RenderPass* root_clear_pass = AddRenderPass(
       &list, root_clear_pass_id, gfx::Rect(viewport_size), gfx::Transform());
   AddRenderPassQuad(root_clear_pass, smaller_pass);
@@ -411,7 +411,7 @@
 
   RenderPassList list;
 
-  RenderPassId root_pass_id(1, 0);
+  int root_pass_id = 1;
   RenderPass* root_pass = AddRenderPass(
       &list, root_pass_id, gfx::Rect(viewport_size), gfx::Transform());
   AddQuad(root_pass, gfx::Rect(viewport_size), SK_ColorGREEN);
diff --git a/cc/playback/clip_display_item.cc b/cc/playback/clip_display_item.cc
index c35eb22e..0d7b8011 100644
--- a/cc/playback/clip_display_item.cc
+++ b/cc/playback/clip_display_item.cc
@@ -22,11 +22,13 @@
 
 ClipDisplayItem::ClipDisplayItem(const gfx::Rect& clip_rect,
                                  const std::vector<SkRRect>& rounded_clip_rects,
-                                 bool antialias) {
+                                 bool antialias)
+    : DisplayItem(CLIP) {
   SetNew(clip_rect, rounded_clip_rects, antialias);
 }
 
-ClipDisplayItem::ClipDisplayItem(const proto::DisplayItem& proto) {
+ClipDisplayItem::ClipDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(CLIP) {
   DCHECK_EQ(proto::DisplayItem::Type_Clip, proto.type());
 
   const proto::ClipDisplayItem& details = proto.clip_item();
@@ -103,13 +105,10 @@
   array->AppendString(value);
 }
 
-size_t ClipDisplayItem::ExternalMemoryUsage() const {
-  return rounded_clip_rects_.capacity() * sizeof(rounded_clip_rects_[0]);
-}
+EndClipDisplayItem::EndClipDisplayItem() : DisplayItem(END_CLIP) {}
 
-EndClipDisplayItem::EndClipDisplayItem() {}
-
-EndClipDisplayItem::EndClipDisplayItem(const proto::DisplayItem& proto) {
+EndClipDisplayItem::EndClipDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(END_CLIP) {
   DCHECK_EQ(proto::DisplayItem::Type_EndClip, proto.type());
 }
 
@@ -132,8 +131,4 @@
                                          visual_rect.ToString().c_str()));
 }
 
-size_t EndClipDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/clip_display_item.h b/cc/playback/clip_display_item.h
index 72fa6bc7..cd5c3b6 100644
--- a/cc/playback/clip_display_item.h
+++ b/cc/playback/clip_display_item.h
@@ -32,8 +32,10 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const {
+    return rounded_clip_rects_.capacity() * sizeof(rounded_clip_rects_[0]);
+  }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -57,7 +59,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/playback/clip_path_display_item.cc b/cc/playback/clip_path_display_item.cc
index e0542b220..5b5240d 100644
--- a/cc/playback/clip_path_display_item.cc
+++ b/cc/playback/clip_path_display_item.cc
@@ -17,11 +17,13 @@
 
 ClipPathDisplayItem::ClipPathDisplayItem(const SkPath& clip_path,
                                          SkClipOp clip_op,
-                                         bool antialias) {
+                                         bool antialias)
+    : DisplayItem(CLIP_PATH) {
   SetNew(clip_path, clip_op, antialias);
 }
 
-ClipPathDisplayItem::ClipPathDisplayItem(const proto::DisplayItem& proto) {
+ClipPathDisplayItem::ClipPathDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(CLIP_PATH) {
   DCHECK_EQ(proto::DisplayItem::Type_ClipPath, proto.type());
 
   const proto::ClipPathDisplayItem& details = proto.clip_path_item();
@@ -79,16 +81,10 @@
       clip_path_.countPoints(), visual_rect.ToString().c_str()));
 }
 
-size_t ClipPathDisplayItem::ExternalMemoryUsage() const {
-  // The size of SkPath's external storage is not currently accounted for (and
-  // may well be shared anyway).
-  return 0;
-}
+EndClipPathDisplayItem::EndClipPathDisplayItem() : DisplayItem(END_CLIP_PATH) {}
 
-EndClipPathDisplayItem::EndClipPathDisplayItem() {}
-
-EndClipPathDisplayItem::EndClipPathDisplayItem(
-    const proto::DisplayItem& proto) {
+EndClipPathDisplayItem::EndClipPathDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(END_CLIP_PATH) {
   DCHECK_EQ(proto::DisplayItem::Type_EndClipPath, proto.type());
 }
 
@@ -113,8 +109,4 @@
                          visual_rect.ToString().c_str()));
 }
 
-size_t EndClipPathDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/clip_path_display_item.h b/cc/playback/clip_path_display_item.h
index e1eccd82..f8ea8ab6 100644
--- a/cc/playback/clip_path_display_item.h
+++ b/cc/playback/clip_path_display_item.h
@@ -30,8 +30,12 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const {
+    // The size of SkPath's external storage is not currently accounted for (and
+    // may well be shared anyway).
+    return 0;
+  }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -57,7 +61,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/playback/compositing_display_item.cc b/cc/playback/compositing_display_item.cc
index 2d8197d..e2eab3b 100644
--- a/cc/playback/compositing_display_item.cc
+++ b/cc/playback/compositing_display_item.cc
@@ -27,13 +27,14 @@
     SkBlendMode xfermode,
     SkRect* bounds,
     sk_sp<SkColorFilter> cf,
-    bool lcd_text_requires_opaque_layer) {
+    bool lcd_text_requires_opaque_layer)
+    : DisplayItem(COMPOSITING) {
   SetNew(alpha, xfermode, bounds, std::move(cf),
          lcd_text_requires_opaque_layer);
 }
 
-CompositingDisplayItem::CompositingDisplayItem(
-    const proto::DisplayItem& proto) {
+CompositingDisplayItem::CompositingDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(COMPOSITING) {
   DCHECK_EQ(proto::DisplayItem::Type_Compositing, proto.type());
 
   const proto::CompositingDisplayItem& details = proto.compositing_item();
@@ -124,15 +125,12 @@
   array->AppendString(info);
 }
 
-size_t CompositingDisplayItem::ExternalMemoryUsage() const {
-  // TODO(pdr): Include color_filter's memory here.
-  return 0;
-}
-
-EndCompositingDisplayItem::EndCompositingDisplayItem() {}
+EndCompositingDisplayItem::EndCompositingDisplayItem()
+    : DisplayItem(END_COMPOSITING) {}
 
 EndCompositingDisplayItem::EndCompositingDisplayItem(
-    const proto::DisplayItem& proto) {
+    const proto::DisplayItem& proto)
+    : DisplayItem(END_COMPOSITING) {
   DCHECK_EQ(proto::DisplayItem::Type_EndCompositing, proto.type());
 }
 
@@ -157,8 +155,4 @@
                          visual_rect.ToString().c_str()));
 }
 
-size_t EndCompositingDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/compositing_display_item.h b/cc/playback/compositing_display_item.h
index 48432828..2e1b740 100644
--- a/cc/playback/compositing_display_item.h
+++ b/cc/playback/compositing_display_item.h
@@ -37,8 +37,11 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const {
+    // TODO(pdr): Include color_filter's memory here.
+    return 0;
+  }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -71,7 +74,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/playback/discardable_image_map.cc b/cc/playback/discardable_image_map.cc
index e4ae644..be273d5b 100644
--- a/cc/playback/discardable_image_map.cc
+++ b/cc/playback/discardable_image_map.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "cc/base/math_util.h"
 #include "cc/playback/display_item_list.h"
+#include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/utils/SkNWayCanvas.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/skia_util.h"
@@ -100,7 +101,7 @@
     }
     SkMatrix matrix;
     matrix.setRectToRect(*src, dst, SkMatrix::kFill_ScaleToFit);
-    matrix.postConcat(ctm);
+    matrix.preConcat(ctm);
     AddImage(sk_ref_sp(image), *src, MapRect(ctm, dst), matrix, paint);
   }
 
@@ -112,6 +113,30 @@
     NOTREACHED();
   }
 
+  void onDrawRect(const SkRect& r, const SkPaint& paint) override {
+    AddPaintImage(r, paint);
+  }
+
+  void onDrawPath(const SkPath& path, const SkPaint& paint) override {
+    AddPaintImage(path.getBounds(), paint);
+  }
+
+  void onDrawOval(const SkRect& r, const SkPaint& paint) override {
+    AddPaintImage(r, paint);
+  }
+
+  void onDrawArc(const SkRect& r,
+                 SkScalar start_angle,
+                 SkScalar sweep_angle,
+                 bool use_center,
+                 const SkPaint& paint) override {
+    AddPaintImage(r, paint);
+  }
+
+  void onDrawRRect(const SkRRect& rr, const SkPaint& paint) override {
+    AddPaintImage(rr.rect(), paint);
+  }
+
   SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
     saved_paints_.push_back(*rec.fPaint);
     return SkNWayCanvas::getSaveLayerStrategy(rec);
@@ -178,6 +203,26 @@
         SafeClampPaintRectToSize(paint_rect, canvas_size_)));
   }
 
+  // Currently this function only handles extracting images from SkImageShaders
+  // embedded in SkPaints. Other embedded image cases, such as SkPictures,
+  // are not yet handled.
+  void AddPaintImage(const SkRect& rect, const SkPaint& paint) {
+    SkShader* shader = paint.getShader();
+    if (shader) {
+      SkMatrix matrix;
+      SkShader::TileMode xy[2];
+      SkImage* image = shader->isAImage(&matrix, xy);
+      if (image) {
+        const SkMatrix& ctm = getTotalMatrix();
+        matrix.postConcat(ctm);
+        // TODO(ericrk): Handle cases where we only need a sub-rect from the
+        // image. crbug.com/671821
+        AddImage(sk_ref_sp(image), SkRect::MakeFromIRect(image->bounds()),
+                 MapRect(ctm, rect), matrix, &paint);
+      }
+    }
+  }
+
   std::vector<std::pair<DrawImage, gfx::Rect>>* image_set_;
   const SkRect canvas_bounds_;
   const gfx::Size canvas_size_;
diff --git a/cc/playback/discardable_image_map_unittest.cc b/cc/playback/discardable_image_map_unittest.cc
index 1dfdb3a..3a8d90b 100644
--- a/cc/playback/discardable_image_map_unittest.cc
+++ b/cc/playback/discardable_image_map_unittest.cc
@@ -25,18 +25,21 @@
 namespace cc {
 namespace {
 
-struct PositionDrawImage {
-  PositionDrawImage(sk_sp<const SkImage> image, const gfx::Rect& image_rect)
-      : image(std::move(image)), image_rect(image_rect) {}
+struct PositionScaleDrawImage {
+  PositionScaleDrawImage(sk_sp<const SkImage> image,
+                         const gfx::Rect& image_rect,
+                         const SkSize& scale)
+      : image(std::move(image)), image_rect(image_rect), scale(scale) {}
   sk_sp<const SkImage> image;
   gfx::Rect image_rect;
+  SkSize scale;
 };
 
 }  // namespace
 
 class DiscardableImageMapTest : public testing::Test {
  public:
-  std::vector<PositionDrawImage> GetDiscardableImagesInRect(
+  std::vector<PositionScaleDrawImage> GetDiscardableImagesInRect(
       const DiscardableImageMap& image_map,
       const gfx::Rect& rect) {
     std::vector<DrawImage> draw_images;
@@ -45,11 +48,12 @@
 
     std::vector<size_t> indices;
     image_map.images_rtree_.Search(rect, &indices);
-    std::vector<PositionDrawImage> position_draw_images;
+    std::vector<PositionScaleDrawImage> position_draw_images;
     for (size_t index : indices) {
       position_draw_images.push_back(
-          PositionDrawImage(image_map.all_images_[index].first.image(),
-                            image_map.all_images_[index].second));
+          PositionScaleDrawImage(image_map.all_images_[index].first.image(),
+                                 image_map.all_images_[index].second,
+                                 image_map.all_images_[index].first.scale()));
     }
 
     EXPECT_EQ(draw_images.size(), position_draw_images.size());
@@ -100,7 +104,7 @@
 
   for (int y = 0; y < 4; ++y) {
     for (int x = 0; x < 4; ++x) {
-      std::vector<PositionDrawImage> images = GetDiscardableImagesInRect(
+      std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(x * 512, y * 512, 500, 500));
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
@@ -115,7 +119,7 @@
   }
 
   // Capture 4 pixel refs.
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
   EXPECT_EQ(4u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image[1][2]);
@@ -173,7 +177,7 @@
 
   for (int y = 0; y < 4; ++y) {
     for (int x = 0; x < 4; ++x) {
-      std::vector<PositionDrawImage> images = GetDiscardableImagesInRect(
+      std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(1024 + x * 512, y * 512, 500, 500));
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
@@ -188,7 +192,7 @@
   }
   // Capture 4 pixel refs.
   {
-    std::vector<PositionDrawImage> images = GetDiscardableImagesInRect(
+    std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
         image_map, gfx::Rect(1024 + 512, 512, 2048, 2048));
     EXPECT_EQ(4u, images.size());
     EXPECT_TRUE(images[0].image == discardable_image[1][2]);
@@ -207,22 +211,22 @@
 
   // Non intersecting rects
   {
-    std::vector<PositionDrawImage> images =
+    std::vector<PositionScaleDrawImage> images =
         GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1000, 1000));
     EXPECT_EQ(0u, images.size());
   }
   {
-    std::vector<PositionDrawImage> images =
+    std::vector<PositionScaleDrawImage> images =
         GetDiscardableImagesInRect(image_map, gfx::Rect(3500, 0, 1000, 1000));
     EXPECT_EQ(0u, images.size());
   }
   {
-    std::vector<PositionDrawImage> images =
+    std::vector<PositionScaleDrawImage> images =
         GetDiscardableImagesInRect(image_map, gfx::Rect(0, 1100, 1000, 1000));
     EXPECT_EQ(0u, images.size());
   }
   {
-    std::vector<PositionDrawImage> images = GetDiscardableImagesInRect(
+    std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
         image_map, gfx::Rect(3500, 1100, 1000, 1000));
     EXPECT_EQ(0u, images.size());
   }
@@ -269,7 +273,7 @@
 
   for (int y = 0; y < 4; ++y) {
     for (int x = 0; x < 4; ++x) {
-      std::vector<PositionDrawImage> images = GetDiscardableImagesInRect(
+      std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
           image_map, gfx::Rect(x * 512 + 256, y * 512 + 256, 1, 1));
       if ((x + y) & 1) {
         EXPECT_EQ(1u, images.size()) << x << " " << y;
@@ -305,7 +309,7 @@
                                                            visible_rect.size());
     display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
   }
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
@@ -332,7 +336,7 @@
     generator.canvas()->restore();
   }
 
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
@@ -360,7 +364,7 @@
                                                            visible_rect.size());
     display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
   }
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(42, 42, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_TRUE(images[0].image == discardable_image);
@@ -399,7 +403,7 @@
                                                            visible_rect.size());
     display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
   }
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), images[0].image_rect);
@@ -450,7 +454,7 @@
                                                            visible_rect.size());
     display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
   }
-  std::vector<PositionDrawImage> images =
+  std::vector<PositionScaleDrawImage> images =
       GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
   EXPECT_EQ(1u, images.size());
   EXPECT_EQ(gfx::Rect(0, 0, 90, 89), images[0].image_rect);
@@ -464,4 +468,79 @@
   EXPECT_EQ(gfx::Rect(0, 500, 1000, 100), images[0].image_rect);
 }
 
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
+  gfx::Rect visible_rect(2048, 2048);
+  FakeContentLayerClient content_layer_client;
+  content_layer_client.set_bounds(visible_rect.size());
+
+  // Discardable pixel refs are found in the following grids:
+  // |---|---|---|---|
+  // |   | x |   | x |
+  // |---|---|---|---|
+  // | x |   | x |   |
+  // |---|---|---|---|
+  // |   | x |   | x |
+  // |---|---|---|---|
+  // | x |   | x |   |
+  // |---|---|---|---|
+  sk_sp<SkImage> discardable_image[4][4];
+  for (int y = 0; y < 4; ++y) {
+    for (int x = 0; x < 4; ++x) {
+      if ((x + y) & 1) {
+        discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500));
+        SkMatrix scale = SkMatrix::MakeScale(x * 0.5f, y * 0.5f);
+        SkPaint paint;
+        paint.setShader(discardable_image[y][x]->makeShader(
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &scale));
+        content_layer_client.add_draw_rect(
+            gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), paint);
+      }
+    }
+  }
+
+  scoped_refptr<DisplayItemList> display_list =
+      content_layer_client.PaintContentsToDisplayList(
+          ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+  DiscardableImageMap image_map;
+  {
+    DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+                                                           visible_rect.size());
+    display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+  }
+
+  for (int y = 0; y < 4; ++y) {
+    for (int x = 0; x < 4; ++x) {
+      std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+          image_map, gfx::Rect(x * 512, y * 512, 500, 500));
+      if ((x + y) & 1) {
+        EXPECT_EQ(1u, images.size()) << x << " " << y;
+        EXPECT_TRUE(images[0].image == discardable_image[y][x]) << x << " "
+                                                                << y;
+        EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
+                  images[0].image_rect);
+        EXPECT_EQ(x * 0.5f, images[0].scale.fWidth);
+        EXPECT_EQ(y * 0.5f, images[0].scale.fHeight);
+      } else {
+        EXPECT_EQ(0u, images.size()) << x << " " << y;
+      }
+    }
+  }
+
+  // Capture 4 pixel refs.
+  std::vector<PositionScaleDrawImage> images =
+      GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
+  EXPECT_EQ(4u, images.size());
+  EXPECT_TRUE(images[0].image == discardable_image[1][2]);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), images[0].image_rect);
+  EXPECT_TRUE(images[1].image == discardable_image[2][1]);
+  EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), images[1].image_rect);
+  EXPECT_TRUE(images[2].image == discardable_image[2][3]);
+  EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500),
+            images[2].image_rect);
+  EXPECT_TRUE(images[3].image == discardable_image[3][2]);
+  EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500),
+            images[3].image_rect);
+}
+
 }  // namespace cc
diff --git a/cc/playback/display_item.cc b/cc/playback/display_item.cc
index 015a0b61..2f8d202 100644
--- a/cc/playback/display_item.cc
+++ b/cc/playback/display_item.cc
@@ -6,9 +6,6 @@
 
 namespace cc {
 
-DisplayItem::DisplayItem() {
-}
-
 sk_sp<const SkPicture> DisplayItem::GetPicture() const {
   return nullptr;
 }
diff --git a/cc/playback/display_item.h b/cc/playback/display_item.h
index 08d3d01..4e74e907 100644
--- a/cc/playback/display_item.h
+++ b/cc/playback/display_item.h
@@ -24,6 +24,22 @@
 
 class CC_EXPORT DisplayItem {
  public:
+  enum Type {
+    CLIP,
+    END_CLIP,
+    CLIP_PATH,
+    END_CLIP_PATH,
+    COMPOSITING,
+    END_COMPOSITING,
+    DRAWING,
+    FILTER,
+    END_FILTER,
+    FLOAT_CLIP,
+    END_FLOAT_CLIP,
+    TRANSFORM,
+    END_TRANSFORM,
+  };
+
   virtual ~DisplayItem() {}
 
   virtual void ToProtobuf(proto::DisplayItem* proto) const = 0;
@@ -32,11 +48,13 @@
                       SkPicture::AbortCallback* callback) const = 0;
   virtual void AsValueInto(const gfx::Rect& visual_rect,
                            base::trace_event::TracedValue* array) const = 0;
-  // For tracing.
-  virtual size_t ExternalMemoryUsage() const = 0;
+
+  Type type() const { return type_; }
 
  protected:
-  DisplayItem();
+  explicit DisplayItem(Type type) : type_(type) {}
+
+  const Type type_;
 };
 
 }  // namespace cc
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
index 068cb00..2009f1c 100644
--- a/cc/playback/display_item_list.cc
+++ b/cc/playback/display_item_list.cc
@@ -16,10 +16,16 @@
 #include "cc/debug/picture_debug_util.h"
 #include "cc/debug/traced_display_item_list.h"
 #include "cc/debug/traced_value.h"
+#include "cc/playback/clip_display_item.h"
+#include "cc/playback/clip_path_display_item.h"
+#include "cc/playback/compositing_display_item.h"
 #include "cc/playback/display_item_list_settings.h"
 #include "cc/playback/display_item_proto_factory.h"
 #include "cc/playback/drawing_display_item.h"
+#include "cc/playback/filter_display_item.h"
+#include "cc/playback/float_clip_display_item.h"
 #include "cc/playback/largest_display_item.h"
+#include "cc/playback/transform_display_item.h"
 #include "cc/proto/display_item.pb.h"
 #include "cc/proto/gfx_conversions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -183,7 +189,6 @@
   return approximate_op_count_;
 }
 
-DISABLE_CFI_PERF
 size_t DisplayItemList::ApproximateMemoryUsage() const {
   size_t memory_usage = sizeof(*this);
 
@@ -191,7 +196,44 @@
   // Warning: this double-counts SkPicture data if use_cached_picture is
   // also true.
   for (const auto& item : inputs_.items) {
-    external_memory_usage += item.ExternalMemoryUsage();
+    size_t bytes = 0;
+    switch (item.type()) {
+      case DisplayItem::CLIP:
+        bytes = static_cast<const ClipDisplayItem&>(item).ExternalMemoryUsage();
+        break;
+      case DisplayItem::CLIP_PATH:
+        bytes =
+            static_cast<const ClipPathDisplayItem&>(item).ExternalMemoryUsage();
+        break;
+      case DisplayItem::COMPOSITING:
+        bytes = static_cast<const CompositingDisplayItem&>(item)
+                    .ExternalMemoryUsage();
+        break;
+      case DisplayItem::DRAWING:
+        bytes =
+            static_cast<const DrawingDisplayItem&>(item).ExternalMemoryUsage();
+        break;
+      case DisplayItem::FLOAT_CLIP:
+        bytes = static_cast<const FloatClipDisplayItem&>(item)
+                    .ExternalMemoryUsage();
+        break;
+      case DisplayItem::FILTER:
+        bytes =
+            static_cast<const FilterDisplayItem&>(item).ExternalMemoryUsage();
+        break;
+      case DisplayItem::TRANSFORM:
+        bytes = static_cast<const TransformDisplayItem&>(item)
+                    .ExternalMemoryUsage();
+        break;
+      case DisplayItem::END_CLIP:
+      case DisplayItem::END_CLIP_PATH:
+      case DisplayItem::END_COMPOSITING:
+      case DisplayItem::END_FLOAT_CLIP:
+      case DisplayItem::END_FILTER:
+      case DisplayItem::END_TRANSFORM:
+        break;
+    }
+    external_memory_usage += bytes;
   }
 
   // Memory outside this class due to |items_|.
diff --git a/cc/playback/drawing_display_item.cc b/cc/playback/drawing_display_item.cc
index 91bdfb7..4d1b3d2 100644
--- a/cc/playback/drawing_display_item.cc
+++ b/cc/playback/drawing_display_item.cc
@@ -26,16 +26,18 @@
 
 namespace cc {
 
-DrawingDisplayItem::DrawingDisplayItem() {}
+DrawingDisplayItem::DrawingDisplayItem() : DisplayItem(DRAWING) {}
 
-DrawingDisplayItem::DrawingDisplayItem(sk_sp<const SkPicture> picture) {
+DrawingDisplayItem::DrawingDisplayItem(sk_sp<const SkPicture> picture)
+    : DisplayItem(DRAWING) {
   SetNew(std::move(picture));
 }
 
 DrawingDisplayItem::DrawingDisplayItem(
     const proto::DisplayItem& proto,
     ClientPictureCache* client_picture_cache,
-    std::vector<uint32_t>* used_engine_picture_ids) {
+    std::vector<uint32_t>* used_engine_picture_ids)
+    : DisplayItem(DRAWING) {
   DCHECK_EQ(proto::DisplayItem::Type_Drawing, proto.type());
   DCHECK(client_picture_cache);
 
@@ -52,7 +54,8 @@
   SetNew(std::move(picture));
 }
 
-DrawingDisplayItem::DrawingDisplayItem(const DrawingDisplayItem& item) {
+DrawingDisplayItem::DrawingDisplayItem(const DrawingDisplayItem& item)
+    : DisplayItem(DRAWING) {
   item.CloneTo(this);
 }
 
diff --git a/cc/playback/drawing_display_item.h b/cc/playback/drawing_display_item.h
index 0bad7753..aad8253 100644
--- a/cc/playback/drawing_display_item.h
+++ b/cc/playback/drawing_display_item.h
@@ -37,8 +37,8 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const;
   int ApproximateOpCount() const;
 
   void CloneTo(DrawingDisplayItem* item) const;
diff --git a/cc/playback/filter_display_item.cc b/cc/playback/filter_display_item.cc
index 212d6e4..41d8784 100644
--- a/cc/playback/filter_display_item.cc
+++ b/cc/playback/filter_display_item.cc
@@ -21,11 +21,13 @@
 
 FilterDisplayItem::FilterDisplayItem(const FilterOperations& filters,
                                      const gfx::RectF& bounds,
-                                     const gfx::PointF& origin) {
+                                     const gfx::PointF& origin)
+    : DisplayItem(FILTER) {
   SetNew(filters, bounds, origin);
 }
 
-FilterDisplayItem::FilterDisplayItem(const proto::DisplayItem& proto) {
+FilterDisplayItem::FilterDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(FILTER) {
   DCHECK_EQ(proto::DisplayItem::Type_Filter, proto.type());
 
   const proto::FilterDisplayItem& details = proto.filter_item();
@@ -82,15 +84,10 @@
       bounds_.ToString().c_str(), visual_rect.ToString().c_str()));
 }
 
-size_t FilterDisplayItem::ExternalMemoryUsage() const {
-  // FilterOperations doesn't expose its capacity, but size is probably good
-  // enough.
-  return filters_.size() * sizeof(filters_.at(0));
-}
+EndFilterDisplayItem::EndFilterDisplayItem() : DisplayItem(END_FILTER) {}
 
-EndFilterDisplayItem::EndFilterDisplayItem() {}
-
-EndFilterDisplayItem::EndFilterDisplayItem(const proto::DisplayItem& proto) {
+EndFilterDisplayItem::EndFilterDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(END_FILTER) {
   DCHECK_EQ(proto::DisplayItem::Type_EndFilter, proto.type());
 }
 
@@ -114,8 +111,4 @@
                          visual_rect.ToString().c_str()));
 }
 
-size_t EndFilterDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/filter_display_item.h b/cc/playback/filter_display_item.h
index b4ba8ce..499d05bb5 100644
--- a/cc/playback/filter_display_item.h
+++ b/cc/playback/filter_display_item.h
@@ -32,8 +32,12 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const {
+    // FilterOperations doesn't expose its capacity, but size is probably good
+    // enough.
+    return filters_.size() * sizeof(filters_.at(0));
+  }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -61,7 +65,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/playback/float_clip_display_item.cc b/cc/playback/float_clip_display_item.cc
index cdf16a04..def09c9 100644
--- a/cc/playback/float_clip_display_item.cc
+++ b/cc/playback/float_clip_display_item.cc
@@ -15,11 +15,13 @@
 
 namespace cc {
 
-FloatClipDisplayItem::FloatClipDisplayItem(const gfx::RectF& clip_rect) {
+FloatClipDisplayItem::FloatClipDisplayItem(const gfx::RectF& clip_rect)
+    : DisplayItem(FLOAT_CLIP) {
   SetNew(clip_rect);
 }
 
-FloatClipDisplayItem::FloatClipDisplayItem(const proto::DisplayItem& proto) {
+FloatClipDisplayItem::FloatClipDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(FLOAT_CLIP) {
   DCHECK_EQ(proto::DisplayItem::Type_FloatClip, proto.type());
 
   const proto::FloatClipDisplayItem& details = proto.float_clip_item();
@@ -56,14 +58,12 @@
       clip_rect_.ToString().c_str(), visual_rect.ToString().c_str()));
 }
 
-size_t FloatClipDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
-EndFloatClipDisplayItem::EndFloatClipDisplayItem() {}
+EndFloatClipDisplayItem::EndFloatClipDisplayItem()
+    : DisplayItem(END_FLOAT_CLIP) {}
 
 EndFloatClipDisplayItem::EndFloatClipDisplayItem(
-    const proto::DisplayItem& proto) {
+    const proto::DisplayItem& proto)
+    : DisplayItem(END_FLOAT_CLIP) {
   DCHECK_EQ(proto::DisplayItem::Type_EndFloatClip, proto.type());
 }
 
@@ -88,8 +88,4 @@
                          visual_rect.ToString().c_str()));
 }
 
-size_t EndFloatClipDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/float_clip_display_item.h b/cc/playback/float_clip_display_item.h
index 744aac15..7f39ad6 100644
--- a/cc/playback/float_clip_display_item.h
+++ b/cc/playback/float_clip_display_item.h
@@ -30,8 +30,8 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const { return 0; }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -55,7 +55,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/playback/image_hijack_canvas.cc b/cc/playback/image_hijack_canvas.cc
index 04b24df..98fd4717 100644
--- a/cc/playback/image_hijack_canvas.cc
+++ b/cc/playback/image_hijack_canvas.cc
@@ -7,6 +7,7 @@
 #include "base/optional.h"
 #include "cc/playback/discardable_image_map.h"
 #include "cc/tiles/image_decode_cache.h"
+#include "third_party/skia/include/core/SkPath.h"
 
 namespace cc {
 namespace {
@@ -38,9 +39,18 @@
     }
   }
 
+  ScopedDecodedImageLock(ScopedDecodedImageLock&& from)
+      : image_decode_cache_(from.image_decode_cache_),
+        draw_image_(std::move(from.draw_image_)),
+        decoded_draw_image_(std::move(from.decoded_draw_image_)),
+        decoded_paint_(std::move(from.decoded_paint_)) {
+    from.image_decode_cache_ = nullptr;
+  }
+
   ~ScopedDecodedImageLock() {
-    image_decode_cache_->DrawWithImageFinished(draw_image_,
-                                               decoded_draw_image_);
+    if (image_decode_cache_)
+      image_decode_cache_->DrawWithImageFinished(draw_image_,
+                                                 decoded_draw_image_);
   }
 
   const DecodedDrawImage& decoded_image() const { return decoded_draw_image_; }
@@ -55,6 +65,59 @@
   base::Optional<SkPaint> decoded_paint_;
 };
 
+// Encapsulates a ScopedDecodedImageLock and an SkPaint. Use of this class
+// ensures that the ScopedDecodedImageLock outlives the dependent SkPaint.
+class ScopedImagePaint {
+ public:
+  // Tries to create a ScopedImagePaint for the provided SkPaint. If a
+  // the SkPaint does not contain an image that we support replacing,
+  // an empty base::Optional will be returned.
+  static base::Optional<ScopedImagePaint> TryCreate(
+      ImageDecodeCache* image_decode_cache,
+      const SkMatrix& ctm,
+      const SkPaint& paint) {
+    SkShader* shader = paint.getShader();
+    if (!shader)
+      return base::Optional<ScopedImagePaint>();
+
+    SkMatrix matrix;
+    SkShader::TileMode xy[2];
+    SkImage* image = shader->isAImage(&matrix, xy);
+    if (!image)
+      return base::Optional<ScopedImagePaint>();
+
+    SkMatrix total_image_matrix = matrix;
+    total_image_matrix.preConcat(ctm);
+
+    ScopedDecodedImageLock scoped_lock(
+        image_decode_cache, sk_ref_sp(image),
+        SkRect::MakeIWH(image->width(), image->height()), total_image_matrix,
+        &paint);
+    const DecodedDrawImage& decoded_image = scoped_lock.decoded_image();
+    if (!decoded_image.image())
+      return base::Optional<ScopedImagePaint>();
+
+    bool need_scale = !decoded_image.is_scale_adjustment_identity();
+    if (need_scale) {
+      matrix.preScale(1.f / decoded_image.scale_adjustment().width(),
+                      1.f / decoded_image.scale_adjustment().height());
+    }
+    SkPaint scratch_paint = paint;
+    scratch_paint.setShader(
+        decoded_image.image()->makeShader(xy[0], xy[1], &matrix));
+    return ScopedImagePaint(std::move(scoped_lock), std::move(scratch_paint));
+  }
+
+  const SkPaint& paint() { return paint_; }
+
+ private:
+  ScopedImagePaint(ScopedDecodedImageLock lock, SkPaint paint)
+      : lock_(std::move(lock)), paint_(std::move(paint)) {}
+
+  ScopedDecodedImageLock lock_;
+  SkPaint paint_;
+};
+
 }  // namespace
 
 ImageHijackCanvas::ImageHijackCanvas(int width,
@@ -144,6 +207,61 @@
                                 decoded_paint, constraint);
 }
 
+void ImageHijackCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
+  base::Optional<ScopedImagePaint> image_paint =
+      ScopedImagePaint::TryCreate(image_decode_cache_, getTotalMatrix(), paint);
+  if (!image_paint.has_value()) {
+    SkNWayCanvas::onDrawRect(r, paint);
+    return;
+  }
+  SkNWayCanvas::onDrawRect(r, image_paint.value().paint());
+}
+
+void ImageHijackCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
+  base::Optional<ScopedImagePaint> image_paint =
+      ScopedImagePaint::TryCreate(image_decode_cache_, getTotalMatrix(), paint);
+  if (!image_paint.has_value()) {
+    SkNWayCanvas::onDrawPath(path, paint);
+    return;
+  }
+  SkNWayCanvas::onDrawPath(path, image_paint.value().paint());
+}
+
+void ImageHijackCanvas::onDrawOval(const SkRect& r, const SkPaint& paint) {
+  base::Optional<ScopedImagePaint> image_paint =
+      ScopedImagePaint::TryCreate(image_decode_cache_, getTotalMatrix(), paint);
+  if (!image_paint.has_value()) {
+    SkNWayCanvas::onDrawOval(r, paint);
+    return;
+  }
+  SkNWayCanvas::onDrawOval(r, image_paint.value().paint());
+}
+
+void ImageHijackCanvas::onDrawArc(const SkRect& r,
+                                  SkScalar start_angle,
+                                  SkScalar sweep_angle,
+                                  bool use_center,
+                                  const SkPaint& paint) {
+  base::Optional<ScopedImagePaint> image_paint =
+      ScopedImagePaint::TryCreate(image_decode_cache_, getTotalMatrix(), paint);
+  if (!image_paint.has_value()) {
+    SkNWayCanvas::onDrawArc(r, start_angle, sweep_angle, use_center, paint);
+    return;
+  }
+  SkNWayCanvas::onDrawArc(r, start_angle, sweep_angle, use_center,
+                          image_paint.value().paint());
+}
+
+void ImageHijackCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) {
+  base::Optional<ScopedImagePaint> image_paint =
+      ScopedImagePaint::TryCreate(image_decode_cache_, getTotalMatrix(), paint);
+  if (!image_paint.has_value()) {
+    SkNWayCanvas::onDrawRRect(rr, paint);
+    return;
+  }
+  SkNWayCanvas::onDrawRRect(rr, image_paint.value().paint());
+}
+
 void ImageHijackCanvas::onDrawImageNine(const SkImage* image,
                                         const SkIRect& center,
                                         const SkRect& dst,
diff --git a/cc/playback/image_hijack_canvas.h b/cc/playback/image_hijack_canvas.h
index 57c2e86c..46495da 100644
--- a/cc/playback/image_hijack_canvas.h
+++ b/cc/playback/image_hijack_canvas.h
@@ -24,7 +24,6 @@
   void onDrawPicture(const SkPicture* picture,
                      const SkMatrix* matrix,
                      const SkPaint* paint) override;
-
   void onDrawImage(const SkImage* image,
                    SkScalar x,
                    SkScalar y,
@@ -34,6 +33,15 @@
                        const SkRect& dst,
                        const SkPaint* paint,
                        SrcRectConstraint constraint) override;
+  void onDrawRect(const SkRect&, const SkPaint&) override;
+  void onDrawPath(const SkPath& path, const SkPaint& paint) override;
+  void onDrawOval(const SkRect& r, const SkPaint& paint) override;
+  void onDrawArc(const SkRect& r,
+                 SkScalar start_angle,
+                 SkScalar sweep_angle,
+                 bool use_center,
+                 const SkPaint& paint) override;
+  void onDrawRRect(const SkRRect& rr, const SkPaint& paint) override;
   void onDrawImageNine(const SkImage* image,
                        const SkIRect& center,
                        const SkRect& dst,
diff --git a/cc/playback/transform_display_item.cc b/cc/playback/transform_display_item.cc
index ef2813626..a495d9a 100644
--- a/cc/playback/transform_display_item.cc
+++ b/cc/playback/transform_display_item.cc
@@ -15,11 +15,12 @@
 namespace cc {
 
 TransformDisplayItem::TransformDisplayItem(const gfx::Transform& transform)
-    : transform_(gfx::Transform::kSkipInitialization) {
+    : DisplayItem(TRANSFORM), transform_(gfx::Transform::kSkipInitialization) {
   SetNew(transform);
 }
 
-TransformDisplayItem::TransformDisplayItem(const proto::DisplayItem& proto) {
+TransformDisplayItem::TransformDisplayItem(const proto::DisplayItem& proto)
+    : DisplayItem(TRANSFORM) {
   DCHECK_EQ(proto::DisplayItem::Type_Transform, proto.type());
 
   const proto::TransformDisplayItem& details = proto.transform_item();
@@ -57,14 +58,12 @@
       transform_.ToString().c_str(), visual_rect.ToString().c_str()));
 }
 
-size_t TransformDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
-EndTransformDisplayItem::EndTransformDisplayItem() {}
+EndTransformDisplayItem::EndTransformDisplayItem()
+    : DisplayItem(END_TRANSFORM) {}
 
 EndTransformDisplayItem::EndTransformDisplayItem(
-    const proto::DisplayItem& proto) {
+    const proto::DisplayItem& proto)
+    : DisplayItem(END_TRANSFORM) {
   DCHECK_EQ(proto::DisplayItem::Type_EndTransform, proto.type());
 }
 
@@ -89,8 +88,4 @@
                          visual_rect.ToString().c_str()));
 }
 
-size_t EndTransformDisplayItem::ExternalMemoryUsage() const {
-  return 0;
-}
-
 }  // namespace cc
diff --git a/cc/playback/transform_display_item.h b/cc/playback/transform_display_item.h
index 15ec94b..728fe9d 100644
--- a/cc/playback/transform_display_item.h
+++ b/cc/playback/transform_display_item.h
@@ -29,8 +29,8 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
+  size_t ExternalMemoryUsage() const { return 0; }
   int ApproximateOpCount() const { return 1; }
 
  private:
@@ -54,7 +54,6 @@
               SkPicture::AbortCallback* callback) const override;
   void AsValueInto(const gfx::Rect& visual_rect,
                    base::trace_event::TracedValue* array) const override;
-  size_t ExternalMemoryUsage() const override;
 
   int ApproximateOpCount() const { return 0; }
 };
diff --git a/cc/quads/draw_polygon_unittest.cc b/cc/quads/draw_polygon_unittest.cc
index 847a25dc..6938b88f 100644
--- a/cc/quads/draw_polygon_unittest.cc
+++ b/cc/quads/draw_polygon_unittest.cc
@@ -380,10 +380,10 @@
       gfx::Vector3dF(sqrt(2) / 2, -sqrt(2) / 2, 0.000000), 1);
 
   // These are well formed, convex polygons.
-  EXPECT_TRUE(IsPlanarForTesting(*(polygon_a.get())));
-  EXPECT_TRUE(IsConvexForTesting(*(polygon_a.get())));
-  EXPECT_TRUE(IsPlanarForTesting(*(polygon_b.get())));
-  EXPECT_TRUE(IsConvexForTesting(*(polygon_b.get())));
+  EXPECT_TRUE(IsPlanarForTesting(*polygon_a));
+  EXPECT_TRUE(IsConvexForTesting(*polygon_a));
+  EXPECT_TRUE(IsPlanarForTesting(*polygon_b));
+  EXPECT_TRUE(IsConvexForTesting(*polygon_b));
 
   std::unique_ptr<DrawPolygon> front_polygon;
   std::unique_ptr<DrawPolygon> back_polygon;
@@ -435,8 +435,8 @@
   test_points_b.push_back(gfx::Point3F(5.0f, 10.0f, -5.0f));
   test_points_b.push_back(gfx::Point3F(5.0f, 0.0f, -5.0f));
   test_points_b.push_back(gfx::Point3F(5.0f, 0.0f, 0.0f));
-  ValidatePoints(*(front_polygon.get()), test_points_a);
-  ValidatePoints(*(back_polygon.get()), test_points_b);
+  ValidatePoints(*front_polygon, test_points_a);
+  ValidatePoints(*back_polygon, test_points_b);
 
   EXPECT_EQ(4u, front_polygon->points().size());
   EXPECT_EQ(4u, back_polygon->points().size());
@@ -482,8 +482,8 @@
   test_points_b.push_back(gfx::Point3F(10.0f, 0.0f, 10.0f));
   test_points_b.push_back(gfx::Point3F(10.0f, 0.0f, 9.0f));
 
-  ValidatePointsWithinDeltaOf(*(front_polygon.get()), test_points_a, 1e-6f);
-  ValidatePointsWithinDeltaOf(*(back_polygon.get()), test_points_b, 1e-6f);
+  ValidatePointsWithinDeltaOf(*front_polygon, test_points_a, 1e-6f);
+  ValidatePointsWithinDeltaOf(*back_polygon, test_points_b, 1e-6f);
 }
 
 // In this test we cut the corner of a quad so that it creates a triangle and
diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc
index d8edaba6..7303a16 100644
--- a/cc/quads/draw_quad_unittest.cc
+++ b/cc/quads/draw_quad_unittest.cc
@@ -432,7 +432,7 @@
 
 TEST(DrawQuadTest, CopyRenderPassDrawQuad) {
   gfx::Rect visible_rect(40, 50, 30, 20);
-  RenderPassId render_pass_id(22, 64);
+  int render_pass_id = 61;
   ResourceId mask_resource_id = 78;
   gfx::Vector2dF mask_uv_scale(33.f, 19.f);
   gfx::Size mask_texture_size(128, 134);
@@ -444,7 +444,7 @@
   background_filters.Append(
       FilterOperation::CreateGrayscaleFilter(1.f));
 
-  RenderPassId copied_render_pass_id(235, 11);
+  int copied_render_pass_id = 235;
   CREATE_SHARED_STATE();
 
   CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
@@ -753,7 +753,7 @@
 
 TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) {
   gfx::Rect visible_rect(40, 50, 30, 20);
-  RenderPassId render_pass_id(22, 64);
+  int render_pass_id = 61;
   ResourceId mask_resource_id = 78;
   gfx::Vector2dF mask_uv_scale(33.f, 19.f);
   gfx::Size mask_texture_size(128, 134);
@@ -765,7 +765,7 @@
   background_filters.Append(
       FilterOperation::CreateGrayscaleFilter(1.f));
 
-  RenderPassId copied_render_pass_id(235, 11);
+  int copied_render_pass_id = 235;
 
   CREATE_SHARED_STATE();
   CREATE_QUAD_NEW_RP(RenderPassDrawQuad, visible_rect, render_pass_id,
diff --git a/cc/quads/render_pass.cc b/cc/quads/render_pass.cc
index 6ce19bf1..58ccc9b 100644
--- a/cc/quads/render_pass.cc
+++ b/cc/quads/render_pass.cc
@@ -60,11 +60,9 @@
 }
 
 RenderPass::RenderPass()
-    : has_transparent_background(true),
-      quad_list(kDefaultNumQuadsToReserve),
+    : quad_list(kDefaultNumQuadsToReserve),
       shared_quad_state_list(sizeof(SharedQuadState),
-                             kDefaultNumSharedQuadStatesToReserve) {
-}
+                             kDefaultNumSharedQuadStatesToReserve) {}
 
 // Each layer usually produces one shared quad state, so the number of layers
 // is a good hint for what to reserve here.
@@ -84,11 +82,11 @@
 
 RenderPass::~RenderPass() {
   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
-      TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"),
-      "cc::RenderPass", id.AsTracingId());
+      TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), "cc::RenderPass",
+      reinterpret_cast<void*>(id));
 }
 
-std::unique_ptr<RenderPass> RenderPass::Copy(RenderPassId new_id) const {
+std::unique_ptr<RenderPass> RenderPass::Copy(int new_id) const {
   std::unique_ptr<RenderPass> copy_pass(
       Create(shared_quad_state_list.size(), quad_list.size()));
   copy_pass->SetAll(new_id,
@@ -145,11 +143,11 @@
     out->push_back(source->DeepCopy());
 }
 
-void RenderPass::SetNew(RenderPassId id,
+void RenderPass::SetNew(int id,
                         const gfx::Rect& output_rect,
                         const gfx::Rect& damage_rect,
                         const gfx::Transform& transform_to_root_target) {
-  DCHECK(id.IsValid());
+  DCHECK(id);
   DCHECK(damage_rect.IsEmpty() || output_rect.Contains(damage_rect))
       << "damage_rect: " << damage_rect.ToString()
       << " output_rect: " << output_rect.ToString();
@@ -163,12 +161,12 @@
   DCHECK(shared_quad_state_list.empty());
 }
 
-void RenderPass::SetAll(RenderPassId id,
+void RenderPass::SetAll(int id,
                         const gfx::Rect& output_rect,
                         const gfx::Rect& damage_rect,
                         const gfx::Transform& transform_to_root_target,
                         bool has_transparent_background) {
-  DCHECK(id.IsValid());
+  DCHECK(id);
 
   this->id = id;
   this->output_rect = output_rect;
@@ -205,10 +203,8 @@
   value->EndArray();
 
   TracedValue::MakeDictIntoImplicitSnapshotWithCategory(
-      TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"),
-      value,
-      "cc::RenderPass",
-      id.AsTracingId());
+      TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), value, "cc::RenderPass",
+      reinterpret_cast<void*>(id));
 }
 
 SharedQuadState* RenderPass::CreateAndAppendSharedQuadState() {
@@ -218,7 +214,7 @@
 RenderPassDrawQuad* RenderPass::CopyFromAndAppendRenderPassDrawQuad(
     const RenderPassDrawQuad* quad,
     const SharedQuadState* shared_quad_state,
-    RenderPassId render_pass_id) {
+    int render_pass_id) {
   RenderPassDrawQuad* copy_quad =
       CopyFromAndAppendTypedDrawQuad<RenderPassDrawQuad>(quad);
   copy_quad->shared_quad_state = shared_quad_state;
diff --git a/cc/quads/render_pass.h b/cc/quads/render_pass.h
index 7859ac32..aa9fc6d 100644
--- a/cc/quads/render_pass.h
+++ b/cc/quads/render_pass.h
@@ -18,8 +18,6 @@
 #include "cc/base/list_container.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/quads/largest_draw_quad.h"
-#include "cc/quads/render_pass_id.h"
-#include "cc/surfaces/surface_id.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/transform.h"
@@ -65,7 +63,7 @@
 
   // A shallow copy of the render pass, which does not include its quads or copy
   // requests.
-  std::unique_ptr<RenderPass> Copy(RenderPassId new_id) const;
+  std::unique_ptr<RenderPass> Copy(int new_id) const;
 
   // A deep copy of the render pass that includes quads.
   std::unique_ptr<RenderPass> DeepCopy() const;
@@ -74,12 +72,12 @@
   static void CopyAll(const std::vector<std::unique_ptr<RenderPass>>& in,
                       std::vector<std::unique_ptr<RenderPass>>* out);
 
-  void SetNew(RenderPassId id,
+  void SetNew(int id,
               const gfx::Rect& output_rect,
               const gfx::Rect& damage_rect,
               const gfx::Transform& transform_to_root_target);
 
-  void SetAll(RenderPassId id,
+  void SetAll(int id,
               const gfx::Rect& output_rect,
               const gfx::Rect& damage_rect,
               const gfx::Transform& transform_to_root_target,
@@ -97,12 +95,12 @@
   RenderPassDrawQuad* CopyFromAndAppendRenderPassDrawQuad(
       const RenderPassDrawQuad* quad,
       const SharedQuadState* shared_quad_state,
-      RenderPassId render_pass_id);
+      int render_pass_id);
   DrawQuad* CopyFromAndAppendDrawQuad(const DrawQuad* quad,
                                       const SharedQuadState* shared_quad_state);
 
   // Uniquely identifies the render pass in the compositor's current frame.
-  RenderPassId id;
+  int id = 0;
 
   // These are in the space of the render pass' physical pixels.
   gfx::Rect output_rect;
@@ -113,7 +111,7 @@
   gfx::Transform transform_to_root_target;
 
   // If false, the pixels in the render pass' texture are all opaque.
-  bool has_transparent_background;
+  bool has_transparent_background = true;
 
   // If non-empty, the renderer should produce a copy of the render pass'
   // contents as a bitmap, and give a copy of the bitmap to each callback in
@@ -139,8 +137,6 @@
 };
 
 using RenderPassList = std::vector<std::unique_ptr<RenderPass>>;
-using RenderPassIdHashMap =
-    std::unordered_map<RenderPassId, RenderPass*, RenderPassIdHash>;
 
 }  // namespace cc
 
diff --git a/cc/quads/render_pass_draw_quad.cc b/cc/quads/render_pass_draw_quad.cc
index 0eb217d..1260a1d 100644
--- a/cc/quads/render_pass_draw_quad.cc
+++ b/cc/quads/render_pass_draw_quad.cc
@@ -25,7 +25,7 @@
 void RenderPassDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
                                 const gfx::Rect& rect,
                                 const gfx::Rect& visible_rect,
-                                RenderPassId render_pass_id,
+                                int render_pass_id,
                                 ResourceId mask_resource_id,
                                 const gfx::Vector2dF& mask_uv_scale,
                                 const gfx::Size& mask_texture_size,
@@ -33,7 +33,7 @@
                                 const gfx::Vector2dF& filters_scale,
                                 const gfx::PointF& filters_origin,
                                 const FilterOperations& background_filters) {
-  DCHECK(render_pass_id.IsValid());
+  DCHECK(render_pass_id);
 
   gfx::Rect opaque_rect;
   bool needs_blending = false;
@@ -47,7 +47,7 @@
                                 const gfx::Rect& opaque_rect,
                                 const gfx::Rect& visible_rect,
                                 bool needs_blending,
-                                RenderPassId render_pass_id,
+                                int render_pass_id,
                                 ResourceId mask_resource_id,
                                 const gfx::Vector2dF& mask_uv_scale,
                                 const gfx::Size& mask_texture_size,
@@ -55,7 +55,7 @@
                                 const gfx::Vector2dF& filters_scale,
                                 const gfx::PointF& filters_origin,
                                 const FilterOperations& background_filters) {
-  DCHECK(render_pass_id.IsValid());
+  DCHECK(render_pass_id);
 
   DrawQuad::SetAll(shared_quad_state, DrawQuad::RENDER_PASS, rect, opaque_rect,
                    visible_rect, needs_blending);
@@ -82,7 +82,8 @@
 
 void RenderPassDrawQuad::ExtendValue(
     base::trace_event::TracedValue* value) const {
-  TracedValue::SetIDRef(render_pass_id.AsTracingId(), value, "render_pass_id");
+  TracedValue::SetIDRef(reinterpret_cast<void*>(render_pass_id), value,
+                        "render_pass_id");
   value->SetInteger("mask_resource_id", resources.ids[kMaskResourceIdIndex]);
   MathUtil::AddToTracedValue("mask_texture_size", mask_texture_size, value);
   MathUtil::AddToTracedValue("mask_uv_scale", mask_uv_scale, value);
diff --git a/cc/quads/render_pass_draw_quad.h b/cc/quads/render_pass_draw_quad.h
index 851451b..ba1cf5a 100644
--- a/cc/quads/render_pass_draw_quad.h
+++ b/cc/quads/render_pass_draw_quad.h
@@ -12,7 +12,6 @@
 #include "cc/base/cc_export.h"
 #include "cc/output/filter_operations.h"
 #include "cc/quads/draw_quad.h"
-#include "cc/quads/render_pass_id.h"
 
 #include "ui/gfx/geometry/point_f.h"
 
@@ -29,7 +28,7 @@
   void SetNew(const SharedQuadState* shared_quad_state,
               const gfx::Rect& rect,
               const gfx::Rect& visible_rect,
-              RenderPassId render_pass_id,
+              int render_pass_id,
               ResourceId mask_resource_id,
               const gfx::Vector2dF& mask_uv_scale,
               const gfx::Size& mask_texture_size,
@@ -43,7 +42,7 @@
               const gfx::Rect& opaque_rect,
               const gfx::Rect& visible_rect,
               bool needs_blending,
-              RenderPassId render_pass_id,
+              int render_pass_id,
               ResourceId mask_resource_id,
               const gfx::Vector2dF& mask_uv_scale,
               const gfx::Size& mask_texture_size,
@@ -52,7 +51,7 @@
               const gfx::PointF& filters_origin,
               const FilterOperations& background_filters);
 
-  RenderPassId render_pass_id;
+  int render_pass_id;
   gfx::Vector2dF mask_uv_scale;
   gfx::Size mask_texture_size;
 
diff --git a/cc/quads/render_pass_id.cc b/cc/quads/render_pass_id.cc
deleted file mode 100644
index 742c191..0000000
--- a/cc/quads/render_pass_id.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/quads/render_pass_id.h"
-
-#include <stddef.h>
-
-#include "base/hash.h"
-
-namespace cc {
-
-void* RenderPassId::AsTracingId() const {
-  static_assert(sizeof(size_t) <= sizeof(void*),  // NOLINT
-                "size of size_t should not be greater than that of a pointer");
-  return reinterpret_cast<void*>(
-      base::HashInts(layer_id, static_cast<int>(index)));
-}
-
-}  // namespace cc
diff --git a/cc/quads/render_pass_id.h b/cc/quads/render_pass_id.h
deleted file mode 100644
index 68a2051..0000000
--- a/cc/quads/render_pass_id.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_QUADS_RENDER_PASS_ID_H_
-#define CC_QUADS_RENDER_PASS_ID_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <tuple>
-
-#include "base/hash.h"
-#include "cc/base/cc_export.h"
-
-namespace cc {
-
-class CC_EXPORT RenderPassId {
- public:
-  int layer_id;
-  uint32_t index;
-
-  RenderPassId() : layer_id(-1), index(0) {}
-  RenderPassId(int layer_id, uint32_t index)
-      : layer_id(layer_id), index(index) {}
-  void* AsTracingId() const;
-
-  bool IsValid() const { return layer_id >= 0; }
-
-  bool operator==(const RenderPassId& other) const {
-    return layer_id == other.layer_id && index == other.index;
-  }
-  bool operator!=(const RenderPassId& other) const { return !(*this == other); }
-  bool operator<(const RenderPassId& other) const {
-    return std::tie(layer_id, index) < std::tie(other.layer_id, other.index);
-  }
-};
-
-struct RenderPassIdHash {
-  size_t operator()(RenderPassId key) const {
-    return base::HashInts(key.layer_id, static_cast<int>(key.index));
-  }
-};
-
-}  // namespace cc
-
-#endif  // CC_QUADS_RENDER_PASS_ID_H_
diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc
index ea2db7d..ba4e9ad1 100644
--- a/cc/quads/render_pass_unittest.cc
+++ b/cc/quads/render_pass_unittest.cc
@@ -22,7 +22,7 @@
 struct RenderPassSize {
   // If you add a new field to this class, make sure to add it to the
   // Copy() tests.
-  RenderPassId id;
+  int id;
   QuadList quad_list;
   SharedQuadStateList shared_quad_state_list;
   gfx::Transform transform_to_root_target;
@@ -63,7 +63,7 @@
 }
 
 TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) {
-  RenderPassId id(3, 2);
+  int id = 3;
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
@@ -88,7 +88,7 @@
   color_quad->SetNew(pass->shared_quad_state_list.back(), gfx::Rect(),
                      gfx::Rect(), SkColor(), false);
 
-  RenderPassId new_id(63, 4);
+  int new_id = 63;
 
   std::unique_ptr<RenderPass> copy = pass->Copy(new_id);
   EXPECT_EQ(new_id, copy->id);
@@ -108,7 +108,7 @@
 TEST(RenderPassTest, CopyAllShouldBeIdentical) {
   RenderPassList pass_list;
 
-  RenderPassId id(3, 2);
+  int id = 3;
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
@@ -157,7 +157,7 @@
                       false);
 
   // A second render pass with a quad.
-  RenderPassId contrib_id(4, 1);
+  int contrib_id = 4;
   gfx::Rect contrib_output_rect(10, 15, 12, 17);
   gfx::Transform contrib_transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
@@ -203,7 +203,7 @@
 TEST(RenderPassTest, CopyAllWithCulledQuads) {
   RenderPassList pass_list;
 
-  RenderPassId id(3, 2);
+  int id = 3;
   gfx::Rect output_rect(45, 22, 120, 13);
   gfx::Transform transform_to_root =
       gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0);
diff --git a/cc/surfaces/compositor_frame_sink_support.cc b/cc/surfaces/compositor_frame_sink_support.cc
index 011d9a09..d28ea4a2 100644
--- a/cc/surfaces/compositor_frame_sink_support.cc
+++ b/cc/surfaces/compositor_frame_sink_support.cc
@@ -8,6 +8,7 @@
 #include "cc/scheduler/begin_frame_source.h"
 #include "cc/surfaces/compositor_frame_sink_support_client.h"
 #include "cc/surfaces/display.h"
+#include "cc/surfaces/surface.h"
 #include "cc/surfaces/surface_manager.h"
 
 namespace cc {
@@ -33,6 +34,11 @@
 }
 
 CompositorFrameSinkSupport::~CompositorFrameSinkSupport() {
+  for (auto& child_frame_sink_id : child_frame_sinks_) {
+    DCHECK(child_frame_sink_id.is_valid());
+    surface_manager_->UnregisterFrameSinkHierarchy(frame_sink_id_,
+                                                   child_frame_sink_id);
+  }
   // SurfaceFactory's destructor will attempt to return resources which will
   // call back into here and access |client_| so we should destroy
   // |surface_factory_|'s resources early on.
@@ -78,6 +84,22 @@
   }
 }
 
+void CompositorFrameSinkSupport::Require(const LocalFrameId& local_frame_id,
+                                         const SurfaceSequence& sequence) {
+  Surface* surface = surface_manager_->GetSurfaceForId(
+      SurfaceId(frame_sink_id_, local_frame_id));
+  if (!surface) {
+    DLOG(ERROR) << "Attempting to require callback on nonexistent surface";
+    return;
+  }
+  surface->AddDestructionDependency(sequence);
+}
+
+void CompositorFrameSinkSupport::Satisfy(const SurfaceSequence& sequence) {
+  std::vector<uint32_t> sequences = {sequence.sequence};
+  surface_manager_->DidSatisfySequences(sequence.frame_sink_id, &sequences);
+}
+
 void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() {
   DCHECK_GT(ack_pending_count_, 0);
   ack_pending_count_--;
@@ -93,14 +115,19 @@
 
 void CompositorFrameSinkSupport::AddChildFrameSink(
     const FrameSinkId& child_frame_sink_id) {
+  child_frame_sinks_.insert(child_frame_sink_id);
   surface_manager_->RegisterFrameSinkHierarchy(frame_sink_id_,
                                                child_frame_sink_id);
 }
 
 void CompositorFrameSinkSupport::RemoveChildFrameSink(
     const FrameSinkId& child_frame_sink_id) {
+  auto it = child_frame_sinks_.find(child_frame_sink_id);
+  DCHECK(it != child_frame_sinks_.end());
+  DCHECK(it->is_valid());
   surface_manager_->UnregisterFrameSinkHierarchy(frame_sink_id_,
                                                  child_frame_sink_id);
+  child_frame_sinks_.erase(it);
 }
 
 void CompositorFrameSinkSupport::DisplayOutputSurfaceLost() {}
diff --git a/cc/surfaces/compositor_frame_sink_support.h b/cc/surfaces/compositor_frame_sink_support.h
index 1d89554..f9973d31 100644
--- a/cc/surfaces/compositor_frame_sink_support.h
+++ b/cc/surfaces/compositor_frame_sink_support.h
@@ -39,6 +39,9 @@
   void SetNeedsBeginFrame(bool needs_begin_frame);
   void SubmitCompositorFrame(const LocalFrameId& local_frame_id,
                              CompositorFrame frame);
+  void Require(const LocalFrameId& local_frame_id,
+               const SurfaceSequence& sequence);
+  void Satisfy(const SurfaceSequence& sequence);
   void AddChildFrameSink(const FrameSinkId& child_frame_sink_id);
   void RemoveChildFrameSink(const FrameSinkId& child_frame_sink_id);
 
@@ -94,6 +97,9 @@
   // Whether or not a frame observer has been added.
   bool added_frame_observer_ = false;
 
+  // The set of BeginFrame children of this CompositorFrameSink.
+  std::unordered_set<FrameSinkId, FrameSinkIdHash> child_frame_sinks_;
+
   base::WeakPtrFactory<CompositorFrameSinkSupport> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CompositorFrameSinkSupport);
diff --git a/cc/surfaces/direct_compositor_frame_sink_unittest.cc b/cc/surfaces/direct_compositor_frame_sink_unittest.cc
index af9b08bf..8b39040 100644
--- a/cc/surfaces/direct_compositor_frame_sink_unittest.cc
+++ b/cc/surfaces/direct_compositor_frame_sink_unittest.cc
@@ -74,8 +74,7 @@
 
   void SwapBuffersWithDamage(const gfx::Rect& damage_rect) {
     std::unique_ptr<RenderPass> render_pass(RenderPass::Create());
-    render_pass->SetNew(RenderPassId(1, 1), display_rect_, damage_rect,
-                        gfx::Transform());
+    render_pass->SetNew(1, display_rect_, damage_rect, gfx::Transform());
 
     CompositorFrame frame;
     frame.render_pass_list.push_back(std::move(render_pass));
diff --git a/cc/surfaces/display_unittest.cc b/cc/surfaces/display_unittest.cc
index 4534edb..a30f5a8 100644
--- a/cc/surfaces/display_unittest.cc
+++ b/cc/surfaces/display_unittest.cc
@@ -202,7 +202,7 @@
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
   pass->output_rect = gfx::Rect(0, 0, 100, 100);
   pass->damage_rect = gfx::Rect(10, 10, 1, 1);
-  pass->id = RenderPassId(1, 1);
+  pass->id = 1;
   pass_list.push_back(std::move(pass));
 
   scheduler_->ResetDamageForTest();
@@ -225,7 +225,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 1, 1);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -248,7 +248,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 0, 0);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -268,7 +268,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 99, 99);
     pass->damage_rect = gfx::Rect(10, 10, 10, 10);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -288,7 +288,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 0, 0);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -313,7 +313,7 @@
     bool copy_called = false;
     pass->copy_requests.push_back(CopyOutputRequest::CreateRequest(
         base::Bind(&CopyCallback, &copy_called)));
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -335,7 +335,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 0, 0);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -366,7 +366,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 200, 200);
     pass->damage_rect = gfx::Rect(10, 10, 10, 10);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -394,7 +394,7 @@
     pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 99, 99);
     pass->damage_rect = gfx::Rect(0, 0, 99, 99);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
 
     pass_list.push_back(std::move(pass));
     scheduler_->ResetDamageForTest();
@@ -445,7 +445,7 @@
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 1, 1);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
     pass_list.push_back(std::move(pass));
 
     SubmitCompositorFrame(&pass_list, local_frame_id);
@@ -471,7 +471,7 @@
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->output_rect = gfx::Rect(0, 0, 200, 200);
     pass->damage_rect = gfx::Rect(10, 10, 1, 1);
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
     pass_list.push_back(std::move(pass));
 
     SubmitCompositorFrame(&pass_list, local_frame_id);
diff --git a/cc/surfaces/surface.cc b/cc/surfaces/surface.cc
index ab51b6a..d1b59d6 100644
--- a/cc/surfaces/surface.cc
+++ b/cc/surfaces/surface.cc
@@ -118,8 +118,7 @@
 }
 
 void Surface::TakeCopyOutputRequests(
-    std::multimap<RenderPassId, std::unique_ptr<CopyOutputRequest>>*
-        copy_requests) {
+    std::multimap<int, std::unique_ptr<CopyOutputRequest>>* copy_requests) {
   DCHECK(copy_requests->empty());
   if (current_frame_) {
     for (const auto& render_pass : current_frame_->render_pass_list) {
diff --git a/cc/surfaces/surface.h b/cc/surfaces/surface.h
index 30a5b0c..cea948fb 100644
--- a/cc/surfaces/surface.h
+++ b/cc/surfaces/surface.h
@@ -19,7 +19,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "cc/output/copy_output_request.h"
-#include "cc/quads/render_pass_id.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/surface_factory.h"
 #include "cc/surfaces/surface_id.h"
@@ -56,10 +55,9 @@
   void EvictFrame();
   void RequestCopyOfOutput(std::unique_ptr<CopyOutputRequest> copy_request);
   // Adds each CopyOutputRequest in the current frame to copy_requests. The
-  // caller takes ownership of them.
+  // caller takes ownership of them. |copy_requests| is keyed by RenderPass ids.
   void TakeCopyOutputRequests(
-      std::multimap<RenderPassId, std::unique_ptr<CopyOutputRequest>>*
-          copy_requests);
+      std::multimap<int, std::unique_ptr<CopyOutputRequest>>* copy_requests);
 
   // Returns the most recent frame that is eligible to be rendered.
   // You must check whether HasFrame() returns true before calling this method.
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index 553a93b63..f32a468 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -33,11 +33,10 @@
 namespace {
 
 void MoveMatchingRequests(
-    RenderPassId id,
-    std::multimap<RenderPassId, std::unique_ptr<CopyOutputRequest>>*
-        copy_requests,
+    int render_pass_id,
+    std::multimap<int, std::unique_ptr<CopyOutputRequest>>* copy_requests,
     std::vector<std::unique_ptr<CopyOutputRequest>>* output_requests) {
-  auto request_range = copy_requests->equal_range(id);
+  auto request_range = copy_requests->equal_range(render_pass_id);
   for (auto it = request_range.first; it != request_range.second; ++it) {
     DCHECK(it->second);
     output_requests->push_back(std::move(it->second));
@@ -100,8 +99,8 @@
     surface_factory->UnrefResources(resources);
 }
 
-RenderPassId SurfaceAggregator::RemapPassId(RenderPassId surface_local_pass_id,
-                                            const SurfaceId& surface_id) {
+int SurfaceAggregator::RemapPassId(int surface_local_pass_id,
+                                   const SurfaceId& surface_id) {
   auto key = std::make_pair(surface_id, surface_local_pass_id);
   auto it = render_pass_allocator_map_.find(key);
   if (it != render_pass_allocator_map_.end()) {
@@ -110,7 +109,7 @@
   }
 
   RenderPassInfo render_pass_info;
-  render_pass_info.id = RenderPassId(1, next_render_pass_id_++);
+  render_pass_info.id = next_render_pass_id_++;
   render_pass_allocator_map_[key] = render_pass_info;
   return render_pass_info.id;
 }
@@ -173,7 +172,8 @@
     return;
   const CompositorFrame& frame = surface->GetEligibleFrame();
 
-  std::multimap<RenderPassId, std::unique_ptr<CopyOutputRequest>> copy_requests;
+  // A map keyed by RenderPass id.
+  std::multimap<int, std::unique_ptr<CopyOutputRequest>> copy_requests;
   surface->TakeCopyOutputRequests(&copy_requests);
 
   const RenderPassList& render_pass_list = frame.render_pass_list;
@@ -204,7 +204,7 @@
     std::unique_ptr<RenderPass> copy_pass(
         RenderPass::Create(sqs_size, dq_size));
 
-    RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id);
+    int remapped_pass_id = RemapPassId(source.id, surface_id);
 
     copy_pass->SetAll(remapped_pass_id, source.output_rect, source.output_rect,
                       source.transform_to_root_target,
@@ -266,7 +266,7 @@
                     child_to_parent_map, surface_transform, quads_clip,
                     dest_pass, surface_id);
   } else {
-    RenderPassId remapped_pass_id = RemapPassId(last_pass.id, surface_id);
+    int remapped_pass_id = RemapPassId(last_pass.id, surface_id);
 
     SharedQuadState* shared_quad_state =
         CopySharedQuadState(surface_quad->shared_quad_state, target_transform,
@@ -403,9 +403,8 @@
       if (quad->material == DrawQuad::RENDER_PASS) {
         const RenderPassDrawQuad* pass_quad =
             RenderPassDrawQuad::MaterialCast(quad);
-        RenderPassId original_pass_id = pass_quad->render_pass_id;
-        RenderPassId remapped_pass_id =
-            RemapPassId(original_pass_id, surface_id);
+        int original_pass_id = pass_quad->render_pass_id;
+        int remapped_pass_id = RemapPassId(original_pass_id, surface_id);
 
         dest_quad = dest_pass->CopyFromAndAppendRenderPassDrawQuad(
             pass_quad, dest_shared_quad_state, remapped_pass_id);
@@ -445,8 +444,9 @@
 void SurfaceAggregator::CopyPasses(const CompositorFrame& frame,
                                    Surface* surface) {
   // The root surface is allowed to have copy output requests, so grab them
-  // off its render passes.
-  std::multimap<RenderPassId, std::unique_ptr<CopyOutputRequest>> copy_requests;
+  // off its render passes. This map contains a set of CopyOutputRequests
+  // keyed by each RenderPass id.
+  std::multimap<int, std::unique_ptr<CopyOutputRequest>> copy_requests;
   surface->TakeCopyOutputRequests(&copy_requests);
 
   const RenderPassList& source_pass_list = frame.render_pass_list;
@@ -470,8 +470,7 @@
 
     MoveMatchingRequests(source.id, &copy_requests, &copy_pass->copy_requests);
 
-    RenderPassId remapped_pass_id =
-        RemapPassId(source.id, surface->surface_id());
+    int remapped_pass_id = RemapPassId(source.id, surface->surface_id());
 
     copy_pass->SetAll(remapped_pass_id, source.output_rect, source.output_rect,
                       source.transform_to_root_target,
@@ -520,7 +519,7 @@
 // return the combined damage rect.
 gfx::Rect SurfaceAggregator::PrewalkTree(const SurfaceId& surface_id,
                                          bool in_moved_pixel_pass,
-                                         RenderPassId parent_pass,
+                                         int parent_pass_id,
                                          PrewalkResult* result) {
   // This is for debugging a possible use after free.
   // TODO(jbauman): Remove this once we have enough information.
@@ -559,24 +558,24 @@
 
   CHECK(debug_weak_this.get());
   if (!frame.render_pass_list.empty()) {
-    RenderPassId remapped_pass_id =
+    int remapped_pass_id =
         RemapPassId(frame.render_pass_list.back()->id, surface_id);
     if (in_moved_pixel_pass)
       moved_pixel_passes_.insert(remapped_pass_id);
-    if (parent_pass.IsValid())
-      render_pass_dependencies_[parent_pass].insert(remapped_pass_id);
+    if (parent_pass_id)
+      render_pass_dependencies_[parent_pass_id].insert(remapped_pass_id);
   }
 
   struct SurfaceInfo {
     SurfaceId id;
     bool has_moved_pixels;
-    RenderPassId parent_pass;
+    int parent_pass_id;
     gfx::Transform target_to_surface_transform;
   };
   std::vector<SurfaceInfo> child_surfaces;
 
   for (const auto& render_pass : base::Reversed(frame.render_pass_list)) {
-    RenderPassId remapped_pass_id = RemapPassId(render_pass->id, surface_id);
+    int remapped_pass_id = RemapPassId(render_pass->id, surface_id);
     bool in_moved_pixel_pass = !!moved_pixel_passes_.count(remapped_pass_id);
     for (auto* quad : render_pass->quad_list) {
       if (quad->material == DrawQuad::SURFACE_CONTENT) {
@@ -640,7 +639,7 @@
   for (const auto& surface_info : child_surfaces) {
     gfx::Rect surface_damage =
         PrewalkTree(surface_info.id, surface_info.has_moved_pixels,
-                    surface_info.parent_pass, result);
+                    surface_info.parent_pass_id, result);
     if (surface_damage.IsEmpty())
       continue;
     if (surface_info.has_moved_pixels) {
@@ -658,7 +657,7 @@
   for (const auto& surface_id : frame.metadata.referenced_surfaces) {
     if (!contained_surfaces_.count(surface_id)) {
       result->undrawn_surfaces.insert(surface_id);
-      PrewalkTree(surface_id, false, RenderPassId(), result);
+      PrewalkTree(surface_id, false, 0, result);
     }
   }
 
@@ -671,7 +670,7 @@
   CHECK(debug_weak_this.get());
   for (const auto& render_pass : frame.render_pass_list) {
     if (!render_pass->copy_requests.empty()) {
-      RenderPassId remapped_pass_id = RemapPassId(render_pass->id, surface_id);
+      int remapped_pass_id = RemapPassId(render_pass->id, surface_id);
       copy_request_passes_.insert(remapped_pass_id);
     }
   }
@@ -725,10 +724,10 @@
 }
 
 void SurfaceAggregator::PropagateCopyRequestPasses() {
-  std::vector<RenderPassId> copy_requests_to_iterate(
-      copy_request_passes_.begin(), copy_request_passes_.end());
+  std::vector<int> copy_requests_to_iterate(copy_request_passes_.begin(),
+                                            copy_request_passes_.end());
   while (!copy_requests_to_iterate.empty()) {
-    RenderPassId first = copy_requests_to_iterate.back();
+    int first = copy_requests_to_iterate.back();
     copy_requests_to_iterate.pop_back();
     auto it = render_pass_dependencies_.find(first);
     if (it == render_pass_dependencies_.end())
@@ -759,8 +758,7 @@
 
   valid_surfaces_.clear();
   PrewalkResult prewalk_result;
-  root_damage_rect_ =
-      PrewalkTree(surface_id, false, RenderPassId(), &prewalk_result);
+  root_damage_rect_ = PrewalkTree(surface_id, false, 0, &prewalk_result);
   PropagateCopyRequestPasses();
   has_copy_requests_ = !copy_request_passes_.empty();
   frame.metadata.may_contain_video = prewalk_result.may_contain_video;
diff --git a/cc/surfaces/surface_aggregator.h b/cc/surfaces/surface_aggregator.h
index 04ecd907..46a2797d 100644
--- a/cc/surfaces/surface_aggregator.h
+++ b/cc/surfaces/surface_aggregator.h
@@ -65,7 +65,7 @@
 
   struct RenderPassInfo {
     // This is the id the pass is mapped to.
-    RenderPassId id;
+    int id;
     // This is true if the pass was used in the last aggregated frame.
     bool in_use = true;
   };
@@ -74,8 +74,7 @@
                              const ClipData& quad_clip,
                              const gfx::Transform& target_transform);
 
-  RenderPassId RemapPassId(RenderPassId surface_local_pass_id,
-                           const SurfaceId& surface_id);
+  int RemapPassId(int surface_local_pass_id, const SurfaceId& surface_id);
 
   void HandleSurfaceQuad(const SurfaceDrawQuad* surface_quad,
                          const gfx::Transform& target_transform,
@@ -95,7 +94,7 @@
       const SurfaceId& surface_id);
   gfx::Rect PrewalkTree(const SurfaceId& surface_id,
                         bool in_moved_pixel_pass,
-                        RenderPassId parent_pass,
+                        int parent_pass,
                         PrewalkResult* result);
   void CopyUndrawnSurfaces(PrewalkResult* prewalk);
   void CopyPasses(const CompositorFrame& frame, Surface* surface);
@@ -117,11 +116,11 @@
   ResourceProvider* provider_;
 
   // Every Surface has its own RenderPass ID namespace. This structure maps
-  // each source RenderPassID to a unified ID namespace that's used in the
-  // aggregated frame. An entry is removed from the map if it's not used
-  // for one output frame.
+  // each source (SurfaceId, RenderPass id) to a unified ID namespace that's
+  // used in the aggregated frame. An entry is removed from the map if it's not
+  // used for one output frame.
   using RenderPassIdAllocatorMap =
-      std::map<std::pair<SurfaceId, RenderPassId>, RenderPassInfo>;
+      std::map<std::pair<SurfaceId, int>, RenderPassInfo>;
   RenderPassIdAllocatorMap render_pass_allocator_map_;
   int next_render_pass_id_;
   const bool aggregate_only_damaged_;
@@ -153,18 +152,15 @@
 
   // This is the set of aggregated pass ids that are affected by filters that
   // move pixels.
-  std::unordered_set<RenderPassId, RenderPassIdHash> moved_pixel_passes_;
+  std::unordered_set<int> moved_pixel_passes_;
 
   // This is the set of aggregated pass ids that are drawn by copy requests, so
   // should not have their damage rects clipped to the root damage rect.
-  std::unordered_set<RenderPassId, RenderPassIdHash> copy_request_passes_;
+  std::unordered_set<int> copy_request_passes_;
 
   // This maps each aggregated pass id to the set of (aggregated) pass ids
   // that its RenderPassDrawQuads depend on
-  std::unordered_map<RenderPassId,
-                     std::unordered_set<RenderPassId, RenderPassIdHash>,
-                     RenderPassIdHash>
-      render_pass_dependencies_;
+  std::unordered_map<int, std::unordered_set<int>> render_pass_dependencies_;
 
   // The root damage rect of the currently-aggregating frame.
   gfx::Rect root_damage_rect_;
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc
index 8f1fda5..14a905cf 100644
--- a/cc/surfaces/surface_aggregator_unittest.cc
+++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -130,7 +130,7 @@
                                 &aggregated_frame.render_pass_list);
 
     // Ensure no duplicate pass ids output.
-    std::set<RenderPassId> used_passes;
+    std::set<int> used_passes;
     for (const auto& pass : aggregated_frame.render_pass_list) {
       EXPECT_TRUE(used_passes.insert(pass->id).second);
     }
@@ -247,9 +247,8 @@
                             test::Quad::SolidColorQuad(SK_ColorLTGRAY)},
                            {test::Quad::SolidColorQuad(SK_ColorGRAY),
                             test::Quad::SolidColorQuad(SK_ColorDKGRAY)}};
-  test::Pass passes[] = {
-      test::Pass(quads[0], arraysize(quads[0]), RenderPassId(1, 1)),
-      test::Pass(quads[1], arraysize(quads[1]), RenderPassId(1, 2))};
+  test::Pass passes[] = {test::Pass(quads[0], arraysize(quads[0]), 1),
+                         test::Pass(quads[1], arraysize(quads[1]), 2)};
 
   SubmitCompositorFrame(&factory_, passes, arraysize(passes),
                         root_local_frame_id_);
@@ -266,9 +265,8 @@
                             test::Quad::SolidColorQuad(SK_ColorLTGRAY)},
                            {test::Quad::SolidColorQuad(SK_ColorGRAY),
                             test::Quad::SolidColorQuad(SK_ColorDKGRAY)}};
-  test::Pass passes[] = {
-      test::Pass(quads[0], arraysize(quads[0]), RenderPassId(1, 2)),
-      test::Pass(quads[1], arraysize(quads[1]), RenderPassId(1, 1))};
+  test::Pass passes[] = {test::Pass(quads[0], arraysize(quads[0]), 2),
+                         test::Pass(quads[1], arraysize(quads[1]), 1)};
 
   SubmitCompositorFrame(&factory_, passes, arraysize(passes),
                         root_local_frame_id_);
@@ -277,25 +275,24 @@
 
   CompositorFrame aggregated_frame;
   aggregated_frame = aggregator_.Aggregate(surface_id);
-  RenderPassId id0 = aggregated_frame.render_pass_list[0]->id;
-  RenderPassId id1 = aggregated_frame.render_pass_list[1]->id;
+  auto id0 = aggregated_frame.render_pass_list[0]->id;
+  auto id1 = aggregated_frame.render_pass_list[1]->id;
   EXPECT_NE(id1, id0);
 
-  // Aggregated RenderPassIds should remain the same between frames.
+  // Aggregated RenderPass ids should remain the same between frames.
   aggregated_frame = aggregator_.Aggregate(surface_id);
   EXPECT_EQ(id0, aggregated_frame.render_pass_list[0]->id);
   EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
 
-  test::Pass passes2[] = {
-      test::Pass(quads[0], arraysize(quads[0]), RenderPassId(1, 3)),
-      test::Pass(quads[1], arraysize(quads[1]), RenderPassId(1, 1))};
+  test::Pass passes2[] = {test::Pass(quads[0], arraysize(quads[0]), 3),
+                          test::Pass(quads[1], arraysize(quads[1]), 1)};
 
   SubmitCompositorFrame(&factory_, passes2, arraysize(passes2),
                         root_local_frame_id_);
 
   // The RenderPass that still exists should keep the same ID.
   aggregated_frame = aggregator_.Aggregate(surface_id);
-  RenderPassId id2 = aggregated_frame.render_pass_list[0]->id;
+  auto id2 = aggregated_frame.render_pass_list[0]->id;
   EXPECT_NE(id2, id1);
   EXPECT_NE(id2, id0);
   EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
@@ -303,10 +300,10 @@
   SubmitCompositorFrame(&factory_, passes, arraysize(passes),
                         root_local_frame_id_);
 
-  // RenderPassId(1, 2) didn't exist in the previous frame, so it should be
+  // |id1| didn't exist in the previous frame, so it should be
   // mapped to a new ID.
   aggregated_frame = aggregator_.Aggregate(surface_id);
-  RenderPassId id3 = aggregated_frame.render_pass_list[0]->id;
+  auto id3 = aggregated_frame.render_pass_list[0]->id;
   EXPECT_NE(id3, id2);
   EXPECT_NE(id3, id1);
   EXPECT_NE(id3, id0);
@@ -433,8 +430,8 @@
                              test::Quad::SolidColorQuad(SK_ColorBLACK)};
   test::Quad root_quads2[] = {test::Quad::SolidColorQuad(SK_ColorRED)};
   test::Pass root_passes[] = {
-      test::Pass(root_quads, arraysize(root_quads), RenderPassId(1, 1)),
-      test::Pass(root_quads2, arraysize(root_quads2), RenderPassId(1, 2))};
+      test::Pass(root_quads, arraysize(root_quads), 1),
+      test::Pass(root_quads2, arraysize(root_quads2), 2)};
   {
     CompositorFrame frame;
     AddPasses(&frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
@@ -588,8 +585,7 @@
   SurfaceId embedded_surface_id(child_factory_.frame_sink_id(),
                                 embedded_local_frame_id);
 
-  RenderPassId pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2),
-                             RenderPassId(1, 3)};
+  int pass_ids[] = {1, 2, 3};
 
   test::Quad embedded_quads[][2] = {
       {test::Quad::SolidColorQuad(1), test::Quad::SolidColorQuad(2)},
@@ -623,7 +619,7 @@
       aggregated_frame.render_pass_list;
 
   ASSERT_EQ(5u, aggregated_pass_list.size());
-  RenderPassId actual_pass_ids[] = {
+  int actual_pass_ids[] = {
       aggregated_pass_list[0]->id, aggregated_pass_list[1]->id,
       aggregated_pass_list[2]->id, aggregated_pass_list[3]->id,
       aggregated_pass_list[4]->id};
@@ -833,7 +829,7 @@
   SurfaceId child_surface_id(child_factory_.frame_sink_id(),
                              child_local_frame_id);
 
-  RenderPassId child_pass_id[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
+  int child_pass_id[] = {1, 2};
   test::Quad child_quad[][1] = {{test::Quad::SolidColorQuad(SK_ColorGREEN)},
                                 {test::Quad::RenderPassQuad(child_pass_id[0])}};
   test::Pass surface_passes[] = {
@@ -844,7 +840,7 @@
                         arraysize(surface_passes), child_local_frame_id);
 
   // Pass IDs from the parent surface may collide with ones from the child.
-  RenderPassId parent_pass_id[] = {RenderPassId(2, 1), RenderPassId(1, 2)};
+  int parent_pass_id[] = {3, 2};
   test::Quad parent_quad[][1] = {
       {test::Quad::SurfaceQuad(child_surface_id, 1.f)},
       {test::Quad::RenderPassQuad(parent_pass_id[0])}};
@@ -862,9 +858,9 @@
       aggregated_frame.render_pass_list;
 
   ASSERT_EQ(3u, aggregated_pass_list.size());
-  RenderPassId actual_pass_ids[] = {aggregated_pass_list[0]->id,
-                                    aggregated_pass_list[1]->id,
-                                    aggregated_pass_list[2]->id};
+  int actual_pass_ids[] = {aggregated_pass_list[0]->id,
+                           aggregated_pass_list[1]->id,
+                           aggregated_pass_list[2]->id};
   // Make sure the aggregated frame's pass IDs are all unique.
   for (size_t i = 0; i < 3; ++i) {
     for (size_t j = 0; j < i; ++j) {
@@ -954,7 +950,7 @@
                                    &empty_client_);
   SurfaceFactory child_two_factory(FrameSinkId(4, 4), &manager_,
                                    &empty_client_);
-  RenderPassId pass_id(1, 1);
+  int pass_id = 1;
   LocalFrameId grandchild_local_frame_id = allocator_.GenerateId();
   SurfaceId grandchild_surface_id(grandchild_factory.frame_sink_id(),
                                   grandchild_local_frame_id);
@@ -1085,7 +1081,7 @@
   SurfaceId child_surface_id(child_factory_.frame_sink_id(),
                              child_local_frame_id);
   {
-    RenderPassId child_pass_id[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
+    int child_pass_id[] = {1, 2};
     test::Quad child_quads[][1] = {
         {test::Quad::SolidColorQuad(SK_ColorGREEN)},
         {test::Quad::RenderPassQuad(child_pass_id[0])},
@@ -1251,9 +1247,9 @@
 TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
   SurfaceFactory parent_factory(kArbitraryMiddleFrameSinkId, &manager_,
                                 &empty_client_);
-  test::Quad child_quads[] = {test::Quad::RenderPassQuad(RenderPassId(1, 1))};
+  test::Quad child_quads[] = {test::Quad::RenderPassQuad(1)};
   test::Pass child_passes[] = {
-      test::Pass(child_quads, arraysize(child_quads), RenderPassId(1, 1))};
+      test::Pass(child_quads, arraysize(child_quads), 1)};
 
   CompositorFrame child_frame;
   AddPasses(&child_frame.render_pass_list, gfx::Rect(SurfaceSize()),
@@ -1274,8 +1270,7 @@
   test::Quad parent_surface_quads[] = {
       test::Quad::SurfaceQuad(child_surface_id, 1.f)};
   test::Pass parent_surface_passes[] = {
-      test::Pass(parent_surface_quads, arraysize(parent_surface_quads),
-                 RenderPassId(1, 1))};
+      test::Pass(parent_surface_quads, arraysize(parent_surface_quads), 1)};
 
   // Parent surface is only used to test if the transform is applied correctly
   // to the child surface's damage.
@@ -1292,14 +1287,11 @@
 
   test::Quad root_surface_quads[] = {
       test::Quad::SurfaceQuad(parent_surface_id, 1.f)};
-  test::Quad root_render_pass_quads[] = {
-      test::Quad::RenderPassQuad(RenderPassId(1, 1))};
+  test::Quad root_render_pass_quads[] = {test::Quad::RenderPassQuad(1)};
 
   test::Pass root_passes[] = {
-      test::Pass(root_surface_quads, arraysize(root_surface_quads),
-                 RenderPassId(1, 1)),
-      test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads),
-                 RenderPassId(2, 1))};
+      test::Pass(root_surface_quads, arraysize(root_surface_quads), 1),
+      test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
 
   CompositorFrame root_frame;
   AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
@@ -1431,9 +1423,8 @@
 TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
   test::Quad root_render_pass_quads[] = {test::Quad::SolidColorQuad(1)};
 
-  test::Pass root_passes[] = {test::Pass(root_render_pass_quads,
-                                         arraysize(root_render_pass_quads),
-                                         RenderPassId(2, 1))};
+  test::Pass root_passes[] = {
+      test::Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
 
   CompositorFrame root_frame;
   AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()), root_passes,
@@ -1464,9 +1455,8 @@
   {
     test::Quad root_render_pass_quads[] = {test::Quad::SolidColorQuad(1)};
 
-    test::Pass root_passes[] = {test::Pass(root_render_pass_quads,
-                                           arraysize(root_render_pass_quads),
-                                           RenderPassId(2, 1))};
+    test::Pass root_passes[] = {test::Pass(
+        root_render_pass_quads, arraysize(root_render_pass_quads), 2)};
 
     CompositorFrame root_frame;
     AddPasses(&root_frame.render_pass_list, gfx::Rect(SurfaceSize()),
@@ -1519,7 +1509,7 @@
   // the other other with a visible rect of 10,10 2x2 (relative to root target
   // space), and one with a non-invertible transform.
   {
-    RenderPassId child_pass_id = RenderPassId(1, 1);
+    int child_pass_id = 1;
     test::Quad child_quads1[] = {test::Quad::RenderPassQuad(child_pass_id)};
     test::Quad child_quads2[] = {test::Quad::RenderPassQuad(child_pass_id)};
     test::Quad child_quads3[] = {test::Quad::RenderPassQuad(child_pass_id)};
@@ -1624,7 +1614,7 @@
   // New child frame has same content and no damage, but has a
   // CopyOutputRequest.
   {
-    RenderPassId child_pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
+    int child_pass_ids[] = {1, 2};
     test::Quad child_quads1[] = {test::Quad::SolidColorQuad(1)};
     test::Quad child_quads2[] = {test::Quad::RenderPassQuad(child_pass_ids[0])};
     test::Pass child_passes[] = {
@@ -1690,7 +1680,7 @@
   // Root surface has smaller damage rect, but filter on render pass means all
   // of it should be aggregated.
   {
-    RenderPassId root_pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
+    int root_pass_ids[] = {1, 2};
     test::Quad root_quads1[] = {test::Quad::SurfaceQuad(child_surface_id, 1.f)};
     test::Quad root_quads2[] = {test::Quad::RenderPassQuad(root_pass_ids[0])};
     test::Pass root_passes[] = {
@@ -1734,7 +1724,7 @@
   // means Surface
   // quad under it should be aggregated.
   {
-    RenderPassId root_pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2)};
+    int root_pass_ids[] = {1, 2};
     test::Quad root_quads1[] = {
         test::Quad::SolidColorQuad(1),
     };
@@ -1828,7 +1818,7 @@
                                         SurfaceId surface_id) {
   CompositorFrame frame;
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
-  pass->id = RenderPassId(1, 1);
+  pass->id = 1;
   SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
   sqs->opacity = 1.f;
   if (child_id.is_valid()) {
@@ -1906,7 +1896,7 @@
 
   CompositorFrame frame;
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
-  pass->id = RenderPassId(1, 1);
+  pass->id = 1;
   TransferableResource resource;
   resource.id = 11;
   // ResourceProvider is software but resource is not, so it should be
@@ -2047,7 +2037,7 @@
 
   {
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
-    pass->id = RenderPassId(1, 1);
+    pass->id = 1;
     SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
     sqs->opacity = 1.f;
     SurfaceDrawQuad* surface_quad =
diff --git a/cc/surfaces/surface_hittest.cc b/cc/surfaces/surface_hittest.cc
index 3ed99c0..ffc9f4c5 100644
--- a/cc/surfaces/surface_hittest.cc
+++ b/cc/surfaces/surface_hittest.cc
@@ -33,9 +33,8 @@
     *transform = gfx::Transform();
 
   std::set<const RenderPass*> referenced_passes;
-  GetTargetSurfaceAtPointInternal(root_surface_id, RenderPassId(), point,
-                                  &referenced_passes, &out_surface_id,
-                                  transform);
+  GetTargetSurfaceAtPointInternal(root_surface_id, 0, point, &referenced_passes,
+                                  &out_surface_id, transform);
 
   return out_surface_id;
 }
@@ -50,8 +49,7 @@
 
   std::set<const RenderPass*> referenced_passes;
   return GetTransformToTargetSurfaceInternal(root_surface_id, target_surface_id,
-                                             RenderPassId(), &referenced_passes,
-                                             transform);
+                                             0, &referenced_passes, transform);
 }
 
 bool SurfaceHittest::TransformPointToTargetSurface(
@@ -80,7 +78,7 @@
 
 bool SurfaceHittest::GetTargetSurfaceAtPointInternal(
     const SurfaceId& surface_id,
-    const RenderPassId& render_pass_id,
+    int render_pass_id,
     const gfx::Point& point_in_root_target,
     std::set<const RenderPass*>* referenced_passes,
     SurfaceId* out_surface_id,
@@ -128,7 +126,7 @@
 
       gfx::Transform transform_to_child_space;
       if (GetTargetSurfaceAtPointInternal(
-              surface_quad->surface_id, RenderPassId(), point_in_quad_space,
+              surface_quad->surface_id, 0, point_in_quad_space,
               referenced_passes, out_surface_id, &transform_to_child_space)) {
         *out_transform = transform_to_child_space * target_to_quad_transform *
                          transform_from_root_target;
@@ -175,7 +173,7 @@
 bool SurfaceHittest::GetTransformToTargetSurfaceInternal(
     const SurfaceId& root_surface_id,
     const SurfaceId& target_surface_id,
-    const RenderPassId& render_pass_id,
+    int render_pass_id,
     std::set<const RenderPass*>* referenced_passes,
     gfx::Transform* out_transform) {
   if (root_surface_id == target_surface_id) {
@@ -221,8 +219,8 @@
       // find the |target_surface_id| there.
       gfx::Transform transform_to_child_space;
       if (GetTransformToTargetSurfaceInternal(
-              surface_quad->surface_id, target_surface_id, RenderPassId(),
-              referenced_passes, &transform_to_child_space)) {
+              surface_quad->surface_id, target_surface_id, 0, referenced_passes,
+              &transform_to_child_space)) {
         *out_transform = transform_to_child_space * target_to_quad_transform *
                          transform_from_root_target;
         return true;
@@ -254,7 +252,7 @@
 
 const RenderPass* SurfaceHittest::GetRenderPassForSurfaceById(
     const SurfaceId& surface_id,
-    const RenderPassId& render_pass_id) {
+    int render_pass_id) {
   Surface* surface = manager_->GetSurfaceForId(surface_id);
   if (!surface)
     return nullptr;
@@ -265,7 +263,7 @@
   if (surface_frame.render_pass_list.empty())
     return nullptr;
 
-  if (!render_pass_id.IsValid())
+  if (!render_pass_id)
     return surface_frame.render_pass_list.back().get();
 
   for (const auto& render_pass : surface_frame.render_pass_list) {
diff --git a/cc/surfaces/surface_hittest.h b/cc/surfaces/surface_hittest.h
index ad5378a..b08fe6b 100644
--- a/cc/surfaces/surface_hittest.h
+++ b/cc/surfaces/surface_hittest.h
@@ -19,7 +19,6 @@
 
 class DrawQuad;
 class RenderPass;
-class RenderPassId;
 class SurfaceHittestDelegate;
 class SurfaceManager;
 
@@ -54,7 +53,7 @@
  private:
   bool GetTargetSurfaceAtPointInternal(
       const SurfaceId& surface_id,
-      const RenderPassId& render_pass_id,
+      int render_pass_id,
       const gfx::Point& point_in_root_target,
       std::set<const RenderPass*>* referenced_passes,
       SurfaceId* out_surface_id,
@@ -63,13 +62,12 @@
   bool GetTransformToTargetSurfaceInternal(
       const SurfaceId& root_surface_id,
       const SurfaceId& target_surface_id,
-      const RenderPassId& render_pass_id,
+      int render_pass_id,
       std::set<const RenderPass*>* referenced_passes,
       gfx::Transform* out_transform);
 
-  const RenderPass* GetRenderPassForSurfaceById(
-      const SurfaceId& surface_id,
-      const RenderPassId& render_pass_id);
+  const RenderPass* GetRenderPassForSurfaceById(const SurfaceId& surface_id,
+                                                int render_pass_id);
 
   bool PointInQuad(const DrawQuad* quad,
                    const gfx::Point& point_in_render_pass_space,
diff --git a/cc/surfaces/surface_hittest_unittest.cc b/cc/surfaces/surface_hittest_unittest.cc
index 1acaf06..13869fe 100644
--- a/cc/surfaces/surface_hittest_unittest.cc
+++ b/cc/surfaces/surface_hittest_unittest.cc
@@ -293,11 +293,9 @@
   CompositorFrame root_frame = CreateCompositorFrame(root_rect, &root_pass);
 
   // Create a RenderPassDrawQuad to a non-existant RenderPass.
-  CreateRenderPassDrawQuad(root_pass,
-                           gfx::Transform(),
-                           root_rect,
-                           root_rect,
-                           RenderPassId(1337, 1337));
+  int invalid_render_pass_id = 1337;
+  CreateRenderPassDrawQuad(root_pass, gfx::Transform(), root_rect, root_rect,
+                           invalid_render_pass_id);
 
   // Add a reference to the child surface on the root surface.
   SurfaceIdAllocator child_allocator;
@@ -396,7 +394,7 @@
   RenderPassList& render_pass_list = root_frame.render_pass_list;
 
   // Create a child RenderPass.
-  RenderPassId child_render_pass_id(1, 3);
+  int child_render_pass_id = 3;
   gfx::Transform transform_to_root_target(1.0f, 0.0f, 0.0f, 50.0f,
                                           0.0f, 1.0f, 0.0f, 50.0f,
                                           0.0f, 0.0f, 1.0f, 0.0f,
@@ -407,7 +405,7 @@
                    &render_pass_list);
 
   // Create the root RenderPass.
-  RenderPassId root_render_pass_id(1, 2);
+  int root_render_pass_id = 2;
   CreateRenderPass(root_render_pass_id, root_rect, gfx::Transform(),
                    &render_pass_list);
 
diff --git a/cc/surfaces/surfaces_pixeltest.cc b/cc/surfaces/surfaces_pixeltest.cc
index 9ec2e40..264e22e 100644
--- a/cc/surfaces/surfaces_pixeltest.cc
+++ b/cc/surfaces/surfaces_pixeltest.cc
@@ -64,7 +64,7 @@
 // Draws a very simple frame with no surface references.
 TEST_F(SurfacesPixelTest, DrawSimpleFrame) {
   gfx::Rect rect(device_viewport_size_);
-  RenderPassId id(1, 1);
+  int id = 1;
   std::unique_ptr<RenderPass> pass = RenderPass::Create();
   pass->SetNew(id, rect, rect, gfx::Transform());
 
@@ -112,7 +112,7 @@
 
   {
     gfx::Rect rect(device_viewport_size_);
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->SetNew(id, rect, rect, gfx::Transform());
 
@@ -144,7 +144,7 @@
 
   {
     gfx::Rect rect(child_size);
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->SetNew(id, rect, rect, gfx::Transform());
 
@@ -203,7 +203,7 @@
 
   {
     gfx::Rect rect(device_viewport_size_);
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->SetNew(id, rect, rect, gfx::Transform());
 
@@ -238,7 +238,7 @@
 
   {
     gfx::Rect rect(child_size);
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->SetNew(id, rect, rect, gfx::Transform());
 
@@ -272,7 +272,7 @@
 
   {
     gfx::Rect rect(child_size);
-    RenderPassId id(1, 1);
+    int id = 1;
     std::unique_ptr<RenderPass> pass = RenderPass::Create();
     pass->SetNew(id, rect, rect, gfx::Transform());
 
diff --git a/cc/test/animation_timelines_test_common.cc b/cc/test/animation_timelines_test_common.cc
index 52c3bd7..d6c2890 100644
--- a/cc/test/animation_timelines_test_common.cc
+++ b/cc/test/animation_timelines_test_common.cc
@@ -424,18 +424,18 @@
   timeline_impl_ = nullptr;
 }
 
-void AnimationTimelinesTest::AnimateLayersTransferEvents(
+void AnimationTimelinesTest::TickAnimationsTransferEvents(
     base::TimeTicks time,
     unsigned expect_events) {
   std::unique_ptr<MutatorEvents> events = host_->CreateEvents();
 
-  host_impl_->AnimateLayers(time);
+  host_impl_->TickAnimations(time);
   host_impl_->UpdateAnimationState(true, events.get());
 
   auto animation_events = static_cast<const AnimationEvents*>(events.get());
   EXPECT_EQ(expect_events, animation_events->events_.size());
 
-  host_->AnimateLayers(time);
+  host_->TickAnimations(time);
   host_->UpdateAnimationState(true, nullptr);
   host_->SetAnimationEvents(std::move(events));
 }
diff --git a/cc/test/animation_timelines_test_common.h b/cc/test/animation_timelines_test_common.h
index 5b34c604..968a05c 100644
--- a/cc/test/animation_timelines_test_common.h
+++ b/cc/test/animation_timelines_test_common.h
@@ -245,8 +245,8 @@
 
   void ReleaseRefPtrs();
 
-  void AnimateLayersTransferEvents(base::TimeTicks time,
-                                   unsigned expect_events);
+  void TickAnimationsTransferEvents(base::TimeTicks time,
+                                    unsigned expect_events);
 
   AnimationPlayer* GetPlayerForElementId(ElementId element_id);
   AnimationPlayer* GetImplPlayerForLayerId(ElementId element_id);
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index d51b8c4..740880a 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -239,8 +239,8 @@
   void UpdateAnimationState(bool start_ready_animations) override {
     LayerTreeHostImpl::UpdateAnimationState(start_ready_animations);
     bool has_unfinished_animation = false;
-    for (const auto& it : animation_host()->active_players_for_testing()) {
-      if (it->HasActiveAnimation()) {
+    for (const auto& it : animation_host()->ticking_players_for_testing()) {
+      if (it->HasTickingAnimation()) {
         has_unfinished_animation = true;
         break;
       }
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index f54217d..fc95c4d 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -23,11 +23,11 @@
 namespace cc {
 
 RenderPass* AddRenderPass(RenderPassList* pass_list,
-                          RenderPassId id,
+                          int render_pass_id,
                           const gfx::Rect& output_rect,
                           const gfx::Transform& root_transform) {
   std::unique_ptr<RenderPass> pass(RenderPass::Create());
-  pass->SetNew(id, output_rect, output_rect, root_transform);
+  pass->SetNew(render_pass_id, output_rect, output_rect, root_transform);
   RenderPass* saved = pass.get();
   pass_list->push_back(std::move(pass));
   return saved;
@@ -113,7 +113,7 @@
 
 void AddOneOfEveryQuadType(RenderPass* to_pass,
                            ResourceProvider* resource_provider,
-                           RenderPassId child_pass,
+                           int child_pass_id,
                            gpu::SyncToken* sync_token_for_mailbox_tebxture) {
   gfx::Rect rect(0, 0, 100, 100);
   gfx::Rect opaque_rect(10, 10, 80, 80);
@@ -173,11 +173,11 @@
       to_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
   debug_border_quad->SetNew(shared_state, rect, visible_rect, SK_ColorRED, 1);
 
-  if (child_pass.layer_id) {
+  if (child_pass_id) {
     RenderPassDrawQuad* render_pass_quad =
         to_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
     render_pass_quad->SetNew(
-        shared_state, rect, visible_rect, child_pass, resource5,
+        shared_state, rect, visible_rect, child_pass_id, resource5,
         gfx::Vector2dF(1.f, 1.f), resource5_size, FilterOperations(),
         gfx::Vector2dF(), gfx::PointF(), FilterOperations());
   }
diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h
index 82b32c5..ed3983d 100644
--- a/cc/test/render_pass_test_utils.h
+++ b/cc/test/render_pass_test_utils.h
@@ -25,7 +25,7 @@
 // Adds a new render pass with the provided properties to the given
 // render pass list.
 RenderPass* AddRenderPass(RenderPassList* pass_list,
-                          RenderPassId id,
+                          int render_pass_id,
                           const gfx::Rect& output_rect,
                           const gfx::Transform& root_transform);
 
@@ -58,7 +58,7 @@
 
 void AddOneOfEveryQuadType(RenderPass* to_pass,
                            ResourceProvider* resource_provider,
-                           RenderPassId child_pass,
+                           int child_pass_id,
                            gpu::SyncToken* sync_token_for_mailbox_texture);
 
 }  // namespace cc
diff --git a/cc/test/surface_aggregator_test_helpers.cc b/cc/test/surface_aggregator_test_helpers.cc
index 3f6504f..b6874efa 100644
--- a/cc/test/surface_aggregator_test_helpers.cc
+++ b/cc/test/surface_aggregator_test_helpers.cc
@@ -44,7 +44,8 @@
   surface_quad->SetNew(pass->shared_quad_state_list.back(), quad_rect,
                        quad_rect, surface_id);
 }
-void AddRenderPassQuad(RenderPass* pass, RenderPassId render_pass_id) {
+
+void AddRenderPassQuad(RenderPass* pass, int render_pass_id) {
   gfx::Rect output_rect = gfx::Rect(0, 0, 5, 5);
   SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), output_rect.size(), output_rect,
diff --git a/cc/test/surface_aggregator_test_helpers.h b/cc/test/surface_aggregator_test_helpers.h
index dcef6c87..e6fc96a 100644
--- a/cc/test/surface_aggregator_test_helpers.h
+++ b/cc/test/surface_aggregator_test_helpers.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "cc/quads/draw_quad.h"
-#include "cc/quads/render_pass_id.h"
 #include "cc/surfaces/surface_id.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/size.h"
@@ -40,7 +39,7 @@
     return quad;
   }
 
-  static Quad RenderPassQuad(RenderPassId id) {
+  static Quad RenderPassQuad(int id) {
     Quad quad;
     quad.material = DrawQuad::RENDER_PASS;
     quad.render_pass_id = id;
@@ -54,21 +53,20 @@
   // Set when material==DrawQuad::SOLID_COLOR.
   SkColor color;
   // Set when material==DrawQuad::RENDER_PASS.
-  RenderPassId render_pass_id;
+  int render_pass_id;
 
  private:
   Quad() : material(DrawQuad::INVALID), opacity(1.f), color(SK_ColorWHITE) {}
 };
 
 struct Pass {
-  Pass(Quad* quads, size_t quad_count, RenderPassId id)
+  Pass(Quad* quads, size_t quad_count, int id)
       : quads(quads), quad_count(quad_count), id(id) {}
-  Pass(Quad* quads, size_t quad_count)
-      : quads(quads), quad_count(quad_count), id(1, 1) {}
+  Pass(Quad* quads, size_t quad_count) : quads(quads), quad_count(quad_count) {}
 
   Quad* quads;
   size_t quad_count;
-  RenderPassId id;
+  int id = 1;
 };
 
 void AddSurfaceQuad(TestRenderPass* pass,
diff --git a/cc/test/surface_hittest_test_helpers.cc b/cc/test/surface_hittest_test_helpers.cc
index 5fcea80..01a11143 100644
--- a/cc/test/surface_hittest_test_helpers.cc
+++ b/cc/test/surface_hittest_test_helpers.cc
@@ -36,7 +36,7 @@
                               const gfx::Transform& transform,
                               const gfx::Rect& root_rect,
                               const gfx::Rect& quad_rect,
-                              const RenderPassId& render_pass_id) {
+                              int render_pass_id) {
   CreateSharedQuadState(pass, transform, root_rect);
   RenderPassDrawQuad* render_pass_quad =
       pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
@@ -58,7 +58,7 @@
                        quad_rect, surface_id);
 }
 
-void CreateRenderPass(const RenderPassId& render_pass_id,
+void CreateRenderPass(int render_pass_id,
                       const gfx::Rect& rect,
                       const gfx::Transform& transform_to_root_target,
                       RenderPassList* render_pass_list) {
@@ -70,7 +70,7 @@
 CompositorFrame CreateCompositorFrame(const gfx::Rect& root_rect,
                                       RenderPass** render_pass) {
   CompositorFrame root_frame;
-  RenderPassId root_id(1, 1);
+  int root_id = 1;
   CreateRenderPass(root_id, root_rect, gfx::Transform(),
                    &root_frame.render_pass_list);
   *render_pass = root_frame.render_pass_list.back().get();
diff --git a/cc/test/surface_hittest_test_helpers.h b/cc/test/surface_hittest_test_helpers.h
index 4dde9a8..2a5b754 100644
--- a/cc/test/surface_hittest_test_helpers.h
+++ b/cc/test/surface_hittest_test_helpers.h
@@ -12,6 +12,7 @@
 #include "cc/quads/render_pass.h"
 #include "cc/surfaces/surface_factory_client.h"
 #include "cc/surfaces/surface_hittest_delegate.h"
+#include "cc/surfaces/surface_id.h"
 #include "ui/gfx/geometry/insets.h"
 
 namespace gfx {
@@ -43,7 +44,7 @@
                               const gfx::Transform& transform,
                               const gfx::Rect& root_rect,
                               const gfx::Rect& quad_rect,
-                              const RenderPassId& render_pass_id);
+                              int render_pass_id);
 
 void CreateSurfaceDrawQuad(RenderPass* pass,
                            const gfx::Transform& transform,
@@ -51,7 +52,7 @@
                            const gfx::Rect& quad_rect,
                            SurfaceId surface_id);
 
-void CreateRenderPass(const RenderPassId& render_pass_id,
+void CreateRenderPass(int render_pass_id,
                       const gfx::Rect& rect,
                       const gfx::Transform& transform_to_root_target,
                       RenderPassList* render_pass_list);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index b8fff40e..e27073c 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -757,8 +757,7 @@
   }
 }
 
-static RenderPass* FindRenderPassById(const RenderPassList& list,
-                                      RenderPassId id) {
+static RenderPass* FindRenderPassById(const RenderPassList& list, int id) {
   auto it = std::find_if(
       list.begin(), list.end(),
       [id](const std::unique_ptr<RenderPass>& p) { return p->id == id; });
@@ -867,7 +866,7 @@
   for (LayerIterator it =
            LayerIterator::Begin(frame->render_surface_layer_list);
        it != end; ++it) {
-    RenderPassId target_render_pass_id =
+    auto target_render_pass_id =
         it.target_render_surface_layer()->render_surface()->GetRenderPassId();
     RenderPass* target_render_pass =
         FindRenderPassById(frame->render_passes, target_render_pass_id);
@@ -1105,11 +1104,10 @@
   DCHECK_GE(frame->render_passes.size(), 1u);
 
   // A set of RenderPasses that we have seen.
-  std::set<RenderPassId> pass_exists;
+  std::set<int> pass_exists;
   // A set of RenderPassDrawQuads that we have seen (stored by the RenderPasses
   // they refer to).
-  base::SmallMap<std::unordered_map<RenderPassId, int, RenderPassIdHash>>
-      pass_references;
+  base::SmallMap<std::unordered_map<int, int>> pass_references;
 
   // Iterate RenderPasses in draw order, removing empty render passes (except
   // the root RenderPass).
@@ -1555,7 +1553,7 @@
 
   if (GetDrawMode() == DRAW_MODE_RESOURCELESS_SOFTWARE) {
     metadata.is_resourceless_software_draw_with_scroll_or_animation =
-        IsActivelyScrolling() || mutator_host_->NeedsAnimateLayers();
+        IsActivelyScrolling() || mutator_host_->NeedsTickAnimations();
   }
 
   for (LayerImpl* surface_layer : active_tree_->SurfaceLayers()) {
@@ -3441,7 +3439,7 @@
 }
 
 bool LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time) {
-  const bool animated = mutator_host_->AnimateLayers(monotonic_time);
+  const bool animated = mutator_host_->TickAnimations(monotonic_time);
 
   // TODO(crbug.com/551134): Only do this if the animations are on the active
   // tree, or if they are on the pending tree waiting for some future time to
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 7776f3a..c540c24d 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -10522,7 +10522,7 @@
 
   // The instant scroll should have marked the smooth scroll animation as
   // aborted.
-  EXPECT_FALSE(GetImplAnimationHost()->HasActiveAnimationForTesting(
+  EXPECT_FALSE(GetImplAnimationHost()->HasTickingAnimationForTesting(
       scrolling_layer->element_id()));
 
   EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(0, y + 50),
@@ -10581,7 +10581,7 @@
 
   // Aborting with the needs completion param should have marked the smooth
   // scroll animation as finished.
-  EXPECT_FALSE(GetImplAnimationHost()->HasActiveAnimationForTesting(
+  EXPECT_FALSE(GetImplAnimationHost()->HasTickingAnimationForTesting(
       scrolling_layer->element_id()));
   EXPECT_TRUE(y > 1 && y < 49);
   EXPECT_EQ(NULL, host_impl_->CurrentlyScrollingLayer());
@@ -11051,7 +11051,7 @@
   EXPECT_LE(1, num_lost_surfaces_);
 }
 
-size_t CountRenderPassesWithId(const RenderPassList& list, RenderPassId id) {
+size_t CountRenderPassesWithId(const RenderPassList& list, int id) {
   return std::count_if(
       list.begin(), list.end(),
       [id](const std::unique_ptr<RenderPass>& p) { return p->id == id; });
@@ -11066,9 +11066,9 @@
   frame.render_passes.push_back(RenderPass::Create());
   RenderPass* pass1 = frame.render_passes.back().get();
 
-  pass1->SetNew(RenderPassId(1, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass2->SetNew(RenderPassId(2, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass3->SetNew(RenderPassId(3, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   // Add a quad to each pass so they aren't empty.
   SolidColorDrawQuad* color_quad;
@@ -11088,13 +11088,10 @@
   // But pass2 is not referenced by pass1. So pass2 and pass3 should be culled.
   FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
   EXPECT_EQ(1u, frame.render_passes.size());
-  EXPECT_EQ(1u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(1, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(2, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(3, 0)));
-  EXPECT_EQ(RenderPassId(1, 0), frame.render_passes[0]->id);
+  EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3));
+  EXPECT_EQ(1, frame.render_passes[0]->id);
 }
 
 TEST_F(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
@@ -11106,9 +11103,9 @@
   frame.render_passes.push_back(RenderPass::Create());
   RenderPass* pass1 = frame.render_passes.back().get();
 
-  pass1->SetNew(RenderPassId(1, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass2->SetNew(RenderPassId(2, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass3->SetNew(RenderPassId(3, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   // pass1 is not empty, but pass2 and pass3 are.
   SolidColorDrawQuad* color_quad;
@@ -11130,13 +11127,10 @@
   // should be removed.
   FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
   EXPECT_EQ(1u, frame.render_passes.size());
-  EXPECT_EQ(1u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(1, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(2, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(3, 0)));
-  EXPECT_EQ(RenderPassId(1, 0), frame.render_passes[0]->id);
+  EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3));
+  EXPECT_EQ(1, frame.render_passes[0]->id);
   // The RenderPassDrawQuad should be removed from pass1.
   EXPECT_EQ(1u, pass1->quad_list.size());
   EXPECT_EQ(DrawQuad::SOLID_COLOR, pass1->quad_list.ElementAt(0)->material);
@@ -11151,9 +11145,9 @@
   frame.render_passes.push_back(RenderPass::Create());
   RenderPass* pass1 = frame.render_passes.back().get();
 
-  pass1->SetNew(RenderPassId(1, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass2->SetNew(RenderPassId(2, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
-  pass3->SetNew(RenderPassId(3, 0), gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
+  pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
 
   // pass3 is referenced by pass2.
   RenderPassDrawQuad* rpdq =
@@ -11171,13 +11165,10 @@
   // not be removed.
   FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
   EXPECT_EQ(1u, frame.render_passes.size());
-  EXPECT_EQ(1u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(1, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(2, 0)));
-  EXPECT_EQ(0u,
-            CountRenderPassesWithId(frame.render_passes, RenderPassId(3, 0)));
-  EXPECT_EQ(RenderPassId(1, 0), frame.render_passes[0]->id);
+  EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2));
+  EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3));
+  EXPECT_EQ(1, frame.render_passes[0]->id);
   // The RenderPassDrawQuad should be removed from pass1.
   EXPECT_EQ(0u, pass1->quad_list.size());
 }
diff --git a/cc/trees/layer_tree_host_in_process.cc b/cc/trees/layer_tree_host_in_process.cc
index 8a9d969..c79ffeed 100644
--- a/cc/trees/layer_tree_host_in_process.cc
+++ b/cc/trees/layer_tree_host_in_process.cc
@@ -789,7 +789,7 @@
   MutatorHost* mutator_host = layer_tree_->mutator_host();
   std::unique_ptr<MutatorEvents> events = mutator_host->CreateEvents();
 
-  if (mutator_host->AnimateLayers(monotonic_time))
+  if (mutator_host->TickAnimations(monotonic_time))
     mutator_host->UpdateAnimationState(true, events.get());
 
   if (!events->IsEmpty())
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index d5a7568..9384e8a 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -6969,14 +6969,13 @@
                                    DrawResult draw_result) override {
     frame->render_passes.clear();
 
-    RenderPass* child_pass =
-        AddRenderPass(&frame->render_passes, RenderPassId(2, 1),
-                      gfx::Rect(3, 3, 10, 10), gfx::Transform());
+    RenderPass* child_pass = AddRenderPass(
+        &frame->render_passes, 2, gfx::Rect(3, 3, 10, 10), gfx::Transform());
     gpu::SyncToken mailbox_sync_token;
-    AddOneOfEveryQuadType(child_pass, host_impl->resource_provider(),
-                          RenderPassId(0, 0), &mailbox_sync_token);
+    AddOneOfEveryQuadType(child_pass, host_impl->resource_provider(), 0,
+                          &mailbox_sync_token);
 
-    RenderPass* pass = AddRenderPass(&frame->render_passes, RenderPassId(1, 1),
+    RenderPass* pass = AddRenderPass(&frame->render_passes, 1,
                                      gfx::Rect(3, 3, 10, 10), gfx::Transform());
     AddOneOfEveryQuadType(pass, host_impl->resource_provider(), child_pass->id,
                           &mailbox_sync_token);
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index cb659bc6c..2d1b3c8 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -251,7 +251,7 @@
   void AnimateLayers(LayerTreeHostImpl* host_impl,
                      base::TimeTicks monotonic_time) override {
     bool have_animations =
-        !GetImplAnimationHost(host_impl)->active_players_for_testing().empty();
+        !GetImplAnimationHost(host_impl)->ticking_players_for_testing().empty();
     if (!started_animating_ && have_animations) {
       started_animating_ = true;
       return;
@@ -1259,7 +1259,7 @@
             player_->element_animations()->has_element_in_active_list());
         EXPECT_FALSE(
             player_->element_animations()->has_element_in_pending_list());
-        EXPECT_TRUE(animation_host()->NeedsAnimateLayers());
+        EXPECT_TRUE(animation_host()->NeedsTickAnimations());
         break;
       case 1:
         layer_->RemoveFromParent();
@@ -1267,7 +1267,7 @@
             player_->element_animations()->has_element_in_active_list());
         EXPECT_FALSE(
             player_->element_animations()->has_element_in_pending_list());
-        EXPECT_FALSE(animation_host()->NeedsAnimateLayers());
+        EXPECT_FALSE(animation_host()->NeedsTickAnimations());
         break;
       case 2:
         layer_tree()->root_layer()->AddChild(layer_);
@@ -1275,7 +1275,7 @@
             player_->element_animations()->has_element_in_active_list());
         EXPECT_FALSE(
             player_->element_animations()->has_element_in_pending_list());
-        EXPECT_TRUE(animation_host()->NeedsAnimateLayers());
+        EXPECT_TRUE(animation_host()->NeedsTickAnimations());
         break;
     }
   }
@@ -1290,17 +1290,17 @@
       case 0:
         EXPECT_TRUE(
             player_impl->element_animations()->has_element_in_active_list());
-        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
+        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
         break;
       case 1:
         EXPECT_FALSE(
             player_impl->element_animations()->has_element_in_active_list());
-        EXPECT_FALSE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
+        EXPECT_FALSE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
         break;
       case 2:
         EXPECT_TRUE(
             player_impl->element_animations()->has_element_in_active_list());
-        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsAnimateLayers());
+        EXPECT_TRUE(GetImplAnimationHost(host_impl)->NeedsTickAnimations());
         EndTest();
         break;
     }
@@ -1367,7 +1367,7 @@
 
     EXPECT_EQ(
         2u,
-        GetImplAnimationHost(host_impl)->active_players_for_testing().size());
+        GetImplAnimationHost(host_impl)->ticking_players_for_testing().size());
 
     Animation* root_anim =
         player_impl_->GetAnimation(TargetProperty::TRANSFORM);
diff --git a/cc/trees/layer_tree_host_unittest_copyrequest.cc b/cc/trees/layer_tree_host_unittest_copyrequest.cc
index 25fe1ec..ae6ff24 100644
--- a/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -532,8 +532,8 @@
 
   void AfterTest() override { EXPECT_TRUE(did_swap_); }
 
-  RenderPassId parent_render_pass_id;
-  RenderPassId copy_layer_render_pass_id;
+  int parent_render_pass_id = 0;
+  int copy_layer_render_pass_id = 0;
   TestCompositorFrameSink* frame_sink_ = nullptr;
   bool did_swap_ = false;
   FakeContentLayerClient client_;
diff --git a/cc/trees/mutator_host.h b/cc/trees/mutator_host.h
index bd3ed1d..8db5f6014 100644
--- a/cc/trees/mutator_host.h
+++ b/cc/trees/mutator_host.h
@@ -49,10 +49,10 @@
   virtual void PushPropertiesTo(MutatorHost* host_impl) = 0;
 
   virtual void SetSupportsScrollAnimations(bool supports_scroll_animations) = 0;
-  virtual bool NeedsAnimateLayers() const = 0;
+  virtual bool NeedsTickAnimations() const = 0;
 
   virtual bool ActivateAnimations() = 0;
-  virtual bool AnimateLayers(base::TimeTicks monotonic_time) = 0;
+  virtual bool TickAnimations(base::TimeTicks monotonic_time) = 0;
   virtual bool UpdateAnimationState(bool start_ready_animations,
                                     MutatorEvents* events) = 0;
 
@@ -110,7 +110,7 @@
                                    float* start_scale) const = 0;
 
   virtual bool HasAnyAnimation(ElementId element_id) const = 0;
-  virtual bool HasActiveAnimationForTesting(ElementId element_id) const = 0;
+  virtual bool HasTickingAnimationForTesting(ElementId element_id) const = 0;
 
   virtual void ImplOnlyScrollAnimationCreate(
       ElementId element_id,
diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h
index 66eec3fe..fddfe3d 100644
--- a/cc/trees/proxy_main.h
+++ b/cc/trees/proxy_main.h
@@ -13,7 +13,6 @@
 
 namespace cc {
 
-class BeginFrameSource;
 class MutatorEvents;
 class CompletionEvent;
 class CompositorFrameSink;
diff --git a/chrome/VERSION b/chrome/VERSION
index fcc300f..8d509d4e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=57
 MINOR=0
-BUILD=2944
+BUILD=2946
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index fdb91d6d..1a7d07e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -245,7 +245,7 @@
       "//components/infobars/core:infobar_enums_java",
       "//components/ntp_snippets:ntp_snippets_java_enums_srcjar",
       "//components/ntp_tiles:ntp_tiles_enums_java",
-      "//components/offline_pages:offline_page_model_enums_java",
+      "//components/offline_pages/core:offline_page_model_enums_java",
       "//components/omnibox/browser:autocomplete_match_javagen",
       "//components/omnibox/browser:autocomplete_match_type_javagen",
       "//components/security_state/core:security_state_enums_java",
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index e363923..02ecd0d 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -496,6 +496,14 @@
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
         </activity>
 
+        <!-- Activities for history. -->
+        <activity android:name="org.chromium.chrome.browser.history.HistoryActivity"
+            android:theme="@style/FullscreenWhiteActivityTheme"
+            android:windowSoftInputMode="stateAlwaysHidden"
+            android:exported="false"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
+        </activity>
+
         <!--
             Activities for webapps.
             TODO(dfalcantara): Remove the aliases for the WebappActivities once we're pretty sure
diff --git a/chrome/android/java/res/drawable-hdpi/history_large.png b/chrome/android/java/res/drawable-hdpi/history_large.png
new file mode 100644
index 0000000..113dead
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/history_large.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/history_large.png b/chrome/android/java/res/drawable-mdpi/history_large.png
new file mode 100644
index 0000000..4083155
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/history_large.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/history_large.png b/chrome/android/java/res/drawable-xhdpi/history_large.png
new file mode 100644
index 0000000..e875087
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/history_large.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/history_large.png b/chrome/android/java/res/drawable-xxhdpi/history_large.png
new file mode 100644
index 0000000..0c91370
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/history_large.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/history_large.png b/chrome/android/java/res/drawable-xxxhdpi/history_large.png
new file mode 100644
index 0000000..efa726c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/history_large.png
Binary files differ
diff --git a/chrome/android/java/res/layout-sw720dp/download_main.xml b/chrome/android/java/res/layout-sw720dp/download_main.xml
index b7f6191..6c201cd 100644
--- a/chrome/android/java/res/layout-sw720dp/download_main.xml
+++ b/chrome/android/java/res/layout-sw720dp/download_main.xml
@@ -13,8 +13,9 @@
         android:layout_width="256dp"
         android:layout_height="match_parent"
         android:layout_gravity="start"
+        android:orientation="vertical"
         android:background="@android:color/white"
-        android:orientation="vertical" >
+        android:clickable="true" >
 
         <include layout="@layout/download_manager_ui_space_widget" />
 
diff --git a/chrome/android/java/res/layout/download_date_view.xml b/chrome/android/java/res/layout/date_view.xml
similarity index 100%
rename from chrome/android/java/res/layout/download_date_view.xml
rename to chrome/android/java/res/layout/date_view.xml
diff --git a/chrome/android/java/res/layout/download_main.xml b/chrome/android/java/res/layout/download_main.xml
index 9030d8c..cbf4c8b 100644
--- a/chrome/android/java/res/layout/download_main.xml
+++ b/chrome/android/java/res/layout/download_main.xml
@@ -24,7 +24,8 @@
         android:layout_height="match_parent"
         android:layout_gravity="start"
         android:orientation="vertical"
-        android:background="@android:color/white" >
+        android:background="@android:color/white"
+        android:clickable="true" >
 
         <include layout="@layout/download_manager_ui_space_widget"/>
 
diff --git a/chrome/android/java/res/layout/history_item_view.xml b/chrome/android/java/res/layout/history_item_view.xml
new file mode 100644
index 0000000..dc314a0b5
--- /dev/null
+++ b/chrome/android/java/res/layout/history_item_view.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!-- TODO(twellington): Polish this after spec is available. -->
+<org.chromium.chrome.browser.history.HistoryItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:chrome="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <RelativeLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp" >
+
+        <ImageView
+            android:id="@+id/icon_view"
+            android:layout_width="@dimen/default_favicon_size"
+            android:layout_height="@dimen/default_favicon_size"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:layout_marginEnd="16dp"
+            android:contentDescription="@null"
+            android:scaleType="center"
+            android:src="@drawable/default_favicon" />
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_toEndOf="@+id/icon_view"
+            android:layout_toStartOf="@+id/remove"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textColor="@color/default_text_color"
+            android:textSize="16sp" />
+
+        <TextView
+            android:id="@+id/domain"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_toEndOf="@+id/icon_view"
+            android:layout_toStartOf="@+id/remove"
+            android:layout_below="@+id/title"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:textColor="@color/google_grey_600"
+            android:textSize="14sp" />
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/remove"
+            android:layout_width="@dimen/selectable_list_layout_end_icon"
+            android:layout_height="@dimen/selectable_list_layout_end_icon"
+            android:layout_centerVertical="true"
+            android:layout_alignParentEnd="true"
+            android:background="@null"
+            android:contentDescription="@string/remove"
+            android:paddingEnd="16dp"
+            android:paddingStart="16dp"
+            android:src="@drawable/btn_trash"
+            chrome:tint="@color/dark_mode_tint" />
+    </RelativeLayout>
+
+</org.chromium.chrome.browser.history.HistoryItemView>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/history_main.xml b/chrome/android/java/res/layout/history_main.xml
new file mode 100644
index 0000000..867a501
--- /dev/null
+++ b/chrome/android/java/res/layout/history_main.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<org.chromium.chrome.browser.widget.selection.SelectableListLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/white" />
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/history_toolbar.xml b/chrome/android/java/res/layout/history_toolbar.xml
new file mode 100644
index 0000000..e482bb7c
--- /dev/null
+++ b/chrome/android/java/res/layout/history_toolbar.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<org.chromium.chrome.browser.history.HistoryManagerToolbar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <include layout="@layout/number_roll_view" />
+
+</org.chromium.chrome.browser.history.HistoryManagerToolbar>
\ No newline at end of file
diff --git a/chrome/android/java/res/menu/history_manager_menu.xml b/chrome/android/java/res/menu/history_manager_menu.xml
new file mode 100644
index 0000000..e530740
--- /dev/null
+++ b/chrome/android/java/res/menu/history_manager_menu.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2016 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:chrome="http://schemas.android.com/apk/res-auto" >
+
+    <group android:id="@+id/normal_menu_group" >
+        <!-- TODO(twellington): add search and info button. -->
+        <item
+            android:id="@+id/close_menu_id"
+            android:icon="@drawable/btn_close"
+            android:title="@string/close"
+            chrome:showAsAction="ifRoom" />
+     </group>
+     <group
+        android:id="@+id/selection_mode_menu_group"
+        android:visible="false" >
+        <item
+            android:id="@+id/selection_mode_delete_menu_id"
+            android:icon="@drawable/ic_delete_white_24dp"
+            android:title="@string/remove"
+            chrome:showAsAction="ifRoom" />
+       <item
+            android:id="@+id/selection_mode_open_in_new_tab"
+            android:title="@string/contextmenu_open_in_new_tab"
+            chrome:showAsAction="never" />
+       <item
+            android:id="@+id/selection_mode_open_in_incognito"
+            android:title="@string/contextmenu_open_in_incognito_tab"
+            chrome:showAsAction="never" />
+       <!-- TODO(twellington): add copy link item -->
+     </group>
+</menu>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index c67cdbb..626a168 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -390,4 +390,5 @@
 
     <!--  Miscellaneous dimensions -->
     <dimen name="action_bar_shadow_height">10dp</dimen>
+    <dimen name="selectable_list_layout_end_icon">24dp</dimen>
 </resources>
diff --git a/chrome/android/java/res/xml/data_reduction_preferences.xml b/chrome/android/java/res/xml/data_reduction_preferences.xml
index 68bd1e04..61002c9 100644
--- a/chrome/android/java/res/xml/data_reduction_preferences.xml
+++ b/chrome/android/java/res/xml/data_reduction_preferences.xml
@@ -8,6 +8,6 @@
     <org.chromium.chrome.browser.preferences.datareduction.DataReductionStatsPreference
         android:key="data_reduction_stats"
         android:layout="@layout/custom_preference"
-        android:widgetLayout="@layout/data_reduction_stats_layout" />
+        android:selectable="false" />
 
 </PreferenceScreen>
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index b090a53..18adaf6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -79,6 +79,7 @@
 import org.chromium.chrome.browser.gsa.GSAAccountChangeListener;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.history.HistoryManagerUtils;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
@@ -1682,8 +1683,10 @@
             WebsiteSettingsPopup.show(
                     this, currentTab, null, WebsiteSettingsPopup.OPENED_FROM_MENU);
         } else if (id == R.id.open_history_menu_id) {
-            currentTab.loadUrl(
-                    new LoadUrlParams(UrlConstants.HISTORY_URL, PageTransition.AUTO_TOPLEVEL));
+            if (!HistoryManagerUtils.showHistoryManager(this, currentTab)) {
+                currentTab.loadUrl(
+                        new LoadUrlParams(UrlConstants.HISTORY_URL, PageTransition.AUTO_TOPLEVEL));
+            }
             RecordUserAction.record("MobileMenuHistory");
             StartupMetrics.getInstance().recordOpenedHistory();
         } else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
index 2096e30..cb700422 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java
@@ -161,7 +161,7 @@
         addShortcut(context, webpageShortcutGroup, R.string.keyboard_shortcut_bookmark_page,
                 KeyEvent.KEYCODE_D, KeyEvent.META_CTRL_ON);
         addShortcut(context, webpageShortcutGroup, R.string.keyboard_shortcut_zoom_in,
-                KeyEvent.KEYCODE_PLUS, KeyEvent.META_CTRL_ON);
+                KeyEvent.KEYCODE_EQUALS, KeyEvent.META_CTRL_ON);
         addShortcut(context, webpageShortcutGroup, R.string.keyboard_shortcut_zoom_out,
                 KeyEvent.KEYCODE_MINUS, KeyEvent.META_CTRL_ON);
         addShortcut(context, webpageShortcutGroup, R.string.keyboard_shortcut_reset_zoom,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
index b4ed7c9..242ae325 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/UrlConstants.java
@@ -19,20 +19,29 @@
 
     public static final String NTP_URL = "chrome-native://newtab/";
     public static final String NTP_HOST = "newtab";
+
     public static final String BOOKMARKS_URL = "chrome-native://bookmarks/";
     public static final String BOOKMARKS_FOLDER_URL = "chrome-native://bookmarks/folder/";
     public static final String
             BOOKMARKS_UNCATEGORIZED_URL = "chrome-native://bookmarks/uncategorized/";
     public static final String BOOKMARKS_HOST = "bookmarks";
+
     public static final String DOWNLOADS_URL = "chrome-native://downloads/";
     public static final String DOWNLOADS_FILTER_URL = "chrome-native://downloads/filter/";
     public static final String DOWNLOADS_HOST = "downloads";
+
     public static final String RECENT_TABS_URL = "chrome-native://recent-tabs/";
     public static final String RECENT_TABS_HOST = "recent-tabs";
+
     public static final String HISTORY_URL = "chrome://history/";
+    public static final String NATIVE_HISTORY_URL = "chrome-native://history/";
+    public static final String HISTORY_HOST = "history";
+
     public static final String INTERESTS_URL = "chrome-native://interests/";
     public static final String INTERESTS_HOST = "interests";
+
     public static final String PHYSICAL_WEB_DIAGNOSTICS_HOST = "physical-web-diagnostics";
+
     public static final String ABOUT_BLANK = "about:blank";
 
     public static final String GOOGLE_ACCOUNT_ACTIVITY_CONTROLS_URL =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 387652f..89747e1c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -49,6 +49,7 @@
 import org.chromium.chrome.browser.datausage.DataUseTabUIManager;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
+import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
 import org.chromium.chrome.browser.fullscreen.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
@@ -368,6 +369,8 @@
 
     @Override
     public void finishNativeInitialization() {
+        FirstRunSignInProcessor.start(this);
+
         final CustomTabsConnection connection = CustomTabsConnection.getInstance(getApplication());
         // If extra headers have been passed, cancel any current prerender, as
         // prerendering doesn't support extra headers.
@@ -718,6 +721,7 @@
      * @param reparenting true iff the activity finishes due to tab reparenting.
      */
     public final void finishAndClose(boolean reparenting) {
+        if (mIsClosing) return;
         mIsClosing = true;
         if (!reparenting) {
             // Closing the activity destroys the renderer as well. Re-create a spare renderer some
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 860c316..7d123b82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -272,6 +272,12 @@
             if (selectedItemsFilterType != wrappedItem.getFilterType()) {
                 selectedItemsFilterType = DownloadFilter.FILTER_ALL;
             }
+            if (wrappedItem.getFilterType() == DownloadFilter.FILTER_OTHER) {
+                RecordHistogram.recordEnumeratedHistogram(
+                        "Android.DownloadManager.OtherExtensions.Share",
+                        wrappedItem.getFileExtensionType(),
+                        DownloadHistoryItemWrapper.FILE_EXTENSION_BOUNDARY);
+            }
 
             String mimeType = Intent.normalizeMimeType(wrappedItem.getMimeType());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
index aecd79a9..ecd1f45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
@@ -25,7 +25,7 @@
     static final int FILTER_AUDIO = 3;
     public static final int FILTER_IMAGE = 4;
     static final int FILTER_DOCUMENT = 5;
-    static final int FILTER_OTHER = 6;
+    public static final int FILTER_OTHER = 6;
     public static final int FILTER_BOUNDARY = 7;
 
     private static final String MIMETYPE_VIDEO = "video";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
index 5884b1cde..43f99ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
@@ -110,7 +110,16 @@
 
         for (DownloadItem item : result) {
             DownloadItemWrapper wrapper = createDownloadItemWrapper(item);
-            if (addDownloadHistoryItemWrapper(wrapper)) itemCounts[wrapper.getFilterType()]++;
+            if (addDownloadHistoryItemWrapper(wrapper)
+                    && wrapper.isVisibleToUser(DownloadFilter.FILTER_ALL)) {
+                itemCounts[wrapper.getFilterType()]++;
+                if (!isOffTheRecord && wrapper.getFilterType() == DownloadFilter.FILTER_OTHER) {
+                    RecordHistogram.recordEnumeratedHistogram(
+                            "Android.DownloadManager.OtherExtensions.InitialCount",
+                            wrapper.getFileExtensionType(),
+                            DownloadHistoryItemWrapper.FILE_EXTENSION_BOUNDARY);
+                }
+            }
         }
 
         if (!isOffTheRecord) recordDownloadCountHistograms(itemCounts);
@@ -186,7 +195,7 @@
 
     @Override
     protected int getTimedItemViewResId() {
-        return R.layout.download_date_view;
+        return R.layout.date_view;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index adc7cc8a..82382d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -21,9 +21,51 @@
 import org.chromium.ui.widget.Toast;
 
 import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
 
 /** Wraps different classes that contain information about downloads. */
 public abstract class DownloadHistoryItemWrapper extends TimedItem {
+    public static final Integer FILE_EXTENSION_OTHER = 0;
+    public static final Integer FILE_EXTENSION_APK = 1;
+    public static final Integer FILE_EXTENSION_CSV = 2;
+    public static final Integer FILE_EXTENSION_DOC = 3;
+    public static final Integer FILE_EXTENSION_DOCX = 4;
+    public static final Integer FILE_EXTENSION_EXE = 5;
+    public static final Integer FILE_EXTENSION_PDF = 6;
+    public static final Integer FILE_EXTENSION_PPT = 7;
+    public static final Integer FILE_EXTENSION_PPTX = 8;
+    public static final Integer FILE_EXTENSION_PSD = 9;
+    public static final Integer FILE_EXTENSION_RTF = 10;
+    public static final Integer FILE_EXTENSION_TXT = 11;
+    public static final Integer FILE_EXTENSION_XLS = 12;
+    public static final Integer FILE_EXTENSION_XLSX = 13;
+    public static final Integer FILE_EXTENSION_ZIP = 14;
+    public static final Integer FILE_EXTENSION_BOUNDARY = 15;
+
+    private static final Map<String, Integer> EXTENSIONS_MAP;
+    static {
+        Map<String, Integer> extensions = new HashMap<>();
+        extensions.put("apk", FILE_EXTENSION_APK);
+        extensions.put("csv", FILE_EXTENSION_CSV);
+        extensions.put("doc", FILE_EXTENSION_DOC);
+        extensions.put("docx", FILE_EXTENSION_DOCX);
+        extensions.put("exe", FILE_EXTENSION_EXE);
+        extensions.put("pdf", FILE_EXTENSION_PDF);
+        extensions.put("ppt", FILE_EXTENSION_PPT);
+        extensions.put("pptx", FILE_EXTENSION_PPTX);
+        extensions.put("psd", FILE_EXTENSION_PSD);
+        extensions.put("rtf", FILE_EXTENSION_RTF);
+        extensions.put("txt", FILE_EXTENSION_TXT);
+        extensions.put("xls", FILE_EXTENSION_XLS);
+        extensions.put("xlsx", FILE_EXTENSION_XLSX);
+        extensions.put("zip", FILE_EXTENSION_ZIP);
+
+        EXTENSIONS_MAP = Collections.unmodifiableMap(extensions);
+    }
+
     protected final BackendProvider mBackendProvider;
     protected final ComponentName mComponentName;
     protected File mFile;
@@ -97,6 +139,9 @@
     /** @return The mime type or null if the item doesn't have one. */
     public abstract String getMimeType();
 
+    /** @return The file extension type. See list at the top of the file. */
+    public abstract int getFileExtensionType();
+
     /** @return How much of the download has completed, or -1 if there is no progress. */
     abstract int getDownloadProgress();
 
@@ -141,16 +186,29 @@
     protected void recordOpenSuccess() {
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Item.OpenSucceeded",
                 getFilterType(), DownloadFilter.FILTER_BOUNDARY);
+
+        if (getFilterType() == DownloadFilter.FILTER_OTHER) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "Android.DownloadManager.OtherExtensions.OpenSucceeded",
+                    getFileExtensionType(), FILE_EXTENSION_BOUNDARY);
+        }
     }
 
     protected void recordOpenFailure() {
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Item.OpenFailed",
                 getFilterType(), DownloadFilter.FILTER_BOUNDARY);
+
+        if (getFilterType() == DownloadFilter.FILTER_OTHER) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "Android.DownloadManager.OtherExtensions.OpenFailed",
+                    getFileExtensionType(), FILE_EXTENSION_BOUNDARY);
+        }
     }
 
     /** Wraps a {@link DownloadItem}. */
     public static class DownloadItemWrapper extends DownloadHistoryItemWrapper {
         private DownloadItem mItem;
+        private Integer mFileExtensionType;
 
         DownloadItemWrapper(DownloadItem item, BackendProvider provider, ComponentName component) {
             super(provider, component);
@@ -219,6 +277,28 @@
         }
 
         @Override
+        public int getFileExtensionType() {
+            if (mFileExtensionType == null) {
+                int extensionIndex = getFilePath().lastIndexOf(".");
+                if (extensionIndex == -1 || extensionIndex == getFilePath().length() - 1) {
+                    mFileExtensionType = FILE_EXTENSION_OTHER;
+                    return mFileExtensionType;
+                }
+
+                String extension = getFilePath().substring(extensionIndex + 1);
+                if (!TextUtils.isEmpty(extension) && EXTENSIONS_MAP.containsKey(
+                        extension.toLowerCase(Locale.getDefault()))) {
+                    mFileExtensionType = EXTENSIONS_MAP.get(
+                            extension.toLowerCase(Locale.getDefault()));
+                } else {
+                    mFileExtensionType = FILE_EXTENSION_OTHER;
+                }
+            }
+
+            return mFileExtensionType;
+        }
+
+        @Override
         public int getDownloadProgress() {
             return mItem.getDownloadInfo().getPercentCompleted();
         }
@@ -409,6 +489,11 @@
         }
 
         @Override
+        public int getFileExtensionType() {
+            return FILE_EXTENSION_OTHER;
+        }
+
+        @Override
         public int getDownloadProgress() {
             return -1;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index 50a72851..c1af1f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -9,6 +9,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.AsyncTask;
+import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
@@ -194,7 +195,9 @@
         mSelectableListLayout =
                 (SelectableListLayout) mMainView.findViewById(R.id.selectable_list);
 
-        mSelectableListLayout.initializeEmptyView(R.drawable.downloads_big,
+        mSelectableListLayout.initializeEmptyView(
+                VectorDrawableCompat.create(mActivity.getResources(),
+                        R.drawable.downloads_big, mActivity.getTheme()),
                 R.string.download_manager_ui_empty);
 
         mHistoryAdapter = new DownloadHistoryAdapter(isOffTheRecord, parentComponent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index 123aac01..a334dd89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -58,6 +58,10 @@
  * The main implementation of the {@link ExternalNavigationDelegate}.
  */
 public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegate {
+    // Instant Apps system resolver activity on N-MR1+.
+    @VisibleForTesting
+    static final String EPHEMERAL_INSTALLER_CLASS =
+            "com.google.android.gms.instantapps.routing.EphemeralInstallerActivity";
     private static final String PDF_VIEWER = "com.google.android.apps.docs";
     private static final String PDF_MIME = "application/pdf";
     private static final String PDF_SUFFIX = ".pdf";
@@ -284,7 +288,16 @@
                 continue;
             }
 
-            result.add(info.activityInfo != null ? info.activityInfo.packageName : "");
+            if (info.activityInfo != null) {
+                if (EPHEMERAL_INSTALLER_CLASS.equals(info.activityInfo.name)) {
+                    // Don't consider the Instant Apps resolver a specialized application.
+                    continue;
+                }
+
+                result.add(info.activityInfo.packageName);
+            } else {
+                result.add("");
+            }
         }
         return result;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java b/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
index 959e21f..07d2002 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
@@ -11,6 +11,7 @@
 import android.provider.Browser;
 import android.text.TextUtils;
 
+import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.chrome.R;
@@ -29,6 +30,7 @@
 public class HelpAndFeedback {
     protected static final String FALLBACK_SUPPORT_URL =
             "https://support.google.com/chrome/topic/6069782";
+    private static final String TAG = "cr_HelpAndFeedback";
 
     private static HelpAndFeedback sInstance;
 
@@ -56,6 +58,7 @@
      */
     protected void show(
             Activity activity, String helpContext, @Nonnull FeedbackCollector collector) {
+        Log.d(TAG, "Feedback data: " + collector.getBundle());
         launchFallbackSupportUri(activity);
     }
 
@@ -114,4 +117,4 @@
         intent.setPackage(context.getPackageName());
         context.startActivity(intent);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
new file mode 100644
index 0000000..f0d1b4ef
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import org.chromium.base.Callback;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.profiles.Profile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** The JNI bridge for Android to fetch and manipulate browsing history. */
+public class BrowsingHistoryBridge {
+
+    private long mNativeHistoryBridge;
+    private Callback<List<HistoryItem>> mCallback;
+
+    public BrowsingHistoryBridge() {
+        mNativeHistoryBridge = nativeInit(Profile.getLastUsedProfile());
+    }
+
+    public void destroy() {
+        if (mNativeHistoryBridge != 0) {
+            nativeDestroy(mNativeHistoryBridge);
+            mNativeHistoryBridge = 0;
+        }
+    }
+
+    /**
+     * Query browsing history. Only one query may be in-flight at any time. See
+     * BrowsingHistoryService::QueryHistory.
+     * @param callback The callback that will receive query results.
+     * @param query The query search text. May be empty.
+     * @param endQueryTime The end of the time range to search. A value of 0 indicates that there
+     *                     is no limit on the end time. See the native QueryOptions.
+     */
+    public void queryHistory(Callback<List<HistoryItem>> callback, String query,
+            long endQueryTime) {
+        mCallback = callback;
+        nativeQueryHistory(mNativeHistoryBridge, new ArrayList<HistoryItem>(), query, endQueryTime);
+    }
+
+    @CalledByNative
+    public static void createHistoryItemAndAddToList(
+            List<HistoryItem> items, String url, String domain, String title, long timestamp) {
+        items.add(new HistoryItem(url, domain, title, timestamp));
+    }
+
+    @CalledByNative
+    public void onQueryHistoryComplete(List<HistoryItem> items) {
+        mCallback.onResult(items);
+    }
+
+    private native long nativeInit(Profile profile);
+    private native void nativeDestroy(long nativeBrowsingHistoryBridge);
+    private native void nativeQueryHistory(long nativeBrowsingHistoryBridge,
+            List<HistoryItem> historyItems, String query, long queryEndTime);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
new file mode 100644
index 0000000..e2f71f3
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.os.Bundle;
+
+import org.chromium.chrome.browser.SynchronousInitializationActivity;
+
+/**
+ * Activity for displaying the browsing history manager.
+ */
+public class HistoryActivity extends SynchronousInitializationActivity {
+    private HistoryManager mHistoryManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mHistoryManager = new HistoryManager(this);
+        setContentView(mHistoryManager.getView());
+    }
+
+    @Override
+    protected void onDestroy() {
+        mHistoryManager.onDestroyed();
+        mHistoryManager = null;
+        super.onDestroy();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
new file mode 100644
index 0000000..6c77d5d5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.DateDividedAdapter;
+import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
+
+import java.util.List;
+
+/**
+ * Bridges the user's browsing history and the UI used to display it.
+ */
+public class HistoryAdapter extends DateDividedAdapter {
+    private static final String EMPTY_QUERY = "";
+
+    private final SelectionDelegate<HistoryItem> mSelectionDelegate;
+    private final BrowsingHistoryBridge mBridge;
+    private final HistoryManager mManager;
+
+    private boolean mDestroyed;
+
+    public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager) {
+        setHasStableIds(true);
+        mSelectionDelegate = delegate;
+        mBridge = new BrowsingHistoryBridge();
+        mManager = manager;
+    }
+
+    /**
+     * Called when the activity/native page is destroyed.
+     */
+    public void onDestroyed() {
+        mBridge.destroy();
+        mDestroyed = true;
+    }
+
+    /**
+     * Initializes the HistoryAdapter and loads the first set of browsing history items.
+     */
+    public void initialize() {
+        mBridge.queryHistory(new Callback<List<HistoryItem>>() {
+            @Override
+            public void onResult(List<HistoryItem> result) {
+                // Return early if the results are returned after the activity/native page is
+                // destroyed to avoid unnecessary work.
+                if (mDestroyed) return;
+
+                loadItems(result);
+            }
+        }, EMPTY_QUERY, 0);
+    }
+
+    @Override
+    protected ViewHolder createViewHolder(ViewGroup parent) {
+        View v = LayoutInflater.from(parent.getContext()).inflate(
+                R.layout.history_item_view, parent, false);
+        return new SelectableItemViewHolder<HistoryItem>(v, mSelectionDelegate);
+    }
+
+    @Override
+    protected void bindViewHolderForTimedItem(ViewHolder current, TimedItem timedItem) {
+        final HistoryItem item = (HistoryItem) timedItem;
+        @SuppressWarnings("unchecked")
+        SelectableItemViewHolder<HistoryItem> holder =
+                (SelectableItemViewHolder<HistoryItem>) current;
+        holder.displayItem(item);
+        item.setHistoryManager(mManager);
+    }
+
+    @Override
+    protected int getTimedItemViewResId() {
+        return R.layout.date_view;
+    }
+
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java
new file mode 100644
index 0000000..3ddc16ec
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItem.java
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.text.TextUtils;
+
+import org.chromium.chrome.browser.widget.DateDividedAdapter.TimedItem;
+
+/** Contains information about a single browsing history item. */
+public class HistoryItem extends TimedItem {
+    private final String mUrl;
+    private final String mDomain;
+    private final String mTitle;
+    private final long mTimestamp;
+    private Long mStableId;
+
+    private HistoryManager mManager;
+
+    /**
+     * @param url The url for this item.
+     * @param domain The string to display for the item's domain.
+     * @param title The string to display for the item's title.
+     * @param timestamp The timestamp for this item.
+     */
+    public HistoryItem(String url, String domain, String title, long timestamp) {
+        mUrl = url;
+        mDomain = domain;
+        mTitle = TextUtils.isEmpty(title) ? url : title;
+        mTimestamp = timestamp;
+    }
+
+    /** @return The url for this item. */
+    public String getUrl() {
+        return mUrl;
+    }
+
+    /** @return The string to display for the item's domain. */
+    public String getDomain() {
+        return mDomain;
+    }
+
+    /** @return The string to display for the item's title. */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    @Override
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
+    @Override
+    public long getStableId() {
+        if (mStableId == null) {
+            // Generate a stable ID that combines the timestamp and the URL.
+            mStableId = (long) mUrl.hashCode();
+            mStableId = (mStableId << 32) + (getTimestamp() & 0x0FFFFFFFF);
+        }
+        return mStableId;
+    }
+
+    /**
+     * @param manager The HistoryManager associated with this item.
+     */
+    public void setHistoryManager(HistoryManager manager) {
+        mManager = manager;
+    }
+
+    /**
+     * Navigates a tab to this item's URL.
+     */
+    public void open() {
+        if (mManager != null) mManager.openItem(mUrl, null, false);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
new file mode 100644
index 0000000..f0dc825f31
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.selection.SelectableItemView;
+
+/**
+ * The SelectableItemView for items displayed in the browsing history UI.
+ */
+public class HistoryItemView extends SelectableItemView<HistoryItem> {
+    private TextView mTitle;
+    private TextView mDomain;
+
+    public HistoryItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTitle = (TextView) findViewById(R.id.title);
+        mDomain = (TextView) findViewById(R.id.domain);
+    }
+
+    @Override
+    public void setItem(HistoryItem item) {
+        super.setItem(item);
+        mTitle.setText(item.getTitle());
+        mDomain.setText(item.getDomain());
+    }
+
+    /**
+     * @param manager The HistoryManager associated with this item.
+     */
+    public void setHistoryManager(HistoryManager manager) {
+        getItem().setHistoryManager(manager);
+    }
+
+    @Override
+    protected void onClick() {
+        if (getItem() != null) getItem().open();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
new file mode 100644
index 0000000..cbe8dfa
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -0,0 +1,140 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Browser;
+import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.IntentHandler;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.util.IntentUtils;
+import org.chromium.chrome.browser.widget.TintedDrawable;
+import org.chromium.chrome.browser.widget.selection.SelectableListLayout;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
+import org.chromium.ui.base.DeviceFormFactor;
+
+import java.util.List;
+
+/**
+ * Displays and manages the UI for browsing history.
+ */
+public class HistoryManager implements OnMenuItemClickListener {
+    private final Activity mActivity;
+    private final SelectableListLayout mSelectableListLayout;
+    private final HistoryAdapter mHistoryAdapter;
+    private final SelectionDelegate<HistoryItem> mSelectionDelegate;
+
+    /**
+     * Creates a new HistoryManager.
+     * @param activity The Activity associated with the HistoryManager.
+     */
+    public HistoryManager(Activity activity) {
+        mActivity = activity;
+
+        mSelectionDelegate = new SelectionDelegate<>();
+        mHistoryAdapter = new HistoryAdapter(mSelectionDelegate, this);
+
+        mSelectableListLayout =
+                (SelectableListLayout) LayoutInflater.from(activity).inflate(
+                        R.layout.history_main, null);
+        mSelectableListLayout.initializeRecyclerView(mHistoryAdapter);
+        mSelectableListLayout.initializeToolbar(R.layout.history_toolbar, mSelectionDelegate,
+                R.string.menu_history, null, R.id.normal_menu_group,
+                R.id.selection_mode_menu_group, this);
+        mSelectableListLayout.initializeEmptyView(
+                TintedDrawable.constructTintedDrawable(mActivity.getResources(),
+                        R.drawable.history_large),
+                R.string.history_manager_empty);
+
+        mHistoryAdapter.initialize();
+
+        // TODO(twellington): add a scroll listener to the RecyclerView that loads more items when
+        //                    the scroll position is near the end.
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        if (item.getItemId() == R.id.close_menu_id && !DeviceFormFactor.isTablet(mActivity)) {
+            mActivity.finish();
+            return true;
+        } else if (item.getItemId() == R.id.selection_mode_open_in_new_tab) {
+            openItemsInNewTabs(mSelectionDelegate.getSelectedItems(), false);
+            mSelectionDelegate.clearSelection();
+            return true;
+        } else if (item.getItemId() == R.id.selection_mode_open_in_incognito) {
+            openItemsInNewTabs(mSelectionDelegate.getSelectedItems(), true);
+            mSelectionDelegate.clearSelection();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return The view that shows the main browsing history UI.
+     */
+    public ViewGroup getView() {
+        return mSelectableListLayout;
+    }
+
+    /**
+     * Called when the activity/native page is destroyed.
+     */
+    public void onDestroyed() {
+        mSelectableListLayout.onDestroyed();
+        mHistoryAdapter.onDestroyed();
+    }
+
+    /**
+     * Open the history item.
+     * @param url The URL of the history item.
+     * @param isIncognito Whether to open the history item in an incognito tab. If null, the tab
+     *                    will open in the current tab model.
+     * @param createNewTab Whether a new tab should be created. If false, the item will clobber the
+     *                     the current tab.
+     */
+    public void openItem(String url, Boolean isIncognito, boolean createNewTab) {
+        // Construct basic intent.
+        Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+        viewIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
+                mActivity.getApplicationContext().getPackageName());
+
+        // Determine component or class name.
+        ComponentName component;
+        if (DeviceFormFactor.isTablet(mActivity)) {
+            component = mActivity.getComponentName();
+        } else {
+            component = IntentUtils.safeGetParcelableExtra(
+                    mActivity.getIntent(), IntentHandler.EXTRA_PARENT_COMPONENT);
+        }
+        if (component != null) {
+            viewIntent.setComponent(component);
+        } else {
+            viewIntent.setClass(mActivity, ChromeLauncherActivity.class);
+        }
+
+        // Set other intent extras.
+        if (isIncognito != null) {
+            viewIntent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, isIncognito);
+        }
+        if (createNewTab) viewIntent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+
+        // Send intent.
+        IntentHandler.startActivityForTrustedIntent(viewIntent, mActivity);
+    }
+
+    private void openItemsInNewTabs(List<HistoryItem> items, boolean isIncognito) {
+        for (HistoryItem item : items) {
+            openItem(item.getUrl(), isIncognito, true);
+        }
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
new file mode 100644
index 0000000..29bfbf0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.selection.SelectionToolbar;
+import org.chromium.ui.base.DeviceFormFactor;
+
+/**
+ * The SelectionToolbar for the browsing history UI.
+ */
+public class HistoryManagerToolbar extends SelectionToolbar<HistoryItem> {
+    public HistoryManagerToolbar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        inflateMenu(R.menu.history_manager_menu);
+
+        if (DeviceFormFactor.isTablet(getContext())) {
+            getMenu().removeItem(R.id.close_menu_id);
+        }
+
+        // TODO(twellington): Add content descriptions to the number roll view.
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerUtils.java
new file mode 100644
index 0000000..2e61b311
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManagerUtils.java
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.IntentHandler;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.variations.VariationsAssociatedData;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.DeviceFormFactor;
+
+/**
+ * Utility methods for the browsing history manager.
+ */
+public class HistoryManagerUtils {
+    private static final String FIELD_TRIAL_NAME = "AndroidHistoryManager";
+    private static final String ENABLE_HISTORY_SWTICH = "enable_android_history_manager";
+    private static final Object NATIVE_HISTORY_ENABLED_LOCK = new Object();
+    private static Boolean sNativeHistoryEnabled;
+
+    /**
+    * @return Whether the Android-specific browsing history manager is enabled.
+    */
+    public static boolean isAndroidHistoryManagerEnabled() {
+        synchronized (NATIVE_HISTORY_ENABLED_LOCK) {
+            if (sNativeHistoryEnabled == null) {
+                if (CommandLine.getInstance().hasSwitch(ENABLE_HISTORY_SWTICH)) {
+                    sNativeHistoryEnabled = true;
+                } else {
+                    sNativeHistoryEnabled = TextUtils.equals("true",
+                            VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME,
+                                    ENABLE_HISTORY_SWTICH));
+                }
+            }
+        }
+
+        return sNativeHistoryEnabled;
+    }
+
+    /**
+    * @return Whether the Android-specific browsing history UI is was shown.
+    */
+    public static boolean showHistoryManager(Activity activity, Tab tab) {
+        if (!isAndroidHistoryManagerEnabled()) return false;
+
+        Context appContext = ContextUtils.getApplicationContext();
+        if (DeviceFormFactor.isTablet(appContext)) {
+            // History shows up as a tab on tablets.
+            LoadUrlParams params = new LoadUrlParams(UrlConstants.NATIVE_HISTORY_URL);
+            tab.loadUrl(params);
+        } else {
+            Intent intent = new Intent();
+            intent.setClass(appContext, HistoryActivity.class);
+            intent.putExtra(IntentHandler.EXTRA_PARENT_COMPONENT, activity.getComponentName());
+            activity.startActivity(intent);
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
new file mode 100644
index 0000000..97e328f9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.history;
+
+import android.app.Activity;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.BasicNativePage;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.tab.Tab;
+
+/**
+ * Native page for managing browsing history.
+ */
+public class HistoryPage extends BasicNativePage {
+    private HistoryManager mHistoryManager;
+    private String mTitle;
+
+    /**
+     * Create a new instance of the history page.
+     * @param activity The {@link Activity} used to get context and instantiate the
+     *                 {@link HistoryManager}.
+     * @param tab The tab to load URLs.
+     */
+    public HistoryPage(Activity activity, Tab tab) {
+        super(activity, tab);
+    }
+
+    @Override
+    protected void initialize(Activity activity, final Tab tab) {
+        mHistoryManager = new HistoryManager(activity);
+        mTitle = activity.getString(R.string.menu_history);
+    }
+
+    @Override
+    public View getView() {
+        return mHistoryManager.getView();
+    }
+
+    @Override
+    public String getTitle() {
+        return mTitle;
+    }
+
+    @Override
+    public String getHost() {
+        return UrlConstants.HISTORY_HOST;
+    }
+
+    @Override
+    public void destroy() {
+        mHistoryManager.onDestroyed();
+        mHistoryManager = null;
+        super.destroy();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/history/OWNERS
new file mode 100644
index 0000000..05d32ba
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/OWNERS
@@ -0,0 +1 @@
+twellington@chromium.org
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
index 68f186c..71caf98 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
@@ -49,8 +49,7 @@
      * @param nativeInfoBarPtr Pointer to the native InfoBarAndroid, not to its subclass.
      */
     @CalledByNative
-    private void setNativeInfoBar(long nativeInfoBarPtr) {
-        assert mNativeInfoBarPtr == 0;
+    private final void setNativeInfoBar(long nativeInfoBarPtr) {
         mNativeInfoBarPtr = nativeInfoBarPtr;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index a757b28..c7edd138 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -27,6 +27,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LoaderErrors;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.R;
@@ -61,6 +62,7 @@
     private MemoryUma mMemoryUma;
     private long mLastUserInteractionTime;
     private boolean mIsTablet;
+    private boolean mHadWarmStart;
 
     public AsyncInitializationActivity() {
         mHandler = new Handler();
@@ -95,6 +97,7 @@
 
     @Override
     public void preInflationStartup() {
+        mHadWarmStart = LibraryLoader.isInitialized();
         // On some devices, OEM modifications have been made to the resource loader that cause the
         // DeviceFormFactor calculation of whether a device is using tablet resources to be
         // incorrect. Check which resources were actually loaded and set the DeviceFormFactor
@@ -397,6 +400,14 @@
         return mIsTablet;
     }
 
+    /**
+     * @return Whether the activity had a warm start because the native library was already fully
+     *     loaded and initialized.
+     */
+    public boolean hadWarmStart() {
+        return mHadWarmStart;
+    }
+
     @Override
     public void onUserInteraction() {
         mLastUserInteractionTime = SystemClock.elapsedRealtime();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/AuthenticatedProxyActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/AuthenticatedProxyActivity.java
index 9afb954e..6364452b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/AuthenticatedProxyActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/AuthenticatedProxyActivity.java
@@ -25,7 +25,13 @@
 
         Intent forwardIntent = (Intent) getIntent().getParcelableExtra(AUTHENTICATED_INTENT_EXTRA);
         if (forwardIntent != null) {
-            startActivityForResult(forwardIntent, -1);
+            // Ensure that the forwardIntent doesn't have FLAG_ACTIVITY_NEW_TASK or
+            // FLAG_ACTIVITY_NEW_DOCUMENT set as those don't work with startActivityForResult().
+            forwardIntent.setFlags(forwardIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
+            forwardIntent.setFlags(forwardIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+            // Non-negative result code ensures that startActivityForResult doesn't degrade to
+            // startActivity() behaviour.
+            startActivityForResult(forwardIntent, 0);
         }
         finish();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
index 52d63fb..c12128f0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java
@@ -75,7 +75,21 @@
 
     /** A histogram to record how long the GMS Core API call took. */
     private static final TimesHistogramSample sInstantAppsApiCallTimes = new TimesHistogramSample(
-            "Android.InstantApps.ApiCallDuration", TimeUnit.MILLISECONDS);
+            "Android.InstantApps.ApiCallDuration2", TimeUnit.MILLISECONDS);
+
+    /**
+     * A histogram to record how long the GMS Core API call took when the instant app was found.
+     */
+    private static final TimesHistogramSample sInstantAppsApiCallTimesHasApp =
+            new TimesHistogramSample("Android.InstantApps.ApiCallDurationWithApp",
+                    TimeUnit.MILLISECONDS);
+
+    /**
+     * A histogram to record how long the GMS Core API call took when the instant app was not found.
+     */
+    private static final TimesHistogramSample sInstantAppsApiCallTimesNoApp =
+            new TimesHistogramSample("Android.InstantApps.ApiCallDurationWithoutApp",
+                    TimeUnit.MILLISECONDS);
 
     /** @return The singleton instance of {@link InstantAppsHandler}. */
     public static InstantAppsHandler getInstance() {
@@ -121,6 +135,19 @@
     }
 
     /**
+     * Record the amount of time spent in the Instant Apps API call.
+     * @param startTime The time at which we started doing computations.
+     * @param hasApp Whether the API has found an Instant App during the call.
+     */
+    protected void recordInstantAppsApiCallTime(long startTime, boolean hasApp) {
+        if (hasApp) {
+            sInstantAppsApiCallTimesHasApp.record(SystemClock.elapsedRealtime() - startTime);
+        } else {
+            sInstantAppsApiCallTimesNoApp.record(SystemClock.elapsedRealtime() - startTime);
+        }
+    }
+
+    /**
      * In the case Chrome is called through the fallback mechanism from Instant Apps, record the
      * amount of time the whole trip took.
      * @param intent The current intent.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java
index 4eb9ec3..1d0d7e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.media.ui;
 
 import android.graphics.Bitmap;
+import android.support.annotation.Nullable;
 
 /**
  * The callback when an image is downloaded. This class is different with
@@ -13,7 +14,8 @@
 public interface MediaImageCallback {
     /**
      * Called when image downloading is complete.
-     * @param bitmap The downloaded image.
+     * @param bitmap The downloaded image. |null| indicates there is no available src for download
+     * or image download failed.
      */
-    void onImageDownloaded(Bitmap image);
+    void onImageDownloaded(@Nullable Bitmap image);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
index 768502b..0010848 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java
@@ -124,7 +124,11 @@
 
         mCallback = callback;
         MediaImage image = selectImage(images);
-        if (image == null) return;
+        if (image == null) {
+            mCallback.onImageDownloaded(null);
+            clearRequests();
+            return;
+        }
 
         mRequestId = mWebContents.downloadImage(
             image.getSrc(), false, 8 * mIdealSize, false, this);
@@ -163,7 +167,7 @@
                 bestScore = newScore;
             }
         }
-        if (bestBitmap != null) mCallback.onImageDownloaded(bestBitmap);
+        mCallback.onImageDownloaded(bestBitmap);
         clearRequests();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 7646bd23..c2f190f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -476,10 +476,11 @@
 
     /**
      * Scale a given bitmap to a proper size for display.
-     * @param icon The bitmap to be resized.
+     * @param icon The bitmap to be resized, can be null.
      * @return A scaled icon to be used in media notification. Returns null if |icon| is null.
      */
-    public static Bitmap scaleIconForDisplay(Bitmap icon) {
+    @Nullable
+    public static Bitmap scaleIconForDisplay(@Nullable Bitmap icon) {
         if (icon == null) return null;
 
         int largeIconSizePx = getMaximumLargeIconSize();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java
index 79910e58..fda2dde2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.support.annotation.IntDef;
 import android.support.annotation.StringRes;
+import android.support.v13.view.ViewCompat;
 import android.view.ContextMenu;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -80,7 +81,7 @@
      *                 are tapped.
      */
     public void createContextMenu(ContextMenu menu, View associatedView, Delegate delegate) {
-        OnMenuItemClickListener listener = new ItemClickListener(delegate);
+        OnMenuItemClickListener listener = new ItemClickListener(delegate, associatedView);
         boolean hasItems = false;
 
         for (@ContextMenuItemId int itemId : MenuItemLabelMatcher.STRING_MAP.keySet()) {
@@ -167,13 +168,23 @@
 
     private static class ItemClickListener implements OnMenuItemClickListener {
         private final Delegate mDelegate;
+        private final View mAssociatedView;
 
-        ItemClickListener(Delegate delegate) {
+        ItemClickListener(Delegate delegate, View associatedView) {
             mDelegate = delegate;
+            mAssociatedView = associatedView;
         }
 
         @Override
         public boolean onMenuItemClick(MenuItem item) {
+            // If the user clicks a snippet then immediately long presses they will create a context
+            // menu while the snippet's URL loads in the background. This means that when they press
+            // an item on context menu the NTP will not actually be open. We add this check here to
+            // prevent taking any action if the user has left the NTP. (https://crbug.com/640468)
+            // Although the menu is supposed to be closed when we navigate away from the NTP, we
+            // have to keep this check because of race conditions. (https://crbug.com/668945)
+            if (!ViewCompat.isAttachedToWindow(mAssociatedView)) return true;
+
             switch (item.getItemId()) {
                 case ID_OPEN_IN_NEW_WINDOW:
                     mDelegate.openItem(WindowOpenDisposition.NEW_WINDOW);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
index 5d8e1eb2..f7961a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.bookmarks.BookmarkPage;
 import org.chromium.chrome.browser.download.DownloadPage;
+import org.chromium.chrome.browser.history.HistoryPage;
 import org.chromium.chrome.browser.physicalweb.PhysicalWebDiagnosticsPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -46,6 +47,10 @@
             return new DownloadPage(activity, tab);
         }
 
+        protected NativePage buildHistoryPage(Activity activity, Tab tab) {
+            return new HistoryPage(activity, tab);
+        }
+
         protected NativePage buildRecentTabsPage(Activity activity, Tab tab) {
             RecentTabsManager recentTabsManager =
                     new RecentTabsManager(tab, tab.getProfile(), activity);
@@ -58,7 +63,7 @@
     }
 
     enum NativePageType {
-        NONE, CANDIDATE, NTP, BOOKMARKS, RECENT_TABS, PHYSICAL_WEB, DOWNLOADS,
+        NONE, CANDIDATE, NTP, BOOKMARKS, RECENT_TABS, PHYSICAL_WEB, DOWNLOADS, HISTORY,
     }
 
     private static NativePageType nativePageType(String url, NativePage candidatePage,
@@ -81,6 +86,8 @@
             return NativePageType.BOOKMARKS;
         } else if (UrlConstants.DOWNLOADS_HOST.equals(host)) {
             return NativePageType.DOWNLOADS;
+        } else if (UrlConstants.HISTORY_HOST.equals(host)) {
+            return NativePageType.HISTORY;
         } else if (UrlConstants.RECENT_TABS_HOST.equals(host) && !isIncognito) {
             return NativePageType.RECENT_TABS;
         } else if (UrlConstants.PHYSICAL_WEB_DIAGNOSTICS_HOST.equals(host)) {
@@ -133,6 +140,9 @@
             case DOWNLOADS:
                 page = sNativePageBuilder.buildDownloadsPage(activity, tab);
                 break;
+            case HISTORY:
+                page = sNativePageBuilder.buildHistoryPage(activity, tab);
+                break;
             case RECENT_TABS:
                 page = sNativePageBuilder.buildRecentTabsPage(activity, tab);
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 8bfa7bcd..03d80418 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -757,6 +757,7 @@
 
         RecordHistogram.recordBooleanHistogram(
                 "NewTabPage.MobileIsUserOnline", NetworkChangeNotifier.isOnline());
+        NewTabPageUma.recordLoadType(mActivity);
     }
 
     private static MostVisitedSites buildMostVisitedSites(Profile profile) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
index fd272c5f..96ef6e50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java
@@ -9,6 +9,8 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
 import org.chromium.chrome.browser.rappor.RapporServiceBridge;
@@ -20,6 +22,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Records UMA stats for which actions the user takes on the NTP in the
@@ -89,6 +92,16 @@
     /** The number of possible results for the NewTabPageLayout calculations. */
     public static final int NUM_NTP_LAYOUT_RESULTS = 5;
 
+    // The NTP was loaded in a cold startup.
+    private static final int LOAD_TYPE_COLD_START = 0;
+    // The NTP was loaded in a warm startup.
+    private static final int LOAD_TYPE_WARM_START = 1;
+    // The NTP was loaded at some other time after activity creation and the user interacted with
+    // the activity in the meantime.
+    private static final int LOAD_TYPE_OTHER = 2;
+    // The number of load types.
+    private static final int LOAD_TYPE_COUNT = 3;
+
     /**
      * Records an action taken by the user on the NTP.
      * @param action One of the ACTION_* values defined in this class.
@@ -183,6 +196,46 @@
     }
 
     /**
+     * Records the type of load for the ntp, such as cold or warm start.
+     */
+    public static void recordLoadType(ChromeActivity activity) {
+        if (activity.getLastUserInteractionTime() > 0) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "NewTabPage.LoadType", LOAD_TYPE_OTHER, LOAD_TYPE_COUNT);
+            return;
+        }
+
+        if (activity.hadWarmStart()) {
+            RecordHistogram.recordEnumeratedHistogram(
+                    "NewTabPage.LoadType", LOAD_TYPE_WARM_START, LOAD_TYPE_COUNT);
+            return;
+        }
+
+        RecordHistogram.recordEnumeratedHistogram(
+                "NewTabPage.LoadType", LOAD_TYPE_COLD_START, LOAD_TYPE_COUNT);
+    }
+
+    /**
+     * Records how much time elapsed from start until the search box became available to the user.
+     */
+    public static void recordSearchAvailableLoadTime(ChromeActivity activity) {
+        // Log the time it took for the search box to be displayed at startup, based on the
+        // timestamp on the intent for the activity. If the user has interacted with the
+        // activity already, it's not a startup, and the timestamp on the activity would not be
+        // relevant either.
+        if (activity.getLastUserInteractionTime() != 0) return;
+        long timeFromIntent = SystemClock.elapsedRealtime()
+                - IntentHandler.getTimestampFromIntent(activity.getIntent());
+        if (activity.hadWarmStart()) {
+            RecordHistogram.recordTimesHistogram("NewTabPage.SearchAvailableLoadTime.WarmStart",
+                    timeFromIntent, TimeUnit.MILLISECONDS);
+        } else {
+            RecordHistogram.recordTimesHistogram("NewTabPage.SearchAvailableLoadTime.ColdStart",
+                    timeFromIntent, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /**
      * Records stats related to content suggestion visits, such as the time spent on the website, or
      * if the user comes back to the NTP. Use through
      * {@link NewTabPageUma#monitorContentSuggestionVisit(Tab, int)}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index fff4729..0437008f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -42,6 +42,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
 import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback;
 import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
@@ -106,6 +107,7 @@
 
     private OnSearchBoxScrollListener mSearchBoxScrollListener;
 
+    private ChromeActivity mActivity;
     private NewTabPageManager mManager;
     private UiConfig mUiConfig;
     private MostVisitedDesign mMostVisitedDesign;
@@ -320,6 +322,7 @@
      */
     public void initialize(
             NewTabPageManager manager, Tab tab, boolean searchProviderHasLogo, int scrollPosition) {
+        mActivity = tab.getActivity();
         mManager = manager;
         mUiConfig = new UiConfig(this);
         ViewStub stub = (ViewStub) findViewById(R.id.new_tab_page_layout_stub);
@@ -870,6 +873,7 @@
         if (mFirstShow) {
             loadTaskCompleted();
             mFirstShow = false;
+            NewTabPageUma.recordSearchAvailableLoadTime(mActivity);
         } else {
             // Trigger a scroll update when reattaching the window to signal the toolbar that
             // it needs to reset the NTP state.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 4e952c4c..4624e4f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -152,7 +152,7 @@
                     }
 
                     @Override
-                    public void offlinePageModelChanged() {
+                    public void offlinePageAdded(OfflinePageItem addedPage) {
                         updateAllSnippetOfflineAvailability();
                     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 7514552..04d12de7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -78,7 +78,7 @@
          * Called when the native side of offline pages is changed due to adding, removing or
          * update an offline page.
          */
-        public void offlinePageModelChanged() {}
+        public void offlinePageAdded(OfflinePageItem addedPage) {}
 
         /**
          * Called when an offline page is deleted. This can be called as a result of
@@ -440,9 +440,9 @@
     }
 
     @CalledByNative
-    protected void offlinePageModelChanged() {
+    protected void offlinePageAdded(OfflinePageItem addedPage) {
         for (OfflinePageModelObserver observer : mObservers) {
-            observer.offlinePageModelChanged();
+            observer.offlinePageAdded(addedPage);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
index 1dced802..5be2a6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.offlinepages.evaluation;
 
 import org.chromium.base.Callback;
+import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
@@ -14,8 +15,17 @@
 import org.chromium.chrome.browser.offlinepages.SavePageRequest;
 import org.chromium.chrome.browser.profiles.Profile;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import java.text.SimpleDateFormat;
+
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Class used for offline page evaluation testing tools.
@@ -65,11 +75,14 @@
         return nativeGetBridgeForProfile(profile, useEvaluationScheduler);
     }
 
+    private static final String TAG = "OPEvalBridge";
     private long mNativeOfflinePageEvaluationBridge;
     private boolean mIsOfflinePageModelLoaded;
     private ObserverList<OfflinePageEvaluationObserver> mObservers =
             new ObserverList<OfflinePageEvaluationObserver>();
 
+    private OutputStreamWriter mLogOutput;
+
     /**
      * Creates an offline page evalutaion bridge for a given profile.
      */
@@ -152,6 +165,10 @@
         nativeRemoveRequestsFromQueue(mNativeOfflinePageEvaluationBridge, ids, callback);
     }
 
+    public void setLogOutputFile(File outputFile) throws IOException {
+        mLogOutput = new FileWriter(outputFile);
+    }
+
     /**
      * @return True if the offline page model has fully loaded.
      */
@@ -160,6 +177,27 @@
     }
 
     @CalledByNative
+    public void log(String sourceTag, String message) {
+        try {
+            Date date = new Date(System.currentTimeMillis());
+            SimpleDateFormat formatter =
+                    new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.getDefault());
+            mLogOutput.write(formatter.format(date) + ": " + sourceTag + " | " + message
+                    + System.getProperty("line.separator"));
+        } catch (IOException e) {
+            Log.e(TAG, e.getMessage(), e);
+        }
+    }
+
+    public void closeLog() {
+        try {
+            mLogOutput.close();
+        } catch (IOException e) {
+            Log.e(TAG, e.getMessage(), e);
+        }
+    }
+
+    @CalledByNative
     void savePageRequestAdded(SavePageRequest request) {
         for (OfflinePageEvaluationObserver observer : mObservers) {
             observer.savePageRequestAdded(request);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/NearbyMessageIntentService.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/NearbyMessageIntentService.java
index 0371e6e..23651ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/NearbyMessageIntentService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/NearbyMessageIntentService.java
@@ -8,30 +8,15 @@
 import android.content.Intent;
 
 import com.google.android.gms.nearby.Nearby;
-import com.google.android.gms.nearby.messages.Message;
 import com.google.android.gms.nearby.messages.MessageListener;
 
 /**
  * Service that handles intents from Nearby.
  */
 public class NearbyMessageIntentService extends IntentService {
-    private static final MessageListener MESSAGE_LISTENER = new MessageListener() {
-        @Override
-        public void onFound(Message message) {
-            String url = PhysicalWebBleClient.getInstance().getUrlFromMessage(message);
-            if (url != null) {
-                UrlManager.getInstance().addUrl(new UrlInfo(url));
-            }
-        }
+    private static final MessageListener MESSAGE_LISTENER =
+            PhysicalWebBleClient.getInstance().createBackgroundMessageListener();
 
-        @Override
-        public void onLost(Message message) {
-            String url = PhysicalWebBleClient.getInstance().getUrlFromMessage(message);
-            if (url != null) {
-                UrlManager.getInstance().removeUrl(new UrlInfo(url));
-            }
-        }
-    };
 
     public NearbyMessageIntentService() {
         super(NearbyMessageIntentService.class.getSimpleName());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBleClient.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBleClient.java
index 2335cfc..9ad9c89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBleClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebBleClient.java
@@ -34,6 +34,24 @@
         public void onFound(Message message) {}
     }
 
+    protected static class BackgroundMessageListener extends MessageListener {
+        @Override
+        public void onFound(Message message) {
+            String url = PhysicalWebBleClient.getInstance().getUrlFromMessage(message);
+            if (url != null) {
+                UrlManager.getInstance().addUrl(new UrlInfo(url));
+            }
+        }
+
+        @Override
+        public void onLost(Message message) {
+            String url = PhysicalWebBleClient.getInstance().getUrlFromMessage(message);
+            if (url != null) {
+                UrlManager.getInstance().removeUrl(new UrlInfo(url));
+            }
+        }
+    };
+
     /**
      * Get a singleton instance of this class.
      * @return an instance of this class (or subclass).
@@ -89,6 +107,14 @@
     }
 
     /**
+     * Create a MessageListener that listens during a background scan.
+     * @return the MessageListener.
+     */
+    MessageListener createBackgroundMessageListener() {
+        return new BackgroundMessageListener();
+    }
+
+    /**
      * Begin a foreground subscription to URLs broadcasted from BLE beacons.
      * This currently does nothing and should be overridden by a subclass.
      * @param activity The Activity that is performing the scan.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlInfo.java
index c05f3e0..f32fb59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlInfo.java
@@ -16,16 +16,19 @@
     private static final String URL_KEY = "url";
     private static final String DISTANCE_KEY = "distance";
     private static final String SCAN_TIMESTAMP_KEY = "scan_timestamp";
+    private static final String DEVICE_ADDRESS_KEY = "device_address";
     private static final String HAS_BEEN_DISPLAYED_KEY = "has_been_displayed";
     private final String mUrl;
     private double mDistance;
     private long mScanTimestamp;
+    private String mDeviceAddress;
     private boolean mHasBeenDisplayed;
 
     public UrlInfo(String url, double distance, long scanTimestamp) {
         mUrl = url;
         mDistance = distance;
         mScanTimestamp = scanTimestamp;
+        mDeviceAddress = null;
         mHasBeenDisplayed = false;
     }
 
@@ -48,8 +51,9 @@
      * Sets the distance of the URL from the scanner in meters.
      * @param distance The estimated distance of the URL from the scanner in meters.
      */
-    public void setDistance(double distance) {
+    public UrlInfo setDistance(double distance) {
         mDistance = distance;
+        return this;
     }
 
     /**
@@ -65,8 +69,9 @@
      * This timestamp should be recorded using System.currentTimeMillis().
      * @param scanTimestamp the new timestamp.
      */
-    public void setScanTimestamp(long scanTimestamp) {
+    public UrlInfo setScanTimestamp(long scanTimestamp) {
         mScanTimestamp = scanTimestamp;
+        return this;
     }
 
     /**
@@ -79,10 +84,29 @@
     }
 
     /**
+     * Sets the device address for the BLE beacon that last emitted this URL.
+     * @param deviceAddress the new device address, matching the
+     *        BluetoothAdapter.checkBluetoothAddress format.
+     */
+    public UrlInfo setDeviceAddress(String deviceAddress) {
+        mDeviceAddress = deviceAddress;
+        return this;
+    }
+
+    /**
+     * Gets the device address for the BLE beacon that last emitted this URL.
+     * @return The device address.
+     */
+    public String getDeviceAddress() {
+        return mDeviceAddress;
+    }
+
+    /**
      * Marks this URL as having been displayed to the user.
      */
-    public void setHasBeenDisplayed() {
+    public UrlInfo setHasBeenDisplayed() {
         mHasBeenDisplayed = true;
+        return this;
     }
 
     /**
@@ -103,6 +127,7 @@
                 .put(URL_KEY, mUrl)
                 .put(DISTANCE_KEY, mDistance)
                 .put(SCAN_TIMESTAMP_KEY, mScanTimestamp)
+                .put(DEVICE_ADDRESS_KEY, mDeviceAddress)
                 .put(HAS_BEEN_DISPLAYED_KEY, mHasBeenDisplayed);
     }
 
@@ -116,7 +141,8 @@
         UrlInfo urlInfo = new UrlInfo(
                 jsonObject.getString(URL_KEY),
                 jsonObject.getDouble(DISTANCE_KEY),
-                jsonObject.getLong(SCAN_TIMESTAMP_KEY));
+                jsonObject.getLong(SCAN_TIMESTAMP_KEY))
+                .setDeviceAddress(jsonObject.optString(DEVICE_ADDRESS_KEY));
         if (jsonObject.optBoolean(HAS_BEEN_DISPLAYED_KEY, false)) {
             urlInfo.setHasBeenDisplayed();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
index ad1c382e..d5ed9efc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java
@@ -401,7 +401,6 @@
         }
 
         // Read the cache.
-        mNearbyUrls.addAll(prefs.getStringSet(PREFS_NEARBY_URLS_KEY, new HashSet<String>()));
         for (String serializedUrl : prefs.getStringSet(PREFS_ALL_URLS_KEY, new HashSet<String>())) {
             try {
                 JSONObject jsonObject = new JSONObject(serializedUrl);
@@ -412,6 +411,8 @@
                 Log.e(TAG, "Could not deserialize UrlInfo", e);
             }
         }
+        mNearbyUrls.addAll(prefs.getStringSet(PREFS_NEARBY_URLS_KEY, new HashSet<String>()));
+        mNearbyUrls.retainAll(mUrlInfoMap.keySet());
         for (String serializedPwsResult : prefs.getStringSet(PREFS_PWS_RESULTS_KEY,
                 new HashSet<String>())) {
             try {
@@ -490,7 +491,12 @@
         } else {
             mUrlsSortedByTimestamp.remove(urlInfo.getUrl());
             currentUrlInfo.setScanTimestamp(urlInfo.getScanTimestamp());
-            currentUrlInfo.setDistance(urlInfo.getDistance());
+            if (urlInfo.getDistance() > 0.0) {
+                currentUrlInfo.setDistance(urlInfo.getDistance());
+            }
+            if (urlInfo.getDeviceAddress() != null) {
+                currentUrlInfo.setDeviceAddress(urlInfo.getDeviceAddress());
+            }
         }
         mUrlsSortedByTimestamp.add(urlInfo.getUrl());
         return currentUrlInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
index c19b4ca..c9e429a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
@@ -12,7 +12,6 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.preference.Preference;
-import android.preference.PreferenceCategory;
 import android.text.format.DateUtils;
 import android.text.format.Formatter;
 import android.util.AttributeSet;
@@ -27,9 +26,9 @@
 import java.util.TimeZone;
 
 /**
- * Preference category used to display statistics on data reduction.
+ * Preference used to display statistics on data reduction.
  */
-public class DataReductionStatsPreference extends PreferenceCategory {
+public class DataReductionStatsPreference extends Preference {
     private NetworkStatsHistory mOriginalNetworkStatsHistory;
     private NetworkStatsHistory mReceivedNetworkStatsHistory;
 
@@ -51,14 +50,17 @@
     public DataReductionStatsPreference(
             Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
     }
 
     public DataReductionStatsPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
     }
 
     public DataReductionStatsPreference(Context context) {
         super(context);
+        setWidgetLayoutResource(R.layout.data_reduction_stats_layout);
     }
 
     /**
@@ -83,11 +85,6 @@
     }
 
     @Override
-    protected boolean onPrepareAddPreference(Preference preference) {
-        return super.onPrepareAddPreference(preference);
-    }
-
-    @Override
     public boolean isEnabled() {
         return super.isEnabled();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index d1d9bcc..8cbbbd1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -845,7 +845,7 @@
      * @return {@link ChromeActivity} that currently contains this {@link Tab} in its
      *         {@link TabModel}.
      */
-    ChromeActivity getActivity() {
+    public ChromeActivity getActivity() {
         if (getWindowAndroid() == null) return null;
         Activity activity = WindowAndroid.activityFromContext(
                 getWindowAndroid().getContext().get());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkMetaData.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkMetaData.java
deleted file mode 100644
index bb48598..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkMetaData.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.webapps;
-
-import java.util.Map;
-
-/**
- * Meta data from the WebAPK's Android Manifest.
- */
-public class WebApkMetaData {
-    public int shellApkVersion;
-    public String manifestUrl;
-    public String startUrl;
-    public String scope;
-    public String name;
-    public String shortName;
-    public int displayMode;
-    public int orientation;
-    public long themeColor;
-    public long backgroundColor;
-    public int iconId;
-    public Map<String, String> iconUrlAndIconMurmur2HashMap;
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
index 01a6f61..860ba03 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java
@@ -67,6 +67,13 @@
         setChecked(mSelectionDelegate.isItemSelected(item));
     }
 
+    /**
+     * @return The item associated with this SelectableItemView.
+     */
+    public E getItem() {
+        return mItem;
+    }
+
     // FrameLayout implementations.
     @Override
     protected void onFinishInflate() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java
new file mode 100644
index 0000000..e80af00
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.widget.selection;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * An ViewHolder for a {@link SelectableItemView}.
+ * @param <E> The type of the item associated with the SelectableItemView.
+ */
+public class SelectableItemViewHolder<E> extends RecyclerView.ViewHolder {
+    private SelectableItemView<E> mItemView;
+
+    /**
+     * @param itemView The SelectableItemView to be held by this ViewHolder.
+     * @param delegate The SelectionDelegate for the itemView.
+     */
+    @SuppressWarnings("unchecked")
+    public SelectableItemViewHolder(View itemView, SelectionDelegate<E> delegate) {
+        super(itemView);
+        mItemView = (SelectableItemView<E>) itemView;
+        mItemView.setSelectionDelegate(delegate);
+    }
+
+    /**
+     * @param item The item to display in the SelectableItemView held by this ViewHolder.
+     */
+    public void displayItem(E item) {
+        mItemView.setItem(item);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
index 6e124262..7783b73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.widget.selection;
 
 import android.content.Context;
-import android.support.graphics.drawable.VectorDrawableCompat;
+import android.graphics.drawable.Drawable;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -131,14 +131,11 @@
     /**
      * Initializes the view shown when the selectable list is empty.
      *
-     * @param emptyIconResId The icon to show when the selectable list is empty.
+     * @param emptyDrawable The Drawable to show when the selectable list is empty.
      * @param emptyStringResId The string to show when the selectable list is empty.
      */
-    public void initializeEmptyView(int emptyIconResId, int emptyStringResId) {
-        mEmptyView.setCompoundDrawablesWithIntrinsicBounds(null,
-                VectorDrawableCompat.create(getResources(), emptyIconResId,
-                        getContext().getTheme()),
-                null, null);
+    public void initializeEmptyView(Drawable emptyDrawable, int emptyStringResId) {
+        mEmptyView.setCompoundDrawablesWithIntrinsicBounds(null, emptyDrawable, null, null);
         mEmptyView.setText(emptyStringResId);
     }
 
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index d4421ba6..381b9a8 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1793,6 +1793,11 @@
         Open with…
       </message>
 
+      <!-- Browsing History UI -->
+      <message name="IDS_HISTORY_MANAGER_EMPTY" desc="Indicates that there are no browsing history items.">
+        No history here
+      </message>
+
       <!-- Document mode messages -->
       <message name="IDS_CLOSE_ALL_INCOGNITO_NOTIFICATION" desc="Message on the notification that closes all incognito tabs in document mode">
         Close all incognito tabs
@@ -2773,61 +2778,61 @@
       </message>
 
       <!-- Keyboard shortcuts in Android N-->
-      <message name="IDS_KEYBOARD_SHORTCUT_OPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut to open a new tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_OPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut to open a new tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open a new tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_REOPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut for reopening the last tab in the chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_REOPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut for reopening the last tab in the chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Reopen the last closed tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_NEW_INCOGNITO_TAB" desc="A text label that appears next to a keyboard shortcut to open a new tab in incognito mode in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_NEW_INCOGNITO_TAB" desc="A text label that appears next to a keyboard shortcut to open a new tab in incognito mode in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open a new tab in Incognito mode
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_OPEN_MENU" desc="A text label that appears next to a keyboard shortcut that opens the overflow menu in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_OPEN_MENU" desc="A text label that appears next to a keyboard shortcut that opens the overflow menu in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open the menu
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_NEXT_TAB" desc="A text label that appears next to the keyboard shortcut that will move the user to the next tab in the Chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_NEXT_TAB" desc="A text label that appears next to the keyboard shortcut that will move the user to the next tab in the Chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Jump to the next tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_PREV_TAB" desc="A text label that appears next to the keyboard shortcut that will move the user to the previous tab in the Chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_PREV_TAB" desc="A text label that appears next to the keyboard shortcut that will move the user to the previous tab in the Chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Jump to the previous tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_CLOSE_TAB" desc="A text label that appears next to the keyboard shortcut that will close the current tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_CLOSE_TAB" desc="A text label that appears next to the keyboard shortcut that will close the current tab in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Close current tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_FIND_BAR" desc="A text label that appears next to the keyboard shortcut that will open the find bar that searches what is on the screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_FIND_BAR" desc="A text label that appears next to the keyboard shortcut that will open the find bar that searches what is on the screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open the Find Bar
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_ADDRESS_BAR" desc="A text label that appears next to the keyboard that will let you focus on the address bar on the screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_ADDRESS_BAR" desc="A text label that appears next to the keyboard that will let you focus on the address bar on the screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Jump to the address bar
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_BOOKMARK_MANAGER" desc="A text label that appears next to the keyboard shortcut that will open the bookmarks manager in Chrome. On a tablet this is a new tab, on the phone this is a new activity. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_BOOKMARK_MANAGER" desc="A text label that appears next to the keyboard shortcut that will open the bookmarks manager in Chrome. On a tablet this is a new tab, on the phone this is a new activity. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open the bookmarks manager
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_BOOKMARK_PAGE" desc="A text label that appears next to the keyboard shortcut that will bookmark the page that is currently on screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_BOOKMARK_PAGE" desc="A text label that appears next to the keyboard shortcut that will bookmark the page that is currently on screen. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Bookmark the current page
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_HISTORY_MANAGER" desc="A text label that appears next to the keyboard shorcut that will open up the history page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_HISTORY_MANAGER" desc="A text label that appears next to the keyboard shorcut that will open up the history page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open the history page
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_PRINT_PAGE" desc="A text label that appears next to the keyboard shortcut that will open the print page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_PRINT_PAGE" desc="A text label that appears next to the keyboard shortcut that will open the print page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open options to print page
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_ZOOM_IN" desc="A text label that appears next to the keyboard shortcut that will increase everything on the page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_ZOOM_IN" desc="A text label that appears next to the keyboard shortcut that will increase everything on the page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Make everything on the page bigger
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_ZOOM_OUT" desc="A text label that appears next to the keyboard shortcut that will decrease everything on the page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_ZOOM_OUT" desc="A text label that appears next to the keyboard shortcut that will decrease everything on the page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Make everything on the page smaller
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_RESET_ZOOM" desc="A text label that appears next to the keyboard shortcut that will reset the zoom back to the original size. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_RESET_ZOOM" desc="A text label that appears next to the keyboard shortcut that will reset the zoom back to the original size. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Return everything on the page to default size
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_RELOAD_PAGE" desc="A text label that appears next to the keyboard shortcut that will reload the current page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_RELOAD_PAGE" desc="A text label that appears next to the keyboard shortcut that will reload the current page. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Reload the current page
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_RELOAD_NO_CACHE" desc="A text label that appears next to the keyboard shortcut that will reload the current page without a cache. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_RELOAD_NO_CACHE" desc="A text label that appears next to the keyboard shortcut that will reload the current page without a cache. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Reload the current page, ignoring cached content
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_HELP_CENTER" desc="A text label that appears next to the keyboard shortcut that will open the Google Chrome Help Center in a new tab. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=50]">
+      <message name="IDS_KEYBOARD_SHORTCUT_HELP_CENTER" desc="A text label that appears next to the keyboard shortcut that will open the Google Chrome Help Center in a new tab. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open the Chrome Help Center in a new tab
       </message>
       <message name="IDS_KEYBOARD_SHORTCUT_TAB_GROUP_HEADER" desc="A text label that appears above a list of shortcuts that are related to the tab window. This group is part of several groups of keyboard shortcuts all shown in a dialog.">
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index f32de15e..dbe2eb09c 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -396,6 +396,15 @@
   "java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java",
   "java/src/org/chromium/chrome/browser/gsa/GSAState.java",
   "java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java",
+  "java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryActivity.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryAdapter.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryPage.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryItem.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryItemView.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryManager.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryManagerToolbar.java",
+  "java/src/org/chromium/chrome/browser/history/HistoryManagerUtils.java",
   "java/src/org/chromium/chrome/browser/historyreport/DeltaFileEntry.java",
   "java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java",
   "java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java",
@@ -1111,6 +1120,7 @@
   "java/src/org/chromium/chrome/browser/widget/incognitotoggle/IncognitoToggleButtonTablet.java",
   "java/src/org/chromium/chrome/browser/widget/selection/SelectableItemHighlightView.java",
   "java/src/org/chromium/chrome/browser/widget/selection/SelectableItemView.java",
+  "java/src/org/chromium/chrome/browser/widget/selection/SelectableItemViewHolder.java",
   "java/src/org/chromium/chrome/browser/widget/selection/SelectionDelegate.java",
   "java/src/org/chromium/chrome/browser/widget/selection/SelectableListLayout.java",
   "java/src/org/chromium/chrome/browser/widget/selection/SelectionToolbar.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index ececf5c3..8c04ebb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -302,7 +302,8 @@
         });
     }
 
-    public void triggerWebAppBanner(String url, String expectedTitle) throws Exception {
+    public void triggerWebAppBanner(String url, String expectedTitle, boolean installApp)
+            throws Exception {
         // Visit the site in a new tab.
         loadUrlInNewTab("about:blank");
         new TabLoadObserver(getActivity().getActivityTab()).fullyLoadUrl(url);
@@ -316,6 +317,11 @@
         });
         waitUntilNoInfoBarsExist();
 
+        // Add the animation listener in.
+        InfoBarContainer container = getActivity().getActivityTab().getInfoBarContainer();
+        final InfobarListener listener = new InfobarListener();
+        container.setAnimationListener(listener);
+
         // Indicate a day has passed, then revisit the page to show the banner.
         AppBannerManager.setTimeDeltaForTesting(1);
         new TabLoadObserver(getActivity().getActivityTab()).fullyLoadUrl(url);
@@ -327,6 +333,20 @@
             }
         });
         waitUntilAppBannerInfoBarAppears(expectedTitle);
+
+        if (!installApp) return;
+
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return listener.mDoneAnimating;
+            }
+        });
+
+        // Click the button to trigger the adding of the shortcut.
+        InfoBar infobar = container.getInfoBarsForTesting().get(0);
+        final Button button = (Button) infobar.getView().findViewById(R.id.button_primary);
+        TouchCommon.singleClickView(button);
     }
 
     @SmallTest
@@ -462,7 +482,7 @@
     @Feature({"AppBanners"})
     @RetryOnFailure
     public void testWebAppBannerAppears() throws Exception {
-        triggerWebAppBanner(mWebAppUrl, WEB_APP_TITLE);
+        triggerWebAppBanner(mWebAppUrl, WEB_APP_TITLE, false);
     }
 
     @SmallTest
@@ -471,7 +491,7 @@
     public void testBannerFallsBackToShortName() throws Exception {
         triggerWebAppBanner(WebappTestPage.urlOfPageWithServiceWorkerAndManifest(
                                     mTestServer, WEB_APP_SHORT_TITLE_MANIFEST),
-                WEB_APP_SHORT_TITLE);
+                WEB_APP_SHORT_TITLE, false);
     }
 
     @SmallTest
@@ -482,47 +502,7 @@
         final TestDataStorageFactory dataStorageFactory = new TestDataStorageFactory();
         WebappDataStorage.setFactoryForTests(dataStorageFactory);
 
-        // Visit the site in a new tab.
-        loadUrlInNewTab("about:blank");
-        new TabLoadObserver(getActivity().getActivityTab()).fullyLoadUrl(mWebAppUrl);
-
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                AppBannerManager manager = getActivity().getActivityTab().getAppBannerManager();
-                return !manager.isActiveForTesting();
-            }
-        });
-        waitUntilNoInfoBarsExist();
-
-        // Add the animation listener in.
-        InfoBarContainer container = getActivity().getActivityTab().getInfoBarContainer();
-        final InfobarListener listener = new InfobarListener();
-        container.setAnimationListener(listener);
-
-        // Indicate a day has passed, then revisit the page to show the banner.
-        AppBannerManager.setTimeDeltaForTesting(1);
-        new TabLoadObserver(getActivity().getActivityTab()).fullyLoadUrl(mWebAppUrl);
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                AppBannerManager manager = getActivity().getActivityTab().getAppBannerManager();
-                return !manager.isActiveForTesting();
-            }
-        });
-        waitUntilAppBannerInfoBarAppears(WEB_APP_TITLE);
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return listener.mDoneAnimating;
-            }
-        });
-
-        // Click the button to trigger the adding of the shortcut.
-        InfoBar infobar = container.getInfoBarsForTesting().get(0);
-        final Button button =
-                (Button) infobar.getView().findViewById(R.id.button_primary);
-        TouchCommon.singleClickView(button);
+        triggerWebAppBanner(mWebAppUrl, WEB_APP_TITLE, true);
 
         // Make sure that the splash screen icon was downloaded.
         CriteriaHelper.pollUiThread(new Criteria() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index f4dfdbe4..a108f0c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -1135,34 +1135,24 @@
     /**
      * Tests swiping the overlay open, after an initial long-press that activates the peeking card,
      * followed by closing the panel.
+     * This test also verifies that we don't create any {@link ContentViewCore} or load any URL
+     * until the panel is opened.
      */
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE)
     public void testLongPressSwipeExpand() throws InterruptedException, TimeoutException {
-        longPressNode("intelligence");
+        simulateLongPressSearch("search");
         assertNoContentViewCore();
-
-        // TODO(pedrosimonetti): Long press does not resolve so we shouldn't be faking one.
-        // Consider changing the fake server to create a fake response automatically,
-        // when one is requested by the Manager.
-
-        // Fake a search term resolution response.
-        fakeResponse(false, 200, "Intelligence", "United States Intelligence", "alternate-term",
-                false);
-        assertContainsParameters("Intelligence", "alternate-term");
-
-        waitForPanelToPeek();
         assertLoadedNoUrl();
-        assertNoContentViewCore();
-        flingPanelUp();
-        waitForPanelToExpand();
+
+        tapPeekingBarToExpandAndAssert();
         assertContentViewCoreCreated();
         assertLoadedNormalPriorityUrl();
         assertEquals(1, mFakeServer.getLoadedUrlCount());
 
         // tap the base page to close.
-        tapBasePageToClosePanel();
+        closePanel();
         assertEquals(1, mFakeServer.getLoadedUrlCount());
         assertNoContentViewCore();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
index e35f8b7..c3a5269e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java
@@ -9,7 +9,6 @@
 import android.content.pm.ResolveInfo;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.test.ChromeActivityTestCaseBase;
 
@@ -31,7 +30,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_NoResolveInfo() {
         String packageName = "";
         List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>();
@@ -40,7 +38,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_NoPathOrAuthority() {
         String packageName = "";
         ResolveInfo info = new ResolveInfo();
@@ -51,7 +48,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_WithPath() {
         String packageName = "";
         ResolveInfo info = new ResolveInfo();
@@ -63,7 +59,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_WithAuthority() {
         String packageName = "";
         ResolveInfo info = new ResolveInfo();
@@ -75,7 +70,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_WithTargetPackage_Matching() {
         String packageName = "com.android.chrome";
         ResolveInfo info = new ResolveInfo();
@@ -89,7 +83,6 @@
     }
 
     @SmallTest
-    @RetryOnFailure
     public void testIsPackageSpecializedHandler_WithTargetPackage_NotMatching() {
         String packageName = "com.android.chrome";
         ResolveInfo info = new ResolveInfo();
@@ -103,7 +96,21 @@
     }
 
     @SmallTest
-    @RetryOnFailure
+    public void testIsPackageSpecializeHandler_withEphemeralResolver() {
+        String packageName = "";
+        ResolveInfo info = new ResolveInfo();
+        info.filter = new IntentFilter();
+        info.filter.addDataPath("somepath", 2);
+        info.activityInfo = new ActivityInfo();
+        info.activityInfo.name = ExternalNavigationDelegateImpl.EPHEMERAL_INSTALLER_CLASS;
+        info.activityInfo.packageName = "com.google.android.gms";
+        List<ResolveInfo> resolveInfos = makeResolveInfos(info);
+        // Ephemeral resolver is not counted as a specialized handler.
+        assertEquals(0, ExternalNavigationDelegateImpl.getSpecializedHandlersWithFilter(
+                resolveInfos, packageName).size());
+    }
+
+    @SmallTest
     public void testIsDownload_noSystemDownloadManager() throws Exception {
         ExternalNavigationDelegateImpl delegate = new ExternalNavigationDelegateImpl(
                 getActivity().getActivityTab());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
index 59e5115..ca6efb9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
@@ -96,7 +96,6 @@
     private boolean mUseTestScheduler;
 
     private LongSparseArray<RequestMetadata> mRequestMetadata;
-    private OutputStreamWriter mLogOutput;
 
     public OfflinePageSavePageLaterEvaluationTest() {
         super(ChromeActivity.class);
@@ -138,6 +137,7 @@
         });
         checkTrue(mClearingSemaphore.tryAcquire(REMOVE_REQUESTS_TIMEOUT_MS, TimeUnit.MILLISECONDS),
                 "Timed out when clearing remaining requests!");
+        mBridge.closeLog();
         super.tearDown();
     }
 
@@ -178,32 +178,25 @@
                     for (String file : files) {
                         File currentFile = new File(externalArchiveDir.getPath(), file);
                         if (!currentFile.delete()) {
-                            logError(file + " cannot be deleted when clearing previous archives.");
+                            log(file + " cannot be deleted when clearing previous archives.");
                         }
                     }
                 }
-            } else if (!externalArchiveDir.mkdir()) {
-                logError("Cannot create directory on external storage to store saved pages.");
+            }
+            if (!externalArchiveDir.mkdir()) {
+                log("Cannot create directory on external storage to store saved pages.");
             }
         } catch (SecurityException e) {
-            logError("Failed to delete or create external archive folder!");
+            log("Failed to delete or create external archive folder!");
         }
         return externalArchiveDir;
     }
 
     /**
-     * Logs error in both console and output file.
+     * Print log message in output file through evaluation bridge.
      */
-    private void logError(String error) {
-        Log.e(TAG, error);
-        if (mLogOutput != null) {
-            try {
-                mLogOutput.write(error + NEW_LINE);
-                mLogOutput.flush();
-            } catch (Exception e) {
-                Log.e(TAG, e.getMessage(), e);
-            }
-        }
+    private void log(String message) {
+        mBridge.log(TAG, message);
     }
 
     /**
@@ -211,14 +204,7 @@
      */
     private void checkTrue(boolean condition, String message) {
         if (!condition) {
-            logError(message);
-            if (mLogOutput != null) {
-                try {
-                    mLogOutput.close();
-                } catch (IOException e) {
-                    Log.e(TAG, e.getMessage(), e);
-                }
-            }
+            log(message);
             fail();
         }
     }
@@ -237,7 +223,7 @@
                 Profile profile = Profile.getLastUsedProfile();
                 mBridge = OfflinePageEvaluationBridge.getForProfile(profile, useTestingScheduler);
                 if (mBridge == null) {
-                    logError("OfflinePageEvaluationBridge initialization failed!");
+                    fail("OfflinePageEvaluationBridge initialization failed!");
                     return;
                 }
                 if (mBridge.isOfflinePageModelLoaded()) {
@@ -264,16 +250,9 @@
      */
     protected void setUpIOAndBridge(final boolean useCustomScheduler) throws InterruptedException {
         try {
-            mLogOutput = getOutputStream(LOG_OUTPUT_FILE_PATH);
-        } catch (IOException e) {
-            Log.wtf(TAG, "Cannot set output file!");
-            Log.wtf(TAG, e.getMessage(), e);
-        }
-        try {
             getUrlListFromInputFile(INPUT_FILE_PATH);
         } catch (IOException e) {
-            Log.wtf(TAG, "Cannot read input file!");
-            Log.wtf(TAG, e.getMessage(), e);
+            Log.wtf(TAG, "Cannot read input file!", e);
         }
         checkTrue(mUrls != null, "URLs weren't loaded.");
         checkTrue(mUrls.size() > 0, "No valid URLs in the input file.");
@@ -307,6 +286,13 @@
             public void savePageRequestChanged(SavePageRequest request) {}
         };
         mBridge.addObserver(mObserver);
+        try {
+            File logOutputFile =
+                    new File(Environment.getExternalStorageDirectory(), LOG_OUTPUT_FILE_PATH);
+            mBridge.setLogOutputFile(logOutputFile);
+        } catch (IOException e) {
+            Log.wtf(TAG, "Cannot set log output file!", e);
+        }
     }
 
     /**
@@ -326,7 +312,7 @@
 
     private void processUrls(List<String> urls) throws InterruptedException, IOException {
         if (mBridge == null) {
-            logError("Test initialization error, aborting. No results would be written.");
+            fail("Test initialization error, aborting. No results would be written.");
             return;
         }
         for (String url : mUrls) {
@@ -428,7 +414,7 @@
         try {
             int failedCount = 0;
             if (mCount < mUrls.size()) {
-                logError("Test terminated before all requests completed.");
+                log("Test terminated before all requests completed.");
             }
             File externalArchiveDir = getExternalArchiveDir();
             for (int i = 0; i < mRequestMetadata.size(); i++) {
@@ -451,7 +437,7 @@
                 File originalPage = new File(page.getFilePath());
                 File externalPage = new File(externalArchiveDir, originalPage.getName());
                 if (!OfflinePageUtils.copyToShareableLocation(originalPage, externalPage)) {
-                    logError("Saved page for url " + page.getUrl() + " cannot be moved.");
+                    log("Saved page for url " + page.getUrl() + " cannot be moved.");
                 }
             }
             output.write(String.format(
@@ -464,9 +450,6 @@
             if (output != null) {
                 output.close();
             }
-            if (mLogOutput != null) {
-                mLogOutput.close();
-            }
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlInfoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlInfoTest.java
index 9a1f9938..65c7c6e2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlInfoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlInfoTest.java
@@ -22,13 +22,15 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mReferenceUrlInfo = new UrlInfo(URL, 99.5, 42);
-        mReferenceUrlInfo.setHasBeenDisplayed();
+        mReferenceUrlInfo = new UrlInfo(URL, 99.5, 42)
+                .setHasBeenDisplayed()
+                .setDeviceAddress("00:11:22:33:AA:BB");
         // Because we can't print JSON sorted by keys, the order is important here.
         mReferenceJsonObject = new JSONObject("{"
                 + "    \"url\": \"" + URL + "\","
                 + "    \"distance\": 99.5,"
                 + "    \"scan_timestamp\": 42,"
+                + "    \"device_address\": \"00:11:22:33:AA:BB\","
                 + "    \"has_been_displayed\": true"
                 + "}");
     }
@@ -44,6 +46,7 @@
         assertEquals(mReferenceUrlInfo.getUrl(), urlInfo.getUrl());
         assertEquals(mReferenceUrlInfo.getDistance(), urlInfo.getDistance());
         assertEquals(mReferenceUrlInfo.getScanTimestamp(), urlInfo.getScanTimestamp());
+        assertEquals(mReferenceUrlInfo.getDeviceAddress(), urlInfo.getDeviceAddress());
         assertEquals(mReferenceUrlInfo.hasBeenDisplayed(), urlInfo.hasBeenDisplayed());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java
index 39102a0..d192ed5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/physicalweb/UrlManagerTest.java
@@ -263,6 +263,30 @@
     }
 
     @SmallTest
+    public void testAddUrlUpdatesCache() throws Exception {
+        addEmptyPwsResult();
+        addEmptyPwsResult();
+
+        UrlInfo urlInfo = new UrlInfo(URL1);
+        mUrlManager.addUrl(urlInfo);
+        List<UrlInfo> urls = mUrlManager.getUrls(true);
+        assertEquals(1, urls.size());
+        assertEquals(urlInfo.getDistance(), urls.get(0).getDistance());
+        assertEquals(urlInfo.getDeviceAddress(), urls.get(0).getDeviceAddress());
+        assertEquals(urlInfo.getScanTimestamp(), urls.get(0).getScanTimestamp());
+
+        urlInfo = new UrlInfo(URL1)
+                .setDistance(100.0)
+                .setDeviceAddress("00:11:22:33:AA:BB");
+        mUrlManager.addUrl(urlInfo);
+        urls = mUrlManager.getUrls(true);
+        assertEquals(1, urls.size());
+        assertEquals(urlInfo.getDistance(), urls.get(0).getDistance());
+        assertEquals(urlInfo.getDeviceAddress(), urls.get(0).getDeviceAddress());
+        assertEquals(urlInfo.getScanTimestamp(), urls.get(0).getScanTimestamp());
+    }
+
+    @SmallTest
     @RetryOnFailure
     public void testAddUrlTwiceWorks() throws Exception {
         // Add and remove an old URL twice and add new URL twice before removing.
@@ -409,6 +433,31 @@
     }
 
     @SmallTest
+    public void testSerializationWorksWithPoorlySerializedResult() throws Exception {
+        addPwsResult1();
+        addPwsResult2();
+        long curTime = System.currentTimeMillis();
+        mUrlManager.addUrl(new UrlInfo(URL1, 99.5, curTime + 42));
+        mUrlManager.addUrl(new UrlInfo(URL2, 100.5, curTime + 43));
+        getInstrumentation().waitForIdleSync();
+
+        // Create an invalid serialization.
+        Set<String> serializedUrls = new HashSet<>();
+        serializedUrls.add(new UrlInfo(URL1, 99.5, curTime + 42).jsonSerialize().toString());
+        serializedUrls.add("{\"not_a_value\": \"This is totally not a serialized UrlInfo.\"}");
+        mSharedPreferences.edit()
+                .putStringSet("physicalweb_all_urls", serializedUrls)
+                .apply();
+
+        // Make sure only the properly serialized URL is restored.
+        Context context = getInstrumentation().getTargetContext().getApplicationContext();
+        UrlManager urlManager = new UrlManager(context);
+        List<UrlInfo> urlInfos = urlManager.getUrls();
+        assertEquals(1, urlInfos.size());
+        assertEquals(URL1, urlInfos.get(0).getUrl());
+    }
+
+    @SmallTest
     @RetryOnFailure
     public void testSerializationWorksWithoutGarbageCollection() throws Exception {
         addPwsResult1();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
index 37b4647..7ff39746 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
@@ -58,11 +58,14 @@
 
     /**
      * Confirm that after a successive refresh of a failed tab that failed to load, change the
-     * button from "Reload" to "Send Feedback".
+     * button from "Reload" to "Send Feedback". If reloaded a third time and it is successful it
+     * reverts from "Send Feedback" to "Reload".
+     * @throws InterruptedException
+     * @throws IllegalArgumentException
      */
     @SmallTest
     @Feature({"SadTab"})
-    public void testChangeSadButtonToFeedbackAfterFailedRefresh() {
+    public void testSadTabPageButtonText() throws IllegalArgumentException, InterruptedException {
         final Tab tab = getActivity().getActivityTab();
 
         assertFalse(tab.isShowingSadTab());
@@ -79,29 +82,13 @@
                 "Expected the sad tab button to have the feedback label after the tab button "
                 + "crashes twice in a row.",
                 getActivity().getString(R.string.sad_tab_send_feedback_label), actualText);
-    }
-
-    /**
-     * Confirm after two failures, if we refresh a third time and it's successful, and then we
-     * crash again, we do not show the "Send Feedback" button and instead show the "Reload" tab.
-     */
-    @SmallTest
-    @Feature({"SadTab"})
-    public void testSadButtonRevertsBackToReloadAfterSuccessfulLoad() {
-        final Tab tab = getActivity().getActivityTab();
-
+        loadUrl("about:blank");
+        assertFalse("Expected about:blank to destroy the sad tab however the sad tab is still in "
+                + "view", tab.isShowingSadTab());
         simulateRendererKilled(tab, true);
-        reloadSadTab(tab);
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                tab.reload(); // Erases the sad tab page
-                tab.didFinishPageLoad(); // Resets the tab counter to 0
-            }
-        });
-        simulateRendererKilled(tab, true);
-        String actualText = getSadTabButton(tab).getText().toString();
-        assertEquals(getActivity().getString(R.string.sad_tab_reload_label), actualText);
+        actualText = getSadTabButton(tab).getText().toString();
+        assertEquals("Expected the sad tab button to have the reload label after a successful load",
+                getActivity().getString(R.string.sad_tab_reload_label), actualText);
     }
 
     /**
diff --git a/chrome/app/address_input_strings.grd b/chrome/app/address_input_strings.grd
index cc61b6c2..cc86c82 100644
--- a/chrome/app/address_input_strings.grd
+++ b/chrome/app/address_input_strings.grd
@@ -35,13 +35,7 @@
     <output filename="address_input_strings_en-GB.pak" type="data_package" lang="en-GB" />
     <output filename="address_input_strings_en-US.pak" type="data_package" lang="en" />
     <output filename="address_input_strings_es.pak" type="data_package" lang="es" />
-    <if expr="is_ios">
-      <!-- iOS uses es-MX for es-419 -->
-      <output filename="address_input_strings_es-MX.pak" type="data_package" lang="es-419" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="address_input_strings_es-419.pak" type="data_package" lang="es-419" />
-    </if>
+    <output filename="address_input_strings_es-419.pak" type="data_package" lang="es-419" />
     <output filename="address_input_strings_et.pak" type="data_package" lang="et" />
     <output filename="address_input_strings_fa.pak" type="data_package" lang="fa" />
     <output filename="address_input_strings_fake-bidi.pak" type="data_package" lang="fake-bidi" />
@@ -68,13 +62,7 @@
          be 'nb'. -->
     <output filename="address_input_strings_nb.pak" type="data_package" lang="no" />
     <output filename="address_input_strings_pl.pak" type="data_package" lang="pl" />
-    <if expr="is_ios">
-      <!-- iOS uses pt for pt-BR -->
-      <output filename="address_input_strings_pt.pak" type="data_package" lang="pt-BR" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="address_input_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
-    </if>
+    <output filename="address_input_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
     <output filename="address_input_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
     <output filename="address_input_strings_ro.pak" type="data_package" lang="ro" />
     <output filename="address_input_strings_ru.pak" type="data_package" lang="ru" />
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index f8616ea..2099993 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -59,6 +59,8 @@
 constexpr char kThirdPartyModulesLoaded[] = "third-party-modules-loaded";
 constexpr char kThirdPartyModulesNotLoaded[] = "third-party-modules-not-loaded";
 
+constexpr char kEnrolledToDomain[] = "enrolled-to-domain";
+
 constexpr char kViewCount[] = "view-count";
 constexpr char kZeroEncodeDetails[] = "zero-encode-details";
 
@@ -104,6 +106,7 @@
       // browser/:
       {kThirdPartyModulesLoaded, kSmallSize},
       {kThirdPartyModulesNotLoaded, kSmallSize},
+      {kEnrolledToDomain, kSmallSize},
 
       // content/:
       {"bad_message_reason", kSmallSize},
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index eafeba7..da97192 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -387,10 +387,12 @@
     // In tests, just respect the flag if given.
     base::FilePath user_data_dir =
         command_line->GetSwitchValuePath(switches::kUserDataDir);
-    if (user_data_dir.EndsWithSeparator())
-      user_data_dir = user_data_dir.StripTrailingSeparators();
-    CHECK(PathService::OverrideAndCreateIfNeeded(chrome::DIR_USER_DATA,
-                                                 user_data_dir, false, true));
+    if (!user_data_dir.empty()) {
+      if (user_data_dir.EndsWithSeparator())
+        user_data_dir = user_data_dir.StripTrailingSeparators();
+      CHECK(PathService::OverrideAndCreateIfNeeded(chrome::DIR_USER_DATA,
+                                                   user_data_dir, false, true));
+    }
   }
 #else  // OS_WIN
   base::FilePath user_data_dir =
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 15e26a80..867ce33 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -20,13 +20,7 @@
     <output filename="chromium_strings_en-GB.pak" type="data_package" lang="en-GB" />
     <output filename="chromium_strings_en-US.pak" type="data_package" lang="en" />
     <output filename="chromium_strings_es.pak" type="data_package" lang="es" />
-    <if expr="is_ios">
-      <!-- iOS uses es-MX for es-419 -->
-      <output filename="chromium_strings_es-MX.pak" type="data_package" lang="es-419" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="chromium_strings_es-419.pak" type="data_package" lang="es-419" />
-    </if>
+    <output filename="chromium_strings_es-419.pak" type="data_package" lang="es-419" />
     <output filename="chromium_strings_et.pak" type="data_package" lang="et" />
     <output filename="chromium_strings_fa.pak" type="data_package" lang="fa" />
     <output filename="chromium_strings_fake-bidi.pak" type="data_package" lang="fake-bidi" />
@@ -52,13 +46,7 @@
     <output filename="chromium_strings_nb.pak" type="data_package" lang="no" />
     <!-- 'no' for Norwegian Bokmål. It should be 'nb'. -->
     <output filename="chromium_strings_pl.pak" type="data_package" lang="pl" />
-    <if expr="is_ios">
-      <!-- iOS uses pt for pt-BR -->
-      <output filename="chromium_strings_pt.pak" type="data_package" lang="pt-BR" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="chromium_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
-    </if>
+    <output filename="chromium_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
     <output filename="chromium_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
     <output filename="chromium_strings_ro.pak" type="data_package" lang="ro" />
     <output filename="chromium_strings_ru.pak" type="data_package" lang="ru" />
@@ -134,7 +122,7 @@
   <release seq="1" allow_pseudo="false">
     <messages fallback_to_english="true">
       <!-- Settings specific strings -->
-      <if expr="not is_android and not is_ios">
+      <if expr="not is_android">
         <part file="settings_chromium_strings.grdp" />
       </if>
 
@@ -307,13 +295,7 @@
           Get Started with Chromium OS
         </message>
       </if>
-      <if expr="is_ios">
-        <message name="IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE"
-                 desc="Title for the hard-coded thumbnail that represents the Google Chrome Welcome page.  This is used on the NTP when there aren't enough thumbnails to show. [Length: 14em]">
-          Welcome
-        </message>
-      </if>
-      <if expr="not chromeos and not is_ios">
+      <if expr="not chromeos">
         <message name="IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE"
                  desc="Title for the hard-coded thumbnail that represents the Google Chrome Welcome page.  This is used on the NTP when there aren't enough thumbnails to show.">
           Welcome to Chromium
@@ -974,7 +956,7 @@
       </if>
 
       <!-- Material Design User Manager -->
-      <if expr="not is_android and not is_ios and not chromeos">
+      <if expr="not is_android and not chromeos">
         <!-- Supervised user create confirmation page -->
         <message name="IDS_SUPERVISED_USER_CREATED_TEXT" desc="Informative text for the confirmation dialog that appears after a supervised user has been created.">
           To set which websites <ph name="NEW_PROFILE_NAME">$1<ex>New User</ex></ph> can view, you can configure restrictions and settings by visiting <ph name="BEGIN_LINK_1">&lt;a target="_blank" href="$3"&gt;</ph><ph name="DISPLAY_LINK">$4</ph><ph name="END_LINK_1">&lt;/a&gt;</ph>. If you do not change the default settings, <ph name="NEW_PROFILE_NAME">$1<ex>New User</ex></ph> can browse everything on the web.
@@ -1279,7 +1261,7 @@
       </if>
 
       <!-- Welcome page (chrome://welcome) strings -->
-      <if expr="not chromeos and not is_android and not is_ios">
+      <if expr="not chromeos and not is_android">
         <message name="IDS_WELCOME_HEADER" desc="A message which will appear as the header on the Welcome UI if the user has never run Chromium before.">
           Welcome to Chromium
         </message>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 1862fa59..fd99536 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -25,13 +25,7 @@
     <output filename="generated_resources_en-GB.pak" type="data_package" lang="en-GB" />
     <output filename="generated_resources_en-US.pak" type="data_package" lang="en" />
     <output filename="generated_resources_es.pak" type="data_package" lang="es" />
-    <if expr="is_ios">
-      <!-- iOS uses es-MX for es-419 -->
-      <output filename="generated_resources_es-MX.pak" type="data_package" lang="es-419" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="generated_resources_es-419.pak" type="data_package" lang="es-419" />
-    </if>
+    <output filename="generated_resources_es-419.pak" type="data_package" lang="es-419" />
     <output filename="generated_resources_et.pak" type="data_package" lang="et" />
     <output filename="generated_resources_fa.pak" type="data_package" lang="fa" />
     <output filename="generated_resources_fake-bidi.pak" type="data_package" lang="fake-bidi" />
@@ -58,13 +52,7 @@
          be 'nb'. -->
     <output filename="generated_resources_nb.pak" type="data_package" lang="no" />
     <output filename="generated_resources_pl.pak" type="data_package" lang="pl" />
-    <if expr="is_ios">
-      <!-- iOS uses pt for pt-BR -->
-      <output filename="generated_resources_pt.pak" type="data_package" lang="pt-BR" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="generated_resources_pt-BR.pak" type="data_package" lang="pt-BR" />
-    </if>
+    <output filename="generated_resources_pt-BR.pak" type="data_package" lang="pt-BR" />
     <output filename="generated_resources_pt-PT.pak" type="data_package" lang="pt-PT" />
     <output filename="generated_resources_ro.pak" type="data_package" lang="ro" />
     <output filename="generated_resources_ru.pak" type="data_package" lang="ru" />
@@ -202,7 +190,7 @@
       </if>
 
       <!-- Settings specific strings -->
-      <if expr="not is_android and not is_ios">
+      <if expr="not is_android">
         <part file="settings_strings.grdp" />
       </if>
 
@@ -6026,6 +6014,12 @@
       <message name="IDS_FLAGS_GPU_RASTERIZATION_MSAA_SAMPLE_COUNT_SIXTEEN" desc="">
         16
       </message>
+      <message name="IDS_FLAGS_ENABLE_SLIMMING_PAINT_INVALIDATION_NAME" desc="Title for about:flags option to enable slimming paint invalidation.">
+        Enable slimming paint invalidation.
+      </message>
+      <message name="IDS_FLAGS_ENABLE_SLIMMING_PAINT_INVALIDATION_DESCRIPTION" desc="Description of about:flags option for enabling slimming paint invalidation.">
+        Enable a new paint invalidation system.
+      </message>
       <message name="IDS_FLAGS_EXPERIMENTAL_SECURITY_FEATURES_NAME" desc="Name for the flag to enable experimental security features.">
         Potentially annoying security features
       </message>
@@ -6306,14 +6300,12 @@
       <message name="IDS_FLAGS_PUSH_API_BACKGROUND_MODE_DESCRIPTION" desc="Description for the flag to enable background mode for the Push API.">
         Enable background mode for the Push API. This allows Chrome to continue running after the last window is closed, and to launch at OS startup, if the Push API needs it.
       </message>
-      <if expr="not is_ios">
-        <message name="IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_NAME" desc="Name of the flag to enable the &quot;stale-while-revalidate&quot; cache directive. This message is intended for web developers who are familiar with technical terms. &quot;stale-while-revalidate&quot; here is the literal string sent by the HTTP server, and so should be untranslated where possible. &quot;cache&quot; here refers to an HTTP cache. &quot;directive&quot; here is an instruction sent by an HTTP server to indicate what caching behaviour should be used.">
-          Enable the "stale-while-revalidate" cache directive
-        </message>
-        <message name="IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_DESCRIPTION" desc="Description of the flag to enable the &quot;stale-while-revalidate&quot; cache directive. This message is intended for web developers who are familiar with technical terms. &quot;Cache-Control: stale-while-revalidate&quot; is the literal string sent by the HTTP server, and so should be untranslated where possible. If necessary for translation it can be split into the HTTP header name part &quot;Cache-Control&quot; and the value part &quot;stale-while-revalidate&quot;. &quot;directive&quot; here is an instruction sent by an HTTP server to indicate what caching behaviour should be used. The directive is typically configured by the server administrator. &quot;servers&quot; here refers to HTTP server software. &quot;resources&quot; here means individual files served by HTTP servers and cached by the browser. &quot;be revalidated&quot; means &quot;be checked with the HTTP server whether or not the browser has the latest version&quot;. &quot;in the background&quot; here means without making the user wait. &quot;latency&quot; here refers to the time the user waits for a web page to be loaded or become usable.">
-          Enable the experimental implementation of the "Cache-Control: stale-while-revalidate" directive. This permits servers to specify that some resources may be revalidated in the background to improve latency.
-        </message>
-      </if>
+      <message name="IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_NAME" desc="Name of the flag to enable the &quot;stale-while-revalidate&quot; cache directive. This message is intended for web developers who are familiar with technical terms. &quot;stale-while-revalidate&quot; here is the literal string sent by the HTTP server, and so should be untranslated where possible. &quot;cache&quot; here refers to an HTTP cache. &quot;directive&quot; here is an instruction sent by an HTTP server to indicate what caching behaviour should be used.">
+        Enable the "stale-while-revalidate" cache directive
+      </message>
+      <message name="IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_DESCRIPTION" desc="Description of the flag to enable the &quot;stale-while-revalidate&quot; cache directive. This message is intended for web developers who are familiar with technical terms. &quot;Cache-Control: stale-while-revalidate&quot; is the literal string sent by the HTTP server, and so should be untranslated where possible. If necessary for translation it can be split into the HTTP header name part &quot;Cache-Control&quot; and the value part &quot;stale-while-revalidate&quot;. &quot;directive&quot; here is an instruction sent by an HTTP server to indicate what caching behaviour should be used. The directive is typically configured by the server administrator. &quot;servers&quot; here refers to HTTP server software. &quot;resources&quot; here means individual files served by HTTP servers and cached by the browser. &quot;be revalidated&quot; means &quot;be checked with the HTTP server whether or not the browser has the latest version&quot;. &quot;in the background&quot; here means without making the user wait. &quot;latency&quot; here refers to the time the user waits for a web page to be loaded or become usable.">
+        Enable the experimental implementation of the "Cache-Control: stale-while-revalidate" directive. This permits servers to specify that some resources may be revalidated in the background to improve latency.
+      </message>
       <message name="IDS_FLAGS_ENABLE_NAVIGATION_TRACING" desc="Name of the flag to enable navigation tracing">
         Enable navigation tracing
       </message>
@@ -6668,12 +6660,6 @@
       <message name="IDS_FLAGS_VIEWS_RECT_BASED_TARGETING_DESCRIPTION" desc="Description of about:flags option for rect-based targeting in views">
         Rect-based targeting uses a heuristic to determine the most probable target of a gesture, where the touch region is represented by a rectangle.
       </message>
-      <message name="IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_NAME" desc="Name of the flag to enable show-on-first-paint for apps.">
-        Show-on-first-paint for apps
-      </message>
-      <message name="IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION" desc="Description of flag to enable show-on-first-paint for apps.">
-        Show apps windows after the first paint. Windows will be shown significantly later for heavy apps loading resources synchronously but it will be insignificant for apps that load most of their resources asynchronously.
-      </message>
       <message name="IDS_FLAGS_PERMISSION_ACTION_REPORTING_NAME" desc="Title for the flag to enable permission action reporting to safe browsing servers.">
         Permission Action Reporting
       </message>
@@ -9183,7 +9169,7 @@
       <message name="IDS_MALWARE_V3_HEADING" desc="The large heading at the top of the malware interstitial.">
         The site ahead contains malware
       </message>
-      <if expr="is_android or is_ios">
+      <if expr="is_android">
         <message name="IDS_MALWARE_V3_PRIMARY_PARAGRAPH" desc="Mobile: The primary explanatory paragraph for the malware interstitial.">
           Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous apps on your device that steal or delete your information (for example, photos, passwords, messages, and credit cards).
         </message>
@@ -9193,7 +9179,7 @@
           Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous programs on your Mac that steal or delete your information (for example, photos, passwords, messages, and credit cards).
         </message>
       </if>
-      <if expr="not is_android and not is_macosx and not is_ios">
+      <if expr="not is_android and not is_macosx">
         <message name="IDS_MALWARE_V3_PRIMARY_PARAGRAPH" desc="The primary explanatory paragraph for the malware interstitial.">
           Attackers currently on <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="SITE">$1<ex>example.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> might attempt to install dangerous programs on your computer that steal or delete your information (for example, photos, passwords, messages, and credit cards).
         </message>
@@ -9899,12 +9885,12 @@
           This language cannot be translated
         </message>
 
-        <if expr="is_android or is_ios">
+        <if expr="is_android">
           <message name="IDS_OPTIONS_SYSTEM_PROXIES_LABEL" desc="Mobile: The info label for the 'Proxy settings' button when managed by the system.">
             <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is using your device's system proxy settings to connect to the network.
           </message>
         </if>
-        <if expr="not is_android and not is_ios">
+        <if expr="not is_android">
           <message name="IDS_OPTIONS_SYSTEM_PROXIES_LABEL" desc="The info label for the 'Proxy settings' button when managed by the system.">
             <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is using your computer's system proxy settings to connect to the network.
           </message>
@@ -10392,19 +10378,6 @@
         </message>
       </if>
 
-      <!-- Auto-login infobar -->
-      <if expr="is_ios">
-        <message name="IDS_AUTOLOGIN_INFOBAR_MESSAGE" desc="The string shown in the infobar explaining that they can press one button to auto-login instead of entering their user name and password. [length: 60em]">
-          Automatic sign-in is available <ph name="EMAIL_ADDRESS">$1</ph>
-        </message>
-        <message name="IDS_AUTOLOGIN_INFOBAR_OK_BUTTON" desc="The string used in the infobar button that the user presses to perform the auto-login. [length: 10em]">
-          Sign in
-        </message>
-        <message name="IDS_AUTOLOGIN_INFOBAR_CANCEL_BUTTON" desc="Mobile: The string used in the autologin infobar button that the user presses to close the infobar without logging in.">
-          Cancel
-        </message>
-      </if>
-
       <message name="IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON" desc="The string used in the dialog for the button that means 'create a new profile'.">
         Create a new user
       </message>
@@ -12155,56 +12128,54 @@
       </if>
 
       <!-- Web and message center notifications -->
-      <if expr="enable_notifications">
-        <message name="IDS_NOTIFICATION_PERMISSIONS" desc="Text requesting permission for Web Notifications.">
-          <ph name="site">$1<ex>mail.google.com</ex></ph> wants to send you notifications.
-        </message>
-        <message name="IDS_NOTIFICATION_PERMISSIONS_FRAGMENT" desc="Permission sentence fragment to show following the prompt 'This site wants to:' in a permissions request">
-          Show notifications
-        </message>
-        <message name="IDS_NOTIFICATION_PERMISSION_YES" desc="The label of the 'allow' button on the notification permission infobar.">
-          Allow
-        </message>
-        <message name="IDS_NOTIFICATION_PERMISSION_NO" desc="The label of the 'deny' button on the notification permission infobar.">
-          Deny
-        </message>
-        <message name="IDS_NOTIFICATION_WELCOME_BODY" desc="Notification body for the Welcome Notification">
-          Stay connected to what you need to know, across all devices.
-        </message>
-        <message name="IDS_NOTIFICATION_WELCOME_BUTTON_LEARN_MORE" desc="Learn more button text for the Welcome Notification">
-          Learn more
-        </message>
-        <message name="IDS_NOTIFICATION_WELCOME_DISPLAY_SOURCE" desc="Display source for the Welcome Notification">
-          Notifications
-        </message>
-        <message name="IDS_NOTIFICATION_WELCOME_TITLE" desc="Notification title for the Welcome Notification">
-          Google Now for Chrome!
-        </message>
-        <message name="IDS_NOTIFICATION_BUTTON_SETTINGS" desc="Short button label to go to the notification settings panel">
-          Settings
-        </message>
-        <message name="IDS_NOTIFICATION_BUTTON_CLOSE" desc="Button label to close a notification">
-          Close
-        </message>
-        <message name="IDS_NOTIFICATION_BUTTON_OPTIONS" desc="Button label to display more options like extra buttons or going to the site settings page.">
-          Options
-        </message>
-        <message name="IDS_NOTIFICATION_SETTINGS" desc="Button label to go to the notification settings panel">
-          Notification settings
-        </message>
-        <message name="IDS_NOTIFIER_WELCOME_BUTTON" desc="Notification body for when a new notifier service is introduced.">
-          Turn off these notifications
-        </message>
-        <message name="IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_NAME" desc="Name of the flag to enable custom layouts for Web Notifications.">
-          Enable custom layouts for Web Notifications.
-        </message>
-        <message name="IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_DESCRIPTION" desc="Description for the flag to enable custom layouts for Web Notifications.">
-          Enable custom layouts for Web Notifications. They will have subtle layout improvements that are otherwise not possible.
-        </message>
-        <message name="IDS_NOTIFICATION_REPLY_PLACEHOLDER" desc="Placeholder text shown in the text box before any text is entered when replying directly to a notification.">
-          Send message
-        </message>
-      </if>
+      <message name="IDS_NOTIFICATION_PERMISSIONS" desc="Text requesting permission for Web Notifications.">
+        <ph name="site">$1<ex>mail.google.com</ex></ph> wants to send you notifications.
+      </message>
+      <message name="IDS_NOTIFICATION_PERMISSIONS_FRAGMENT" desc="Permission sentence fragment to show following the prompt 'This site wants to:' in a permissions request">
+        Show notifications
+      </message>
+      <message name="IDS_NOTIFICATION_PERMISSION_YES" desc="The label of the 'allow' button on the notification permission infobar.">
+        Allow
+      </message>
+      <message name="IDS_NOTIFICATION_PERMISSION_NO" desc="The label of the 'deny' button on the notification permission infobar.">
+        Deny
+      </message>
+      <message name="IDS_NOTIFICATION_WELCOME_BODY" desc="Notification body for the Welcome Notification">
+        Stay connected to what you need to know, across all devices.
+      </message>
+      <message name="IDS_NOTIFICATION_WELCOME_BUTTON_LEARN_MORE" desc="Learn more button text for the Welcome Notification">
+        Learn more
+      </message>
+      <message name="IDS_NOTIFICATION_WELCOME_DISPLAY_SOURCE" desc="Display source for the Welcome Notification">
+        Notifications
+      </message>
+      <message name="IDS_NOTIFICATION_WELCOME_TITLE" desc="Notification title for the Welcome Notification">
+        Google Now for Chrome!
+      </message>
+      <message name="IDS_NOTIFICATION_BUTTON_SETTINGS" desc="Short button label to go to the notification settings panel">
+        Settings
+      </message>
+      <message name="IDS_NOTIFICATION_BUTTON_CLOSE" desc="Button label to close a notification">
+        Close
+      </message>
+      <message name="IDS_NOTIFICATION_BUTTON_OPTIONS" desc="Button label to display more options like extra buttons or going to the site settings page.">
+        Options
+      </message>
+      <message name="IDS_NOTIFICATION_SETTINGS" desc="Button label to go to the notification settings panel">
+        Notification settings
+      </message>
+      <message name="IDS_NOTIFIER_WELCOME_BUTTON" desc="Notification body for when a new notifier service is introduced.">
+        Turn off these notifications
+      </message>
+      <message name="IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_NAME" desc="Name of the flag to enable custom layouts for Web Notifications.">
+        Enable custom layouts for Web Notifications.
+      </message>
+      <message name="IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_DESCRIPTION" desc="Description for the flag to enable custom layouts for Web Notifications.">
+        Enable custom layouts for Web Notifications. They will have subtle layout improvements that are otherwise not possible.
+      </message>
+      <message name="IDS_NOTIFICATION_REPLY_PLACEHOLDER" desc="Placeholder text shown in the text box before any text is entered when replying directly to a notification.">
+        Send message
+      </message>
 
       <!-- Mac AppleScript -->
       <if expr="is_macosx">
@@ -12248,7 +12219,7 @@
 
 
       <!-- Mac Menubar Menus -->
-      <if expr="is_macosx or is_ios">
+      <if expr="is_macosx">
         <!-- Menubar Menu Titles -->
         <!-- NOTE: Some of these exist in context menus with Title Case support, but we use different IDs in case we need slightly different strings in some language due to the different context. -->
         <message name="IDS_FILE_MENU_MAC" desc="The menu title of the Mac file menu.">
@@ -12513,7 +12484,7 @@
       </if> <!-- is_macosx -->
 
       <!-- Linux Global Menubar Menus -->
-      <if expr="is_posix and not is_macosx and not is_ios">
+      <if expr="is_posix and not is_macosx">
         <!-- Unity and some other GNOME configurations now have a permanent,
              Mac-like menu bar that lives outside the window at the top of the
              screen. The following strings should be in Linux style, not Mac
@@ -12892,7 +12863,7 @@
         </message>
       </if>
 
-      <if expr="is_posix and not is_macosx and not is_ios">
+      <if expr="is_posix and not is_macosx">
         <!-- Linux proxy configuration fallback help -->
         <message name="IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE" desc="Title of HTML page shown on systems where system proxy configuration is unsupported.">
           Proxy Configuration Help
@@ -13334,7 +13305,7 @@
         </message>
       </if>
 
-      <if expr="not is_android and not is_ios">
+      <if expr="not is_android">
         <!-- User manager web UI -->
         <if expr="use_titlecase">
           <message name="IDS_ADD_USER_BUTTON" desc="Text shown on an add user button on login/locker screen">
@@ -13989,16 +13960,6 @@
         </message>
       </if>
 
-      <!-- iOS Chrome to Device strings-->
-      <if expr="is_ios">
-        <message name="IDS_CHROME_TO_DEVICE_PRINT_TO_PHONE" desc="The message indicating print-to-phone jobs are now available. [Length: 30em]">
-          Your document is ready to view.
-        </message>
-        <message name="IDS_CHROME_TO_DEVICE_SNAPSHOTS" desc="The message indicating snapshot jobs are now available. [Length: 30em]">
-          Your page is available to view.
-        </message>
-      </if>
-
       <!-- Pepper 3D and WebGL (client 3D APIs) infobar strings -->
       <message name="IDS_3D_APIS_BLOCKED_TEXT" desc="Infobar message when 3D graphics APIs are blocked because the graphics processor was reset recently. NOTE that 'rats' here is being used like 'darn'.">
         Rats! <ph name="API_NAME">$1<ex>WebGL</ex></ph> hit a snag.
@@ -14420,14 +14381,12 @@
       <message name="IDS_PUSH_MESSSAGING_BLOCK_RADIO" desc="A radio button in Content Settings dialog to deny a site to send push messages.">
         Do not allow any sites to send push messages
       </message>
-      <if expr="enable_notifications">
-        <message name="IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY" desc="Body text of an auto-generated notification informing the user that the site may have received an update in the background, in case the site did not satisfy the visible notification requirement itself.">
-          This site has been updated in the background.
-        </message>
-        <message name="IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME" desc="The name of the Notifications service that can run in the background">
-          Notifications
-        </message>
-      </if>
+      <message name="IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY" desc="Body text of an auto-generated notification informing the user that the site may have received an update in the background, in case the site did not satisfy the visible notification requirement itself.">
+        This site has been updated in the background.
+      </message>
+      <message name="IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME" desc="The name of the Notifications service that can run in the background">
+        Notifications
+      </message>
 
       <!-- Easy Unlock strings -->
       <!-- Strings for the Easy Unlock promo notification -->
@@ -15046,33 +15005,31 @@
       </message>
     </if>
 
-    <if expr="not is_ios">
-      <!-- WebUsb Notification -->
-      <message name="IDS_WEBUSB_DEVICE_DETECTED_NOTIFICATION" desc="Content for notification shown to the user when a USB device gets plugged in.">
-        Go to <ph name="LANDING_PAGE">$1<ex>www.google.com</ex></ph> to connect.
-      </message>
-      <message name="IDS_WEBUSB_DEVICE_DETECTED_NOTIFICATION_TITLE" desc="Title for notification shown to the user when a USB device gets plugged in.">
-        <ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph> detected
+    <!-- WebUsb Notification -->
+    <message name="IDS_WEBUSB_DEVICE_DETECTED_NOTIFICATION" desc="Content for notification shown to the user when a USB device gets plugged in.">
+      Go to <ph name="LANDING_PAGE">$1<ex>www.google.com</ex></ph> to connect.
+    </message>
+    <message name="IDS_WEBUSB_DEVICE_DETECTED_NOTIFICATION_TITLE" desc="Title for notification shown to the user when a USB device gets plugged in.">
+      <ph name="USB_DEVICE_NAME">$1<ex>Nexus 5</ex></ph> detected
+    </message>
+
+    <!-- Audio device strings. -->
+    <message name="IDS_DEFAULT_AUDIO_DEVICE_NAME" desc="System default audio device (microphone or loudspeaker). This is typically presented to users in a drop-down list of device choices.">
+        Default
+    </message>
+    <if expr="is_win">
+      <message name="IDS_COMMUNICATIONS_AUDIO_DEVICE_NAME" desc="System default communications audio device (microphone or loudspeaker). This is typically presented to users in a drop-down list of device choices.">
+        Communications
       </message>
     </if>
-
-      <!-- Audio device strings. -->
-      <message name="IDS_DEFAULT_AUDIO_DEVICE_NAME" desc="System default audio device (microphone or loudspeaker). This is typically presented to users in a drop-down list of device choices.">
-          Default
+    <if expr="chromeos">
+      <message name="IDS_BEAMFORMING_ON_DEFAULT_AUDIO_INPUT_DEVICE_NAME" desc="Default audio input device (microphone) with a narrow angle coverage. Only the user in front of the device (i.e. you) will be listened to. This is typically presented to users in a drop-down list of device choices.">
+        Default (pick up just you)
       </message>
-      <if expr="is_win">
-        <message name="IDS_COMMUNICATIONS_AUDIO_DEVICE_NAME" desc="System default communications audio device (microphone or loudspeaker). This is typically presented to users in a drop-down list of device choices.">
-          Communications
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_BEAMFORMING_ON_DEFAULT_AUDIO_INPUT_DEVICE_NAME" desc="Default audio input device (microphone) with a narrow angle coverage. Only the user in front of the device (i.e. you) will be listened to. This is typically presented to users in a drop-down list of device choices.">
-          Default (pick up just you)
-        </message>
-        <message name="IDS_BEAMFORMING_OFF_DEFAULT_AUDIO_INPUT_DEVICE_NAME" desc="Default audio input device (microphone) with a wide angle coverage. All the sounds in the vicinity will be listend to. This is typically presented to users in a drop-down list of device choices.">
-          Default (pick up everything)
-        </message>
-      </if>
+      <message name="IDS_BEAMFORMING_OFF_DEFAULT_AUDIO_INPUT_DEVICE_NAME" desc="Default audio input device (microphone) with a wide angle coverage. All the sounds in the vicinity will be listend to. This is typically presented to users in a drop-down list of device choices.">
+        Default (pick up everything)
+      </message>
+    </if>
 
     <if expr="is_android">
       <!-- Data Use -->
@@ -15143,7 +15100,7 @@
       </message>
     </if>
 
-    <if expr="not is_android and not is_ios">
+    <if expr="not is_android">
       <!-- Device Chooser Prompt -->
       <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a website.">
         <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to pair
@@ -15394,7 +15351,7 @@
       </message>
     </if>
 
-    <if expr="not is_android and not is_ios and _google_chrome">
+    <if expr="not is_android and _google_chrome">
       <message name="IDS_FLAGS_GOOGLE_BRANDED_CONTEXT_MENU_NAME" desc="Name for the flag to enable Google branding in the context menu.">
         Google branding in the context menu
       </message>
@@ -15723,6 +15680,20 @@
     <message name="IDS_FLAGS_MEDIA_REMOTING_ENCRYPTED_DESCRIPTION" desc="Desciption for the flag to enable Media Remoting of encrypted content" translateable="false">
       When Media Remoting is enabled, this flag must be enabled to allow the remoting of encrypted content. When disabled, only non-encrypted content can be remoted.
     </message>
+
+    <!-- Chrome OS component updates chrome://flags strings -->
+    <message name="IDS_FLAGS_CROS_COMP_UPDATES_NAME" desc="Name for the flag to enable Chrome OS component flash updates" translateable="false">
+      Chrome OS Flash Component Updates
+    </message>
+    <message name="IDS_FLAGS_CROS_COMP_UPDATES_DESCRIPTION" desc="Description for the flag to enable Chrome OS component flash updates" translateable="false">
+      Enable Flash component updates for Chrome OS.
+    </message>
+    <message name="IDS_FLAGS_COMPONENT_FLASH_ONLY_NAME" desc="Name for the flag to use component flash only" translateable="false">
+      Component Flash Only
+    </message>
+    <message name="IDS_FLAGS_COMPONENT_FLASH_ONLY_DESCRIPTION" desc="Description for the flag to use component flash only" translateable="false">
+      This flag should be used only for testing Flash component updates.
+    </message>
   </messages>
  </release>
 </grit>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 0a88281..45dc7a1 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -22,13 +22,7 @@
     <output filename="google_chrome_strings_en-GB.pak" type="data_package" lang="en-GB" />
     <output filename="google_chrome_strings_en-US.pak" type="data_package" lang="en" />
     <output filename="google_chrome_strings_es.pak" type="data_package" lang="es" />
-    <if expr="is_ios">
-      <!-- iOS uses es-MX for es-419 -->
-      <output filename="google_chrome_strings_es-MX.pak" type="data_package" lang="es-419" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="google_chrome_strings_es-419.pak" type="data_package" lang="es-419" />
-    </if>
+    <output filename="google_chrome_strings_es-419.pak" type="data_package" lang="es-419" />
     <output filename="google_chrome_strings_et.pak" type="data_package" lang="et" />
     <output filename="google_chrome_strings_fa.pak" type="data_package" lang="fa" />
     <output filename="google_chrome_strings_fi.pak" type="data_package" lang="fi" />
@@ -53,13 +47,7 @@
     <output filename="google_chrome_strings_nb.pak" type="data_package" lang="no" />
     <!-- 'no' for Norwegian Bokmål. It should be 'nb'. -->
     <output filename="google_chrome_strings_pl.pak" type="data_package" lang="pl" />
-    <if expr="is_ios">
-      <!-- iOS uses pt for pt-BR -->
-      <output filename="google_chrome_strings_pt.pak" type="data_package" lang="pt-BR" />
-    </if>
-    <if expr="not is_ios">
-      <output filename="google_chrome_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
-    </if>
+    <output filename="google_chrome_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
     <output filename="google_chrome_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
     <output filename="google_chrome_strings_ro.pak" type="data_package" lang="ro" />
     <output filename="google_chrome_strings_ru.pak" type="data_package" lang="ru" />
@@ -136,7 +124,7 @@
   <release seq="1" allow_pseudo="false">
     <messages fallback_to_english="true">
       <!-- Settings specific strings -->
-      <if expr="not is_android and not is_ios">
+      <if expr="not is_android">
         <part file="settings_google_chrome_strings.grdp" />
       </if>
 
@@ -308,13 +296,7 @@
           Get Started with Chrome OS
         </message>
       </if>
-      <if expr="is_ios">
-        <message name="IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE"
-                 desc="Title for the hard-coded thumbnail that represents the Google Chrome Welcome page.  This is used on the NTP when there aren't enough thumbnails to show. [Length: 14em]">
-          Welcome
-        </message>
-      </if>
-      <if expr="not chromeos and not is_ios">
+      <if expr="not chromeos">
         <message name="IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE"
                  desc="Title for the hard-coded thumbnail that represents the Google Chrome OS Welcome page. This is used on the NTP when there aren't enough thumbnails to show.">
           Welcome to Google Chrome
@@ -975,7 +957,7 @@
       </if>
 
       <!-- Material Design User Manager -->
-      <if expr="not is_android and not is_ios and not chromeos">
+      <if expr="not is_android and not chromeos">
         <!-- Supervised user create confirmation page -->
         <message name="IDS_SUPERVISED_USER_CREATED_TEXT" desc="Informative text for the confirmation dialog that appears after a supervised user has been created.">
           To set which websites <ph name="NEW_PROFILE_NAME">$1<ex>New User</ex></ph> can view, you can configure restrictions and settings by visiting <ph name="BEGIN_LINK_1">&lt;a target="_blank" href="$3"&gt;</ph><ph name="DISPLAY_LINK">$4</ph><ph name="END_LINK_1">&lt;/a&gt;</ph>. If you do not change the default settings, <ph name="NEW_PROFILE_NAME">$1<ex>New User</ex></ph> can browse everything on the web.
diff --git a/chrome/app/mash/BUILD.gn b/chrome/app/mash/BUILD.gn
index a9ddd64c..02bbd48 100644
--- a/chrome/app/mash/BUILD.gn
+++ b/chrome/app/mash/BUILD.gn
@@ -43,7 +43,6 @@
   source = "chrome_mash_manifest.json"
   deps = [
     "//ash/autoclick/mus:manifest",
-    "//ash/mus:manifest",
     "//ash/touch_hud/mus:manifest",
     "//mash/catalog_viewer:manifest",
     "//mash/quick_launch:manifest",
@@ -54,7 +53,6 @@
   ]
   packaged_services = [
     "accessibility_autoclick",
-    "ash",
     "catalog_viewer",
     "mash_session",
     "quick_launch",
@@ -64,6 +62,11 @@
     "ui",
   ]
 
+  if (is_chromeos) {
+    deps += [ "//ash/mus:manifest" ]
+    packaged_services += [ "ash" ]
+  }
+
   if (is_linux && !is_android) {
     deps += [ "//components/font_service:manifest" ]
     packaged_services += [ "font_service" ]
diff --git a/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json b/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
index cc3774c..33ca4f7 100644
--- a/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
+++ b/chrome/app/mash/chrome_mash_content_browser_manifest_overlay.json
@@ -18,7 +18,7 @@
         "accessibility_autoclick": [ "ash:autoclick" ],
         "mash_session": [ "app" ],
         "task_viewer": [ "mash:launchable" ],
-        "ui": [ "ui:ime_registrar" ]
+        "ui": [ "ime_registrar" ]
       }
     }
   }
diff --git a/chrome/app/mash/mash_runner.cc b/chrome/app/mash/mash_runner.cc
index c1beac6..6f43edb 100644
--- a/chrome/app/mash/mash_runner.cc
+++ b/chrome/app/mash/mash_runner.cc
@@ -187,6 +187,8 @@
   // because it wouldn't be visible to the service's dynamic library.
   // https://crbug.com/664996
 
+  service_manager::WaitForDebuggerIfNecessary();
+
   base::FilePath path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
           switches::kChildProcess);
diff --git a/chrome/app/resources/generated_resources_fr.xtb b/chrome/app/resources/generated_resources_fr.xtb
index 3c5a58f..b7524497e 100644
--- a/chrome/app/resources/generated_resources_fr.xtb
+++ b/chrome/app/resources/generated_resources_fr.xtb
@@ -1797,7 +1797,7 @@
 <translation id="3303855915957856445">Aucun résultat de recherche n'a été trouvé.</translation>
 <translation id="3305389145870741612">Le processus de formatage peut prendre quelques secondes. Veuillez patienter.</translation>
 <translation id="3305661444342691068">Ouvrir le PDF dans Aperçu</translation>
-<translation id="3306684685104080068">Activer la diffusion sur des services basés sur le cloud tels que Google Hangouts.</translation>
+<translation id="3306684685104080068">Activer la diffusion sur des services basés sur le cloud tels que Google Hangouts.</translation>
 <translation id="3307950238492803740">Tout déboguer</translation>
 <translation id="3308006649705061278">Unité d'organisation (OU)</translation>
 <translation id="3308116878371095290">Le stockage des cookies n'est pas autorisé pour cette page.</translation>
@@ -2590,7 +2590,7 @@
 <translation id="4358697938732213860">Ajouter une adresse</translation>
 <translation id="4359408040881008151">Installée, car une ou plusieurs extensions dépendent de celle-ci.</translation>
 <translation id="4361190688154226069">Ciblage basé sur un rectangle</translation>
-<translation id="4363771538994847871">Aucune destination Cast trouvée. Besoin d'aide ?</translation>
+<translation id="4363771538994847871">Aucune destination Cast trouvée. Besoin d'aide?</translation>
 <translation id="4364444725319685468">"<ph name="FILE_NAME" />" téléchargé</translation>
 <translation id="4364567974334641491"><ph name="APP_NAME" /> partage une fenêtre.</translation>
 <translation id="4364830672918311045">Afficher les notifications</translation>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 05594e9..47503147 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -15,6 +15,7 @@
 import("//net/features.gni")
 import("//ppapi/features/features.gni")
 import("//printing/features/features.gni")
+import("//sandbox/features.gni")
 import("//third_party/protobuf/proto_library.gni")
 import("//ui/base/ui_features.gni")
 
@@ -386,6 +387,8 @@
     "favicon/large_icon_service_factory.h",
     "features.cc",
     "features.h",
+    "field_trial_recorder.cc",
+    "field_trial_recorder.h",
     "file_select_helper.cc",
     "file_select_helper.h",
     "file_select_helper_mac.mm",
@@ -504,6 +507,8 @@
     "mac/bluetooth_utility.mm",
     "mac/dock.h",
     "mac/dock.mm",
+    "mac/exception_processor.h",
+    "mac/exception_processor.mm",
     "mac/install_from_dmg.h",
     "mac/install_from_dmg.mm",
     "mac/keystone_glue.h",
@@ -691,6 +696,46 @@
     "net/timed_cache.h",
     "net/url_info.cc",
     "net/url_info.h",
+    "notifications/alert_dispatcher_mac.h",
+    "notifications/desktop_notification_profile_util.cc",
+    "notifications/desktop_notification_profile_util.h",
+    "notifications/login_state_notification_blocker_chromeos.cc",
+    "notifications/login_state_notification_blocker_chromeos.h",
+    "notifications/message_center_display_service.cc",
+    "notifications/message_center_display_service.h",
+    "notifications/native_notification_display_service.cc",
+    "notifications/native_notification_display_service.h",
+    "notifications/non_persistent_notification_handler.cc",
+    "notifications/non_persistent_notification_handler.h",
+    "notifications/notification.cc",
+    "notifications/notification.h",
+    "notifications/notification_common.cc",
+    "notifications/notification_common.h",
+    "notifications/notification_delegate.h",
+    "notifications/notification_display_service.h",
+    "notifications/notification_display_service_factory.cc",
+    "notifications/notification_display_service_factory.h",
+    "notifications/notification_handler.h",
+    "notifications/notification_object_proxy.cc",
+    "notifications/notification_object_proxy.h",
+    "notifications/notification_permission_context.cc",
+    "notifications/notification_permission_context.h",
+    "notifications/notification_platform_bridge.h",
+    "notifications/notification_platform_bridge_mac.h",
+    "notifications/notification_platform_bridge_mac.mm",
+    "notifications/notification_ui_manager.h",
+    "notifications/notifier_state_tracker.cc",
+    "notifications/notifier_state_tracker.h",
+    "notifications/notifier_state_tracker_factory.cc",
+    "notifications/notifier_state_tracker_factory.h",
+    "notifications/persistent_notification_delegate.cc",
+    "notifications/persistent_notification_delegate.h",
+    "notifications/persistent_notification_handler.cc",
+    "notifications/persistent_notification_handler.h",
+    "notifications/platform_notification_service_impl.cc",
+    "notifications/platform_notification_service_impl.h",
+    "notifications/web_notification_delegate.cc",
+    "notifications/web_notification_delegate.h",
     "ntp_snippets/bookmark_last_visit_updater.cc",
     "ntp_snippets/bookmark_last_visit_updater.h",
     "ntp_snippets/content_suggestions_service_factory.cc",
@@ -740,6 +785,8 @@
     "page_load_metrics/page_load_metrics_util.h",
     "page_load_metrics/page_load_tracker.cc",
     "page_load_metrics/page_load_tracker.h",
+    "page_load_metrics/user_input_tracker.cc",
+    "page_load_metrics/user_input_tracker.h",
     "password_manager/chrome_password_manager_client.cc",
     "password_manager/chrome_password_manager_client.h",
     "password_manager/password_manager_setting_migrator_service_factory.cc",
@@ -983,6 +1030,8 @@
     "push_messaging/push_messaging_app_identifier.h",
     "push_messaging/push_messaging_constants.cc",
     "push_messaging/push_messaging_constants.h",
+    "push_messaging/push_messaging_notification_manager.cc",
+    "push_messaging/push_messaging_notification_manager.h",
     "push_messaging/push_messaging_service_factory.cc",
     "push_messaging/push_messaging_service_factory.h",
     "push_messaging/push_messaging_service_impl.cc",
@@ -1360,10 +1409,10 @@
     "//components/network_time",
     "//components/ntp_snippets",
     "//components/ntp_tiles",
-    "//components/offline_pages",
-    "//components/offline_pages/background:background_offliner",
-    "//components/offline_pages/downloads:offline_pages_ui_adapter",
-    "//components/offline_pages/request_header:request_header",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core/background:background_offliner",
+    "//components/offline_pages/core/downloads:offline_pages_ui_adapter",
+    "//components/offline_pages/core/request_header:request_header",
     "//components/omnibox/browser",
     "//components/os_crypt",
     "//components/packed_ct_ev_whitelist",
@@ -2305,98 +2354,6 @@
       sources -= [ "net/nss_context_linux.cc" ]
     }
   }
-  if (enable_notifications) {
-    sources += [
-      "notifications/alert_dispatcher_mac.h",
-      "notifications/desktop_notification_profile_util.cc",
-      "notifications/desktop_notification_profile_util.h",
-      "notifications/login_state_notification_blocker_chromeos.cc",
-      "notifications/login_state_notification_blocker_chromeos.h",
-      "notifications/message_center_display_service.cc",
-      "notifications/message_center_display_service.h",
-      "notifications/native_notification_display_service.cc",
-      "notifications/native_notification_display_service.h",
-      "notifications/non_persistent_notification_handler.cc",
-      "notifications/non_persistent_notification_handler.h",
-      "notifications/notification.cc",
-      "notifications/notification.h",
-      "notifications/notification_common.cc",
-      "notifications/notification_common.h",
-      "notifications/notification_delegate.h",
-      "notifications/notification_display_service.h",
-      "notifications/notification_display_service_factory.cc",
-      "notifications/notification_display_service_factory.h",
-      "notifications/notification_handler.h",
-      "notifications/notification_object_proxy.cc",
-      "notifications/notification_object_proxy.h",
-      "notifications/notification_permission_context.cc",
-      "notifications/notification_permission_context.h",
-      "notifications/notification_platform_bridge.h",
-      "notifications/notification_platform_bridge_mac.h",
-      "notifications/notification_platform_bridge_mac.mm",
-      "notifications/notification_ui_manager.h",
-      "notifications/notifier_state_tracker.cc",
-      "notifications/notifier_state_tracker.h",
-      "notifications/notifier_state_tracker_factory.cc",
-      "notifications/notifier_state_tracker_factory.h",
-      "notifications/persistent_notification_delegate.cc",
-      "notifications/persistent_notification_delegate.h",
-      "notifications/persistent_notification_handler.cc",
-      "notifications/persistent_notification_handler.h",
-      "notifications/platform_notification_service_impl.cc",
-      "notifications/platform_notification_service_impl.h",
-      "notifications/web_notification_delegate.cc",
-      "notifications/web_notification_delegate.h",
-      "push_messaging/push_messaging_notification_manager.cc",
-      "push_messaging/push_messaging_notification_manager.h",
-    ]
-    if (android_java_ui) {
-      sources += [
-        "notifications/notification_permission_infobar_delegate.cc",
-        "notifications/notification_permission_infobar_delegate.h",
-        "notifications/notification_platform_bridge_android.cc",
-        "notifications/notification_platform_bridge_android.h",
-      ]
-    } else {
-      sources += [
-        "notifications/application_notifier_source.cc",
-        "notifications/application_notifier_source.h",
-        "notifications/arc_application_notifier_source_chromeos.cc",
-        "notifications/arc_application_notifier_source_chromeos.h",
-        "notifications/extension_welcome_notification.cc",
-        "notifications/extension_welcome_notification.h",
-        "notifications/extension_welcome_notification_factory.cc",
-        "notifications/extension_welcome_notification_factory.h",
-        "notifications/fullscreen_notification_blocker.cc",
-        "notifications/fullscreen_notification_blocker.h",
-        "notifications/google_now_notification_stats_collector.cc",
-        "notifications/google_now_notification_stats_collector.h",
-        "notifications/message_center_notification_manager.cc",
-        "notifications/message_center_notification_manager.h",
-        "notifications/message_center_settings_controller.cc",
-        "notifications/message_center_settings_controller.h",
-        "notifications/message_center_stats_collector.cc",
-        "notifications/message_center_stats_collector.h",
-        "notifications/notification_conversion_helper.cc",
-        "notifications/notification_conversion_helper.h",
-        "notifications/notification_system_observer.cc",
-        "notifications/notification_system_observer.h",
-        "notifications/notification_ui_manager_desktop.cc",
-        "notifications/notifier_source.h",
-        "notifications/profile_notification.cc",
-        "notifications/profile_notification.h",
-        "notifications/screen_lock_notification_blocker.cc",
-        "notifications/screen_lock_notification_blocker.h",
-        "notifications/system_component_notifier_source_chromeos.cc",
-        "notifications/system_component_notifier_source_chromeos.h",
-        "notifications/web_page_notifier_source.cc",
-        "notifications/web_page_notifier_source.h",
-      ]
-    }
-    if (is_mac) {
-      deps += [ "//chrome/browser/ui/cocoa/notifications:common" ]
-    }
-  }
   if (enable_themes) {
     sources += [
       "sync/glue/theme_data_type_controller.cc",
@@ -2592,6 +2549,7 @@
       "//components/cdm/browser",
       "//components/payments/android:payments_jni",
       "//components/resources:components_resources",
+      "//sandbox:sandbox_features",
       "//third_party/android_opengl/etc1",
       "//third_party/android_tools:cpu_features",
       "//third_party/libaddressinput:util",
@@ -2599,7 +2557,6 @@
 
     deps -= [ "//components/storage_monitor" ]
     if (use_seccomp_bpf) {
-      defines += [ "USE_SECCOMP_BPF" ]
       deps += [ "//sandbox/linux:seccomp_bpf" ]
     }
   } else {
@@ -3079,6 +3036,8 @@
       "android/find_in_page/find_in_page_bridge.h",
       "android/foreign_session_helper.cc",
       "android/foreign_session_helper.h",
+      "android/history/browsing_history_bridge.cc",
+      "android/history/browsing_history_bridge.h",
       "android/history_report/data_observer.cc",
       "android/history_report/data_observer.h",
       "android/history_report/data_provider.cc",
@@ -3347,6 +3306,10 @@
       "net/spdyproxy/data_reduction_promo_infobar_delegate_android.h",
       "net/spdyproxy/data_reduction_proxy_settings_android.cc",
       "net/spdyproxy/data_reduction_proxy_settings_android.h",
+      "notifications/notification_permission_infobar_delegate.cc",
+      "notifications/notification_permission_infobar_delegate.h",
+      "notifications/notification_platform_bridge_android.cc",
+      "notifications/notification_platform_bridge_android.h",
       "page_load_metrics/observers/android_page_load_metrics_observer.cc",
       "page_load_metrics/observers/android_page_load_metrics_observer.h",
       "password_manager/account_chooser_dialog_android.cc",
@@ -3436,6 +3399,42 @@
         "android/offline_pages/evaluation/offline_page_evaluation_bridge.h",
       ]
     }
+  } else {
+    # ! android_java_ui
+    sources += [
+      "notifications/application_notifier_source.cc",
+      "notifications/application_notifier_source.h",
+      "notifications/arc_application_notifier_source_chromeos.cc",
+      "notifications/arc_application_notifier_source_chromeos.h",
+      "notifications/extension_welcome_notification.cc",
+      "notifications/extension_welcome_notification.h",
+      "notifications/extension_welcome_notification_factory.cc",
+      "notifications/extension_welcome_notification_factory.h",
+      "notifications/fullscreen_notification_blocker.cc",
+      "notifications/fullscreen_notification_blocker.h",
+      "notifications/google_now_notification_stats_collector.cc",
+      "notifications/google_now_notification_stats_collector.h",
+      "notifications/message_center_notification_manager.cc",
+      "notifications/message_center_notification_manager.h",
+      "notifications/message_center_settings_controller.cc",
+      "notifications/message_center_settings_controller.h",
+      "notifications/message_center_stats_collector.cc",
+      "notifications/message_center_stats_collector.h",
+      "notifications/notification_conversion_helper.cc",
+      "notifications/notification_conversion_helper.h",
+      "notifications/notification_system_observer.cc",
+      "notifications/notification_system_observer.h",
+      "notifications/notification_ui_manager_desktop.cc",
+      "notifications/notifier_source.h",
+      "notifications/profile_notification.cc",
+      "notifications/profile_notification.h",
+      "notifications/screen_lock_notification_blocker.cc",
+      "notifications/screen_lock_notification_blocker.h",
+      "notifications/system_component_notifier_source_chromeos.cc",
+      "notifications/system_component_notifier_source_chromeos.h",
+      "notifications/web_page_notifier_source.cc",
+      "notifications/web_page_notifier_source.h",
+    ]
   }
 
   if (enable_vr_shell || enable_webvr) {
@@ -3451,6 +3450,7 @@
     deps += [
       "//chrome/app_shim",
       "//chrome/browser/apps/app_shim",
+      "//chrome/browser/ui/cocoa/notifications:common",
       "//third_party/crashpad/crashpad/client:client",
       "//third_party/google_toolbox_for_mac",
       "//third_party/mozilla",
@@ -3548,10 +3548,11 @@
 
   if (is_linux || is_win) {
     sources += [
+      "payments/payment_request_impl.cc",
+      "payments/payment_request_impl.h",
       "renderer_context_menu/spelling_options_submenu_observer.cc",
       "renderer_context_menu/spelling_options_submenu_observer.h",
     ]
-    deps += [ "//chrome/browser/payments:payments" ]
   }
 
   if (is_desktop_linux) {
@@ -3716,6 +3717,12 @@
       "supervised_user/supervised_user_theme.h",
     ]
   }
+  if (enable_supervised_users && !is_android && !is_chromeos) {
+    sources += [
+      "supervised_user/supervised_user_creation_policy_handler.cc",
+      "supervised_user/supervised_user_creation_policy_handler.h",
+    ]
+  }
   if (enable_webrtc) {
     sources += [
       "media/audio_debug_recordings_handler.h",
@@ -3917,6 +3924,7 @@
       "../android/java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java",
       "../android/java/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java",
       "../android/java/src/org/chromium/chrome/browser/findinpage/FindInPageBridge.java",
+      "../android/java/src/org/chromium/chrome/browser/history/BrowsingHistoryBridge.java",
       "../android/java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java",
       "../android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index daeeff6..32b2ef74 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -47,7 +47,7 @@
 #include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/ntp_snippets_constants.h"
 #include "components/ntp_tiles/switches.h"
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_feature.h"
 #include "components/omnibox/browser/omnibox_switches.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/proximity_auth/switches.h"
@@ -829,6 +829,10 @@
      IDS_FLAGS_GPU_RASTERIZATION_MSAA_SAMPLE_COUNT_NAME,
      IDS_FLAGS_GPU_RASTERIZATION_MSAA_SAMPLE_COUNT_DESCRIPTION, kOsAll,
      MULTI_VALUE_TYPE(kGpuRasterizationMSAASampleCountChoices)},
+    {"enable-slimming-paint-invalidation",
+     IDS_FLAGS_ENABLE_SLIMMING_PAINT_INVALIDATION_NAME,
+     IDS_FLAGS_ENABLE_SLIMMING_PAINT_INVALIDATION_DESCRIPTION, kOsAll,
+     SINGLE_VALUE_TYPE(switches::kEnableSlimmingPaintInvalidation)},
     {"enable-experimental-web-platform-features",
      IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_NAME,
      IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_DESCRIPTION, kOsAll,
@@ -1306,11 +1310,6 @@
      SINGLE_DISABLE_VALUE_TYPE(
          views::switches::kDisableViewsRectBasedTargeting)},
 #endif  // TOOLKIT_VIEWS
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-    {"enable-apps-show-on-first-paint", IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_NAME,
-     IDS_FLAGS_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION, kOsDesktop,
-     SINGLE_VALUE_TYPE(extensions::switches::kEnableAppsShowOnFirstPaint)},
-#endif  // ENABLE_EXTENSIONS
 #if defined(OS_ANDROID)
     {"reader-mode-heuristics", IDS_FLAGS_READER_MODE_HEURISTICS_NAME,
      IDS_FLAGS_READER_MODE_HEURISTICS_DESCRIPTION, kOsAndroid,
@@ -1692,13 +1691,13 @@
      IDS_FLAGS_CROS_REGIONS_MODE_DESCRIPTION, kOsCrOS,
      MULTI_VALUE_TYPE(kCrosRegionsModeChoices)},
 #endif  // OS_CHROMEOS
-#if defined(ENABLE_NOTIFICATIONS) && defined(OS_ANDROID)
+#if defined(OS_ANDROID)
     {"enable-web-notification-custom-layouts",
      IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_NAME,
      IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_DESCRIPTION, kOsAndroid,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableWebNotificationCustomLayouts,
                                switches::kDisableWebNotificationCustomLayouts)},
-#endif  // ENABLE_NOTIFICATIONS && OS_ANDROID
+#endif  // OS_ANDROID
 #if defined(OS_WIN)
     {"enable-appcontainer", IDS_FLAGS_ENABLE_APPCONTAINER_NAME,
      IDS_FLAGS_ENABLE_APPCONTAINER_DESCRIPTION, kOsWin,
@@ -1989,8 +1988,7 @@
      FEATURE_VALUE_TYPE(media::kNewAudioRenderingMixingStrategy)},
     {"disable-background-video-track",
      IDS_BACKGROUND_VIDEO_TRACK_OPTIMIZATION_NAME,
-     IDS_BACKGROUND_VIDEO_TRACK_OPTIMIZATION_DESCRIPTION,
-     kOsAll,
+     IDS_BACKGROUND_VIDEO_TRACK_OPTIMIZATION_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(media::kBackgroundVideoTrackOptimization)},
 #if defined(OS_CHROMEOS)
     {"files-quick-view", IDS_FLAGS_FILES_QUICK_VIEW_NAME,
@@ -2087,8 +2085,7 @@
      IDS_FLAGS_OFFLINE_PAGES_SVELTE_CONCURRENT_LOADING_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(
          offline_pages::kOfflinePagesSvelteConcurrentLoadingFeature)},
-    {"web-payments-modifiers",
-     IDS_FLAGS_WEB_PAYMENTS_MODIFIERS_NAME,
+    {"web-payments-modifiers", IDS_FLAGS_WEB_PAYMENTS_MODIFIERS_NAME,
      IDS_FLAGS_WEB_PAYMENTS_MODIFIERS_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kWebPaymentsModifiers)},
 #endif
@@ -2109,6 +2106,14 @@
      IDS_FLAGS_VIDEO_FULLSCREEN_ORIENTATION_LOCK_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(media::kVideoFullscreenOrientationLock)},
 #endif
+#if defined(OS_CHROMEOS)
+    {"cros-comp-updates", IDS_FLAGS_CROS_COMP_UPDATES_NAME,
+     IDS_FLAGS_CROS_COMP_UPDATES_DESCRIPTION, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kCrosCompUpdates)},
+    {"component-flash-only", IDS_FLAGS_COMPONENT_FLASH_ONLY_NAME,
+     IDS_FLAGS_COMPONENT_FLASH_ONLY_DESCRIPTION, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kComponentFlashOnly)},
+#endif
 
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index 14ac2dc..da28b78 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -11,6 +11,7 @@
   "+components/toolbar",
   "+components/web_contents_delegate_android",
   "+sandbox/linux/seccomp-bpf/sandbox_bpf.h",
+  "+sandbox/sandbox_features.h",
   "+third_party/gvr-android-sdk",
 ]
 
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
index 75bff38..0007725 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
@@ -20,7 +20,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/android/infobars/app_banner_infobar_android.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/manifest.h"
 #include "jni/AppBannerInfoBarDelegateAndroid_jni.h"
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index e31e369..dfda1e8 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -14,7 +14,7 @@
 #include "chrome/common/chrome_features.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
 #include "components/ntp_snippets/features.h"
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_feature.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "content/public/common/content_features.h"
 #include "jni/ChromeFeatureList_jni.h"
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 106415a7..5a33cd54 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -53,6 +53,7 @@
 #include "chrome/browser/android/feedback/screenshot_task.h"
 #include "chrome/browser/android/find_in_page/find_in_page_bridge.h"
 #include "chrome/browser/android/foreign_session_helper.h"
+#include "chrome/browser/android/history/browsing_history_bridge.h"
 #include "chrome/browser/android/history_report/history_report_jni_bridge.h"
 #include "chrome/browser/android/instantapps/instant_apps_infobar_delegate.h"
 #include "chrome/browser/android/instantapps/instant_apps_settings.h"
@@ -238,6 +239,7 @@
     {"BluetoothChooserAndroid", BluetoothChooserAndroid::Register},
     {"BookmarkBridge", BookmarkBridge::RegisterBookmarkBridge},
     {"BrowsingDataCounterBridge", BrowsingDataCounterBridge::Register},
+    {"BrowsingHistoryBridge", RegisterBrowsingHistoryBridge},
     {"CardUnmaskPrompt", autofill::CardUnmaskPromptViewAndroid::Register},
     {"CertificateViewer", RegisterCertificateViewer},
     {"ChildAccountService", RegisterChildAccountService},
diff --git a/chrome/browser/android/history/OWNERS b/chrome/browser/android/history/OWNERS
new file mode 100644
index 0000000..05d32ba
--- /dev/null
+++ b/chrome/browser/android/history/OWNERS
@@ -0,0 +1 @@
+twellington@chromium.org
\ No newline at end of file
diff --git a/chrome/browser/android/history/browsing_history_bridge.cc b/chrome/browser/android/history/browsing_history_bridge.cc
new file mode 100644
index 0000000..768254a
--- /dev/null
+++ b/chrome/browser/android/history/browsing_history_bridge.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/history/browsing_history_bridge.h"
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "components/url_formatter/url_formatter.h"
+#include "jni/BrowsingHistoryBridge_jni.h"
+
+const int kMaxQueryCount = 100;
+
+BrowsingHistoryBridge::BrowsingHistoryBridge(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    jobject j_profile) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  browsing_history_service_.reset(new BrowsingHistoryService(profile, this));
+  j_history_service_obj_.Reset(env, obj);
+}
+
+BrowsingHistoryBridge::~BrowsingHistoryBridge() {}
+
+void BrowsingHistoryBridge::Destroy(JNIEnv*, const JavaParamRef<jobject>&) {
+  delete this;
+}
+
+void BrowsingHistoryBridge::QueryHistory(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& j_result_obj,
+    jstring j_query,
+    int64_t j_query_end_time) {
+  j_query_result_obj_.Reset(env, j_result_obj);
+
+  history::QueryOptions options;
+  options.max_count = kMaxQueryCount;
+  options.end_time = base::Time::FromJavaTime(j_query_end_time);
+  options.duplicate_policy = history::QueryOptions::REMOVE_DUPLICATES_PER_DAY;
+
+  browsing_history_service_->QueryHistory(
+      base::android::ConvertJavaStringToUTF16(env, j_query), options);
+}
+
+// BrowsingHistoryServiceHandler implementation
+void BrowsingHistoryBridge::OnQueryComplete(
+    std::vector<BrowsingHistoryService::HistoryEntry>* results,
+    BrowsingHistoryService::QueryResultsInfo* query_results_info) {
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  for (auto it = results->begin(); it != results->end(); ++it) {
+
+    // TODO(twellington): move the domain logic to BrowsingHistoryServce so it
+    // can be shared with BrowsingHistoryHandler.
+    base::string16 domain = url_formatter::IDNToUnicode(it->url.host());
+    // When the domain is empty, use the scheme instead. This allows for a
+    // sensible treatment of e.g. file: URLs when group by domain is on.
+    if (domain.empty())
+      domain = base::UTF8ToUTF16(it->url.scheme() + ":");
+
+    Java_BrowsingHistoryBridge_createHistoryItemAndAddToList(
+        env,
+        j_query_result_obj_.obj(),
+        base::android::ConvertUTF8ToJavaString(env, it->url.spec()),
+        base::android::ConvertUTF16ToJavaString(env, domain),
+        base::android::ConvertUTF16ToJavaString(env, it->title),
+        it->time.ToJavaTime());
+  }
+
+  Java_BrowsingHistoryBridge_onQueryHistoryComplete(
+      env,
+      j_history_service_obj_.obj(),
+      j_query_result_obj_.obj());
+
+  j_query_result_obj_.Release();
+}
+
+void BrowsingHistoryBridge::OnRemoveVisitsComplete() {
+  // TODO(twellington): implement
+}
+
+void BrowsingHistoryBridge::OnRemoveVisitsFailed() {
+  // TODO(twellington): implement
+}
+
+void BrowsingHistoryBridge::HistoryDeleted() {
+  // TODO(twellington): implement
+}
+
+void BrowsingHistoryBridge::HasOtherFormsOfBrowsingHistory(
+    bool has_other_forms, bool has_synced_results) {
+  // TODO(twellington): implement
+}
+
+bool RegisterBrowsingHistoryBridge(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+static jlong Init(JNIEnv* env,
+                  const JavaParamRef<jobject>& obj,
+                  const JavaParamRef<jobject>& j_profile) {
+  BrowsingHistoryBridge* bridge =
+      new BrowsingHistoryBridge(env, obj, j_profile);
+  return reinterpret_cast<intptr_t>(bridge);
+}
diff --git a/chrome/browser/android/history/browsing_history_bridge.h b/chrome/browser/android/history/browsing_history_bridge.h
new file mode 100644
index 0000000..d94a063
--- /dev/null
+++ b/chrome/browser/android/history/browsing_history_bridge.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_HISTORY_BROWSING_HISTORY_BRIDGE_H_
+#define CHROME_BROWSER_ANDROID_HISTORY_BROWSING_HISTORY_BRIDGE_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/history/browsing_history_service_handler.h"
+
+using base::android::JavaParamRef;
+
+// The bridge for fetching browsing history information for the Android
+// history UI. This queries the BrowsingHistoryService and listens
+// for callbacks.
+class BrowsingHistoryBridge : public BrowsingHistoryServiceHandler {
+
+ public:
+  explicit BrowsingHistoryBridge(JNIEnv* env,
+                                 const JavaParamRef<jobject>& obj,
+                                 jobject j_profile);
+  void Destroy(JNIEnv*, const JavaParamRef<jobject>&);
+
+  void QueryHistory(JNIEnv* env,
+                    const JavaParamRef<jobject>& obj,
+                    const JavaParamRef<jobject>& j_result_obj,
+                    jstring j_query,
+                    int64_t j_query_end_time);
+
+  // BrowsingHistoryServiceHandler implementation
+  void OnQueryComplete(
+      std::vector<BrowsingHistoryService::HistoryEntry>* results,
+      BrowsingHistoryService::QueryResultsInfo* query_results_info) override;
+  void OnRemoveVisitsComplete() override;
+  void OnRemoveVisitsFailed() override;
+  void HistoryDeleted() override;
+  void HasOtherFormsOfBrowsingHistory(
+      bool has_other_forms, bool has_synced_results) override;
+
+ private:
+  ~BrowsingHistoryBridge() override;
+
+  std::unique_ptr<BrowsingHistoryService> browsing_history_service_;
+  base::android::ScopedJavaGlobalRef<jobject> j_history_service_obj_;
+  base::android::ScopedJavaGlobalRef<jobject> j_query_result_obj_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryBridge);
+};
+
+bool RegisterBrowsingHistoryBridge(JNIEnv* env);
+
+#endif  // CHROME_BROWSER_ANDROID_HISTORY_BROWSING_HISTORY_BRIDGE_H_
diff --git a/chrome/browser/android/metrics/launch_metrics.cc b/chrome/browser/android/metrics/launch_metrics.cc
index 1c2eebb..b231fe5 100644
--- a/chrome/browser/android/metrics/launch_metrics.cc
+++ b/chrome/browser/android/metrics/launch_metrics.cc
@@ -14,7 +14,8 @@
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/prefs/pref_metrics_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/LaunchMetrics_jni.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/android/offline_pages/DEPS b/chrome/browser/android/offline_pages/DEPS
index 7945571..e58dcb0 100644
--- a/chrome/browser/android/offline_pages/DEPS
+++ b/chrome/browser/android/offline_pages/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
-  "+components/offline_pages",
-  "+components/offline_pages/background",
+  "+components/offline_pages/core",
+  "+components/offline_pages/core/background",
 ]
diff --git a/chrome/browser/android/offline_pages/background_scheduler_bridge.cc b/chrome/browser/android/offline_pages/background_scheduler_bridge.cc
index 4feb822d..a282c3f 100644
--- a/chrome/browser/android/offline_pages/background_scheduler_bridge.cc
+++ b/chrome/browser/android/offline_pages/background_scheduler_bridge.cc
@@ -9,8 +9,8 @@
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
 #include "jni/BackgroundSchedulerBridge_jni.h"
 
 using base::android::JavaParamRef;
diff --git a/chrome/browser/android/offline_pages/background_scheduler_bridge.h b/chrome/browser/android/offline_pages/background_scheduler_bridge.h
index 8abbb3cbd..7d47ca8 100644
--- a/chrome/browser/android/offline_pages/background_scheduler_bridge.h
+++ b/chrome/browser/android/offline_pages/background_scheduler_bridge.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_ANDROID_OFFLINE_PAGES_BACKGROUND_SCHEDULER_BRIDGE_H_
 #define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_BACKGROUND_SCHEDULER_BRIDGE_H_
 
-#include "components/offline_pages/background/scheduler.h"
+#include "components/offline_pages/core/background/scheduler.h"
 
 #include "base/android/jni_android.h"
 
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
index 28826ae..af41f949 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
+++ b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
@@ -22,12 +22,12 @@
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/OfflinePageDownloadBridge_jni.h"
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
index 0f9b5a4..7ac42bc 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
+++ b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.h
@@ -11,7 +11,7 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/macros.h"
 #include "base/supports_user_data.h"
-#include "components/offline_pages/downloads/download_ui_adapter.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h b/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h
index 4047b91..a22ad3d 100644
--- a/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h
+++ b/chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h
@@ -9,7 +9,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/macros.h"
-#include "components/offline_pages/downloads/offline_page_download_notifier.h"
+#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
 
 namespace offline_pages {
 namespace android {
diff --git a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc b/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc
index 03eabe2..c4b07df 100644
--- a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc
+++ b/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.cc
@@ -9,8 +9,9 @@
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/offline_event_logger.h"
 #include "net/base/network_change_notifier.h"
 
 namespace offline_pages {
@@ -19,6 +20,8 @@
 
 namespace {
 
+const char kLogTag[] = "EvaluationTestScheduler";
+
 void StartProcessing();
 
 void ProcessingDoneCallback(bool result) {
@@ -51,6 +54,16 @@
 
 void EvaluationTestScheduler::Schedule(
     const TriggerConditions& trigger_conditions) {
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  if (!coordinator_) {
+    coordinator_ =
+        RequestCoordinatorFactory::GetInstance()->GetForBrowserContext(profile);
+    // It's not expected that the coordinator would be nullptr since this bridge
+    // would only be used for testing scenario.
+    DCHECK(coordinator_);
+  }
+  coordinator_->GetLogger()->RecordActivity(std::string(kLogTag) +
+                                            " Start schedule!");
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
                                                 base::Bind(&StartProcessing));
 }
diff --git a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h b/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h
index e17d436b..b7acd9d 100644
--- a/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h
+++ b/chrome/browser/android/offline_pages/evaluation/evaluation_test_scheduler.h
@@ -5,9 +5,12 @@
 #ifndef CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
 #define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_EVALUATION_EVALUATION_TEST_SCHEDULER_H_
 
-#include "components/offline_pages/background/scheduler.h"
+#include "components/offline_pages/core/background/scheduler.h"
 
 namespace offline_pages {
+
+class RequestCoordinator;
+
 namespace android {
 
 class EvaluationTestScheduler : public Scheduler {
@@ -20,6 +23,9 @@
 
   // Callback used by user request.
   void ImmediateScheduleCallback(bool result);
+
+ private:
+  RequestCoordinator* coordinator_;
 };
 
 }  // namespace android
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
index 1ca6516..10612b1 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
+++ b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.cc
@@ -21,17 +21,17 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/common/chrome_constants.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/request_queue_store_sql.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/downloads/download_notifying_observer.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/offliner_factory.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "jni/OfflinePageEvaluationBridge_jni.h"
@@ -47,6 +47,7 @@
 namespace android {
 
 namespace {
+const char kNativeTag[] = "OPNative";
 
 void ToJavaOfflinePageList(JNIEnv* env,
                            jobject j_result_obj,
@@ -214,6 +215,8 @@
   NotifyIfDoneLoading();
   offline_page_model_->AddObserver(this);
   request_coordinator_->AddObserver(this);
+  offline_page_model_->GetLogger()->SetClient(this);
+  request_coordinator_->GetLogger()->SetClient(this);
 }
 
 OfflinePageEvaluationBridge::~OfflinePageEvaluationBridge() {
@@ -229,8 +232,9 @@
   NotifyIfDoneLoading();
 }
 
-void OfflinePageEvaluationBridge::OfflinePageModelChanged(
-    OfflinePageModel* model) {}
+void OfflinePageEvaluationBridge::OfflinePageAdded(
+    OfflinePageModel* model,
+    const OfflinePageItem& added_page) {}
 
 void OfflinePageEvaluationBridge::OfflinePageDeleted(
     int64_t offline_id,
@@ -258,6 +262,13 @@
       env, java_ref_, ToJavaSavePageRequest(env, request));
 }
 
+void OfflinePageEvaluationBridge::CustomLog(const std::string& message) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_OfflinePageEvaluationBridge_log(env, java_ref_,
+                                       ConvertUTF8ToJavaString(env, kNativeTag),
+                                       ConvertUTF8ToJavaString(env, message));
+}
+
 void OfflinePageEvaluationBridge::GetAllPages(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h
index e860143..f6326174 100644
--- a/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h
+++ b/chrome/browser/android/offline_pages/evaluation/offline_page_evaluation_bridge.h
@@ -9,9 +9,10 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/offline_page_model.h"
 
 namespace content {
 class BrowserContext;
@@ -25,7 +26,8 @@
  * Bridge for exposing native implementation which are used by evaluation.
  */
 class OfflinePageEvaluationBridge : public OfflinePageModel::Observer,
-                                    public RequestCoordinator::Observer {
+                                    public RequestCoordinator::Observer,
+                                    public OfflineEventLogger::Client {
  public:
   static bool Register(JNIEnv* env);
   static std::unique_ptr<KeyedService> GetTestingRequestCoordinator(
@@ -40,7 +42,8 @@
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(OfflinePageModel* model) override;
-  void OfflinePageModelChanged(OfflinePageModel* model) override;
+  void OfflinePageAdded(OfflinePageModel* model,
+                        const OfflinePageItem& added_page) override;
   void OfflinePageDeleted(int64_t offline_id,
                           const ClientId& client_id) override;
 
@@ -50,6 +53,10 @@
                    RequestNotifier::BackgroundSavePageResult status) override;
   void OnChanged(const SavePageRequest& request) override;
 
+  // OfflineEventLogger::Client implementation.
+  void CustomLog(const std::string& message) override;
+
+  // Gets all pages in offline page model.
   void GetAllPages(JNIEnv* env,
                    const base::android::JavaParamRef<jobject>& obj,
                    const base::android::JavaParamRef<jobject>& j_result_obj,
diff --git a/chrome/browser/android/offline_pages/offline_page_bookmark_observer.cc b/chrome/browser/android/offline_pages/offline_page_bookmark_observer.cc
index cfac9242..597adff 100644
--- a/chrome/browser/android/offline_pages/offline_page_bookmark_observer.cc
+++ b/chrome/browser/android/offline_pages/offline_page_bookmark_observer.cc
@@ -6,8 +6,8 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "components/bookmarks/browser/bookmark_node.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "url/gurl.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/android/offline_pages/offline_page_bookmark_observer.h b/chrome/browser/android/offline_pages/offline_page_bookmark_observer.h
index 33c2a9cf..0e9194e 100644
--- a/chrome/browser/android/offline_pages/offline_page_bookmark_observer.h
+++ b/chrome/browser/android/offline_pages/offline_page_bookmark_observer.h
@@ -8,7 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/offline_page_types.h"
 
 class GURL;
 
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.cc b/chrome/browser/android/offline_pages/offline_page_bridge.cc
index 93bd5ee..ff555ba 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.cc
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.cc
@@ -22,13 +22,13 @@
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/OfflinePageBridge_jni.h"
@@ -278,10 +278,13 @@
   NotifyIfDoneLoading();
 }
 
-void OfflinePageBridge::OfflinePageModelChanged(OfflinePageModel* model) {
+void OfflinePageBridge::OfflinePageAdded(OfflinePageModel* model,
+                                         const OfflinePageItem& added_page) {
   DCHECK_EQ(offline_page_model_, model);
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_OfflinePageBridge_offlinePageModelChanged(env, java_ref_);
+
+  Java_OfflinePageBridge_offlinePageAdded(
+      env, java_ref_, ToJavaOfflinePageItem(env, added_page));
 }
 
 void OfflinePageBridge::OfflinePageDeleted(int64_t offline_id,
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.h b/chrome/browser/android/offline_pages/offline_page_bridge.h
index c7e0e68..52de41e 100644
--- a/chrome/browser/android/offline_pages/offline_page_bridge.h
+++ b/chrome/browser/android/offline_pages/offline_page_bridge.h
@@ -12,8 +12,8 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 #include "base/supports_user_data.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
 
 namespace content {
 class BrowserContext;
@@ -40,7 +40,8 @@
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(OfflinePageModel* model) override;
-  void OfflinePageModelChanged(OfflinePageModel* model) override;
+  void OfflinePageAdded(OfflinePageModel* model,
+                        const OfflinePageItem& added_page) override;
   void OfflinePageDeleted(int64_t offline_id,
                           const ClientId& client_id) override;
 
diff --git a/chrome/browser/android/offline_pages/offline_page_info_handler.cc b/chrome/browser/android/offline_pages/offline_page_info_handler.cc
index 204a524..d96cfcc 100644
--- a/chrome/browser/android/offline_pages/offline_page_info_handler.cc
+++ b/chrome/browser/android/offline_pages/offline_page_info_handler.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/android/offline_pages/offline_page_info_handler.h"
 
 #include "base/strings/string_util.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "components/sessions/content/content_serialized_navigation_driver.h"
 #include "content/public/browser/navigation_entry.h"
 
diff --git a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
index c693743..9dedbdd 100644
--- a/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
+++ b/chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h
@@ -14,7 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "components/offline_pages/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
 
 namespace base {
 class FilePath;
diff --git a/chrome/browser/android/offline_pages/offline_page_model_factory.cc b/chrome/browser/android/offline_pages/offline_page_model_factory.cc
index 6201cc2..2201011 100644
--- a/chrome/browser/android/offline_pages/offline_page_model_factory.cc
+++ b/chrome/browser/android/offline_pages/offline_page_model_factory.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/offline_pages/offline_page_metadata_store_sql.h"
-#include "components/offline_pages/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/android/offline_pages/offline_page_request_job.cc b/chrome/browser/android/offline_pages/offline_page_request_job.cc
index c9b50f5..fd252af 100644
--- a/chrome/browser/android/offline_pages/offline_page_request_job.cc
+++ b/chrome/browser/android/offline_pages/offline_page_request_job.cc
@@ -17,8 +17,8 @@
 #include "chrome/browser/android/offline_pages/offline_page_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "components/previews/core/previews_decider.h"
 #include "components/previews/core/previews_experiments.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_request_job_unittest.cc b/chrome/browser/android/offline_pages/offline_page_request_job_unittest.cc
index 3b977ecb..952f9cbe 100644
--- a/chrome/browser/android/offline_pages/offline_page_request_job_unittest.cc
+++ b/chrome/browser/android/offline_pages/offline_page_request_job_unittest.cc
@@ -23,8 +23,8 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_model_impl.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
 #include "components/previews/core/previews_decider.h"
 #include "components/previews/core/previews_experiments.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_tab_helper.cc b/chrome/browser/android/offline_pages/offline_page_tab_helper.cc
index c287db62..5ba8a72 100644
--- a/chrome/browser/android/offline_pages/offline_page_tab_helper.cc
+++ b/chrome/browser/android/offline_pages/offline_page_tab_helper.cc
@@ -9,7 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/android/offline_pages/offline_page_request_job.h"
 #include "chrome/browser/android/offline_pages/offline_page_utils.h"
-#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_item.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_tab_helper.h b/chrome/browser/android/offline_pages/offline_page_tab_helper.h
index a196fa5..8e64365 100644
--- a/chrome/browser/android/offline_pages/offline_page_tab_helper.h
+++ b/chrome/browser/android/offline_pages/offline_page_tab_helper.h
@@ -8,7 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_utils.cc b/chrome/browser/android/offline_pages/offline_page_utils.cc
index 485ed493..01ac502 100644
--- a/chrome/browser/android/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/android/offline_pages/offline_page_utils.cc
@@ -16,14 +16,14 @@
 #include "chrome/browser/android/offline_pages/offline_page_tab_helper.h"
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/android/tab_android.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/android/offline_pages/offline_page_utils.h b/chrome/browser/android/offline_pages/offline_page_utils.h
index 74b75cc..9e7e292 100644
--- a/chrome/browser/android/offline_pages/offline_page_utils.h
+++ b/chrome/browser/android/offline_pages/offline_page_utils.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "base/callback.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
 
 class GURL;
 
diff --git a/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc b/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
index ae269ca..2be4550e 100644
--- a/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
+++ b/chrome/browser/android/offline_pages/offline_page_utils_unittest.cc
@@ -23,14 +23,14 @@
 #include "chrome/browser/android/offline_pages/test_request_coordinator_builder.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/offline_pages/background/network_quality_provider_stub.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_test_archiver.h"
-#include "components/offline_pages/offline_page_test_store.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_test_archiver.h"
+#include "components/offline_pages/core/offline_page_test_store.h"
+#include "components/offline_pages/core/offline_page_types.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/filename_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/android/offline_pages/prerendering_loader.h b/chrome/browser/android/offline_pages/prerendering_loader.h
index 1dee3b9..027002e 100644
--- a/chrome/browser/android/offline_pages/prerendering_loader.h
+++ b/chrome/browser/android/offline_pages/prerendering_loader.h
@@ -9,8 +9,8 @@
 
 #include "base/callback.h"
 #include "chrome/browser/android/offline_pages/prerender_adapter.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/snapshot_controller.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/snapshot_controller.h"
 
 class GURL;
 
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner.cc b/chrome/browser/android/offline_pages/prerendering_offliner.cc
index 6773988..c2484e4 100644
--- a/chrome/browser/android/offline_pages/prerendering_offliner.cc
+++ b/chrome/browser/android/offline_pages/prerendering_offliner.cc
@@ -12,9 +12,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/content_settings/core/common/pref_names.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner.h b/chrome/browser/android/offline_pages/prerendering_offliner.h
index a259b06..45eca74 100644
--- a/chrome/browser/android/offline_pages/prerendering_offliner.h
+++ b/chrome/browser/android/offline_pages/prerendering_offliner.h
@@ -10,9 +10,9 @@
 #include "base/android/application_status_listener.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/android/offline_pages/prerendering_loader.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_types.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_types.h"
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner_factory.h b/chrome/browser/android/offline_pages/prerendering_offliner_factory.h
index f2adb78..e5ec88b83 100644
--- a/chrome/browser/android/offline_pages/prerendering_offliner_factory.h
+++ b/chrome/browser/android/offline_pages/prerendering_offliner_factory.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PRERENDERING_OFFLINER_FACTORY_H_
 
 #include "base/macros.h"
-#include "components/offline_pages/background/offliner_factory.h"
+#include "components/offline_pages/core/background/offliner_factory.h"
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/android/offline_pages/prerendering_offliner_unittest.cc b/chrome/browser/android/offline_pages/prerendering_offliner_unittest.cc
index ab0d6262..50eb9f75 100644
--- a/chrome/browser/android/offline_pages/prerendering_offliner_unittest.cc
+++ b/chrome/browser/android/offline_pages/prerendering_offliner_unittest.cc
@@ -15,9 +15,9 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/common/pref_names.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/web_contents_tester.h"
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper.cc b/chrome/browser/android/offline_pages/recent_tab_helper.cc
index 71a103a..9863079 100644
--- a/chrome/browser/android/offline_pages/recent_tab_helper.cc
+++ b/chrome/browser/android/offline_pages/recent_tab_helper.cc
@@ -20,11 +20,11 @@
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/offline_page_utils.h"
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper.h b/chrome/browser/android/offline_pages/recent_tab_helper.h
index 664c2c763..d150d90 100644
--- a/chrome/browser/android/offline_pages/recent_tab_helper.h
+++ b/chrome/browser/android/offline_pages/recent_tab_helper.h
@@ -9,9 +9,9 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/snapshot_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/snapshot_controller.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
index a79e9d1..922d807 100644
--- a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
+++ b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
@@ -13,10 +13,10 @@
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/test_offline_page_model_builder.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_test_archiver.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_test_archiver.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
@@ -95,13 +95,14 @@
     return task_runner_;
   }
 
-  size_t model_changed_count() { return model_changed_count_; }
+  size_t page_added_count() { return page_added_count_; }
   size_t model_removed_count() { return model_removed_count_; }
 
   // OfflinePageModel::Observer
   void OfflinePageModelLoaded(OfflinePageModel* model) override { }
-  void OfflinePageModelChanged(OfflinePageModel* model) override {
-    model_changed_count_++;
+  void OfflinePageAdded(OfflinePageModel* model,
+                        const OfflinePageItem& added_page) override {
+    page_added_count_++;
   }
   void OfflinePageDeleted(int64_t, const offline_pages::ClientId&) override {
     model_removed_count_++;
@@ -115,7 +116,7 @@
 
   RecentTabHelper* recent_tab_helper_;  // Owned by WebContents.
   OfflinePageModel* model_;  // Keyed service
-  size_t model_changed_count_;
+  size_t page_added_count_;
   size_t model_removed_count_;
   std::vector<OfflinePageItem> all_pages_;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
@@ -158,11 +159,10 @@
 RecentTabHelperTest::RecentTabHelperTest()
     : recent_tab_helper_(nullptr),
       model_(nullptr),
-      model_changed_count_(0),
+      page_added_count_(0),
       model_removed_count_(0),
       task_runner_(new base::TestMockTimeTaskRunner),
-      weak_ptr_factory_(this) {
-}
+      weak_ptr_factory_(this) {}
 
 void RecentTabHelperTest::SetUp() {
   content::RenderViewHostTestHarness::SetUp();
@@ -259,11 +259,11 @@
   recent_tab_helper()->DocumentAvailableInMainFrame();
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(0U, model_changed_count());
+  EXPECT_EQ(0U, page_added_count());
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   GetAllPages();
   EXPECT_EQ(1U, all_pages().size());
@@ -275,7 +275,7 @@
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(2U, model_changed_count());
+  EXPECT_EQ(2U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
   // the same page should be simply overridden.
   GetAllPages();
@@ -293,11 +293,11 @@
   recent_tab_helper()->DocumentAvailableInMainFrame();
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(0U, model_changed_count());
+  EXPECT_EQ(0U, page_added_count());
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   GetAllPages();
   EXPECT_EQ(1U, all_pages().size());
@@ -319,7 +319,7 @@
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   // The exact same page should still be available.
   GetAllPages();
@@ -336,11 +336,11 @@
   recent_tab_helper()->DocumentAvailableInMainFrame();
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(0U, model_changed_count());
+  EXPECT_EQ(0U, page_added_count());
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   GetAllPages();
   EXPECT_EQ(1U, all_pages().size());
@@ -353,7 +353,7 @@
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(2U, model_changed_count());
+  EXPECT_EQ(2U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
   // the same page should be simply overridden.
   GetAllPages();
@@ -371,11 +371,11 @@
   recent_tab_helper()->DocumentAvailableInMainFrame();
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(0U, model_changed_count());
+  EXPECT_EQ(0U, page_added_count());
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   GetAllPages();
   EXPECT_EQ(1U, all_pages().size());
@@ -397,7 +397,7 @@
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
   // the same page should be simply overridden.
   GetAllPages();
@@ -412,11 +412,11 @@
   recent_tab_helper()->DocumentAvailableInMainFrame();
   RunUntilIdle();
   EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(0U, model_changed_count());
+  EXPECT_EQ(0U, page_added_count());
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(1U, model_changed_count());
+  EXPECT_EQ(1U, page_added_count());
   EXPECT_EQ(0U, model_removed_count());
   GetAllPages();
   EXPECT_EQ(1U, all_pages().size());
@@ -428,7 +428,7 @@
   // Move the snapshot controller's time forward so it gets past timeouts.
   FastForwardSnapshotController();
   RunUntilIdle();
-  EXPECT_EQ(2U, model_changed_count());
+  EXPECT_EQ(2U, page_added_count());
   EXPECT_EQ(1U, model_removed_count());
   // the same page should be simply overridden.
   GetAllPages();
diff --git a/chrome/browser/android/offline_pages/request_coordinator_factory.cc b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
index 8cdcc94..a4b0244 100644
--- a/chrome/browser/android/offline_pages/request_coordinator_factory.cc
+++ b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
@@ -17,14 +17,14 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/request_queue_store_sql.h"
-#include "components/offline_pages/background/scheduler.h"
-#include "components/offline_pages/downloads/download_notifying_observer.h"
+#include "components/offline_pages/core/background/offliner_factory.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
+#include "components/offline_pages/core/background/scheduler.h"
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/nqe/network_quality_estimator.h"
 
diff --git a/chrome/browser/android/offline_pages/request_coordinator_factory_unittest.cc b/chrome/browser/android/offline_pages/request_coordinator_factory_unittest.cc
index 2121edf..2ff93c1 100644
--- a/chrome/browser/android/offline_pages/request_coordinator_factory_unittest.cc
+++ b/chrome/browser/android/offline_pages/request_coordinator_factory_unittest.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/offline_pages/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
 #include "content/public/browser/browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/android/offline_pages/test_offline_page_model_builder.cc b/chrome/browser/android/offline_pages/test_offline_page_model_builder.cc
index aad3940..de75394 100644
--- a/chrome/browser/android/offline_pages/test_offline_page_model_builder.cc
+++ b/chrome/browser/android/offline_pages/test_offline_page_model_builder.cc
@@ -10,8 +10,8 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/common/chrome_constants.h"
-#include "components/offline_pages/offline_page_model_impl.h"
-#include "components/offline_pages/offline_page_test_store.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_test_store.h"
 #include "content/public/browser/browser_context.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/android/offline_pages/test_request_coordinator_builder.cc b/chrome/browser/android/offline_pages/test_request_coordinator_builder.cc
index 138a631..58e45ba 100644
--- a/chrome/browser/android/offline_pages/test_request_coordinator_builder.cc
+++ b/chrome/browser/android/offline_pages/test_request_coordinator_builder.cc
@@ -6,13 +6,13 @@
 
 #include <utility>
 
-#include "components/offline_pages/background/network_quality_provider_stub.h"
-#include "components/offline_pages/background/offliner_factory_stub.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/scheduler_stub.h"
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+#include "components/offline_pages/core/background/offliner_factory_stub.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/scheduler_stub.h"
 #include "content/public/browser/browser_context.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc
index 95a3398..1a6a57f 100644
--- a/chrome/browser/android/preferences/pref_service_bridge.cc
+++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -648,7 +648,7 @@
     filter_builder->AddRegisterableDomain(domain);
   }
 
-  if (!excluding_domains.empty()) {
+  if (!excluding_domains.empty() || !ignoring_domains.empty()) {
     ImportantSitesUtil::RecordBlacklistedAndIgnoredImportantSites(
         GetOriginalProfile(), excluding_domains, excluding_domain_reasons,
         ignoring_domains, ignoring_domain_reasons);
diff --git a/chrome/browser/android/rappor/rappor_service_bridge.cc b/chrome/browser/android/rappor/rappor_service_bridge.cc
index a7a77b5..804f142d 100644
--- a/chrome/browser/android/rappor/rappor_service_bridge.cc
+++ b/chrome/browser/android/rappor/rappor_service_bridge.cc
@@ -6,7 +6,8 @@
 
 #include "base/android/jni_string.h"
 #include "chrome/browser/browser_process.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "jni/RapporServiceBridge_jni.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/android/seccomp_support_detector.cc b/chrome/browser/android/seccomp_support_detector.cc
index d5ee643..debcd332 100644
--- a/chrome/browser/android/seccomp_support_detector.cc
+++ b/chrome/browser/android/seccomp_support_detector.cc
@@ -10,8 +10,9 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "content/public/browser/browser_thread.h"
+#include "sandbox/sandbox_features.h"
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #endif
 
@@ -67,7 +68,7 @@
 void SeccompSupportDetector::DetectSeccomp() {
   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   bool prctl_supported = sandbox::SandboxBPF::SupportsSeccompSandbox(
       sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED);
 #else
diff --git a/chrome/browser/android/shortcut_info.cc b/chrome/browser/android/shortcut_info.cc
index 632eff18..ebfe836 100644
--- a/chrome/browser/android/shortcut_info.cc
+++ b/chrome/browser/android/shortcut_info.cc
@@ -50,10 +50,12 @@
   // Set the orientation based on the manifest value, if any.
   if (manifest.orientation != blink::WebScreenOrientationLockDefault) {
     // Ignore the orientation if the display mode is different from
-    // 'standalone'.
+    // 'standalone' or 'fullscreen'.
     // TODO(mlamouri): send a message to the developer console about this.
-    if (display == blink::WebDisplayModeStandalone)
+    if (display == blink::WebDisplayModeStandalone ||
+        display == blink::WebDisplayModeFullscreen) {
       orientation = manifest.orientation;
+    }
   }
 
   // Set the theme color based on the manifest value, if any.
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 1f58785..4ec05013 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -60,9 +60,9 @@
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/navigation_interception/navigation_params.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "components/sessions/content/content_live_tab.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/url_formatter/url_fixer.h"
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 0f916c63..6182fa39 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -59,6 +59,7 @@
       "//base",
       "//cc",
       "//components/rappor",
+      "//components/security_state/core",
       "//content/public/browser",
       "//content/public/common",
       "//device/vr",
@@ -94,7 +95,9 @@
       ":vr_common",
       "//base/test:run_all_unittests",
       "//base/test:test_support",
+      "//chrome/browser",
       "//testing/gtest",
+      "//third_party/WebKit/public:blink",
       "//ui/gfx/geometry",
     ]
   }
diff --git a/chrome/browser/android/vr_shell/ui_interface.cc b/chrome/browser/android/vr_shell/ui_interface.cc
index c498993..59e5c459 100644
--- a/chrome/browser/android/vr_shell/ui_interface.cc
+++ b/chrome/browser/android/vr_shell/ui_interface.cc
@@ -38,8 +38,13 @@
   FlushUpdates();
 }
 
-void UiInterface::SetSecureOrigin(bool secure) {
-  updates_.SetBoolean("secureOrigin", secure);
+void UiInterface::SetSecurityLevel(int level) {
+  updates_.SetInteger("securityLevel", level);
+  FlushUpdates();
+}
+
+void UiInterface::SetWebVRSecureOrigin(bool secure) {
+  updates_.SetBoolean("webVRSecureOrigin", secure);
   FlushUpdates();
 }
 
diff --git a/chrome/browser/android/vr_shell/ui_interface.h b/chrome/browser/android/vr_shell/ui_interface.h
index 9088ee4..6750535 100644
--- a/chrome/browser/android/vr_shell/ui_interface.h
+++ b/chrome/browser/android/vr_shell/ui_interface.h
@@ -35,8 +35,8 @@
   bool GetMenuMode() { return menu_mode_; }
   void SetFullscreen(bool enabled);
   bool GetFullscreen() { return fullscreen_; }
-
-  void SetSecureOrigin(bool secure);
+  void SetSecurityLevel(int level);
+  void SetWebVRSecureOrigin(bool secure);
   void SetLoading(bool loading);
   void SetURL(const GURL& url);
 
diff --git a/chrome/browser/android/vr_shell/vr_controller.cc b/chrome/browser/android/vr_shell/vr_controller.cc
index 0666eacd..6733d25 100644
--- a/chrome/browser/android/vr_shell/vr_controller.cc
+++ b/chrome/browser/android/vr_shell/vr_controller.cc
@@ -110,19 +110,19 @@
   return controller_state_->GetOrientation();
 }
 
-bool VrController::IsTouchDown() {
+bool VrController::TouchDownHappened() {
   return controller_state_->GetTouchDown();
 }
 
-bool VrController::IsTouchUp() {
+bool VrController::TouchUpHappened() {
   return controller_state_->GetTouchUp();
 }
 
-bool VrController::IsButtonDown(gvr::ControllerButton button) {
+bool VrController::ButtonDownHappened(gvr::ControllerButton button) {
   return controller_state_->GetButtonDown(button);
 }
 
-bool VrController::IsButtonUp(gvr::ControllerButton button) {
+bool VrController::ButtonUpHappened(gvr::ControllerButton button) {
   return controller_state_->GetButtonUp(button);
 }
 
@@ -149,8 +149,8 @@
   gvr::Vec2f position;
   position.x = TouchPosX();
   position.y = TouchPosY();
-  touch_info_->touch_up = IsTouchUp();
-  touch_info_->touch_down = IsTouchDown();
+  touch_info_->touch_up = TouchUpHappened();
+  touch_info_->touch_down = TouchDownHappened();
   touch_info_->is_touching = IsTouching();
   touch_info_->touch_point.position = position;
   Vector::ClampTouchpadPosition(&touch_info_->touch_point.position);
@@ -197,24 +197,16 @@
   UpdateGestureFromTouchInfo(gesture.get());
 
   if (gesture->type == WebInputEvent::Undefined &&
-      IsButtonDown(gvr::kControllerButtonClick)) {
+      ButtonUpHappened(gvr::kControllerButtonClick)) {
     gesture->type = WebInputEvent::GestureTapDown;
-    gesture->data.tapDown.width = 0;
-    gesture->data.tapDown.height = 0;
+    gesture->x = 0;
+    gesture->y = 0;
   }
   gesture->sourceDevice = blink::WebGestureDeviceTouchpad;
   gesture_list.push_back(std::move(gesture));
 
   if (gesture_list.back()->type == WebInputEvent::GestureScrollEnd) {
-    if (IsButtonDown(gvr::kControllerButtonClick)) {
-      std::unique_ptr<WebGestureEvent> tap_down(new WebGestureEvent());
-      tap_down->type = WebInputEvent::GestureTapDown;
-      tap_down->timeStampSeconds = gesture_list.back()->timeStampSeconds;
-      tap_down->sourceDevice = blink::WebGestureDeviceTouchpad;
-      tap_down->data.tapDown.width = 0;
-      tap_down->data.tapDown.height = 0;
-      gesture_list.push_back(std::move(tap_down));
-    } else {
+    if (!ButtonDownHappened(gvr::kControllerButtonClick)) {
       std::unique_ptr<WebGestureEvent> fling(new WebGestureEvent());
       fling->timeStampSeconds = gesture_list.back()->timeStampSeconds;
       fling->sourceDevice = blink::WebGestureDeviceTouchpad;
@@ -282,7 +274,7 @@
   // and the Controller's button is not down.
   if (UpdateCurrentTouchpoint() && touch_info_->is_touching &&
       !InSlop(touch_info_->touch_point.position) &&
-      !IsButtonDown(gvr::kControllerButtonClick)) {
+      !ButtonDownHappened(gvr::kControllerButtonClick)) {
     state_ = SCROLLING;
     gesture->type = WebInputEvent::GestureScrollBegin;
     UpdateGesture(gesture);
@@ -297,7 +289,7 @@
   // Update current touch point.
   bool touch_position_changed = UpdateCurrentTouchpoint();
   if (touch_info_->touch_up || !(touch_info_->is_touching) ||
-      IsButtonDown(gvr::kControllerButtonClick)) {
+      ButtonDownHappened(gvr::kControllerButtonClick)) {
     // Gesture ends.
     gesture->type = WebInputEvent::GestureScrollEnd;
     UpdateGesture(gesture);
diff --git a/chrome/browser/android/vr_shell/vr_controller.h b/chrome/browser/android/vr_shell/vr_controller.h
index 18a7326..7aaebec 100644
--- a/chrome/browser/android/vr_shell/vr_controller.h
+++ b/chrome/browser/android/vr_shell/vr_controller.h
@@ -50,12 +50,12 @@
 
   const gvr::Quatf Orientation();
 
-  bool IsTouchDown();
+  bool TouchDownHappened();
 
-  bool IsTouchUp();
+  bool TouchUpHappened();
 
-  bool IsButtonUp(gvr::ControllerButton button);
-  bool IsButtonDown(gvr::ControllerButton button);
+  bool ButtonUpHappened(gvr::ControllerButton button);
+  bool ButtonDownHappened(gvr::ControllerButton button);
 
   bool IsConnected();
 
diff --git a/chrome/browser/android/vr_shell/vr_input_manager.cc b/chrome/browser/android/vr_shell/vr_input_manager.cc
index e622603..92f5bee 100644
--- a/chrome/browser/android/vr_shell/vr_input_manager.cc
+++ b/chrome/browser/android/vr_shell/vr_input_manager.cc
@@ -56,9 +56,9 @@
     ForwardGestureEvent(gesture);
 
     // Generate and forward Tap
-    WebGestureEvent tap_event = MakeGestureEvent(
-        WebInputEvent::GestureTap, gesture.timeStampSeconds,
-        gesture.data.tapDown.width, gesture.data.tapDown.height);
+    WebGestureEvent tap_event =
+        MakeGestureEvent(WebInputEvent::GestureTap, gesture.timeStampSeconds,
+                         gesture.x, gesture.y);
     tap_event.data.tap.tapCount = 1;
     ForwardGestureEvent(tap_event);
   } else {
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index 229ef49..f9152a5 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -367,9 +367,10 @@
   controller_->UpdateState();
 
 #if defined(ENABLE_VR_SHELL)
-  // Note that button up/down state is transient, so IsButtonUp only returns
+  // Note that button up/down state is transient, so ButtonUpHappened only
+  // returns
   // true for a single frame (and we're guaranteed not to miss it).
-  if (controller_->IsButtonUp(
+  if (controller_->ButtonUpHappened(
           gvr::ControllerButton::GVR_CONTROLLER_BUTTON_APP)) {
     html_interface_->SetMenuMode(!html_interface_->GetMenuMode());
 
@@ -387,7 +388,8 @@
   if (html_interface_->GetMode() == UiInterface::Mode::WEB_VR) {
     // Process screen touch events for Cardboard button compatibility.
     // Also send tap events for controller "touchpad click" events.
-    if (touch_pending_ || controller_->IsButtonUp(
+    if (touch_pending_ ||
+        controller_->ButtonUpHappened(
             gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK)) {
       touch_pending_ = false;
       std::unique_ptr<WebGestureEvent> gesture(new WebGestureEvent());
@@ -395,8 +397,8 @@
       gesture->timeStampSeconds =
           (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
       gesture->type = WebInputEvent::GestureTapDown;
-      gesture->data.tapDown.width = 0;
-      gesture->data.tapDown.height = 0;
+      gesture->x = 0;
+      gesture->y = 0;
       SendGestureOnGL(CONTENT, std::move(gesture));
     }
 
@@ -513,8 +515,8 @@
   if (gesture->type == WebInputEvent::GestureScrollEnd) {
     CHECK(gesture_list.size() == 2);
     if (gesture_list.back()->type == WebInputEvent::GestureTapDown) {
-      gesture_list.back()->data.tapDown.width = pixel_x;
-      gesture_list.back()->data.tapDown.height = pixel_y;
+      gesture_list.back()->x = pixel_x;
+      gesture_list.back()->y = pixel_y;
       if (input_target != NONE)
         SendGestureOnGL(input_target, std::move(gesture_list.back()));
     } else if (gesture_list.back()->type == WebInputEvent::GestureFlingStart) {
@@ -550,8 +552,8 @@
           (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
     }
     event->type = WebInputEvent::GestureTapDown;
-    event->data.tapDown.width = pixel_x;
-    event->data.tapDown.height = pixel_y;
+    event->x = pixel_x;
+    event->y = pixel_y;
     SendGestureOnGL(current_input_target_, std::move(event));
   }
 }
@@ -982,7 +984,8 @@
 }
 
 void VrShell::SetWebVRSecureOrigin(bool secure_origin) {
-  html_interface_->SetSecureOrigin(secure_origin);
+  // TODO(cjgrant): Align this state with the logic that drives the omnibox.
+  html_interface_->SetWebVRSecureOrigin(secure_origin);
 }
 
 void VrShell::SubmitWebVRFrame() {}
diff --git a/chrome/browser/android/vr_shell/vr_usage_monitor.cc b/chrome/browser/android/vr_shell/vr_usage_monitor.cc
index 3b52df039..8bfae60 100644
--- a/chrome/browser/android/vr_shell/vr_usage_monitor.cc
+++ b/chrome/browser/android/vr_shell/vr_usage_monitor.cc
@@ -6,7 +6,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/browser_process.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace vr_shell {
diff --git a/chrome/browser/android/vr_shell/vr_web_contents_observer.cc b/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
index dd39ac5..48cddbc 100644
--- a/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
+++ b/chrome/browser/android/vr_shell/vr_web_contents_observer.cc
@@ -6,6 +6,8 @@
 
 #include "chrome/browser/android/vr_shell/ui_interface.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
+#include "chrome/browser/ssl/security_state_tab_helper.h"
+#include "components/security_state/core/security_state.h"
 #include "content/public/browser/navigation_handle.h"
 
 namespace vr_shell {
@@ -15,7 +17,10 @@
                                              VrShell* vr_shell)
     : WebContentsObserver(web_contents),
       ui_interface_(ui_interface),
-      vr_shell_(vr_shell) {}
+      vr_shell_(vr_shell) {
+  ui_interface_->SetURL(web_contents->GetVisibleURL());
+  SetSecurityLevel();
+}
 
 VrWebContentsObserver::~VrWebContentsObserver() {}
 
@@ -33,17 +38,34 @@
 
 void VrWebContentsObserver::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
-  ui_interface_->SetURL(navigation_handle->GetURL());
+  if (navigation_handle->IsInMainFrame()) {
+    ui_interface_->SetURL(navigation_handle->GetURL());
+    SetSecurityLevel();
+  }
 }
 
 void VrWebContentsObserver::DidRedirectNavigation(
     content::NavigationHandle* navigation_handle) {
-  ui_interface_->SetURL(navigation_handle->GetURL());
+  if (navigation_handle->IsInMainFrame()) {
+    ui_interface_->SetURL(navigation_handle->GetURL());
+    SetSecurityLevel();
+  }
 }
 
 void VrWebContentsObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  ui_interface_->SetURL(navigation_handle->GetURL());
+  if (navigation_handle->IsInMainFrame()) {
+    ui_interface_->SetURL(navigation_handle->GetURL());
+    SetSecurityLevel();
+  }
+}
+
+void VrWebContentsObserver::SetSecurityLevel() {
+  const auto* helper = SecurityStateTabHelper::FromWebContents(web_contents());
+  DCHECK(helper);
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  ui_interface_->SetSecurityLevel(security_info.security_level);
 }
 
 void VrWebContentsObserver::DidToggleFullscreenModeForTab(
diff --git a/chrome/browser/android/vr_shell/vr_web_contents_observer.h b/chrome/browser/android/vr_shell/vr_web_contents_observer.h
index d82b1940..9b1a5943 100644
--- a/chrome/browser/android/vr_shell/vr_web_contents_observer.h
+++ b/chrome/browser/android/vr_shell/vr_web_contents_observer.h
@@ -42,6 +42,8 @@
   void WebContentsDestroyed() override;
   void WasHidden() override;
 
+  void SetSecurityLevel();
+
   // This class does not own the UI interface.
   UiInterface* ui_interface_;
   VrShell* vr_shell_;
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index c6da1e2d..aef343c 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -88,7 +88,7 @@
   info.best_icon_url = best_icon_url;
   info.manifest_url = web_manifest_url;
 
-  base::android::JavaArrayOfByteArrayToStringVector(
+  base::android::AppendJavaStringArrayToStringVector(
       env, java_icon_urls.obj(), &info.icon_urls);
 
   gfx::JavaBitmap java_bitmap_lock(java_best_icon_bitmap);
diff --git a/chrome/browser/apps/app_browsertest.cc b/chrome/browser/apps/app_browsertest.cc
index 6a55b63..823407a 100644
--- a/chrome/browser/apps/app_browsertest.cc
+++ b/chrome/browser/apps/app_browsertest.cc
@@ -443,11 +443,7 @@
                        DisallowBackgroundPageNavigation) {
   // The test will try to open in app urls and external urls via clicking links
   // and window.open(). Only the external urls should succeed in opening tabs.
-  // TODO(lazyboy): non-external urls also succeed right now because of
-  // http://crbug.com/585570 not being fixed. Fix the test once the bug is
-  // fixed.
-  // const size_t kExpectedNumberOfTabs = 2u;
-  const size_t kExpectedNumberOfTabs = 6u;
+  const size_t kExpectedNumberOfTabs = 2u;
   TabsAddedNotificationObserver observer(kExpectedNumberOfTabs);
   ASSERT_TRUE(RunPlatformAppTest("platform_apps/background_page_navigation")) <<
       message_;
diff --git a/chrome/browser/apps/app_url_redirector_browsertest.cc b/chrome/browser/apps/app_url_redirector_browsertest.cc
index b4ca00c..838d257 100644
--- a/chrome/browser/apps/app_url_redirector_browsertest.cc
+++ b/chrome/browser/apps/app_url_redirector_browsertest.cc
@@ -8,6 +8,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
@@ -23,6 +24,7 @@
 class PlatformAppUrlRedirectorBrowserTest : public PlatformAppBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override;
+  void SetUpOnMainThread() override;
 
  protected:
   // Performs the following sequence:
@@ -118,8 +120,12 @@
     base::CommandLine* command_line) {
   PlatformAppBrowserTest::SetUpCommandLine(command_line);
   command_line->AppendSwitch(::switches::kDisablePopupBlocking);
-  command_line->AppendSwitchASCII(::switches::kPrerenderMode,
-                                  ::switches::kPrerenderModeSwitchValueEnabled);
+}
+
+void PlatformAppUrlRedirectorBrowserTest::SetUpOnMainThread() {
+  PlatformAppBrowserTest::SetUpOnMainThread();
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
 }
 
 // TODO(sergeygs): Factor out common functionality from TestXyz,
diff --git a/chrome/browser/apps/drive/drive_app_mapping.cc b/chrome/browser/apps/drive/drive_app_mapping.cc
index 73b08b5..ea8fb0f 100644
--- a/chrome/browser/apps/drive/drive_app_mapping.cc
+++ b/chrome/browser/apps/drive/drive_app_mapping.cc
@@ -84,7 +84,7 @@
        it.Advance()) {
     const base::DictionaryValue* info_dict;
     std::string value_string;
-    DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+    DCHECK(it.value().IsType(base::Value::Type::DICTIONARY));
     if (it.value().GetAsDictionary(&info_dict) &&
         info_dict->GetStringWithoutPathExpansion(kKeyChromeApp,
                                                  &value_string) &&
@@ -104,7 +104,7 @@
     const base::DictionaryValue* info_dict;
     std::string value_string;
     bool generated = false;
-    DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+    DCHECK(it.value().IsType(base::Value::Type::DICTIONARY));
     if (it.value().GetAsDictionary(&info_dict) &&
         info_dict->GetStringWithoutPathExpansion(kKeyChromeApp,
                                                  &value_string) &&
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 9aa96dba..99e2628 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -56,12 +56,9 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
-
-#if defined(ENABLE_NOTIFICATIONS)
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
-#endif
 
 using content::SiteInstance;
 using content::WebContents;
@@ -80,12 +77,10 @@
       g_browser_process->notification_ui_manager();
   bool cancelled = notification_ui_manager->CancelById(balloon_id, profile_id);
   if (cancelled) {
-#if defined(ENABLE_NOTIFICATIONS)
     // TODO(dewittj): Add this functionality to the notification UI manager's
     // API.
     g_browser_process->message_center()->SetVisibility(
         message_center::VISIBILITY_TRANSIENT);
-#endif
   }
 }
 
@@ -158,7 +153,6 @@
   DISALLOW_COPY_AND_ASSIGN(CrashNotificationDelegate);
 };
 
-#if defined(ENABLE_NOTIFICATIONS)
 void NotificationImageReady(
     const std::string extension_name,
     const base::string16 message,
@@ -192,12 +186,10 @@
 
   g_browser_process->notification_ui_manager()->Add(notification, profile);
 }
-#endif
 
 // Show a popup notification balloon with a crash message for a given app/
 // extension.
 void ShowBalloon(const Extension* extension, Profile* profile) {
-#if defined(ENABLE_NOTIFICATIONS)
   const base::string16 message = l10n_util::GetStringFUTF16(
       extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
                             IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE,
@@ -220,7 +212,6 @@
           message,
           make_scoped_refptr(new CrashNotificationDelegate(profile, extension)),
           profile));
-#endif
 }
 
 void ReloadExtension(const std::string& extension_id, Profile* profile) {
diff --git a/chrome/browser/background/background_contents_service_unittest.cc b/chrome/browser/background/background_contents_service_unittest.cc
index ee49d79e..0c3db32 100644
--- a/chrome/browser/background/background_contents_service_unittest.cc
+++ b/chrome/browser/background/background_contents_service_unittest.cc
@@ -15,6 +15,8 @@
 #include "chrome/browser/background/background_contents.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/notifications/message_center_notification_manager.h"
+#include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/common/pref_names.h"
@@ -30,15 +32,10 @@
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
-#include "url/gurl.h"
-
-#if defined(ENABLE_NOTIFICATIONS)
-#include "chrome/browser/notifications/message_center_notification_manager.h"
-#include "chrome/browser/notifications/notification.h"
 #include "ui/message_center/fake_message_center_tray_delegate.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_observer.h"
-#endif
+#include "url/gurl.h"
 
 class BackgroundContentsServiceTest : public testing::Test {
  public:
@@ -126,7 +123,6 @@
   Profile* profile_;
 };
 
-#if defined(ENABLE_NOTIFICATIONS)
 // Wait for the notification created.
 class NotificationWaiter : public message_center::MessageCenterObserver {
  public:
@@ -226,7 +222,6 @@
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundContentsServiceNotificationTest);
 };
-#endif  // ENABLE_NOTIFICATIONS
 
 TEST_F(BackgroundContentsServiceTest, Create) {
   // Check for creation and leaks.
@@ -360,7 +355,6 @@
   EXPECT_EQ(url2.spec(), GetPrefURLForApp(&profile, contents2->appid()));
 }
 
-#if defined(ENABLE_NOTIFICATIONS)
 TEST_F(BackgroundContentsServiceNotificationTest, TestShowBalloon) {
   scoped_refptr<extensions::Extension> extension =
       extension_test_util::LoadManifest("image_loading_tracker", "app.json");
@@ -420,4 +414,3 @@
       message_center->GetVisibleNotifications();
   ASSERT_EQ(1u, notifications.size());
 }
-#endif
diff --git a/chrome/browser/background_sync/background_sync_controller_impl.cc b/chrome/browser/background_sync/background_sync_controller_impl.cc
index 3720cff..b386988 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl.cc
+++ b/chrome/browser/background_sync/background_sync_controller_impl.cc
@@ -8,7 +8,8 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/background_sync_parameters.h"
 
@@ -109,7 +110,7 @@
     return;
 
   rappor::SampleDomainAndRegistryFromGURL(
-      GetRapporService(), "BackgroundSync.Register.Origin", origin);
+      GetRapporServiceImpl(), "BackgroundSync.Register.Origin", origin);
 }
 
 void BackgroundSyncControllerImpl::RunInBackground(bool enabled,
@@ -126,6 +127,7 @@
 #endif
 }
 
-rappor::RapporService* BackgroundSyncControllerImpl::GetRapporService() {
+rappor::RapporServiceImpl*
+BackgroundSyncControllerImpl::GetRapporServiceImpl() {
   return g_browser_process->rappor_service();
 }
diff --git a/chrome/browser/background_sync/background_sync_controller_impl.h b/chrome/browser/background_sync/background_sync_controller_impl.h
index 97924b9..564af71 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl.h
+++ b/chrome/browser/background_sync/background_sync_controller_impl.h
@@ -18,7 +18,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 class Profile;
@@ -45,7 +45,7 @@
 
  protected:
   // Virtual for testing.
-  virtual rappor::RapporService* GetRapporService();
+  virtual rappor::RapporServiceImpl* GetRapporServiceImpl();
 
  private:
   Profile* profile_;  // This object is owned by profile_.
diff --git a/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc b/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
index 93d1b78..7e32abe2 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
+++ b/chrome/browser/background_sync/background_sync_controller_impl_unittest.cc
@@ -28,16 +28,19 @@
 
 class TestBackgroundSyncControllerImpl : public BackgroundSyncControllerImpl {
  public:
-  TestBackgroundSyncControllerImpl(Profile* profile,
-                                   rappor::TestRapporService* rappor_service)
+  TestBackgroundSyncControllerImpl(
+      Profile* profile,
+      rappor::TestRapporServiceImpl* rappor_service)
       : BackgroundSyncControllerImpl(profile),
         rappor_service_(rappor_service) {}
 
  protected:
-  rappor::RapporService* GetRapporService() override { return rappor_service_; }
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override {
+    return rappor_service_;
+  }
 
  private:
-  rappor::TestRapporService* rappor_service_;
+  rappor::TestRapporServiceImpl* rappor_service_;
 
   DISALLOW_COPY_AND_ASSIGN(TestBackgroundSyncControllerImpl);
 };
@@ -65,7 +68,7 @@
 
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
-  rappor::TestRapporService rappor_service_;
+  rappor::TestRapporServiceImpl rappor_service_;
   std::unique_ptr<TestBackgroundSyncControllerImpl> controller_;
   std::unique_ptr<base::FieldTrialList> field_trial_list_;
 
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index b89fce3..bad0034 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -17,7 +17,8 @@
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/banners/app_banner_settings_helper.cc b/chrome/browser/banners/app_banner_settings_helper.cc
index c93f08a..75e02fe 100644
--- a/chrome/browser/banners/app_banner_settings_helper.cc
+++ b/chrome/browser/banners/app_banner_settings_helper.cc
@@ -24,7 +24,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/escape.h"
@@ -354,7 +355,7 @@
   // the times are converted to local dates.
   base::Time date = BucketTimeToResolution(time, gMinimumMinutesBetweenVisits);
   for (auto it = could_show_list->begin(); it != could_show_list->end();) {
-    if ((*it)->IsType(base::Value::TYPE_DICTIONARY)) {
+    if ((*it)->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* internal_value;
       double internal_date;
       (*it)->GetAsDictionary(&internal_value);
@@ -496,7 +497,7 @@
     return result;
 
   for (const auto& value : *could_show_list) {
-    if (value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (value->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* internal_value;
       double internal_date = 0;
       value->GetAsDictionary(&internal_value);
@@ -581,7 +582,7 @@
   // homescreen recently, return true.
   for (base::DictionaryValue::Iterator it(*origin_dict); !it.IsAtEnd();
        it.Advance()) {
-    if (it.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    if (it.value().IsType(base::Value::Type::DICTIONARY)) {
       const base::DictionaryValue* value;
       it.value().GetAsDictionary(&value);
 
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc
index ab3f3447..77d83cb 100644
--- a/chrome/browser/bookmarks/bookmark_html_writer.cc
+++ b/chrome/browser/bookmarks/bookmark_html_writer.cc
@@ -113,10 +113,10 @@
 
     base::Value* roots = NULL;
     if (!Write(kHeader) ||
-        bookmarks_->GetType() != base::Value::TYPE_DICTIONARY ||
+        bookmarks_->GetType() != base::Value::Type::DICTIONARY ||
         !static_cast<base::DictionaryValue*>(bookmarks_.get())->Get(
             BookmarkCodec::kRootsKey, &roots) ||
-        roots->GetType() != base::Value::TYPE_DICTIONARY) {
+        roots->GetType() != base::Value::Type::DICTIONARY) {
       NOTREACHED();
       return;
     }
@@ -128,13 +128,13 @@
     base::Value* mobile_folder_value = NULL;
     if (!roots_d_value->Get(BookmarkCodec::kRootFolderNameKey,
                             &root_folder_value) ||
-        root_folder_value->GetType() != base::Value::TYPE_DICTIONARY ||
+        root_folder_value->GetType() != base::Value::Type::DICTIONARY ||
         !roots_d_value->Get(BookmarkCodec::kOtherBookmarkFolderNameKey,
                             &other_folder_value) ||
-        other_folder_value->GetType() != base::Value::TYPE_DICTIONARY ||
+        other_folder_value->GetType() != base::Value::Type::DICTIONARY ||
         !roots_d_value->Get(BookmarkCodec::kMobileBookmarkFolderNameKey,
                             &mobile_folder_value) ||
-        mobile_folder_value->GetType() != base::Value::TYPE_DICTIONARY) {
+        mobile_folder_value->GetType() != base::Value::Type::DICTIONARY) {
       NOTREACHED();
       return;  // Invalid type for root folder and/or other folder.
     }
@@ -306,7 +306,7 @@
     if (!value.GetString(BookmarkCodec::kDateModifiedKey,
                          &last_modified_date) ||
         !value.Get(BookmarkCodec::kChildrenKey, &child_values) ||
-        child_values->GetType() != base::Value::TYPE_LIST) {
+        child_values->GetType() != base::Value::Type::LIST) {
       NOTREACHED();
       return false;
     }
@@ -346,7 +346,7 @@
     for (size_t i = 0; i < children->GetSize(); ++i) {
       const base::Value* child_value;
       if (!children->Get(i, &child_value) ||
-          child_value->GetType() != base::Value::TYPE_DICTIONARY) {
+          child_value->GetType() != base::Value::Type::DICTIONARY) {
         NOTREACHED();
         return false;
       }
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.cc b/chrome/browser/bookmarks/chrome_bookmark_client.cc
index cd45fb5..48f8a8b 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.cc
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.cc
@@ -59,30 +59,29 @@
       page_url, type, callback, tracker);
 }
 
-bool ChromeBookmarkClient::SupportsTypedCountForNodes() {
+bool ChromeBookmarkClient::SupportsTypedCountForUrls() {
   return true;
 }
 
-void ChromeBookmarkClient::GetTypedCountForNodes(
-    const NodeSet& nodes,
-    NodeTypedCountPairs* node_typed_count_pairs) {
+void ChromeBookmarkClient::GetTypedCountForUrls(
+    UrlTypedCountMap* url_typed_count_map) {
   history::HistoryService* history_service =
       HistoryServiceFactory::GetForProfileIfExists(
           profile_, ServiceAccessType::EXPLICIT_ACCESS);
   history::URLDatabase* url_db =
       history_service ? history_service->InMemoryDatabase() : nullptr;
-  for (NodeSet::const_iterator i = nodes.begin(); i != nodes.end(); ++i) {
+  for (auto& url_typed_count_pair : *url_typed_count_map) {
     int typed_count = 0;
 
     // If |url_db| is the InMemoryDatabase, it might not cache all URLRows, but
     // it guarantees to contain those with |typed_count| > 0. Thus, if we cannot
     // fetch the URLRow, it is safe to assume that its |typed_count| is 0.
-    history::URLRow url;
-    if (url_db && url_db->GetRowForURL((*i)->url(), &url))
-      typed_count = url.typed_count();
+    history::URLRow url_row;
+    const GURL* url = url_typed_count_pair.first;
+    if (url_db && url && url_db->GetRowForURL(*url, &url_row))
+      typed_count = url_row.typed_count();
 
-    NodeTypedCountPair pair(*i, typed_count);
-    node_typed_count_pairs->push_back(pair);
+    url_typed_count_pair.second = typed_count;
   }
 }
 
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.h b/chrome/browser/bookmarks/chrome_bookmark_client.h
index 5d1debb5..6c57c860 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.h
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.h
@@ -46,10 +46,8 @@
       favicon_base::IconType type,
       const favicon_base::FaviconImageCallback& callback,
       base::CancelableTaskTracker* tracker) override;
-  bool SupportsTypedCountForNodes() override;
-  void GetTypedCountForNodes(
-      const NodeSet& nodes,
-      NodeTypedCountPairs* node_typed_count_pairs) override;
+  bool SupportsTypedCountForUrls() override;
+  void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map) override;
   bool IsPermanentNodeVisible(
       const bookmarks::BookmarkPermanentNode* node) override;
   void RecordAction(const base::UserMetricsAction& action) override;
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 189a1a8..3801f73 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -111,7 +111,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace safe_browsing {
@@ -141,7 +141,7 @@
 
   // Services: any of these getters may return NULL
   virtual metrics::MetricsService* metrics_service() = 0;
-  virtual rappor::RapporService* rappor_service() = 0;
+  virtual rappor::RapporServiceImpl* rappor_service() = 0;
   virtual ProfileManager* profile_manager() = 0;
   virtual PrefService* local_state() = 0;
   virtual net::URLRequestContextGetter* system_request_context() = 0;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 5be550d8..ace2dd11 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -98,7 +98,8 @@
 #include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/safe_json/safe_json_parser.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/subresource_filter/content/browser/content_ruleset_service_delegate.h"
@@ -534,9 +535,9 @@
   return GetMetricsServicesManager()->GetMetricsService();
 }
 
-rappor::RapporService* BrowserProcessImpl::rappor_service() {
+rappor::RapporServiceImpl* BrowserProcessImpl::rappor_service() {
   DCHECK(CalledOnValidThread());
-  return GetMetricsServicesManager()->GetRapporService();
+  return GetMetricsServicesManager()->GetRapporServiceImpl();
 }
 
 IOThread* BrowserProcessImpl::io_thread() {
@@ -1139,7 +1140,7 @@
 }
 
 void BrowserProcessImpl::CreateNotificationPlatformBridge() {
-#if (defined(OS_ANDROID) || defined(OS_MACOSX)) && defined(ENABLE_NOTIFICATIONS)
+#if (defined(OS_ANDROID) || defined(OS_MACOSX))
   DCHECK(!notification_bridge_);
   notification_bridge_.reset(NotificationPlatformBridge::Create());
   created_notification_bridge_ = true;
@@ -1149,7 +1150,7 @@
 void BrowserProcessImpl::CreateNotificationUIManager() {
 // Android does not use the NotificationUIManager anymore
 // All notification traffic is routed through NotificationPlatformBridge.
-#if defined(ENABLE_NOTIFICATIONS) && !defined(OS_ANDROID)
+#if !defined(OS_ANDROID)
   DCHECK(!notification_ui_manager_);
   notification_ui_manager_.reset(NotificationUIManager::Create());
   created_notification_ui_manager_ = true;
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 81c45e5..562cb903 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -95,7 +95,7 @@
   metrics_services_manager::MetricsServicesManager* GetMetricsServicesManager()
       override;
   metrics::MetricsService* metrics_service() override;
-  rappor::RapporService* rappor_service() override;
+  rappor::RapporServiceImpl* rappor_service() override;
   IOThread* io_thread() override;
   WatchDogThread* watchdog_thread() override;
   ProfileManager* profile_manager() override;
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 3ed79a4..cc955a2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -96,8 +96,8 @@
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/webapps/webapp_registry.h"
 #include "chrome/browser/precache/precache_manager_factory.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "components/precache/content/precache_manager.h"
 #endif
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index 0f4e795..07381b0 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -26,7 +26,7 @@
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "components/prefs/pref_member.h"
 #include "components/search_engines/template_url_service.h"
 #include "media/media_features.h"
diff --git a/chrome/browser/browsing_data/registrable_domain_filter_builder.cc b/chrome/browser/browsing_data/registrable_domain_filter_builder.cc
index 2fe4b83..7ad7b69b 100644
--- a/chrome/browser/browsing_data/registrable_domain_filter_builder.cc
+++ b/chrome/browser/browsing_data/registrable_domain_filter_builder.cc
@@ -35,6 +35,88 @@
 // 2. IsSubdomainOfARegistrableDomain(domain)      - e.g. www.google.com
 // 3. GetDomainAndRegistry(domain, _) == ""        - e.g. localhost, 127.0.0.1
 
+
+// True if the domain of |url| is in the whitelist, or isn't in the blacklist.
+// The whitelist or blacklist is represented as |registerable_domains|
+// and |mode|.
+bool MatchesURL(
+    const std::set<std::string>& registerable_domains,
+    BrowsingDataFilterBuilder::Mode mode,
+    const GURL& url) {
+  std::string url_registerable_domain =
+      GetDomainAndRegistry(url, INCLUDE_PRIVATE_REGISTRIES);
+  return (registerable_domains.find(
+              url_registerable_domain != "" ? url_registerable_domain
+                                            : url.host()) !=
+          registerable_domains.end()) ==
+         (mode == BrowsingDataFilterBuilder::WHITELIST);
+}
+
+// True if the pattern something in the whitelist, or doesn't match something
+// in the blacklist.
+// The whitelist or blacklist is represented as |patterns|,  and |mode|.
+bool MatchesWebsiteSettingsPattern(
+    const std::vector<ContentSettingsPattern>& domain_patterns,
+    BrowsingDataFilterBuilder::Mode mode,
+    const ContentSettingsPattern& pattern) {
+  for (const ContentSettingsPattern& domain : domain_patterns) {
+    DCHECK(domain.IsValid());
+    Relation relation = pattern.Compare(domain);
+    if (relation == Relation::IDENTITY || relation == Relation::PREDECESSOR)
+      return mode == BrowsingDataFilterBuilder::WHITELIST;
+  }
+  return mode != BrowsingDataFilterBuilder::WHITELIST;
+}
+
+// True if no domains can see the given cookie and we're a blacklist, or any
+// domains can see the cookie and we're a whitelist.
+// The whitelist or blacklist is represented as |domains_and_ips| and |mode|.
+bool MatchesCookieForRegisterableDomainsAndIPs(
+    const std::set<std::string>& domains_and_ips,
+    BrowsingDataFilterBuilder::Mode mode,
+    const net::CanonicalCookie& cookie) {
+  if (domains_and_ips.empty())
+    return mode == BrowsingDataFilterBuilder::BLACKLIST;
+  std::string cookie_domain = cookie.Domain();
+  if (cookie.IsDomainCookie())
+    cookie_domain = cookie_domain.substr(1);
+  std::string parsed_cookie_domain =
+      GetDomainAndRegistry(cookie_domain, INCLUDE_PRIVATE_REGISTRIES);
+  // This means we're an IP address or an internal hostname.
+  if (parsed_cookie_domain.empty())
+    parsed_cookie_domain = cookie_domain;
+  return (mode == BrowsingDataFilterBuilder::WHITELIST) ==
+      (domains_and_ips.find(parsed_cookie_domain) != domains_and_ips.end());
+}
+
+// True if none of the supplied domains matches this Channel ID's server ID
+// and we're a blacklist, or one of them does and we're a whitelist.
+// The whitelist or blacklist is represented as |domains_and_ips| and |mode|.
+bool MatchesChannelIDForRegisterableDomainsAndIPs(
+    const std::set<std::string>& domains_and_ips,
+    BrowsingDataFilterBuilder::Mode mode,
+    const std::string& channel_id_server_id) {
+  return ((mode == BrowsingDataFilterBuilder::WHITELIST) ==
+      (domains_and_ips.find(channel_id_server_id) != domains_and_ips.end()));
+}
+
+// True if none of the supplied domains matches this plugin's |site| and we're
+// a blacklist, or one of them does and we're a whitelist. The whitelist or
+// blacklist is represented by |domains_and_ips| and |mode|.
+bool MatchesPluginSiteForRegisterableDomainsAndIPs(
+    const std::set<std::string>& domains_and_ips,
+    BrowsingDataFilterBuilder::Mode mode,
+    const std::string& site) {
+  // If |site| is a third- or lower-level domain, find the corresponding eTLD+1.
+  std::string domain_or_ip =
+      GetDomainAndRegistry(site, INCLUDE_PRIVATE_REGISTRIES);
+  if (domain_or_ip.empty())
+    domain_or_ip = site;
+
+  return ((mode == BrowsingDataFilterBuilder::WHITELIST) ==
+      (domains_and_ips.find(domain_or_ip) != domains_and_ips.end()));
+}
+
 }  // namespace
 
 RegistrableDomainFilterBuilder::RegistrableDomainFilterBuilder(Mode mode)
@@ -48,24 +130,21 @@
   // We check that the domain we're given is actually a eTLD+1, an IP address,
   // or an internal hostname.
   DCHECK(!IsSubdomainOfARegistrableDomain(domain));
-  domain_list_.insert(domain);
+  domains_.insert(domain);
 }
 
 base::Callback<bool(const GURL&)>
 RegistrableDomainFilterBuilder::BuildGeneralFilter() const {
-  std::set<std::string>* domains = new std::set<std::string>(domain_list_);
-  return base::Bind(&RegistrableDomainFilterBuilder::MatchesURL,
-                    base::Owned(domains), mode());
+  return base::BindRepeating(MatchesURL, domains_, mode());
 }
 
 base::Callback<bool(const ContentSettingsPattern& pattern)>
 RegistrableDomainFilterBuilder
     ::BuildWebsiteSettingsPatternMatchesFilter() const {
-  std::vector<ContentSettingsPattern>* patterns_from_domains =
-      new std::vector<ContentSettingsPattern>();
-  patterns_from_domains->reserve(domain_list_.size());
+  std::vector<ContentSettingsPattern> patterns_from_domains;
+  patterns_from_domains.reserve(domains_.size());
 
-  for (const std::string& domain : domain_list_) {
+  for (const std::string& domain : domains_) {
     std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
         ContentSettingsPattern::CreateBuilder(/* use_legacy_validate */ false));
     builder->WithSchemeWildcard()
@@ -75,126 +154,40 @@
     if (IsRegistrableDomain(domain))
       builder->WithDomainWildcard();
 
-    patterns_from_domains->push_back(builder->Build());
+    patterns_from_domains.push_back(builder->Build());
   }
 
-  for (const ContentSettingsPattern& domain : *patterns_from_domains) {
+  for (const ContentSettingsPattern& domain : patterns_from_domains) {
     DCHECK(domain.IsValid());
   }
 
-  return base::Bind(
-      &RegistrableDomainFilterBuilder::MatchesWebsiteSettingsPattern,
-      base::Owned(patterns_from_domains), mode());
+  return base::BindRepeating(&MatchesWebsiteSettingsPattern,
+                             std::move(patterns_from_domains), mode());
 }
 
 base::Callback<bool(const net::CanonicalCookie& cookie)>
 RegistrableDomainFilterBuilder::BuildCookieFilter() const {
-  std::set<std::string>* domains_and_ips =
-      new std::set<std::string>(domain_list_);
-  return base::Bind(
-      &RegistrableDomainFilterBuilder
-          ::MatchesCookieForRegisterableDomainsAndIPs,
-      base::Owned(domains_and_ips), mode());
+  return base::BindRepeating(&MatchesCookieForRegisterableDomainsAndIPs,
+                             domains_, mode());
 }
 
 base::Callback<bool(const std::string& cookie)>
 RegistrableDomainFilterBuilder::BuildChannelIDFilter() const {
-  std::set<std::string>* domains_and_ips =
-      new std::set<std::string>(domain_list_);
-  return base::Bind(
-      &RegistrableDomainFilterBuilder
-          ::MatchesChannelIDForRegisterableDomainsAndIPs,
-      base::Owned(domains_and_ips), mode());
+  return base::BindRepeating(&MatchesChannelIDForRegisterableDomainsAndIPs,
+                             domains_, mode());
 }
 
 base::Callback<bool(const std::string& site)>
 RegistrableDomainFilterBuilder::BuildPluginFilter() const {
-  std::set<std::string>* domains_and_ips =
-      new std::set<std::string>(domain_list_);
-  return base::Bind(
-      &RegistrableDomainFilterBuilder
-          ::MatchesPluginSiteForRegisterableDomainsAndIPs,
-      base::Owned(domains_and_ips), mode());
+  return base::BindRepeating(&MatchesPluginSiteForRegisterableDomainsAndIPs,
+                             domains_, mode());
 }
 
 bool RegistrableDomainFilterBuilder::operator==(
     const RegistrableDomainFilterBuilder& other) const {
-  return domain_list_ == other.domain_list_ && mode() == other.mode();
+  return domains_ == other.domains_ && mode() == other.mode();
 }
 
 bool RegistrableDomainFilterBuilder::IsEmpty() const {
-  return domain_list_.empty();
-}
-
-// static
-bool RegistrableDomainFilterBuilder::MatchesURL(
-    std::set<std::string>* registerable_domains,
-    Mode mode,
-    const GURL& url) {
-  std::string url_registerable_domain =
-      GetDomainAndRegistry(url, INCLUDE_PRIVATE_REGISTRIES);
-  return (registerable_domains->find(
-              url_registerable_domain != "" ? url_registerable_domain
-                                            : url.host()) !=
-          registerable_domains->end()) ==
-         (mode == WHITELIST);
-}
-
-// static
-bool RegistrableDomainFilterBuilder::MatchesWebsiteSettingsPattern(
-    std::vector<ContentSettingsPattern>* domain_patterns,
-    Mode mode,
-    const ContentSettingsPattern& pattern) {
-  for (const ContentSettingsPattern& domain : *domain_patterns) {
-    DCHECK(domain.IsValid());
-    Relation relation = pattern.Compare(domain);
-    if (relation == Relation::IDENTITY || relation == Relation::PREDECESSOR)
-      return mode == WHITELIST;
-  }
-  return mode != WHITELIST;
-}
-
-// static
-bool RegistrableDomainFilterBuilder::MatchesCookieForRegisterableDomainsAndIPs(
-    std::set<std::string>* domains_and_ips,
-    Mode mode,
-    const net::CanonicalCookie& cookie) {
-  if (domains_and_ips->empty())
-    return mode == BLACKLIST;
-  std::string cookie_domain = cookie.Domain();
-  if (cookie.IsDomainCookie())
-    cookie_domain = cookie_domain.substr(1);
-  std::string parsed_cookie_domain =
-      GetDomainAndRegistry(cookie_domain, INCLUDE_PRIVATE_REGISTRIES);
-  // This means we're an IP address or an internal hostname.
-  if (parsed_cookie_domain.empty())
-    parsed_cookie_domain = cookie_domain;
-  return (mode == WHITELIST) == (domains_and_ips->find(parsed_cookie_domain) !=
-                                 domains_and_ips->end());
-}
-
-// static
-bool
-RegistrableDomainFilterBuilder::MatchesChannelIDForRegisterableDomainsAndIPs(
-    std::set<std::string>* domains_and_ips,
-    Mode mode,
-    const std::string& channel_id_server_id) {
-  return ((mode == WHITELIST) == (domains_and_ips->find(channel_id_server_id) !=
-                                  domains_and_ips->end()));
-}
-
-// static
-bool
-RegistrableDomainFilterBuilder::MatchesPluginSiteForRegisterableDomainsAndIPs(
-    std::set<std::string>* domains_and_ips,
-    Mode mode,
-    const std::string& site) {
-  // If |site| is a third- or lower-level domain, find the corresponding eTLD+1.
-  std::string domain_or_ip =
-      GetDomainAndRegistry(site, INCLUDE_PRIVATE_REGISTRIES);
-  if (domain_or_ip.empty())
-    domain_or_ip = site;
-
-  return ((mode == WHITELIST) == (domains_and_ips->find(domain_or_ip) !=
-                                  domains_and_ips->end()));
+  return domains_.empty();
 }
diff --git a/chrome/browser/browsing_data/registrable_domain_filter_builder.h b/chrome/browser/browsing_data/registrable_domain_filter_builder.h
index 00bcafa..1f7d186 100644
--- a/chrome/browser/browsing_data/registrable_domain_filter_builder.h
+++ b/chrome/browser/browsing_data/registrable_domain_filter_builder.h
@@ -97,48 +97,9 @@
   bool IsEmpty() const override;
 
  private:
-  // True if the domain of |url| is in the whitelist, or isn't in the blacklist.
-  // The whitelist or blacklist is represented as |registerable_domains|
-  // and |mode|.
-  static bool MatchesURL(std::set<std::string>* registerable_domains,
-                         Mode mode,
-                         const GURL& url);
-
-  // True if the pattern something in the whitelist, or doesn't match something
-  // in the blacklist.
-  // The whitelist or blacklist is represented as |patterns|,  and |mode|.
-  static bool MatchesWebsiteSettingsPattern(
-      std::vector<ContentSettingsPattern>* patterns,
-      Mode mode,
-      const ContentSettingsPattern& pattern);
-
-  // True if no domains can see the given cookie and we're a blacklist, or any
-  // domains can see the cookie and we're a whitelist.
-  // The whitelist or blacklist is represented as |domains_and_ips| and |mode|.
-  static bool MatchesCookieForRegisterableDomainsAndIPs(
-      std::set<std::string>* domains_and_ips,
-      Mode mode,
-      const net::CanonicalCookie& cookie);
-
-  // True if none of the supplied domains matches this Channel ID's server ID
-  // and we're a blacklist, or one of them does and we're a whitelist.
-  // The whitelist or blacklist is represented as |domains_and_ips| and |mode|.
-  static bool MatchesChannelIDForRegisterableDomainsAndIPs(
-      std::set<std::string>* domains_and_ips,
-      Mode mode,
-      const std::string& channel_id_server_id);
-
-  // True if none of the supplied domains matches this plugin's |site| and we're
-  // a blacklist, or one of them does and we're a whitelist. The whitelist or
-  // blacklist is represented by |domains_and_ips| and |mode|.
-  static bool MatchesPluginSiteForRegisterableDomainsAndIPs(
-      std::set<std::string>* domains_and_ips,
-      Mode mode,
-      const std::string& site);
-
-  // The list of domains and whether they should be interpreted as a whitelist
-  // or blacklist.
-  std::set<std::string> domain_list_;
+  // The set of domains that constitute the whitelist or the blacklist,
+  // depending on mode().
+  std::set<std::string> domains_;
 
   DISALLOW_COPY_AND_ASSIGN(RegistrableDomainFilterBuilder);
 };
diff --git a/chrome/browser/budget_service/OWNERS b/chrome/browser/budget_service/OWNERS
index bc1cace..6391f8c7 100644
--- a/chrome/browser/budget_service/OWNERS
+++ b/chrome/browser/budget_service/OWNERS
@@ -1,2 +1,3 @@
+harkness@chromium.org
 johnme@chromium.org
 peter@chromium.org
diff --git a/chrome/browser/chrome_browser_application_mac.h b/chrome/browser/chrome_browser_application_mac.h
index 86e70bc..286f9da 100644
--- a/chrome/browser/chrome_browser_application_mac.h
+++ b/chrome/browser/chrome_browser_application_mac.h
@@ -30,21 +30,6 @@
 - (BOOL)isCyclingWindows;
 @end
 
-namespace chrome_browser_application_mac {
-
-// Bin for unknown exceptions. Exposed for testing purposes.
-extern const size_t kUnknownNSException;
-
-// Returns the histogram bin for |exception| if it is one we track
-// specifically, or |kUnknownNSException| if unknown.  Exposed for testing
-// purposes.
-size_t BinForException(NSException* exception);
-
-// Use UMA to track exception occurance. Exposed for testing purposes.
-void RecordExceptionWithUma(NSException* exception);
-
-}  // namespace chrome_browser_application_mac
-
 #endif  // __OBJC__
 
 namespace chrome_browser_application_mac {
diff --git a/chrome/browser/chrome_browser_application_mac.mm b/chrome/browser/chrome_browser_application_mac.mm
index 5a63b9f..63f3b083 100644
--- a/chrome/browser/chrome_browser_application_mac.mm
+++ b/chrome/browser/chrome_browser_application_mac.mm
@@ -4,120 +4,22 @@
 
 #import "chrome/browser/chrome_browser_application_mac.h"
 
-#include <AvailabilityMacros.h>
-#include <objc/objc-exception.h>
-
-#import "base/auto_reset.h"
+#include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
-#include "base/debug/stack_trace.h"
-#import "base/logging.h"
+#include "base/logging.h"
 #include "base/mac/call_with_eh_frame.h"
-#import "base/mac/scoped_nsobject.h"
-#import "base/mac/scoped_objc_class_swizzler.h"
-#include "base/macros.h"
-#import "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
-#import "base/strings/sys_string_conversions.h"
+#include "base/strings/sys_string_conversions.h"
 #import "chrome/browser/app_controller_mac.h"
-#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
+#import "chrome/browser/mac/exception_processor.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/crash_keys.h"
 #import "components/crash/core/common/objc_zombie.h"
 #include "content/public/browser/browser_accessibility_state.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
 
 namespace chrome_browser_application_mac {
 
-// Maximum number of known named exceptions we'll support.  There is
-// no central registration, but I only find about 75 possibilities in
-// the system frameworks, and many of them are probably not
-// interesting to track in aggregate (those relating to distributed
-// objects, for instance).
-const size_t kKnownNSExceptionCount = 25;
-
-const size_t kUnknownNSException = kKnownNSExceptionCount;
-
-size_t BinForException(NSException* exception) {
-  // A list of common known exceptions.  The list position will
-  // determine where they live in the histogram, so never move them
-  // around, only add to the end.
-  static NSString* const kKnownNSExceptionNames[] = {
-    // Grab-bag exception, not very common.  CFArray (or other
-    // container) mutated while being enumerated is one case seen in
-    // production.
-    NSGenericException,
-
-    // Out-of-range on NSString or NSArray.  Quite common.
-    NSRangeException,
-
-    // Invalid arg to method, unrecognized selector.  Quite common.
-    NSInvalidArgumentException,
-
-    // malloc() returned null in object creation, I think.  Turns out
-    // to be very uncommon in production, because of the OOM killer.
-    NSMallocException,
-
-    // This contains things like windowserver errors, trying to draw
-    // views which aren't in windows, unable to read nib files.  By
-    // far the most common exception seen on the crash server.
-    NSInternalInconsistencyException,
-
-    nil
-  };
-
-  // Make sure our array hasn't outgrown our abilities to track it.
-  DCHECK_LE(arraysize(kKnownNSExceptionNames), kKnownNSExceptionCount);
-
-  NSString* name = [exception name];
-  for (int i = 0; kKnownNSExceptionNames[i]; ++i) {
-    if (name == kKnownNSExceptionNames[i]) {
-      return i;
-    }
-  }
-  return kUnknownNSException;
-}
-
-void RecordExceptionWithUma(NSException* exception) {
-  UMA_HISTOGRAM_ENUMERATION("OSX.NSException",
-      BinForException(exception), kUnknownNSException);
-}
-
-namespace {
-
-objc_exception_preprocessor g_next_preprocessor = nullptr;
-
-id ExceptionPreprocessor(id exception) {
-  static bool seen_first_exception = false;
-
-  RecordExceptionWithUma(exception);
-
-  const char* const kExceptionKey =
-      seen_first_exception ? crash_keys::mac::kLastNSException
-                           : crash_keys::mac::kFirstNSException;
-  NSString* value = [NSString stringWithFormat:@"%@ reason %@",
-      [exception name], [exception reason]];
-  base::debug::SetCrashKeyValue(kExceptionKey, [value UTF8String]);
-
-  const char* const kExceptionTraceKey =
-      seen_first_exception ? crash_keys::mac::kLastNSExceptionTrace
-                           : crash_keys::mac::kFirstNSExceptionTrace;
-  // This exception preprocessor runs prior to the one in libobjc, which sets
-  // the -[NSException callStackReturnAddresses].
-  base::debug::SetCrashKeyToStackTrace(kExceptionTraceKey,
-                                       base::debug::StackTrace());
-
-  seen_first_exception = true;
-
-  // Forward to the original version.
-  if (g_next_preprocessor)
-    return g_next_preprocessor(exception);
-  return exception;
-}
-
-}  // namespace
-
 void RegisterBrowserCrApp() {
   [BrowserCrApplication sharedApplication];
 };
@@ -145,11 +47,7 @@
   // the most recent 10,000 of them on the treadmill.
   ObjcEvilDoers::ZombieEnable(true, 10000);
 
-  if (!chrome_browser_application_mac::g_next_preprocessor) {
-    chrome_browser_application_mac::g_next_preprocessor =
-        objc_setExceptionPreprocessor(
-            &chrome_browser_application_mac::ExceptionPreprocessor);
-  }
+  chrome::InstallObjcExceptionPreprocessor();
 }
 
 - (id)init {
@@ -339,7 +237,11 @@
       aTarget);
   base::debug::ScopedCrashKey key(crash_keys::mac::kSendAction, value);
 
-  return [super sendAction:anAction to:aTarget from:sender];
+  __block BOOL rv;
+  base::mac::CallWithEHFrame(^{
+    rv = [super sendAction:anAction to:aTarget from:sender];
+  });
+  return rv;
 }
 
 - (BOOL)isHandlingSendEvent {
diff --git a/chrome/browser/chrome_browser_application_mac_unittest.mm b/chrome/browser/chrome_browser_application_mac_unittest.mm
deleted file mode 100644
index 4252a75..0000000
--- a/chrome/browser/chrome_browser_application_mac_unittest.mm
+++ /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.
-
-#import "chrome/browser/chrome_browser_application_mac.h"
-
-#import <Cocoa/Cocoa.h>
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/statistics_recorder.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::HistogramBase;
-using base::HistogramSamples;
-using base::StatisticsRecorder;
-
-namespace chrome_browser_application_mac {
-
-// Generate an NSException with the given name.
-NSException* ExceptionNamed(NSString* name) {
-  return [NSException exceptionWithName:name
-                                 reason:@"No reason given"
-                               userInfo:nil];
-}
-
-// Helper to keep binning expectations readible.
-size_t BinForExceptionNamed(NSString* name) {
-  return BinForException(ExceptionNamed(name));
-}
-
-TEST(ChromeApplicationMacTest, ExceptionBinning) {
-  // These exceptions must be in this order.
-  EXPECT_EQ(BinForExceptionNamed(NSGenericException), 0U);
-  EXPECT_EQ(BinForExceptionNamed(NSRangeException), 1U);
-  EXPECT_EQ(BinForExceptionNamed(NSInvalidArgumentException), 2U);
-  EXPECT_EQ(BinForExceptionNamed(NSMallocException), 3U);
-
-  // Random other exceptions map to |kUnknownNSException|.
-  EXPECT_EQ(BinForExceptionNamed(@"CustomName"), kUnknownNSException);
-  EXPECT_EQ(BinForExceptionNamed(@"Custom Name"), kUnknownNSException);
-  EXPECT_EQ(BinForExceptionNamed(@""), kUnknownNSException);
-  EXPECT_EQ(BinForException(nil), kUnknownNSException);
-}
-
-TEST(ChromeApplicationMacTest, RecordException) {
-  // Start up a histogram recorder.
-  // TODO(rtenneti): Leaks StatisticsRecorder and will update suppressions.
-  base::StatisticsRecorder::Initialize();
-
-  StatisticsRecorder::Histograms histograms;
-  StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
-  EXPECT_EQ(0U, histograms.size());
-
-  // Record some known exceptions.
-  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
-  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
-  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
-  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
-  RecordExceptionWithUma(ExceptionNamed(NSRangeException));
-  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
-  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
-  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
-  RecordExceptionWithUma(ExceptionNamed(NSMallocException));
-  RecordExceptionWithUma(ExceptionNamed(NSMallocException));
-
-  // Record some unknown exceptions.
-  RecordExceptionWithUma(ExceptionNamed(@"CustomName"));
-  RecordExceptionWithUma(ExceptionNamed(@"Custom Name"));
-  RecordExceptionWithUma(ExceptionNamed(@""));
-  RecordExceptionWithUma(nil);
-
-  // We should have exactly the right number of exceptions.
-  StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
-  EXPECT_EQ(1U, histograms.size());
-  EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histograms[0]->flags());
-
-  std::unique_ptr<HistogramSamples> samples(histograms[0]->SnapshotSamples());
-  EXPECT_EQ(4, samples->GetCount(0));
-  EXPECT_EQ(1, samples->GetCount(1));
-  EXPECT_EQ(3, samples->GetCount(2));
-  EXPECT_EQ(2, samples->GetCount(3));
-
-  // The unknown exceptions should end up in the overflow bucket.
-  EXPECT_TRUE(histograms[0]->HasConstructionArguments(1,
-                                                      kUnknownNSException,
-                                                      kUnknownNSException + 1));
-  EXPECT_EQ(4, samples->GetCount(kUnknownNSException));
-}
-
-}  // chrome_browser_application_mac
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 816b922..7139e9e 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -126,10 +126,7 @@
 
 }  // namespace
 
-ChromeBrowserFieldTrials::ChromeBrowserFieldTrials(
-    const base::CommandLine& parsed_command_line)
-    : parsed_command_line_(parsed_command_line) {
-}
+ChromeBrowserFieldTrials::ChromeBrowserFieldTrials() {}
 
 ChromeBrowserFieldTrials::~ChromeBrowserFieldTrials() {
 }
@@ -139,9 +136,9 @@
   InstantiateDynamicTrials();
 
 #if defined(OS_ANDROID)
-  chrome::SetupMobileFieldTrials(parsed_command_line_);
+  chrome::SetupMobileFieldTrials();
 #else
-  chrome::SetupDesktopFieldTrials(parsed_command_line_);
+  chrome::SetupDesktopFieldTrials();
 #endif
 }
 
diff --git a/chrome/browser/chrome_browser_field_trials.h b/chrome/browser/chrome_browser_field_trials.h
index 48467d54..4b22f76c 100644
--- a/chrome/browser/chrome_browser_field_trials.h
+++ b/chrome/browser/chrome_browser_field_trials.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_H_
 #define CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_H_
 
-#include "base/command_line.h"
 #include "base/macros.h"
 
 namespace base {
@@ -14,7 +13,7 @@
 
 class ChromeBrowserFieldTrials {
  public:
-  explicit ChromeBrowserFieldTrials(const base::CommandLine& command_line);
+  ChromeBrowserFieldTrials();
   ~ChromeBrowserFieldTrials();
 
   void SetupFieldTrials();
@@ -33,8 +32,6 @@
   // reported as used.
   void InstantiateDynamicTrials();
 
-  const base::CommandLine& parsed_command_line_;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserFieldTrials);
 };
 
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.cc b/chrome/browser/chrome_browser_field_trials_desktop.cc
index 6ebdc45f..6b562d56 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.cc
+++ b/chrome/browser/chrome_browser_field_trials_desktop.cc
@@ -107,8 +107,8 @@
 
 }  // namespace
 
-void SetupDesktopFieldTrials(const base::CommandLine& parsed_command_line) {
-  prerender::ConfigurePrerender(parsed_command_line);
+void SetupDesktopFieldTrials() {
+  prerender::ConfigurePrerender();
   SetupStunProbeTrial();
 #if defined(OS_WIN)
   SetupStabilityDebugging();
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.h b/chrome/browser/chrome_browser_field_trials_desktop.h
index aab5163..41d5fe9 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.h
+++ b/chrome/browser/chrome_browser_field_trials_desktop.h
@@ -5,19 +5,13 @@
 #ifndef CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_DESKTOP_H_
 #define CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_DESKTOP_H_
 
-#include "base/time/time.h"
-
-namespace base {
-class CommandLine;
-}
-
 namespace chrome {
 
 // Sets up common desktop-only field trials.
 // Add an invocation of your field trial init function to this method, or to
 // SetupFieldTrials in chrome_browser_field_trials.cc if it is for all
 // platforms.
-void SetupDesktopFieldTrials(const base::CommandLine& parsed_command_line);
+void SetupDesktopFieldTrials();
 
 }  // namespace chrome
 
diff --git a/chrome/browser/chrome_browser_field_trials_mobile.cc b/chrome/browser/chrome_browser_field_trials_mobile.cc
index 1793b68..8bdcb7c 100644
--- a/chrome/browser/chrome_browser_field_trials_mobile.cc
+++ b/chrome/browser/chrome_browser_field_trials_mobile.cc
@@ -17,9 +17,9 @@
 
 namespace chrome {
 
-void SetupMobileFieldTrials(const base::CommandLine& parsed_command_line) {
+void SetupMobileFieldTrials() {
 #if defined(OS_ANDROID)
-  prerender::ConfigurePrerender(parsed_command_line);
+  prerender::ConfigurePrerender();
 
   // Force-enable profiler timing depending on the field trial.
   if (base::FieldTrialList::FindFullName("ProfilerTiming") == "Enable")
diff --git a/chrome/browser/chrome_browser_field_trials_mobile.h b/chrome/browser/chrome_browser_field_trials_mobile.h
index 1e15967..941f9ce 100644
--- a/chrome/browser/chrome_browser_field_trials_mobile.h
+++ b/chrome/browser/chrome_browser_field_trials_mobile.h
@@ -5,19 +5,13 @@
 #ifndef CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_MOBILE_H_
 #define CHROME_BROWSER_CHROME_BROWSER_FIELD_TRIALS_MOBILE_H_
 
-#include "base/time/time.h"
-
-namespace base {
-class CommandLine;
-}
-
 namespace chrome {
 
 // Sets up mobile-only field trials.
 // Add an invocation of your field trial init function to this method, or to
 // SetupFieldTrials in chrome_browser_field_trials.cc if it is for all
 // platforms.
-void SetupMobileFieldTrials(const base::CommandLine& parsed_command_line);
+void SetupMobileFieldTrials();
 
 }  // namespace chrome
 
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index ed2ddd5..19a377e0 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -131,7 +131,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_value_store.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/task_scheduler_util/initialization_util.h"
@@ -624,7 +624,6 @@
       result_code_(content::RESULT_CODE_NORMAL_EXIT),
       startup_watcher_(new StartupTimeBomb()),
       shutdown_watcher_(new ShutdownWatcherHelper()),
-      browser_field_trials_(parameters.command_line),
       sampling_profiler_(
           base::PlatformThread::CurrentId(),
           StackSamplingConfiguration::Get()->
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 465239d..45cb0af5 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -43,6 +43,7 @@
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_utility_messages.h"
+#include "chrome/common/crash_keys.h"
 #include "chrome/common/env_vars.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -289,6 +290,11 @@
 }
 
 int ChromeBrowserMainPartsWin::PreCreateThreads() {
+// Record whether the machine is domain joined in a crash key. This will be used
+// to better identify whether crashes are from enterprise users.
+  base::debug::SetCrashKeyValue(crash_keys::kEnrolledToDomain,
+                                base::win::IsEnrolledToDomain() ? "yes" : "no");
+
   int rv = ChromeBrowserMainParts::PreCreateThreads();
 
   // TODO(viettrungluu): why don't we run this earlier?
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2cab771..f2983d7 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/engagement/site_engagement_eviction_policy.h"
+#include "chrome/browser/field_trial_recorder.h"
 #include "chrome/browser/font_family_cache.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/memory/chrome_memory_coordinator_delegate.h"
@@ -136,8 +137,9 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_recorder_impl.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/spellcheck/spellcheck_build_features.h"
@@ -2241,7 +2243,7 @@
 
   if (filter.get()) {
     // Try to automatically select a client certificate.
-    if (filter->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (filter->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* filter_dict =
           static_cast<base::DictionaryValue*>(filter.get());
 
@@ -2269,12 +2271,7 @@
 
 content::PlatformNotificationService*
 ChromeContentBrowserClient::GetPlatformNotificationService() {
-#if defined(ENABLE_NOTIFICATIONS)
   return PlatformNotificationServiceImpl::GetInstance();
-#else
-  NOTIMPLEMENTED();
-  return NULL;
-#endif
 }
 
 bool ChromeContentBrowserClient::CanCreateWindow(
@@ -2341,19 +2338,20 @@
     InfoMap* map = io_data->GetExtensionInfoMap();
     const Extension* extension =
         map->extensions().GetExtensionOrAppByURL(opener_url);
-    // TODO(lazyboy): http://crbug.com/585570, if |extension| is a platform app,
-    // disallow loading it in a tab via window.open(). Currently there are apps
-    // that rely on this to be allowed to get Cast API to work, so we are
-    // allowing this temporarily. Once Media Router is available on stable, this
-    // exception should not be required.
     if (extension && extension->is_platform_app()) {
       AppLoadedInTabSource source =
           opener_top_level_frame_url ==
                   extensions::BackgroundInfo::GetBackgroundURL(extension)
               ? APP_LOADED_IN_TAB_SOURCE_BACKGROUND_PAGE
               : APP_LOADED_IN_TAB_SOURCE_APP;
+      // TODO(lazyboy): Remove this UMA once the change below to disallow apps
+      // in tabs has settled in stable branch.
       UMA_HISTOGRAM_ENUMERATION("Extensions.AppLoadedInTab", source,
                                 APP_LOADED_IN_TAB_SOURCE_MAX);
+      // Platform apps and their background pages should not be able to call
+      // window.open() to load v2 apps in regular tab.
+      // Simply disallow window.open() calls in this case.
+      return false;
     }
   }
 #endif
@@ -2789,6 +2787,10 @@
     extra_parts_[i]->GetURLRequestAutoMountHandlers(handlers);
 }
 
+::rappor::RapporService* ChromeContentBrowserClient::GetRapporService() {
+  return g_browser_process->rappor_service();
+}
+
 void ChromeContentBrowserClient::GetAdditionalFileSystemBackends(
     content::BrowserContext* browser_context,
     const base::FilePath& storage_partition_path,
@@ -2938,6 +2940,9 @@
       base::Bind(&BudgetServiceImpl::Create, render_process_host->GetID()),
       ui_task_runner);
   registry->AddInterface(
+      base::Bind(&FieldTrialRecorder::Create),
+      ui_task_runner);
+  registry->AddInterface(
       base::Bind(&rappor::RapporRecorderImpl::Create,
                  g_browser_process->rappor_service()),
       ui_task_runner);
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 67575e36..1a7d5dc2 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -309,6 +309,7 @@
       content::NavigationHandle* navigation_handle) override;
   std::unique_ptr<content::MemoryCoordinatorDelegate>
   GetMemoryCoordinatorDelegate() override;
+  ::rappor::RapporService* GetRapporService() override;
 
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
   void CreateMediaRemoter(content::RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index f5a1abe..7beeeaf 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -7,6 +7,7 @@
         "renderer": [
           "autofill::mojom::AutofillDriver",
           "autofill::mojom::PasswordManagerDriver",
+          "chrome::mojom::FieldTrialRecorder",
           "extensions::StashService",
           "metrics::mojom::LeakDetector",
           "rappor::mojom::RapporRecorder",
@@ -19,16 +20,15 @@
         "ash": [
           // Under classic ash the browser provides some of the ash interfaces
           // to itself.
+          "ash::mojom::AcceleratorController",
           "ash::mojom::CastConfig",
           "ash::mojom::LocaleNotificationController",
-          "ash::mojom::NewWindowClient",
+          "ash::mojom::NewWindowController",
           "ash::mojom::ShelfController",
           "ash::mojom::ShutdownController",
           "ash::mojom::SystemTray",
           "ash::mojom::TouchViewManager",
           "ash::mojom::WallpaperController",
-          "ash::mojom::WallpaperManager",
-          "ash::mojom::VolumeController",
           "ash::mojom::VpnList",
           "keyboard::mojom::Keyboard"
         ],
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index e385c9e..753d37c6 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -362,3 +362,39 @@
   observer.Wait();
   EXPECT_EQ(0U, test_guest_view_manager()->GetNumGuestsActive());
 }
+
+// Verify that a popup can be opened after navigating a remote frame.  This has
+// to be a chrome/ test to ensure that the popup blocker doesn't block the
+// popup.  See https://crbug.com/670770.
+IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
+                       NavigateRemoteFrameAndOpenPopup) {
+  // Start on a page with an <iframe>.
+  GURL main_url(embedded_test_server()->GetURL("a.com", "/iframe.html"));
+  ui_test_utils::NavigateToURL(browser(), main_url);
+
+  // Navigate the iframe cross-site.
+  content::WebContents* active_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  EXPECT_TRUE(NavigateIframeToURL(active_web_contents, "test", frame_url));
+
+  // Run a script in the parent frame to (1) navigate iframe to another URL,
+  // and (2) open a popup.  Note that ExecuteScript will run this with a user
+  // gesture, so both steps should succeed.
+  frame_url = embedded_test_server()->GetURL("c.com", "/title1.html");
+  content::WindowedNotificationObserver popup_observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  bool popup_handle_is_valid = false;
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      active_web_contents,
+      "document.querySelector('iframe').src = '" + frame_url.spec() + "';\n"
+      "var w = window.open('about:blank');\n"
+      "window.domAutomationController.send(!!w);\n",
+      &popup_handle_is_valid));
+  popup_observer.Wait();
+
+  // The popup shouldn't be blocked.
+  EXPECT_TRUE(popup_handle_is_valid);
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+}
diff --git a/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
index ee08de0..986be19 100644
--- a/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
@@ -5,9 +5,7 @@
 #include <stddef.h>
 
 #include "ash/common/system/tray/system_tray.h"
-#include "ash/common/test/wm_shell_test_api.h"
 #include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "ash/shell.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/sticky_keys/sticky_keys_overlay.h"
@@ -32,40 +30,12 @@
 
 namespace chromeos {
 
-class CountingNewWindowClient : public ash::mojom::NewWindowClient {
- public:
-  CountingNewWindowClient() {}
-  ~CountingNewWindowClient() override {}
-
-  int new_tab_action_count() const { return new_tab_action_count_; }
-
-  // ash::mojom::NewWindowClient:
-  void NewTab() override { new_tab_action_count_++; }
-  void NewWindow(bool incognito) override {}
-  void OpenFileManager() override {}
-  void OpenCrosh() override {}
-  void OpenGetHelp() override {}
-  void RestoreTab() override {}
-  void ShowKeyboardOverlay() override {}
-  void ShowTaskManager() override {}
-  void OpenFeedbackPage() override {}
-
- private:
-  int new_tab_action_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(CountingNewWindowClient);
-};
-
 class StickyKeysBrowserTest : public InProcessBrowserTest {
  public:
   void SetUpOnMainThread() override {
     content::BrowserTestBase::SetUpOnMainThread();
     event_generator_.reset(
         new ui::test::EventGenerator(browser()->window()->GetNativeWindow()));
-
-    new_window_client_ = new CountingNewWindowClient;
-    ash::WmShellTestApi().SetNewWindowClient(
-        base::WrapUnique(new_window_client_));
   }
 
  protected:
@@ -86,12 +56,13 @@
 
   void SendKeyPress(ui::KeyboardCode key) {
     event_generator_->PressKey(key, ui::EF_NONE);
+    content::RunAllPendingInMessageLoop();
     event_generator_->ReleaseKey(key, ui::EF_NONE);
+    content::RunAllPendingInMessageLoop();
   }
 
   content::NotificationRegistrar registrar_;
   std::unique_ptr<ui::test::EventGenerator> event_generator_;
-  CountingNewWindowClient* new_window_client_;
 
   DISALLOW_COPY_AND_ASSIGN(StickyKeysBrowserTest);
 };
@@ -128,23 +99,24 @@
   SendKeyPress(ui::VKEY_CONTROL);
 
   // In the locked state, pressing 't' should open a new tab each time.
-  int tab_count = 0;
-  for (; tab_count < 4; ++tab_count) {
-    EXPECT_EQ(tab_count, new_window_client_->new_tab_action_count());
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  int tab_count = 1;
+  for (; tab_count < 5; ++tab_count) {
+    EXPECT_EQ(tab_count, tab_strip_model->count());
     SendKeyPress(ui::VKEY_T);
   }
 
   // Unlock the modifier key and shortcut should no longer activate.
   SendKeyPress(ui::VKEY_CONTROL);
   SendKeyPress(ui::VKEY_T);
-  EXPECT_EQ(tab_count, new_window_client_->new_tab_action_count());
+  EXPECT_EQ(tab_count, tab_strip_model->count());
 
   // Shortcut should not work after disabling sticky keys.
   DisableStickyKeys();
   SendKeyPress(ui::VKEY_CONTROL);
   SendKeyPress(ui::VKEY_CONTROL);
   SendKeyPress(ui::VKEY_T);
-  EXPECT_EQ(tab_count, new_window_client_->new_tab_action_count());
+  EXPECT_EQ(tab_count, tab_strip_model->count());
 }
 
 IN_PROC_BROWSER_TEST_F(StickyKeysBrowserTest, CtrlClickHomeButton) {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index f4be753..a24c7bf 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -10,6 +10,7 @@
 
 #include "base/barrier_closure.h"
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -35,6 +36,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chromeos/chromeos_paths.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -183,6 +185,12 @@
       base::Bind(&PerformDelayedCryptohomeRemovals));
 }
 
+// static
+bool KioskAppManager::IsConsumerKioskEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableConsumerKiosk);
+}
+
 KioskAppManager::App::App(const KioskAppData& data,
                           bool is_extension_pending,
                           bool auto_launched_with_zero_delay)
@@ -255,6 +263,12 @@
 
 void KioskAppManager::EnableConsumerKioskAutoLaunch(
     const KioskAppManager::EnableKioskAutoLaunchCallback& callback) {
+  if (!IsConsumerKioskEnabled()) {
+    if (!callback.is_null())
+      callback.Run(false);
+    return;
+  }
+
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   connector->GetInstallAttributes()->LockDevice(
@@ -268,6 +282,12 @@
 
 void KioskAppManager::GetConsumerKioskAutoLaunchStatus(
     const KioskAppManager::GetConsumerKioskAutoLaunchStatusCallback& callback) {
+  if (!IsConsumerKioskEnabled()) {
+    if (!callback.is_null())
+      callback.Run(CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
+    return;
+  }
+
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   connector->GetInstallAttributes()->ReadImmutableAttributes(
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
index aeae5df..7aac2e7 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
@@ -108,6 +108,8 @@
   // Removes cryptohomes which could not be removed during the previous session.
   static void RemoveObsoleteCryptohomes();
 
+  static bool IsConsumerKioskEnabled();
+
   // Initiates reading of consumer kiosk mode auto-launch status.
   void GetConsumerKioskAutoLaunchStatus(
       const GetConsumerKioskAutoLaunchStatusCallback& callback);
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
index 7218eac..05c9c45 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/test_utils.h"
@@ -780,79 +781,81 @@
 }
 
 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, EnableConsumerKiosk) {
-  std::unique_ptr<KioskAppManager::ConsumerKioskAutoLaunchStatus> status(
-      new KioskAppManager::ConsumerKioskAutoLaunchStatus(
-          KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED));
-  std::unique_ptr<bool> locked(new bool(false));
+  // Consumer kiosk is disabled by default. Enable it for test.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableConsumerKiosk);
+
+  KioskAppManager::ConsumerKioskAutoLaunchStatus status =
+      KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
+  bool locked = false;
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
-  manager()->GetConsumerKioskAutoLaunchStatus(
-      base::Bind(&ConsumerKioskAutoLaunchStatusCheck,
-                 status.get(),
-                 runner->QuitClosure()));
+  manager()->GetConsumerKioskAutoLaunchStatus(base::Bind(
+      &ConsumerKioskAutoLaunchStatusCheck, &status, runner->QuitClosure()));
   runner->Run();
-  EXPECT_EQ(*status.get(),
-            KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
+  EXPECT_EQ(status, KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
 
   scoped_refptr<content::MessageLoopRunner> runner2 =
       new content::MessageLoopRunner;
   manager()->EnableConsumerKioskAutoLaunch(
-      base::Bind(&ConsumerKioskModeLockCheck,
-                 locked.get(),
-                 runner2->QuitClosure()));
+      base::Bind(&ConsumerKioskModeLockCheck, &locked, runner2->QuitClosure()));
   runner2->Run();
-  EXPECT_TRUE(*locked.get());
+  EXPECT_TRUE(locked);
 
   scoped_refptr<content::MessageLoopRunner> runner3 =
       new content::MessageLoopRunner;
-  manager()->GetConsumerKioskAutoLaunchStatus(
-      base::Bind(&ConsumerKioskAutoLaunchStatusCheck,
-                 status.get(),
-                 runner3->QuitClosure()));
+  manager()->GetConsumerKioskAutoLaunchStatus(base::Bind(
+      &ConsumerKioskAutoLaunchStatusCheck, &status, runner3->QuitClosure()));
   runner3->Run();
-  EXPECT_EQ(*status.get(),
-            KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED);
+  EXPECT_EQ(status, KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED);
+}
+
+IN_PROC_BROWSER_TEST_F(KioskAppManagerTest, ConsumerKioskDisabled) {
+  KioskAppManager::ConsumerKioskAutoLaunchStatus status =
+      KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE;
+
+  scoped_refptr<content::MessageLoopRunner> runner =
+      new content::MessageLoopRunner;
+  manager()->GetConsumerKioskAutoLaunchStatus(base::Bind(
+      &ConsumerKioskAutoLaunchStatusCheck, &status, runner->QuitClosure()));
+  runner->Run();
+  EXPECT_EQ(status, KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
 }
 
 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest,
                        PreventEnableConsumerKioskForEnterprise) {
-  // First, lock the device as enterprise.
+  // Consumer kiosk is disabled by default. Enable it for test.
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableConsumerKiosk);
+
+  // Lock the device as enterprise.
   EXPECT_EQ(LockDeviceForEnterprise(), InstallAttributes::LOCK_SUCCESS);
 
-  std::unique_ptr<KioskAppManager::ConsumerKioskAutoLaunchStatus> status(
-      new KioskAppManager::ConsumerKioskAutoLaunchStatus(
-          KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED));
-  std::unique_ptr<bool> locked(new bool(true));
+  KioskAppManager::ConsumerKioskAutoLaunchStatus status =
+      KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED;
+  bool locked = true;
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
-  manager()->GetConsumerKioskAutoLaunchStatus(
-      base::Bind(&ConsumerKioskAutoLaunchStatusCheck,
-                 status.get(),
-                 runner->QuitClosure()));
+  manager()->GetConsumerKioskAutoLaunchStatus(base::Bind(
+      &ConsumerKioskAutoLaunchStatusCheck, &status, runner->QuitClosure()));
   runner->Run();
-  EXPECT_EQ(*status.get(),
-            KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
+  EXPECT_EQ(status, KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
 
   scoped_refptr<content::MessageLoopRunner> runner2 =
       new content::MessageLoopRunner;
   manager()->EnableConsumerKioskAutoLaunch(
-      base::Bind(&ConsumerKioskModeLockCheck,
-                 locked.get(),
-                 runner2->QuitClosure()));
+      base::Bind(&ConsumerKioskModeLockCheck, &locked, runner2->QuitClosure()));
   runner2->Run();
-  EXPECT_FALSE(*locked.get());
+  EXPECT_FALSE(locked);
 
   scoped_refptr<content::MessageLoopRunner> runner3 =
       new content::MessageLoopRunner;
-  manager()->GetConsumerKioskAutoLaunchStatus(
-      base::Bind(&ConsumerKioskAutoLaunchStatusCheck,
-                 status.get(),
-                 runner3->QuitClosure()));
+  manager()->GetConsumerKioskAutoLaunchStatus(base::Bind(
+      &ConsumerKioskAutoLaunchStatusCheck, &status, runner3->QuitClosure()));
   runner3->Run();
-  EXPECT_EQ(*status.get(),
-            KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
+  EXPECT_EQ(status, KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_DISABLED);
 }
 
 IN_PROC_BROWSER_TEST_F(KioskAppManagerTest,
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 3159554..6107f7f 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -110,6 +110,7 @@
     user_prefs::PrefRegistrySyncable* registry) {
   // TODO(dspaid): Implement a mechanism to allow this to sync on first boot
   // only.
+  registry->RegisterBooleanPref(prefs::kArcDataRemoveRequested, false);
   registry->RegisterBooleanPref(prefs::kArcEnabled, false);
   registry->RegisterBooleanPref(prefs::kArcSignedIn, false);
   registry->RegisterBooleanPref(prefs::kArcTermsAccepted, false);
@@ -195,30 +196,36 @@
     OnProvisioningFinished(ProvisioningResult::ARC_STOPPED);
   }
 
-  if (clear_required_) {
+  if (profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested)) {
     // This should be always true, but just in case as this is looked at
     // inside RemoveArcData() at first.
     DCHECK(arc_bridge_service()->stopped());
     RemoveArcData();
   } else {
     // To support special "Stop and enable ARC" procedure for enterprise,
-    // here call OnArcDataRemoved(true) as if the data removal is successfully
-    // done.
+    // here call MaybeReenableArc() asyncronously.
     // TODO(hidehiko): Restructure the code. crbug.com/665316
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&ArcSessionManager::OnArcDataRemoved,
-                              weak_ptr_factory_.GetWeakPtr(), true));
+        FROM_HERE, base::Bind(&ArcSessionManager::MaybeReenableArc,
+                              weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
 void ArcSessionManager::RemoveArcData() {
+  // Ignore redundant data removal request.
+  if (state() == State::REMOVING_DATA_DIR)
+    return;
+
+  // OnArcDataRemoved resets this flag.
+  profile_->GetPrefs()->SetBoolean(prefs::kArcDataRemoveRequested, true);
+
   if (!arc_bridge_service()->stopped()) {
     // Just set a flag. On bridge stopped, this will be re-called,
     // then session manager should remove the data.
-    clear_required_ = true;
     return;
   }
-  clear_required_ = false;
+
+  SetState(State::REMOVING_DATA_DIR);
   chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->RemoveArcData(
       cryptohome::Identification(
           multi_user_util::GetAccountIdFromProfile(profile_)),
@@ -229,12 +236,27 @@
 void ArcSessionManager::OnArcDataRemoved(bool success) {
   LOG_IF(ERROR, !success) << "Required ARC user data wipe failed.";
 
+  // TODO(khmel): Browser tests may shutdown profile by itself. Update browser
+  // tests and remove this check.
+  if (state() == State::NOT_INITIALIZED)
+    return;
+
+  for (auto& observer : observer_list_)
+    observer.OnArcDataRemoved();
+
+  profile_->GetPrefs()->SetBoolean(prefs::kArcDataRemoveRequested, false);
+  DCHECK_EQ(state(), State::REMOVING_DATA_DIR);
+  SetState(State::STOPPED);
+
+  MaybeReenableArc();
+}
+
+void ArcSessionManager::MaybeReenableArc() {
   // Here check if |reenable_arc_| is marked or not.
   // The only case this happens should be in the special case for enterprise
   // "on managed lost" case. In that case, OnBridgeStopped() should trigger
   // the RemoveArcData(), then this.
-  // TODO(hidehiko): Restructure the code.
-  if (!reenable_arc_)
+  if (!reenable_arc_ || !IsArcEnabled())
     return;
 
   // Restart ARC anyway. Let the enterprise reporting instance decide whether
@@ -246,7 +268,23 @@
 
 void ArcSessionManager::OnProvisioningFinished(ProvisioningResult result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK_EQ(state_, State::ACTIVE);
+
+  // Due asynchronous nature of stopping Arc bridge, OnProvisioningFinished may
+  // arrive after setting the |State::STOPPED| state and |State::Active| is not
+  // guaranty set here. prefs::kArcDataRemoveRequested is also can be active
+  // for now.
+
+  if (provisioning_reported_) {
+    // We don't expect ProvisioningResult::SUCCESS is reported twice or reported
+    // after an error.
+    DCHECK_NE(result, ProvisioningResult::SUCCESS);
+    // TODO (khmel): Consider changing LOG to NOTREACHED once we guaranty that
+    // no double message can happen in production.
+    LOG(WARNING) << " Provisioning result was already reported. Ignoring "
+                 << " additional result " << static_cast<int>(result) << ".";
+    return;
+  }
+  provisioning_reported_ = true;
 
   if (result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) {
     if (IsArcKioskMode()) {
@@ -412,7 +450,14 @@
       base::Bind(&ArcSessionManager::OnOptInPreferenceChanged,
                  weak_ptr_factory_.GetWeakPtr()));
   if (profile_->GetPrefs()->GetBoolean(prefs::kArcEnabled)) {
-    OnOptInPreferenceChanged();
+    // Don't start ARC if there is a pending request to remove the data. Restart
+    // ARC once data removal finishes.
+    if (profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested)) {
+      reenable_arc_ = true;
+      RemoveArcData();
+    } else {
+      OnOptInPreferenceChanged();
+    }
   } else {
     RemoveArcData();
     PrefServiceSyncableFromProfile(profile_)->AddObserver(this);
@@ -492,6 +537,8 @@
     observer.OnOptInEnabled(arc_enabled);
 
   if (!arc_enabled) {
+    // Reset any pending request to re-enable Arc.
+    reenable_arc_ = false;
     StopArc();
     RemoveArcData();
     return;
@@ -500,6 +547,13 @@
   if (state_ == State::ACTIVE)
     return;
 
+  if (state_ == State::REMOVING_DATA_DIR) {
+    // Data removal request is in progress. Set flag to re-enable Arc once it is
+    // finished.
+    reenable_arc_ = true;
+    return;
+  }
+
   if (support_host_)
     support_host_->SetArcManaged(IsArcManaged());
 
@@ -564,7 +618,7 @@
   terms_of_service_negotiator_.reset();
   android_management_checker_.reset();
   arc_bridge_service()->RequestStop();
-  if (state_ != State::NOT_INITIALIZED)
+  if (state_ != State::NOT_INITIALIZED && state_ != State::REMOVING_DATA_DIR)
     SetState(State::STOPPED);
   for (auto& observer : observer_list_)
     observer.OnShutdownBridge();
@@ -591,6 +645,12 @@
 
 void ArcSessionManager::StartArc() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Arc must be started only if no pending data removal request exists.
+  DCHECK(!profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+
+  provisioning_reported_ = false;
+
   arc_bridge_service()->RequestStart();
   SetState(State::ACTIVE);
 }
@@ -849,6 +909,8 @@
       return os << "SHOWING_TERMS_OF_SERVICE";
     case ArcSessionManager::State::CHECKING_ANDROID_MANAGEMENT:
       return os << "CHECKING_ANDROID_MANAGEMENT";
+    case ArcSessionManager::State::REMOVING_DATA_DIR:
+      return os << "REMOVING_DATA_DIR";
     case ArcSessionManager::State::ACTIVE:
       return os << "ACTIVE";
   }
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index 8c14697..79f590f4 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -94,6 +94,7 @@
     STOPPED,
     SHOWING_TERMS_OF_SERVICE,
     CHECKING_ANDROID_MANAGEMENT,
+    REMOVING_DATA_DIR,
     ACTIVE,
   };
 
@@ -109,6 +110,10 @@
 
     // Called to notify that ARC has been initialized successfully.
     virtual void OnInitialStart() {}
+
+    // Called to notify that Android data has been removed. Used in
+    // browser_tests
+    virtual void OnArcDataRemoved() {}
   };
 
   explicit ArcSessionManager(ArcBridgeService* bridge_service);
@@ -216,6 +221,7 @@
   void PrepareContextForAuthCodeRequest();
 
   void StartArcAndroidManagementCheck();
+  void MaybeReenableArc();
 
   // Called when the Android management check is done in opt-in flow or
   // re-auth flow.
@@ -236,8 +242,8 @@
   State state_ = State::NOT_INITIALIZED;
   base::ObserverList<Observer> observer_list_;
   std::unique_ptr<ArcAppLauncher> playstore_launcher_;
-  bool clear_required_ = false;
   bool reenable_arc_ = false;
+  bool provisioning_reported_ = false;
   base::OneShotTimer arc_sign_in_timer_;
 
   std::unique_ptr<ArcSupportHost> support_host_;
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
index 6b6db154..5d787ddc6 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
@@ -96,6 +96,37 @@
   DISALLOW_COPY_AND_ASSIGN(ArcSessionManagerShutdownObserver);
 };
 
+// Observer of ARC data has been removed.
+class ArcSessionManagerDataRemovedObserver
+    : public ArcSessionManager::Observer {
+ public:
+  ArcSessionManagerDataRemovedObserver() {
+    ArcSessionManager::Get()->AddObserver(this);
+  }
+
+  ~ArcSessionManagerDataRemovedObserver() override {
+    ArcSessionManager::Get()->RemoveObserver(this);
+  }
+
+  void Wait() {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  // ArcSessionManager::Observer:
+  void OnArcDataRemoved() override {
+    if (!run_loop_)
+      return;
+    run_loop_->Quit();
+  }
+
+ private:
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcSessionManagerDataRemovedObserver);
+};
+
 class ArcSessionManagerTest : public InProcessBrowserTest {
  protected:
   ArcSessionManagerTest() {}
@@ -177,6 +208,12 @@
         user_manager::UserManager::Get());
   }
 
+  void EnableArc() {
+    PrefService* const prefs = profile()->GetPrefs();
+    prefs->SetBoolean(prefs::kArcEnabled, true);
+    base::RunLoop().RunUntilIdle();
+  }
+
   void set_profile_name(const std::string& username) {
     profile_->set_profile_name(username);
   }
@@ -196,8 +233,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ArcSessionManagerTest, ConsumerAccount) {
-  PrefService* const prefs = profile()->GetPrefs();
-  prefs->SetBoolean(prefs::kArcEnabled, true);
+  EnableArc();
   token_service()->IssueTokenForAllPendingRequests(kUnmanagedAuthToken,
                                                    base::Time::Max());
   ASSERT_EQ(ArcSessionManager::State::ACTIVE,
@@ -206,9 +242,7 @@
 
 IN_PROC_BROWSER_TEST_F(ArcSessionManagerTest, WellKnownConsumerAccount) {
   set_profile_name(kWellKnownConsumerName);
-  PrefService* const prefs = profile()->GetPrefs();
-
-  prefs->SetBoolean(prefs::kArcEnabled, true);
+  EnableArc();
   ASSERT_EQ(ArcSessionManager::State::ACTIVE,
             ArcSessionManager::Get()->state());
 }
@@ -218,21 +252,19 @@
       policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile());
   connector->OverrideIsManagedForTesting(true);
 
-  PrefService* const pref = profile()->GetPrefs();
-
-  pref->SetBoolean(prefs::kArcEnabled, true);
+  EnableArc();
   ASSERT_EQ(ArcSessionManager::State::ACTIVE,
             ArcSessionManager::Get()->state());
 }
 
 IN_PROC_BROWSER_TEST_F(ArcSessionManagerTest, ManagedAndroidAccount) {
-  PrefService* const prefs = profile()->GetPrefs();
-
-  prefs->SetBoolean(prefs::kArcEnabled, true);
+  EnableArc();
   token_service()->IssueTokenForAllPendingRequests(kManagedAuthToken,
                                                    base::Time::Max());
-  ArcSessionManagerShutdownObserver observer;
-  observer.Wait();
+  ArcSessionManagerShutdownObserver().Wait();
+  ASSERT_EQ(ArcSessionManager::State::REMOVING_DATA_DIR,
+            ArcSessionManager::Get()->state());
+  ArcSessionManagerDataRemovedObserver().Wait();
   ASSERT_EQ(ArcSessionManager::State::STOPPED,
             ArcSessionManager::Get()->state());
 }
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index 7403af09..98b7e135 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -27,6 +27,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/test/fake_arc_bridge_service.h"
 #include "components/prefs/pref_service.h"
@@ -53,6 +54,9 @@
   ~ArcSessionManagerTest() override = default;
 
   void SetUp() override {
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+        base::MakeUnique<chromeos::FakeSessionManagerClient>());
+
     chromeos::DBusThreadManager::Initialize();
 
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -98,6 +102,18 @@
     return arc_session_manager_.get();
   }
 
+  bool WaitForDataRemoved(ArcSessionManager::State expected_state) {
+    if (arc_session_manager()->state() !=
+        ArcSessionManager::State::REMOVING_DATA_DIR)
+      return false;
+
+    base::RunLoop().RunUntilIdle();
+    if (arc_session_manager()->state() != expected_state)
+      return false;
+
+    return true;
+  }
+
  private:
   void StartPreferenceSyncing() const {
     PrefServiceSyncableFromProfile(profile_.get())
@@ -127,14 +143,17 @@
   ASSERT_FALSE(pref->GetBoolean(prefs::kArcEnabled));
 
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
-  ASSERT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state());
+
+  ASSERT_TRUE(WaitForDataRemoved(ArcSessionManager::State::STOPPED));
 
   pref->SetBoolean(prefs::kArcEnabled, true);
+  base::RunLoop().RunUntilIdle();
   ASSERT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE,
             arc_session_manager()->state());
 
   pref->SetBoolean(prefs::kArcEnabled, false);
-  ASSERT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state());
+
+  ASSERT_TRUE(WaitForDataRemoved(ArcSessionManager::State::STOPPED));
 
   // Correctly stop service.
   arc_session_manager()->Shutdown();
@@ -193,9 +212,10 @@
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
 
   // By default ARC is not enabled.
-  ASSERT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state());
+  ASSERT_TRUE(WaitForDataRemoved(ArcSessionManager::State::STOPPED));
 
   profile()->GetPrefs()->SetBoolean(prefs::kArcEnabled, true);
+  base::RunLoop().RunUntilIdle();
 
   // Setting profile and pref initiates a code fetching process.
   ASSERT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE,
@@ -236,11 +256,16 @@
 
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
   pref->SetBoolean(prefs::kArcEnabled, true);
+  base::RunLoop().RunUntilIdle();
+
   ASSERT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE,
             arc_session_manager()->state());
 
   arc_session_manager()->CancelAuthCode();
-  ASSERT_EQ(ArcSessionManager::State::STOPPED, arc_session_manager()->state());
+
+  // Wait until data is removed.
+  ASSERT_TRUE(WaitForDataRemoved(ArcSessionManager::State::STOPPED));
+
   ASSERT_FALSE(pref->GetBoolean(prefs::kArcEnabled));
 
   // Correctly stop service.
@@ -252,6 +277,7 @@
 
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
   pref->SetBoolean(prefs::kArcEnabled, true);
+  base::RunLoop().RunUntilIdle();
 
   arc_session_manager()->StartArc();
 
@@ -377,4 +403,77 @@
   arc_session_manager()->Shutdown();
 }
 
+TEST_F(ArcSessionManagerTest, RemoveDataFolder) {
+  profile()->GetPrefs()->SetBoolean(prefs::kArcEnabled, false);
+  // Starting session manager with prefs::kArcEnabled off automatically removes
+  // Android's data folder.
+  arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
+  EXPECT_TRUE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+  EXPECT_EQ(ArcSessionManager::State::REMOVING_DATA_DIR,
+            arc_session_manager()->state());
+  // Enable ARC. Data is removed asyncronously. At this moment session manager
+  // should be in REMOVING_DATA_DIR state.
+  profile()->GetPrefs()->SetBoolean(prefs::kArcEnabled, true);
+  EXPECT_TRUE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+  EXPECT_EQ(ArcSessionManager::State::REMOVING_DATA_DIR,
+            arc_session_manager()->state());
+  // Wait until data is removed.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+  EXPECT_EQ(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE,
+            arc_session_manager()->state());
+  arc_session_manager()->StartArc();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  // Now request to remove data and stop session manager.
+  arc_session_manager()->RemoveArcData();
+  ASSERT_TRUE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+  arc_session_manager()->Shutdown();
+  base::RunLoop().RunUntilIdle();
+  // Request should persist.
+  ASSERT_TRUE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+
+  // Emulate next sign-in. Data should be removed first and ARC started after.
+  arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
+  EXPECT_TRUE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+
+  ASSERT_TRUE(
+      WaitForDataRemoved(ArcSessionManager::State::SHOWING_TERMS_OF_SERVICE));
+
+  EXPECT_FALSE(
+      profile()->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested));
+
+  arc_session_manager()->StartArc();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  arc_session_manager()->Shutdown();
+}
+
+TEST_F(ArcSessionManagerTest, IgnoreSecondErrorReporting) {
+  profile()->GetPrefs()->SetBoolean(prefs::kArcEnabled, true);
+  arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
+  arc_session_manager()->StartArc();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  // Report some failure that does not stop the bridge.
+  arc_session_manager()->OnProvisioningFinished(
+      ProvisioningResult::GMS_SIGN_IN_FAILED);
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  // Try to send another error that stops the bridge if sent first. It should
+  // be ignored.
+  arc_session_manager()->OnProvisioningFinished(
+      ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR);
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  arc_session_manager()->Shutdown();
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
index 72c9383e..e8fb5980 100644
--- a/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/chromeos/arc/policy/arc_policy_bridge.cc
@@ -53,7 +53,7 @@
   const base::Value* const policy_value = policy_map.GetValue(policy_name);
   if (!policy_value)
     return;
-  if (!policy_value->IsType(base::Value::TYPE_BOOLEAN)) {
+  if (!policy_value->IsType(base::Value::Type::BOOLEAN)) {
     LOG(ERROR) << "Policy " << policy_name << " is not a boolean.";
     return;
   }
@@ -73,7 +73,7 @@
   const base::Value* const policy_value = policy_map.GetValue(policy_name);
   if (!policy_value)
     return;
-  if (!policy_value->IsType(base::Value::TYPE_INTEGER)) {
+  if (!policy_value->IsType(base::Value::Type::INTEGER)) {
     LOG(ERROR) << "Policy " << policy_name << " is not an integer.";
     return;
   }
diff --git a/chrome/browser/chromeos/chrome_interface_factory.cc b/chrome/browser/chromeos/chrome_interface_factory.cc
index d6824f6f..559d52a 100644
--- a/chrome/browser/chromeos/chrome_interface_factory.cc
+++ b/chrome/browser/chromeos/chrome_interface_factory.cc
@@ -8,21 +8,17 @@
 #include <utility>
 
 #include "ash/common/mojo_interface_factory.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "ash/public/interfaces/shutdown.mojom.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/app_list/app_list_presenter_service.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/chrome_new_window_client.h"
 #include "chrome/browser/ui/ash/keyboard_ui_service.h"
-#include "chrome/browser/ui/ash/volume_controller_chromeos.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "content/public/common/service_manager_connection.h"
@@ -117,23 +113,6 @@
     launchable_->ProcessRequest(std::move(request));
   }
 
-  void BindRequest(ash::mojom::NewWindowClientRequest request) {
-    if (!new_window_client_)
-      new_window_client_.reset(new ChromeNewWindowClient);
-    new_window_client_bindings_.AddBinding(new_window_client_.get(),
-                                           std::move(request));
-  }
-
-  void BindRequest(ash::mojom::WallpaperManagerRequest request) {
-    WallpaperManager::Get()->BindRequest(std::move(request));
-  }
-
-  void BindRequest(ash::mojom::VolumeControllerRequest request) {
-    if (!volume_controller_)
-      volume_controller_ = base::MakeUnique<VolumeController>();
-    volume_controller_->BindRequest(std::move(request));
-  }
-
   void BindRequest(app_list::mojom::AppListPresenterRequest request) {
     if (!app_list_presenter_service_)
       app_list_presenter_service_ = base::MakeUnique<AppListPresenterService>();
@@ -146,9 +125,6 @@
   std::unique_ptr<KeyboardUIService> keyboard_ui_service_;
   mojo::BindingSet<keyboard::mojom::Keyboard> keyboard_bindings_;
   std::unique_ptr<ChromeLaunchable> launchable_;
-  std::unique_ptr<ChromeNewWindowClient> new_window_client_;
-  mojo::BindingSet<ash::mojom::NewWindowClient> new_window_client_bindings_;
-  std::unique_ptr<VolumeController> volume_controller_;
   std::unique_ptr<AppListPresenterService> app_list_presenter_service_;
   mojo::BindingSet<app_list::mojom::AppListPresenter>
       app_list_presenter_bindings_;
@@ -177,12 +153,6 @@
                                                      main_thread_task_runner_);
   FactoryImpl::AddFactory<mash::mojom::Launchable>(registry,
                                                    main_thread_task_runner_);
-  FactoryImpl::AddFactory<ash::mojom::NewWindowClient>(
-      registry, main_thread_task_runner_);
-  FactoryImpl::AddFactory<ash::mojom::VolumeController>(
-      registry, main_thread_task_runner_);
-  FactoryImpl::AddFactory<ash::mojom::WallpaperManager>(
-      registry, main_thread_task_runner_);
   FactoryImpl::AddFactory<app_list::mojom::AppListPresenter>(
       registry, main_thread_task_runner_);
 
diff --git a/chrome/browser/chromeos/extensions/echo_private_apitest.cc b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
index 4cc1c62..0714c63e 100644
--- a/chrome/browser/chromeos/extensions/echo_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_apitest.cc
@@ -55,7 +55,7 @@
         browser()));
 
     ASSERT_TRUE(result.get());
-    ASSERT_EQ(base::Value::TYPE_BOOLEAN, result->GetType());
+    ASSERT_EQ(base::Value::Type::BOOLEAN, result->GetType());
 
     bool result_as_boolean = false;
     ASSERT_TRUE(result->GetAsBoolean(&result_as_boolean));
diff --git a/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
index 344e12b..36be0a6 100644
--- a/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <cstddef>
+#include <memory>
 #include <string>
 
 #include "base/logging.h"
@@ -49,7 +50,7 @@
     return true;
   const extensions::Feature* feature =
       extensions::FeatureProvider::GetBehaviorFeature(
-          extensions::BehaviorFeature::kSigninScreen);
+          extensions::behavior_feature::kSigninScreen);
   CHECK(feature);
   extensions::Feature::Availability availability =
       feature->IsAvailableToExtension(extension);
diff --git a/chrome/browser/chromeos/first_run/goodies_displayer.cc b/chrome/browser/chromeos/first_run/goodies_displayer.cc
index 01b02d74..7a6449f 100644
--- a/chrome/browser/chromeos/first_run/goodies_displayer.cc
+++ b/chrome/browser/chromeos/first_run/goodies_displayer.cc
@@ -18,6 +18,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
@@ -106,12 +108,13 @@
 // new Chromebooks; when appropriate, it uses pref to mark page as shown,
 // removes itself from BrowserListObservers, and deletes itself.
 void GoodiesDisplayer::OnBrowserSetLastActive(Browser* browser) {
-  // 1. Must be an actual tabbed brower window.
+  // 1. Must be an actual tabbed browser window.
   if (browser->type() != Browser::TYPE_TABBED)
     return;
 
-  // 2. Not guest or incognito session (keep observing).
-  if (browser->profile()->IsOffTheRecord())
+  // 2. Not guest or incognito session or supervised user (keep observing).
+  user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
+  if (browser->profile()->IsOffTheRecord() || (user && user->IsSupervised()))
     return;
 
   PrefService* local_state = g_browser_process->local_state();
diff --git a/chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.cc b/chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.cc
index c474b44..5f56af6 100644
--- a/chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.cc
@@ -217,11 +217,11 @@
   std::set<std::string> languages;
   const base::Value* language_value = NULL;
   if (dict.Get(extensions::manifest_keys::kLanguage, &language_value)) {
-    if (language_value->GetType() == base::Value::TYPE_STRING) {
+    if (language_value->GetType() == base::Value::Type::STRING) {
       std::string language_str;
       language_value->GetAsString(&language_str);
       languages.insert(language_str);
-    } else if (language_value->GetType() == base::Value::TYPE_LIST) {
+    } else if (language_value->GetType() == base::Value::Type::LIST) {
       const base::ListValue* language_list = NULL;
       language_value->GetAsList(&language_list);
       for (size_t j = 0; j < language_list->GetSize(); ++j) {
diff --git a/chrome/browser/chromeos/locale_change_guard.cc b/chrome/browser/chromeos/locale_change_guard.cc
index 0d69eb9..c7950b6 100644
--- a/chrome/browser/chromeos/locale_change_guard.cc
+++ b/chrome/browser/chromeos/locale_change_guard.cc
@@ -25,7 +25,6 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -73,12 +72,8 @@
   if (!connector)
     return;
 
-  if (chrome::IsRunningInMash()) {
-    connector->ConnectToInterface("ash", &notification_controller_);
-  } else {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &notification_controller_);
-  }
+  connector->ConnectToInterface(ash_util::GetAshServiceName(),
+                                &notification_controller_);
 }
 
 void LocaleChangeGuard::RevertLocaleChange() {
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 77b8cf13..7d6b23c 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -120,6 +120,7 @@
     ::switches::kEnablePreferCompositingToLCDText,
     ::switches::kEnableRGBA4444Textures,
     ::switches::kEnableSlimmingPaintV2,
+    ::switches::kEnableSlimmingPaintInvalidation,
     ::switches::kEnableTouchDragDrop,
     ::switches::kEnableUnifiedDesktop,
     ::switches::kEnableUseZoomForDSF,
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
index 367766a..0b031e8 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
@@ -74,9 +74,6 @@
   captive_portal_status_ = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN;
 }
 
-void AutoEnrollmentCheckScreen::PrepareToShow() {
-}
-
 void AutoEnrollmentCheckScreen::Show() {
   // If the decision got made already, don't show the screen at all.
   if (AutoEnrollmentController::GetMode() !=
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
index c68b8a7..ce4241da 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
@@ -48,7 +48,6 @@
   }
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
index 7b063163..9e6cee7 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
@@ -141,10 +141,6 @@
   callback.Run();
 }
 
-void EnrollmentScreen::PrepareToShow() {
-  actor_->PrepareToShow();
-}
-
 void EnrollmentScreen::Show() {
   UMA(policy::kMetricEnrollmentTriggered);
   switch (current_auth_) {
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
index 9bd361d0..d7b7bd6e 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
@@ -54,7 +54,6 @@
       pairing_chromeos::ControllerPairingController* shark_controller);
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen_actor.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen_actor.h
index 60cd18e..9d2fa2d 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen_actor.h
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen_actor.h
@@ -43,9 +43,6 @@
   virtual void SetParameters(Controller* controller,
                              const policy::EnrollmentConfig& config) = 0;
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
diff --git a/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h
index c037da4..a9be5222 100644
--- a/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h
+++ b/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h
@@ -29,7 +29,6 @@
 
   MOCK_METHOD2(SetParameters,
                void(Controller*, const policy::EnrollmentConfig& config));
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD0(ShowSigninScreen, void());
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index e859b047..b276792 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -542,6 +542,9 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OobeBaseTest::SetUpCommandLine(command_line);
     fake_cws_->Init(embedded_test_server());
+
+    if (use_consumer_kiosk_mode_)
+      command_line->AppendSwitch(switches::kEnableConsumerKiosk);
   }
 
   void LaunchApp(const std::string& app_id, bool diagnostic_mode) {
@@ -714,15 +717,13 @@
   }
 
   void EnableConsumerKioskMode() {
-    std::unique_ptr<bool> locked(new bool(false));
+    bool locked = false;
     scoped_refptr<content::MessageLoopRunner> runner =
         new content::MessageLoopRunner;
-    KioskAppManager::Get()->EnableConsumerKioskAutoLaunch(
-        base::Bind(&ConsumerKioskModeAutoStartLockCheck,
-                   locked.get(),
-                   runner->QuitClosure()));
+    KioskAppManager::Get()->EnableConsumerKioskAutoLaunch(base::Bind(
+        &ConsumerKioskModeAutoStartLockCheck, &locked, runner->QuitClosure()));
     runner->Run();
-    EXPECT_TRUE(*locked.get());
+    EXPECT_TRUE(locked);
   }
 
   KioskAppManager::ConsumerKioskAutoLaunchStatus
diff --git a/chrome/browser/chromeos/login/resource_loader_browsertest.cc b/chrome/browser/chromeos/login/resource_loader_browsertest.cc
index ed093c87..ab0d23a 100644
--- a/chrome/browser/chromeos/login/resource_loader_browsertest.cc
+++ b/chrome/browser/chromeos/login/resource_loader_browsertest.cc
@@ -4,19 +4,22 @@
 
 #include <string>
 
+#include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/macros.h"
+#include "base/path_service.h"
+#include "base/strings/string_piece.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_details.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/resource/scale_factor.h"
 #include "ui/login/grit/login_resources.h"
 #include "url/gurl.h"
 
@@ -35,14 +38,21 @@
   ResourceLoaderBrowserTest() {}
 
  protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Needed to load file:// URLs in XHRs.
-    command_line->AppendSwitch(switches::kDisableWebSecurity);
-  }
-
   void SetUpOnMainThread() override {
+    // Load the data pack containing resource_loader.js.
+    base::FilePath resources_pack_path;
+    ASSERT_TRUE(PathService::Get(base::DIR_MODULE, &resources_pack_path));
+    resources_pack_path =
+        resources_pack_path.AppendASCII("gen/ui/login/login_resources.pak");
+    ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        resources_pack_path, ui::SCALE_FACTOR_NONE);
+    const base::StringPiece resource_loader_js =
+        ResourceBundle::GetSharedInstance().GetRawDataResource(
+            IDR_OOBE_RESOURCE_LOADER_JS);
+    EXPECT_FALSE(resource_loader_js.empty());
+
     // Create the root page containing resource_loader.js.
-    std::string root_page =
+    const std::string root_page =
         "<html>"
         "<head>"
         "<script>"
@@ -52,10 +62,8 @@
         "  };"
         "  $ = document.getElementById.bind(document);"
         "</script>"
-        "<script>";
-    ResourceBundle::GetSharedInstance().GetRawDataResource(
-        IDR_OOBE_RESOURCE_LOADER_JS).AppendToString(&root_page);
-    root_page +=
+        "<script>" +
+        resource_loader_js.as_string() +
         "</script>"
         "</head>"
         "<body>"
diff --git a/chrome/browser/chromeos/login/screens/app_launch_splash_screen_actor.h b/chrome/browser/chromeos/login/screens/app_launch_splash_screen_actor.h
index a1816bf..27163215 100644
--- a/chrome/browser/chromeos/login/screens/app_launch_splash_screen_actor.h
+++ b/chrome/browser/chromeos/login/screens/app_launch_splash_screen_actor.h
@@ -46,9 +46,6 @@
   // Sets screen this actor belongs to.
   virtual void SetDelegate(Delegate* screen) = 0;
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show(const std::string& app_id) = 0;
 
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
index 51b06870..1da3072 100644
--- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
@@ -28,8 +28,6 @@
     actor_->SetDelegate(nullptr);
 }
 
-void ArcTermsOfServiceScreen::PrepareToShow() {}
-
 void ArcTermsOfServiceScreen::Show() {
   if (!actor_)
     return;
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
index 7a90777..b046ea8 100644
--- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
@@ -23,7 +23,6 @@
   ~ArcTermsOfServiceScreen() override;
 
   // BaseScreen:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/base_screen.cc b/chrome/browser/chromeos/login/screens/base_screen.cc
index 6231ea6d..55397f17 100644
--- a/chrome/browser/chromeos/login/screens/base_screen.cc
+++ b/chrome/browser/chromeos/login/screens/base_screen.cc
@@ -92,10 +92,6 @@
   return true;
 }
 
-bool BaseScreen::IsPermanent() {
-  return false;
-}
-
 std::string BaseScreen::GetID() const {
   // TODO (ygorshenin, crbug.com/433797): elimitate intermediate
   // GetName() ASAP.
diff --git a/chrome/browser/chromeos/login/screens/base_screen.h b/chrome/browser/chromeos/login/screens/base_screen.h
index f170120..c5d0b95 100644
--- a/chrome/browser/chromeos/login/screens/base_screen.h
+++ b/chrome/browser/chromeos/login/screens/base_screen.h
@@ -25,9 +25,7 @@
 // Screens are identified by ID, screen and it's JS counterpart must have same
 // id.
 // Most of the screens will be re-created for each appearance with Initialize()
-// method called just once. However if initialization is too expensive, screen
-// can override result of IsPermanent() method, and do clean-up upon subsequent
-// Initialize() method calls.
+// method called just once.
 class BaseScreen {
  public:
   explicit BaseScreen(BaseScreenDelegate* base_screen_delegate);
@@ -35,8 +33,6 @@
 
   // ---- Old implementation ----
 
-  virtual void PrepareToShow() = 0;
-
   // Makes wizard screen visible.
   virtual void Show() = 0;
 
@@ -50,8 +46,7 @@
 
   // Called to perform initialization of the screen. UI is guaranteed to exist
   // at this point. Screen can alter context, resulting context will be passed
-  // to JS. This method will be called once per instance of the Screen object,
-  // unless |IsPermanent()| returns |true|.
+  // to JS. This method will be called once per instance of the Screen object.
   virtual void Initialize(::login::ScreenContext* context);
 
   // Called when screen appears.
@@ -69,10 +64,6 @@
   // displayed.
   virtual bool IsStatusAreaDisplayed();
 
-  // If this method returns |true|, screen will not be deleted once we leave it.
-  // However, Initialize() might be called several times in this case.
-  virtual bool IsPermanent();
-
   // Returns the identifier of the screen.
   virtual std::string GetID() const;
 
diff --git a/chrome/browser/chromeos/login/screens/controller_pairing_screen.cc b/chrome/browser/chromeos/login/screens/controller_pairing_screen.cc
index 20a9d7f..51e7105 100644
--- a/chrome/browser/chromeos/login/screens/controller_pairing_screen.cc
+++ b/chrome/browser/chromeos/login/screens/controller_pairing_screen.cc
@@ -53,9 +53,6 @@
   return stage == current_stage_;
 }
 
-void ControllerPairingScreen::PrepareToShow() {
-}
-
 void ControllerPairingScreen::Show() {
   if (actor_)
     actor_->Show();
diff --git a/chrome/browser/chromeos/login/screens/controller_pairing_screen.h b/chrome/browser/chromeos/login/screens/controller_pairing_screen.h
index 3fd6376..6dcfa18 100644
--- a/chrome/browser/chromeos/login/screens/controller_pairing_screen.h
+++ b/chrome/browser/chromeos/login/screens/controller_pairing_screen.h
@@ -44,7 +44,6 @@
   bool ExpectStageIs(Stage stage) const;
 
   // Overridden from BaseScreen:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/device_disabled_screen.cc b/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
index c3bfcfc..5c51620f 100644
--- a/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
+++ b/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
@@ -33,9 +33,6 @@
   device_disabling_manager_->RemoveObserver(this);
 }
 
-void DeviceDisabledScreen::PrepareToShow() {
-}
-
 void DeviceDisabledScreen::Show() {
   if (!actor_ || showing_)
     return;
diff --git a/chrome/browser/chromeos/login/screens/device_disabled_screen.h b/chrome/browser/chromeos/login/screens/device_disabled_screen.h
index 73f13c2b..ad1004d 100644
--- a/chrome/browser/chromeos/login/screens/device_disabled_screen.h
+++ b/chrome/browser/chromeos/login/screens/device_disabled_screen.h
@@ -28,7 +28,6 @@
   ~DeviceDisabledScreen() override;
 
   // BaseScreen:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/enable_debugging_screen.cc b/chrome/browser/chromeos/login/screens/enable_debugging_screen.cc
index 7f74832..a4e605e 100644
--- a/chrome/browser/chromeos/login/screens/enable_debugging_screen.cc
+++ b/chrome/browser/chromeos/login/screens/enable_debugging_screen.cc
@@ -24,11 +24,6 @@
     actor_->SetDelegate(NULL);
 }
 
-void EnableDebuggingScreen::PrepareToShow() {
-  if (actor_)
-    actor_->PrepareToShow();
-}
-
 void EnableDebuggingScreen::Show() {
   if (actor_)
     actor_->Show();
diff --git a/chrome/browser/chromeos/login/screens/enable_debugging_screen.h b/chrome/browser/chromeos/login/screens/enable_debugging_screen.h
index ea727c0..49cad4b8 100644
--- a/chrome/browser/chromeos/login/screens/enable_debugging_screen.h
+++ b/chrome/browser/chromeos/login/screens/enable_debugging_screen.h
@@ -23,7 +23,6 @@
   ~EnableDebuggingScreen() override;
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/enable_debugging_screen_actor.h b/chrome/browser/chromeos/login/screens/enable_debugging_screen_actor.h
index 2b1592f..66a1d99a 100644
--- a/chrome/browser/chromeos/login/screens/enable_debugging_screen_actor.h
+++ b/chrome/browser/chromeos/login/screens/enable_debugging_screen_actor.h
@@ -28,7 +28,6 @@
 
   virtual ~EnableDebuggingScreenActor() {}
 
-  virtual void PrepareToShow() = 0;
   virtual void Show() = 0;
   virtual void Hide() = 0;
   virtual void SetDelegate(Delegate* delegate) = 0;
diff --git a/chrome/browser/chromeos/login/screens/error_screen.cc b/chrome/browser/chromeos/login/screens/error_screen.cc
index 95fe458..cd1561b0 100644
--- a/chrome/browser/chromeos/login/screens/error_screen.cc
+++ b/chrome/browser/chromeos/login/screens/error_screen.cc
@@ -71,11 +71,6 @@
     view_->Unbind();
 }
 
-void ErrorScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 void ErrorScreen::Show() {
   if (!on_hide_callback_) {
     SetHideCallback(base::Bind(&ErrorScreen::DefaultHideCallback,
diff --git a/chrome/browser/chromeos/login/screens/error_screen.h b/chrome/browser/chromeos/login/screens/error_screen.h
index 7d1bfc6..d8f1fa9 100644
--- a/chrome/browser/chromeos/login/screens/error_screen.h
+++ b/chrome/browser/chromeos/login/screens/error_screen.h
@@ -33,7 +33,6 @@
   ~ErrorScreen() override;
 
   // NetworkErrorModel:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void OnShow() override;
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.cc b/chrome/browser/chromeos/login/screens/eula_screen.cc
index dfcd778..3b2719fd 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/eula_screen.cc
@@ -36,11 +36,6 @@
     view_->Unbind();
 }
 
-void EulaScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 void EulaScreen::Show() {
   // Command to own the TPM.
   DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.h b/chrome/browser/chromeos/login/screens/eula_screen.h
index d6137e7..83a0d4a 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/eula_screen.h
@@ -36,7 +36,6 @@
   ~EulaScreen() override;
 
   // EulaModel implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   GURL GetOemEulaUrl() const override;
diff --git a/chrome/browser/chromeos/login/screens/eula_view.h b/chrome/browser/chromeos/login/screens/eula_view.h
index 99cb947..1d12715 100644
--- a/chrome/browser/chromeos/login/screens/eula_view.h
+++ b/chrome/browser/chromeos/login/screens/eula_view.h
@@ -18,7 +18,6 @@
  public:
   virtual ~EulaView() {}
 
-  virtual void PrepareToShow() = 0;
   virtual void Show() = 0;
   virtual void Hide() = 0;
   virtual void Bind(EulaModel& model) = 0;
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
index 9c80bd7..763e851 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
@@ -78,11 +78,6 @@
     adapter_->RemoveObserver(this);
 }
 
-void HIDDetectionScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 void HIDDetectionScreen::Show() {
   showing_ = true;
   GetContextEditor().SetBoolean(kContextKeyNumKeysEnteredExpected, false);
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.h b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
index 9f552a1e..66cb6ba 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.h
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
@@ -48,7 +48,6 @@
   ~HIDDetectionScreen() override;
 
   // HIDDetectionModel implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Initialize(::login::ScreenContext* context) override;
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_view.h b/chrome/browser/chromeos/login/screens/hid_detection_view.h
index affe37a..f325fcbe 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_view.h
+++ b/chrome/browser/chromeos/login/screens/hid_detection_view.h
@@ -20,7 +20,6 @@
  public:
   virtual ~HIDDetectionView() {}
 
-  virtual void PrepareToShow() = 0;
   virtual void Show() = 0;
   virtual void Hide() = 0;
   virtual void Bind(HIDDetectionModel& model) = 0;
diff --git a/chrome/browser/chromeos/login/screens/host_pairing_screen.cc b/chrome/browser/chromeos/login/screens/host_pairing_screen.cc
index 6597f096..93136a8 100644
--- a/chrome/browser/chromeos/login/screens/host_pairing_screen.cc
+++ b/chrome/browser/chromeos/login/screens/host_pairing_screen.cc
@@ -47,9 +47,6 @@
     actor_->OnContextChanged(diff);
 }
 
-void HostPairingScreen::PrepareToShow() {
-}
-
 void HostPairingScreen::Show() {
   if (actor_)
     actor_->Show();
diff --git a/chrome/browser/chromeos/login/screens/host_pairing_screen.h b/chrome/browser/chromeos/login/screens/host_pairing_screen.h
index 984ae672..ed03abf 100644
--- a/chrome/browser/chromeos/login/screens/host_pairing_screen.h
+++ b/chrome/browser/chromeos/login/screens/host_pairing_screen.h
@@ -49,7 +49,6 @@
   void CommitContextChanges();
 
   // Overridden from BaseScreen:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
index f9316d0a..e5e01b4 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
+++ b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
@@ -24,7 +24,6 @@
   ~KioskAutolaunchScreen() override;
 
   // BaseScreen implementation:
-  void PrepareToShow() override {}
   void Show() override;
   void Hide() override {}
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/kiosk_enable_screen.h b/chrome/browser/chromeos/login/screens/kiosk_enable_screen.h
index c154612e..baad510b 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_enable_screen.h
+++ b/chrome/browser/chromeos/login/screens/kiosk_enable_screen.h
@@ -24,7 +24,6 @@
   ~KioskEnableScreen() override;
 
   // BaseScreen implementation:
-  void PrepareToShow() override {}
   void Show() override;
   void Hide() override {}
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/mock_enable_debugging_screen.h b/chrome/browser/chromeos/login/screens/mock_enable_debugging_screen.h
index 85ec708..27076dc 100644
--- a/chrome/browser/chromeos/login/screens/mock_enable_debugging_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_enable_debugging_screen.h
@@ -25,7 +25,6 @@
   MockEnableDebuggingScreenActor();
   ~MockEnableDebuggingScreenActor() override;
 
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(MockSetDelegate, void(Delegate* delegate));
diff --git a/chrome/browser/chromeos/login/screens/mock_error_screen.h b/chrome/browser/chromeos/login/screens/mock_error_screen.h
index 33c5e11..0a073a0 100644
--- a/chrome/browser/chromeos/login/screens/mock_error_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_error_screen.h
@@ -39,7 +39,6 @@
   void Bind(NetworkErrorModel& model) override;
   void Unbind() override;
 
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(MockBind, void(NetworkErrorModel& model));
diff --git a/chrome/browser/chromeos/login/screens/mock_eula_screen.h b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
index 2186750..59743422 100644
--- a/chrome/browser/chromeos/login/screens/mock_eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
@@ -28,7 +28,6 @@
   void Bind(EulaModel& model) override;
   void Unbind() override;
 
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
 
diff --git a/chrome/browser/chromeos/login/screens/mock_network_screen.h b/chrome/browser/chromeos/login/screens/mock_network_screen.h
index 9ccae6bd..06bd599 100644
--- a/chrome/browser/chromeos/login/screens/mock_network_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_network_screen.h
@@ -33,7 +33,6 @@
 
   MOCK_METHOD1(MockBind, void(NetworkModel& model));
   MOCK_METHOD0(MockUnbind, void());
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(ShowError, void(const base::string16& message));
diff --git a/chrome/browser/chromeos/login/screens/mock_update_screen.h b/chrome/browser/chromeos/login/screens/mock_update_screen.h
index e6b0334..896eb77e 100644
--- a/chrome/browser/chromeos/login/screens/mock_update_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_update_screen.h
@@ -29,7 +29,6 @@
   void Bind(UpdateModel& model) override;
   void Unbind() override;
 
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(MockBind, void(UpdateModel& model));
diff --git a/chrome/browser/chromeos/login/screens/mock_wrong_hwid_screen.h b/chrome/browser/chromeos/login/screens/mock_wrong_hwid_screen.h
index 067ce4b..dfa180f 100644
--- a/chrome/browser/chromeos/login/screens/mock_wrong_hwid_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_wrong_hwid_screen.h
@@ -27,7 +27,6 @@
 
   void SetDelegate(Delegate* delegate) override;
 
-  MOCK_METHOD0(PrepareToShow, void());
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(MockSetDelegate, void(Delegate*));
diff --git a/chrome/browser/chromeos/login/screens/network_error_view.h b/chrome/browser/chromeos/login/screens/network_error_view.h
index 112dee0..eb5ae272 100644
--- a/chrome/browser/chromeos/login/screens/network_error_view.h
+++ b/chrome/browser/chromeos/login/screens/network_error_view.h
@@ -18,9 +18,6 @@
  public:
   virtual ~NetworkErrorView() {}
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
diff --git a/chrome/browser/chromeos/login/screens/network_screen.cc b/chrome/browser/chromeos/login/screens/network_screen.cc
index a0235c9..9676037 100644
--- a/chrome/browser/chromeos/login/screens/network_screen.cc
+++ b/chrome/browser/chromeos/login/screens/network_screen.cc
@@ -79,11 +79,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // NetworkScreen, NetworkModel implementation:
 
-void NetworkScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 void NetworkScreen::Show() {
   Refresh();
 
diff --git a/chrome/browser/chromeos/login/screens/network_screen.h b/chrome/browser/chromeos/login/screens/network_screen.h
index 0026786..512da2c3 100644
--- a/chrome/browser/chromeos/login/screens/network_screen.h
+++ b/chrome/browser/chromeos/login/screens/network_screen.h
@@ -62,7 +62,6 @@
   static NetworkScreen* Get(ScreenManager* manager);
 
   // NetworkModel implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Initialize(::login::ScreenContext* context) override;
diff --git a/chrome/browser/chromeos/login/screens/network_view.h b/chrome/browser/chromeos/login/screens/network_view.h
index 29f4bac..37bcac8a 100644
--- a/chrome/browser/chromeos/login/screens/network_view.h
+++ b/chrome/browser/chromeos/login/screens/network_view.h
@@ -17,9 +17,6 @@
  public:
   virtual ~NetworkView() {}
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
diff --git a/chrome/browser/chromeos/login/screens/reset_screen.cc b/chrome/browser/chromeos/login/screens/reset_screen.cc
index 20c6d9c..c6d577c 100644
--- a/chrome/browser/chromeos/login/screens/reset_screen.cc
+++ b/chrome/browser/chromeos/login/screens/reset_screen.cc
@@ -49,11 +49,6 @@
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
 }
 
-void ResetScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 void ResetScreen::Show() {
   if (view_)
     view_->Show();
diff --git a/chrome/browser/chromeos/login/screens/reset_screen.h b/chrome/browser/chromeos/login/screens/reset_screen.h
index 0785934..40fac93 100644
--- a/chrome/browser/chromeos/login/screens/reset_screen.h
+++ b/chrome/browser/chromeos/login/screens/reset_screen.h
@@ -30,7 +30,6 @@
   ~ResetScreen() override;
 
   // ResetModel implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void OnViewDestroyed(ResetView* view) override;
diff --git a/chrome/browser/chromeos/login/screens/reset_view.h b/chrome/browser/chromeos/login/screens/reset_view.h
index b72e6ae..b146019 100644
--- a/chrome/browser/chromeos/login/screens/reset_view.h
+++ b/chrome/browser/chromeos/login/screens/reset_view.h
@@ -17,7 +17,6 @@
 
   virtual void Bind(ResetModel& model) = 0;
   virtual void Unbind() = 0;
-  virtual void PrepareToShow() = 0;
   virtual void Show() = 0;
   virtual void Hide() = 0;
 };
diff --git a/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
index 00182ee..5595a5fe 100644
--- a/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
+++ b/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
@@ -40,9 +40,6 @@
     actor_->SetDelegate(NULL);
 }
 
-void TermsOfServiceScreen::PrepareToShow() {
-}
-
 void TermsOfServiceScreen::Show() {
   if (!actor_)
     return;
diff --git a/chrome/browser/chromeos/login/screens/terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
index 78ce182..26f26c6 100644
--- a/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
+++ b/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
@@ -35,7 +35,6 @@
   ~TermsOfServiceScreen() override;
 
   // BaseScreen:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc
index 7376bba..46175f8 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen.cc
@@ -321,13 +321,6 @@
   network_portal_detector::GetInstance()->AddAndFireObserver(this);
 }
 
-void UpdateScreen::PrepareToShow() {
-  if (!view_)
-    return;
-
-  view_->PrepareToShow();
-}
-
 void UpdateScreen::Show() {
   is_shown_ = true;
   histogram_helper_->OnScreenShow();
diff --git a/chrome/browser/chromeos/login/screens/update_screen.h b/chrome/browser/chromeos/login/screens/update_screen.h
index c4c6baa..020c000 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.h
+++ b/chrome/browser/chromeos/login/screens/update_screen.h
@@ -42,7 +42,6 @@
   static UpdateScreen* Get(ScreenManager* manager);
 
   // UpdateModel:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Initialize(::login::ScreenContext* context) override;
diff --git a/chrome/browser/chromeos/login/screens/update_view.h b/chrome/browser/chromeos/login/screens/update_view.h
index b188aff..58c3391 100644
--- a/chrome/browser/chromeos/login/screens/update_view.h
+++ b/chrome/browser/chromeos/login/screens/update_view.h
@@ -17,9 +17,6 @@
  public:
   virtual ~UpdateView() {}
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index d0d6e83..bd038496 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -216,12 +216,6 @@
   ExitScreen();
 }
 
-
-void UserImageScreen::PrepareToShow() {
-  if (view_)
-    view_->PrepareToShow();
-}
-
 const user_manager::User* UserImageScreen::GetUser() {
   return user_manager::UserManager::Get()->GetActiveUser();
 }
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.h b/chrome/browser/chromeos/login/screens/user_image_screen.h
index 338d373..08bcce8 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.h
@@ -47,7 +47,6 @@
   static UserImageScreen* Get(ScreenManager* manager);
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
 
diff --git a/chrome/browser/chromeos/login/screens/user_image_view.h b/chrome/browser/chromeos/login/screens/user_image_view.h
index ee52038..701cd5d 100644
--- a/chrome/browser/chromeos/login/screens/user_image_view.h
+++ b/chrome/browser/chromeos/login/screens/user_image_view.h
@@ -19,9 +19,6 @@
 
   virtual void Unbind() = 0;
 
-  // Prepare the contents to showing.
-  virtual void PrepareToShow() = 0;
-
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
diff --git a/chrome/browser/chromeos/login/screens/wrong_hwid_screen.cc b/chrome/browser/chromeos/login/screens/wrong_hwid_screen.cc
index b5baca1..661b0d25 100644
--- a/chrome/browser/chromeos/login/screens/wrong_hwid_screen.cc
+++ b/chrome/browser/chromeos/login/screens/wrong_hwid_screen.cc
@@ -22,11 +22,6 @@
     actor_->SetDelegate(NULL);
 }
 
-void WrongHWIDScreen::PrepareToShow() {
-  if (actor_)
-    actor_->PrepareToShow();
-}
-
 void WrongHWIDScreen::Show() {
   if (actor_)
     actor_->Show();
diff --git a/chrome/browser/chromeos/login/screens/wrong_hwid_screen.h b/chrome/browser/chromeos/login/screens/wrong_hwid_screen.h
index 1631d1e..55e486ae 100644
--- a/chrome/browser/chromeos/login/screens/wrong_hwid_screen.h
+++ b/chrome/browser/chromeos/login/screens/wrong_hwid_screen.h
@@ -24,7 +24,6 @@
   ~WrongHWIDScreen() override;
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/screens/wrong_hwid_screen_actor.h b/chrome/browser/chromeos/login/screens/wrong_hwid_screen_actor.h
index 4e41f22a..fa9faac1 100644
--- a/chrome/browser/chromeos/login/screens/wrong_hwid_screen_actor.h
+++ b/chrome/browser/chromeos/login/screens/wrong_hwid_screen_actor.h
@@ -28,7 +28,6 @@
 
   virtual ~WrongHWIDScreenActor() {}
 
-  virtual void PrepareToShow() = 0;
   virtual void Show() = 0;
   virtual void Hide() = 0;
   virtual void SetDelegate(Delegate* delegate) = 0;
diff --git a/chrome/browser/chromeos/login/signin/merge_session_resource_throttle.cc b/chrome/browser/chromeos/login/signin/merge_session_resource_throttle.cc
index 41d90bfa..5483789 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_resource_throttle.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_resource_throttle.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/chromeos/login/signin/merge_session_xhr_request_waiter.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "net/url_request/url_request.h"
@@ -77,5 +76,5 @@
 
 void MergeSessionResourceThrottle::OnBlockingPageComplete() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  controller()->Resume();
+  Resume();
 }
diff --git a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
index 182474e..edab4bd 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
+++ b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.cc
@@ -184,4 +184,27 @@
                                        google_util::ALLOW_SUBDOMAIN);
 }
 
+bool IsSessionRestorePending(Profile* profile) {
+  if (!profile)
+    return false;
+
+  chromeos::OAuth2LoginManager* login_manager =
+      chromeos::OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
+          profile);
+  bool pending_session_restore = false;
+  if (login_manager) {
+    switch (login_manager->state()) {
+      case chromeos::OAuth2LoginManager::SESSION_RESTORE_PREPARING:
+      case chromeos::OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS:
+        pending_session_restore = true;
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  return pending_session_restore;
+}
+
 }  // namespace merge_session_throttling_utils
diff --git a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h
index e1227da..2249868 100644
--- a/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h
+++ b/chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h
@@ -47,6 +47,9 @@
 // called on any thread.
 bool ShouldDelayUrl(const GURL& url);
 
+// True if session restore hasn't started or in progress.
+bool IsSessionRestorePending(Profile* profile);
+
 }  // namespace merge_session_throttling_utils
 
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SIGNIN_MERGE_SESSION_THROTTLING_UTILS_H_
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
index 5e266f5..c1ce06f9 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.cc
@@ -128,11 +128,6 @@
   network_portal_detector::GetInstance()->RemoveObserver(this);
 }
 
-void SupervisedUserCreationScreen::PrepareToShow() {
-  if (actor_)
-    actor_->PrepareToShow();
-}
-
 void SupervisedUserCreationScreen::Show() {
   CameraPresenceNotifier::GetInstance()->AddObserver(this);
   if (actor_) {
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
index 63780ea..e123bb0 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h
@@ -75,7 +75,6 @@
   void OnSupervisedUsersChanged() override;
 
   // BaseScreen implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
index 32aa642..c8bb5f6 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_impl.cc
@@ -1133,7 +1133,7 @@
                                  ash::kShellWindowId_LockScreenContainer);
   } else {
     using ui::mojom::WindowManager;
-    params.mus_properties[WindowManager::kInitialContainerId_Property] =
+    params.mus_properties[WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(
             ash::kShellWindowId_LockScreenContainer);
   }
diff --git a/chrome/browser/chromeos/login/ui/models/user_board_model.h b/chrome/browser/chromeos/login/ui/models/user_board_model.h
index 5a568e0..ebbebdd1 100644
--- a/chrome/browser/chromeos/login/ui/models/user_board_model.h
+++ b/chrome/browser/chromeos/login/ui/models/user_board_model.h
@@ -28,7 +28,6 @@
   std::string GetName() const override;
 
   // Temorary unused methods:
-  void PrepareToShow() override{};
   void Show() override{};
   void Hide() override{};
 };
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc
index a83d5c8f..a9d53c9 100644
--- a/chrome/browser/chromeos/login/ui/webui_login_view.cc
+++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -19,6 +19,7 @@
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
 #include "chrome/browser/chromeos/login/ui/proxy_settings_dialog.h"
 #include "chrome/browser/chromeos/login/ui/web_contents_set_background_color.h"
@@ -191,9 +192,11 @@
   accel_map_[ui::Accelerator(
       ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
       kAccelNameEnrollmentAd;
-  accel_map_[ui::Accelerator(ui::VKEY_K,
-                             ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
-      kAccelNameKioskEnable;
+  if (KioskAppManager::IsConsumerKioskEnabled()) {
+    accel_map_[ui::Accelerator(ui::VKEY_K,
+                               ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
+        kAccelNameKioskEnable;
+  }
   accel_map_[ui::Accelerator(ui::VKEY_V, ui::EF_ALT_DOWN)] =
       kAccelNameVersion;
   accel_map_[ui::Accelerator(ui::VKEY_R,
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index d633dd1..e2e49ef 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -182,7 +182,8 @@
       return;
 
     ash::mojom::WallpaperControllerPtr wallpaper_controller;
-    connector->ConnectToInterface("ash", &wallpaper_controller);
+    connector->ConnectToInterface(ash_util::GetAshServiceName(),
+                                  &wallpaper_controller);
     // TODO(crbug.com/655875): Optimize ash wallpaper transport; avoid sending
     // large bitmaps over Mojo; use shared memory like BitmapUploader, etc.
     wallpaper_controller->SetWallpaper(*image.bitmap(), layout);
@@ -369,11 +370,6 @@
   wallpaper_manager = nullptr;
 }
 
-void WallpaperManager::BindRequest(
-    ash::mojom::WallpaperManagerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
 WallpaperManager::WallpaperResolution
 WallpaperManager::GetAppropriateResolution() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -807,7 +803,7 @@
 // WallpaperManager, private: --------------------------------------------------
 
 WallpaperManager::WallpaperManager()
-    : pending_inactive_(NULL), weak_factory_(this) {
+    : binding_(this), pending_inactive_(nullptr), weak_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   wallpaper::WallpaperManagerBase::SetPathIds(
       chrome::DIR_USER_DATA, chrome::DIR_CHROMEOS_WALLPAPERS,
@@ -828,6 +824,18 @@
               sequence_token_, base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
 
   user_manager::UserManager::Get()->AddSessionStateObserver(this);
+
+  content::ServiceManagerConnection* connection =
+      content::ServiceManagerConnection::GetForProcess();
+  if (connection && connection->GetConnector()) {
+    // Connect to the wallpaper controller interface in the ash service.
+    ash::mojom::WallpaperControllerPtr wallpaper_controller_ptr;
+    connection->GetConnector()->ConnectToInterface(
+        ash_util::GetAshServiceName(), &wallpaper_controller_ptr);
+    // Register this object as the wallpaper picker.
+    wallpaper_controller_ptr->SetWallpaperPicker(
+        binding_.CreateInterfacePtrAndBind());
+  }
 }
 
 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper(
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
index 2a6d7cb..206bcfef 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h
@@ -21,14 +21,14 @@
 #include "components/wallpaper/wallpaper_manager_base.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/gfx/image/image_skia.h"
 
 namespace chromeos {
 
 class WallpaperManager
     : public wallpaper::WallpaperManagerBase,
-      public ash::mojom::WallpaperManager,
+      public ash::mojom::WallpaperPicker,
       public content::NotificationObserver,
       public user_manager::UserManager::UserSessionStateObserver {
  public:
@@ -47,9 +47,6 @@
   // WallpaperManager to remove any observers it has registered.
   static void Shutdown();
 
-  // Binds the mojom::WallpaperManager interface request to this object.
-  void BindRequest(ash::mojom::WallpaperManagerRequest request);
-
   // wallpaper::WallpaperManagerBase:
   WallpaperResolution GetAppropriateResolution() override;
   void AddObservers() override;
@@ -85,7 +82,7 @@
   wallpaper::WallpaperFilesId GetFilesId(
       const AccountId& account_id) const override;
 
-  // ash::mojom::WallpaperManager:
+  // ash::mojom::WallpaperPicker:
   void Open() override;
 
   // content::NotificationObserver:
@@ -163,7 +160,7 @@
       const base::FilePath& customized_default_wallpaper_file_large,
       std::unique_ptr<gfx::ImageSkia> large_wallpaper_image) override;
 
-  mojo::BindingSet<ash::mojom::WallpaperManager> bindings_;
+  mojo::Binding<ash::mojom::WallpaperPicker> binding_;
 
   std::unique_ptr<CrosSettings::ObserverSubscription>
       show_user_name_on_signin_subscription_;
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
index 0afe0d39..8192aa4 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/files/file_util.h"
 #include "base/json/json_writer.h"
 #include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
@@ -162,15 +163,13 @@
     const base::FilePath user_key_file =
         user_keys_dir.AppendASCII(sanitized_user_id)
                      .AppendASCII("policy.pub");
-    std::vector<uint8_t> user_key_bits;
-    EXPECT_TRUE(user_policy_builder->GetSigningKey()->
-                ExportPublicKey(&user_key_bits));
+    std::string user_key_bits =
+        user_policy_builder->GetPublicSigningKeyAsString();
+    EXPECT_FALSE(user_key_bits.empty());
     EXPECT_TRUE(base::CreateDirectory(user_key_file.DirName()));
-    EXPECT_EQ(base::WriteFile(
-                  user_key_file,
-                  reinterpret_cast<const char*>(user_key_bits.data()),
-                  user_key_bits.size()),
-              static_cast<int>(user_key_bits.size()));
+    EXPECT_EQ(base::WriteFile(user_key_file, user_key_bits.data(),
+                              user_key_bits.length()),
+              base::checked_cast<int>(user_key_bits.length()));
     user_policy_builder->policy_data().set_username(account_id.GetUserEmail());
     return user_policy_builder;
   }
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc
index c3b1e1b..a53ed1e 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -131,7 +131,7 @@
   std::unique_ptr<base::Value> root =
       base::JSONReader::Read(config, base::JSON_ALLOW_TRAILING_COMMAS);
   DCHECK(root.get() != NULL);
-  if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (!root.get() || root->GetType() != base::Value::Type::DICTIONARY) {
     LOG(WARNING) << "Bad cellular config file";
     return false;
   }
diff --git a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
index 54fa9c16..928be755 100644
--- a/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
+++ b/chrome/browser/chromeos/ownership/owner_settings_service_chromeos.cc
@@ -59,7 +59,7 @@
     return false;
   }
   const base::Value* value = CrosSettings::Get()->GetPref(kDeviceOwner);
-  if (!value || value->GetType() != base::Value::TYPE_STRING)
+  if (!value || value->GetType() != base::Value::Type::STRING)
     return false;
   return static_cast<const base::StringValue*>(value)->GetString() == user_id;
 }
@@ -275,7 +275,7 @@
                                                 const base::Value& value) {
   DCHECK(thread_checker_.CalledOnValidThread());
   const base::Value* old_value = CrosSettings::Get()->GetPref(setting);
-  if (old_value && !old_value->IsType(base::Value::TYPE_LIST))
+  if (old_value && !old_value->IsType(base::Value::Type::LIST))
     return false;
   std::unique_ptr<base::ListValue> new_value(
       old_value ? static_cast<const base::ListValue*>(old_value)->DeepCopy()
@@ -288,7 +288,7 @@
                                                   const base::Value& value) {
   DCHECK(thread_checker_.CalledOnValidThread());
   const base::Value* old_value = CrosSettings::Get()->GetPref(setting);
-  if (old_value && !old_value->IsType(base::Value::TYPE_LIST))
+  if (old_value && !old_value->IsType(base::Value::Type::LIST))
     return false;
   std::unique_ptr<base::ListValue> new_value(
       old_value ? static_cast<const base::ListValue*>(old_value)->DeepCopy()
diff --git a/chrome/browser/chromeos/policy/affiliation_test_helper.cc b/chrome/browser/chromeos/policy/affiliation_test_helper.cc
index 27e76167..13310b5 100644
--- a/chrome/browser/chromeos/policy/affiliation_test_helper.cc
+++ b/chrome/browser/chromeos/policy/affiliation_test_helper.cc
@@ -6,11 +6,11 @@
 #include <stdint.h>
 
 #include <string>
-#include <vector>
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -54,13 +54,12 @@
           cryptohome::Identification(account_id));
   const base::FilePath user_key_file =
       user_keys_dir.AppendASCII(sanitized_username).AppendASCII("policy.pub");
-  std::vector<uint8_t> user_key_bits;
-  ASSERT_TRUE(user_policy->GetSigningKey()->ExportPublicKey(&user_key_bits));
+  std::string user_key_bits = user_policy->GetPublicSigningKeyAsString();
+  ASSERT_FALSE(user_key_bits.empty());
   ASSERT_TRUE(base::CreateDirectory(user_key_file.DirName()));
-  ASSERT_EQ(base::WriteFile(user_key_file,
-                            reinterpret_cast<const char*>(user_key_bits.data()),
-                            user_key_bits.size()),
-            static_cast<int>(user_key_bits.size()));
+  ASSERT_EQ(base::WriteFile(user_key_file, user_key_bits.data(),
+                            user_key_bits.length()),
+            base::checked_cast<int>(user_key_bits.length()));
 }
 
 void SetDeviceAffiliationID(
diff --git a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
index a21f61a..1ef2da9 100644
--- a/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.cc
@@ -124,8 +124,7 @@
 }  // namespace
 
 ExternalDataPolicyHandler::ExternalDataPolicyHandler(const char* policy_name)
-    : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_DICTIONARY) {
-}
+    : TypeCheckingPolicyHandler(policy_name, base::Value::Type::DICTIONARY) {}
 
 ExternalDataPolicyHandler::~ExternalDataPolicyHandler() {
 }
@@ -277,10 +276,9 @@
     const char* policy_name,
     onc::ONCSource onc_source,
     const char* pref_path)
-    : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_STRING),
+    : TypeCheckingPolicyHandler(policy_name, base::Value::Type::STRING),
       onc_source_(onc_source),
-      pref_path_(pref_path) {
-}
+      pref_path_(pref_path) {}
 
 // static
 std::unique_ptr<base::Value>
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 e1fd4d38..5484c75 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
@@ -7,7 +7,6 @@
 #include <stdint.h>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -93,7 +92,7 @@
         base::Value::Equals(&expected,
                             store_->policy_map().GetValue(
                                 key::kDeviceMetricsReportingEnabled)));
-    EXPECT_NE(std::string(), store_->policy_signature_public_key());
+    EXPECT_FALSE(store_->policy_signature_public_key().empty());
   }
 
   void PrepareExistingPolicy() {
@@ -123,25 +122,6 @@
         base::ThreadTaskRunnerHandle::Get()));
   }
 
-  static std::string ConvertPublicKeyToString(
-      const std::vector<uint8_t>& public_key) {
-    return std::string(reinterpret_cast<const char*>(public_key.data()),
-                       public_key.size());
-  }
-
-  std::string GetPolicyPublicKeyAsString() {
-    std::vector<uint8_t> public_key;
-    EXPECT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&public_key));
-    return ConvertPublicKeyToString(public_key);
-  }
-
-  std::string GetPolicyNewPublicKeyAsString() {
-    std::vector<uint8_t> new_public_key;
-    EXPECT_TRUE(
-        device_policy_.GetNewSigningKey()->ExportPublicKey(&new_public_key));
-    return ConvertPublicKeyToString(new_public_key);
-  }
-
   ScopedTestingLocalState local_state_;
   chromeos::FakeCryptohomeClient* fake_cryptohome_client_;
   std::unique_ptr<chromeos::InstallAttributes> install_attributes_;
@@ -177,7 +157,7 @@
   store_->Load();
   FlushDeviceSettings();
   ExpectSuccess();
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -186,7 +166,7 @@
   store_->Store(device_policy_.policy());
   FlushDeviceSettings();
   ExpectSuccess();
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -198,7 +178,7 @@
   EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, store_->status());
   EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE,
             store_->validation_status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -210,7 +190,7 @@
   EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, store_->status());
   EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE,
             store_->validation_status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -225,7 +205,7 @@
       *device_policy_.GetNewSigningKey());
   ReloadDeviceSettings();
   ExpectSuccess();
-  EXPECT_EQ(GetPolicyNewPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicNewSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -241,7 +221,7 @@
   EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, store_->status());
   EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE,
             store_->validation_status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -257,7 +237,7 @@
   EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, store_->status());
   EXPECT_EQ(CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE,
             store_->validation_status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -266,7 +246,7 @@
   store_->InstallInitialPolicy(device_policy_.policy());
   FlushDeviceSettings();
   ExpectSuccess();
-  EXPECT_EQ(GetPolicyNewPublicKeyAsString(),
+  EXPECT_EQ(device_policy_.GetPublicNewSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
index c4148a86..be658686 100644
--- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
+++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
@@ -6,10 +6,11 @@
 
 #include <stdint.h>
 
-#include <vector>
+#include <string>
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/settings/install_attributes.h"
@@ -43,9 +44,8 @@
   base::FilePath install_attrs_file;
   ASSERT_TRUE(
       PathService::Get(chromeos::FILE_INSTALL_ATTRIBUTES, &install_attrs_file));
-  ASSERT_EQ(static_cast<int>(install_attrs_blob.size()),
-            base::WriteFile(install_attrs_file,
-                            install_attrs_blob.c_str(),
+  ASSERT_EQ(base::checked_cast<int>(install_attrs_blob.size()),
+            base::WriteFile(install_attrs_file, install_attrs_blob.c_str(),
                             install_attrs_blob.size()));
 }
 
@@ -58,13 +58,11 @@
 
   base::FilePath owner_key_file;
   ASSERT_TRUE(PathService::Get(chromeos::FILE_OWNER_KEY, &owner_key_file));
-  std::vector<uint8_t> owner_key_bits;
-  ASSERT_TRUE(
-      device_policy()->GetSigningKey()->ExportPublicKey(&owner_key_bits));
-  ASSERT_EQ(base::WriteFile(owner_key_file, reinterpret_cast<const char*>(
-                                                owner_key_bits.data()),
-                            owner_key_bits.size()),
-            static_cast<int>(owner_key_bits.size()));
+  std::string owner_key_bits = device_policy()->GetPublicSigningKeyAsString();
+  ASSERT_FALSE(owner_key_bits.empty());
+  ASSERT_EQ(base::checked_cast<int>(owner_key_bits.length()),
+            base::WriteFile(owner_key_file, owner_key_bits.data(),
+                            owner_key_bits.length()));
 }
 
 // static
diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
index d5a080d..19a5ec5 100644
--- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -211,14 +212,12 @@
   base::FilePath user_key_file =
       user_keys_dir.AppendASCII(sanitized_username)
                    .AppendASCII("policy.pub");
-  std::vector<uint8_t> user_key_bits;
-  ASSERT_TRUE(user_policy_.GetSigningKey()->ExportPublicKey(&user_key_bits));
+  std::string user_key_bits = user_policy_.GetPublicSigningKeyAsString();
+  ASSERT_FALSE(user_key_bits.empty());
   ASSERT_TRUE(base::CreateDirectory(user_key_file.DirName()));
-  ASSERT_EQ(base::WriteFile(
-                user_key_file,
-                reinterpret_cast<const char*>(user_key_bits.data()),
-                user_key_bits.size()),
-            static_cast<int>(user_key_bits.size()));
+  ASSERT_EQ(base::checked_cast<int>(user_key_bits.length()),
+            base::WriteFile(user_key_file, user_key_bits.data(),
+                            user_key_bits.length()));
 }
 
 void PowerPolicyBrowserTestBase::StoreAndReloadUserPolicy() {
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
index 6ab6043..91f3452 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -76,8 +75,8 @@
 
     // Install the initial public key, so that by default the validation of
     // the stored/loaded policy blob succeeds.
-    std::vector<uint8_t> public_key;
-    ASSERT_TRUE(policy_.GetSigningKey()->ExportPublicKey(&public_key));
+    std::string public_key = policy_.GetPublicSigningKeyAsString();
+    ASSERT_FALSE(public_key.empty());
     StoreUserPolicyKey(public_key);
 
     policy_.payload().mutable_homepagelocation()->set_value(kDefaultHomepage);
@@ -126,12 +125,10 @@
     EXPECT_TRUE(base::StringValue(expected_value).Equals(entry->value.get()));
   }
 
-  void StoreUserPolicyKey(const std::vector<uint8_t>& public_key) {
+  void StoreUserPolicyKey(const std::string& public_key) {
     ASSERT_TRUE(base::CreateDirectory(user_policy_key_file().DirName()));
-    ASSERT_TRUE(
-        base::WriteFile(user_policy_key_file(),
-                        reinterpret_cast<const char*>(public_key.data()),
-                        public_key.size()));
+    ASSERT_TRUE(base::WriteFile(user_policy_key_file(), public_key.data(),
+                                public_key.size()));
   }
 
   // Stores the current |policy_| and verifies that it is published.
@@ -141,7 +138,7 @@
   // value will be expected; otherwise no previous policy is expected.
   // If |new_value| is set then a new policy with that value is expected after
   // storing the |policy_| blob.
-  void PerformStorePolicy(const std::vector<uint8_t>* new_public_key,
+  void PerformStorePolicy(const std::string* new_public_key,
                           const char* previous_value,
                           const char* new_value) {
     const CloudPolicyStore::Status initial_status = store_->status();
@@ -201,24 +198,6 @@
     EXPECT_EQ(CloudPolicyStore::STATUS_VALIDATION_ERROR, store_->status());
   }
 
-  static std::string ConvertPublicKeyToString(
-      const std::vector<uint8_t>& public_key) {
-    return std::string(reinterpret_cast<const char*>(public_key.data()),
-                       public_key.size());
-  }
-
-  std::string GetPolicyPublicKeyAsString() {
-    std::vector<uint8_t> public_key;
-    EXPECT_TRUE(policy_.GetSigningKey()->ExportPublicKey(&public_key));
-    return ConvertPublicKeyToString(public_key);
-  }
-
-  std::string GetPolicyNewPublicKeyAsString() {
-    std::vector<uint8_t> new_public_key;
-    EXPECT_TRUE(policy_.GetNewSigningKey()->ExportPublicKey(&new_public_key));
-    return ConvertPublicKeyToString(new_public_key);
-  }
-
   base::FilePath user_policy_dir() {
     return tmp_dir_.GetPath().AppendASCII("var_run_user_policy");
   }
@@ -251,12 +230,11 @@
   // Make the policy blob contain a new public key.
   policy_.SetDefaultNewSigningKey();
   policy_.Build();
-  std::vector<uint8_t> new_public_key;
-  ASSERT_TRUE(policy_.GetNewSigningKey()->ExportPublicKey(&new_public_key));
+  std::string new_public_key = policy_.GetPublicNewSigningKeyAsString();
+  ASSERT_FALSE(new_public_key.empty());
   ASSERT_NO_FATAL_FAILURE(
       PerformStorePolicy(&new_public_key, nullptr, kDefaultHomepage));
-  EXPECT_EQ(ConvertPublicKeyToString(new_public_key),
-            store_->policy_signature_public_key());
+  EXPECT_EQ(new_public_key, store_->policy_signature_public_key());
 }
 
 TEST_F(UserCloudPolicyStoreChromeOSTest, InitialStoreValidationFail) {
@@ -297,7 +275,7 @@
 TEST_F(UserCloudPolicyStoreChromeOSTest, StoreWithExistingKey) {
   ASSERT_NO_FATAL_FAILURE(
       PerformStorePolicy(nullptr, nullptr, kDefaultHomepage));
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -305,12 +283,11 @@
   // Make the policy blob contain a new public key.
   policy_.SetDefaultNewSigningKey();
   policy_.Build();
-  std::vector<uint8_t> new_public_key;
-  ASSERT_TRUE(policy_.GetNewSigningKey()->ExportPublicKey(&new_public_key));
+  std::string new_public_key = policy_.GetPublicNewSigningKeyAsString();
+  ASSERT_FALSE(new_public_key.empty());
   ASSERT_NO_FATAL_FAILURE(
       PerformStorePolicy(&new_public_key, nullptr, kDefaultHomepage));
-  EXPECT_EQ(ConvertPublicKeyToString(new_public_key),
-            store_->policy_signature_public_key());
+  EXPECT_EQ(new_public_key, store_->policy_signature_public_key());
 }
 
 TEST_F(UserCloudPolicyStoreChromeOSTest,
@@ -422,7 +399,7 @@
   // Store initial policy signed with the initial public key.
   ASSERT_NO_FATAL_FAILURE(
       PerformStorePolicy(nullptr, nullptr, kDefaultHomepage));
-  const std::string initial_public_key = GetPolicyPublicKeyAsString();
+  const std::string initial_public_key = policy_.GetPublicSigningKeyAsString();
   EXPECT_EQ(initial_public_key, store_->policy_signature_public_key());
 
   // Try storing an invalid policy signed with the new public key.
@@ -438,11 +415,11 @@
   // Store the correct policy signed with the new public key.
   policy_.policy_data().set_policy_type(dm_protocol::kChromeUserPolicyType);
   policy_.Build();
-  std::vector<uint8_t> new_public_key;
-  ASSERT_TRUE(policy_.GetNewSigningKey()->ExportPublicKey(&new_public_key));
+  std::string new_public_key = policy_.GetPublicNewSigningKeyAsString();
+  ASSERT_FALSE(new_public_key.empty());
   ASSERT_NO_FATAL_FAILURE(
       PerformStorePolicy(&new_public_key, kDefaultHomepage, kDefaultHomepage));
-  EXPECT_EQ(GetPolicyNewPublicKeyAsString(),
+  EXPECT_EQ(policy_.GetPublicNewSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -457,7 +434,7 @@
             store_->policy()->SerializeAsString());
   VerifyPolicyMap(kDefaultHomepage);
   EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
@@ -535,7 +512,7 @@
             store_->policy()->SerializeAsString());
   VerifyPolicyMap(kDefaultHomepage);
   EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
-  EXPECT_EQ(GetPolicyPublicKeyAsString(),
+  EXPECT_EQ(policy_.GetPublicSigningKeyAsString(),
             store_->policy_signature_public_key());
 }
 
diff --git a/chrome/browser/chromeos/proxy_cros_settings_parser.cc b/chrome/browser/chromeos/proxy_cros_settings_parser.cc
index 69eca3d..07353a4 100644
--- a/chrome/browser/chromeos/proxy_cros_settings_parser.cc
+++ b/chrome/browser/chromeos/proxy_cros_settings_parser.cc
@@ -261,7 +261,7 @@
     }
   } else if (path == kProxyIgnoreList) {
     net::ProxyBypassRules bypass_rules;
-    if (in_value->GetType() == base::Value::TYPE_LIST) {
+    if (in_value->GetType() == base::Value::Type::LIST) {
       const base::ListValue* list_value =
           static_cast<const base::ListValue*>(in_value);
       for (size_t x = 0; x < list_value->GetSize(); x++) {
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc b/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
index 4561da25d..e9fce75 100644
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
+++ b/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
@@ -18,7 +18,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
@@ -225,7 +225,7 @@
 
 // static
 std::unique_ptr<rappor::Sample> ResourceReporter::CreateRapporSample(
-    rappor::RapporService* rappor_service,
+    rappor::RapporServiceImpl* rappor_service,
     const ResourceReporter::TaskRecord& task_record) {
   std::unique_ptr<rappor::Sample> sample(
       rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE));
@@ -372,7 +372,7 @@
         kRapporUsageRangeFlagsField,
         GET_ENUM_VAL(GetCpuUsageRange(sampled_cpu_task->cpu_percent)),
         GET_ENUM_VAL(CpuUsageRange::NUM_RANGES));
-    rappor_service->RecordSampleObj(kCpuRapporMetric, std::move(cpu_sample));
+    rappor_service->RecordSample(kCpuRapporMetric, std::move(cpu_sample));
   }
 
   // Use weighted random sampling to select a task to report in the memory
@@ -385,8 +385,7 @@
         kRapporUsageRangeFlagsField,
         GET_ENUM_VAL(GetMemoryUsageRange(sampled_memory_task->memory_bytes)),
         GET_ENUM_VAL(MemoryUsageRange::NUM_RANGES));
-    rappor_service->RecordSampleObj(kMemoryRapporMetric,
-                                    std::move(memory_sample));
+    rappor_service->RecordSample(kMemoryRapporMetric, std::move(memory_sample));
   }
 }
 
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.h b/chrome/browser/chromeos/resource_reporter/resource_reporter.h
index f180a41..cfa2bc8 100644
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.h
+++ b/chrome/browser/chromeos/resource_reporter/resource_reporter.h
@@ -19,7 +19,7 @@
 #include "chrome/browser/task_manager/task_manager_observer.h"
 #include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_registry_simple.h"
-#include "components/rappor/sample.h"
+#include "components/rappor/public/sample.h"
 
 namespace chromeos {
 
@@ -140,7 +140,7 @@
 
   // Creates a Rappor sample for the given |task_record|.
   static std::unique_ptr<rappor::Sample> CreateRapporSample(
-      rappor::RapporService* rappor_service,
+      rappor::RapporServiceImpl* rappor_service,
       const TaskRecord& task_record);
 
   // Gets the CPU/memory usage ranges given the |cpu| / |memory_in_bytes|
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index a86a6821..6775222e 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -93,6 +93,7 @@
     kSystemLogUploadEnabled,
     kSystemTimezonePolicy,
     kSystemUse24HourClock,
+    kTargetVersionPrefix,
     kUpdateDisabled,
     kVariationsRestrictParameter,
 };
@@ -330,6 +331,12 @@
       new_values_cache->SetBoolean(kUpdateDisabled,
                                    au_settings_proto.update_disabled());
     }
+
+    if (au_settings_proto.has_target_version_prefix()) {
+      new_values_cache->SetString(kTargetVersionPrefix,
+                                  au_settings_proto.target_version_prefix());
+    }
+
     const RepeatedField<int>& allowed_connection_types =
         au_settings_proto.allowed_connection_types();
     std::unique_ptr<base::ListValue> list(new base::ListValue());
diff --git a/chrome/browser/chromeos/settings/device_settings_service_unittest.cc b/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
index 6174113..667ea7a9 100644
--- a/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_service_unittest.cc
@@ -206,9 +206,8 @@
   CheckPolicy();
 
   // Check the new key has been loaded.
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetNewSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicNewSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
 }
 
 TEST_F(DeviceSettingsServiceTest, OwnershipStatus) {
@@ -239,9 +238,8 @@
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN, ownership_status_);
@@ -256,8 +254,8 @@
   EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN, ownership_status_);
@@ -285,9 +283,8 @@
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_FALSE(is_owner_set_);
@@ -298,8 +295,8 @@
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
@@ -326,9 +323,8 @@
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
 
@@ -339,8 +335,8 @@
   EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
 }
 
 TEST_F(DeviceSettingsServiceTest, OnTPMTokenReadyForOwner) {
@@ -364,9 +360,8 @@
   EXPECT_FALSE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_FALSE(is_owner_set_);
@@ -378,8 +373,8 @@
   EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_TRUE(is_owner_set_);
@@ -405,9 +400,8 @@
   EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  std::vector<uint8_t> key;
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_FALSE(is_owner_set_);
@@ -423,8 +417,8 @@
   EXPECT_TRUE(device_settings_service_.HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_.GetPublicKey().get());
   ASSERT_TRUE(device_settings_service_.GetPublicKey()->is_loaded());
-  ASSERT_TRUE(device_policy_.GetSigningKey()->ExportPublicKey(&key));
-  EXPECT_EQ(device_settings_service_.GetPublicKey()->data(), key);
+  EXPECT_EQ(device_policy_.GetPublicSigningKeyAsString(),
+            device_settings_service_.GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
             device_settings_service_.GetOwnershipStatus());
   EXPECT_TRUE(is_owner_set_);
diff --git a/chrome/browser/chromeos/settings/shutdown_policy_forwarder.cc b/chrome/browser/chromeos/settings/shutdown_policy_forwarder.cc
index b6ec58c..2d618575 100644
--- a/chrome/browser/chromeos/settings/shutdown_policy_forwarder.cc
+++ b/chrome/browser/chromeos/settings/shutdown_policy_forwarder.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace chromeos {
@@ -22,20 +21,11 @@
 ShutdownPolicyForwarder::~ShutdownPolicyForwarder() {}
 
 void ShutdownPolicyForwarder::OnShutdownPolicyChanged(bool reboot_on_shutdown) {
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-
   // Shutdown policy changes rarely so don't bother caching the connection.
   ash::mojom::ShutdownControllerPtr shutdown_controller;
-
-  // Under mash the ShutdownController interface is in the ash process. In
-  // classic ash the browser provides it to itself.
-  if (chrome::IsRunningInMash()) {
-    connector->ConnectToInterface("ash", &shutdown_controller);
-  } else {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &shutdown_controller);
-  }
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ConnectToInterface(ash_util::GetAshServiceName(), &shutdown_controller);
 
   // Forward the setting to ash.
   shutdown_controller->SetRebootOnShutdown(reboot_on_shutdown);
diff --git a/chrome/browser/chromeos/system_logs/command_line_log_source.cc b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
index de59f36..a1675314 100644
--- a/chrome/browser/chromeos/system_logs/command_line_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/process/launch.h"
+#include "base/sys_info.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
@@ -66,13 +67,17 @@
   commands.push_back(std::make_pair("modetest", command));
 #endif
 
-  // Get a list of file sizes for the logged in user (excluding the names of
-  // the files in the Downloads directory for privay reasons).
-  command = base::CommandLine(base::FilePath("/bin/sh"));
-  command.AppendArg("-c");
-  command.AppendArg("/usr/bin/du -h /home/chronos/user |"
-                    " grep -v -e \\/home\\/chronos\\/user\\/Downloads\\/");
-  commands.push_back(std::make_pair("user_files", command));
+  // Get a list of file sizes for the whole system (excluding the names of the
+  // files in the Downloads directory for privay reasons).
+  if (base::SysInfo::IsRunningOnChromeOS()) {
+    // The following command would hang if run in Linux Chrome OS build on a
+    // Linux Workstation.
+    command = base::CommandLine(base::FilePath("/bin/sh"));
+    command.AppendArg("-c");
+    command.AppendArg(
+        "/usr/bin/du -h / | grep -v -e \\/home\\/.*\\/Downloads\\/");
+    commands.push_back(std::make_pair("system_files", command));
+  }
 
   // Get disk space usage information
   command = base::CommandLine(base::FilePath("/bin/df"));
diff --git a/chrome/browser/component_updater/component_installers_unittest.cc b/chrome/browser/component_updater/component_installers_unittest.cc
index 14c128e..39b1a151 100644
--- a/chrome/browser/component_updater/component_installers_unittest.cc
+++ b/chrome/browser/component_updater/component_installers_unittest.cc
@@ -89,7 +89,7 @@
       base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
 
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
 
   // This checks that the whole manifest is compatible.
   base::Version version;
diff --git a/chrome/browser/component_updater/component_updater_resource_throttle.cc b/chrome/browser/component_updater/component_updater_resource_throttle.cc
index dac716ea..498ddf3e 100644
--- a/chrome/browser/component_updater/component_updater_resource_throttle.cc
+++ b/chrome/browser/component_updater/component_updater_resource_throttle.cc
@@ -8,7 +8,6 @@
 #include "base/memory/weak_ptr.h"
 #include "components/component_updater/component_updater_service.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 
 using content::BrowserThread;
@@ -76,7 +75,7 @@
 void CUResourceThrottle::Unblock() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (state_ == BLOCKED)
-    controller()->Resume();
+    Resume();
   state_ = UNBLOCKED;
 }
 
diff --git a/chrome/browser/component_updater/pepper_flash_component_installer.cc b/chrome/browser/component_updater/pepper_flash_component_installer.cc
index 77e2617..852d6ea 100644
--- a/chrome/browser/component_updater/pepper_flash_component_installer.cc
+++ b/chrome/browser/component_updater/pepper_flash_component_installer.cc
@@ -45,7 +45,9 @@
 #include "ppapi/shared_impl/ppapi_permissions.h"
 
 #if defined(OS_CHROMEOS)
-#include "base/feature_list.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/ash/system_tray_delegate_chromeos.h"
+#include "chrome/common/chrome_features.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/image_loader_client.h"
@@ -80,12 +82,20 @@
 #if defined(OS_CHROMEOS)
 void LogRegistrationResult(chromeos::DBusMethodCallStatus call_status,
                            bool result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
     LOG(ERROR) << "Call to imageloader service failed.";
     return;
   }
-  if (!result)
+  if (!result) {
     LOG(ERROR) << "Component flash registration failed";
+    return;
+  }
+  chromeos::SystemTrayDelegateChromeOS* tray =
+      chromeos::SystemTrayDelegateChromeOS::instance();
+  if (tray) {
+    tray->SetFlashUpdateAvailable();
+  }
 }
 
 void ImageLoaderRegistration(const std::string& version,
@@ -101,6 +111,46 @@
     LOG(ERROR) << "Failed to get ImageLoaderClient object.";
   }
 }
+
+// Determine whether or not to skip registering flash component updates.
+bool SkipFlashRegistration(ComponentUpdateService* cus) {
+   const base::Feature kCrosCompUpdates {
+     "CrosCompUpdates", base::FEATURE_DISABLED_BY_DEFAULT
+   };
+   if (!base::FeatureList::IsEnabled(kCrosCompUpdates))
+     return true;
+
+   // If the version of Chrome is pinned on the device (probably via enterprise
+   // policy), do not component update Flash player.
+   chromeos::CrosSettingsProvider::TrustedStatus status =
+       chromeos::CrosSettings::Get()->PrepareTrustedValues(
+           base::Bind(&RegisterPepperFlashComponent, cus));
+
+   // Only if the settings are trusted, read the update settings and allow them
+   // to disable Flash component updates. If the settings are untrusted, then we
+   // fail-safe and allow the security updates.
+   std::string version_prefix;
+   bool update_disabled = false;
+   switch (status) {
+     case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
+       // Return and allow flash registration to occur once the settings are
+       // trusted.
+       return true;
+     case chromeos::CrosSettingsProvider::TRUSTED:
+       chromeos::CrosSettings::Get()->GetBoolean(chromeos::kUpdateDisabled,
+                                                 &update_disabled);
+       chromeos::CrosSettings::Get()->GetString(chromeos::kTargetVersionPrefix,
+                                                &version_prefix);
+
+       return update_disabled || !version_prefix.empty();
+     case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
+       return false;
+   }
+
+   // Default to not skipping component flash registration since updates are
+   // security critical.
+   return false;
+}
 #endif  // defined(OS_CHROMEOS)
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
@@ -316,11 +366,8 @@
     return;
 
 #if defined(OS_CHROMEOS)
-   const base::Feature kCrosCompUpdates {
-     "CrosCompUpdates", base::FEATURE_DISABLED_BY_DEFAULT
-   };
-   if (!base::FeatureList::IsEnabled(kCrosCompUpdates))
-     return;
+  if (SkipFlashRegistration(cus))
+    return;
 #endif  // defined(OS_CHROMEOS)
 
   std::unique_ptr<ComponentInstallerTraits> traits(
diff --git a/chrome/browser/component_updater/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl_component_installer.cc
index 8e46bbd0..f3b747b 100644
--- a/chrome/browser/component_updater/pnacl_component_installer.cc
+++ b/chrome/browser/component_updater/pnacl_component_installer.cc
@@ -133,7 +133,7 @@
   std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
   if (!root.get())
     return NULL;
-  if (!root->IsType(base::Value::TYPE_DICTIONARY))
+  if (!root->IsType(base::Value::Type::DICTIONARY))
     return NULL;
   return static_cast<base::DictionaryValue*>(root.release());
 }
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index fc88910..9c6d799 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -18,6 +18,7 @@
 #include "base/files/file_util.h"
 #include "base/json/json_file_value_serializer.h"
 #include "base/logging.h"
+#include "base/task_scheduler/post_task.h"
 #if defined(OS_MACOSX)
 #include "base/mac/authorization_util.h"
 #include "base/mac/scoped_authorizationref.h"
@@ -27,7 +28,6 @@
 #include "base/process/kill.h"
 #include "base/process/launch.h"
 #include "base/process/process.h"
-#include "base/threading/worker_pool.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -224,17 +224,23 @@
   }
   base::Process process = base::Process::Open(pid);
 #endif
-  base::WorkerPool::PostTask(
-      FROM_HERE,
-      base::Bind(&WaitForElevatedInstallToComplete, base::Passed(&process)),
-      true);
+  base::PostTaskWithTraits(
+      FROM_HERE, base::TaskTraits()
+                     .WithShutdownBehavior(
+                         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+                     .WithPriority(base::TaskPriority::BACKGROUND)
+                     .WithWait(),
+      base::Bind(&WaitForElevatedInstallToComplete, base::Passed(&process)));
 }
 
 void ElevatedInstallRecoveryComponent(const base::FilePath& installer_path) {
-  base::WorkerPool::PostTask(
-      FROM_HERE,
-      base::Bind(&DoElevatedInstallRecoveryComponent, installer_path),
-      true);
+  base::PostTaskWithTraits(
+      FROM_HERE, base::TaskTraits()
+                     .WithShutdownBehavior(
+                         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+                     .WithPriority(base::TaskPriority::BACKGROUND)
+                     .WithFileIO(),
+      base::Bind(&DoElevatedInstallRecoveryComponent, installer_path));
 }
 #endif  // defined(OS_WIN)
 
@@ -365,11 +371,14 @@
     return false;
 
   // Let worker pool thread wait for us so we don't block Chrome shutdown.
-  base::WorkerPool::PostTask(
-      FROM_HERE,
-      base::Bind(&WaitForInstallToComplete,
-                 base::Passed(&process), installer_folder, prefs_),
-      true);
+  base::PostTaskWithTraits(
+      FROM_HERE, base::TaskTraits()
+                     .WithShutdownBehavior(
+                         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+                     .WithPriority(base::TaskPriority::BACKGROUND)
+                     .WithWait(),
+      base::Bind(&WaitForInstallToComplete, base::Passed(&process),
+                 installer_folder, prefs_));
 
   // Returns true regardless of install result since from updater service
   // perspective the install is done, even we may need to do elevated
diff --git a/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc b/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
index 66adec4..54363f2 100644
--- a/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
+++ b/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
@@ -273,7 +273,7 @@
       &provider, google_url, google_url,
       CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE, std::string(), false));
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, cert_filter->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, cert_filter->GetType());
   base::DictionaryValue* dict_value =
       static_cast<base::DictionaryValue*>(cert_filter.get());
   std::string actual_common_name;
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
index cd1785d..8aca728 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.cc
@@ -40,7 +40,8 @@
 
 ChromeDataUseAscriber::~ChromeDataUseAscriber() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK_EQ(0u, data_use_recorders_.size());
+  DCHECK(subframe_to_mainframe_map_.empty());
+  DCHECK(data_use_recorders_.empty());
 }
 
 ChromeDataUseRecorder* ChromeDataUseAscriber::GetDataUseRecorder(
@@ -96,8 +97,13 @@
     // URLRequests racing the frame create events.
     // TODO(kundaji): Add UMA.
     RenderFrameHostID frame_key(render_process_id, render_frame_id);
-    auto frame_iter = render_frame_data_use_map_.find(frame_key);
-    if (frame_iter == render_frame_data_use_map_.end()) {
+    auto main_frame_key_iter = subframe_to_mainframe_map_.find(frame_key);
+    if (main_frame_key_iter == subframe_to_mainframe_map_.end()) {
+      return data_use_recorders_.end();
+    }
+    auto frame_iter =
+        main_render_frame_data_use_map_.find(main_frame_key_iter->second);
+    if (frame_iter == main_render_frame_data_use_map_.end()) {
       return data_use_recorders_.end();
     }
 
@@ -119,7 +125,7 @@
       return new_entry;
     }
 
-    DCHECK(frame_iter != render_frame_data_use_map_.end());
+    DCHECK(frame_iter != main_render_frame_data_use_map_.end());
     auto entry = frame_iter->second;
     request->SetUserData(DataUseRecorderEntryAsUserData::kUserDataKey,
                          new DataUseRecorderEntryAsUserData(entry));
@@ -134,60 +140,6 @@
   return entry;
 }
 
-void ChromeDataUseAscriber::OnBeforeUrlRequest(net::URLRequest* request) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DataUseAscriber::OnBeforeUrlRequest(request);
-
-  // TODO(kundaji): Handle PlzNavigate.
-  if (content::IsBrowserSideNavigationEnabled())
-    return;
-
-  auto service = static_cast<DataUseUserData*>(
-      request->GetUserData(DataUseUserData::kUserDataKey));
-  if (service)
-    return;
-
-  const content::ResourceRequestInfo* request_info =
-      content::ResourceRequestInfo::ForRequest(request);
-  content::ResourceType resource_type = request_info
-                                            ? request_info->GetResourceType()
-                                            : content::RESOURCE_TYPE_LAST_TYPE;
-
-  if (resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
-    return;
-
-  int render_process_id = -1;
-  int render_frame_id = -1;
-  bool has_valid_render_frame_id =
-      content::ResourceRequestInfo::GetRenderFrameForRequest(
-          request, &render_process_id, &render_frame_id);
-  DCHECK(has_valid_render_frame_id);
-  // Browser tests may not set up DataUseWebContentsObservers in which case
-  // this class never sees navigation and frame events so DataUseRecorders
-  // will never be destroyed. To avoid this, we ignore requests whose
-  // render frames don't have a record. However, this can also be caused by
-  // URLRequests racing the frame create events.
-  // TODO(kundaji): Add UMA.
-  if (render_frame_data_use_map_.find(
-          RenderFrameHostID(render_process_id, render_frame_id)) ==
-      render_frame_data_use_map_.end()) {
-    return;
-  }
-
-  // If this request is already being tracked, do not create a new entry.
-  auto user_data = static_cast<DataUseRecorderEntryAsUserData*>(
-      request->GetUserData(DataUseRecorderEntryAsUserData::kUserDataKey));
-  if (user_data)
-    return;
-
-  DataUseRecorderEntry entry = data_use_recorders_.emplace(
-      data_use_recorders_.end());
-  request->SetUserData(DataUseRecorderEntryAsUserData::kUserDataKey,
-                       new DataUseRecorderEntryAsUserData(entry));
-  pending_navigation_data_use_map_.insert(
-      std::make_pair(request_info->GetGlobalRequestID(), entry));
-}
-
 void ChromeDataUseAscriber::OnUrlRequestDestroyed(net::URLRequest* request) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -199,9 +151,9 @@
   DataUseRecorder* recorder = &(*entry);
 
   RenderFrameHostID frame_key = entry->main_frame_id();
-  auto frame_iter = render_frame_data_use_map_.find(frame_key);
+  auto frame_iter = main_render_frame_data_use_map_.find(frame_key);
   bool is_in_render_frame_map =
-      frame_iter != render_frame_data_use_map_.end() &&
+      frame_iter != main_render_frame_data_use_map_.end() &&
       frame_iter->second->HasPendingURLRequest(request);
 
   const content::ResourceRequestInfo* request_info =
@@ -238,60 +190,55 @@
 
 void ChromeDataUseAscriber::RenderFrameCreated(int render_process_id,
                                                int render_frame_id,
-                                               int parent_render_process_id,
-                                               int parent_render_frame_id) {
+                                               int main_render_process_id,
+                                               int main_render_frame_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   if (content::IsBrowserSideNavigationEnabled())
     return;
 
-  if (parent_render_process_id != -1 && parent_render_frame_id != -1) {
-    // Create an entry in |render_frame_data_use_map_| for this frame with
-    // the same DataUseRecorderEntry as its parent frame.
-    auto frame_iter = render_frame_data_use_map_.find(
-        RenderFrameHostID(parent_render_process_id, parent_render_frame_id));
-
-    DCHECK(frame_iter != render_frame_data_use_map_.end());
-
-    DataUseRecorderEntry entry = frame_iter->second;
-    render_frame_data_use_map_.insert(std::make_pair(
-        RenderFrameHostID(render_process_id, render_frame_id), entry));
+  if (main_render_process_id != -1 && main_render_frame_id != -1) {
+    // Create an entry in |subframe_to_mainframe_map_| for this frame mapped to
+    // it's parent frame.
+    subframe_to_mainframe_map_.insert(std::make_pair(
+        RenderFrameHostID(render_process_id, render_frame_id),
+        RenderFrameHostID(main_render_process_id, main_render_frame_id)));
   } else {
-    auto frame_iter = render_frame_data_use_map_.find(
+    subframe_to_mainframe_map_.insert(
+        std::make_pair(RenderFrameHostID(render_process_id, render_frame_id),
+                       RenderFrameHostID(render_process_id, render_frame_id)));
+    auto frame_iter = main_render_frame_data_use_map_.find(
         RenderFrameHostID(render_process_id, render_frame_id));
-    DCHECK(frame_iter == render_frame_data_use_map_.end());
+    DCHECK(frame_iter == main_render_frame_data_use_map_.end());
     DataUseRecorderEntry entry = CreateNewDataUseRecorder(nullptr);
     entry->set_main_frame_id(
         RenderFrameHostID(render_process_id, render_frame_id));
-    render_frame_data_use_map_.insert(std::make_pair(
+    main_render_frame_data_use_map_.insert(std::make_pair(
         RenderFrameHostID(render_process_id, render_frame_id), entry));
   }
 }
 
 void ChromeDataUseAscriber::RenderFrameDeleted(int render_process_id,
                                                int render_frame_id,
-                                               int parent_render_process_id,
-                                               int parent_render_frame_id) {
+                                               int main_render_process_id,
+                                               int main_render_frame_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   if (content::IsBrowserSideNavigationEnabled())
     return;
 
   RenderFrameHostID key(render_process_id, render_frame_id);
-  auto frame_iter = render_frame_data_use_map_.find(key);
 
-  DCHECK(frame_iter != render_frame_data_use_map_.end());
-
-  DataUseRecorderEntry entry = frame_iter->second;
-  DataUseRecorder* recorder = &(*entry);
-
-  if (parent_render_process_id == -1 && parent_render_frame_id == -1 &&
-      recorder->IsDataUseComplete()) {
-    OnDataUseCompleted(entry);
-    data_use_recorders_.erase(entry);
+  if (main_render_process_id == -1 && main_render_frame_id == -1) {
+    auto frame_iter = main_render_frame_data_use_map_.find(key);
+    DataUseRecorderEntry entry = frame_iter->second;
+    if (entry->IsDataUseComplete()) {
+      OnDataUseCompleted(entry);
+      data_use_recorders_.erase(entry);
+    }
+    main_render_frame_data_use_map_.erase(frame_iter);
   }
-
-  render_frame_data_use_map_.erase(frame_iter);
+  subframe_to_mainframe_map_.erase(key);
 }
 
 void ChromeDataUseAscriber::DidStartMainFrameNavigation(
@@ -312,7 +259,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   // Find the DataUseRecorderEntry the frame is associated with
-  auto frame_it = render_frame_data_use_map_.find(
+  auto frame_it = main_render_frame_data_use_map_.find(
       RenderFrameHostID(render_process_id, render_frame_id));
 
   // Find the pending navigation entry.
@@ -323,9 +270,9 @@
   if (navigation_iter == pending_navigation_data_use_map_.end()) {
     // No pending navigation entry to worry about. However, the old frame entry
     // must be removed from frame map, and possibly marked complete and deleted.
-    if (frame_it != render_frame_data_use_map_.end()) {
+    if (frame_it != main_render_frame_data_use_map_.end()) {
       DataUseRecorderEntry old_frame_entry = frame_it->second;
-      render_frame_data_use_map_.erase(frame_it);
+      main_render_frame_data_use_map_.erase(frame_it);
       if (old_frame_entry->IsDataUseComplete()) {
         OnDataUseCompleted(old_frame_entry);
         data_use_recorders_.erase(old_frame_entry);
@@ -337,7 +284,7 @@
       std::pair<int, int> frame_key =
           RenderFrameHostID(render_process_id, render_frame_id);
       entry->set_main_frame_id(frame_key);
-      render_frame_data_use_map_.insert(std::make_pair(frame_key, entry));
+      main_render_frame_data_use_map_.insert(std::make_pair(frame_key, entry));
     }
     return;
   }
@@ -349,7 +296,7 @@
 
   // If the frame has already been deleted then mark this navigation as having
   // completed its data use.
-  if (frame_it == render_frame_data_use_map_.end()) {
+  if (frame_it == main_render_frame_data_use_map_.end()) {
     if (entry->IsDataUseComplete()) {
       OnDataUseCompleted(entry);
       data_use_recorders_.erase(entry);
@@ -372,8 +319,8 @@
     data_use_recorders_.erase(entry);
   } else {
     // Navigation is not same page, so remove old entry from
-    // |render_frame_data_use_map_|, possibly marking it complete.
-    render_frame_data_use_map_.erase(frame_it);
+    // |main_render_frame_data_use_map_|, possibly marking it complete.
+    main_render_frame_data_use_map_.erase(frame_it);
     if (old_frame_entry->IsDataUseComplete()) {
       OnDataUseCompleted(old_frame_entry);
       data_use_recorders_.erase(old_frame_entry);
@@ -389,7 +336,7 @@
       data_use.set_url(gurl);
     }
 
-    render_frame_data_use_map_.insert(std::make_pair(
+    main_render_frame_data_use_map_.insert(std::make_pair(
         RenderFrameHostID(render_process_id, render_frame_id), entry));
   }
 }
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
index 2ce46c3d..c50efd73 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber.h
@@ -57,22 +57,23 @@
   // DataUseAscriber implementation:
   ChromeDataUseRecorder* GetDataUseRecorder(net::URLRequest* request,
                                             bool can_create_new) override;
-  void OnBeforeUrlRequest(net::URLRequest* request) override;
   void OnUrlRequestDestroyed(net::URLRequest* request) override;
   std::unique_ptr<URLRequestClassifier> CreateURLRequestClassifier()
       const override;
 
-  // Called when a render frame host is created.
+  // Called when a render frame host is created. When the render frame is a main
+  // frame, |main_render_process_id| and |main_render_frame_id| should be -1.
   void RenderFrameCreated(int render_process_id,
                           int render_frame_id,
-                          int parent_render_process_id,
-                          int parent_render_frame_id);
+                          int main_render_process_id,
+                          int main_render_frame_id);
 
-  // Called when a render frame host is deleted.
+  // Called when a render frame host is deleted. When the render frame is a main
+  // frame, |main_render_process_id| and |main_render_frame_id| should be -1.
   void RenderFrameDeleted(int render_process_id,
                           int render_frame_id,
-                          int parent_render_process_id,
-                          int parent_render_frame_id);
+                          int main_render_process_id,
+                          int main_render_frame_id);
 
   // Called when a main frame navigation is started.
   void DidStartMainFrameNavigation(GURL gurl,
@@ -138,9 +139,15 @@
   DataUseRecorderList data_use_recorders_;
 
   // Map from RenderFrameHost to the DataUseRecorderEntry in
-  // |data_use_recorders_| that the frame ascribe data use to.
+  // |data_use_recorders_| that the main frame ascribes data use to.
   base::hash_map<RenderFrameHostID, DataUseRecorderEntry>
-      render_frame_data_use_map_;
+      main_render_frame_data_use_map_;
+
+  // Maps subframe IDs to the mainframe ID, so the mainframe lifetime can have
+  // ownership over the lifetime of entries in |data_use_recorders_|. Mainframes
+  // are mapped to themselves.
+  base::hash_map<RenderFrameHostID, RenderFrameHostID>
+      subframe_to_mainframe_map_;
 
   // Map from pending navigations to the DataUseRecorderEntry in
   // |data_use_recorders_| that the navigation ascribes data use to.
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_service.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_service.cc
index d76cb6aa..966a1a9 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_service.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_service.cc
@@ -25,6 +25,15 @@
   return io_thread->globals()->data_use_ascriber.get();
 }
 
+// Returns the top most parent of |render_frame_host|.
+content::RenderFrameHost* GetMainFrame(
+    content::RenderFrameHost* render_frame_host) {
+  content::RenderFrameHost* render_main_frame_host = render_frame_host;
+  while (render_main_frame_host->GetParent())
+    render_main_frame_host = render_main_frame_host->GetParent();
+  return render_main_frame_host;
+}
+
 }  // namespace
 
 namespace data_use_measurement {
@@ -64,20 +73,21 @@
   if (!ascriber_)
     return;
 
-  int parent_render_process_id = -1;
-  int parent_render_frame_id = -1;
-  if (render_frame_host->GetParent()) {
-    parent_render_process_id =
-        render_frame_host->GetParent()->GetProcess()->GetID();
-    parent_render_frame_id = render_frame_host->GetParent()->GetRoutingID();
+  int main_render_process_id = -1;
+  int main_render_frame_id = -1;
+  content::RenderFrameHost* main_frame = GetMainFrame(render_frame_host);
+  if (main_frame != render_frame_host) {
+    main_render_process_id = main_frame->GetProcess()->GetID();
+    main_render_frame_id = main_frame->GetRoutingID();
   }
+
   content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&ChromeDataUseAscriber::RenderFrameCreated,
                  base::Unretained(ascriber_),
                  render_frame_host->GetProcess()->GetID(),
-                 render_frame_host->GetRoutingID(), parent_render_process_id,
-                 parent_render_frame_id));
+                 render_frame_host->GetRoutingID(), main_render_process_id,
+                 main_render_frame_id));
 }
 
 void ChromeDataUseAscriberService::RenderFrameDeleted(
@@ -95,21 +105,21 @@
   if (!ascriber_)
     return;
 
-  int parent_render_frame_id = -1;
-  int parent_render_process_id = -1;
-
-  if (render_frame_host->GetParent()) {
-    parent_render_process_id =
-        render_frame_host->GetParent()->GetProcess()->GetID();
-    parent_render_frame_id = render_frame_host->GetParent()->GetRoutingID();
+  int main_render_process_id = -1;
+  int main_render_frame_id = -1;
+  content::RenderFrameHost* main_frame = GetMainFrame(render_frame_host);
+  if (main_frame != render_frame_host) {
+    main_render_process_id = main_frame->GetProcess()->GetID();
+    main_render_frame_id = main_frame->GetRoutingID();
   }
+
   content::BrowserThread::PostTask(
       content::BrowserThread::IO, FROM_HERE,
       base::Bind(&ChromeDataUseAscriber::RenderFrameDeleted,
                  base::Unretained(ascriber_),
                  render_frame_host->GetProcess()->GetID(),
-                 render_frame_host->GetRoutingID(), parent_render_process_id,
-                 parent_render_frame_id));
+                 render_frame_host->GetRoutingID(), main_render_process_id,
+                 main_render_frame_id));
 }
 
 void ChromeDataUseAscriberService::DidStartNavigation(
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
index b11265aa..bda7395 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
@@ -97,6 +97,8 @@
   // Request should cause a recorder to be created.
   ascriber()->OnBeforeUrlRequest(request.get());
   EXPECT_EQ(2u, recorders().size());
+
+  ascriber()->RenderFrameDeleted(kRenderProcessId, kRenderFrameId, -1, -1);
 }
 
 }  // namespace data_use_measurement
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index a438fc4..58103af1 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -199,7 +199,7 @@
     return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
                                                          kLocationsParam);
   for (const auto& item : *locations) {
-    if (!item->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!item->IsType(base::Value::Type::DICTIONARY)) {
       return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
                                                            kLocationsParam);
     }
diff --git a/chrome/browser/devtools/devtools_protocol.cc b/chrome/browser/devtools/devtools_protocol.cc
index 34bc3b6..a22811053 100644
--- a/chrome/browser/devtools/devtools_protocol.cc
+++ b/chrome/browser/devtools/devtools_protocol.cc
@@ -93,7 +93,7 @@
     std::string* method,
     std::unique_ptr<base::DictionaryValue>* params) {
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-  if (!value || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value || !value->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   std::unique_ptr<base::DictionaryValue> dict(
@@ -104,7 +104,7 @@
 
   std::unique_ptr<base::Value> params_value;
   dict->Remove(kParamsParam, &params_value);
-  if (params_value && params_value->IsType(base::Value::TYPE_DICTIONARY))
+  if (params_value && params_value->IsType(base::Value::Type::DICTIONARY))
     params->reset(static_cast<base::DictionaryValue*>(params_value.release()));
 
   return true;
@@ -115,7 +115,7 @@
                                      int* command_id,
                                      int* error_code) {
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-  if (!value || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value || !value->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   std::unique_ptr<base::DictionaryValue> dict(
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 718fce4..5d37fc7 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -691,7 +691,7 @@
 void DevToolsUIBindings::ShowCertificateViewer(const std::string& cert_chain) {
   std::unique_ptr<base::Value> value =
       base::JSONReader::Read(cert_chain);
-  if (!value || value->GetType() != base::Value::TYPE_LIST) {
+  if (!value || value->GetType() != base::Value::Type::LIST) {
     NOTREACHED();
     return;
   }
@@ -701,7 +701,7 @@
   std::vector<std::string> decoded;
   for (size_t i = 0; i < list->GetSize(); ++i) {
     base::Value* item;
-    if (!list->Get(i, &item) || item->GetType() != base::Value::TYPE_STRING) {
+    if (!list->Get(i, &item) || item->GetType() != base::Value::Type::STRING) {
       NOTREACHED();
       return;
     }
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index a090462..0b2824f2e 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -101,7 +101,7 @@
     return;
   DictionaryPrefUpdate update(profile->GetPrefs(), prefs::kDevToolsPreferences);
   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
-    if (!it.value().IsType(base::Value::TYPE_STRING))
+    if (!it.value().IsType(base::Value::Type::STRING))
       continue;
     update.Get()->SetWithoutPathExpansion(
         it.key(), it.value().CreateDeepCopy());
diff --git a/chrome/browser/download/download_dir_policy_handler.cc b/chrome/browser/download/download_dir_policy_handler.cc
index a99db00..b64c616f7 100644
--- a/chrome/browser/download/download_dir_policy_handler.cc
+++ b/chrome/browser/download/download_dir_policy_handler.cc
@@ -40,7 +40,7 @@
 
 DownloadDirPolicyHandler::DownloadDirPolicyHandler()
     : TypeCheckingPolicyHandler(policy::key::kDownloadDirectory,
-                                base::Value::TYPE_STRING) {}
+                                base::Value::Type::STRING) {}
 
 DownloadDirPolicyHandler::~DownloadDirPolicyHandler() {}
 
diff --git a/chrome/browser/download/download_file_picker.cc b/chrome/browser/download/download_file_picker.cc
index 1e625ba..07d1ca3 100644
--- a/chrome/browser/download/download_file_picker.cc
+++ b/chrome/browser/download/download_file_picker.cc
@@ -63,8 +63,16 @@
   should_record_file_picker_result_ = !prefs->PromptForDownload();
 
   WebContents* web_contents = item->GetWebContents();
+  if (!web_contents || !web_contents->GetNativeView())
+    return;
+
   select_file_dialog_ = ui::SelectFileDialog::Create(
       this, new ChromeSelectFilePolicy(web_contents));
+  // |select_file_dialog_| could be null in Linux. See CreateSelectFileDialog()
+  // in shell_dialog_linux.cc.
+  if (!select_file_dialog_.get())
+    return;
+
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   // Platform file pickers, notably on Mac and Windows, tend to break
   // with double extensions like .tar.gz, so only pass in normal ones.
diff --git a/chrome/browser/download/download_resource_throttle.cc b/chrome/browser/download/download_resource_throttle.cc
index 68c4bae6..1bbd3b3 100644
--- a/chrome/browser/download/download_resource_throttle.cc
+++ b/chrome/browser/download/download_resource_throttle.cc
@@ -10,7 +10,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/download/download_stats.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/resource_controller.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/download/download_controller_base.h"
@@ -130,7 +129,7 @@
   }
 
   if (!request_allowed_)
-    controller()->Cancel();
+    Cancel();
 }
 
 void DownloadResourceThrottle::ContinueDownload(bool allow) {
@@ -149,9 +148,9 @@
   if (request_deferred_) {
     request_deferred_ = false;
     if (allow) {
-      controller()->Resume();
+      Resume();
     } else {
-      controller()->Cancel();
+      Cancel();
     }
   }
 }
diff --git a/chrome/browser/download/download_resource_throttle_unittest.cc b/chrome/browser/download/download_resource_throttle_unittest.cc
index 03274121..03dcc30 100644
--- a/chrome/browser/download/download_resource_throttle_unittest.cc
+++ b/chrome/browser/download/download_resource_throttle_unittest.cc
@@ -11,7 +11,6 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -35,7 +34,8 @@
   ~MockWebContentsDelegate() override {}
 };
 
-class MockResourceController : public content::ResourceController {
+class MockResourceThrottleDelegate
+    : public content::ResourceThrottle::Delegate {
  public:
   MOCK_METHOD0(Cancel, void());
   MOCK_METHOD0(CancelAndIgnore, void());
@@ -82,7 +82,7 @@
         limiter_,
         base::Bind(&tab_util::GetWebContentsByID, process_id, render_view_id),
         GURL(kTestUrl), "GET");
-    throttle_->set_controller_for_testing(&resource_controller_);
+    throttle_->set_delegate_for_testing(&resource_throttle_delegate_);
     bool defer;
     throttle_->WillStartRequest(&defer);
     EXPECT_EQ(true, defer);
@@ -102,7 +102,7 @@
   content::ResourceThrottle* throttle_;
   MockWebContentsDelegate delegate_;
   scoped_refptr<DownloadRequestLimiter> limiter_;
-  ::testing::NiceMock<MockResourceController> resource_controller_;
+  ::testing::NiceMock<MockResourceThrottleDelegate> resource_throttle_delegate_;
   std::unique_ptr<base::RunLoop> run_loop_;
 #if BUILDFLAG(ANDROID_JAVA_UI)
   chrome::android::MockDownloadController download_controller_;
@@ -110,7 +110,7 @@
 };
 
 TEST_F(DownloadResourceThrottleTest, StartDownloadThrottle_Basic) {
-  EXPECT_CALL(resource_controller_, Resume())
+  EXPECT_CALL(resource_throttle_delegate_, Resume())
       .WillOnce(QuitLoop(run_loop_->QuitClosure()));
   StartThrottle();
 }
@@ -119,7 +119,7 @@
 TEST_F(DownloadResourceThrottleTest, DownloadWithFailedFileAcecssRequest) {
   DownloadControllerBase::Get()
       ->SetApproveFileAccessRequestForTesting(false);
-  EXPECT_CALL(resource_controller_, Cancel())
+  EXPECT_CALL(resource_throttle_delegate_, Cancel())
       .WillOnce(QuitLoop(run_loop_->QuitClosure()));
   StartThrottle();
 }
diff --git a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
index e9248b0..b35b4a0 100644
--- a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
@@ -36,8 +36,12 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ExtensionBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kEnableExtensionActivityLogging);
-    command_line->AppendSwitchASCII(switches::kPrerenderMode,
-                                    switches::kPrerenderModeSwitchValueEnabled);
+  }
+
+  void SetUpOnMainThread() override {
+    ExtensionApiTest::SetUpOnMainThread();
+    prerender::PrerenderManager::SetMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
   }
 
   static void Prerender_Arguments(
diff --git a/chrome/browser/extensions/activity_log/counting_policy.cc b/chrome/browser/extensions/activity_log/counting_policy.cc
index d5946576..b4b6f9ac 100644
--- a/chrome/browser/extensions/activity_log/counting_policy.cc
+++ b/chrome/browser/extensions/activity_log/counting_policy.cc
@@ -499,7 +499,7 @@
     if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
       std::unique_ptr<base::Value> parsed_value =
           base::JSONReader::Read(query.ColumnString(4));
-      if (parsed_value && parsed_value->IsType(base::Value::TYPE_LIST)) {
+      if (parsed_value && parsed_value->IsType(base::Value::Type::LIST)) {
         action->set_args(base::WrapUnique(
             static_cast<base::ListValue*>(parsed_value.release())));
       }
@@ -512,7 +512,7 @@
     if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
       std::unique_ptr<base::Value> parsed_value =
           base::JSONReader::Read(query.ColumnString(8));
-      if (parsed_value && parsed_value->IsType(base::Value::TYPE_DICTIONARY)) {
+      if (parsed_value && parsed_value->IsType(base::Value::Type::DICTIONARY)) {
         action->set_other(base::WrapUnique(
             static_cast<base::DictionaryValue*>(parsed_value.release())));
       }
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
index 78eece6..0188a53 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
@@ -196,7 +196,7 @@
     if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
       std::unique_ptr<base::Value> parsed_value =
           base::JSONReader::Read(query.ColumnString(4));
-      if (parsed_value && parsed_value->IsType(base::Value::TYPE_LIST)) {
+      if (parsed_value && parsed_value->IsType(base::Value::Type::LIST)) {
         action->set_args(base::WrapUnique(
             static_cast<base::ListValue*>(parsed_value.release())));
       }
@@ -209,7 +209,7 @@
     if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
       std::unique_ptr<base::Value> parsed_value =
           base::JSONReader::Read(query.ColumnString(8));
-      if (parsed_value && parsed_value->IsType(base::Value::TYPE_DICTIONARY)) {
+      if (parsed_value && parsed_value->IsType(base::Value::Type::DICTIONARY)) {
         action->set_other(base::WrapUnique(
             static_cast<base::DictionaryValue*>(parsed_value.release())));
       }
diff --git a/chrome/browser/extensions/alert_apitest.cc b/chrome/browser/extensions/alert_apitest.cc
index 718bc18e..9d638b9 100644
--- a/chrome/browser/extensions/alert_apitest.cc
+++ b/chrome/browser/extensions/alert_apitest.cc
@@ -55,7 +55,7 @@
                       size_t* call_count,
                       const base::Value* value) {
   ASSERT_TRUE(value) << dialog_name;
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_NULL));
+  ASSERT_TRUE(value->IsType(base::Value::Type::NONE));
   ++*call_count;
 }
 
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 6242e561..b0ad14b 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -432,7 +432,7 @@
     return;
 
   std::unique_ptr<base::Value> result = base::JSONReader::Read(message);
-  if (!result || !result->IsType(base::Value::TYPE_DICTIONARY))
+  if (!result || !result->IsType(base::Value::Type::DICTIONARY))
     return;
   base::DictionaryValue* dictionary =
       static_cast<base::DictionaryValue*>(result.get());
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
index 991ceeba..4297246c8 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -33,6 +33,7 @@
 #include "components/cryptauth/cryptauth_enrollment_manager.h"
 #include "components/cryptauth/cryptauth_enrollment_utils.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/cryptauth/secure_message_delegate.h"
 #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
 #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h"
@@ -40,7 +41,6 @@
 #include "components/proximity_auth/bluetooth_util.h"
 #include "components/proximity_auth/logging/logging.h"
 #include "components/proximity_auth/proximity_auth_client.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/screenlock_bridge.h"
 #include "components/proximity_auth/screenlock_state.h"
 #include "components/proximity_auth/switches.h"
@@ -1090,7 +1090,7 @@
   // |params->setup_service_uuid|.
   connection_finder_.reset(
       new proximity_auth::BluetoothLowEnergyConnectionFinder(
-          proximity_auth::RemoteDevice(), params->setup_service_uuid,
+          cryptauth::RemoteDevice(), params->setup_service_uuid,
           proximity_auth::BluetoothLowEnergyConnectionFinder::FIND_ANY_DEVICE,
           nullptr, bluetooth_throttler_.get(), 3));
 
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
index e38c95b..6d0d2f3 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
@@ -579,7 +579,7 @@
       extensions::api_test_utils::RunFunctionAndReturnSingleResult(
           function.get(), "[]", profile()));
   ASSERT_TRUE(value.get());
-  ASSERT_EQ(base::Value::TYPE_LIST, value->GetType());
+  ASSERT_EQ(base::Value::Type::LIST, value->GetType());
 
   base::ListValue* list_value = static_cast<base::ListValue*>(value.get());
   EXPECT_EQ(2u, list_value->GetSize());
@@ -588,8 +588,8 @@
   base::Value* remote_device2;
   ASSERT_TRUE(list_value->Get(0, &remote_device1));
   ASSERT_TRUE(list_value->Get(1, &remote_device2));
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, remote_device1->GetType());
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, remote_device2->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, remote_device1->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, remote_device2->GetType());
 
   std::string name1, name2;
   EXPECT_TRUE(static_cast<base::DictionaryValue*>(remote_device1)
@@ -613,7 +613,7 @@
       extensions::api_test_utils::RunFunctionAndReturnSingleResult(
           function.get(), "[]", profile()));
   ASSERT_TRUE(value.get());
-  ASSERT_EQ(base::Value::TYPE_LIST, value->GetType());
+  ASSERT_EQ(base::Value::Type::LIST, value->GetType());
 
   base::ListValue* list_value = static_cast<base::ListValue*>(value.get());
   EXPECT_EQ(0u, list_value->GetSize());
@@ -634,7 +634,7 @@
       extensions::api_test_utils::RunFunctionAndReturnSingleResult(
           function.get(), "[]", profile()));
   ASSERT_TRUE(value);
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
   base::DictionaryValue* permit_access =
       static_cast<base::DictionaryValue*>(value.get());
 
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 91555cb..8556def 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -372,21 +372,21 @@
     return true;
 
   switch (first_arg->GetType()) {
-    case base::Value::TYPE_INTEGER:
+    case base::Value::Type::INTEGER:
       CHECK(first_arg->GetAsInteger(&tab_id_));
       break;
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       // Found the details argument.
       details_ = static_cast<base::DictionaryValue*>(first_arg);
       // Still need to check for the tabId within details.
       base::Value* tab_id_value = NULL;
       if (details_->Get("tabId", &tab_id_value)) {
         switch (tab_id_value->GetType()) {
-          case base::Value::TYPE_NULL:
+          case base::Value::Type::NONE:
             // OK; tabId is optional, leave it default.
             return true;
-          case base::Value::TYPE_INTEGER:
+          case base::Value::Type::INTEGER:
             CHECK(tab_id_value->GetAsInteger(&tab_id_));
             return true;
           default:
@@ -398,7 +398,7 @@
       break;
     }
 
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       // The tabId might be an optional argument.
       break;
 
@@ -502,7 +502,7 @@
   base::Value* color_value = NULL;
   EXTENSION_FUNCTION_VALIDATE(details_->Get("color", &color_value));
   SkColor color = 0;
-  if (color_value->IsType(base::Value::TYPE_LIST)) {
+  if (color_value->IsType(base::Value::Type::LIST)) {
     base::ListValue* list = NULL;
     EXTENSION_FUNCTION_VALIDATE(details_->GetList("color", &list));
     EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
@@ -514,7 +514,7 @@
 
     color = SkColorSetARGB(color_array[3], color_array[0],
                            color_array[1], color_array[2]);
-  } else if (color_value->IsType(base::Value::TYPE_STRING)) {
+  } else if (color_value->IsType(base::Value::Type::STRING)) {
     std::string color_string;
     EXTENSION_FUNCTION_VALIDATE(details_->GetString("color", &color_string));
     if (!image_util::ParseCssColorString(color_string, &color))
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 15a39a5..75c9f026 100644
--- a/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -244,7 +244,7 @@
   std::unique_ptr<base::Value> result(util::RunFunctionAndReturnSingleResult(
       function.get(), base::StringPrintf("[\"%s\"]", kId), browser()));
   ASSERT_TRUE(result.get() != NULL);
-  ASSERT_TRUE(result->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(result->IsType(base::Value::Type::DICTIONARY));
   base::DictionaryValue* dict =
       static_cast<base::DictionaryValue*>(result.get());
   std::string reason;
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 281ebe37..978aaf3 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -201,8 +201,7 @@
                                   "prohibited.html"));
 }
 
-// Disabled. See http://crbug.com/176023
-IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, DISABLED_LaunchPanelApp) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchPanelApp) {
   ExtensionService* service = extensions::ExtensionSystem::Get(
       browser()->profile())->extension_service();
 
diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.cc b/chrome/browser/extensions/api/messaging/extension_message_port.cc
index caf0f06..09e5560 100644
--- a/chrome/browser/extensions/api/messaging/extension_message_port.cc
+++ b/chrome/browser/extensions/api/messaging/extension_message_port.cc
@@ -91,7 +91,7 @@
 
 ExtensionMessagePort::ExtensionMessagePort(
     base::WeakPtr<MessageService> message_service,
-    int port_id,
+    const PortId& port_id,
     const std::string& extension_id,
     content::RenderProcessHost* extension_process)
     : weak_message_service_(message_service),
@@ -112,7 +112,7 @@
 
 ExtensionMessagePort::ExtensionMessagePort(
     base::WeakPtr<MessageService> message_service,
-    int port_id,
+    const PortId& port_id,
     const std::string& extension_id,
     content::RenderFrameHost* rfh,
     bool include_child_frames)
diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.h b/chrome/browser/extensions/api/messaging/extension_message_port.h
index af122c0..4c2ba8a8 100644
--- a/chrome/browser/extensions/api/messaging/extension_message_port.h
+++ b/chrome/browser/extensions/api/messaging/extension_message_port.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/extensions/api/messaging/message_service.h"
+#include "extensions/common/api/messaging/port_id.h"
 
 class GURL;
 
@@ -29,14 +30,14 @@
  public:
   // Create a port that is tied to frame(s) in a single tab.
   ExtensionMessagePort(base::WeakPtr<MessageService> message_service,
-                       int port_id,
+                       const PortId& port_id,
                        const std::string& extension_id,
                        content::RenderFrameHost* rfh,
                        bool include_child_frames);
   // Create a port that is tied to all frames of an extension, possibly spanning
   // multiple tabs, including the invisible background page, popups, etc.
   ExtensionMessagePort(base::WeakPtr<MessageService> message_service,
-                       int port_id,
+                       const PortId& port_id,
                        const std::string& extension_id,
                        content::RenderProcessHost* extension_process);
   ~ExtensionMessagePort() override;
@@ -85,7 +86,7 @@
 
   base::WeakPtr<MessageService> weak_message_service_;
 
-  int port_id_;
+  const PortId port_id_;
   std::string extension_id_;
   content::BrowserContext* browser_context_;
   // Only for receivers in an extension process.
diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc
index 5b4c382..2dda966 100644
--- a/chrome/browser/extensions/api/messaging/message_service.cc
+++ b/chrome/browser/extensions/api/messaging/message_service.cc
@@ -8,7 +8,6 @@
 #include <limits>
 #include <utility>
 
-#include "base/atomic_sequence_num.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/json/json_writer.h"
@@ -63,18 +62,6 @@
 using content::SiteInstance;
 using content::WebContents;
 
-// Since we have 2 ports for every channel, we just index channels by half the
-// port ID.
-#define GET_CHANNEL_ID(port_id) ((port_id) / 2)
-#define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
-#define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
-
-// Port1 is always even, port2 is always odd.
-#define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
-
-// Change even to odd and vice versa, to get the other side of a given channel.
-#define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1)
-
 namespace extensions {
 
 MessageService::PolicyPermission MessageService::IsNativeMessagingHostAllowed(
@@ -138,7 +125,7 @@
   std::unique_ptr<base::DictionaryValue> source_tab;
   int source_frame_id;
   std::unique_ptr<MessagePort> receiver;
-  int receiver_port_id;
+  PortId receiver_port_id;
   std::string source_extension_id;
   std::string target_extension_id;
   GURL source_url;
@@ -153,7 +140,7 @@
                     std::unique_ptr<base::DictionaryValue> source_tab,
                     int source_frame_id,
                     MessagePort* receiver,
-                    int receiver_port_id,
+                    const PortId& receiver_port_id,
                     const std::string& source_extension_id,
                     const std::string& target_extension_id,
                     const GURL& source_url,
@@ -181,8 +168,6 @@
 
 namespace {
 
-static base::StaticAtomicSequenceNumber g_next_channel_id;
-
 static content::RenderProcessHost* GetExtensionProcess(
     BrowserContext* context,
     const std::string& extension_id) {
@@ -201,28 +186,6 @@
   return false;
 }
 
-// static
-void MessageService::AllocatePortIdPair(int* port1, int* port2) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  unsigned channel_id = static_cast<unsigned>(g_next_channel_id.GetNext()) %
-                        (std::numeric_limits<int32_t>::max() / 2);
-  unsigned port1_id = channel_id * 2;
-  unsigned port2_id = channel_id * 2 + 1;
-
-  // Sanity checks to make sure our channel<->port converters are correct.
-  DCHECK(IS_OPENER_PORT_ID(port1_id));
-  DCHECK_EQ(GET_OPPOSITE_PORT_ID(port1_id), port2_id);
-  DCHECK_EQ(GET_OPPOSITE_PORT_ID(port2_id), port1_id);
-  DCHECK_EQ(GET_CHANNEL_ID(port1_id), GET_CHANNEL_ID(port2_id));
-  DCHECK_EQ(GET_CHANNEL_ID(port1_id), channel_id);
-  DCHECK_EQ(GET_CHANNEL_OPENER_ID(channel_id), port1_id);
-  DCHECK_EQ(GET_CHANNEL_RECEIVERS_ID(channel_id), port2_id);
-
-  *port1 = port1_id;
-  *port2 = port2_id;
-}
-
 MessageService::MessageService(BrowserContext* context)
     : lazy_background_task_queue_(
           LazyBackgroundTaskQueue::Get(context)),
@@ -251,13 +214,16 @@
 }
 
 void MessageService::OpenChannelToExtension(
-    int source_process_id, int source_routing_id, int receiver_port_id,
+    int source_process_id,
+    int source_routing_id,
+    const PortId& source_port_id,
     const std::string& source_extension_id,
     const std::string& target_extension_id,
     const GURL& source_url,
     const std::string& channel_name,
     bool include_tls_channel_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(source_port_id.is_opener);
 
   content::RenderFrameHost* source =
       content::RenderFrameHost::FromID(source_process_id, source_routing_id);
@@ -268,6 +234,8 @@
   ExtensionRegistry* registry = ExtensionRegistry::Get(context);
   const Extension* target_extension =
       registry->enabled_extensions().GetByID(target_extension_id);
+  PortId receiver_port_id(source_port_id.context_id, source_port_id.port_number,
+                          false);
   if (!target_extension) {
     DispatchOnDisconnect(
         source, receiver_port_id, kReceivingEndDoesntExistError);
@@ -355,7 +323,7 @@
       target_extension_id, source_url, channel_name, include_tls_channel_id,
       include_guest_process_info));
 
-  pending_incognito_channels_[GET_CHANNEL_ID(params->receiver_port_id)] =
+  pending_incognito_channels_[params->receiver_port_id.GetChannelId()] =
       PendingMessagesQueue();
   if (context->IsOffTheRecord() &&
       !util::IsIncognitoEnabled(target_extension_id, context)) {
@@ -405,9 +373,10 @@
 void MessageService::OpenChannelToNativeApp(
     int source_process_id,
     int source_routing_id,
-    int receiver_port_id,
+    const PortId& source_port_id,
     const std::string& native_app_name) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(source_port_id.is_opener);
 
   content::RenderFrameHost* source =
       content::RenderFrameHost::FromID(source_process_id, source_routing_id);
@@ -430,6 +399,8 @@
                         extension->permissions_data()->HasAPIPermission(
                             APIPermission::kNativeMessaging);
 
+  PortId receiver_port_id(source_port_id.context_id, source_port_id.port_number,
+                          false);
   if (!has_permission) {
     DispatchOnDisconnect(source, receiver_port_id, kMissingPermissionError);
     return;
@@ -449,8 +420,7 @@
 
   std::unique_ptr<MessageChannel> channel(new MessageChannel());
   channel->opener.reset(
-      new ExtensionMessagePort(weak_factory_.GetWeakPtr(),
-                               GET_OPPOSITE_PORT_ID(receiver_port_id),
+      new ExtensionMessagePort(weak_factory_.GetWeakPtr(), source_port_id,
                                extension->id(), source, false));
   if (!channel->opener->IsValidPort())
     return;
@@ -487,13 +457,14 @@
 
 void MessageService::OpenChannelToTab(int source_process_id,
                                       int source_routing_id,
-                                      int receiver_port_id,
+                                      const PortId& source_port_id,
                                       int tab_id,
                                       int frame_id,
                                       const std::string& extension_id,
                                       const std::string& channel_name) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_GE(frame_id, -1);
+  DCHECK(source_port_id.is_opener);
 
   content::RenderFrameHost* source =
       content::RenderFrameHost::FromID(source_process_id, source_routing_id);
@@ -504,6 +475,8 @@
 
   WebContents* contents = NULL;
   std::unique_ptr<MessagePort> receiver;
+  PortId receiver_port_id(source_port_id.context_id, source_port_id.port_number,
+                          false);
   if (!ExtensionTabUtil::GetTabById(tab_id, profile, true, NULL, NULL,
                                     &contents, NULL) ||
       contents->GetController().NeedsReload()) {
@@ -572,10 +545,9 @@
     return;
   }
 
-  std::unique_ptr<ExtensionMessagePort> opener(
-      new ExtensionMessagePort(weak_factory_.GetWeakPtr(),
-                               GET_OPPOSITE_PORT_ID(params->receiver_port_id),
-                               params->source_extension_id, source, false));
+  std::unique_ptr<ExtensionMessagePort> opener(new ExtensionMessagePort(
+      weak_factory_.GetWeakPtr(), params->receiver_port_id.GetOppositePortId(),
+      params->source_extension_id, source, false));
   if (!opener->IsValidPort())
     return;
   opener->OpenPort(params->source_process_id, params->source_routing_id);
@@ -654,20 +626,22 @@
 }
 
 void MessageService::AddChannel(std::unique_ptr<MessageChannel> channel,
-                                int receiver_port_id) {
+                                const PortId& receiver_port_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  int channel_id = GET_CHANNEL_ID(receiver_port_id);
+  ChannelId channel_id = receiver_port_id.GetChannelId();
   CHECK(channels_.find(channel_id) == channels_.end());
   channels_[channel_id] = std::move(channel);
   pending_lazy_background_page_channels_.erase(channel_id);
 }
 
-void MessageService::OpenPort(int port_id, int process_id, int routing_id) {
+void MessageService::OpenPort(const PortId& port_id,
+                              int process_id,
+                              int routing_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(!IS_OPENER_PORT_ID(port_id));
+  DCHECK(!port_id.is_opener);
 
-  int channel_id = GET_CHANNEL_ID(port_id);
+  ChannelId channel_id = port_id.GetChannelId();
   MessageChannelMap::iterator it = channels_.find(channel_id);
   if (it == channels_.end())
     return;
@@ -675,26 +649,28 @@
   it->second->receiver->OpenPort(process_id, routing_id);
 }
 
-void MessageService::ClosePort(
-    int port_id, int process_id, int routing_id, bool force_close) {
+void MessageService::ClosePort(const PortId& port_id,
+                               int process_id,
+                               int routing_id,
+                               bool force_close) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ClosePortImpl(port_id, process_id, routing_id, force_close, std::string());
 }
 
-void MessageService::CloseChannel(int port_id,
+void MessageService::CloseChannel(const PortId& port_id,
                                   const std::string& error_message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   ClosePortImpl(port_id, content::ChildProcessHost::kInvalidUniqueID,
                 MSG_ROUTING_NONE, true, error_message);
 }
 
-void MessageService::ClosePortImpl(int port_id,
+void MessageService::ClosePortImpl(const PortId& port_id,
                                    int process_id,
                                    int routing_id,
                                    bool force_close,
                                    const std::string& error_message) {
   // Note: The channel might be gone already, if the other side closed first.
-  int channel_id = GET_CHANNEL_ID(port_id);
+  ChannelId channel_id = port_id.GetChannelId();
   MessageChannelMap::iterator it = channels_.find(channel_id);
   if (it == channels_.end()) {
     PendingLazyBackgroundPageChannelMap::iterator pending =
@@ -714,18 +690,17 @@
   // receivers, whereas closing a channel always forces all ports to be closed.
   if (force_close) {
     CloseChannelImpl(it, port_id, error_message, true);
-  } else if (IS_OPENER_PORT_ID(port_id)) {
+  } else if (port_id.is_opener) {
     it->second->opener->ClosePort(process_id, routing_id);
   } else {
     it->second->receiver->ClosePort(process_id, routing_id);
   }
 }
 
-void MessageService::CloseChannelImpl(
-    MessageChannelMap::iterator channel_iter,
-    int closing_port_id,
-    const std::string& error_message,
-    bool notify_other_port) {
+void MessageService::CloseChannelImpl(MessageChannelMap::iterator channel_iter,
+                                      const PortId& closing_port_id,
+                                      const std::string& error_message,
+                                      bool notify_other_port) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   std::unique_ptr<MessageChannel> channel = std::move(channel_iter->second);
@@ -735,8 +710,8 @@
 
   // Notify the other side.
   if (notify_other_port) {
-    MessagePort* port = IS_OPENER_PORT_ID(closing_port_id) ?
-        channel->receiver.get() : channel->opener.get();
+    MessagePort* port = closing_port_id.is_opener ? channel->receiver.get()
+                                                  : channel->opener.get();
     port->DispatchOnDisconnect(error_message);
   }
 
@@ -745,10 +720,11 @@
   channel->receiver->DecrementLazyKeepaliveCount();
 }
 
-void MessageService::PostMessage(int source_port_id, const Message& message) {
+void MessageService::PostMessage(const PortId& source_port_id,
+                                 const Message& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  int channel_id = GET_CHANNEL_ID(source_port_id);
+  ChannelId channel_id = source_port_id.GetChannelId();
   MessageChannelMap::iterator iter = channels_.find(channel_id);
   if (iter == channels_.end()) {
     // If this channel is pending, queue up the PostMessage to run once
@@ -760,8 +736,8 @@
   DispatchMessage(source_port_id, iter->second.get(), message);
 }
 
-void MessageService::EnqueuePendingMessage(int source_port_id,
-                                           int channel_id,
+void MessageService::EnqueuePendingMessage(const PortId& source_port_id,
+                                           const ChannelId& channel_id,
                                            const Message& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -794,8 +770,8 @@
 }
 
 void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
-    int source_port_id,
-    int channel_id,
+    const PortId& source_port_id,
+    const ChannelId& channel_id,
     const Message& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -809,17 +785,16 @@
   }
 }
 
-void MessageService::DispatchMessage(int source_port_id,
+void MessageService::DispatchMessage(const PortId& source_port_id,
                                      MessageChannel* channel,
                                      const Message& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Figure out which port the ID corresponds to.
-  int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id);
-  MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ?
-      channel->opener.get() : channel->receiver.get();
+  MessagePort* dest_port = source_port_id.is_opener ? channel->receiver.get()
+                                                    : channel->opener.get();
 
-  port->DispatchOnMessage(message);
+  dest_port->DispatchOnMessage(message);
 }
 
 bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask(
@@ -841,7 +816,7 @@
   if (!lazy_background_task_queue_->ShouldEnqueueTask(context, extension))
     return false;
 
-  int channel_id = GET_CHANNEL_ID((*params)->receiver_port_id);
+  ChannelId channel_id = (*params)->receiver_port_id.GetChannelId();
   pending_lazy_background_page_channels_[channel_id] =
       PendingLazyBackgroundPageChannel(context, extension->id());
   int source_id = (*params)->source_process_id;
@@ -862,7 +837,7 @@
     bool allowed) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  int channel_id = GET_CHANNEL_ID(params->receiver_port_id);
+  ChannelId channel_id = params->receiver_port_id.GetChannelId();
 
   PendingChannelMap::iterator pending_for_incognito =
       pending_incognito_channels_.find(channel_id);
@@ -944,7 +919,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   params->tls_channel_id.assign(tls_channel_id);
-  int channel_id = GET_CHANNEL_ID(params->receiver_port_id);
+  ChannelId channel_id = params->receiver_port_id.GetChannelId();
 
   PendingChannelMap::iterator pending_for_tls_channel_id =
       pending_tls_channel_id_channels_.find(channel_id);
@@ -1000,19 +975,19 @@
 }
 
 void MessageService::DispatchOnDisconnect(content::RenderFrameHost* source,
-                                          int port_id,
+                                          const PortId& port_id,
                                           const std::string& error_message) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   ExtensionMessagePort port(weak_factory_.GetWeakPtr(),
-                            GET_OPPOSITE_PORT_ID(port_id), "", source, false);
+                            port_id.GetOppositePortId(), "", source, false);
   if (!port.IsValidPort())
     return;
   port.DispatchOnDisconnect(error_message);
 }
 
 void MessageService::DispatchPendingMessages(const PendingMessagesQueue& queue,
-                                             int channel_id) {
+                                             const ChannelId& channel_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   MessageChannelMap::iterator channel_iter = channels_.find(channel_id);
diff --git a/chrome/browser/extensions/api/messaging/message_service.h b/chrome/browser/extensions/api/messaging/message_service.h
index bc04a0f5..e5d5322 100644
--- a/chrome/browser/extensions/api/messaging/message_service.h
+++ b/chrome/browser/extensions/api/messaging/message_service.h
@@ -19,6 +19,7 @@
 #include "extensions/browser/api/messaging/native_message_host.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/extension_id.h"
 
 class GURL;
@@ -122,10 +123,6 @@
       const PrefService* pref_service,
       const std::string& native_host_name);
 
-  // Allocates a pair of port ids.
-  // NOTE: this can be called from any thread.
-  static void AllocatePortIdPair(int* port1, int* port2);
-
   explicit MessageService(content::BrowserContext* context);
   ~MessageService() override;
 
@@ -138,46 +135,49 @@
   // Given an extension's ID, opens a channel between the given renderer "port"
   // and every listening context owned by that extension. |channel_name| is
   // an optional identifier for use by extension developers.
-  void OpenChannelToExtension(
-      int source_process_id, int source_routing_id, int receiver_port_id,
-      const std::string& source_extension_id,
-      const std::string& target_extension_id,
-      const GURL& source_url,
-      const std::string& channel_name,
-      bool include_tls_channel_id);
+  void OpenChannelToExtension(int source_process_id,
+                              int source_routing_id,
+                              const PortId& source_port_id,
+                              const std::string& source_extension_id,
+                              const std::string& target_extension_id,
+                              const GURL& source_url,
+                              const std::string& channel_name,
+                              bool include_tls_channel_id);
 
   // Same as above, but opens a channel to the tab with the given ID.  Messages
   // are restricted to that tab, so if there are multiple tabs in that process,
   // only the targeted tab will receive messages.
   void OpenChannelToTab(int source_process_id,
                         int source_routing_id,
-                        int receiver_port_id,
+                        const PortId& source_port_id,
                         int tab_id,
                         int frame_id,
                         const std::string& extension_id,
                         const std::string& channel_name);
 
-  void OpenChannelToNativeApp(
-      int source_process_id,
-      int source_routing_id,
-      int receiver_port_id,
-      const std::string& native_app_name);
+  void OpenChannelToNativeApp(int source_process_id,
+                              int source_routing_id,
+                              const PortId& source_port_id,
+                              const std::string& native_app_name);
 
   // Mark the given port as opened by the frame identified by
   // (process_id, routing_id).
-  void OpenPort(int port_id, int process_id, int routing_id);
+  void OpenPort(const PortId& port_id, int process_id, int routing_id);
 
   // Closes the given port in the given frame. If this was the last frame or if
   // |force_close| is true, then the other side is closed as well.
-  void ClosePort(int port_id, int process_id, int routing_id, bool force_close);
+  void ClosePort(const PortId& port_id,
+                 int process_id,
+                 int routing_id,
+                 bool force_close);
 
   // Closes the message channel associated with the given port, and notifies
   // the other side.
-  void CloseChannel(int port_id, const std::string& error_message);
+  void CloseChannel(const PortId& port_id, const std::string& error_message);
 
   // Enqueues a message on a pending channel, or sends a message to the given
   // port if the channel isn't pending.
-  void PostMessage(int port_id, const Message& message);
+  void PostMessage(const PortId& port_id, const Message& message);
 
  private:
   friend class MockMessageService;
@@ -185,20 +185,21 @@
   struct OpenChannelParams;
 
   // A map of channel ID to its channel object.
-  using MessageChannelMap = std::map<int, std::unique_ptr<MessageChannel>>;
+  using MessageChannelMap =
+      std::map<ChannelId, std::unique_ptr<MessageChannel>>;
 
-  using PendingMessage = std::pair<int, Message>;
+  using PendingMessage = std::pair<PortId, Message>;
   using PendingMessagesQueue = std::vector<PendingMessage>;
   // A set of channel IDs waiting to complete opening, and any pending messages
   // queued to be sent on those channels.
-  using PendingChannelMap = std::map<int, PendingMessagesQueue>;
+  using PendingChannelMap = std::map<ChannelId, PendingMessagesQueue>;
 
   // A map of channel ID to information about the extension that is waiting
   // for that channel to open. Used for lazy background pages.
   using PendingLazyBackgroundPageChannel =
       std::pair<content::BrowserContext*, ExtensionId>;
   using PendingLazyBackgroundPageChannelMap =
-      std::map<int, PendingLazyBackgroundPageChannel>;
+      std::map<ChannelId, PendingLazyBackgroundPageChannel>;
 
   // Common implementation for opening a channel configured by |params|.
   //
@@ -212,21 +213,21 @@
                        const Extension* target_extension,
                        bool did_enqueue);
 
-  void ClosePortImpl(int port_id,
+  void ClosePortImpl(const PortId& port_id,
                      int process_id,
                      int routing_id,
                      bool force_close,
                      const std::string& error_message);
 
   void CloseChannelImpl(MessageChannelMap::iterator channel_iter,
-                        int port_id,
+                        const PortId& port_id,
                         const std::string& error_message,
                         bool notify_other_port);
 
   // Have MessageService take ownership of |channel|, and remove any pending
   // channels with the same id.
   void AddChannel(std::unique_ptr<MessageChannel> channel,
-                  int receiver_port_id);
+                  const PortId& receiver_port_id);
 
   // If the channel is being opened from an incognito tab the user must allow
   // the connection.
@@ -236,16 +237,18 @@
                     const std::string& tls_channel_id);
 
   // Enqueues a message on a pending channel.
-  void EnqueuePendingMessage(int port_id, int channel_id,
+  void EnqueuePendingMessage(const PortId& port_id,
+                             const ChannelId& channel_id,
                              const Message& message);
 
   // Enqueues a message on a channel pending on a lazy background page load.
-  void EnqueuePendingMessageForLazyBackgroundLoad(int port_id,
-                                                  int channel_id,
+  void EnqueuePendingMessageForLazyBackgroundLoad(const PortId& port_id,
+                                                  const ChannelId& channel_id,
                                                   const Message& message);
 
   // Immediately sends a message to the given port.
-  void DispatchMessage(int port_id, MessageChannel* channel,
+  void DispatchMessage(const PortId& port_id,
+                       MessageChannel* channel,
                        const Message& message);
 
   // Potentially registers a pending task with the LazyBackgroundTaskQueue
@@ -264,7 +267,7 @@
       std::unique_ptr<OpenChannelParams> params,
       int source_process_id,
       extensions::ExtensionHost* host);
-  void PendingLazyBackgroundPageClosePort(int port_id,
+  void PendingLazyBackgroundPageClosePort(const PortId& port_id,
                                           int process_id,
                                           int routing_id,
                                           bool force_close,
@@ -274,7 +277,7 @@
       ClosePortImpl(port_id, process_id, routing_id, force_close,
                     error_message);
   }
-  void PendingLazyBackgroundPagePostMessage(int port_id,
+  void PendingLazyBackgroundPagePostMessage(const PortId& port_id,
                                             const Message& message,
                                             extensions::ExtensionHost* host) {
     if (host)
@@ -284,11 +287,11 @@
   // Immediate dispatches a disconnect to |source| for |port_id|. Sets source's
   // runtime.lastMessage to |error_message|, if any.
   void DispatchOnDisconnect(content::RenderFrameHost* source,
-                            int port_id,
+                            const PortId& port_id,
                             const std::string& error_message);
 
   void DispatchPendingMessages(const PendingMessagesQueue& queue,
-                               int channel_id);
+                               const ChannelId& channel_id);
 
   // BrowserContextKeyedAPI implementation.
   static const char* service_name() {
diff --git a/chrome/browser/extensions/api/messaging/native_message_port.cc b/chrome/browser/extensions/api/messaging/native_message_port.cc
index ab9e4ac..d41cb9f 100644
--- a/chrome/browser/extensions/api/messaging/native_message_port.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_port.cc
@@ -86,7 +86,7 @@
 
 NativeMessagePort::NativeMessagePort(
     base::WeakPtr<MessageService> message_service,
-    int port_id,
+    const PortId& port_id,
     std::unique_ptr<NativeMessageHost> native_message_host)
     : weak_message_service_(message_service),
       host_task_runner_(native_message_host->task_runner()),
diff --git a/chrome/browser/extensions/api/messaging/native_message_port.h b/chrome/browser/extensions/api/messaging/native_message_port.h
index 330bec2..ecfa6ac9 100644
--- a/chrome/browser/extensions/api/messaging/native_message_port.h
+++ b/chrome/browser/extensions/api/messaging/native_message_port.h
@@ -7,6 +7,7 @@
 
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/extensions/api/messaging/message_service.h"
+#include "extensions/common/api/messaging/port_id.h"
 
 namespace extensions {
 
@@ -15,7 +16,7 @@
 class NativeMessagePort : public MessageService::MessagePort {
  public:
   NativeMessagePort(base::WeakPtr<MessageService> message_service,
-                    int port_id,
+                    const PortId& port_id,
                     std::unique_ptr<NativeMessageHost> native_message_host);
   ~NativeMessagePort() override;
 
@@ -31,7 +32,7 @@
   base::ThreadChecker thread_checker_;
   base::WeakPtr<MessageService> weak_message_service_;
   scoped_refptr<base::SingleThreadTaskRunner> host_task_runner_;
-  int port_id_;
+  const PortId port_id_;
   std::unique_ptr<Core> core_;
 
   base::WeakPtrFactory<NativeMessagePort> weak_factory_;
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc b/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
index fee82b6c..bb153dc8 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_policy_handler.cc
@@ -21,7 +21,7 @@
     const char* policy_name,
     const char* pref_path,
     bool allow_wildcards)
-    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
+    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
       pref_path_(pref_path),
       allow_wildcards_(allow_wildcards) {}
 
@@ -71,7 +71,7 @@
     if (!(*entry)->GetAsString(&name)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_STRING));
+                       base::Value::GetTypeName(base::Value::Type::STRING));
       continue;
     }
     if (!(allow_wildcards_ && name == "*") &&
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index b03ea5d..39abdd9 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -278,7 +278,7 @@
     std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
         notification_function.get(), "[]", browser(), utils::NONE));
 
-    EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
+    EXPECT_EQ(base::Value::Type::STRING, result->GetType());
     std::string permission_level;
     EXPECT_TRUE(result->GetAsString(&permission_level));
     EXPECT_EQ("granted", permission_level);
@@ -303,7 +303,7 @@
     std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
         notification_function.get(), "[]", browser(), utils::NONE));
 
-    EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
+    EXPECT_EQ(base::Value::Type::STRING, result->GetType());
     std::string permission_level;
     EXPECT_TRUE(result->GetAsString(&permission_level));
     EXPECT_EQ("denied", permission_level);
diff --git a/chrome/browser/extensions/api/proxy/proxy_api.cc b/chrome/browser/extensions/api/proxy/proxy_api.cc
index e52ebb1..c2ad7f3 100644
--- a/chrome/browser/extensions/api/proxy/proxy_api.cc
+++ b/chrome/browser/extensions/api/proxy/proxy_api.cc
@@ -103,7 +103,7 @@
   // When ExtensionToBrowserPref is called, the format of |extension_pref|
   // has been verified already by the extension API to match the schema
   // defined in the extension API JSON.
-  CHECK(extension_pref->IsType(base::Value::TYPE_DICTIONARY));
+  CHECK(extension_pref->IsType(base::Value::Type::DICTIONARY));
   const base::DictionaryValue* config =
       static_cast<const base::DictionaryValue*>(extension_pref);
 
@@ -140,7 +140,7 @@
 
 base::Value* ProxyPrefTransformer::BrowserToExtensionPref(
     const base::Value* browser_pref) {
-  CHECK(browser_pref->IsType(base::Value::TYPE_DICTIONARY));
+  CHECK(browser_pref->IsType(base::Value::Type::DICTIONARY));
 
   // This is a dictionary wrapper that exposes the proxy configuration stored in
   // the browser preferences.
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index fe8bc3f..0b7d6d97 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -358,17 +358,17 @@
 settings_private::PrefType PrefsUtil::GetType(const std::string& name,
                                               base::Value::Type type) {
   switch (type) {
-    case base::Value::Type::TYPE_BOOLEAN:
+    case base::Value::Type::BOOLEAN:
       return settings_private::PrefType::PREF_TYPE_BOOLEAN;
-    case base::Value::Type::TYPE_INTEGER:
-    case base::Value::Type::TYPE_DOUBLE:
+    case base::Value::Type::INTEGER:
+    case base::Value::Type::DOUBLE:
       return settings_private::PrefType::PREF_TYPE_NUMBER;
-    case base::Value::Type::TYPE_STRING:
+    case base::Value::Type::STRING:
       return IsPrefTypeURL(name) ? settings_private::PrefType::PREF_TYPE_URL
                                  : settings_private::PrefType::PREF_TYPE_STRING;
-    case base::Value::Type::TYPE_LIST:
+    case base::Value::Type::LIST:
       return settings_private::PrefType::PREF_TYPE_LIST;
-    case base::Value::Type::TYPE_DICTIONARY:
+    case base::Value::Type::DICTIONARY:
       return settings_private::PrefType::PREF_TYPE_DICTIONARY;
     default:
       return settings_private::PrefType::PREF_TYPE_NONE;
@@ -503,13 +503,13 @@
   DCHECK_EQ(pref->GetType(), value->GetType());
 
   switch (pref->GetType()) {
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_LIST:
-    case base::Value::TYPE_DICTIONARY:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::LIST:
+    case base::Value::Type::DICTIONARY:
       pref_service->Set(pref_name, *value);
       break;
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       // In JS all numbers are doubles.
       double double_value;
       if (!value->GetAsDouble(&double_value))
@@ -518,7 +518,7 @@
       pref_service->SetInteger(pref_name, static_cast<int>(double_value));
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string string_value;
       if (!value->GetAsString(&string_value))
         return PREF_TYPE_MISMATCH;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_api.cc b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
index a73f78f..64104da4 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_api.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_api.cc
@@ -99,7 +99,7 @@
     return RespondNow(Error(kDelegateIsNull));
 
   std::unique_ptr<base::Value> value = delegate->GetPref(parameters->name);
-  if (value->IsType(base::Value::TYPE_NULL))
+  if (value->IsType(base::Value::Type::NONE))
     return RespondNow(Error("Pref * does not exist", parameters->name));
   else
     return RespondNow(OneArgument(std::move(value)));
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
index 13ed8a8..1311f2f 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
@@ -46,7 +46,7 @@
   const TypedPrefMap& keys = prefs_util_->GetWhitelistedKeys();
   for (const auto& it : keys) {
     std::unique_ptr<base::Value> pref = GetPref(it.first);
-    if (!pref->IsType(base::Value::TYPE_NULL))
+    if (!pref->IsType(base::Value::Type::NONE))
       prefs->Append(std::move(pref));
   }
 
diff --git a/chrome/browser/extensions/api/storage/settings_apitest.cc b/chrome/browser/extensions/api/storage/settings_apitest.cc
index b934a9b..ac1d74b7 100644
--- a/chrome/browser/extensions/api/storage/settings_apitest.cc
+++ b/chrome/browser/extensions/api/storage/settings_apitest.cc
@@ -442,37 +442,37 @@
   ASSERT_TRUE(schema);
 
   ASSERT_TRUE(schema->valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema->type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema->type());
   ASSERT_TRUE(schema->GetKnownProperty("string-policy").valid());
-  EXPECT_EQ(base::Value::TYPE_STRING,
+  EXPECT_EQ(base::Value::Type::STRING,
             schema->GetKnownProperty("string-policy").type());
   ASSERT_TRUE(schema->GetKnownProperty("int-policy").valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER,
+  EXPECT_EQ(base::Value::Type::INTEGER,
             schema->GetKnownProperty("int-policy").type());
   ASSERT_TRUE(schema->GetKnownProperty("double-policy").valid());
-  EXPECT_EQ(base::Value::TYPE_DOUBLE,
+  EXPECT_EQ(base::Value::Type::DOUBLE,
             schema->GetKnownProperty("double-policy").type());
   ASSERT_TRUE(schema->GetKnownProperty("boolean-policy").valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN,
+  EXPECT_EQ(base::Value::Type::BOOLEAN,
             schema->GetKnownProperty("boolean-policy").type());
 
   policy::Schema list = schema->GetKnownProperty("list-policy");
   ASSERT_TRUE(list.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, list.type());
+  ASSERT_EQ(base::Value::Type::LIST, list.type());
   ASSERT_TRUE(list.GetItems().valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, list.GetItems().type());
+  EXPECT_EQ(base::Value::Type::STRING, list.GetItems().type());
 
   policy::Schema dict = schema->GetKnownProperty("dict-policy");
   ASSERT_TRUE(dict.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, dict.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, dict.type());
   list = dict.GetKnownProperty("list");
   ASSERT_TRUE(list.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, list.type());
+  ASSERT_EQ(base::Value::Type::LIST, list.type());
   dict = list.GetItems();
   ASSERT_TRUE(dict.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, dict.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, dict.type());
   ASSERT_TRUE(dict.GetProperty("anything").valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, dict.GetProperty("anything").type());
+  EXPECT_EQ(base::Value::Type::INTEGER, dict.GetProperty("anything").type());
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) {
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
index 580193a7..9f9cd8d 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
@@ -19,7 +19,6 @@
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/render_process_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/download_test_observer.h"
 #include "extensions/browser/event_router.h"
@@ -37,7 +36,6 @@
 using content::DownloadItem;
 using content::DownloadManager;
 using content::DownloadUrlParameters;
-using content::ResourceController;
 using content::WebContents;
 using extensions::Event;
 using extensions::ExtensionSystem;
diff --git a/chrome/browser/extensions/api/tabs/ash_panel_contents.cc b/chrome/browser/extensions/api/tabs/ash_panel_contents.cc
index 4279baa..c2509c6d 100644
--- a/chrome/browser/extensions/api/tabs/ash_panel_contents.cc
+++ b/chrome/browser/extensions/api/tabs/ash_panel_contents.cc
@@ -71,9 +71,6 @@
 void AshPanelContents::NativeWindowClosed() {
 }
 
-void AshPanelContents::DispatchWindowShownForTests() const {
-}
-
 void AshPanelContents::OnWindowReady() {}
 
 content::WebContents* AshPanelContents::GetWebContents() const {
diff --git a/chrome/browser/extensions/api/tabs/ash_panel_contents.h b/chrome/browser/extensions/api/tabs/ash_panel_contents.h
index 78299ec9..d0f7730 100644
--- a/chrome/browser/extensions/api/tabs/ash_panel_contents.h
+++ b/chrome/browser/extensions/api/tabs/ash_panel_contents.h
@@ -36,7 +36,6 @@
   void NativeWindowChanged(
       extensions::NativeAppWindow* native_app_window) override;
   void NativeWindowClosed() override;
-  void DispatchWindowShownForTests() const override;
   void OnWindowReady() override;
   content::WebContents* GetWebContents() const override;
   extensions::WindowController* GetWindowController() const override;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 110f53cd..ba4fa0c6 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1176,11 +1176,17 @@
 
   int tab_index = -1;
   TabStripModel* tab_strip = NULL;
-  if (!GetTabById(tab_id, browser_context(), include_incognito(), NULL,
+  Browser* browser = nullptr;
+  if (!GetTabById(tab_id, browser_context(), include_incognito(), &browser,
                   &tab_strip, &contents, &tab_index, &error_)) {
     return false;
   }
 
+  if (!ExtensionTabUtil::BrowserSupportsTabs(browser)) {
+    error_ = keys::kNoCurrentWindowError;
+    return false;
+  }
+
   web_contents_ = contents;
 
   // TODO(rafaelw): handle setting remaining tab properties:
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 9e33edf..b42eb04 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -943,6 +943,34 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
+                       ExtensionAPICannotNavigateDevtools) {
+  std::unique_ptr<base::DictionaryValue> test_extension_value(
+      api_test_utils::ParseDictionary(
+          "{\"name\": \"Test\", \"version\": \"1.0\", \"permissions\": "
+          "[\"tabs\"]}"));
+  scoped_refptr<Extension> extension(
+      api_test_utils::CreateExtension(test_extension_value.get()));
+
+  DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
+      browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
+
+  scoped_refptr<TabsUpdateFunction> function =
+      new TabsUpdateFunction();
+  function->set_extension(extension.get());
+
+  EXPECT_TRUE(base::MatchPattern(
+      utils::RunFunctionAndReturnError(
+          function.get(), base::StringPrintf(
+              "[%d, {\"url\":\"http://example.com\"}]",
+              ExtensionTabUtil::GetTabId(
+                  DevToolsWindowTesting::Get(devtools)->main_web_contents())),
+          DevToolsWindowTesting::Get(devtools)->browser()),
+      tabs_constants::kNoCurrentWindowError));
+
+  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
                        NoDevtoolsAndAppWindows) {
   DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
       browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
@@ -1168,7 +1196,7 @@
   int duplicate_tab_window_id = GetTabWindowId(duplicate_result.get());
   int duplicate_tab_index =
       api_test_utils::GetInteger(duplicate_result.get(), "index");
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, duplicate_result->GetType());
   // Duplicate tab id should be different from the original tab id.
   EXPECT_NE(tab_id, duplicate_tab_id);
   EXPECT_EQ(window_id, duplicate_tab_window_id);
@@ -1205,7 +1233,7 @@
   int duplicate_tab_window_id = GetTabWindowId(duplicate_result.get());
   int duplicate_tab_index =
       api_test_utils::GetInteger(duplicate_result.get(), "index");
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, duplicate_result->GetType());
   // Duplicate tab id should be different from the original tab id.
   EXPECT_NE(tab_id, duplicate_tab_id);
   EXPECT_EQ(window_id, duplicate_tab_window_id);
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index c1bbaf1..ed9baeb 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -37,7 +37,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/web_contents.h"
@@ -94,7 +93,7 @@
     WeakThrottleList::const_iterator it;
     for (it = throttles_.begin(); it != throttles_.end(); ++it) {
       if (it->get())
-        (*it)->Resume();
+        (*it)->ResumeHandler();
     }
     throttles_.clear();
   }
@@ -110,7 +109,7 @@
     WeakThrottleList::iterator it;
     for (it = throttles_.begin(); it != throttles_.end(); ++it) {
       if (it->get() && it->get()->url() == url) {
-        (*it)->Resume();
+        (*it)->ResumeHandler();
         throttles_.erase(it);
         break;
       }
@@ -141,9 +140,7 @@
   class Throttle : public content::ResourceThrottle,
                    public base::SupportsWeakPtr<Throttle> {
    public:
-    void Resume() {
-      controller()->Resume();
-    }
+    void ResumeHandler() { Resume(); }
 
     // content::ResourceThrottle implementation.
     void WillStartRequest(bool* defer) override { *defer = true; }
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 004116e..8cd15af 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -598,7 +598,7 @@
   std::unique_ptr<const base::Value> form_data =
       base::JSONReader::Read(kFormData);
   ASSERT_TRUE(form_data.get() != NULL);
-  ASSERT_TRUE(form_data->GetType() == base::Value::TYPE_DICTIONARY);
+  ASSERT_TRUE(form_data->GetType() == base::Value::Type::DICTIONARY);
   // Contents of raw.
   base::ListValue raw;
   extensions::subtle::AppendKeyValuePair(
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 5af2c5a..2fb92dd 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -402,7 +402,7 @@
     std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
         function.get(), kEmptyArgs, browser()));
     ASSERT_TRUE(result);
-    EXPECT_EQ(base::Value::TYPE_STRING, result->GetType());
+    EXPECT_EQ(base::Value::Type::STRING, result->GetType());
     std::string webgl_status;
     EXPECT_TRUE(result->GetAsString(&webgl_status));
     EXPECT_STREQ(webgl_allowed ? kWebGLStatusAllowed : kWebGLStatusBlocked,
diff --git a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
index 19dab65..1fd5b62 100644
--- a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
+++ b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
@@ -14,7 +14,7 @@
 #include "chrome/common/extensions/chrome_extension_messages.h"
 #include "chrome/common/extensions/extension_process_policy.h"
 #include "chrome/common/url_constants.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/render_frame_host.h"
@@ -219,11 +219,12 @@
       }
     }
 
-    if (rappor::RapporService* rappor = g_browser_process->rappor_service()) {
+    if (rappor::RapporServiceImpl* rappor =
+            g_browser_process->rappor_service()) {
       const std::string& extension_id =
           parent_is_extension ? parent_url.host() : frame_url.host();
-      rappor->RecordSample("Extensions.AffectedByIsolateExtensions",
-                           rappor::UMA_RAPPOR_TYPE, extension_id);
+      rappor->RecordSampleString("Extensions.AffectedByIsolateExtensions",
+                                 rappor::UMA_RAPPOR_TYPE, extension_id);
     }
   }
 }
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 1955d9187..2dabbd35 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -166,7 +166,7 @@
   JSONStringValueDeserializer deserializer(manifest_contents);
   std::unique_ptr<base::Value> manifest = deserializer.Deserialize(NULL, NULL);
 
-  if (!manifest.get() || !manifest->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!manifest.get() || !manifest->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Failed to parse extension manifest.";
     return std::unique_ptr<base::DictionaryValue>();
   }
diff --git a/chrome/browser/extensions/extension_function_test_utils.cc b/chrome/browser/extensions/extension_function_test_utils.cc
index 332dd99..2670e8e 100644
--- a/chrome/browser/extensions/extension_function_test_utils.cc
+++ b/chrome/browser/extensions/extension_function_test_utils.cc
@@ -59,13 +59,13 @@
 
 base::DictionaryValue* ToDictionary(base::Value* val) {
   EXPECT_TRUE(val);
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, val->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, val->GetType());
   return static_cast<base::DictionaryValue*>(val);
 }
 
 base::ListValue* ToList(base::Value* val) {
   EXPECT_TRUE(val);
-  EXPECT_EQ(base::Value::TYPE_LIST, val->GetType());
+  EXPECT_EQ(base::Value::Type::LIST, val->GetType());
   return static_cast<base::ListValue*>(val);
 }
 
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index aff5c2c..7af13aff 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -250,26 +250,26 @@
   // Load all extension management settings preferences.
   const base::ListValue* allowed_list_pref =
       static_cast<const base::ListValue*>(LoadPreference(
-          pref_names::kInstallAllowList, true, base::Value::TYPE_LIST));
+          pref_names::kInstallAllowList, true, base::Value::Type::LIST));
   // Allow user to use preference to block certain extensions. Note that policy
   // managed forcelist or whitelist will always override this.
   const base::ListValue* denied_list_pref =
       static_cast<const base::ListValue*>(LoadPreference(
-          pref_names::kInstallDenyList, false, base::Value::TYPE_LIST));
+          pref_names::kInstallDenyList, false, base::Value::Type::LIST));
   const base::DictionaryValue* forced_list_pref =
       static_cast<const base::DictionaryValue*>(LoadPreference(
-          pref_names::kInstallForceList, true, base::Value::TYPE_DICTIONARY));
+          pref_names::kInstallForceList, true, base::Value::Type::DICTIONARY));
   const base::ListValue* install_sources_pref =
       static_cast<const base::ListValue*>(LoadPreference(
-          pref_names::kAllowedInstallSites, true, base::Value::TYPE_LIST));
+          pref_names::kAllowedInstallSites, true, base::Value::Type::LIST));
   const base::ListValue* allowed_types_pref =
       static_cast<const base::ListValue*>(LoadPreference(
-          pref_names::kAllowedTypes, true, base::Value::TYPE_LIST));
+          pref_names::kAllowedTypes, true, base::Value::Type::LIST));
   const base::DictionaryValue* dict_pref =
       static_cast<const base::DictionaryValue*>(
           LoadPreference(pref_names::kExtensionManagement,
                          true,
-                         base::Value::TYPE_DICTIONARY));
+                         base::Value::Type::DICTIONARY));
 
   // Reset all settings.
   global_settings_.reset(new internal::GlobalSettings());
diff --git a/chrome/browser/extensions/extension_management_test_util.cc b/chrome/browser/extensions/extension_management_test_util.cc
index b873645..85b19468 100644
--- a/chrome/browser/extensions/extension_management_test_util.cc
+++ b/chrome/browser/extensions/extension_management_test_util.cc
@@ -63,7 +63,7 @@
     ClearInstallationModesForIndividualExtensions() {
   for (base::DictionaryValue::Iterator it(*pref_); !it.IsAtEnd();
        it.Advance()) {
-    DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+    DCHECK(it.value().IsType(base::Value::Type::DICTIONARY));
     if (it.key() != schema::kWildcard) {
       DCHECK(crx_file::id_util::IdIsValid(it.key()));
       pref_->Remove(make_path(it.key(), schema::kInstallationMode), nullptr);
diff --git a/chrome/browser/extensions/extension_management_unittest.cc b/chrome/browser/extensions/extension_management_unittest.cc
index ba9bfc51..56618b81 100644
--- a/chrome/browser/extensions/extension_management_unittest.cc
+++ b/chrome/browser/extensions/extension_management_unittest.cc
@@ -153,7 +153,7 @@
     std::unique_ptr<base::Value> parsed = base::JSONReader::ReadAndReturnError(
         kExampleDictPreference,
         base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS, NULL, &error_msg);
-    ASSERT_TRUE(parsed && parsed->IsType(base::Value::TYPE_DICTIONARY))
+    ASSERT_TRUE(parsed && parsed->IsType(base::Value::Type::DICTIONARY))
         << error_msg;
     SetPref(true, pref_names::kExtensionManagement, parsed.release());
   }
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 744a4fa..663e4c5 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -494,7 +494,7 @@
     std::unique_ptr<base::Value> json_value =
         deserializer.Deserialize(NULL, NULL);
 
-    if (!json_value || !json_value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!json_value || !json_value->IsType(base::Value::Type::DICTIONARY)) {
       ADD_FAILURE() << "Unable to deserialize json data";
       return std::unique_ptr<base::DictionaryValue>();
     } else {
diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc
index d862662f..59e242655 100644
--- a/chrome/browser/extensions/extension_system_impl.cc
+++ b/chrome/browser/extensions/extension_system_impl.cc
@@ -33,6 +33,8 @@
 #include "chrome/browser/extensions/state_store_notification_observer.h"
 #include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/extensions/update_install_gate.h"
+#include "chrome/browser/notifications/notifier_state_tracker.h"
+#include "chrome/browser/notifications/notifier_state_tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
@@ -55,12 +57,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/manifest_url_handlers.h"
-
-#if defined(ENABLE_NOTIFICATIONS)
-#include "chrome/browser/notifications/notifier_state_tracker.h"
-#include "chrome/browser/notifications/notifier_state_tracker_factory.h"
 #include "ui/message_center/notifier_settings.h"
-#endif
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/app_mode/app_mode_utils.h"
@@ -430,7 +427,6 @@
   bool incognito_enabled = util::IsIncognitoEnabled(extension->id(), profile_);
 
   bool notifications_disabled = false;
-#if defined(ENABLE_NOTIFICATIONS)
   message_center::NotifierId notifier_id(
       message_center::NotifierId::APPLICATION,
       extension->id());
@@ -439,7 +435,6 @@
       NotifierStateTrackerFactory::GetForProfile(profile_);
   notifications_disabled =
       !notifier_state_tracker->IsNotifierEnabled(notifier_id);
-#endif
 
   BrowserThread::PostTaskAndReply(
       BrowserThread::IO, FROM_HERE,
diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc
index df5aeb7..5faefc6e 100644
--- a/chrome/browser/extensions/extension_util.cc
+++ b/chrome/browser/extensions/extension_util.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/extensions/extension_util.h"
 
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
@@ -51,7 +53,7 @@
 // Returns true if |extension| should always be enabled in incognito mode.
 bool IsWhitelistedForIncognito(const Extension* extension) {
   const Feature* feature = FeatureProvider::GetBehaviorFeature(
-      BehaviorFeature::kWhitelistedForIncognito);
+      behavior_feature::kWhitelistedForIncognito);
   return feature && feature->IsAvailableToExtension(extension).is_available();
 }
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 6d6dba0..aa4cc32 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -211,7 +211,7 @@
 
     bool has_external_version = false;
     if (extension->Get(kExternalVersion, &external_version_value)) {
-      if (external_version_value->IsType(base::Value::TYPE_STRING)) {
+      if (external_version_value->IsType(base::Value::Type::STRING)) {
         external_version_value->GetAsString(&external_version);
         has_external_version = true;
       } else {
diff --git a/chrome/browser/extensions/policy_handlers.cc b/chrome/browser/extensions/policy_handlers.cc
index b0963d6..fa80158 100644
--- a/chrome/browser/extensions/policy_handlers.cc
+++ b/chrome/browser/extensions/policy_handlers.cc
@@ -28,7 +28,7 @@
 ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name,
                                                        const char* pref_path,
                                                        bool allow_wildcards)
-    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
+    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
       pref_path_(pref_path),
       allow_wildcards_(allow_wildcards) {}
 
@@ -81,7 +81,7 @@
     if (!(*entry)->GetAsString(&id)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_STRING));
+                       base::Value::GetTypeName(base::Value::Type::STRING));
       continue;
     }
     if (!(allow_wildcards_ && id == "*") && !crx_file::id_util::IdIsValid(id)) {
@@ -103,7 +103,7 @@
 
 ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
     : policy::TypeCheckingPolicyHandler(policy::key::kExtensionInstallForcelist,
-                                        base::Value::TYPE_LIST) {}
+                                        base::Value::Type::LIST) {}
 
 ExtensionInstallForcelistPolicyHandler::
     ~ExtensionInstallForcelistPolicyHandler() {}
@@ -149,7 +149,7 @@
       if (errors) {
         errors->AddError(policy_name(), entry - policy_list_value->begin(),
                          IDS_POLICY_TYPE_ERROR,
-                         base::Value::GetTypeName(base::Value::TYPE_STRING));
+                         base::Value::GetTypeName(base::Value::Type::STRING));
       }
       continue;
     }
@@ -193,7 +193,7 @@
 ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
     const char* policy_name,
     const char* pref_path)
-    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
+    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
       pref_path_(pref_path) {}
 
 ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
@@ -221,7 +221,7 @@
     if (!(*entry)->GetAsString(&url_pattern_string)) {
       errors->AddError(policy_name(), entry - list_value->begin(),
                        IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_STRING));
+                       base::Value::GetTypeName(base::Value::Type::STRING));
       return false;
     }
 
@@ -272,14 +272,14 @@
   // |policy_value| is expected to conform to the defined schema. But it's
   // not strictly valid since there are additional restrictions.
   const base::DictionaryValue* dict_value = NULL;
-  DCHECK(policy_value->IsType(base::Value::TYPE_DICTIONARY));
+  DCHECK(policy_value->IsType(base::Value::Type::DICTIONARY));
   policy_value->GetAsDictionary(&dict_value);
 
   for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
        it.Advance()) {
     DCHECK(it.key() == schema_constants::kWildcard ||
            crx_file::id_util::IdIsValid(it.key()));
-    DCHECK(it.value().IsType(base::Value::TYPE_DICTIONARY));
+    DCHECK(it.value().IsType(base::Value::Type::DICTIONARY));
 
     // Extracts sub dictionary.
     const base::DictionaryValue* sub_dict = NULL;
diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc
index d992b93a..5c47c49 100644
--- a/chrome/browser/extensions/user_script_listener.cc
+++ b/chrome/browser/extensions/user_script_listener.cc
@@ -13,7 +13,6 @@
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
@@ -33,14 +32,14 @@
   Throttle() : should_defer_(true), did_defer_(false) {
   }
 
-  void Resume() {
+  void ResumeIfDeferred() {
     DCHECK(should_defer_);
     should_defer_ = false;
     // Only resume the request if |this| has deferred it.
     if (did_defer_) {
       UMA_HISTOGRAM_TIMES("Extensions.ThrottledNetworkRequestDelay",
                           timer_->Elapsed());
-      controller()->Resume();
+      Resume();
     }
   }
 
@@ -146,7 +145,7 @@
   WeakThrottleList::const_iterator it;
   for (it = throttles_.begin(); it != throttles_.end(); ++it) {
     if (it->get())
-      (*it)->Resume();
+      (*it)->ResumeIfDeferred();
   }
   throttles_.clear();
 }
diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc
index 4f740b9..48a6f9a0 100644
--- a/chrome/browser/extensions/user_script_listener_unittest.cc
+++ b/chrome/browser/extensions/user_script_listener_unittest.cc
@@ -18,7 +18,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 #include "extensions/browser/extension_registry.h"
 #include "net/base/request_priority.h"
@@ -29,7 +28,6 @@
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using content::ResourceController;
 using content::ResourceThrottle;
 
 namespace extensions {
@@ -40,16 +38,15 @@
 const char kNotMatchingUrl[] = "http://example.com/";
 const char kTestData[] = "Hello, World!";
 
-class ThrottleController : public base::SupportsUserData::Data,
-                           public ResourceController {
+class ThrottleDelegate : public base::SupportsUserData::Data,
+                         public ResourceThrottle::Delegate {
  public:
-  ThrottleController(net::URLRequest* request, ResourceThrottle* throttle)
-      : request_(request),
-        throttle_(throttle) {
-    throttle_->set_controller_for_testing(this);
+  ThrottleDelegate(net::URLRequest* request, ResourceThrottle* throttle)
+      : request_(request), throttle_(throttle) {
+    throttle_->set_delegate_for_testing(this);
   }
 
-  // ResourceController implementation:
+  // ResourceThrottle::Delegate implementation:
   void Resume() override { request_->Start(); }
   void Cancel() override { NOTREACHED(); }
   void CancelAndIgnore() override { NOTREACHED(); }
@@ -166,8 +163,8 @@
 
     bool defer = false;
     if (throttle) {
-      request->SetUserData(NULL,
-                           new ThrottleController(request.get(), throttle));
+      request->SetUserData(nullptr,
+                           new ThrottleDelegate(request.get(), throttle));
 
       throttle->WillStartRequest(&defer);
     }
@@ -334,7 +331,7 @@
   ResourceThrottle* throttle =
       listener_->CreateResourceThrottle(url, content::RESOURCE_TYPE_MAIN_FRAME);
   ASSERT_TRUE(throttle);
-  request->SetUserData(NULL, new ThrottleController(request.get(), throttle));
+  request->SetUserData(nullptr, new ThrottleDelegate(request.get(), throttle));
 
   ASSERT_FALSE(request->is_pending());
 
diff --git a/chrome/browser/extensions/webstore_data_fetcher.cc b/chrome/browser/extensions/webstore_data_fetcher.cc
index 1586b92..c914ff3 100644
--- a/chrome/browser/extensions/webstore_data_fetcher.cc
+++ b/chrome/browser/extensions/webstore_data_fetcher.cc
@@ -56,7 +56,7 @@
 
 void WebstoreDataFetcher::OnJsonParseSuccess(
     std::unique_ptr<base::Value> parsed_json) {
-  if (!parsed_json->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!parsed_json->IsType(base::Value::Type::DICTIONARY)) {
     OnJsonParseFailure(kInvalidWebstoreResponseError);
     return;
   }
diff --git a/chrome/browser/field_trial_recorder.cc b/chrome/browser/field_trial_recorder.cc
new file mode 100644
index 0000000..2bc5b9f
--- /dev/null
+++ b/chrome/browser/field_trial_recorder.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/field_trial_recorder.h"
+
+#include "base/metrics/field_trial.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+FieldTrialRecorder::FieldTrialRecorder() = default;
+
+FieldTrialRecorder::~FieldTrialRecorder() = default;
+
+// static
+void FieldTrialRecorder::Create(
+    chrome::mojom::FieldTrialRecorderRequest request) {
+  mojo::MakeStrongBinding(base::MakeUnique<FieldTrialRecorder>(),
+                          std::move(request));
+}
+
+void FieldTrialRecorder::FieldTrialActivated(const std::string& trial_name) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Activate the trial in the browser process to match its state in the
+  // renderer. This is done by calling FindFullName which finalizes the group
+  // and activates the trial.
+  base::FieldTrialList::FindFullName(trial_name);
+}
diff --git a/chrome/browser/field_trial_recorder.h b/chrome/browser/field_trial_recorder.h
new file mode 100644
index 0000000..4737abe
--- /dev/null
+++ b/chrome/browser/field_trial_recorder.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_FIELD_TRIAL_RECORDER_H_
+#define CHROME_BROWSER_FIELD_TRIAL_RECORDER_H_
+
+#include "base/threading/thread_checker.h"
+#include "chrome/common/field_trial_recorder.mojom.h"
+
+class FieldTrialRecorder : public chrome::mojom::FieldTrialRecorder {
+ public:
+  FieldTrialRecorder();
+  ~FieldTrialRecorder() override;
+
+  static void Create(chrome::mojom::FieldTrialRecorderRequest request);
+
+ private:
+  // chrome::mojom::FieldTrialRecorder:
+  void FieldTrialActivated(const std::string& trial_name) override;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(FieldTrialRecorder);
+};
+
+#endif  // CHROME_BROWSER_FIELD_TRIAL_RECORDER_H_
diff --git a/chrome/browser/fullscreen.h b/chrome/browser/fullscreen.h
index 26ee79f..cbb916f 100644
--- a/chrome/browser/fullscreen.h
+++ b/chrome/browser/fullscreen.h
@@ -5,8 +5,11 @@
 #ifndef CHROME_BROWSER_FULLSCREEN_H_
 #define CHROME_BROWSER_FULLSCREEN_H_
 
+#include <stdint.h>
+
 #include "build/build_config.h"
 
-bool IsFullScreenMode();
+// |display_id| is used in USE_ASH build config only, ignored otherwise.
+bool IsFullScreenMode(int64_t display_id);
 
 #endif  // CHROME_BROWSER_FULLSCREEN_H_
diff --git a/chrome/browser/fullscreen_aurax11.cc b/chrome/browser/fullscreen_aurax11.cc
index a370f88d..2ceb01de 100644
--- a/chrome/browser/fullscreen_aurax11.cc
+++ b/chrome/browser/fullscreen_aurax11.cc
@@ -6,17 +6,11 @@
 
 #include <vector>
 
-#include "ash/root_window_controller.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
 #include "ui/views/widget/widget.h"
 
-bool IsFullScreenMode() {
-#if defined(USE_ASH)
-  ash::RootWindowController* controller =
-      ash::RootWindowController::ForTargetRootWindow();
-  return controller && controller->GetWindowForFullscreenMode();
-#else
+bool IsFullScreenMode(int64_t display_id) {
   std::vector<aura::Window*> all_windows =
       views::DesktopWindowTreeHostX11::GetAllOpenWindows();
   // Only the topmost window is checked. This works fine in the most cases, but
@@ -28,5 +22,4 @@
   views::Widget* widget =
       views::Widget::GetWidgetForNativeWindow(all_windows[0]);
   return widget && widget->IsFullscreen();
-#endif  // USE_ASH
 }
diff --git a/chrome/browser/fullscreen_chromeos.cc b/chrome/browser/fullscreen_chromeos.cc
index 8deeb23..12df5ef2e 100644
--- a/chrome/browser/fullscreen_chromeos.cc
+++ b/chrome/browser/fullscreen_chromeos.cc
@@ -5,17 +5,26 @@
 #include "chrome/browser/fullscreen.h"
 
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "chrome/browser/ui/ash/ash_util.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 
-bool IsFullScreenMode() {
+bool IsFullScreenMode(int64_t display_id) {
   if (chrome::IsRunningInMash()) {
     // TODO: http://crbug.com/640390.
     NOTIMPLEMENTED();
     return false;
   }
-  // TODO(oshima): Fullscreen is per display state. Investigate
-  // and fix if necessary.
-  ash::RootWindowController* controller =
-      ash::RootWindowController::ForTargetRootWindow();
-  return controller && controller->GetWindowForFullscreenMode();
+
+  for (ash::RootWindowController* controller :
+       ash::Shell::GetInstance()->GetAllRootWindowControllers()) {
+    if (display::Screen::GetScreen()
+            ->GetDisplayNearestWindow(controller->GetRootWindow())
+            .id() == display_id) {
+      return controller && controller->GetWindowForFullscreenMode();
+    }
+  }
+
+  return false;
 }
diff --git a/chrome/browser/fullscreen_mac.mm b/chrome/browser/fullscreen_mac.mm
index 350ea688..15fb90633 100644
--- a/chrome/browser/fullscreen_mac.mm
+++ b/chrome/browser/fullscreen_mac.mm
@@ -8,7 +8,7 @@
 
 #include "base/command_line.h"
 
-bool IsFullScreenMode() {
+bool IsFullScreenMode(int64_t display_id) {
   NSApplicationPresentationOptions options =
       [NSApp currentSystemPresentationOptions];
 
diff --git a/chrome/browser/fullscreen_win.cc b/chrome/browser/fullscreen_win.cc
index 75dfa01d..aaecff9b 100644
--- a/chrome/browser/fullscreen_win.cc
+++ b/chrome/browser/fullscreen_win.cc
@@ -11,10 +11,6 @@
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 
-#if defined(USE_ASH)
-#include "ash/root_window_controller.h"
-#endif
-
 static bool IsPlatformFullScreenMode() {
   // SHQueryUserNotificationState is only available for Vista and above.
 #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_VISTA)
@@ -100,14 +96,8 @@
   return (modes & (CONSOLE_FULLSCREEN | CONSOLE_FULLSCREEN_HARDWARE)) != 0;
 }
 
-bool IsFullScreenMode() {
-#if defined(USE_ASH)
-  ash::RootWindowController* controller =
-      ash::RootWindowController::ForTargetRootWindow();
-  return controller && controller->GetWindowForFullscreenMode();
-#else
+bool IsFullScreenMode(int64_t display_id) {
   return IsPlatformFullScreenMode() ||
          IsFullScreenWindowMode() ||
          IsFullScreenConsoleMode();
-#endif  // USE_ASH
 }
diff --git a/chrome/browser/history/browsing_history_service_handler.h b/chrome/browser/history/browsing_history_service_handler.h
index a00cb83..5f82b16 100644
--- a/chrome/browser/history/browsing_history_service_handler.h
+++ b/chrome/browser/history/browsing_history_service_handler.h
@@ -32,6 +32,7 @@
 
  protected:
   BrowsingHistoryServiceHandler() {}
+  virtual ~BrowsingHistoryServiceHandler() {}
 
   DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryServiceHandler);
 };
diff --git a/chrome/browser/interstitials/chrome_metrics_helper.cc b/chrome/browser/interstitials/chrome_metrics_helper.cc
index f69b390a..98dda57c 100644
--- a/chrome/browser/interstitials/chrome_metrics_helper.cc
+++ b/chrome/browser/interstitials/chrome_metrics_helper.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/features.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/features/features.h"
 
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index 31d58fb..e603602 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -358,6 +358,10 @@
 }
 
 void NotifyAndTerminate(bool fast_path) {
+  NotifyAndTerminate(fast_path, RebootPolicy::kOptionalReboot);
+}
+
+void NotifyAndTerminate(bool fast_path, RebootPolicy reboot_policy) {
 #if defined(OS_CHROMEOS)
   static bool notified = false;
   // Return if a shutdown request has already been sent.
@@ -376,15 +380,17 @@
   if (base::SysInfo::IsRunningOnChromeOS()) {
     // If we're on a ChromeOS device, reboot if an update has been applied,
     // or else signal the session manager to log out.
-    chromeos::UpdateEngineClient* update_engine_client
-        = chromeos::DBusThreadManager::Get()->GetUpdateEngineClient();
+    chromeos::UpdateEngineClient* update_engine_client =
+        chromeos::DBusThreadManager::Get()->GetUpdateEngineClient();
     if (update_engine_client->GetLastStatus().status ==
-        chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
+            chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT ||
+        reboot_policy == RebootPolicy::kForceReboot) {
       update_engine_client->RebootAfterUpdate();
     } else if (g_send_stop_request_to_session_manager) {
       // Don't ask SessionManager to stop session if the shutdown request comes
       // from session manager.
-      chromeos::DBusThreadManager::Get()->GetSessionManagerClient()
+      chromeos::DBusThreadManager::Get()
+          ->GetSessionManagerClient()
           ->StopSession();
     }
   } else {
diff --git a/chrome/browser/lifetime/application_lifetime.h b/chrome/browser/lifetime/application_lifetime.h
index 2d426ae..cccbe17 100644
--- a/chrome/browser/lifetime/application_lifetime.h
+++ b/chrome/browser/lifetime/application_lifetime.h
@@ -76,7 +76,13 @@
 
 // Send out notifications.
 // For ChromeOS, also request session manager to end the session.
+// |lifetime| is used to signal whether or not a reboot should be forced. By
+// default, the functions only reboot the system if an update is available. When
+// a component flash update is present, but not a system update, the
+// kForceReboot flag is passed.
+enum class RebootPolicy { kForceReboot, kOptionalReboot };
 void NotifyAndTerminate(bool fast_path);
+void NotifyAndTerminate(bool fast_path, RebootPolicy reboot_policy);
 
 #if !defined(OS_ANDROID)
 // Called once the application is exiting.
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index a2f943e4..d4204d7 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -48,7 +48,8 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/policy/core/common/cloud/policy_header_io_helper.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/loader/data_reduction_proxy_resource_throttle_android.cc b/chrome/browser/loader/data_reduction_proxy_resource_throttle_android.cc
index a370f04..c4ce04da6 100644
--- a/chrome/browser/loader/data_reduction_proxy_resource_throttle_android.cc
+++ b/chrome/browser/loader/data_reduction_proxy_resource_throttle_android.cc
@@ -8,10 +8,10 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/resource_context.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/load_flags.h"
@@ -43,9 +43,10 @@
     SafeBrowsingService* sb_service) {
   ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
   // Don't create the throttle if we can't handle the request.
-  if (io_data->IsOffTheRecord() || !io_data->IsDataReductionProxyEnabled() ||
+  if (io_data->IsOffTheRecord() || !io_data->data_reduction_proxy_io_data() ||
+      !io_data->data_reduction_proxy_io_data()->IsEnabled() ||
       request->url().SchemeIsCryptographic()) {
-    return NULL;
+    return nullptr;
   }
 
   return new DataReductionProxyResourceThrottle(request, resource_type,
@@ -79,7 +80,7 @@
     return;
 
   if (request_->load_flags() & net::LOAD_PREFETCH) {
-    controller()->Cancel();
+    Cancel();
     return;
   }
   const content::ResourceRequestInfo* info =
@@ -145,7 +146,7 @@
   if (proceed)
     ResumeRequest();
   else
-    controller()->Cancel();
+    Cancel();
 }
 
 SBThreatType DataReductionProxyResourceThrottle::CheckUrl() {
@@ -176,5 +177,5 @@
 
   // Inject the header before resuming the request.
   request_->SetExtraRequestHeaderByName(kUnsafeUrlProceedHeader, "1", true);
-  controller()->Resume();
+  Resume();
 }
diff --git a/chrome/browser/loader/safe_browsing_resource_throttle.cc b/chrome/browser/loader/safe_browsing_resource_throttle.cc
index 416e28e8..8fccf4b 100644
--- a/chrome/browser/loader/safe_browsing_resource_throttle.cc
+++ b/chrome/browser/loader/safe_browsing_resource_throttle.cc
@@ -21,7 +21,6 @@
 #include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/load_flags.h"
@@ -270,7 +269,7 @@
 
   if (request_->load_flags() & net::LOAD_PREFETCH) {
     // Don't prefetch resources that fail safe browsing, disallow them.
-    controller()->Cancel();
+    Cancel();
     UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.UnsafePrefetchCanceled",
                               resource_type_, content::RESOURCE_TYPE_LAST_TYPE);
     return;
@@ -352,10 +351,6 @@
       base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle));
 }
 
-void SafeBrowsingResourceThrottle::Cancel() {
-  controller()->Cancel();
-}
-
 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
 // thread when the user has decided to proceed with the current request, or
 // go back.
@@ -369,7 +364,7 @@
       ResumeRequest();
     }
   } else {
-    controller()->Cancel();
+    Cancel();
   }
 }
 
@@ -442,6 +437,6 @@
 
   if (resume) {
     defer_state_ = DEFERRED_NONE;
-    controller()->Resume();
+    Resume();
   }
 }
diff --git a/chrome/browser/loader/safe_browsing_resource_throttle.h b/chrome/browser/loader/safe_browsing_resource_throttle.h
index 2941a6be4..8a821412 100644
--- a/chrome/browser/loader/safe_browsing_resource_throttle.h
+++ b/chrome/browser/loader/safe_browsing_resource_throttle.h
@@ -134,10 +134,6 @@
       scoped_refptr<safe_browsing::SafeBrowsingUIManager> ui_manager,
       const security_interstitials::UnsafeResource& resource);
 
-  // Called on the IO thread if the request turned out to be for a prerendered
-  // page.
-  void Cancel();
-
   // Resumes the request, by continuing the deferred action (either starting the
   // request, or following a redirect).
   void ResumeRequest();
diff --git a/chrome/browser/mac/exception_processor.h b/chrome/browser/mac/exception_processor.h
new file mode 100644
index 0000000..3a9f391
--- /dev/null
+++ b/chrome/browser/mac/exception_processor.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MAC_EXCEPTION_PROCESSOR_H_
+#define CHROME_BROWSER_MAC_EXCEPTION_PROCESSOR_H_
+
+#include <stddef.h>
+
+@class NSException;
+
+namespace chrome {
+
+// Installs the Objective-C exception preprocessor. This records UMA and crash
+// keys for NSException objects. The preprocessor will also make fatal any
+// exception that is not handled.
+void InstallObjcExceptionPreprocessor();
+
+// The items below are exposed only for testing.
+////////////////////////////////////////////////////////////////////////////////
+
+// Removes the exception preprocessor if it is installed.
+void UninstallObjcExceptionPreprocessor();
+
+// Bin for unknown exceptions.
+extern const size_t kUnknownNSException;
+
+// Returns the histogram bin for |exception| if it is one we track
+// specifically, or |kUnknownNSException| if unknown.
+size_t BinForException(NSException* exception);
+
+// Use UMA to track exception occurance.
+void RecordExceptionWithUma(NSException* exception);
+
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_MAC_EXCEPTION_PROCESSOR_H_
diff --git a/chrome/browser/mac/exception_processor.mm b/chrome/browser/mac/exception_processor.mm
new file mode 100644
index 0000000..2b0cf21
--- /dev/null
+++ b/chrome/browser/mac/exception_processor.mm
@@ -0,0 +1,177 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/mac/exception_processor.h"
+
+#import <Foundation/Foundation.h>
+#include <libunwind.h>
+#include <objc/objc-exception.h>
+
+#include <type_traits>
+
+#include "base/compiler_specific.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/common/crash_keys.h"
+
+namespace chrome {
+
+// Maximum number of known named exceptions we'll support.  There is
+// no central registration, but I only find about 75 possibilities in
+// the system frameworks, and many of them are probably not
+// interesting to track in aggregate (those relating to distributed
+// objects, for instance).
+constexpr size_t kKnownNSExceptionCount = 25;
+
+const size_t kUnknownNSException = kKnownNSExceptionCount;
+
+size_t BinForException(NSException* exception) {
+  // A list of common known exceptions.  The list position will
+  // determine where they live in the histogram, so never move them
+  // around, only add to the end.
+  NSString* const kKnownNSExceptionNames[] = {
+    // Grab-bag exception, not very common.  CFArray (or other
+    // container) mutated while being enumerated is one case seen in
+    // production.
+    NSGenericException,
+
+    // Out-of-range on NSString or NSArray.  Quite common.
+    NSRangeException,
+
+    // Invalid arg to method, unrecognized selector.  Quite common.
+    NSInvalidArgumentException,
+
+    // malloc() returned null in object creation, I think.  Turns out
+    // to be very uncommon in production, because of the OOM killer.
+    NSMallocException,
+
+    // This contains things like windowserver errors, trying to draw
+    // views which aren't in windows, unable to read nib files.  By
+    // far the most common exception seen on the crash server.
+    NSInternalInconsistencyException,
+  };
+
+  // Make sure our array hasn't outgrown our abilities to track it.
+  static_assert(arraysize(kKnownNSExceptionNames) < kKnownNSExceptionCount,
+                "Cannot track more exceptions");
+
+  NSString* name = [exception name];
+  for (size_t i = 0; i < arraysize(kKnownNSExceptionNames); ++i) {
+    if (name == kKnownNSExceptionNames[i]) {
+      return i;
+    }
+  }
+  return kUnknownNSException;
+}
+
+void RecordExceptionWithUma(NSException* exception) {
+  UMA_HISTOGRAM_ENUMERATION("OSX.NSException",
+      BinForException(exception), kUnknownNSException);
+}
+
+static objc_exception_preprocessor g_next_preprocessor = nullptr;
+
+static const char* const kExceptionSinkholes[] = {
+  "CFRunLoopRunSpecific",
+  "_dispatch_client_callout",
+};
+
+// This function is used to make it clear to the crash processor that this is
+// a forced exception crash.
+static NOINLINE void TERMINATING_FROM_UNCAUGHT_NSEXCEPTION(id exception) {
+  LOG(FATAL) << "Terminating from Objective-C exception: "
+             << base::SysNSStringToUTF8([exception name])
+             << ": " << base::SysNSStringToUTF8([exception reason]);
+}
+
+static id ObjcExceptionPreprocessor(id exception) {
+  static bool seen_first_exception = false;
+
+  // Record UMA and crash keys about the exception.
+  RecordExceptionWithUma(exception);
+
+  const char* const kExceptionKey =
+      seen_first_exception ? crash_keys::mac::kLastNSException
+                           : crash_keys::mac::kFirstNSException;
+  NSString* value = [NSString stringWithFormat:@"%@ reason %@",
+      [exception name], [exception reason]];
+  base::debug::SetCrashKeyValue(kExceptionKey, base::SysNSStringToUTF8(value));
+
+  const char* const kExceptionTraceKey =
+      seen_first_exception ? crash_keys::mac::kLastNSExceptionTrace
+                           : crash_keys::mac::kFirstNSExceptionTrace;
+  // This exception preprocessor runs prior to the one in libobjc, which sets
+  // the -[NSException callStackReturnAddresses].
+  base::debug::SetCrashKeyToStackTrace(kExceptionTraceKey,
+                                       base::debug::StackTrace());
+
+  seen_first_exception = true;
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  // Unwind the stack looking for any exception handlers. If an exception
+  // handler is encountered, test to see if it is a function known to catch-
+  // and-rethrow as a "top-level" exception handler. Various routines in
+  // Cocoa do this, and it obscures the crashing stack, since the original
+  // throw location is no longer present on the stack (just the re-throw) when
+  // Crashpad captures the crash report.
+  unw_context_t context;
+  unw_getcontext(&context);
+
+  unw_cursor_t cursor;
+  unw_init_local(&cursor, &context);
+
+  while (unw_step(&cursor) > 0) {
+    unw_proc_info_t frame_info;
+    if (unw_get_proc_info(&cursor, &frame_info) != UNW_ESUCCESS) {
+      continue;
+    }
+
+    // This frame has an exception handler.
+    if (frame_info.handler != 0) {
+      char proc_name[64];
+      unw_word_t offset;
+      if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name),
+                            &offset) != UNW_ESUCCESS) {
+        continue;
+      }
+
+      // Check if the function is one that is known to obscure (by way of
+      // catch-and-rethrow) exception stack traces. If it is, sinkhole it
+      // by crashing here at the point of throw.
+      for (const auto& sinkhole : kExceptionSinkholes) {
+        if (strcmp(sinkhole, proc_name) == 0) {
+          TERMINATING_FROM_UNCAUGHT_NSEXCEPTION(exception);
+        }
+      }
+
+      break;
+    }
+  }
+
+  // Forward to the next preprocessor.
+  if (g_next_preprocessor)
+    return g_next_preprocessor(exception);
+
+  return exception;
+}
+
+void InstallObjcExceptionPreprocessor() {
+  if (g_next_preprocessor)
+    return;
+
+  g_next_preprocessor =
+      objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
+}
+
+void UninstallObjcExceptionPreprocessor() {
+  objc_setExceptionPreprocessor(g_next_preprocessor);
+  g_next_preprocessor = nullptr;
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/mac/exception_processor_unittest.mm b/chrome/browser/mac/exception_processor_unittest.mm
new file mode 100644
index 0000000..74c1128
--- /dev/null
+++ b/chrome/browser/mac/exception_processor_unittest.mm
@@ -0,0 +1,182 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/mac/exception_processor.h"
+
+#import <Cocoa/Cocoa.h>
+#include <stddef.h>
+#include <sys/wait.h>
+
+#include "base/mac/os_crash_dumps.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::HistogramBase;
+using base::HistogramSamples;
+using base::StatisticsRecorder;
+
+namespace chrome {
+
+// Generate an NSException with the given name.
+NSException* ExceptionNamed(NSString* name) {
+  return [NSException exceptionWithName:name
+                                 reason:@"No reason given"
+                               userInfo:nil];
+}
+
+// Helper to keep binning expectations readible.
+size_t BinForExceptionNamed(NSString* name) {
+  return BinForException(ExceptionNamed(name));
+}
+
+TEST(ExceptionProcessorTest, ExceptionBinning) {
+  // These exceptions must be in this order.
+  EXPECT_EQ(BinForExceptionNamed(NSGenericException), 0U);
+  EXPECT_EQ(BinForExceptionNamed(NSRangeException), 1U);
+  EXPECT_EQ(BinForExceptionNamed(NSInvalidArgumentException), 2U);
+  EXPECT_EQ(BinForExceptionNamed(NSMallocException), 3U);
+
+  // Random other exceptions map to |kUnknownNSException|.
+  EXPECT_EQ(BinForExceptionNamed(@"CustomName"), kUnknownNSException);
+  EXPECT_EQ(BinForExceptionNamed(@"Custom Name"), kUnknownNSException);
+  EXPECT_EQ(BinForExceptionNamed(@""), kUnknownNSException);
+  EXPECT_EQ(BinForException(nil), kUnknownNSException);
+}
+
+TEST(ExceptionProcessorTest, RecordException) {
+  // Start up a histogram recorder.
+  // TODO(rtenneti): Leaks StatisticsRecorder and will update suppressions.
+  base::StatisticsRecorder::Initialize();
+
+  StatisticsRecorder::Histograms histograms;
+  StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
+  EXPECT_EQ(0U, histograms.size());
+
+  // Record some known exceptions.
+  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+  RecordExceptionWithUma(ExceptionNamed(NSGenericException));
+  RecordExceptionWithUma(ExceptionNamed(NSRangeException));
+  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+  RecordExceptionWithUma(ExceptionNamed(NSInvalidArgumentException));
+  RecordExceptionWithUma(ExceptionNamed(NSMallocException));
+  RecordExceptionWithUma(ExceptionNamed(NSMallocException));
+
+  // Record some unknown exceptions.
+  RecordExceptionWithUma(ExceptionNamed(@"CustomName"));
+  RecordExceptionWithUma(ExceptionNamed(@"Custom Name"));
+  RecordExceptionWithUma(ExceptionNamed(@""));
+  RecordExceptionWithUma(nil);
+
+  // We should have exactly the right number of exceptions.
+  StatisticsRecorder::GetSnapshot("OSX.NSException", &histograms);
+  EXPECT_EQ(1U, histograms.size());
+  EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, histograms[0]->flags());
+
+  std::unique_ptr<HistogramSamples> samples(histograms[0]->SnapshotSamples());
+  EXPECT_EQ(4, samples->GetCount(0));
+  EXPECT_EQ(1, samples->GetCount(1));
+  EXPECT_EQ(3, samples->GetCount(2));
+  EXPECT_EQ(2, samples->GetCount(3));
+
+  // The unknown exceptions should end up in the overflow bucket.
+  EXPECT_TRUE(histograms[0]->HasConstructionArguments(1,
+                                                      kUnknownNSException,
+                                                      kUnknownNSException + 1));
+  EXPECT_EQ(4, samples->GetCount(kUnknownNSException));
+}
+
+void RaiseExceptionInRunLoop() {
+  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
+
+  CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{
+    [NSException raise:@"ThrowExceptionInRunLoop" format:nil];
+  });
+  CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{
+    CFRunLoopStop(run_loop);
+  });
+  CFRunLoopRun();
+}
+
+void ThrowExceptionInRunLoop() {
+  base::mac::DisableOSCrashDumps();
+  chrome::InstallObjcExceptionPreprocessor();
+
+  RaiseExceptionInRunLoop();
+
+  fprintf(stderr, "TEST FAILED\n");
+  exit(1);
+}
+
+// Tests that when the preprocessor is installed, exceptions thrown from
+// a runloop callout are made fatal, so that the stack trace is useful.
+TEST(ExceptionProcessorTest, ThrowExceptionInRunLoop) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  EXPECT_DEATH(ThrowExceptionInRunLoop(),
+               ".*FATAL:exception_processor\\.mm.*"
+               "Terminating from Objective-C exception:.*");
+}
+
+void ThrowAndCatchExceptionInRunLoop() {
+  base::mac::DisableOSCrashDumps();
+  chrome::InstallObjcExceptionPreprocessor();
+
+  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
+  CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{
+    @try {
+      [NSException raise:@"ObjcExceptionPreprocessCaught" format:nil];
+    } @catch (id exception) {
+    }
+  });
+
+  CFRunLoopPerformBlock(run_loop, kCFRunLoopCommonModes, ^{
+    CFRunLoopStop(run_loop);
+  });
+
+  CFRunLoopRun();
+
+  fprintf(stderr, "TEST PASS\n");
+  exit(0);
+}
+
+// Tests that exceptions can still be caught when the preprocessor is enabled.
+TEST(ExceptionProcessorTest, ThrowAndCatchExceptionInRunLoop) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  EXPECT_EXIT(ThrowAndCatchExceptionInRunLoop(),
+              [](int exit_code) -> bool {
+                return WEXITSTATUS(exit_code) == 0;
+              },
+              ".*TEST PASS.*");
+}
+
+void ThrowExceptionInRunLoopWithoutProcessor() {
+  base::mac::DisableOSCrashDumps();
+  chrome::UninstallObjcExceptionPreprocessor();
+
+  @try {
+    RaiseExceptionInRunLoop();
+  } @catch (id exception) {
+    fprintf(stderr, "TEST PASS\n");
+    exit(0);
+  }
+
+  fprintf(stderr, "TEST FAILED\n");
+  exit(1);
+}
+
+// Tests basic exception handling when the preprocessor is disabled.
+TEST(ExceptionProcessorTest, ThrowExceptionInRunLoopWithoutProcessor) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  EXPECT_EXIT(ThrowExceptionInRunLoopWithoutProcessor(),
+              [](int exit_code) -> bool {
+                return WEXITSTATUS(exit_code) == 0;
+              },
+              ".*TEST PASS.*");
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/media/router/create_presentation_connection_request.cc b/chrome/browser/media/router/create_presentation_connection_request.cc
index 5bec76d..9dd49c9 100644
--- a/chrome/browser/media/router/create_presentation_connection_request.cc
+++ b/chrome/browser/media/router/create_presentation_connection_request.cc
@@ -15,13 +15,11 @@
 
 CreatePresentationConnectionRequest::CreatePresentationConnectionRequest(
     const RenderFrameHostId& render_frame_host_id,
-    const GURL& presentation_url,
+    const std::vector<GURL>& presentation_urls,
     const GURL& frame_url,
     const PresentationSessionSuccessCallback& success_cb,
     const PresentationSessionErrorCallback& error_cb)
-    : presentation_request_(render_frame_host_id,
-                            {presentation_url},
-                            frame_url),
+    : presentation_request_(render_frame_host_id, presentation_urls, frame_url),
       success_cb_(success_cb),
       error_cb_(error_cb),
       cb_invoked_(false) {
@@ -38,12 +36,12 @@
 
 void CreatePresentationConnectionRequest::InvokeSuccessCallback(
     const std::string& presentation_id,
+    const GURL& presentation_url,
     const MediaRoute& route) {
   DCHECK(!cb_invoked_);
   if (!cb_invoked_) {
     success_cb_.Run(
-        content::PresentationSessionInfo(
-            presentation_request_.presentation_url(), presentation_id),
+        content::PresentationSessionInfo(presentation_url, presentation_id),
         route);
     cb_invoked_ = true;
   }
@@ -66,8 +64,8 @@
     presentation_request->InvokeErrorCallback(content::PresentationError(
         content::PRESENTATION_ERROR_UNKNOWN, result.error()));
   } else {
-    presentation_request->InvokeSuccessCallback(result.presentation_id(),
-                                                *result.route());
+    presentation_request->InvokeSuccessCallback(
+        result.presentation_id(), result.presentation_url(), *result.route());
   }
 }
 
diff --git a/chrome/browser/media/router/create_presentation_connection_request.h b/chrome/browser/media/router/create_presentation_connection_request.h
index 9dd9c03..41e5da49 100644
--- a/chrome/browser/media/router/create_presentation_connection_request.h
+++ b/chrome/browser/media/router/create_presentation_connection_request.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "chrome/browser/media/router/media_route.h"
@@ -46,7 +47,7 @@
   // |erorr_cb|: Callback to invoke when the request fails. Must be valid.
   CreatePresentationConnectionRequest(
       const RenderFrameHostId& render_frame_host_id,
-      const GURL& presentation_url,
+      const std::vector<GURL>& presentation_urls,
       const GURL& frame_url,
       const PresentationSessionSuccessCallback& success_cb,
       const PresentationSessionErrorCallback& error_cb);
@@ -60,6 +61,7 @@
   // These functions can only be invoked once per instance. It is an error
   // to invoke these functions more than once.
   void InvokeSuccessCallback(const std::string& presentation_id,
+                             const GURL& presentation_url,
                              const MediaRoute& route);
   void InvokeErrorCallback(const content::PresentationError& error);
 
diff --git a/chrome/browser/media/router/create_presentation_connection_request_unittest.cc b/chrome/browser/media/router/create_presentation_connection_request_unittest.cc
index 6d9fdf30..8e71ecb4 100644
--- a/chrome/browser/media/router/create_presentation_connection_request_unittest.cc
+++ b/chrome/browser/media/router/create_presentation_connection_request_unittest.cc
@@ -12,11 +12,13 @@
 
 namespace {
 
-const char kPresentationUrl[] = "http://foo.com";
-const char kFrameUrl[] = "http://google.com";
-const char kPresentationId[] = "presentationId";
-const char kRouteId[] =
-    "urn:x-org.chromium:media:route:presentationId/cast-sink1/http://foo.com";
+constexpr char kPresentationUrl1[] = "http://www.example.com/presentation.html";
+constexpr char kPresentationUrl2[] = "http://www.example.net/alternate.html";
+constexpr char kFrameUrl[] = "http://google.com";
+constexpr char kPresentationId[] = "presentationId";
+constexpr char kRouteId[] =
+    "urn:x-org.chromium:media:route:presentationId/cast-sink1/"
+    "http://www.example.com/presentation.html";
 
 }  // namespace
 
@@ -25,8 +27,8 @@
   CreatePresentationConnectionRequestTest()
       : cb_invoked_(false),
         render_frame_host_id_(1, 2),
-        presentation_url_(kPresentationUrl),
-        presentation_urls_({presentation_url_}) {}
+        presentation_url_(kPresentationUrl1),
+        presentation_urls_({presentation_url_, GURL(kPresentationUrl2)}) {}
 
   ~CreatePresentationConnectionRequestTest() override {}
 
@@ -36,6 +38,7 @@
     cb_invoked_ = true;
     EXPECT_EQ(expected_info.presentation_url, actual_info.presentation_url);
     EXPECT_EQ(expected_info.presentation_id, actual_info.presentation_id);
+    EXPECT_EQ(route.media_route_id(), kRouteId);
   }
 
   void OnError(const content::PresentationError& expected_error,
@@ -65,7 +68,7 @@
   content::PresentationError error(content::PRESENTATION_ERROR_UNKNOWN,
                                    "Unknown error.");
   CreatePresentationConnectionRequest request(
-      render_frame_host_id_, presentation_url_, GURL(kFrameUrl),
+      render_frame_host_id_, presentation_urls_, GURL(kFrameUrl),
       base::Bind(&CreatePresentationConnectionRequestTest::FailOnSuccess,
                  base::Unretained(this)),
       base::Bind(&CreatePresentationConnectionRequestTest::OnError,
@@ -82,14 +85,14 @@
   content::PresentationSessionInfo session_info(presentation_url_,
                                                 kPresentationId);
   CreatePresentationConnectionRequest request(
-      render_frame_host_id_, presentation_url_, GURL(kFrameUrl),
+      render_frame_host_id_, {presentation_url_}, GURL(kFrameUrl),
       base::Bind(&CreatePresentationConnectionRequestTest::OnSuccess,
                  base::Unretained(this), session_info),
       base::Bind(&CreatePresentationConnectionRequestTest::FailOnError,
                  base::Unretained(this)));
   MediaRoute route(kRouteId, MediaSourceForTab(1), "sinkId", "Description",
                    false, "", false);
-  request.InvokeSuccessCallback(kPresentationId, route);
+  request.InvokeSuccessCallback(kPresentationId, presentation_url_, route);
   EXPECT_TRUE(cb_invoked_);
 }
 
@@ -98,7 +101,7 @@
       content::PRESENTATION_ERROR_SESSION_REQUEST_CANCELLED,
       "This is an error message");
   CreatePresentationConnectionRequest request(
-      render_frame_host_id_, presentation_url_, GURL(kFrameUrl),
+      render_frame_host_id_, presentation_urls_, GURL(kFrameUrl),
       base::Bind(&CreatePresentationConnectionRequestTest::FailOnSuccess,
                  base::Unretained(this)),
       base::Bind(&CreatePresentationConnectionRequestTest::OnError,
diff --git a/chrome/browser/media/router/media_router_dialog_controller_unittest.cc b/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
index a302d35..9b36a4a 100644
--- a/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
+++ b/chrome/browser/media/router/media_router_dialog_controller_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <vector>
 
 #include "chrome/browser/media/router/create_presentation_connection_request.h"
 #include "chrome/browser/media/router/media_router_dialog_controller.h"
@@ -66,13 +67,15 @@
   void RequestError(const content::PresentationError& error) {}
 
   std::unique_ptr<CreatePresentationConnectionRequest> GetRequest() {
-    return base::MakeUnique<CreatePresentationConnectionRequest>(
-        RenderFrameHostId(1, 2), GURL("http://example.com"),
-        GURL("http://google.com"),
-        base::Bind(&MediaRouterDialogControllerTest::RequestSuccess,
-                   base::Unretained(this)),
-        base::Bind(&MediaRouterDialogControllerTest::RequestError,
-                   base::Unretained(this)));
+    return std::unique_ptr<CreatePresentationConnectionRequest>(
+        new CreatePresentationConnectionRequest(
+            RenderFrameHostId(1, 2),
+            {GURL("http://example.com"), GURL("http://example2.com")},
+            GURL("http://google.com"),
+            base::Bind(&MediaRouterDialogControllerTest::RequestSuccess,
+                       base::Unretained(this)),
+            base::Bind(&MediaRouterDialogControllerTest::RequestError,
+                       base::Unretained(this))));
   }
 
   std::unique_ptr<TestMediaRouterDialogController> dialog_controller_;
diff --git a/chrome/browser/media/router/media_source.cc b/chrome/browser/media/router/media_source.cc
index 359ccf1..d08ebbd 100644
--- a/chrome/browser/media/router/media_source.cc
+++ b/chrome/browser/media/router/media_source.cc
@@ -6,15 +6,13 @@
 
 #include <string>
 
-#include "url/gurl.h"
-
 namespace media_router {
 
 MediaSource::MediaSource(const MediaSource::Id& source_id) : id_(source_id) {
 }
 
 MediaSource::MediaSource(const GURL& presentation_url)
-    : id_(presentation_url.spec()) {}
+    : id_(presentation_url.spec()), url_(presentation_url) {}
 
 MediaSource::~MediaSource() {}
 
@@ -22,6 +20,10 @@
   return id_;
 }
 
+GURL MediaSource::url() const {
+  return url_;
+}
+
 bool MediaSource::operator==(const MediaSource& other) const {
   return id_ == other.id();
 }
diff --git a/chrome/browser/media/router/media_source.h b/chrome/browser/media/router/media_source.h
index 279edf0..ac5da714 100644
--- a/chrome/browser/media/router/media_source.h
+++ b/chrome/browser/media/router/media_source.h
@@ -11,8 +11,7 @@
 #include <string>
 
 #include "base/hash.h"
-
-class GURL;
+#include "url/gurl.h"
 
 // TODO(mfoltz): Right now this is a wrapper for std::string.  Factor methods
 // from media_source_helper here so this object becomes useful; and don't just
@@ -30,6 +29,10 @@
   // Gets the ID of the media source.
   MediaSource::Id id() const;
 
+  // If MediaSource is created from a URL, return the URL; otherwise return an
+  // empty GURL.
+  GURL url() const;
+
   // Returns true if two MediaSource objects use the same media ID.
   bool operator==(const MediaSource& other) const;
 
@@ -45,6 +48,7 @@
 
  private:
   MediaSource::Id id_;
+  GURL url_;
 };
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/media_source_helper.cc b/chrome/browser/media/router/media_source_helper.cc
index 2365cdd..7c618b9 100644
--- a/chrome/browser/media/router/media_source_helper.cc
+++ b/chrome/browser/media/router/media_source_helper.cc
@@ -22,7 +22,8 @@
 constexpr char kDesktopMediaUrn[] = "urn:x-org.chromium.media:source:desktop";
 constexpr char kTabRemotingUrnFormat[] =
     "urn:x-org.chromium.media:source:tab_content_remoting:%d";
-
+constexpr char kCastPresentationUrlDomain[] = "google.com";
+constexpr char kCastPresentationUrlPath[] = "/cast";
 }  // namespace
 
 MediaSource MediaSourceForTab(int tab_id) {
@@ -59,6 +60,17 @@
          IsTabMirroringMediaSource(source);
 }
 
+bool CanConnectToMediaSource(const MediaSource& source) {
+  // compare host, port, scheme, and path prefix for source.url()
+  if (!source.url().SchemeIs(url::kHttpsScheme) ||
+      !source.url().DomainIs(kCastPresentationUrlDomain) ||
+      (!source.url().has_path() ||
+       source.url().path() != kCastPresentationUrlPath))
+    return false;
+
+  return true;
+}
+
 int TabIdFromMediaSource(const MediaSource& source) {
   int tab_id;
   if (sscanf(source.id().c_str(), kTabMediaUrnFormat, &tab_id) == 1)
diff --git a/chrome/browser/media/router/media_source_helper.h b/chrome/browser/media/router/media_source_helper.h
index f9356cd10..64d6e74 100644
--- a/chrome/browser/media/router/media_source_helper.h
+++ b/chrome/browser/media/router/media_source_helper.h
@@ -29,6 +29,10 @@
 bool IsTabMirroringMediaSource(const MediaSource& source);
 bool IsMirroringMediaSource(const MediaSource& source);
 
+// Returns true if |source| is a media source type that can be connected to the
+// Presentation API by from a request initiated by the browser.
+bool CanConnectToMediaSource(const MediaSource& source);
+
 // Parses the |source| and returns the SessionTabHelper tab ID referencing a
 // source tab. Returns a non-positive value on error.
 int TabIdFromMediaSource(const MediaSource& source);
diff --git a/chrome/browser/media/router/media_source_helper_unittest.cc b/chrome/browser/media/router/media_source_helper_unittest.cc
index c556f6cc..1b708fd 100644
--- a/chrome/browser/media/router/media_source_helper_unittest.cc
+++ b/chrome/browser/media/router/media_source_helper_unittest.cc
@@ -45,5 +45,24 @@
       MediaSourceForPresentationUrl(GURL("totally not a url"))));
 }
 
+TEST(MediaSourcesTest, CanConnectToMediaSource) {
+  EXPECT_TRUE(CanConnectToMediaSource(
+      MediaSource(GURL("https://google.com/cast#__castAppId__=233637DE"))));
+  // false scheme
+  EXPECT_FALSE(CanConnectToMediaSource(
+      MediaSource(GURL("http://google.com/cast#__castAppId__=233637DE"))));
+  // false domain
+  EXPECT_FALSE(CanConnectToMediaSource(
+      MediaSource(GURL("https://google2.com/cast#__castAppId__=233637DE"))));
+  // empty path
+  EXPECT_FALSE(
+      CanConnectToMediaSource(MediaSource(GURL("https://www.google.com"))));
+  // false path
+  EXPECT_FALSE(CanConnectToMediaSource(
+      MediaSource(GURL("https://www.google.com/path"))));
+
+  EXPECT_FALSE(CanConnectToMediaSource(MediaSource(GURL(""))));
+}
+
 }  // namespace media_router
 
diff --git a/chrome/browser/media/router/media_source_unittest.cc b/chrome/browser/media/router/media_source_unittest.cc
index 470b572..a4e92148 100644
--- a/chrome/browser/media/router/media_source_unittest.cc
+++ b/chrome/browser/media/router/media_source_unittest.cc
@@ -11,6 +11,14 @@
 TEST(MediaSourceTest, Constructor) {
   MediaSource source1("urn:x-com.google.cast:application:DEADBEEF");
   EXPECT_EQ("urn:x-com.google.cast:application:DEADBEEF", source1.id());
+  EXPECT_EQ(GURL(""), source1.url());
+}
+
+TEST(MediaSourceTest, ConstructorWithGURL) {
+  GURL test_url = GURL("http://google.com");
+  MediaSource source1(test_url);
+  EXPECT_EQ(test_url.spec(), source1.id());
+  EXPECT_EQ(test_url, source1.url());
 }
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/mojo/media_router.mojom b/chrome/browser/media/router/mojo/media_router.mojom
index ea00ea56..9d9d176 100644
--- a/chrome/browser/media/router/mojo/media_router.mojom
+++ b/chrome/browser/media/router/mojo/media_router.mojom
@@ -4,7 +4,7 @@
 
 module media_router.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 
 // Represents an output sink to which media can be routed.
 struct MediaSink {
diff --git a/chrome/browser/media/router/presentation_request.h b/chrome/browser/media/router/presentation_request.h
index 56265c9ac..1879ec1 100644
--- a/chrome/browser/media/router/presentation_request.h
+++ b/chrome/browser/media/router/presentation_request.h
@@ -32,8 +32,9 @@
   const RenderFrameHostId& render_frame_host_id() const {
     return render_frame_host_id_;
   }
-  // TODO(crbug.com/627655): Use multiple URLs.
-  const GURL& presentation_url() const { return presentation_urls_[0]; }
+  const std::vector<GURL>& presentation_urls() const {
+    return presentation_urls_;
+  }
   const GURL& frame_url() const { return frame_url_; }
 
  private:
diff --git a/chrome/browser/media/router/presentation_request_unittest.cc b/chrome/browser/media/router/presentation_request_unittest.cc
index 9fe5a10a..165b5d0 100644
--- a/chrome/browser/media/router/presentation_request_unittest.cc
+++ b/chrome/browser/media/router/presentation_request_unittest.cc
@@ -9,8 +9,9 @@
 
 TEST(PresentationRequestTest, Equals) {
   GURL frame_url("http://www.site.com/");
-  std::vector<GURL> presentation_urls =
-      {GURL("http://www.example.com/presentation.html")};
+  std::vector<GURL> presentation_urls = {
+      GURL("http://www.example.com/presentation.html"),
+      GURL("http://www.example.net/alternate.html")};
 
   PresentationRequest request1(RenderFrameHostId(1, 2), presentation_urls,
                                frame_url);
@@ -23,7 +24,9 @@
   // Presentation URLs are different.
   PresentationRequest request3(
       RenderFrameHostId(1, 2),
-      {GURL("http://www.example.net/presentation.html")}, frame_url);
+      {GURL("http://www.example.net/presentation.html"),
+       GURL("http://www.example.com/presentation.html")},
+      frame_url);
   EXPECT_FALSE(request1.Equals(request3));
 
   // Frame URLs are different.
@@ -33,7 +36,8 @@
 
   PresentationRequest request5(
       RenderFrameHostId(1, 2),
-      {GURL("http://www.example.com/presentation.html")},
+      {GURL("http://www.example.com/presentation.html"),
+       GURL("http://www.example.net/alternate.html")},
       GURL("http://www.site.com/"));
   EXPECT_TRUE(request1.Equals(request5));
 }
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc
index d0d1a19..67d43a3 100644
--- a/chrome/browser/media/router/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc
@@ -366,9 +366,9 @@
   // Sets or clears the default presentation request and callback for the given
   // frame. Also sets / clears the default presentation requests for the owning
   // tab WebContents.
-  void SetDefaultPresentationUrl(
+  void SetDefaultPresentationUrls(
       const RenderFrameHostId& render_frame_host_id,
-      const GURL& default_presentation_url,
+      const std::vector<GURL>& default_presentation_urls,
       const content::PresentationSessionStartedCallback& callback);
   void AddDelegateObserver(const RenderFrameHostId& render_frame_host_id,
                            DelegateObserver* observer);
@@ -545,21 +545,20 @@
   it->second->ListenForSessionMessages(session, message_cb);
 }
 
-void PresentationFrameManager::SetDefaultPresentationUrl(
+void PresentationFrameManager::SetDefaultPresentationUrls(
     const RenderFrameHostId& render_frame_host_id,
-    const GURL& default_presentation_url,
+    const std::vector<GURL>& default_presentation_urls,
     const content::PresentationSessionStartedCallback& callback) {
   if (!IsMainFrame(render_frame_host_id))
     return;
 
-  if (default_presentation_url.is_empty()) {
+  if (default_presentation_urls.empty()) {
     ClearDefaultPresentationRequest();
   } else {
     DCHECK(!callback.is_null());
     GURL frame_url(
         GetLastCommittedURLForFrame(render_frame_host_id, web_contents_));
-    PresentationRequest request(render_frame_host_id,
-                                std::vector<GURL>({default_presentation_url}),
+    PresentationRequest request(render_frame_host_id, default_presentation_urls,
                                 frame_url);
     default_presentation_started_callback_ = callback;
     SetDefaultPresentationRequest(request);
@@ -726,14 +725,8 @@
     const std::vector<GURL>& default_presentation_urls,
     const content::PresentationSessionStartedCallback& callback) {
   RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id);
-  if (default_presentation_urls.empty()) {
-    frame_manager_->SetDefaultPresentationUrl(render_frame_host_id, GURL(),
-                                              callback);
-  } else {
-    // TODO(crbug.com/627655): Handle multiple URLs.
-    frame_manager_->SetDefaultPresentationUrl(
-        render_frame_host_id, default_presentation_urls[0], callback);
-  }
+  frame_manager_->SetDefaultPresentationUrls(
+      render_frame_host_id, default_presentation_urls, callback);
 }
 
 void PresentationServiceDelegateImpl::OnJoinRouteResponse(
@@ -790,19 +783,21 @@
     return;
   }
 
-  // TODO(crbug.com/627655): Handle multiple URLs.
-  const GURL& presentation_url = presentation_urls[0];
-  if (presentation_url.is_empty() ||
-      !IsValidPresentationUrl(presentation_url)) {
-    error_cb.Run(content::PresentationError(content::PRESENTATION_ERROR_UNKNOWN,
-                                            "Invalid presentation arguments."));
+  // TODO(crbug.com/670848): Improve handling of invalid URLs in
+  // PresentationService::start().
+  if (presentation_urls.empty() ||
+      std::find_if_not(presentation_urls.begin(), presentation_urls.end(),
+                       IsValidPresentationUrl) != presentation_urls.end()) {
+    error_cb.Run(content::PresentationError(
+        content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND,
+        "Invalid presentation URL."));
     return;
   }
 
   RenderFrameHostId render_frame_host_id(render_process_id, render_frame_id);
   std::unique_ptr<CreatePresentationConnectionRequest> request(
       new CreatePresentationConnectionRequest(
-          render_frame_host_id, presentation_url,
+          render_frame_host_id, presentation_urls,
           GetLastCommittedURLForFrame(render_frame_host_id, web_contents_),
           base::Bind(&PresentationServiceDelegateImpl::OnStartSessionSucceeded,
                      weak_factory_.GetWeakPtr(), render_process_id,
@@ -825,10 +820,12 @@
     const std::string& presentation_id,
     const content::PresentationSessionStartedCallback& success_cb,
     const content::PresentationSessionErrorCallback& error_cb) {
+  DVLOG(2) << "PresentationServiceDelegateImpl::JoinSession";
   if (presentation_urls.empty()) {
     error_cb.Run(content::PresentationError(
         content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND,
         "Invalid presentation arguments."));
+    return;
   }
 
   // TODO(crbug.com/627655): Handle multiple URLs.
@@ -928,11 +925,14 @@
 void PresentationServiceDelegateImpl::OnRouteResponse(
     const PresentationRequest& presentation_request,
     const RouteRequestResult& result) {
-  if (!result.route())
+  if (!result.route() ||
+      !base::ContainsValue(presentation_request.presentation_urls(),
+                           result.presentation_url())) {
     return;
+  }
 
-  content::PresentationSessionInfo session_info(
-      presentation_request.presentation_url(), result.presentation_id());
+  content::PresentationSessionInfo session_info(result.presentation_url(),
+                                                result.presentation_id());
   frame_manager_->OnDefaultPresentationSessionStarted(
       presentation_request, session_info, *result.route());
 }
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation_service_delegate_impl_unittest.cc
index 55c550e..2de3a32f 100644
--- a/chrome/browser/media/router/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation_service_delegate_impl_unittest.cc
@@ -31,6 +31,8 @@
 
 const char kPresentationUrl1[] = "http://foo.fakeurl.com/";
 const char kPresentationUrl2[] = "http://bar.fakeurl.com/";
+const char kPresentationUrl3[] =
+    "https://google.com/cast#__castAppId__=233637DE";
 const char kFrameUrl[] = "http://anotherframeurl.fakeurl.com/";
 
 }  // namespace
@@ -284,7 +286,7 @@
   ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
   PresentationRequest request1 =
       delegate_impl_->GetDefaultPresentationRequest();
-  EXPECT_EQ(presentation_url1_, request1.presentation_url());
+  EXPECT_EQ(presentation_url1_, request1.presentation_urls()[0]);
   EXPECT_EQ(RenderFrameHostId(render_process_id, routing_id),
             request1.render_frame_host_id());
   EXPECT_EQ(frame_url, request1.frame_url());
@@ -296,7 +298,7 @@
   ASSERT_TRUE(delegate_impl_->HasDefaultPresentationRequest());
   PresentationRequest request2 =
       delegate_impl_->GetDefaultPresentationRequest();
-  EXPECT_EQ(presentation_url2_, request2.presentation_url());
+  EXPECT_EQ(presentation_url2_, request2.presentation_urls()[0]);
   EXPECT_EQ(RenderFrameHostId(render_process_id, routing_id),
             request2.render_frame_host_id());
   EXPECT_EQ(frame_url, request2.frame_url());
@@ -385,6 +387,7 @@
       .WillOnce(SaveArg<4>(&route_response_callbacks));
 
   const std::string kPresentationId("pid");
+  presentation_urls_.push_back(GURL(kPresentationUrl3));
   MockCreatePresentationConnnectionCallbacks mock_create_connection_callbacks;
   delegate_impl_->JoinSession(
       render_process_id, routing_id, presentation_urls_, kPresentationId,
diff --git a/chrome/browser/media/router/route_request_result.cc b/chrome/browser/media/router/route_request_result.cc
index 62a257e..7f6f226 100644
--- a/chrome/browser/media/router/route_request_result.cc
+++ b/chrome/browser/media/router/route_request_result.cc
@@ -33,7 +33,10 @@
     : route_(std::move(route)),
       presentation_id_(presentation_id),
       error_(error),
-      result_code_(result_code) {}
+      result_code_(result_code) {
+  if (route_)
+    presentation_url_ = route_->media_source().url();
+}
 
 RouteRequestResult::~RouteRequestResult() = default;
 
diff --git a/chrome/browser/media/router/route_request_result.h b/chrome/browser/media/router/route_request_result.h
index 8b0deea..c953a72 100644
--- a/chrome/browser/media/router/route_request_result.h
+++ b/chrome/browser/media/router/route_request_result.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "url/gurl.h"
 
 namespace media_router {
 
@@ -62,6 +63,7 @@
   // create a copy if they wish to use it after this object is destroyed.
   const MediaRoute* route() const { return route_.get(); }
   std::string presentation_id() const { return presentation_id_; }
+  GURL presentation_url() const { return presentation_url_; }
   std::string error() const { return error_; }
   ResultCode result_code() const { return result_code_; }
 
@@ -73,6 +75,7 @@
 
   std::unique_ptr<MediaRoute> route_;
   std::string presentation_id_;
+  GURL presentation_url_;
   std::string error_;
   ResultCode result_code_;
 
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index e5376dd8..d232889 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -128,7 +128,7 @@
     const std::string& json_array) {
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json_array);
   EXPECT_TRUE(value);
-  EXPECT_TRUE(value->IsType(base::Value::TYPE_LIST));
+  EXPECT_TRUE(value->IsType(base::Value::Type::LIST));
   std::unique_ptr<base::ListValue> list =
       base::ListValue::From(std::move(value));
   std::vector<std::string> vector;
@@ -136,7 +136,7 @@
   for (size_t i = 0; i < list->GetSize(); ++i) {
     base::Value* item;
     EXPECT_TRUE(list->Get(i, &item));
-    EXPECT_TRUE(item->IsType(base::Value::TYPE_STRING));
+    EXPECT_TRUE(item->IsType(base::Value::Type::STRING));
     std::string item_str;
     EXPECT_TRUE(item->GetAsString(&item_str));
     vector.push_back(std::move(item_str));
diff --git a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
index 48492e6..76630fa 100644
--- a/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getmediadevices_browsertest.cc
@@ -75,7 +75,7 @@
         &error_message);
 
     ASSERT_TRUE(value.get() != NULL) << error_message;
-    EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);
+    EXPECT_EQ(value->GetType(), base::Value::Type::LIST);
 
     base::ListValue* values;
     ASSERT_TRUE(value->GetAsList(&values));
diff --git a/chrome/browser/media/webrtc/webrtc_perf_browsertest.cc b/chrome/browser/media/webrtc/webrtc_perf_browsertest.cc
deleted file mode 100644
index 6ed799b..0000000
--- a/chrome/browser/media/webrtc/webrtc_perf_browsertest.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/macros.h"
-#include "base/strings/string_split.h"
-#include "base/test/test_timeouts.h"
-#include "base/time/time.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
-#include "chrome/browser/media/webrtc/webrtc_browsertest_common.h"
-#include "chrome/browser/media/webrtc/webrtc_browsertest_perf.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/feature_h264_with_openh264_ffmpeg.h"
-#include "content/public/common/features.h"
-#include "content/public/test/browser_test_utils.h"
-#include "media/base/media_switches.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/perf/perf_test.h"
-
-static const char kMainWebrtcTestHtmlPage[] =
-    "/webrtc/webrtc_jsep01_test.html";
-
-std::string MakePerfTestLabel(std::string base, bool opus_dtx) {
-  if (opus_dtx) {
-    return base + "_with_opus_dtx";
-  }
-  return base;
-}
-
-// Performance browsertest for WebRTC. This test is manual since it takes long
-// to execute and requires the reference files provided by the webrtc.DEPS
-// solution (which is only available on WebRTC internal bots).
-class WebRtcInternalsPerfBrowserTest : public WebRtcTestBase {
- public:
-  void SetUpInProcessBrowserTestFixture() override {
-    DetectErrorsInJavaScript();  // Look for errors in our rather complex js.
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Ensure the infobar is enabled, since we expect that in this test.
-    EXPECT_FALSE(command_line->HasSwitch(switches::kUseFakeUIForMediaStream));
-
-    // Play a suitable, somewhat realistic video file.
-    base::FilePath input_video = test::GetReferenceFilesDir()
-        .Append(test::kReferenceFileName360p)
-        .AddExtension(test::kY4mFileExtension);
-    command_line->AppendSwitchPath(switches::kUseFileForFakeVideoCapture,
-                                   input_video);
-    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
-  }
-
-  // Tries to extract data from peerConnectionDataStore in the webrtc-internals
-  // tab. The caller owns the parsed data. Returns NULL on failure.
-  base::DictionaryValue* GetWebrtcInternalsData(
-      content::WebContents* webrtc_internals_tab) {
-    std::string all_stats_json = ExecuteJavascript(
-        "window.domAutomationController.send("
-        "    JSON.stringify(peerConnectionDataStore));",
-        webrtc_internals_tab);
-
-    std::unique_ptr<base::Value> parsed_json =
-        base::JSONReader::Read(all_stats_json);
-    base::DictionaryValue* result;
-    if (parsed_json.get() && parsed_json->GetAsDictionary(&result)) {
-      ignore_result(parsed_json.release());
-      return result;
-    }
-
-    return NULL;
-  }
-
-  const base::DictionaryValue* GetDataOnPeerConnection(
-      const base::DictionaryValue* all_data,
-      int peer_connection_index) {
-    base::DictionaryValue::Iterator iterator(*all_data);
-
-    for (int i = 0; i < peer_connection_index && !iterator.IsAtEnd();
-        --peer_connection_index) {
-      iterator.Advance();
-    }
-
-    const base::DictionaryValue* result;
-    if (!iterator.IsAtEnd() && iterator.value().GetAsDictionary(&result))
-      return result;
-
-    return NULL;
-  }
-
-  std::unique_ptr<base::DictionaryValue> MeasureWebRtcInternalsData(
-      int duration_msec) {
-    chrome::AddTabAt(browser(), GURL(), -1, true);
-    ui_test_utils::NavigateToURL(browser(), GURL("chrome://webrtc-internals"));
-    content::WebContents* webrtc_internals_tab =
-        browser()->tab_strip_model()->GetActiveWebContents();
-
-    test::SleepInJavascript(webrtc_internals_tab, duration_msec);
-
-    return std::unique_ptr<base::DictionaryValue>(
-        GetWebrtcInternalsData(webrtc_internals_tab));
-  }
-
-  void RunsAudioVideoCall60SecsAndLogsInternalMetrics(
-      const std::string& video_codec) {
-    ASSERT_TRUE(test::HasReferenceFilesInCheckout());
-    ASSERT_TRUE(embedded_test_server()->Start());
-
-    ASSERT_GE(TestTimeouts::action_max_timeout().InSeconds(), 100)
-        << "This is a long-running test; you must specify "
-           "--ui-test-action-max-timeout to have a value of at least 100000.";
-
-    content::WebContents* left_tab =
-        OpenTestPageAndGetUserMediaInNewTab(kMainWebrtcTestHtmlPage);
-    content::WebContents* right_tab =
-        OpenTestPageAndGetUserMediaInNewTab(kMainWebrtcTestHtmlPage);
-
-    SetupPeerconnectionWithLocalStream(left_tab);
-    SetupPeerconnectionWithLocalStream(right_tab);
-
-    if (!video_codec.empty()) {
-      SetDefaultVideoCodec(left_tab, video_codec);
-      SetDefaultVideoCodec(right_tab, video_codec);
-    }
-    NegotiateCall(left_tab, right_tab);
-
-    StartDetectingVideo(left_tab, "remote-view");
-    StartDetectingVideo(right_tab, "remote-view");
-
-    WaitForVideoToPlay(left_tab);
-    WaitForVideoToPlay(right_tab);
-
-    // Let values stabilize, bandwidth ramp up, etc.
-    test::SleepInJavascript(left_tab, 60000);
-
-    // Start measurements.
-    std::unique_ptr<base::DictionaryValue> all_data =
-        MeasureWebRtcInternalsData(10000);
-    ASSERT_TRUE(all_data.get() != NULL);
-
-    const base::DictionaryValue* first_pc_dict =
-        GetDataOnPeerConnection(all_data.get(), 0);
-    ASSERT_TRUE(first_pc_dict != NULL);
-    test::PrintBweForVideoMetrics(*first_pc_dict, "", video_codec);
-    test::PrintMetricsForAllStreams(*first_pc_dict, "", video_codec);
-
-    HangUp(left_tab);
-    HangUp(right_tab);
-  }
-
-  void RunsOneWayCall60SecsAndLogsInternalMetrics(
-      const std::string& video_codec,
-      bool opus_dtx) {
-    ASSERT_TRUE(test::HasReferenceFilesInCheckout());
-    ASSERT_TRUE(embedded_test_server()->Start());
-
-    ASSERT_GE(TestTimeouts::action_max_timeout().InSeconds(), 100)
-        << "This is a long-running test; you must specify "
-           "--ui-test-action-max-timeout to have a value of at least 100000.";
-
-    content::WebContents* left_tab =
-        OpenTestPageAndGetUserMediaInNewTab(kMainWebrtcTestHtmlPage);
-    content::WebContents* right_tab =
-        OpenTestPageAndGetUserMediaInNewTab(kMainWebrtcTestHtmlPage);
-
-    SetupPeerconnectionWithLocalStream(left_tab);
-    SetupPeerconnectionWithoutLocalStream(right_tab);
-
-    if (!video_codec.empty()) {
-      SetDefaultVideoCodec(left_tab, video_codec);
-      SetDefaultVideoCodec(right_tab, video_codec);
-    }
-    if (opus_dtx) {
-      EnableOpusDtx(left_tab);
-      EnableOpusDtx(right_tab);
-    }
-    NegotiateCall(left_tab, right_tab);
-
-    // Remote video will only play in one tab since the call is one-way.
-    StartDetectingVideo(right_tab, "remote-view");
-    WaitForVideoToPlay(right_tab);
-
-    // Let values stabilize, bandwidth ramp up, etc.
-    test::SleepInJavascript(left_tab, 60000);
-
-    std::unique_ptr<base::DictionaryValue> all_data =
-        MeasureWebRtcInternalsData(10000);
-    ASSERT_TRUE(all_data.get() != NULL);
-
-    // This assumes the sending peer connection is always listed first in the
-    // data store, and the receiving second.
-    const base::DictionaryValue* first_pc_dict =
-        GetDataOnPeerConnection(all_data.get(), 0);
-    ASSERT_TRUE(first_pc_dict != NULL);
-    test::PrintBweForVideoMetrics(
-        *first_pc_dict, MakePerfTestLabel("_sendonly", opus_dtx), video_codec);
-    test::PrintMetricsForSendStreams(
-        *first_pc_dict, MakePerfTestLabel("_sendonly", opus_dtx), video_codec);
-
-    const base::DictionaryValue* second_pc_dict =
-        GetDataOnPeerConnection(all_data.get(), 1);
-    ASSERT_TRUE(second_pc_dict != NULL);
-    test::PrintBweForVideoMetrics(
-        *second_pc_dict, MakePerfTestLabel("_recvonly", opus_dtx), video_codec);
-    test::PrintMetricsForRecvStreams(
-        *second_pc_dict, MakePerfTestLabel("_recvonly", opus_dtx), video_codec);
-
-    HangUp(left_tab);
-    HangUp(right_tab);
-  }
-};
-
-// This is manual for its long execution time.
-
-IN_PROC_BROWSER_TEST_F(
-    WebRtcInternalsPerfBrowserTest,
-    MANUAL_RunsAudioVideoCall60SecsAndLogsInternalMetricsVp8) {
-  RunsAudioVideoCall60SecsAndLogsInternalMetrics("VP8");
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebRtcInternalsPerfBrowserTest,
-    MANUAL_RunsAudioVideoCall60SecsAndLogsInternalMetricsVp9) {
-  RunsAudioVideoCall60SecsAndLogsInternalMetrics("VP9");
-}
-
-#if BUILDFLAG(RTC_USE_H264)
-
-IN_PROC_BROWSER_TEST_F(
-    WebRtcInternalsPerfBrowserTest,
-    MANUAL_RunsAudioVideoCall60SecsAndLogsInternalMetricsH264) {
-  // Only run test if run-time feature corresponding to |rtc_use_h264| is on.
-  if (!base::FeatureList::IsEnabled(content::kWebRtcH264WithOpenH264FFmpeg)) {
-    LOG(WARNING) << "Run-time feature WebRTC-H264WithOpenH264FFmpeg disabled. "
-        "Skipping WebRtcInternalsPerfBrowserTest."
-        "MANUAL_RunsAudioVideoCall60SecsAndLogsInternalMetricsH264 (test "
-        "\"OK\")";
-    return;
-  }
-  RunsAudioVideoCall60SecsAndLogsInternalMetrics("H264");
-}
-
-#endif  // BUILDFLAG(RTC_USE_H264)
-
-IN_PROC_BROWSER_TEST_F(
-    WebRtcInternalsPerfBrowserTest,
-    MANUAL_RunsOneWayCall60SecsAndLogsInternalMetricsDefault) {
-  RunsOneWayCall60SecsAndLogsInternalMetrics("", false);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    WebRtcInternalsPerfBrowserTest,
-    MANUAL_RunsOneWayCall60SecsAndLogsInternalMetricsWithOpusDtx) {
-  RunsOneWayCall60SecsAndLogsInternalMetrics("", true);
-}
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 3280f6a..98a9764 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/google/google_brand.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
-#include "chrome/browser/metrics/https_engagement_metrics_provider.h"
 #include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/metrics/sampling_metrics_provider.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
@@ -692,10 +691,6 @@
       std::unique_ptr<metrics::MetricsProvider>(
           new syncer::DeviceCountMetricsProvider(base::Bind(
               &browser_sync::ChromeSyncClient::GetDeviceInfoTrackers))));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new HttpsEngagementMetricsProvider()));
 }
 
 bool ChromeMetricsServiceClient::ShouldIncludeProfilerDataInLog() {
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index c8163dc..82a5a10a 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -20,7 +20,7 @@
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/service/variations_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
@@ -188,10 +188,10 @@
   return true;
 }
 
-std::unique_ptr<rappor::RapporService>
-ChromeMetricsServicesManagerClient::CreateRapporService() {
+std::unique_ptr<rappor::RapporServiceImpl>
+ChromeMetricsServicesManagerClient::CreateRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return base::MakeUnique<rappor::RapporService>(
+  return base::MakeUnique<rappor::RapporServiceImpl>(
       local_state_, base::Bind(&chrome::IsIncognitoSessionActive));
 }
 
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.h b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
index fe3863f..eea9eeb 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.h
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
@@ -60,7 +60,7 @@
   class ChromeEnabledStateProvider;
 
   // metrics_services_manager::MetricsServicesManagerClient:
-  std::unique_ptr<rappor::RapporService> CreateRapporService() override;
+  std::unique_ptr<rappor::RapporServiceImpl> CreateRapporServiceImpl() override;
   std::unique_ptr<variations::VariationsService> CreateVariationsService()
       override;
   std::unique_ptr<metrics::MetricsServiceClient> CreateMetricsServiceClient()
diff --git a/chrome/browser/net/chrome_extensions_network_delegate.cc b/chrome/browser/net/chrome_extensions_network_delegate.cc
index 86161ddd..d08dcec6 100644
--- a/chrome/browser/net/chrome_extensions_network_delegate.cc
+++ b/chrome/browser/net/chrome_extensions_network_delegate.cc
@@ -173,7 +173,7 @@
     GURL* new_url) {
   const content::ResourceRequestInfo* info =
       content::ResourceRequestInfo::ForRequest(request);
-  GURL url(request->url());
+  const GURL& url(request->url());
 
   // Block top-level navigations to blob: or filesystem: URLs with extension
   // origin from non-extension processes.  See https://crbug.com/645028.
@@ -188,37 +188,39 @@
   bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob();
   bool is_navigation =
       info && content::IsResourceTypeFrame(info->GetResourceType());
-  url::Origin origin(url);
-  if (is_nested_url && is_navigation && info->IsMainFrame() &&
-      origin.scheme() == extensions::kExtensionScheme &&
-      !extension_info_map_->process_map().Contains(info->GetChildID()) &&
-      !content::IsBrowserSideNavigationEnabled()) {
-    // Relax this restriction for apps that use <webview>.  See
-    // https://crbug.com/652077.
-    const extensions::Extension* extension =
-        extension_info_map_->extensions().GetByID(origin.host());
-    bool has_webview_permission =
-        extension &&
-        extension->permissions_data()->HasAPIPermission(
-            extensions::APIPermission::kWebView);
-    // Check whether the request is coming from a <webview> guest process via
-    // ChildProcessSecurityPolicy.  A guest process should have already been
-    // granted permission to request |origin| when its WebContents was created.
-    // See https://crbug.com/656752.
-    auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
-    bool from_guest =
-        policy->HasSpecificPermissionForOrigin(info->GetChildID(), origin);
-    if (!has_webview_permission || !from_guest) {
-      // TODO(alexmos): Temporary instrumentation to find any regressions for
-      // this blocking.  Remove after verifying that this is not breaking any
-      // legitimate use cases.
-      char origin_copy[256];
-      base::strlcpy(origin_copy, origin.Serialize().c_str(),
-                    arraysize(origin_copy));
-      base::debug::Alias(&origin_copy);
-      base::debug::Alias(&from_guest);
-      base::debug::DumpWithoutCrashing();
-      return net::ERR_ABORTED;
+  if (is_nested_url && is_navigation && info->IsMainFrame()) {
+    // Nested conditional so we don't always pay the GURL -> Origin conversion.
+    url::Origin origin = url::Origin(url);
+    if (origin.scheme() == extensions::kExtensionScheme &&
+        !extension_info_map_->process_map().Contains(info->GetChildID()) &&
+        !content::IsBrowserSideNavigationEnabled()) {
+      // Relax this restriction for apps that use <webview>.  See
+      // https://crbug.com/652077.
+      const extensions::Extension* extension =
+          extension_info_map_->extensions().GetByID(origin.host());
+      bool has_webview_permission =
+          extension &&
+          extension->permissions_data()->HasAPIPermission(
+              extensions::APIPermission::kWebView);
+      // Check whether the request is coming from a <webview> guest process via
+      // ChildProcessSecurityPolicy.  A guest process should have already been
+      // granted permission to request |origin| when its WebContents was
+      // created. See https://crbug.com/656752.
+      auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
+      bool from_guest =
+          policy->HasSpecificPermissionForOrigin(info->GetChildID(), origin);
+      if (!has_webview_permission || !from_guest) {
+        // TODO(alexmos): Temporary instrumentation to find any regressions for
+        // this blocking.  Remove after verifying that this is not breaking any
+        // legitimate use cases.
+        char origin_copy[256];
+        base::strlcpy(origin_copy, origin.Serialize().c_str(),
+                      arraysize(origin_copy));
+        base::debug::Alias(&origin_copy);
+        base::debug::Alias(&from_guest);
+        base::debug::DumpWithoutCrashing();
+        return net::ERR_ABORTED;
+      }
     }
   }
 
diff --git a/chrome/browser/net/disk_cache_dir_policy_handler.cc b/chrome/browser/net/disk_cache_dir_policy_handler.cc
index 498ce9e..ec3402f12 100644
--- a/chrome/browser/net/disk_cache_dir_policy_handler.cc
+++ b/chrome/browser/net/disk_cache_dir_policy_handler.cc
@@ -16,7 +16,8 @@
 namespace policy {
 
 DiskCacheDirPolicyHandler::DiskCacheDirPolicyHandler()
-    : TypeCheckingPolicyHandler(key::kDiskCacheDir, base::Value::TYPE_STRING) {}
+    : TypeCheckingPolicyHandler(key::kDiskCacheDir, base::Value::Type::STRING) {
+}
 
 DiskCacheDirPolicyHandler::~DiskCacheDirPolicyHandler() {}
 
diff --git a/chrome/browser/net/net_error_tab_helper.cc b/chrome/browser/net/net_error_tab_helper.cc
index 8015b77..c3c977d 100644
--- a/chrome/browser/net/net_error_tab_helper.cc
+++ b/chrome/browser/net/net_error_tab_helper.cc
@@ -29,8 +29,8 @@
 #if BUILDFLAG(ANDROID_JAVA_UI)
 #include "base/guid.h"
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #endif  // BUILDFLAG(ANDROID_JAVA_UI)
 
 using content::BrowserContext;
diff --git a/chrome/browser/net/referrer.cc b/chrome/browser/net/referrer.cc
index b72b6f2..e73057f 100644
--- a/chrome/browser/net/referrer.cc
+++ b/chrome/browser/net/referrer.cc
@@ -109,7 +109,7 @@
 }
 
 void Referrer::Deserialize(const base::Value& value) {
-  if (value.GetType() != base::Value::TYPE_LIST)
+  if (value.GetType() != base::Value::Type::LIST)
     return;
   const base::ListValue* subresource_list(
       static_cast<const base::ListValue*>(&value));
diff --git a/chrome/browser/notifications/fullscreen_notification_blocker.cc b/chrome/browser/notifications/fullscreen_notification_blocker.cc
index b555a3d..feb4122 100644
--- a/chrome/browser/notifications/fullscreen_notification_blocker.cc
+++ b/chrome/browser/notifications/fullscreen_notification_blocker.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/fullscreen.h"
 #include "content/public/browser/notification_service.h"
+#include "ui/display/types/display_constants.h"
 #include "ui/message_center/notifier_settings.h"
 
 #if defined(USE_ASH)
@@ -45,8 +46,8 @@
         ->hide_shelf_when_fullscreen();
   }
 #endif
-
-  return IsFullScreenMode();
+  // Fullscreen is global state on platforms other than chromeos.
+  return IsFullScreenMode(display::kInvalidDisplayId);
 }
 
 }  // namespace
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
index 2b623d6..90fd846 100644
--- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
+++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -59,7 +59,7 @@
 #include "chrome/browser/ntp_snippets/download_suggestions_provider.h"
 #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h"
 #include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "components/physical_web/data_source/physical_web_data_source.h"
 
 using content::DownloadManager;
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider.cc b/chrome/browser/ntp_snippets/download_suggestions_provider.cc
index 9e1f110..1189dd79 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider.cc
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider.cc
@@ -22,7 +22,7 @@
 #include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/pref_names.h"
 #include "components/ntp_snippets/pref_util.h"
-#include "components/offline_pages/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -306,8 +306,10 @@
   AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true);
 }
 
-void DownloadSuggestionsProvider::OfflinePageModelChanged(
-    offline_pages::OfflinePageModel* model) {
+void DownloadSuggestionsProvider::OfflinePageAdded(
+    offline_pages::OfflinePageModel* model,
+    const offline_pages::OfflinePageItem& added_page) {
+  // TODO(dewittj, vitaliii): Don't refetch everything when this is called.
   DCHECK_EQ(offline_page_model_, model);
   AsynchronouslyFetchOfflinePagesDownloads(/*notify=*/true);
 }
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider.h b/chrome/browser/ntp_snippets/download_suggestions_provider.h
index 5696d03..9b7cdb2 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider.h
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider.h
@@ -18,7 +18,7 @@
 #include "components/ntp_snippets/category_status.h"
 #include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/content_suggestions_provider.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
 #include "content/public/browser/download_manager.h"
 
 class PrefRegistrySimple;
@@ -84,7 +84,9 @@
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
-  void OfflinePageModelChanged(offline_pages::OfflinePageModel* model) override;
+  void OfflinePageAdded(
+      offline_pages::OfflinePageModel* model,
+      const offline_pages::OfflinePageItem& added_page) override;
   void OfflinePageDeleted(int64_t offline_id,
                           const offline_pages::ClientId& client_id) override;
 
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
index 37da5ad..c68d1d3c 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
@@ -14,7 +14,7 @@
 #include "components/ntp_snippets/category_factory.h"
 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
 #include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h"
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/mock_download_item.h"
 #include "content/public/test/mock_download_manager.h"
@@ -276,9 +276,10 @@
     provider_->OfflinePageModelLoaded(&offline_pages_model_);
   }
 
-  void FireOfflinePageModelChanged() {
+  void AddOfflinePage(const offline_pages::OfflinePageItem& added_page) {
     DCHECK(provider_);
-    provider_->OfflinePageModelChanged(&offline_pages_model_);
+    offline_pages_model_.mutable_items()->push_back(added_page);
+    provider_->OfflinePageAdded(&offline_pages_model_, added_page);
   }
 
   void FireOfflinePageDeleted(const OfflinePageItem& item) {
@@ -525,8 +526,9 @@
       *observer(),
       OnNewSuggestions(_, downloads_category(),
                        UnorderedElementsAre(HasUrl("http://dummy.com/2"),
+                                            HasUrl("http://dummy.com/3"),
                                             HasUrl("http://download.com/2"))));
-  FireOfflinePageModelChanged();
+  AddOfflinePage(CreateDummyOfflinePage(3));
 }
 
 TEST_F(DownloadSuggestionsProviderTest, ShouldReturnDismissedSuggestions) {
@@ -617,9 +619,10 @@
       *observer(),
       OnNewSuggestions(_, downloads_category(),
                        UnorderedElementsAre(HasUrl("http://dummy.com/2"),
+                                            HasUrl("http://dummy.com/3"),
                                             HasUrl("http://download.com/1"),
                                             HasUrl("http://download.com/2"))));
-  FireOfflinePageModelChanged();
+  AddOfflinePage(CreateDummyOfflinePage(3));
 }
 
 TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceDismissedItemWithNewData) {
@@ -645,18 +648,20 @@
 
   provider()->DismissSuggestion(
       GetDummySuggestionId(1, /*is_offline_page=*/false));
+  provider()->DismissSuggestion(
+      GetDummySuggestionId(2, /*is_offline_page=*/false));
 
   // The provider is not notified about the 6th item, however, it must report
   // it now.
   EXPECT_CALL(
       *observer(),
       OnNewSuggestions(_, downloads_category(),
-                       UnorderedElementsAre(HasUrl("http://download.com/2"),
+                       UnorderedElementsAre(HasUrl("http://dummy.com/1"),
                                             HasUrl("http://download.com/3"),
                                             HasUrl("http://download.com/4"),
                                             HasUrl("http://download.com/5"),
                                             HasUrl("http://download.com/6"))));
-  FireOfflinePageModelChanged();
+  AddOfflinePage(CreateDummyOfflinePage(1));
 }
 
 TEST_F(DownloadSuggestionsProviderTest,
@@ -742,20 +747,21 @@
   EXPECT_CALL(
       *observer(),
       OnNewSuggestions(_, downloads_category(),
-                       UnorderedElementsAre(HasUrl("http://download.com/1"),
+                       UnorderedElementsAre(HasUrl("http://dummy.com/6"),
+                                            HasUrl("http://download.com/1"),
                                             HasUrl("http://download.com/2"),
                                             HasUrl("http://download.com/3"),
-                                            HasUrl("http://download.com/4"),
-                                            HasUrl("http://download.com/5"))));
-  FireOfflinePageModelChanged();
+                                            HasUrl("http://download.com/4"))));
+  AddOfflinePage(CreateDummyOfflinePage(6));
 }
 
 TEST_F(DownloadSuggestionsProviderTest, ShouldPruneOfflinePagesDismissedIDs) {
   IgnoreOnCategoryStatusChangedToAvailable();
   IgnoreOnSuggestionInvalidated();
 
-  *(offline_pages_model()->mutable_items()) =
-      CreateDummyOfflinePages({1, 2, 3});
+  auto offline_pages = CreateDummyOfflinePages({1, 2, 3});
+
+  *(offline_pages_model()->mutable_items()) = offline_pages;
   EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(),
                                             UnorderedElementsAre(
                                                 HasUrl("http://dummy.com/1"),
@@ -771,18 +777,9 @@
       GetDummySuggestionId(3, /*is_offline_page=*/true));
   EXPECT_THAT(GetDismissedSuggestions(), SizeIs(3));
 
-  // Prune on getting all offline pages.
-  EXPECT_CALL(*observer(),
-              OnNewSuggestions(_, downloads_category(), IsEmpty()));
-
-  *(offline_pages_model()->mutable_items()) =
-      CreateDummyOfflinePages({2, 3});
-  FireOfflinePageModelChanged();
-
-  // The first suggestion is added back to the |offline_pages_model| storage,
-  // because otherwise |GetDismissedSuggestions| cannot return it.
-  *(offline_pages_model()->mutable_items()) =
-      CreateDummyOfflinePages({1, 2, 3});
+  // Note that the first suggestion is not removed from |offline_pages_model|
+  // storage, because otherwise |GetDismissedSuggestions| cannot return it.
+  FireOfflinePageDeleted(offline_pages[0]);
   EXPECT_THAT(GetDismissedSuggestions(), SizeIs(2));
 
   // Prune when offline page is deleted.
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index adcc1f1..571ef10 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -37,6 +37,23 @@
 
 namespace page_load_metrics {
 
+namespace {
+
+UserInitiatedInfo CreateUserInitiatedInfo(
+    content::NavigationHandle* navigation_handle,
+    PageLoadTracker* committed_load) {
+  if (!navigation_handle->IsRendererInitiated())
+    return UserInitiatedInfo::BrowserInitiated();
+
+  return UserInitiatedInfo::RenderInitiated(
+      navigation_handle->HasUserGesture(),
+      committed_load &&
+          committed_load->input_tracker()->FindAndConsumeInputEventsBefore(
+              navigation_handle->NavigationStart()));
+}
+
+}  // namespace
+
 // static
 MetricsWebContentsObserver::MetricsWebContentsObserver(
     content::WebContents* web_contents,
@@ -64,7 +81,7 @@
 
 MetricsWebContentsObserver::~MetricsWebContentsObserver() {
   // TODO(csharrison): Use a more user-initiated signal for CLOSE.
-  NotifyAbortAllLoads(ABORT_CLOSE, false);
+  NotifyAbortAllLoads(ABORT_CLOSE, UserInitiatedInfo::NotUserInitiated());
 }
 
 void MetricsWebContentsObserver::RegisterInputEventObserver(
@@ -107,8 +124,11 @@
   if (!navigation_handle->IsInMainFrame())
     return;
 
+  UserInitiatedInfo user_initiated_info(
+      CreateUserInitiatedInfo(navigation_handle, committed_load_.get()));
   std::unique_ptr<PageLoadTracker> last_aborted =
-      NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle);
+      NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle,
+                                                 user_initiated_info);
 
   int chain_size_same_url = 0;
   int chain_size = 0;
@@ -151,7 +171,8 @@
       navigation_handle,
       base::MakeUnique<PageLoadTracker>(
           in_foreground_, embedder_interface_.get(), currently_committed_url,
-          navigation_handle, chain_size, chain_size_same_url)));
+          navigation_handle, user_initiated_info, chain_size,
+          chain_size_same_url)));
 }
 
 void MetricsWebContentsObserver::OnRequestComplete(
@@ -209,13 +230,17 @@
     finished_nav->StopTracking();
 
   if (navigation_handle->HasCommitted()) {
+    UserInitiatedInfo user_initiated_info =
+        finished_nav
+            ? finished_nav->user_initiated_info()
+            : CreateUserInitiatedInfo(navigation_handle, committed_load_.get());
+
     // Notify other loads that they may have been aborted by this committed
     // load. is_certainly_browser_timestamp is set to false because
     // NavigationStart() could be set in either the renderer or browser process.
     NotifyAbortAllLoadsWithTimestamp(
         AbortTypeForPageTransition(navigation_handle->GetPageTransition()),
-        IsNavigationUserInitiated(navigation_handle),
-        navigation_handle->NavigationStart(), false);
+        user_initiated_info, navigation_handle->NavigationStart(), false);
 
     if (should_track) {
       HandleCommittedNavigationForTrackedLoad(navigation_handle,
@@ -245,7 +270,8 @@
   // net::ERR_ABORTED: An aborted provisional load has error
   // net::ERR_ABORTED.
   if ((error == net::OK) || (error == net::ERR_ABORTED)) {
-    tracker->NotifyAbort(ABORT_OTHER, false, base::TimeTicks::Now(), true);
+    tracker->NotifyAbort(ABORT_OTHER, UserInitiatedInfo::NotUserInitiated(),
+                         base::TimeTicks::Now(), true);
     aborted_provisional_loads_.push_back(std::move(tracker));
   }
 }
@@ -256,8 +282,11 @@
   if (!IsNavigationUserInitiated(navigation_handle) &&
       (navigation_handle->GetPageTransition() &
        ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0 &&
-      committed_load_)
+      committed_load_) {
+    // TODO(bmcquade): consider carrying the user_gesture bit forward to the
+    // redirected navigation.
     committed_load_->NotifyClientRedirectTo(*tracker);
+  }
 
   committed_load_ = std::move(tracker);
   committed_load_->Commit(navigation_handle);
@@ -265,7 +294,7 @@
 
 void MetricsWebContentsObserver::NavigationStopped() {
   // TODO(csharrison): Use a more user-initiated signal for STOP.
-  NotifyAbortAllLoads(ABORT_STOP, false);
+  NotifyAbortAllLoads(ABORT_STOP, UserInitiatedInfo::NotUserInitiated());
 }
 
 void MetricsWebContentsObserver::OnInputEvent(
@@ -347,28 +376,29 @@
   aborted_provisional_loads_.clear();
 }
 
-void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type,
-                                                     bool user_initiated) {
-  NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated,
+void MetricsWebContentsObserver::NotifyAbortAllLoads(
+    UserAbortType abort_type,
+    UserInitiatedInfo user_initiated_info) {
+  NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated_info,
                                    base::TimeTicks::Now(), true);
 }
 
 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
     UserAbortType abort_type,
-    bool user_initiated,
+    UserInitiatedInfo user_initiated_info,
     base::TimeTicks timestamp,
     bool is_certainly_browser_timestamp) {
   if (committed_load_) {
-    committed_load_->NotifyAbort(abort_type, user_initiated, timestamp,
+    committed_load_->NotifyAbort(abort_type, user_initiated_info, timestamp,
                                  is_certainly_browser_timestamp);
   }
   for (const auto& kv : provisional_loads_) {
-    kv.second->NotifyAbort(abort_type, user_initiated, timestamp,
+    kv.second->NotifyAbort(abort_type, user_initiated_info, timestamp,
                            is_certainly_browser_timestamp);
   }
   for (const auto& tracker : aborted_provisional_loads_) {
     if (tracker->IsLikelyProvisionalAbort(timestamp)) {
-      tracker->UpdateAbort(abort_type, user_initiated, timestamp,
+      tracker->UpdateAbort(abort_type, user_initiated_info, timestamp,
                            is_certainly_browser_timestamp);
     }
   }
@@ -377,7 +407,8 @@
 
 std::unique_ptr<PageLoadTracker>
 MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation(
-    content::NavigationHandle* new_navigation) {
+    content::NavigationHandle* new_navigation,
+    UserInitiatedInfo user_initiated_info) {
   // If there are multiple aborted loads that can be attributed to this one,
   // just count the latest one for simplicity. Other loads will fall into the
   // OTHER bucket, though there shouldn't be very many.
@@ -394,7 +425,7 @@
   if (last_aborted_load->IsLikelyProvisionalAbort(timestamp)) {
     last_aborted_load->UpdateAbort(
         AbortTypeForPageTransition(new_navigation->GetPageTransition()),
-        IsNavigationUserInitiated(new_navigation), timestamp, false);
+        user_initiated_info, timestamp, false);
   }
 
   aborted_provisional_loads_.clear();
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 5ecd66c..02d4a80 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -96,9 +96,10 @@
 
   // Notify all loads, provisional and committed, that we performed an action
   // that might abort them.
-  void NotifyAbortAllLoads(UserAbortType abort_type, bool user_initiated);
+  void NotifyAbortAllLoads(UserAbortType abort_type,
+                           UserInitiatedInfo user_initiated_info);
   void NotifyAbortAllLoadsWithTimestamp(UserAbortType abort_type,
-                                        bool user_initiated,
+                                        UserInitiatedInfo user_initiated_info,
                                         base::TimeTicks timestamp,
                                         bool is_certainly_browser_timestamp);
 
@@ -111,7 +112,8 @@
   // loads. This method returns the provisional load that was likely aborted
   // by this navigation, to help instantiate the new PageLoadTracker.
   std::unique_ptr<PageLoadTracker> NotifyAbortedProvisionalLoadsNewNavigation(
-      content::NavigationHandle* new_navigation);
+      content::NavigationHandle* new_navigation,
+      UserInitiatedInfo user_initiated_info);
 
   void OnTimingUpdated(content::RenderFrameHost*,
                        const PageLoadTiming& timing,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index ee45eed..b3e0021 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
index da73fa5b..bee219e8 100644
--- a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.cc
@@ -58,48 +58,57 @@
 const char kHistogramAbortBackgroundDuringParse[] =
     "PageLoad.Experimental.AbortTiming.Background.DuringParse";
 
-// These metrics should be temporary until we have landed on a one-size-fits-all
-// abort metric.
-const char kHistogramAbortNewNavigationUserInitiated[] =
-    "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
-    "UserInitiated";
-const char kHistogramAbortForwardBackUserInitiated[] =
-    "PageLoad.Experimental.AbortTiming.ForwardBackNavigation.BeforeCommit."
-    "UserInitiated";
-const char kHistogramAbortReloadUserInitiated[] =
-    "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit.UserInitiated";
-
 }  // namespace internal
 
 namespace {
 
-bool IsAbortUserInitiated(const page_load_metrics::PageLoadExtraInfo& info) {
-  // We consider an abort to be user initiated if the abort was triggered by a
-  // user action, and the page load being aborted was also user initiated. A
-  // user may abort a non-user-initiated page load, but we exclude these from
-  // our user initiated abort tracking since it's less clear that such an abort
-  // is interesting from a user perspective.
-  return info.abort_user_initiated && info.user_initiated;
-}
-
-void RecordAbortBeforeCommit(UserAbortType abort_type,
-                             bool user_initiated,
-                             base::TimeDelta time_to_abort) {
+void RecordAbortBeforeCommit(
+    UserAbortType abort_type,
+    page_load_metrics::UserInitiatedInfo user_initiated_info,
+    base::TimeDelta time_to_abort) {
   switch (abort_type) {
     case UserAbortType::ABORT_RELOAD:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadBeforeCommit,
                           time_to_abort);
-      if (user_initiated) {
-        PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadUserInitiated,
-                            time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit."
+            "UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit."
+            "UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit."
+            "BrowserInitiated",
+            time_to_abort);
       }
       return;
     case UserAbortType::ABORT_FORWARD_BACK:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackBeforeCommit,
                           time_to_abort);
-      if (user_initiated) {
-        PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackUserInitiated,
-                            time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "BeforeCommit.UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "BeforeCommit.UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "BeforeCommit.BrowserInitiated",
+            time_to_abort);
       }
       return;
     case UserAbortType::ABORT_CLIENT_REDIRECT:
@@ -109,9 +118,23 @@
     case UserAbortType::ABORT_NEW_NAVIGATION:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationBeforeCommit,
                           time_to_abort);
-      if (user_initiated) {
-        PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationUserInitiated,
-                            time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
+            "UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
+            "UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
+            "BrowserInitiated",
+            time_to_abort);
       }
       return;
     case UserAbortType::ABORT_STOP:
@@ -138,16 +161,54 @@
   NOTREACHED();
 }
 
-void RecordAbortAfterCommitBeforePaint(UserAbortType abort_type,
-                                       base::TimeDelta time_to_abort) {
+void RecordAbortAfterCommitBeforePaint(
+    UserAbortType abort_type,
+    page_load_metrics::UserInitiatedInfo user_initiated_info,
+    base::TimeDelta time_to_abort) {
   switch (abort_type) {
     case UserAbortType::ABORT_RELOAD:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadBeforePaint,
                           time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint."
+            "UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint."
+            "UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint."
+            "BrowserInitiated",
+            time_to_abort);
+      }
       return;
     case UserAbortType::ABORT_FORWARD_BACK:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackBeforePaint,
                           time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "AfterCommit.BeforePaint.UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "AfterCommit.BeforePaint.UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
+            "AfterCommit.BeforePaint.BrowserInitiated",
+            time_to_abort);
+      }
       return;
     case UserAbortType::ABORT_CLIENT_REDIRECT:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortClientRedirectBeforePaint,
@@ -156,6 +217,24 @@
     case UserAbortType::ABORT_NEW_NAVIGATION:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationBeforePaint,
                           time_to_abort);
+      if (user_initiated_info.user_gesture) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit."
+            "BeforePaint.UserGesture",
+            time_to_abort);
+      }
+      if (user_initiated_info.user_input_event) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit."
+            "BeforePaint.UserInputEvent",
+            time_to_abort);
+      }
+      if (user_initiated_info.browser_initiated) {
+        PAGE_LOAD_HISTOGRAM(
+            "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit."
+            "BeforePaint.BrowserInitiated",
+            time_to_abort);
+      }
       return;
     case UserAbortType::ABORT_STOP:
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortStopBeforePaint,
@@ -264,7 +343,9 @@
     RecordAbortDuringParse(extra_info.abort_type, time_to_abort);
   }
   if (!timing.first_paint || timing.first_paint >= time_to_abort) {
-    RecordAbortAfterCommitBeforePaint(extra_info.abort_type, time_to_abort);
+    RecordAbortAfterCommitBeforePaint(extra_info.abort_type,
+                                      extra_info.abort_user_initiated_info,
+                                      time_to_abort);
   }
 }
 
@@ -275,6 +356,6 @@
     return;
 
   RecordAbortBeforeCommit(extra_info.abort_type,
-                          IsAbortUserInitiated(extra_info),
+                          extra_info.abort_user_initiated_info,
                           extra_info.time_to_abort.value());
 }
diff --git a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
index 1eea3ad..b7511815 100644
--- a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
@@ -94,7 +94,6 @@
   NavigateAndCommit(GURL("https://www.example.com"));
   histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforePaint, 1);
-  EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, ReloadBeforePaint) {
@@ -105,7 +104,6 @@
                                       ui::PAGE_TRANSITION_RELOAD);
   histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortReloadBeforePaint, 1);
-  EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, ForwardBackBeforePaint) {
@@ -118,7 +116,6 @@
                                 ui::PAGE_TRANSITION_FORWARD_BACK));
   histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortForwardBackBeforePaint, 1);
-  EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, BackgroundBeforePaint) {
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
index f7333be..6d6e2b5 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -10,8 +10,8 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "net/http/http_response_headers.h"
 #include "ui/base/page_transition_types.h"
 
@@ -195,8 +195,8 @@
 
 const char kHistogramFirstMeaningfulPaintStatus[] =
     "PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintStatus";
-const char kHistogramFirstMeaningfulPaintSignalStatus[] =
-    "PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintSignalStatus";
+const char kHistogramFirstMeaningfulPaintSignalStatus2[] =
+    "PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintSignalStatus2";
 
 const char kHistogramFirstNonScrollInputAfterFirstPaint[] =
     "PageLoad.InputTiming.NavigationToFirstNonScroll.AfterPaint";
@@ -333,7 +333,10 @@
                           timing.first_contentful_paint.value());
     }
 
-    if (info.user_initiated) {
+    // TODO(bmcquade): consider adding a histogram that uses
+    // UserInputInfo.user_input_event.
+    if (info.user_initiated_info.browser_initiated ||
+        info.user_initiated_info.user_gesture) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstContentfulPaintUserInitiated,
                           timing.first_contentful_paint.value());
     }
@@ -345,13 +348,31 @@
           timing.style_sheet_timing.author_style_sheet_parse_duration_before_fcp
               .value());
     }
+    if (timing.style_sheet_timing.update_style_duration_before_fcp) {
+      PAGE_LOAD_HISTOGRAM(
+          "PageLoad.CSSTiming.Update.BeforeFirstContentfulPaint",
+          timing.style_sheet_timing.update_style_duration_before_fcp.value());
+    }
+    if (timing.style_sheet_timing
+            .author_style_sheet_parse_duration_before_fcp ||
+        timing.style_sheet_timing.update_style_duration_before_fcp) {
+      PAGE_LOAD_HISTOGRAM(
+          "PageLoad.CSSTiming.ParseAndUpdate.BeforeFirstContentfulPaint",
+          timing.style_sheet_timing.author_style_sheet_parse_duration_before_fcp
+                  .value_or(base::TimeDelta()) +
+              timing.style_sheet_timing.update_style_duration_before_fcp
+                  .value_or(base::TimeDelta()));
+    }
 
     switch (GetPageLoadType(transition_)) {
       case LOAD_TYPE_RELOAD:
         PAGE_LOAD_HISTOGRAM(
             internal::kHistogramLoadTypeFirstContentfulPaintReload,
             timing.first_contentful_paint.value());
-        if (info.user_initiated) {
+        // TODO(bmcquade): consider adding a histogram that uses
+        // UserInputInfo.user_input_event.
+        if (info.user_initiated_info.browser_initiated ||
+            info.user_initiated_info.user_gesture) {
           PAGE_LOAD_HISTOGRAM(
               internal::kHistogramLoadTypeFirstContentfulPaintReloadByGesture,
               timing.first_contentful_paint.value());
@@ -582,18 +603,20 @@
         internal::FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_FIRST_CONTENTFUL_PAINT);
   }
 
-  enum FirstMeaningfulPaintSignalStatus {
-    HAD_USER_INPUT = 1 << 0,
-    NETWORK_STABLE = 1 << 1,
-    FIRST_MEANINGFUL_PAINT_SIGNAL_STATUS_LAST_ENTRY = 1 << 2
-  };
-  int signal_status =
-      (first_user_interaction_after_first_paint_.is_null() ?
-       0 : HAD_USER_INPUT) +
-      (timing.first_meaningful_paint ? NETWORK_STABLE : 0);
-  UMA_HISTOGRAM_ENUMERATION(
-      internal::kHistogramFirstMeaningfulPaintSignalStatus,
-      signal_status, FIRST_MEANINGFUL_PAINT_SIGNAL_STATUS_LAST_ENTRY);
+  if (timing.first_paint) {
+    enum FirstMeaningfulPaintSignalStatus {
+      HAD_USER_INPUT = 1 << 0,
+      NETWORK_STABLE = 1 << 1,
+      FIRST_MEANINGFUL_PAINT_SIGNAL_STATUS_LAST_ENTRY = 1 << 2
+    };
+    int signal_status =
+        (first_user_interaction_after_first_paint_.is_null() ?
+         0 : HAD_USER_INPUT) +
+        (timing.first_meaningful_paint ? NETWORK_STABLE : 0);
+    UMA_HISTOGRAM_ENUMERATION(
+        internal::kHistogramFirstMeaningfulPaintSignalStatus2,
+        signal_status, FIRST_MEANINGFUL_PAINT_SIGNAL_STATUS_LAST_ENTRY);
+  }
   if (timing.first_meaningful_paint) {
     if (first_user_interaction_after_first_paint_.is_null()) {
       PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstMeaningfulPaintNoUserInput,
@@ -614,7 +637,8 @@
   // IsShuttingDown() first.
   if (g_browser_process->IsShuttingDown())
     return;
-  rappor::RapporService* rappor_service = g_browser_process->rappor_service();
+  rappor::RapporServiceImpl* rappor_service =
+      g_browser_process->rappor_service();
   if (!rappor_service)
     return;
   if (info.committed_url.is_empty())
@@ -636,8 +660,8 @@
     // was > 10s.
     sample->SetFlagsField(
         "IsSlow", timing.first_contentful_paint.value().InSecondsF() >= 10, 1);
-    rappor_service->RecordSampleObj(internal::kRapporMetricsNameCoarseTiming,
-                                    std::move(sample));
+    rappor_service->RecordSample(internal::kRapporMetricsNameCoarseTiming,
+                                 std::move(sample));
   }
 
   // Log the eTLD+1 of sites that did not report first meaningful paint.
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
index 712466a..773e54ab 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/test_rappor_service.h"
 
 namespace {
@@ -28,10 +28,10 @@
 
   void SetUp() override {
     page_load_metrics::PageLoadMetricsObserverTestHarness::SetUp();
-    TestingBrowserProcess::GetGlobal()->SetRapporService(&rappor_tester_);
+    TestingBrowserProcess::GetGlobal()->SetRapporServiceImpl(&rappor_tester_);
   }
 
-  rappor::TestRapporService rappor_tester_;
+  rappor::TestRapporServiceImpl rappor_tester_;
 };
 
 TEST_F(CorePageLoadMetricsObserverTest, NoMetrics) {
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index 796017b1..499c440 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -552,4 +552,8 @@
                                      1);
   histogram_tester_.ExpectTotalCount(
       "PageLoad.CSSTiming.Parse.BeforeFirstContentfulPaint", 1);
+  histogram_tester_.ExpectTotalCount(
+      "PageLoad.CSSTiming.Update.BeforeFirstContentfulPaint", 1);
+  histogram_tester_.ExpectTotalCount(
+      "PageLoad.CSSTiming.ParseAndUpdate.BeforeFirstContentfulPaint", 1);
 }
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 055a84c..c6d1f3b 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
index 24a8553..cb27c0d8 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
@@ -10,11 +10,11 @@
     const base::Optional<base::TimeDelta>& first_background_time,
     const base::Optional<base::TimeDelta>& first_foreground_time,
     bool started_in_foreground,
-    bool user_initiated,
+    UserInitiatedInfo user_initiated_info,
     const GURL& committed_url,
     const GURL& start_url,
     UserAbortType abort_type,
-    bool abort_user_initiated,
+    UserInitiatedInfo abort_user_initiated_info,
     const base::Optional<base::TimeDelta>& time_to_abort,
     int num_cache_requests,
     int num_network_requests,
@@ -22,11 +22,11 @@
     : first_background_time(first_background_time),
       first_foreground_time(first_foreground_time),
       started_in_foreground(started_in_foreground),
-      user_initiated(user_initiated),
+      user_initiated_info(user_initiated_info),
       committed_url(committed_url),
       start_url(start_url),
       abort_type(abort_type),
-      abort_user_initiated(abort_user_initiated),
+      abort_user_initiated_info(abort_user_initiated_info),
       time_to_abort(time_to_abort),
       num_cache_requests(num_cache_requests),
       num_network_requests(num_network_requests),
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index cb72407..7d4c1af 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -65,16 +65,56 @@
   net::Error error;
 };
 
+// Information related to whether an associated action, such as a navigation or
+// an abort, was initiated by a user. Clicking a link or tapping on a UI
+// element are examples of user initiation actions.
+struct UserInitiatedInfo {
+  static UserInitiatedInfo NotUserInitiated() {
+    return UserInitiatedInfo(false, false, false);
+  }
+
+  static UserInitiatedInfo BrowserInitiated() {
+    return UserInitiatedInfo(true, false, false);
+  }
+
+  static UserInitiatedInfo RenderInitiated(bool user_gesture,
+                                           bool user_input_event) {
+    return UserInitiatedInfo(false, user_gesture, user_input_event);
+  }
+
+  // Whether the associated action was initiated from the browser process, as
+  // opposed to from the render process. We generally assume that all actions
+  // initiated from the browser process are user initiated.
+  bool browser_initiated;
+
+  // Whether the associated action was initiated by a user, according to user
+  // gesture tracking in content and Blink, as reported by NavigationHandle.
+  bool user_gesture;
+
+  // Whether the associated action was initiated by a user, based on our
+  // heuristic-driven implementation that tests to see if there was an input
+  // event that happened shortly before the given action.
+  bool user_input_event;
+
+ private:
+  UserInitiatedInfo(bool browser_initiated,
+                    bool user_gesture,
+                    bool user_input_event)
+      : browser_initiated(browser_initiated),
+        user_gesture(user_gesture),
+        user_input_event(user_input_event) {}
+};
+
 struct PageLoadExtraInfo {
   PageLoadExtraInfo(
       const base::Optional<base::TimeDelta>& first_background_time,
       const base::Optional<base::TimeDelta>& first_foreground_time,
       bool started_in_foreground,
-      bool user_initiated,
+      UserInitiatedInfo user_initiated_info,
       const GURL& committed_url,
       const GURL& start_url,
       UserAbortType abort_type,
-      bool abort_user_initiated,
+      UserInitiatedInfo abort_user_initiated_info,
       const base::Optional<base::TimeDelta>& time_to_abort,
       int num_cache_requests,
       int num_network_requests,
@@ -93,9 +133,8 @@
   // True if the page load started in the foreground.
   const bool started_in_foreground;
 
-  // True if this is either a browser initiated navigation or the user_gesture
-  // bit is true in the renderer.
-  const bool user_initiated;
+  // Whether the page load was initiated by a user.
+  const UserInitiatedInfo user_initiated_info;
 
   // Committed URL. If the page load did not commit, |committed_url| will be
   // empty.
@@ -113,13 +152,13 @@
   // that new navigation was user-initiated. This field is only useful if this
   // page load's abort type is a value other than ABORT_NONE. Note that this
   // value is currently experimental, and is subject to change. In particular,
-  // this field is never set to true for some abort types, such as stop and
+  // this field is not currently set for some abort types, such as stop and
   // close, since we don't yet have sufficient instrumentation to know if a stop
   // or close was caused by a user action.
   //
   // TODO(csharrison): If more metadata for aborts is needed we should provide a
   // better abstraction. Note that this is an approximation.
-  bool abort_user_initiated;
+  UserInitiatedInfo abort_user_initiated_info;
 
   const base::Optional<base::TimeDelta> time_to_abort;
 
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index fa68a29..83888d86 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -287,6 +287,7 @@
     PageLoadMetricsEmbedderInterface* embedder_interface,
     const GURL& currently_committed_url,
     content::NavigationHandle* navigation_handle,
+    UserInitiatedInfo user_initiated_info,
     int aborted_chain_size,
     int aborted_chain_size_same_url)
     : did_stop_tracking_(false),
@@ -294,12 +295,12 @@
       navigation_start_(navigation_handle->NavigationStart()),
       start_url_(navigation_handle->GetURL()),
       abort_type_(ABORT_NONE),
-      abort_user_initiated_(false),
+      abort_user_initiated_info_(UserInitiatedInfo::NotUserInitiated()),
       started_in_foreground_(in_foreground),
       page_transition_(navigation_handle->GetPageTransition()),
       num_cache_requests_(0),
       num_network_requests_(0),
-      user_initiated_(IsNavigationUserInitiated(navigation_handle)),
+      user_initiated_info_(user_initiated_info),
       aborted_chain_size_(aborted_chain_size),
       aborted_chain_size_same_url_(aborted_chain_size_same_url),
       embedder_interface_(embedder_interface) {
@@ -414,9 +415,8 @@
     // can't be certain that we were backgrounded due to a user action. For
     // example, on Android, the screen times out after a period of inactivity,
     // resulting in a non-user-initiated backgrounding.
-    const bool abort_is_user_initiated = false;
-    NotifyAbort(ABORT_BACKGROUND, abort_is_user_initiated, background_time_,
-                true);
+    NotifyAbort(ABORT_BACKGROUND, UserInitiatedInfo::NotUserInitiated(),
+                background_time_, true);
   }
   const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
   INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, timing_, info);
@@ -440,7 +440,7 @@
   committed_url_ = navigation_handle->GetURL();
   // Some transitions (like CLIENT_REDIRECT) are only known at commit time.
   page_transition_ = navigation_handle->GetPageTransition();
-  user_initiated_ = IsNavigationUserInitiated(navigation_handle);
+  user_initiated_info_.user_gesture = navigation_handle->HasUserGesture();
 
   INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle);
   LogAbortChainHistograms(navigation_handle);
@@ -459,6 +459,7 @@
 }
 
 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) {
+  input_tracker_.OnInputEvent(event);
   for (const auto& observer : observers_) {
     observer->OnUserInput(event);
   }
@@ -595,17 +596,21 @@
     DCHECK(abort_time_.is_null());
   }
 
-  // abort_type_ == ABORT_NONE implies !abort_user_initiated_.
-  DCHECK(abort_type_ != ABORT_NONE || !abort_user_initiated_);
+  // abort_type_ == ABORT_NONE implies abort_user_initiated_info_ is not user
+  // initiated.
+  DCHECK(abort_type_ != ABORT_NONE ||
+         (!abort_user_initiated_info_.browser_initiated &&
+          !abort_user_initiated_info_.user_gesture &&
+          !abort_user_initiated_info_.user_input_event));
   return PageLoadExtraInfo(
       first_background_time, first_foreground_time, started_in_foreground_,
-      user_initiated_, committed_url_, start_url_, abort_type_,
-      abort_user_initiated_, time_to_abort, num_cache_requests_,
+      user_initiated_info_, committed_url_, start_url_, abort_type_,
+      abort_user_initiated_info_, time_to_abort, num_cache_requests_,
       num_network_requests_, metadata_);
 }
 
 void PageLoadTracker::NotifyAbort(UserAbortType abort_type,
-                                  bool user_initiated,
+                                  UserInitiatedInfo user_initiated_info,
                                   base::TimeTicks timestamp,
                                   bool is_certainly_browser_timestamp) {
   DCHECK_NE(abort_type, ABORT_NONE);
@@ -613,12 +618,12 @@
   if (abort_type_ != ABORT_NONE)
     return;
 
-  UpdateAbortInternal(abort_type, user_initiated, timestamp,
+  UpdateAbortInternal(abort_type, user_initiated_info, timestamp,
                       is_certainly_browser_timestamp);
 }
 
 void PageLoadTracker::UpdateAbort(UserAbortType abort_type,
-                                  bool user_initiated,
+                                  UserInitiatedInfo user_initiated_info,
                                   base::TimeTicks timestamp,
                                   bool is_certainly_browser_timestamp) {
   DCHECK_NE(abort_type, ABORT_NONE);
@@ -628,7 +633,7 @@
   // For some aborts (e.g. navigations), the initiated timestamp can be earlier
   // than the timestamp that aborted the load. Taking the minimum gives the
   // closest user initiated time known.
-  UpdateAbortInternal(abort_type, user_initiated,
+  UpdateAbortInternal(abort_type, user_initiated_info,
                       std::min(abort_time_, timestamp),
                       is_certainly_browser_timestamp);
 }
@@ -649,7 +654,7 @@
 }
 
 void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type,
-                                          bool user_initiated,
+                                          UserInitiatedInfo user_initiated_info,
                                           base::TimeTicks timestamp,
                                           bool is_certainly_browser_timestamp) {
   // When a provisional navigation commits, that navigation's start time is
@@ -677,7 +682,8 @@
   // these navs may sometimes be reported as user initiated by Blink. Thus, we
   // explicitly filter these types of aborts out when deciding if the abort was
   // user initiated.
-  abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT;
+  if (abort_type != ABORT_CLIENT_REDIRECT)
+    abort_user_initiated_info_ = user_initiated_info;
 
   if (is_certainly_browser_timestamp) {
     ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_);
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index b6411f71..817bc4fd 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/user_input_tracker.h"
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "ui/base/page_transition_types.h"
 
@@ -127,6 +128,7 @@
                   PageLoadMetricsEmbedderInterface* embedder_interface,
                   const GURL& currently_committed_url,
                   content::NavigationHandle* navigation_handle,
+                  UserInitiatedInfo user_initiated_info,
                   int aborted_chain_size,
                   int aborted_chain_size_same_url);
   ~PageLoadTracker();
@@ -175,11 +177,11 @@
   // browser process or not. We need this to possibly clamp browser timestamp on
   // a machine with inter process time tick skew.
   void NotifyAbort(UserAbortType abort_type,
-                   bool user_initiated,
+                   UserInitiatedInfo user_initiated_info,
                    base::TimeTicks timestamp,
                    bool is_certainly_browser_timestamp);
   void UpdateAbort(UserAbortType abort_type,
-                   bool user_initiated,
+                   UserInitiatedInfo user_initiated_info,
                    base::TimeTicks timestamp,
                    bool is_certainly_browser_timestamp);
 
@@ -204,6 +206,10 @@
 
   ui::PageTransition page_transition() const { return page_transition_; }
 
+  UserInitiatedInfo user_initiated_info() const { return user_initiated_info_; }
+
+  UserInputTracker* input_tracker() { return &input_tracker_; }
+
  private:
   // This function converts a TimeTicks value taken in the browser process
   // to navigation_start_ if:
@@ -214,7 +220,7 @@
       base::TimeTicks* event_time);
 
   void UpdateAbortInternal(UserAbortType abort_type,
-                           bool user_initiated,
+                           UserInitiatedInfo user_initiated_info,
                            base::TimeTicks timestamp,
                            bool is_certainly_browser_timestamp);
 
@@ -223,6 +229,8 @@
   // committed load.
   void LogAbortChainHistograms(content::NavigationHandle* final_navigation);
 
+  UserInputTracker input_tracker_;
+
   // Whether we stopped tracking this navigation after it was initiated. We may
   // stop tracking a navigation if it doesn't meet the criteria for tracking
   // metrics in DidFinishNavigation.
@@ -255,7 +263,7 @@
   // this field is never set to true for some abort types, such as stop and
   // close, since we don't yet have sufficient instrumentation to know if a stop
   // or close was caused by a user action.
-  bool abort_user_initiated_;
+  UserInitiatedInfo abort_user_initiated_info_;
 
   base::TimeTicks abort_time_;
 
@@ -277,9 +285,8 @@
   int num_cache_requests_;
   int num_network_requests_;
 
-  // This is derived from the user gesture bit in the renderer. For browser
-  // initiated navigations this will always be true.
-  bool user_initiated_;
+  // Whether this page load was user initiated.
+  UserInitiatedInfo user_initiated_info_;
 
   // This is a subtle member. If a provisional load A gets aborted by
   // provisional load B, which gets aborted by C that eventually commits, then
diff --git a/chrome/browser/page_load_metrics/user_input_tracker.cc b/chrome/browser/page_load_metrics/user_input_tracker.cc
new file mode 100644
index 0000000..c72b94e9
--- /dev/null
+++ b/chrome/browser/page_load_metrics/user_input_tracker.cc
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/user_input_tracker.h"
+
+#include <algorithm>
+
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+
+namespace page_load_metrics {
+
+namespace {
+
+// Blink's UserGestureIndicator allows events to be associated with gestures
+// that are up to 1 second old, based on guidance in the HTML spec:
+// https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation.
+const int kMaxEventAgeSeconds = 1;
+
+// Allow for up to 2x the oldest time. This allows consumers to continue to
+// find events for timestamps up to 1 second in the past.
+const int kOldestAllowedEventAgeSeconds = kMaxEventAgeSeconds * 2;
+
+// In order to limit to at most kMaxTrackedEvents, we rate limit the recorded
+// events,
+// allowing one per rate limit period.
+const int kRateLimitClampMillis = (kOldestAllowedEventAgeSeconds * 1000) /
+                                  UserInputTracker::kMaxTrackedEvents;
+
+bool IsInterestingInputEvent(const blink::WebInputEvent& event) {
+  // Ignore synthesized auto repeat events.
+  if (event.modifiers & blink::WebInputEvent::IsAutoRepeat)
+    return false;
+
+  switch (event.type) {
+    case blink::WebInputEvent::MouseDown:
+    case blink::WebInputEvent::MouseUp:
+    case blink::WebInputEvent::RawKeyDown:
+    case blink::WebInputEvent::KeyDown:
+    case blink::WebInputEvent::Char:
+    case blink::WebInputEvent::TouchStart:
+    case blink::WebInputEvent::TouchEnd:
+      return true;
+    default:
+      return false;
+  }
+}
+
+base::TimeTicks GetTimeTicksFromSeconds(double seconds) {
+  // WebInputEvent::timeStampSeconds is a double representing number of
+  // monotonic seconds in TimeTicks time base. There's no convenience API for
+  // initializing a TimeTicks from such a value. The canonical way to perform
+  // this initialization is to create a TimeTicks with value 0 and add a
+  // TimeDelta to it.
+  return base::TimeTicks() + base::TimeDelta::FromSecondsD(seconds);
+}
+
+}  // namespace
+
+UserInputTracker::UserInputTracker() {
+  sorted_event_times_.reserve(kMaxTrackedEvents);
+}
+
+UserInputTracker::~UserInputTracker() {}
+
+const size_t UserInputTracker::kMaxTrackedEvents = 100;
+
+// static
+base::TimeTicks UserInputTracker::GetEventTime(
+    const blink::WebInputEvent& event) {
+  return GetTimeTicksFromSeconds(event.timeStampSeconds);
+}
+
+// static
+base::TimeTicks UserInputTracker::RoundToRateLimitedOffset(
+    base::TimeTicks time) {
+  base::TimeDelta time_as_delta = time - base::TimeTicks();
+  base::TimeDelta rate_limit_remainder =
+      time_as_delta % base::TimeDelta::FromMilliseconds(kRateLimitClampMillis);
+  return time - rate_limit_remainder;
+}
+
+void UserInputTracker::OnInputEvent(const blink::WebInputEvent& event) {
+  RemoveInputEventsUpToInclusive(base::TimeTicks::Now() -
+                                 GetOldEventThreshold());
+
+  if (!IsInterestingInputEvent(event))
+    return;
+
+  // TODO(bmcquade): ideally we'd limit tracking to events generated by a user
+  // action, as opposed to those generated from JavaScript. The JS API isTrusted
+  // can be used to distinguish these cases. isTrusted isn't yet a property of
+  // WebInputEvent. We should consider adding it.
+
+  const base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeTicks time = RoundToRateLimitedOffset(GetEventTime(event));
+  if (time <=
+      std::max(most_recent_consumed_time_, now - GetOldEventThreshold()))
+    return;
+
+  if (time > now) {
+    DCHECK(!base::TimeTicks::IsHighResolution());
+    return;
+  }
+
+  // lower_bound finds the first element >= |time|.
+  auto it = std::lower_bound(sorted_event_times_.begin(),
+                             sorted_event_times_.end(), time);
+  if (it != sorted_event_times_.end() && *it == time) {
+    // Don't insert duplicate values.
+    return;
+  }
+
+  sorted_event_times_.insert(it, time);
+  DCHECK_LE(sorted_event_times_.size(), kMaxTrackedEvents);
+  DCHECK(
+      std::is_sorted(sorted_event_times_.begin(), sorted_event_times_.end()));
+}
+
+bool UserInputTracker::FindAndConsumeInputEventsBefore(base::TimeTicks time) {
+  base::TimeTicks recent_input_event_time =
+      FindMostRecentUserInputEventBefore(time);
+
+  if (recent_input_event_time.is_null())
+    return false;
+
+  RemoveInputEventsUpToInclusive(recent_input_event_time);
+  return true;
+}
+
+base::TimeTicks UserInputTracker::FindMostRecentUserInputEventBefore(
+    base::TimeTicks time) {
+  RemoveInputEventsUpToInclusive(base::TimeTicks::Now() -
+                                 GetOldEventThreshold());
+
+  if (sorted_event_times_.empty())
+    return base::TimeTicks();
+
+  // lower_bound finds the first element >= |time|.
+  auto it = std::lower_bound(sorted_event_times_.begin(),
+                             sorted_event_times_.end(), time);
+
+  // If all times are after the requested time, then we don't have a time to
+  // return.
+  if (it == sorted_event_times_.begin())
+    return base::TimeTicks();
+
+  // |it| points to the first event >= the specified time, so decrement once to
+  // find the greatest event before the specified time.
+  --it;
+  base::TimeTicks candidate = *it;
+  DCHECK_LT(candidate, time);
+
+  // If the most recent event is too old, then don't return it.
+  if (candidate < time - base::TimeDelta::FromSeconds(kMaxEventAgeSeconds))
+    return base::TimeTicks();
+
+  return candidate;
+}
+
+void UserInputTracker::RemoveInputEventsUpToInclusive(base::TimeTicks cutoff) {
+  cutoff = std::max(RoundToRateLimitedOffset(cutoff),
+                    base::TimeTicks::Now() - GetOldEventThreshold());
+  most_recent_consumed_time_ = std::max(most_recent_consumed_time_, cutoff);
+  sorted_event_times_.erase(
+      sorted_event_times_.begin(),
+      std::upper_bound(sorted_event_times_.begin(), sorted_event_times_.end(),
+                       cutoff));
+}
+
+// static
+base::TimeDelta UserInputTracker::GetOldEventThreshold() {
+  return base::TimeDelta::FromSeconds(kOldestAllowedEventAgeSeconds);
+}
+
+}  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/user_input_tracker.h b/chrome/browser/page_load_metrics/user_input_tracker.h
new file mode 100644
index 0000000..5e3ce4d
--- /dev/null
+++ b/chrome/browser/page_load_metrics/user_input_tracker.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_USER_INPUT_TRACKER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_USER_INPUT_TRACKER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace blink {
+class WebInputEvent;
+}  // namespace blink
+
+namespace page_load_metrics {
+
+// UserInputTracker keeps track of user input events processed by web pages, and
+// allows clients to find and consume those input events. This allows us to
+// heuristically attribute user input events to navigations, in order to keep
+// track of which page loads and aborts were initiated by a user action.
+//
+// There are issues with the existing user gesture tracking in Blink and content
+// that make it unsuitable for our needs. For example, Blink considers events
+// such as navigations that occur within 1 second of a user action event to have
+// been initiated by a user action, based on the HTML spec
+// (https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation).
+// This can be problematic in cases where a web page issues many navigations in
+// rapid succession, e.g. JS code that dispatches new navigation requests in a
+// tight loop can result in dozens of programmatically generated navigations
+// being user initiated.
+//
+// Note that UserInputTracker does not keep track of input events processed by
+// the browser, such as interactions with the Chrome browser UI (e.g. clicking
+// the 'reload' button).
+class UserInputTracker {
+ public:
+  // Only public for tests.
+  static const size_t kMaxTrackedEvents;
+  static base::TimeTicks GetEventTime(const blink::WebInputEvent& event);
+
+  // Given a time, round to the nearest rate-limited offset. UserInputTracker
+  // rate limits events, such that at most one event will be recorded per every
+  // 20ms. RoundToRateLimitedOffset round a TimeTicks down to its nearest whole
+  // 20ms.
+  static base::TimeTicks RoundToRateLimitedOffset(base::TimeTicks time);
+
+  UserInputTracker();
+  ~UserInputTracker();
+
+  void OnInputEvent(const blink::WebInputEvent& event);
+
+  // Attempts to find the most recent user input event before the given time,
+  // and, if that input event exists, consumes all events up to that
+  // event. Returns whether an input event before the given time was found and
+  // consumed.
+  bool FindAndConsumeInputEventsBefore(base::TimeTicks time);
+
+  // Finds the time of the most recent user input event before the given time,
+  // or a null TimeTicks if there are no user input events before the given
+  // time. Consumers of this class should use
+  // FindAndConsumeInputEventsBefore. This method is public only for testing.
+  base::TimeTicks FindMostRecentUserInputEventBefore(base::TimeTicks time);
+
+ private:
+  void RemoveInputEventsUpToInclusive(base::TimeTicks cutoff);
+
+  static base::TimeDelta GetOldEventThreshold();
+
+  std::vector<base::TimeTicks> sorted_event_times_;
+  base::TimeTicks most_recent_consumed_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserInputTracker);
+};
+
+}  // namespace page_load_metrics
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_USER_INPUT_TRACKER_H_
diff --git a/chrome/browser/page_load_metrics/user_input_tracker_unittest.cc b/chrome/browser/page_load_metrics/user_input_tracker_unittest.cc
new file mode 100644
index 0000000..8602f19
--- /dev/null
+++ b/chrome/browser/page_load_metrics/user_input_tracker_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/user_input_tracker.h"
+
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+
+namespace page_load_metrics {
+
+namespace {
+
+// UserInputTracker allows events to be at most 2 seconds old. Thus we use
+// 2100ms to make sure the age is greater than 2 seconds.
+const int kTooOldMilliseconds = 2100;
+
+double ToMonotonicallyIncreasingSeconds(base::TimeTicks t) {
+  return (t - base::TimeTicks()).InSecondsF();
+}
+
+class FakeInputEvent : public blink::WebInputEvent {
+ public:
+  FakeInputEvent() : WebInputEvent(sizeof(FakeInputEvent)) {
+    timeStampSeconds = ToMonotonicallyIncreasingSeconds(base::TimeTicks::Now());
+    type = blink::WebInputEvent::Char;
+  }
+
+  base::TimeTicks GetTimeStamp() {
+    return UserInputTracker::GetEventTime(*this);
+  }
+
+  base::TimeTicks GetTimeStampRounded() {
+    return UserInputTracker::RoundToRateLimitedOffset(GetTimeStamp());
+  }
+};
+
+}  // namespace
+
+class UserInputTrackerTest : public testing::Test {};
+
+TEST_F(UserInputTrackerTest, Basic) {
+  UserInputTracker tracker;
+  EXPECT_EQ(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(base::TimeTicks()));
+  EXPECT_EQ(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(base::TimeTicks::Now()));
+}
+
+TEST_F(UserInputTrackerTest, SingleEvent) {
+  UserInputTracker tracker;
+  FakeInputEvent e;
+  tracker.OnInputEvent(e);
+
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e.GetTimeStampRounded()));
+
+  base::TimeTicks after =
+      e.GetTimeStampRounded() + base::TimeDelta::FromMicroseconds(1);
+
+  EXPECT_EQ(e.GetTimeStampRounded(),
+            tracker.FindMostRecentUserInputEventBefore(after));
+
+  EXPECT_TRUE(tracker.FindAndConsumeInputEventsBefore(after));
+
+  EXPECT_EQ(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(after));
+}
+
+TEST_F(UserInputTrackerTest, MultipleEvents) {
+  FakeInputEvent e1;
+  FakeInputEvent e2;
+
+  // Make sure that the two events are monotonically increasing, and that both
+  // are in the past.
+  e1.timeStampSeconds = ToMonotonicallyIncreasingSeconds(
+      e2.GetTimeStamp() - base::TimeDelta::FromMilliseconds(100));
+
+  base::TimeTicks after =
+      e2.GetTimeStampRounded() + base::TimeDelta::FromMicroseconds(1);
+
+  {
+    UserInputTracker tracker;
+    tracker.OnInputEvent(e1);
+    tracker.OnInputEvent(e2);
+
+    EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                     e1.GetTimeStampRounded()));
+    EXPECT_EQ(
+        e1.GetTimeStampRounded(),
+        tracker.FindMostRecentUserInputEventBefore(e2.GetTimeStampRounded()));
+
+    EXPECT_EQ(e2.GetTimeStampRounded(),
+              tracker.FindMostRecentUserInputEventBefore(after));
+
+    EXPECT_FALSE(
+        tracker.FindAndConsumeInputEventsBefore(e1.GetTimeStampRounded()));
+    EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                     e1.GetTimeStampRounded()));
+    EXPECT_EQ(
+        e1.GetTimeStampRounded(),
+        tracker.FindMostRecentUserInputEventBefore(e2.GetTimeStampRounded()));
+    EXPECT_EQ(e2.GetTimeStampRounded(),
+              tracker.FindMostRecentUserInputEventBefore(after));
+
+    EXPECT_TRUE(tracker.FindAndConsumeInputEventsBefore(after));
+    EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                     e1.GetTimeStampRounded()));
+    EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                     e2.GetTimeStampRounded()));
+    EXPECT_EQ(base::TimeTicks(),
+              tracker.FindMostRecentUserInputEventBefore(after));
+  }
+
+  {
+    UserInputTracker tracker;
+    tracker.OnInputEvent(e1);
+    tracker.OnInputEvent(e2);
+    EXPECT_EQ(e2.GetTimeStampRounded(),
+              tracker.FindMostRecentUserInputEventBefore(after));
+    EXPECT_TRUE(tracker.FindAndConsumeInputEventsBefore(after));
+    EXPECT_EQ(base::TimeTicks(),
+              tracker.FindMostRecentUserInputEventBefore(after));
+  }
+}
+
+TEST_F(UserInputTrackerTest, IgnoreEventsOlderThanConsumed) {
+  FakeInputEvent e1;
+  FakeInputEvent e2;
+
+  // Make sure that the two events are monotonically increasing, and that both
+  // are in the past.
+  e1.timeStampSeconds = ToMonotonicallyIncreasingSeconds(
+      e2.GetTimeStamp() - base::TimeDelta::FromMilliseconds(100));
+
+  base::TimeTicks after =
+      e2.GetTimeStampRounded() + base::TimeDelta::FromMicroseconds(1);
+
+  UserInputTracker tracker;
+  tracker.OnInputEvent(e2);
+
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e1.GetTimeStampRounded()));
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e2.GetTimeStampRounded()));
+  EXPECT_EQ(e2.GetTimeStampRounded(),
+            tracker.FindMostRecentUserInputEventBefore(after));
+
+  EXPECT_TRUE(tracker.FindAndConsumeInputEventsBefore(after));
+  EXPECT_EQ(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(after));
+
+  tracker.OnInputEvent(e1);
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e1.GetTimeStampRounded()));
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e1.GetTimeStampRounded() +
+                                   base::TimeDelta::FromMicroseconds(1)));
+}
+
+TEST_F(UserInputTrackerTest, ExcludeOldEvents) {
+  UserInputTracker tracker;
+  FakeInputEvent e1;
+  FakeInputEvent e2;
+  // make sure e1 is too old to be considered.
+  e1.timeStampSeconds = ToMonotonicallyIncreasingSeconds(
+      e2.GetTimeStamp() -
+      base::TimeDelta::FromMilliseconds(kTooOldMilliseconds));
+
+  tracker.OnInputEvent(e1);
+  tracker.OnInputEvent(e2);
+
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e1.GetTimeStampRounded() +
+                                   base::TimeDelta::FromMilliseconds(1)));
+  EXPECT_EQ(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(
+                e2.GetTimeStampRounded() +
+                base::TimeDelta::FromMilliseconds(kTooOldMilliseconds)));
+  EXPECT_EQ(
+      e2.GetTimeStampRounded(),
+      tracker.FindMostRecentUserInputEventBefore(
+          e2.GetTimeStampRounded() + base::TimeDelta::FromMilliseconds(1)));
+
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e2.GetTimeStampRounded()));
+}
+
+TEST_F(UserInputTrackerTest, RateLimit) {
+  const size_t kTooManyEntries = UserInputTracker::kMaxTrackedEvents * 5;
+
+  UserInputTracker tracker;
+  FakeInputEvent e;
+
+  // UserInputTracker DCHECKs that event timestamps aren't after the current
+  // time, so to be safe, we use a starting timestamp that is twice
+  // kTooManyEntries milliseconds in the past, and then synthesize one event for
+  // each of kTooManyEntries after this start point. This guarantees that all
+  // events are in the past.
+  e.timeStampSeconds = ToMonotonicallyIncreasingSeconds(
+      e.GetTimeStamp() -
+      base::TimeDelta::FromMilliseconds(kTooManyEntries * 2));
+
+  // Insert more than kMaxEntries entries. The rate limiting logic should
+  // prevent more than kMaxEntries entries from actually being inserted. A
+  // DCHECK in OnInputEvent verifies that we don't exceed the expected capacity.
+  for (size_t i = 0; i < kTooManyEntries; ++i) {
+    tracker.OnInputEvent(e);
+    e.timeStampSeconds += base::TimeDelta::FromMilliseconds(1).InSecondsF();
+  }
+
+  // Do a basic sanity check to make sure we can find events in the tracker.
+  EXPECT_NE(base::TimeTicks(),
+            tracker.FindMostRecentUserInputEventBefore(base::TimeTicks::Now()));
+}
+
+TEST_F(UserInputTrackerTest, IgnoredEventType) {
+  UserInputTracker tracker;
+  FakeInputEvent e;
+  e.type = blink::WebInputEvent::MouseMove;
+  tracker.OnInputEvent(e);
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e.GetTimeStampRounded() +
+                                   base::TimeDelta::FromMilliseconds(1)));
+}
+
+TEST_F(UserInputTrackerTest, IgnoreRepeatEvents) {
+  UserInputTracker tracker;
+  FakeInputEvent e;
+  e.modifiers |= blink::WebInputEvent::IsAutoRepeat;
+  tracker.OnInputEvent(e);
+  EXPECT_EQ(base::TimeTicks(), tracker.FindMostRecentUserInputEventBefore(
+                                   e.GetTimeStampRounded() +
+                                   base::TimeDelta::FromMilliseconds(1)));
+}
+
+}  // namespace page_load_metrics
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc
index ae3f6db..5d377405 100644
--- a/chrome/browser/password_manager/password_store_mac.cc
+++ b/chrome/browser/password_manager/password_store_mac.cc
@@ -1345,13 +1345,12 @@
     login_metadata_db_->stats_table().RemoveRow(origin_domain);
 }
 
-std::vector<std::unique_ptr<password_manager::InteractionsStats>>
+std::vector<password_manager::InteractionsStats>
 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) {
   DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
   return login_metadata_db_
              ? login_metadata_db_->stats_table().GetRows(origin_domain)
-             : std::vector<
-                   std::unique_ptr<password_manager::InteractionsStats>>();
+             : std::vector<password_manager::InteractionsStats>();
 }
 
 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
diff --git a/chrome/browser/password_manager/password_store_mac.h b/chrome/browser/password_manager/password_store_mac.h
index 9c6b3d4..b7a58150 100644
--- a/chrome/browser/password_manager/password_store_mac.h
+++ b/chrome/browser/password_manager/password_store_mac.h
@@ -112,8 +112,8 @@
   void AddSiteStatsImpl(
       const password_manager::InteractionsStats& stats) override;
   void RemoveSiteStatsImpl(const GURL& origin_domain) override;
-  std::vector<std::unique_ptr<password_manager::InteractionsStats>>
-  GetSiteStatsImpl(const GURL& origin_domain) override;
+  std::vector<password_manager::InteractionsStats> GetSiteStatsImpl(
+      const GURL& origin_domain) override;
 
   // Adds the given form to the Keychain if it's something we want to store
   // there (i.e., not a blacklist entry or a federated login). Returns true if
diff --git a/chrome/browser/password_manager/password_store_proxy_mac.cc b/chrome/browser/password_manager/password_store_proxy_mac.cc
index c58e09b..2d103a4 100644
--- a/chrome/browser/password_manager/password_store_proxy_mac.cc
+++ b/chrome/browser/password_manager/password_store_proxy_mac.cc
@@ -215,7 +215,7 @@
   GetBackend()->RemoveSiteStatsImpl(origin_domain);
 }
 
-std::vector<std::unique_ptr<password_manager::InteractionsStats>>
+std::vector<password_manager::InteractionsStats>
 PasswordStoreProxyMac::GetSiteStatsImpl(const GURL& origin_domain) {
   return GetBackend()->GetSiteStatsImpl(origin_domain);
 }
diff --git a/chrome/browser/password_manager/password_store_proxy_mac.h b/chrome/browser/password_manager/password_store_proxy_mac.h
index 776c83d..3f5c5f2 100644
--- a/chrome/browser/password_manager/password_store_proxy_mac.h
+++ b/chrome/browser/password_manager/password_store_proxy_mac.h
@@ -100,8 +100,8 @@
   void AddSiteStatsImpl(
       const password_manager::InteractionsStats& stats) override;
   void RemoveSiteStatsImpl(const GURL& origin_domain) override;
-  std::vector<std::unique_ptr<password_manager::InteractionsStats>>
-  GetSiteStatsImpl(const GURL& origin_domain) override;
+  std::vector<password_manager::InteractionsStats> GetSiteStatsImpl(
+      const GURL& origin_domain) override;
 
   scoped_refptr<SimplePasswordStoreMac> password_store_simple_;
 
diff --git a/chrome/browser/payments/BUILD.gn b/chrome/browser/payments/BUILD.gn
deleted file mode 100644
index 55a0226..0000000
--- a/chrome/browser/payments/BUILD.gn
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("payments") {
-  sources = [
-    "payment_request_impl.cc",
-    "payment_request_impl.h",
-    "ui/payment_request_dialog.cc",
-    "ui/payment_request_dialog.h",
-  ]
-  deps = [
-    "//components/payments:payment_request",
-    "//ui/views",
-  ]
-}
diff --git a/chrome/browser/payments/payment_request_impl.cc b/chrome/browser/payments/payment_request_impl.cc
index 04e2468..18ce94c9 100644
--- a/chrome/browser/payments/payment_request_impl.cc
+++ b/chrome/browser/payments/payment_request_impl.cc
@@ -7,10 +7,7 @@
 #include <map>
 
 #include "base/lazy_instance.h"
-#include "chrome/browser/payments/ui/payment_request_dialog.h"
-#include "components/web_modal/web_contents_modal_dialog_host.h"
-#include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
@@ -51,8 +48,7 @@
     content::WebContents* web_contents,
     mojo::InterfaceRequest<payments::mojom::PaymentRequest> request)
     : web_contents_(web_contents),
-      binding_(this, std::move(request)),
-      dialog_(nullptr) {
+      binding_(this, std::move(request)) {
   binding_.set_connection_error_handler(
       base::Bind(&PaymentRequestImpl::OnError, this));
 }
@@ -65,19 +61,17 @@
     payments::mojom::PaymentDetailsPtr details,
     payments::mojom::PaymentOptionsPtr options) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  dialog_ = new PaymentRequestDialog(std::move(client));
-  views::DialogDelegate::CreateDialogWidget(
-      dialog_, nullptr,
-      web_modal::WebContentsModalDialogManager::FromWebContents(web_contents_)
-          ->delegate()
-          ->GetWebContentsModalDialogHost()
-          ->GetHostView())
-      ->Show();
+  client_ = std::move(client);
+  chrome::ShowPaymentRequestDialog(this);
+}
+
+void PaymentRequestImpl::Cancel() {
+  client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL);
 }
 
 void PaymentRequestImpl::OnError() {
   binding_.Close();
-  // TODO(krb): Call dialog_->Close() here, but avoid double-free
+  // TODO(krb): Close the dialog here, but avoid double-free
   payment_request_factory.Get().UnassignPaymentRequest(web_contents_);
 }
 
diff --git a/chrome/browser/payments/payment_request_impl.h b/chrome/browser/payments/payment_request_impl.h
index 2057aeb..39ee68f 100644
--- a/chrome/browser/payments/payment_request_impl.h
+++ b/chrome/browser/payments/payment_request_impl.h
@@ -14,8 +14,6 @@
 
 namespace payments {
 
-class PaymentRequestDialog;
-
 class PaymentRequestImpl : payments::mojom::PaymentRequest,
                            public base::RefCounted<PaymentRequestImpl> {
  public:
@@ -34,15 +32,19 @@
   void Complete(payments::mojom::PaymentComplete result) override {}
   void CanMakePayment() override {}
 
+  void Cancel();
   void OnError();
 
+  content::WebContents* web_contents() { return web_contents_; }
+
  private:
   friend class base::RefCounted<PaymentRequestImpl>;
   ~PaymentRequestImpl() override;
 
   content::WebContents* web_contents_;
   mojo::Binding<payments::mojom::PaymentRequest> binding_;
-  PaymentRequestDialog* dialog_;
+  payments::mojom::PaymentRequestClientPtr client_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestImpl);
 };
 
diff --git a/chrome/browser/payments/ui/payment_request_dialog.cc b/chrome/browser/payments/ui/payment_request_dialog.cc
deleted file mode 100644
index 4c4c480..0000000
--- a/chrome/browser/payments/ui/payment_request_dialog.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/payments/ui/payment_request_dialog.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/views/layout/fill_layout.h"
-
-namespace payments {
-
-PaymentRequestDialog::PaymentRequestDialog(
-    payments::mojom::PaymentRequestClientPtr client)
-    : client_(std::move(client)),
-      label_(new views::Label(base::ASCIIToUTF16("Payments dialog"))) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  SetLayoutManager(new views::FillLayout());
-  AddChildView(label_.get());
-}
-
-PaymentRequestDialog::~PaymentRequestDialog() {}
-
-ui::ModalType PaymentRequestDialog::GetModalType() const {
-  return ui::MODAL_TYPE_CHILD;
-}
-
-gfx::Size PaymentRequestDialog::GetPreferredSize() const {
-  gfx::Size ps = label_->GetPreferredSize();
-  ps.Enlarge(200, 200);
-  return ps;
-}
-
-bool PaymentRequestDialog::Cancel() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  client_->OnError(payments::mojom::PaymentErrorReason::USER_CANCEL);
-  return true;
-}
-
-}  // namespace payments
diff --git a/chrome/browser/payments/ui/payment_request_dialog.h b/chrome/browser/payments/ui/payment_request_dialog.h
deleted file mode 100644
index 6834c11..0000000
--- a/chrome/browser/payments/ui/payment_request_dialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PAYMENTS_UI_PAYMENT_REQUEST_DIALOG_H_
-#define CHROME_BROWSER_PAYMENTS_UI_PAYMENT_REQUEST_DIALOG_H_
-
-#include "components/payments/payment_request.mojom.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/window/dialog_delegate.h"
-
-namespace payments {
-
-class PaymentRequestDialog : public views::DialogDelegateView {
- public:
-  explicit PaymentRequestDialog(
-      payments::mojom::PaymentRequestClientPtr client);
-  ~PaymentRequestDialog() override;
-
-  // views::WidgetDelegate
-  ui::ModalType GetModalType() const override;
-
-  // views::View
-  gfx::Size GetPreferredSize() const override;
-
-  // views::DialogDelegate
-  bool Cancel() override;
-
- private:
-  payments::mojom::PaymentRequestClientPtr client_;
-  std::unique_ptr<views::Label> label_;
-
-  DISALLOW_COPY_AND_ASSIGN(PaymentRequestDialog);
-};
-
-}  // namespace payments
-
-#endif  // CHROME_BROWSER_PAYMENTS_UI_PAYMENT_REQUEST_DIALOG_H_
diff --git a/chrome/browser/permissions/permission_context_base_unittest.cc b/chrome/browser/permissions/permission_context_base_unittest.cc
index fb2e822..4dfe1e41 100644
--- a/chrome/browser/permissions/permission_context_base_unittest.cc
+++ b/chrome/browser/permissions/permission_context_base_unittest.cc
@@ -621,11 +621,9 @@
   TestGrantAndRevoke_TestContent(content::PermissionType::GEOLOCATION,
                                  CONTENT_SETTINGS_TYPE_GEOLOCATION,
                                  CONTENT_SETTING_ASK);
-#if defined(ENABLE_NOTIFICATIONS)
   TestGrantAndRevoke_TestContent(content::PermissionType::NOTIFICATIONS,
                                  CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
                                  CONTENT_SETTING_ASK);
-#endif
   TestGrantAndRevoke_TestContent(content::PermissionType::MIDI_SYSEX,
                                  CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
                                  CONTENT_SETTING_ASK);
diff --git a/chrome/browser/permissions/permission_infobar_delegate.cc b/chrome/browser/permissions/permission_infobar_delegate.cc
index 33131dce..3d7ef82 100644
--- a/chrome/browser/permissions/permission_infobar_delegate.cc
+++ b/chrome/browser/permissions/permission_infobar_delegate.cc
@@ -52,13 +52,11 @@
       return std::unique_ptr<PermissionInfoBarDelegate>(
               new GeolocationInfoBarDelegateAndroid(
                   requesting_frame, user_gesture, profile, callback));
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
     case content::PermissionType::PUSH_MESSAGING:
       return std::unique_ptr<PermissionInfoBarDelegate>(
           new NotificationPermissionInfoBarDelegate(
               type, requesting_frame, user_gesture, profile, callback));
-#endif  // ENABLE_NOTIFICATIONS
     case content::PermissionType::MIDI_SYSEX:
       return std::unique_ptr<PermissionInfoBarDelegate>(
               new MidiPermissionInfoBarDelegateAndroid(
diff --git a/chrome/browser/permissions/permission_request_impl.cc b/chrome/browser/permissions/permission_request_impl.cc
index a9bbd496..62cf0d94 100644
--- a/chrome/browser/permissions/permission_request_impl.cc
+++ b/chrome/browser/permissions/permission_request_impl.cc
@@ -46,11 +46,9 @@
   switch (permission_type_) {
     case content::PermissionType::GEOLOCATION:
       return IDR_ANDROID_INFOBAR_GEOLOCATION;
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
     case content::PermissionType::PUSH_MESSAGING:
       return IDR_ANDROID_INFOBAR_NOTIFICATIONS;
-#endif
     case content::PermissionType::MIDI_SYSEX:
       return IDR_ANDROID_INFOBAR_MIDI;
     case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
@@ -63,11 +61,9 @@
   switch (permission_type_) {
     case content::PermissionType::GEOLOCATION:
       return gfx::VectorIconId::LOCATION_ON;
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
     case content::PermissionType::PUSH_MESSAGING:
       return gfx::VectorIconId::NOTIFICATIONS;
-#endif
 #if defined(OS_CHROMEOS)
     // TODO(xhwang): fix this icon, see crrev.com/863263007
     case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
@@ -90,12 +86,10 @@
     case content::PermissionType::GEOLOCATION:
       message_id = IDS_GEOLOCATION_INFOBAR_PERMISSION_FRAGMENT;
       break;
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
     case content::PermissionType::PUSH_MESSAGING:
       message_id = IDS_NOTIFICATION_PERMISSIONS_FRAGMENT;
       break;
-#endif
     case content::PermissionType::MIDI_SYSEX:
       message_id = IDS_MIDI_SYSEX_PERMISSION_FRAGMENT;
       break;
@@ -158,9 +152,7 @@
     case content::PermissionType::GEOLOCATION:
       return CONTENT_SETTINGS_TYPE_GEOLOCATION;
     case content::PermissionType::PUSH_MESSAGING:
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
-#endif
       return CONTENT_SETTINGS_TYPE_NOTIFICATIONS;
     case content::PermissionType::MIDI_SYSEX:
       return CONTENT_SETTINGS_TYPE_MIDI_SYSEX;
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index 63fa224..133b5f9 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -22,8 +22,8 @@
 #include "chrome/common/pref_names.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/common/origin_util.h"
 #include "url/gurl.h"
@@ -97,14 +97,15 @@
                              const GURL& requesting_origin,
                              const GURL& embedding_origin,
                              Profile* profile) {
-  rappor::RapporService* rappor_service = g_browser_process->rappor_service();
+  rappor::RapporServiceImpl* rappor_service =
+      g_browser_process->rappor_service();
   if (rappor_service) {
     if (permission == PermissionType::GEOLOCATION) {
       // TODO(dominickn): remove this deprecated metric - crbug.com/605836.
       rappor::SampleDomainAndRegistryFromGURL(
           rappor_service, "ContentSettings.PermissionRequested.Geolocation.Url",
           requesting_origin);
-      rappor_service->RecordSample(
+      rappor_service->RecordSampleString(
           "ContentSettings.PermissionRequested.Geolocation.Url2",
           rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
           rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
@@ -114,7 +115,7 @@
           rappor_service,
           "ContentSettings.PermissionRequested.Notifications.Url",
           requesting_origin);
-      rappor_service->RecordSample(
+      rappor_service->RecordSampleString(
           "ContentSettings.PermissionRequested.Notifications.Url2",
           rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
           rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
@@ -124,12 +125,12 @@
       rappor::SampleDomainAndRegistryFromGURL(
           rappor_service, "ContentSettings.PermissionRequested.Midi.Url",
           requesting_origin);
-      rappor_service->RecordSample(
+      rappor_service->RecordSampleString(
           "ContentSettings.PermissionRequested.Midi.Url2",
           rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
           rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
     } else if (permission == PermissionType::PROTECTED_MEDIA_IDENTIFIER) {
-      rappor_service->RecordSample(
+      rappor_service->RecordSampleString(
           "ContentSettings.PermissionRequested.ProtectedMedia.Url2",
           rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
           rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
@@ -710,13 +711,14 @@
   // TODO(dominickn): remove the deprecated metric and replace it solely with
   // the new one in GetRapporMetric - crbug.com/605836.
   const std::string deprecated_metric = GetRapporMetric(permission, action);
-  rappor::RapporService* rappor_service = g_browser_process->rappor_service();
+  rappor::RapporServiceImpl* rappor_service =
+      g_browser_process->rappor_service();
   if (!deprecated_metric.empty() && rappor_service) {
     rappor::SampleDomainAndRegistryFromGURL(rappor_service, deprecated_metric,
                                             requesting_origin);
 
     std::string rappor_metric = deprecated_metric + "2";
-    rappor_service->RecordSample(
+    rappor_service->RecordSampleString(
         rappor_metric, rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
         rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
   }
diff --git a/chrome/browser/permissions/permission_util.cc b/chrome/browser/permissions/permission_util.cc
index 3048533..0643a0f 100644
--- a/chrome/browser/permissions/permission_util.cc
+++ b/chrome/browser/permissions/permission_util.cc
@@ -60,10 +60,8 @@
   switch (type) {
     case content::PermissionType::GEOLOCATION:
       return PermissionRequestType::PERMISSION_GEOLOCATION;
-#if defined(ENABLE_NOTIFICATIONS)
     case content::PermissionType::NOTIFICATIONS:
       return PermissionRequestType::PERMISSION_NOTIFICATIONS;
-#endif
     case content::PermissionType::MIDI_SYSEX:
       return PermissionRequestType::PERMISSION_MIDI_SYSEX;
     case content::PermissionType::PUSH_MESSAGING:
diff --git a/chrome/browser/plugins/plugin_finder.cc b/chrome/browser/plugins/plugin_finder.cc
index f4cb1eb4..38cf704 100644
--- a/chrome/browser/plugins/plugin_finder.cc
+++ b/chrome/browser/plugins/plugin_finder.cc
@@ -230,7 +230,7 @@
     return nullptr;
   }
 
-  if (value->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (value->GetType() != base::Value::Type::DICTIONARY) {
     // JSONReader::JSON_PARSE_ERROR_COUNT is used for the case where the JSON
     // value has the wrong type.
     RecordBuiltInPluginListError(PluginListError::SCHEMA_ERROR);
diff --git a/chrome/browser/plugins/plugin_finder_unittest.cc b/chrome/browser/plugins/plugin_finder_unittest.cc
index 0d6a711..ffe88b5a 100644
--- a/chrome/browser/plugins/plugin_finder_unittest.cc
+++ b/chrome/browser/plugins/plugin_finder_unittest.cc
@@ -17,7 +17,7 @@
   ASSERT_TRUE(plugin_list.get());
   std::unique_ptr<base::Value> version;
   ASSERT_TRUE(plugin_list->Remove("x-version", &version));
-  EXPECT_EQ(base::Value::TYPE_INTEGER, version->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, version->GetType());
 
   for (base::DictionaryValue::Iterator plugin_it(*plugin_list);
        !plugin_it.IsAtEnd(); plugin_it.Advance()) {
diff --git a/chrome/browser/plugins/plugin_info_message_filter.cc b/chrome/browser/plugins/plugin_info_message_filter.cc
index 69395b8..3543c2e76 100644
--- a/chrome/browser/plugins/plugin_info_message_filter.cc
+++ b/chrome/browser/plugins/plugin_info_message_filter.cc
@@ -38,7 +38,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/plugin_service.h"
 #include "content/public/browser/plugin_service_filter.h"
@@ -119,7 +119,8 @@
 
   if (chrome::IsIncognitoSessionActive())
     return;
-  rappor::RapporService* rappor_service = g_browser_process->rappor_service();
+  rappor::RapporServiceImpl* rappor_service =
+      g_browser_process->rappor_service();
   if (!rappor_service)
     return;
   if (main_frame_origin.unique())
@@ -127,16 +128,15 @@
 
   if (mime_type == content::kFlashPluginSwfMimeType ||
       mime_type == content::kFlashPluginSplMimeType) {
-    rappor_service->RecordSample(
+    rappor_service->RecordSampleString(
         "Plugins.FlashOriginUrl", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
         net::registry_controlled_domains::GetDomainAndRegistry(
             main_frame_origin.GetURL(),
             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
-    rappor_service->RecordSample(
+    rappor_service->RecordSampleString(
         "Plugins.FlashUrl", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
         net::registry_controlled_domains::GetDomainAndRegistry(
-            url,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
+            url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
   }
 }
 
diff --git a/chrome/browser/plugins/plugin_policy_handler.cc b/chrome/browser/plugins/plugin_policy_handler.cc
index 7d03b3b..0194502 100644
--- a/chrome/browser/plugins/plugin_policy_handler.cc
+++ b/chrome/browser/plugins/plugin_policy_handler.cc
@@ -78,9 +78,9 @@
   bool ok = true;
   for (size_t i = 0; i < arraysize(checked_policies); ++i) {
     const base::Value* value = policies.GetValue(checked_policies[i]);
-    if (value && !value->IsType(base::Value::TYPE_LIST)) {
+    if (value && !value->IsType(base::Value::Type::LIST)) {
       errors->AddError(checked_policies[i], IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_LIST));
+                       base::Value::GetTypeName(base::Value::Type::LIST));
       ok = false;
     }
   }
diff --git a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
index 3f25e5d..cd6db5b 100644
--- a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
@@ -283,7 +284,7 @@
   void SetServerPolicy(const std::string& policy) {
     int result = base::WriteFile(policy_file_path(), policy.data(),
                                  policy.size());
-    ASSERT_EQ(static_cast<int>(policy.size()), result);
+    ASSERT_EQ(base::checked_cast<int>(policy.size()), result);
   }
 
   base::FilePath policy_file_path() const {
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 6697f77..0a97cd64 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/profiles/guest_mode_policy_handler.h"
 #include "chrome/browser/profiles/incognito_mode_policy_handler.h"
 #include "chrome/browser/sessions/restore_on_startup_policy_handler.h"
+#include "chrome/browser/supervised_user/supervised_user_creation_policy_handler.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/features.h"
 #include "chrome/common/pref_names.h"
@@ -100,523 +101,521 @@
 const PolicyToPreferenceMapEntry kSimplePolicyMap[] = {
   { key::kHomepageLocation,
     prefs::kHomePage,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kHomepageIsNewTabPage,
     prefs::kHomePageIsNewTabPage,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kRestoreOnStartupURLs,
     prefs::kURLsToRestoreOnStartup,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kAlternateErrorPagesEnabled,
     prefs::kAlternateErrorPagesEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSearchSuggestEnabled,
     prefs::kSearchSuggestEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kBuiltInDnsClientEnabled,
     prefs::kBuiltInDnsClientEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kWPADQuickCheckEnabled,
     prefs::kQuickCheckEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kPacHttpsUrlStrippingEnabled,
     prefs::kPacHttpsUrlStrippingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSafeBrowsingEnabled,
     prefs::kSafeBrowsingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kForceGoogleSafeSearch,
     prefs::kForceGoogleSafeSearch,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kForceYouTubeRestrict,
     prefs::kForceYouTubeRestrict,
-    base::Value::TYPE_INTEGER},
+    base::Value::Type::INTEGER},
   { key::kPasswordManagerEnabled,
     password_manager::prefs::kPasswordManagerSavingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kPrintingEnabled,
     prefs::kPrintingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDisablePrintPreview,
     prefs::kPrintPreviewDisabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDefaultPrinterSelection,
     prefs::kPrintPreviewDefaultDestinationSelectionRules,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kApplicationLocaleValue,
     prefs::kApplicationLocale,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kAlwaysOpenPdfExternally,
     prefs::kPluginsAlwaysOpenPdfExternally,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kShowHomeButton,
     prefs::kShowHomeButton,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSavingBrowserHistoryDisabled,
     prefs::kSavingBrowserHistoryDisabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowDeletingBrowserHistory,
     prefs::kAllowDeletingBrowserHistory,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDeveloperToolsDisabled,
     prefs::kDevToolsDisabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kBlockThirdPartyCookies,
     prefs::kBlockThirdPartyCookies,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDefaultCookiesSetting,
     prefs::kManagedDefaultCookiesSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultImagesSetting,
     prefs::kManagedDefaultImagesSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultPluginsSetting,
     prefs::kManagedDefaultPluginsSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultPopupsSetting,
     prefs::kManagedDefaultPopupsSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultKeygenSetting,
     prefs::kManagedDefaultKeygenSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kAutoSelectCertificateForUrls,
     prefs::kManagedAutoSelectCertificateForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kCookiesAllowedForUrls,
     prefs::kManagedCookiesAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kCookiesBlockedForUrls,
     prefs::kManagedCookiesBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kCookiesSessionOnlyForUrls,
     prefs::kManagedCookiesSessionOnlyForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kImagesAllowedForUrls,
     prefs::kManagedImagesAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kImagesBlockedForUrls,
     prefs::kManagedImagesBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kJavaScriptAllowedForUrls,
     prefs::kManagedJavaScriptAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kJavaScriptBlockedForUrls,
     prefs::kManagedJavaScriptBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kPluginsAllowedForUrls,
     prefs::kManagedPluginsAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kPluginsBlockedForUrls,
     prefs::kManagedPluginsBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kPopupsAllowedForUrls,
     prefs::kManagedPopupsAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kPopupsBlockedForUrls,
     prefs::kManagedPopupsBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kKeygenAllowedForUrls,
     prefs::kManagedKeygenAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kKeygenBlockedForUrls,
     prefs::kManagedKeygenBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kNotificationsAllowedForUrls,
     prefs::kManagedNotificationsAllowedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kNotificationsBlockedForUrls,
     prefs::kManagedNotificationsBlockedForUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kDefaultNotificationsSetting,
     prefs::kManagedDefaultNotificationsSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultGeolocationSetting,
     prefs::kManagedDefaultGeolocationSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kSigninAllowed,
     prefs::kSigninAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kEnableOnlineRevocationChecks,
     ssl_config::prefs::kCertRevocationCheckingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kRequireOnlineRevocationChecksForLocalAnchors,
     ssl_config::prefs::kCertRevocationCheckingRequiredLocalAnchors,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kEnableSha1ForLocalAnchors,
     ssl_config::prefs::kCertEnableSha1LocalAnchors,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAuthSchemes,
     prefs::kAuthSchemes,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDisableAuthNegotiateCnameLookup,
     prefs::kDisableAuthNegotiateCnameLookup,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kEnableAuthNegotiatePort,
     prefs::kEnableAuthNegotiatePort,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAuthServerWhitelist,
     prefs::kAuthServerWhitelist,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kAuthNegotiateDelegateWhitelist,
     prefs::kAuthNegotiateDelegateWhitelist,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kGSSAPILibraryName,
     prefs::kGSSAPILibraryName,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kAllowCrossOriginAuthPrompt,
     prefs::kAllowCrossOriginAuthPrompt,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDisable3DAPIs,
     prefs::kDisable3DAPIs,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDisablePluginFinder,
     prefs::kDisablePluginFinder,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDiskCacheSize,
     prefs::kDiskCacheSize,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kMediaCacheSize,
     prefs::kMediaCacheSize,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kPolicyRefreshRate,
     policy_prefs::kUserPolicyRefreshRate,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDevicePolicyRefreshRate,
     prefs::kDevicePolicyRefreshRate,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultBrowserSettingEnabled,
     prefs::kDefaultBrowserSettingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kCloudPrintProxyEnabled,
     prefs::kCloudPrintProxyEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kCloudPrintSubmitEnabled,
     prefs::kCloudPrintSubmitEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kTranslateEnabled,
     prefs::kEnableTranslate,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowOutdatedPlugins,
     prefs::kPluginsAllowOutdated,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAlwaysAuthorizePlugins,
     prefs::kPluginsAlwaysAuthorize,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kBookmarkBarEnabled,
     bookmarks::prefs::kShowBookmarkBar,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kEditBookmarksEnabled,
     bookmarks::prefs::kEditBookmarksEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kShowAppsShortcutInBookmarkBar,
     bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowFileSelectionDialogs,
     prefs::kAllowFileSelectionDialogs,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportBookmarks,
     prefs::kImportBookmarks,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportHistory,
     prefs::kImportHistory,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportHomepage,
     prefs::kImportHomepage,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportSearchEngine,
     prefs::kImportSearchEngine,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportSavedPasswords,
     prefs::kImportSavedPasswords,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kImportAutofillFormData,
     prefs::kImportAutofillFormData,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kMaxConnectionsPerProxy,
     prefs::kMaxConnectionsPerProxy,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kURLWhitelist,
     policy_prefs::kUrlWhitelist,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kRestrictSigninToPattern,
     prefs::kGoogleServicesUsernamePattern,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultWebBluetoothGuardSetting,
     prefs::kManagedDefaultWebBluetoothGuardSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDefaultMediaStreamSetting,
     prefs::kManagedDefaultMediaStreamSetting,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kDisableSafeBrowsingProceedAnyway,
     prefs::kSafeBrowsingProceedAnywayDisabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSafeBrowsingExtendedReportingOptInAllowed,
     prefs::kSafeBrowsingExtendedReportingOptInAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSSLErrorOverrideAllowed,
     prefs::kSSLErrorOverrideAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kHardwareAccelerationModeEnabled,
     prefs::kHardwareAccelerationModeEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowDinosaurEasterEgg,
     prefs::kAllowDinosaurEasterEgg,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowedDomainsForApps,
     prefs::kAllowedDomainsForApps,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kComponentUpdatesEnabled,
     prefs::kComponentUpdatesEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
   { key::kSpellCheckServiceEnabled,
     spellcheck::prefs::kSpellCheckUseSpellingService,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // BUILDFLAG(ENABLE_SPELLCHECK)
 
   { key::kDisableScreenshots,
     prefs::kDisableScreenshots,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAudioCaptureAllowed,
     prefs::kAudioCaptureAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kVideoCaptureAllowed,
     prefs::kVideoCaptureAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAudioCaptureAllowedUrls,
     prefs::kAudioCaptureAllowedUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kVideoCaptureAllowedUrls,
     prefs::kVideoCaptureAllowedUrls,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kHideWebStoreIcon,
     prefs::kHideWebStoreIcon,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kVariationsRestrictParameter,
     variations::prefs::kVariationsRestrictParameter,
-    base::Value::TYPE_STRING },
-  { key::kSupervisedUserCreationEnabled,
-    prefs::kSupervisedUserCreationAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::STRING },
   { key::kForceEphemeralProfiles,
     prefs::kForceEphemeralProfiles,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDHEEnabled,
     ssl_config::prefs::kDHEEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kNTPContentSuggestionsEnabled,
     ntp_snippets::prefs::kEnableSnippets,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #if defined(ENABLE_MEDIA_ROUTER)
   { key::kEnableMediaRouter,
     prefs::kEnableMediaRouter,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // defined(ENABLE_MEDIA_ROUTER)
 #if BUILDFLAG(ENABLE_WEBRTC)
   { key::kWebRtcUdpPortRange,
     prefs::kWebRTCUDPPortRange,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
 #endif  // BUILDFLAG(ENABLE_WEBRTC)
 #if !defined(OS_MACOSX)
   { key::kFullscreenAllowed,
     prefs::kFullscreenAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   { key::kFullscreenAllowed,
     extensions::pref_names::kAppFullscreenAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 #endif  // !defined(OS_MACOSX)
 
 #if defined(OS_CHROMEOS)
   { key::kChromeOsLockOnIdleSuspend,
     prefs::kEnableAutoScreenLock,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kChromeOsReleaseChannel,
     prefs::kChromeOsReleaseChannel,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDriveDisabled,
     drive::prefs::kDisableDrive,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDriveDisabledOverCellular,
     drive::prefs::kDisableDriveOverCellular,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kExternalStorageDisabled,
     prefs::kExternalStorageDisabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kExternalStorageReadOnly,
     prefs::kExternalStorageReadOnly,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAudioOutputAllowed,
     chromeos::prefs::kAudioOutputAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kShowLogoutButtonInTray,
     prefs::kShowLogoutButtonInTray,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kShelfAutoHideBehavior,
     prefs::kShelfAutoHideBehaviorLocal,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kSessionLengthLimit,
     prefs::kSessionLengthLimit,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
   { key::kWaitForInitialUserActivity,
     prefs::kSessionWaitForInitialUserActivity,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kPowerManagementUsesAudioActivity,
     prefs::kPowerUseAudioActivity,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kPowerManagementUsesVideoActivity,
     prefs::kPowerUseVideoActivity,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAllowScreenWakeLocks,
     prefs::kPowerAllowScreenWakeLocks,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kWaitForInitialUserActivity,
     prefs::kPowerWaitForInitialUserActivity,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kTermsOfServiceURL,
     prefs::kTermsOfServiceURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kShowAccessibilityOptionsInSystemTrayMenu,
     prefs::kShouldAlwaysShowAccessibilityMenu,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kLargeCursorEnabled,
     prefs::kAccessibilityLargeCursorEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kSpokenFeedbackEnabled,
     prefs::kAccessibilitySpokenFeedbackEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kHighContrastEnabled,
     prefs::kAccessibilityHighContrastEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kVirtualKeyboardEnabled,
     prefs::kAccessibilityVirtualKeyboardEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultLargeCursorEnabled,
     NULL,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultSpokenFeedbackEnabled,
     NULL,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultHighContrastEnabled,
     NULL,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDeviceLoginScreenDefaultVirtualKeyboardEnabled,
     NULL,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kRebootAfterUpdate,
     prefs::kRebootAfterUpdate,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAttestationEnabledForUser,
     prefs::kAttestationEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kChromeOsMultiProfileUserBehavior,
     prefs::kMultiProfileUserBehavior,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kKeyboardDefaultToFunctionKeys,
     prefs::kLanguageSendFunctionKeys,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kTouchVirtualKeyboardEnabled,
     prefs::kTouchVirtualKeyboardEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kEasyUnlockAllowed,
     prefs::kEasyUnlockAllowed,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kCaptivePortalAuthenticationIgnoresProxy,
     prefs::kCaptivePortalAuthenticationIgnoresProxy,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kForceMaximizeOnFirstRun,
     prefs::kForceMaximizeOnFirstRun,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kUnifiedDesktopEnabledByDefault,
     prefs::kUnifiedDesktopEnabledByDefault,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kArcEnabled,
     prefs::kArcEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kArcBackupRestoreEnabled,
     prefs::kArcBackupRestoreEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kReportArcStatusEnabled,
     prefs::kReportArcStatusEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kNativePrinters,
     prefs::kRecommendedNativePrinters,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
 #endif  // defined(OS_CHROMEOS)
 
 // Metrics reporting is controlled by a platform specific policy for ChromeOS
 #if defined(OS_CHROMEOS)
   { key::kDeviceMetricsReportingEnabled,
     metrics::prefs::kMetricsReportingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #else
   { key::kMetricsReportingEnabled,
     metrics::prefs::kMetricsReportingEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif
 
 #if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
   { key::kBackgroundModeEnabled,
     prefs::kBackgroundModeEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
   { key::kDataCompressionProxyEnabled,
     prefs::kDataSaverEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kAuthAndroidNegotiateAccountType,
     prefs::kAuthAndroidNegotiateAccountType,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
 #endif  // BUILDFLAG(ANDROID_JAVA_UI)
 
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
   { key::kNativeMessagingUserLevelHosts,
     extensions::pref_names::kNativeMessagingUserLevelHosts,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kBrowserAddPersonEnabled,
     prefs::kBrowserAddPersonEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kForceBrowserSignin,
     prefs::kForceBrowserSignin,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 
 #if defined(OS_WIN)
   { key::kWelcomePageOnOSUpgradeEnabled,
     prefs::kWelcomePageOnOSUpgradeEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // OS_WIN
 
 #if !defined(OS_ANDROID)
   { key::kSuppressUnsupportedOSWarning,
     prefs::kSuppressUnsupportedOSWarning,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 #endif  // !OS_ANDROID
 
 #if defined(OS_CHROMEOS)
   { key::kSystemTimezoneAutomaticDetection,
     prefs::kSystemTimezoneAutomaticDetectionPolicy,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
 #endif
 
   { key::kTaskManagerEndProcessEnabled,
     prefs::kTaskManagerEndProcessEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
 
 #if defined(OS_CHROMEOS)
   { key::kNetworkThrottlingEnabled,
     prefs::kNetworkThrottlingEnabled,
-    base::Value::TYPE_DICTIONARY },
+    base::Value::Type::DICTIONARY },
 
-  { key::kAllowScreenLock, prefs::kAllowScreenLock, base::Value::TYPE_BOOLEAN },
+  { key::kAllowScreenLock, prefs::kAllowScreenLock,
+    base::Value::Type::BOOLEAN },
 
   { key::kQuickUnlockModeWhitelist, prefs::kQuickUnlockModeWhitelist,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kQuickUnlockTimeout, prefs::kQuickUnlockTimeout,
-    base::Value::TYPE_INTEGER },
+    base::Value::Type::INTEGER },
 #endif
 };
 
@@ -624,7 +623,7 @@
  public:
   ForceSafeSearchPolicyHandler()
       : TypeCheckingPolicyHandler(key::kForceSafeSearch,
-                                  base::Value::TYPE_BOOLEAN) {}
+                                  base::Value::Type::BOOLEAN) {}
   ~ForceSafeSearchPolicyHandler() override {}
 
   // ConfigurationPolicyHandler implementation:
@@ -665,7 +664,7 @@
  public:
   ForceYouTubeSafetyModePolicyHandler()
       : TypeCheckingPolicyHandler(key::kForceYouTubeSafetyMode,
-                                  base::Value::TYPE_BOOLEAN) {}
+                                  base::Value::Type::BOOLEAN) {}
   ~ForceYouTubeSafetyModePolicyHandler() override {}
 
   // ConfigurationPolicyHandler implementation:
@@ -806,6 +805,7 @@
       base::MakeUnique<extensions::NativeMessagingHostListPolicyHandler>(
           key::kNativeMessagingBlacklist,
           extensions::pref_names::kNativeMessagingBlacklist, true));
+  handlers->AddHandler(base::MakeUnique<SupervisedUserCreationPolicyHandler>());
 #endif  // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/policy/file_selection_dialogs_policy_handler.cc b/chrome/browser/policy/file_selection_dialogs_policy_handler.cc
index b2815e9..1a304928 100644
--- a/chrome/browser/policy/file_selection_dialogs_policy_handler.cc
+++ b/chrome/browser/policy/file_selection_dialogs_policy_handler.cc
@@ -14,7 +14,7 @@
 
 FileSelectionDialogsPolicyHandler::FileSelectionDialogsPolicyHandler()
     : TypeCheckingPolicyHandler(key::kAllowFileSelectionDialogs,
-                                base::Value::TYPE_BOOLEAN) {}
+                                base::Value::Type::BOOLEAN) {}
 
 FileSelectionDialogsPolicyHandler::~FileSelectionDialogsPolicyHandler() {}
 
diff --git a/chrome/browser/policy/javascript_policy_handler.cc b/chrome/browser/policy/javascript_policy_handler.cc
index edd65f43..8f2194a 100644
--- a/chrome/browser/policy/javascript_policy_handler.cc
+++ b/chrome/browser/policy/javascript_policy_handler.cc
@@ -27,14 +27,14 @@
       policies.GetValue(key::kDefaultJavaScriptSetting);
 
   if (javascript_enabled &&
-      !javascript_enabled->IsType(base::Value::TYPE_BOOLEAN)) {
+      !javascript_enabled->IsType(base::Value::Type::BOOLEAN)) {
     errors->AddError(key::kJavascriptEnabled, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_BOOLEAN));
+                     base::Value::GetTypeName(base::Value::Type::BOOLEAN));
   }
 
-  if (default_setting && !default_setting->IsType(base::Value::TYPE_INTEGER)) {
+  if (default_setting && !default_setting->IsType(base::Value::Type::INTEGER)) {
     errors->AddError(key::kDefaultJavaScriptSetting, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_INTEGER));
+                     base::Value::GetTypeName(base::Value::Type::INTEGER));
   }
 
   if (javascript_enabled && default_setting) {
diff --git a/chrome/browser/policy/network_prediction_policy_handler.cc b/chrome/browser/policy/network_prediction_policy_handler.cc
index 0276e2e7..793dbb6 100644
--- a/chrome/browser/policy/network_prediction_policy_handler.cc
+++ b/chrome/browser/policy/network_prediction_policy_handler.cc
@@ -32,15 +32,15 @@
       policies.GetValue(key::kNetworkPredictionOptions);
 
   if (network_prediction_enabled &&
-      !network_prediction_enabled->IsType(base::Value::TYPE_BOOLEAN)) {
+      !network_prediction_enabled->IsType(base::Value::Type::BOOLEAN)) {
     errors->AddError(key::kDnsPrefetchingEnabled, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_BOOLEAN));
+                     base::Value::GetTypeName(base::Value::Type::BOOLEAN));
   }
 
   if (network_prediction_options &&
-      !network_prediction_options->IsType(base::Value::TYPE_INTEGER)) {
+      !network_prediction_options->IsType(base::Value::Type::INTEGER)) {
     errors->AddError(key::kNetworkPredictionOptions, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_INTEGER));
+                     base::Value::GetTypeName(base::Value::Type::INTEGER));
   }
 
   if (network_prediction_enabled && network_prediction_options) {
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index d89f788..5d9eaa9c 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -468,7 +468,7 @@
       content::ExecuteScriptAndGetValue(contents->GetMainFrame(), "123");
   int result = 0;
   if (!value->GetAsInteger(&result))
-    EXPECT_EQ(base::Value::TYPE_NULL, value->GetType());
+    EXPECT_EQ(base::Value::Type::NONE, value->GetType());
   return result == 123;
 }
 
diff --git a/chrome/browser/policy/test/local_policy_test_server.cc b/chrome/browser/policy/test/local_policy_test_server.cc
index d2fa7db1..a3bf21a 100644
--- a/chrome/browser/policy/test/local_policy_test_server.cc
+++ b/chrome/browser/policy/test/local_policy_test_server.cc
@@ -13,6 +13,7 @@
 #include "base/base_paths.h"
 #include "base/files/file_util.h"
 #include "base/json/json_writer.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
@@ -101,7 +102,7 @@
       policy_key_, reinterpret_cast<const char*>(signing_key_bits.data()),
       signing_key_bits.size());
 
-  if (bytes_written != static_cast<int>(signing_key_bits.size()))
+  if (bytes_written != base::checked_cast<int>(signing_key_bits.size()))
     return false;
 
   // Write the signature data.
@@ -112,7 +113,7 @@
       signature.c_str(),
       signature.size());
 
-  return bytes_written == static_cast<int>(signature.size());
+  return bytes_written == base::checked_cast<int>(signature.size());
 }
 
 void LocalPolicyTestServer::EnableAutomaticRotationOfSigningKeys() {
@@ -152,7 +153,7 @@
       base::StringPrintf("policy_%s.bin", selector.c_str()));
 
   return base::WriteFile(policy_file, policy.c_str(), policy.size()) ==
-      static_cast<int>(policy.size());
+         base::checked_cast<int>(policy.size());
 }
 
 bool LocalPolicyTestServer::UpdatePolicyData(const std::string& type,
@@ -165,7 +166,7 @@
       base::StringPrintf("policy_%s.data", selector.c_str()));
 
   return base::WriteFile(data_file, data.c_str(), data.size()) ==
-      static_cast<int>(data.size());
+         base::checked_cast<int>(data.size());
 }
 
 GURL LocalPolicyTestServer::GetServiceURL() const {
@@ -255,7 +256,7 @@
       base::FilePath client_state_file =
           server_data_dir_.GetPath().Append(kClientStateFileName);
       if (base::WriteFile(client_state_file, json.c_str(), json.size()) !=
-          static_cast<int>(json.size())) {
+          base::checked_cast<int>(json.size())) {
         return false;
       }
       arguments->SetString("client-state", client_state_file.AsUTF8Unsafe());
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 2f95eb44..95e7e7d 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -93,7 +93,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/ssl_config/ssl_config_service_manager.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
@@ -363,7 +363,7 @@
   PrefProxyConfigTrackerImpl::RegisterPrefs(registry);
   ProfileInfoCache::RegisterPrefs(registry);
   profiles::RegisterPrefs(registry);
-  rappor::RapporService::RegisterPrefs(registry);
+  rappor::RapporServiceImpl::RegisterPrefs(registry);
   RegisterScreenshotPrefs(registry);
   SigninManagerFactory::RegisterPrefs(registry);
   ssl_config::SSLConfigServiceManager::RegisterPrefs(registry);
@@ -490,6 +490,7 @@
   MediaCaptureDevicesDispatcher::RegisterProfilePrefs(registry);
   MediaDeviceIDSalt::RegisterProfilePrefs(registry);
   MediaStreamDevicesController::RegisterProfilePrefs(registry);
+  NotifierStateTracker::RegisterProfilePrefs(registry);
   ntp_snippets::BookmarkSuggestionsProvider::RegisterProfilePrefs(registry);
   ntp_snippets::ForeignSessionsSuggestionsProvider::RegisterProfilePrefs(
       registry);
@@ -529,12 +530,7 @@
   extensions::RuntimeAPI::RegisterPrefs(registry);
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-#if defined(ENABLE_NOTIFICATIONS)
-  NotifierStateTracker::RegisterProfilePrefs(registry);
-#endif
-
-#if defined(ENABLE_NOTIFICATIONS) && BUILDFLAG(ENABLE_EXTENSIONS) && \
-    !defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_ANDROID)
   // The extension welcome notification requires a build that enables extensions
   // and notifications, and uses the UI message center.
   ExtensionWelcomeNotification::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
index 972232b..50547cf 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
@@ -37,7 +37,7 @@
   void VerifyProxyMode(ProxyPrefs::ProxyMode expected_mode) {
     const base::Value* value = NULL;
     ASSERT_TRUE(GetValue(proxy_config::prefs::kProxy, &value));
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
     ProxyConfigDictionary dict(
         static_cast<const base::DictionaryValue*>(value));
     ProxyPrefs::ProxyMode actual_mode;
@@ -49,7 +49,7 @@
                              size_t cipher_count) {
     const base::Value* value = NULL;
     ASSERT_TRUE(GetValue(ssl_config::prefs::kCipherSuiteBlacklist, &value));
-    ASSERT_EQ(base::Value::TYPE_LIST, value->GetType());
+    ASSERT_EQ(base::Value::Type::LIST, value->GetType());
     const base::ListValue* list_value =
         static_cast<const base::ListValue*>(value);
     ASSERT_EQ(cipher_count, list_value->GetSize());
@@ -121,7 +121,7 @@
 
   const base::Value* value = NULL;
   ASSERT_TRUE(store->GetValue(proxy_config::prefs::kProxy, &value));
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
   ProxyConfigDictionary dict(static_cast<const base::DictionaryValue*>(value));
 
   std::string string_result;
diff --git a/chrome/browser/prefs/chrome_pref_service_unittest.cc b/chrome/browser/prefs/chrome_pref_service_unittest.cc
index 214b864..31067d3 100644
--- a/chrome/browser/prefs/chrome_pref_service_unittest.cc
+++ b/chrome/browser/prefs/chrome_pref_service_unittest.cc
@@ -27,7 +27,7 @@
   ASSERT_TRUE(pref);
   const base::Value* value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, value->GetType());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, value->GetType());
   bool actual_bool_value = true;
   EXPECT_TRUE(value->GetAsBoolean(&actual_bool_value));
   EXPECT_FALSE(actual_bool_value);
@@ -42,7 +42,7 @@
   ASSERT_TRUE(pref);
   value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, value->GetType());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, value->GetType());
   actual_bool_value = false;
   EXPECT_TRUE(value->GetAsBoolean(&actual_bool_value));
   EXPECT_TRUE(actual_bool_value);
diff --git a/chrome/browser/prefs/pref_metrics_service.cc b/chrome/browser/prefs/pref_metrics_service.cc
index 1b99ad2..f9c4813 100644
--- a/chrome/browser/prefs/pref_metrics_service.cc
+++ b/chrome/browser/prefs/pref_metrics_service.cc
@@ -23,7 +23,8 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_preferences/synced_pref_change_registrar.h"
diff --git a/chrome/browser/prefs/pref_service_browsertest.cc b/chrome/browser/prefs/pref_service_browsertest.cc
index 8097862..8f1eec42 100644
--- a/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chrome/browser/prefs/pref_service_browsertest.cc
@@ -90,7 +90,7 @@
   std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, NULL);
 
   ASSERT_TRUE(root.get());
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
 
   base::DictionaryValue* root_dict =
       static_cast<base::DictionaryValue*>(root.get());
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index f77616a6..ea7def8 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -117,7 +117,7 @@
     ADD_FAILURE() << "Error #" << error_code << ": " << error_str;
     return std::unique_ptr<base::DictionaryValue>();
   }
-  if (!prefs->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!prefs->IsType(base::Value::Type::DICTIONARY)) {
     ADD_FAILURE();
     return std::unique_ptr<base::DictionaryValue>();
   }
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 7cc9c62f..345bfaf 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -574,10 +574,10 @@
                                 expected_number_of_loads);
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    PrerenderInProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kPrerenderMode,
-                                    switches::kPrerenderModeSwitchValueEnabled);
+  void SetUpOnMainThread() override {
+    test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
+    prerender::PrerenderManager::SetMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
   }
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -2625,7 +2625,6 @@
   void SetUp() override { PrerenderBrowserTest::SetUp(); }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    PrerenderBrowserTest::SetUpCommandLine(command_line);
     ExtensionApiTest::SetUpCommandLine(command_line);
   }
 
@@ -3297,7 +3296,6 @@
   ~PrerenderBrowserTestWithNaCl() override {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    PrerenderBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kEnableNaCl);
   }
 };
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 7df655f..f7c11cf 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -70,7 +70,7 @@
     std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) {
   for (size_t i = 0; i < throttles.size(); i++) {
     if (throttles[i])
-      throttles[i]->Resume();
+      throttles[i]->ResumeHandler();
   }
 }
 
diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc
index a726394b..8857b8c 100644
--- a/chrome/browser/prerender/prerender_field_trial.cc
+++ b/chrome/browser/prerender/prerender_field_trial.cc
@@ -6,37 +6,45 @@
 
 #include <string>
 
-#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/common/chrome_switches.h"
+#include "components/variations/variations_associated_data.h"
 
 namespace prerender {
 
-void ConfigurePrerender(const base::CommandLine& command_line) {
+// NoStatePrefetch feature modes, to control the PrerenderManager mode using
+// the base::Feature API and field trials.
+const char kNoStatePrefetchFeatureModeParameterName[] = "mode";
+const char kNoStatePrefetchFeatureModeParameterPrefetch[] = "no_state_prefetch";
+const char kNoStatePrefetchFeatureModeParameterPrerender[] = "prerender";
+const char kNoStatePrefetchFeatureModeParameterSimpleLoad[] = "simple_load";
+
+const base::Feature kNoStatePrefetchFeature{"NoStatePrefetch",
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
+
+void ConfigurePrerender() {
   PrerenderManager::PrerenderManagerMode mode =
       PrerenderManager::PRERENDER_MODE_ENABLED;
-  if (command_line.HasSwitch(switches::kPrerenderMode)) {
-    const std::string switch_value =
-        command_line.GetSwitchValueASCII(switches::kPrerenderMode);
-
-    if (switch_value == switches::kPrerenderModeSwitchValueDisabled) {
-      mode = PrerenderManager::PRERENDER_MODE_DISABLED;
-    } else if (switch_value == switches::kPrerenderModeSwitchValuePrefetch) {
+  if (!base::FeatureList::IsEnabled(kNoStatePrefetchFeature)) {
+    mode = PrerenderManager::PRERENDER_MODE_DISABLED;
+  } else {
+    std::string mode_value = variations::GetVariationParamValueByFeature(
+        kNoStatePrefetchFeature, kNoStatePrefetchFeatureModeParameterName);
+    if (mode_value == kNoStatePrefetchFeatureModeParameterPrefetch) {
       mode = PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH;
-    } else if (switch_value.empty() ||
-               switch_value == switches::kPrerenderModeSwitchValueEnabled) {
+    } else if (mode_value.empty() ||
+               mode_value == kNoStatePrefetchFeatureModeParameterPrerender) {
       // The empty string means the option was provided with no value, and that
       // means enable.
       mode = PrerenderManager::PRERENDER_MODE_ENABLED;
-    } else if (switch_value == switches::kPrerenderModeSwitchValueSimpleLoad) {
+    } else if (mode_value == kNoStatePrefetchFeatureModeParameterSimpleLoad) {
       mode = PrerenderManager::PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT;
     } else {
-      mode = PrerenderManager::PRERENDER_MODE_DISABLED;
-      LOG(ERROR) << "Invalid --prerender option received on command line: "
-                 << switch_value;
+      LOG(ERROR) << "Invalid prerender mode: " << mode_value;
       LOG(ERROR) << "Disabling prerendering!";
+      mode = PrerenderManager::PRERENDER_MODE_DISABLED;
     }
   }
 
diff --git a/chrome/browser/prerender/prerender_field_trial.h b/chrome/browser/prerender/prerender_field_trial.h
index 4480732e..1e1e8e6 100644
--- a/chrome/browser/prerender/prerender_field_trial.h
+++ b/chrome/browser/prerender/prerender_field_trial.h
@@ -5,18 +5,20 @@
 #ifndef CHROME_BROWSER_PRERENDER_PRERENDER_FIELD_TRIAL_H_
 #define CHROME_BROWSER_PRERENDER_PRERENDER_FIELD_TRIAL_H_
 
-class Profile;
+#include "base/feature_list.h"
 
-namespace base {
-class CommandLine;
-}
+class Profile;
 
 namespace prerender {
 
-// Parse the --prerender= command line switch, which controls prerendering. If
-// the switch is unset or is set to "auto" then the user is assigned to a
-// field trial.
-void ConfigurePrerender(const base::CommandLine& command_line);
+// Exposed for testing.
+extern const base::Feature kNoStatePrefetchFeature;
+extern const char kNoStatePrefetchFeatureModeParameterName[];
+extern const char kNoStatePrefetchFeatureModeParameterSimpleLoad[];
+
+// Configures prerender using kNoStatePrefetchFeature and field trial
+// parameters.
+void ConfigurePrerender();
 
 // Returns true if the user has opted in or has been opted in to the
 // prerendering from Omnibox experiment.
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 7b4f765c..44be880 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -20,7 +20,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/escape.h"
@@ -59,10 +58,10 @@
  public:
   NoStatePrefetchBrowserTest() {}
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    PrerenderInProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(
-        switches::kPrerenderMode, switches::kPrerenderModeSwitchValuePrefetch);
+  void SetUpOnMainThread() override {
+    test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
+    PrerenderManager::SetMode(
+        PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
   }
 
   // Set up a request counter for |path|, which is also the location of the data
diff --git a/chrome/browser/prerender/prerender_resource_throttle.cc b/chrome/browser/prerender/prerender_resource_throttle.cc
index e5812b7..8efbcfe1 100644
--- a/chrome/browser/prerender/prerender_resource_throttle.cc
+++ b/chrome/browser/prerender/prerender_resource_throttle.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/prerender/prerender_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents.h"
 #include "net/http/http_response_headers.h"
@@ -140,12 +139,8 @@
   return "PrerenderResourceThrottle";
 }
 
-void PrerenderResourceThrottle::Resume() {
-  controller()->Resume();
-}
-
-void PrerenderResourceThrottle::Cancel() {
-  controller()->Cancel();
+void PrerenderResourceThrottle::ResumeHandler() {
+  Resume();
 }
 
 // static
@@ -194,7 +189,7 @@
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(cancel ? &PrerenderResourceThrottle::Cancel
-                        : &PrerenderResourceThrottle::Resume,
+                        : &PrerenderResourceThrottle::ResumeHandler,
                  throttle));
 }
 
@@ -241,7 +236,7 @@
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(cancel ? &PrerenderResourceThrottle::Cancel
-                        : &PrerenderResourceThrottle::Resume,
+                        : &PrerenderResourceThrottle::ResumeHandler,
                  throttle));
 }
 
diff --git a/chrome/browser/prerender/prerender_resource_throttle.h b/chrome/browser/prerender/prerender_resource_throttle.h
index 7e097b9..472dca6d 100644
--- a/chrome/browser/prerender/prerender_resource_throttle.h
+++ b/chrome/browser/prerender/prerender_resource_throttle.h
@@ -46,15 +46,11 @@
 
   // Called by the PrerenderContents when a prerender becomes visible.
   // May only be called if currently throttling the resource.
-  void Resume();
+  void ResumeHandler();
 
   static void OverridePrerenderContentsForTesting(PrerenderContents* contents);
 
  private:
-  // Helper method to cancel the request. May only be called if currently
-  // throttling the resource.
-  void Cancel();
-
   static void WillStartRequestOnUI(
       const base::WeakPtr<PrerenderResourceThrottle>& throttle,
       const std::string& method,
diff --git a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
index 10d2256..35479f3 100644
--- a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
+++ b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
@@ -16,8 +16,8 @@
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_resource_throttle.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
+#include "content/public/browser/resource_throttle.h"
 #include "content/public/test/test_browser_thread.h"
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
@@ -91,7 +91,7 @@
 };
 
 class DeferredRedirectDelegate : public net::URLRequest::Delegate,
-                                 public content::ResourceController {
+                                 public content::ResourceThrottle::Delegate {
  public:
   DeferredRedirectDelegate()
       : throttle_(NULL),
@@ -102,7 +102,7 @@
 
   void SetThrottle(PrerenderResourceThrottle* throttle) {
     throttle_ = throttle;
-    throttle_->set_controller_for_testing(this);
+    throttle_->set_delegate_for_testing(this);
   }
 
   void Run() {
@@ -128,7 +128,7 @@
   void OnResponseStarted(net::URLRequest* request, int net_error) override {}
   void OnReadCompleted(net::URLRequest* request, int bytes_read) override {}
 
-  // content::ResourceController implementation:
+  // content::ResourceThrottle::Delegate implementation:
   void Cancel() override {
     EXPECT_FALSE(cancel_called_);
     EXPECT_FALSE(resume_called_);
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 68d6c5bb..9820d3f 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -14,7 +14,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
@@ -32,6 +34,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/test_browser_thread.h"
 #include "net/base/network_change_notifier.h"
@@ -411,12 +414,53 @@
 TEST_F(PrerenderTest, PrerenderRespectsDisableFlag) {
   RestorePrerenderMode restore_prerender_mode;
   ASSERT_TRUE(PrerenderManager::IsPrerenderingPossible());
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  command_line->AppendSwitchASCII(
-    switches::kPrerenderMode,
-    switches::kPrerenderModeSwitchValueDisabled);
-  prerender::ConfigurePrerender(*command_line);
-  ASSERT_FALSE(PrerenderManager::IsPrerenderingPossible());
+  ASSERT_EQ(PrerenderManager::PRERENDER_MODE_ENABLED,
+            PrerenderManager::GetMode());
+
+  {
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitAndDisableFeature(kNoStatePrefetchFeature);
+    prerender::ConfigurePrerender();
+    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
+              PrerenderManager::GetMode());
+    EXPECT_FALSE(PrerenderManager::IsPrerenderingPossible());
+  }
+}
+
+TEST_F(PrerenderTest, PrerenderRespectsFieldTrialParameters) {
+  RestorePrerenderMode restore_prerender_mode;
+
+  // Set up the prerender mode through a field trial.
+  std::string kTrialName = "name";
+  std::string kTrialGroup = "group";
+  base::FieldTrial* trial =
+      base::FieldTrialList::CreateFieldTrial(kTrialName, kTrialGroup);
+  std::map<std::string, std::string> params = {
+      {kNoStatePrefetchFeatureModeParameterName,
+       kNoStatePrefetchFeatureModeParameterSimpleLoad}};
+  ASSERT_TRUE(
+      variations::AssociateVariationParams(kTrialName, kTrialGroup, params));
+
+  std::unique_ptr<base::FeatureList> feature_list =
+      base::MakeUnique<base::FeatureList>();
+  feature_list->RegisterFieldTrialOverride(
+      kNoStatePrefetchFeature.name, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+      trial);
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
+
+  EXPECT_EQ(base::FeatureList::GetFieldTrial(kNoStatePrefetchFeature), trial);
+
+  std::map<std::string, std::string> actual_params;
+  EXPECT_TRUE(variations::GetVariationParamsByFeature(kNoStatePrefetchFeature,
+                                                      &actual_params));
+  EXPECT_EQ(params, actual_params);
+
+  prerender::ConfigurePrerender();
+  EXPECT_TRUE(PrerenderManager::IsPrerenderingPossible());
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT,
+            PrerenderManager::GetMode());
 }
 
 TEST_F(PrerenderTest, PrerenderRespectsThirdPartyCookiesPref) {
diff --git a/chrome/browser/previews/previews_infobar_tab_helper_unittest.cc b/chrome/browser/previews/previews_infobar_tab_helper_unittest.cc
index 1a7651dc..8464fcbf 100644
--- a/chrome/browser/previews/previews_infobar_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_infobar_tab_helper_unittest.cc
@@ -17,8 +17,8 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/request_header/offline_page_header.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/request_header/offline_page_header.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/chrome/browser/profile_resetter/brandcoded_default_settings.cc b/chrome/browser/profile_resetter/brandcoded_default_settings.cc
index 7e600f8..b213ee01 100644
--- a/chrome/browser/profile_resetter/brandcoded_default_settings.cc
+++ b/chrome/browser/profile_resetter/brandcoded_default_settings.cc
@@ -23,7 +23,7 @@
       VLOG(1) << "Failed to parse brandcode prefs file: " << error;
       return;
     }
-    if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!root->IsType(base::Value::Type::DICTIONARY)) {
       VLOG(1) << "Failed to parse brandcode prefs file: "
               << "Root item must be a dictionary.";
       return;
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 c4f48eb..9e971b2 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -214,12 +214,10 @@
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
   CrossDevicePromoFactory::GetInstance();
 #endif
-#if defined(ENABLE_NOTIFICATIONS)
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionWelcomeNotificationFactory::GetInstance();
 #endif
   NotifierStateTrackerFactory::GetInstance();
-#endif  // defined(ENABLE_NOTIFICATIONS)
   data_use_measurement::ChromeDataUseAscriberServiceFactory::GetInstance();
   dom_distiller::DomDistillerServiceFactory::GetInstance();
   domain_reliability::DomainReliabilityServiceFactory::GetInstance();
diff --git a/chrome/browser/profiles/guest_mode_policy_handler.cc b/chrome/browser/profiles/guest_mode_policy_handler.cc
index 151cf07..5d40424c 100644
--- a/chrome/browser/profiles/guest_mode_policy_handler.cc
+++ b/chrome/browser/profiles/guest_mode_policy_handler.cc
@@ -14,7 +14,7 @@
 
 GuestModePolicyHandler::GuestModePolicyHandler()
     : TypeCheckingPolicyHandler(key::kBrowserGuestModeEnabled,
-                                base::Value::TYPE_BOOLEAN) {}
+                                base::Value::Type::BOOLEAN) {}
 
 GuestModePolicyHandler::~GuestModePolicyHandler() {}
 
diff --git a/chrome/browser/profiles/incognito_mode_policy_handler.cc b/chrome/browser/profiles/incognito_mode_policy_handler.cc
index 57afa036..224af9d 100644
--- a/chrome/browser/profiles/incognito_mode_policy_handler.cc
+++ b/chrome/browser/profiles/incognito_mode_policy_handler.cc
@@ -29,7 +29,7 @@
     int int_value = IncognitoModePrefs::ENABLED;
     if (!availability->GetAsInteger(&int_value)) {
       errors->AddError(key::kIncognitoModeAvailability, IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_INTEGER));
+                       base::Value::GetTypeName(base::Value::Type::INTEGER));
       return false;
     }
     IncognitoModePrefs::Availability availability_enum_value;
@@ -46,9 +46,9 @@
   const base::Value* deprecated_enabled =
       policies.GetValue(key::kIncognitoEnabled);
   if (deprecated_enabled &&
-      !deprecated_enabled->IsType(base::Value::TYPE_BOOLEAN)) {
+      !deprecated_enabled->IsType(base::Value::Type::BOOLEAN)) {
     errors->AddError(key::kIncognitoEnabled, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_BOOLEAN));
+                     base::Value::GetTypeName(base::Value::Type::BOOLEAN));
     return false;
   }
   return true;
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 1520bd36..4167a9c 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -907,11 +907,6 @@
   return enable_metrics_.GetValue();
 }
 
-bool ProfileIOData::IsDataReductionProxyEnabled() const {
-  return data_reduction_proxy_io_data() &&
-      data_reduction_proxy_io_data()->IsEnabled();
-}
-
 chrome_browser_net::Predictor* ProfileIOData::GetPredictor() {
   return nullptr;
 }
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 888c97f..adfdefc 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -247,8 +247,6 @@
     client_cert_store_factory_ = factory;
   }
 
-  bool IsDataReductionProxyEnabled() const;
-
   data_reduction_proxy::DataReductionProxyIOData*
   data_reduction_proxy_io_data() const {
     return data_reduction_proxy_io_data_.get();
diff --git a/chrome/browser/profiles/profile_statistics_aggregator.cc b/chrome/browser/profiles/profile_statistics_aggregator.cc
index 10b9728..e3582be 100644
--- a/chrome/browser/profiles/profile_statistics_aggregator.cc
+++ b/chrome/browser/profiles/profile_statistics_aggregator.cc
@@ -225,7 +225,7 @@
       const PrefService::Preference* pref = pref_service->
                                                 FindPreference(it.key());
       // Skip all dictionaries (which must be empty by the function call above).
-      if (it.value().GetType() != base::Value::TYPE_DICTIONARY &&
+      if (it.value().GetType() != base::Value::Type::DICTIONARY &&
         pref && pref->IsUserControlled() && !pref->IsDefaultValue()) {
         ++count;
       }
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 1efd1a3..7208af9 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -111,9 +111,7 @@
     https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
     ASSERT_TRUE(https_server_->Start());
 
-#if defined(ENABLE_NOTIFICATIONS)
     notification_manager_.reset(new StubNotificationUIManager);
-#endif
 
     InProcessBrowserTest::SetUp();
   }
@@ -135,12 +133,10 @@
 
     push_service_ =
         PushMessagingServiceFactory::GetForProfile(GetBrowser()->profile());
-#if defined(ENABLE_NOTIFICATIONS)
     display_service_.reset(new MessageCenterDisplayService(
         GetBrowser()->profile(), notification_manager_.get()));
     notification_service()->SetNotificationDisplayServiceForTesting(
         display_service_.get());
-#endif
 
     LoadTestPage();
     InProcessBrowserTest::SetUpOnMainThread();
@@ -174,10 +170,7 @@
 
   // InProcessBrowserTest:
   void TearDown() override {
-#if defined(ENABLE_NOTIFICATIONS)
     notification_service()->SetNotificationDisplayServiceForTesting(nullptr);
-#endif
-
     InProcessBrowserTest::TearDown();
   }
 
@@ -240,7 +233,6 @@
 
   net::EmbeddedTestServer* https_server() const { return https_server_.get(); }
 
-#if defined(ENABLE_NOTIFICATIONS)
   // To be called when delivery of a push message has finished. The |run_loop|
   // will be told to quit after |messages_required| messages were received.
   void OnDeliveryFinished(std::vector<size_t>* number_of_notifications_shown,
@@ -260,7 +252,6 @@
   PlatformNotificationServiceImpl* notification_service() const {
     return PlatformNotificationServiceImpl::GetInstance();
   }
-#endif
 
   PushMessagingServiceImpl* push_service() const { return push_service_; }
 
@@ -287,10 +278,8 @@
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   PushMessagingServiceImpl* push_service_;
 
-#if defined(ENABLE_NOTIFICATIONS)
   std::unique_ptr<StubNotificationUIManager> notification_manager_;
   std::unique_ptr<MessageCenterDisplayService> display_service_;
-#endif
 
   DISALLOW_COPY_AND_ASSIGN(PushMessagingBrowserTest);
 };
@@ -1204,7 +1193,6 @@
       content::PUSH_UNREGISTRATION_REASON_DELIVERY_PERMISSION_DENIED, 1);
 }
 
-#if defined(ENABLE_NOTIFICATIONS)
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        PushEventEnforcesUserVisibleNotification) {
   std::string script_result;
@@ -1469,7 +1457,6 @@
   ASSERT_TRUE(RunScript("permissionState()", &script_result));
   EXPECT_EQ("permission status - granted", script_result);
 }
-#endif
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysPrompt) {
   std::string script_result;
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index 485d682d..e6fb974a 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -19,7 +19,8 @@
 #include "chrome/browser/push_messaging/push_messaging_constants.h"
 #include "chrome/common/features.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index 93f3e815..1f255623 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -41,7 +41,8 @@
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_frame_host.h"
@@ -186,9 +187,7 @@
     : profile_(profile),
       push_subscription_count_(0),
       pending_push_subscription_count_(0),
-#if defined(ENABLE_NOTIFICATIONS)
       notification_manager_(profile),
-#endif
       push_messaging_service_observer_(PushMessagingServiceObserver::Create()),
       weak_factory_(this) {
   DCHECK(profile);
@@ -255,6 +254,8 @@
 void PushMessagingServiceImpl::OnStoreReset() {
   // Delete all cached subscriptions, since they are now invalid.
   for (const auto& identifier : PushMessagingAppIdentifier::GetAll(profile_)) {
+    RecordUnsubscribeReason(
+        content::PUSH_UNREGISTRATION_REASON_GCM_STORE_RESET);
     // Clear all the subscriptions in parallel, to reduce risk that shutdown
     // occurs before we finish clearing them.
     ClearPushSubscriptionId(profile_, identifier.origin(),
@@ -374,7 +375,6 @@
     case content::PUSH_DELIVERY_STATUS_SUCCESS:
     case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
     case content::PUSH_DELIVERY_STATUS_TIMEOUT:
-#if defined(ENABLE_NOTIFICATIONS)
       // Only enforce the user visible requirements if this is currently running
       // as the delivery callback for the last in-flight message, and silent
       // push has not been enabled through a command line flag.
@@ -385,7 +385,6 @@
             requesting_origin, service_worker_registration_id,
             completion_closure_runner.Release());
       }
-#endif
       break;
     case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
       // Do nothing, and hope the error is transient.
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 59dd061..66c7694 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/background/background_trigger.h"
+#include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
 #include "chrome/common/features.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -30,10 +31,6 @@
 #include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h"
 #include "third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h"
 
-#if defined(ENABLE_NOTIFICATIONS)
-#include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
-#endif
-
 class Profile;
 class PushMessagingAppIdentifier;
 class PushMessagingServiceObserver;
@@ -252,9 +249,7 @@
   base::Closure message_callback_for_testing_;
   base::Closure content_setting_changed_callback_for_testing_;
 
-#if defined(ENABLE_NOTIFICATIONS)
   PushMessagingNotificationManager notification_manager_;
-#endif
 
   // A multiset containing one entry for each in-flight push message delivery,
   // keyed by the receiver's app id.
diff --git a/chrome/browser/renderer_host/chrome_extension_message_filter.cc b/chrome/browser/renderer_host/chrome_extension_message_filter.cc
index 3b1ef45..85891b1d 100644
--- a/chrome/browser/renderer_host/chrome_extension_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_extension_message_filter.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/files/file_path.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -80,8 +81,6 @@
   IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
                         OnOpenChannelToExtension)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtensionSync,
-                        OnOpenChannelToExtensionSync)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp,
                         OnOpenChannelToNativeApp)
@@ -105,6 +104,9 @@
 void ChromeExtensionMessageFilter::OverrideThreadForMessage(
     const IPC::Message& message, BrowserThread::ID* thread) {
   switch (message.type()) {
+    case ExtensionHostMsg_OpenChannelToExtension::ID:
+    case ExtensionHostMsg_OpenChannelToTab::ID:
+    case ExtensionHostMsg_OpenChannelToNativeApp::ID:
     case ExtensionHostMsg_OpenMessagePort::ID:
     case ExtensionHostMsg_CloseMessagePort::ID:
     case ExtensionHostMsg_PostMessage::ID:
@@ -131,84 +133,23 @@
     const ExtensionMsg_ExternalConnectionInfo& info,
     const std::string& channel_name,
     bool include_tls_channel_id,
-    int request_id) {
-  int port1_id = 0;
-  int port2_id = 0;
-  extensions::MessageService::AllocatePortIdPair(&port1_id, &port2_id);
-  Send(new ExtensionMsg_AssignPortId(routing_id, port1_id, request_id));
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(
-          &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread, this,
-          render_process_id_, routing_id, port2_id, info, channel_name,
-          include_tls_channel_id));
-}
-
-void ChromeExtensionMessageFilter::OnOpenChannelToExtensionSync(
-    int routing_id,
-    const ExtensionMsg_ExternalConnectionInfo& info,
-    const std::string& channel_name,
-    bool include_tls_channel_id,
-    int* port_id) {
-  int port2_id = 0;
-  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(
-          &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread, this,
-          render_process_id_, routing_id, port2_id, info, channel_name,
-          include_tls_channel_id));
-}
-
-void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread(
-    int source_process_id, int source_routing_id,
-    int receiver_port_id,
-    const ExtensionMsg_ExternalConnectionInfo& info,
-    const std::string& channel_name,
-    bool include_tls_channel_id) {
+    const extensions::PortId& port_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (profile_) {
-    extensions::MessageService::Get(profile_)
-        ->OpenChannelToExtension(source_process_id,
-                                 source_routing_id,
-                                 receiver_port_id,
-                                 info.source_id,
-                                 info.target_id,
-                                 info.source_url,
-                                 channel_name,
-                                 include_tls_channel_id);
+    extensions::MessageService::Get(profile_)->OpenChannelToExtension(
+        render_process_id_, routing_id, port_id, info.source_id, info.target_id,
+        info.source_url, channel_name, include_tls_channel_id);
   }
 }
 
 void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp(
     int routing_id,
     const std::string& native_app_name,
-    int request_id) {
-  int port1_id = 0;
-  int port2_id = 0;
-  extensions::MessageService::AllocatePortIdPair(&port1_id, &port2_id);
-  Send(new ExtensionMsg_AssignPortId(routing_id, port1_id, request_id));
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(
-          &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread,
-          this, routing_id, port2_id, native_app_name));
-}
-
-void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread(
-    int source_routing_id,
-    int receiver_port_id,
-    const std::string& native_app_name) {
+    const extensions::PortId& port_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (profile_) {
-    extensions::MessageService::Get(profile_)
-        ->OpenChannelToNativeApp(render_process_id_,
-                                 source_routing_id,
-                                 receiver_port_id,
-                                 native_app_name);
+    extensions::MessageService::Get(profile_)->OpenChannelToNativeApp(
+        render_process_id_, routing_id, port_id, native_app_name);
   }
 }
 
@@ -217,41 +158,18 @@
     const ExtensionMsg_TabTargetConnectionInfo& info,
     const std::string& extension_id,
     const std::string& channel_name,
-    int request_id) {
-  int port1_id = 0;
-  int port2_id = 0;
-  extensions::MessageService::AllocatePortIdPair(&port1_id, &port2_id);
-  Send(new ExtensionMsg_AssignPortId(routing_id, port1_id, request_id));
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread,
-                 this, render_process_id_, routing_id, port2_id, info,
-                 extension_id, channel_name));
-}
-
-void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread(
-    int source_process_id,
-    int source_routing_id,
-    int receiver_port_id,
-    const ExtensionMsg_TabTargetConnectionInfo& info,
-    const std::string& extension_id,
-    const std::string& channel_name) {
+    const extensions::PortId& port_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (profile_) {
-    extensions::MessageService::Get(profile_)
-        ->OpenChannelToTab(source_process_id,
-                           source_routing_id,
-                           receiver_port_id,
-                           info.tab_id,
-                           info.frame_id,
-                           extension_id,
-                           channel_name);
+    extensions::MessageService::Get(profile_)->OpenChannelToTab(
+        render_process_id_, routing_id, port_id, info.tab_id, info.frame_id,
+        extension_id, channel_name);
   }
 }
 
-void ChromeExtensionMessageFilter::OnOpenMessagePort(int routing_id,
-                                                     int port_id) {
+void ChromeExtensionMessageFilter::OnOpenMessagePort(
+    int routing_id,
+    const extensions::PortId& port_id) {
   if (!profile_)
     return;
 
@@ -259,9 +177,10 @@
       port_id, render_process_id_, routing_id);
 }
 
-void ChromeExtensionMessageFilter::OnCloseMessagePort(int routing_id,
-                                                      int port_id,
-                                                      bool force_close) {
+void ChromeExtensionMessageFilter::OnCloseMessagePort(
+    int routing_id,
+    const extensions::PortId& port_id,
+    bool force_close) {
   if (!profile_)
     return;
 
@@ -270,7 +189,7 @@
 }
 
 void ChromeExtensionMessageFilter::OnPostMessage(
-    int port_id,
+    const extensions::PortId& port_id,
     const extensions::Message& message) {
   if (!profile_)
     return;
diff --git a/chrome/browser/renderer_host/chrome_extension_message_filter.h b/chrome/browser/renderer_host/chrome_extension_message_filter.h
index 7602e6e4..ddce9ca 100644
--- a/chrome/browser/renderer_host/chrome_extension_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_extension_message_filter.h
@@ -23,6 +23,7 @@
 class ActivityLog;
 class InfoMap;
 struct Message;
+struct PortId;
 }
 
 // This class filters out incoming Chrome-specific IPC messages from the
@@ -52,41 +53,21 @@
                                 const ExtensionMsg_ExternalConnectionInfo& info,
                                 const std::string& channel_name,
                                 bool include_tls_channel_id,
-                                int request_id);
-  void OnOpenChannelToExtensionSync(
-      int routing_id,
-      const ExtensionMsg_ExternalConnectionInfo& info,
-      const std::string& channel_name,
-      bool include_tls_channel_id,
-      int* port_id);
-  void OpenChannelToExtensionOnUIThread(
-      int source_process_id,
-      int source_routing_id,
-      int receiver_port_id,
-      const ExtensionMsg_ExternalConnectionInfo& info,
-      const std::string& channel_name,
-      bool include_tls_channel_id);
+                                const extensions::PortId& port_id);
   void OnOpenChannelToNativeApp(int routing_id,
                                 const std::string& native_app_name,
-                                int request_id);
-  void OpenChannelToNativeAppOnUIThread(int source_routing_id,
-                                        int receiver_port_id,
-                                        const std::string& native_app_name);
+                                const extensions::PortId& port_id);
   void OnOpenChannelToTab(int routing_id,
                           const ExtensionMsg_TabTargetConnectionInfo& info,
                           const std::string& extension_id,
                           const std::string& channel_name,
-                          int request_id);
-  void OpenChannelToTabOnUIThread(
-      int source_process_id,
-      int source_routing_id,
-      int receiver_port_id,
-      const ExtensionMsg_TabTargetConnectionInfo& info,
-      const std::string& extension_id,
-      const std::string& channel_name);
-  void OnOpenMessagePort(int routing_id, int port_id);
-  void OnCloseMessagePort(int routing_id, int port_id, bool force_close);
-  void OnPostMessage(int port_id, const extensions::Message& message);
+                          const extensions::PortId& port_id);
+  void OnOpenMessagePort(int routing_id, const extensions::PortId& port_id);
+  void OnCloseMessagePort(int routing_id,
+                          const extensions::PortId& port_id,
+                          bool force_close);
+  void OnPostMessage(const extensions::PortId& port_id,
+                     const extensions::Message& message);
   void OnGetExtMessageBundle(const std::string& extension_id,
                              IPC::Message* reply_msg);
   void OnGetExtMessageBundleOnBlockingPool(
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 78235e6..b263a9d 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -12,7 +12,6 @@
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/metrics/field_trial.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
@@ -87,8 +86,6 @@
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_IsCrashReportingEnabled,
                         OnIsCrashReportingEnabled)
 #endif
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FieldTrialActivated,
-                        OnFieldTrialActivated)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -346,11 +343,3 @@
   *enabled = ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
 }
 #endif
-
-void ChromeRenderMessageFilter::OnFieldTrialActivated(
-    const std::string& trial_name) {
-  // Activate the trial in the browser process to match its state in the
-  // renderer. This is done by calling FindFullName which finalizes the group
-  // and activates the trial.
-  base::FieldTrialList::FindFullName(trial_name);
-}
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 30694e60..d6a1953 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -120,10 +120,6 @@
   void OnIsCrashReportingEnabled(bool* enabled);
 #endif
 
-  // Called when a message is received from a renderer that a trial has been
-  // activated (ChromeViewHostMsg_FieldTrialActivated).
-  void OnFieldTrialActivated(const std::string& trial_name);
-
   const int render_process_id_;
 
   // The Profile associated with our renderer process.  This should only be
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/keyboard_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/keyboard_handler.js
index 8d4990f3..2f3505ff 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/keyboard_handler.js
@@ -18,6 +18,9 @@
   /** @type {number} @private */
   this.passThroughKeyUpCount_ = 0;
 
+  /** @type {Set} @private */
+  this.eatenKeyDowns_ = new Set();
+
   document.addEventListener('keydown', this.onKeyDown.bind(this), false);
   document.addEventListener('keyup', this.onKeyUp.bind(this), false);
 };
@@ -38,6 +41,7 @@
         !cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt)) {
       evt.preventDefault();
       evt.stopPropagation();
+      this.eatenKeyDowns_.add(evt.keyCode);
     }
     Output.forceModeForNextSpeechUtterance(cvox.QueueMode.FLUSH);
     return false;
@@ -59,6 +63,13 @@
         this.passThroughKeyUpCount_++;
       }
     }
+
+    if (this.eatenKeyDowns_.has(evt.keyCode)) {
+      evt.preventDefault();
+      evt.stopPropagation();
+      this.eatenKeyDowns_.delete(evt.keyCode);
+    }
+
     return false;
   },
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index d2599e1..36bbf65d7 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -1057,7 +1057,7 @@
             }
           }
           options.annotation.push(token);
-          if (selectedText) {
+          if (selectedText && !this.formatOptions_.braille) {
             this.append_(buff, selectedText, options);
             this.append_(buff, Msgs.getMsg('selected'));
           } else {
@@ -1721,6 +1721,7 @@
   mergeBraille_: function(spans) {
     var separator = '';  // Changes to space as appropriate.
     var prevHasInlineNode = false;
+    var prevIsName = false;
     return spans.reduce(function(result, cur) {
       // Ignore empty spans except when they contain a selection.
       var hasSelection = cur.getSpanInstanceOf(Output.SelectionSpan);
@@ -1746,15 +1747,18 @@
                 s.node.role == RoleType.inlineTextBox;
           });
 
+      var isName = cur.hasSpan('name');
+
       // Now, decide whether we should include separators between the previous
       // span and |cur|.
       // Never separate chunks without something already there at this point.
 
       // The only case where we know for certain that a separator is not needed
-      // is when the previous and current node are in-lined. In all other cases,
-      // use the surrounding whitespace to ensure we only have one separator
-      // between the node text.
-      if (result.length == 0 || (hasInlineNode && prevHasInlineNode))
+      // is when the previous and current values are in-lined and part of the
+      // node's name. In all other cases, use the surrounding whitespace to
+      // ensure we only have one separator between the node text.
+      if (result.length == 0 ||
+          (hasInlineNode && prevHasInlineNode && isName && prevIsName))
         separator = '';
       else if (result.toString()[result.length - 1] == Output.SPACE ||
           cur.toString()[0] == Output.SPACE)
@@ -1763,6 +1767,7 @@
         separator = Output.SPACE;
 
       prevHasInlineNode = hasInlineNode;
+      prevIsName = isName;
       result.append(separator);
       result.append(cur);
       return result;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index c5a7942..f74f4a2 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -688,3 +688,27 @@
         o);
   });
 });
+
+TEST_F('OutputE2ETest', 'BrailleAncestry', function() {
+  this.runWithLoadedTree(function() {/*!
+    <ul><li><a href="#">test</a></li></ul>
+  */},
+  function(root) {
+    var text = root.find({role: 'inlineTextBox'});
+    var link = root.find({role: 'link'});
+    var listItem = root.find({role: 'listItem'});
+    var list = root.find({role: 'list'});
+    var range = cursors.Range.fromNode(text);
+    var o = new Output().withBraille(range, null, 'navigate');
+    checkBrailleOutput(
+        'test lnk lstitm list +1',
+        [
+          {value: new Output.NodeSpan(text), start: 0, end: 4},
+          {value: new Output.NodeSpan(link), start: 5, end: 8},
+          {value: new Output.NodeSpan(listItem), start: 9, end: 15},
+          {value: new Output.NodeSpan(list), start: 16, end: 23}
+        ],
+
+        o);
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
index d278914e..a5890f6 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
@@ -310,10 +310,14 @@
         var range = cursors.Range.fromNode(node);
         output.withSpeech(range, range, Output.EventType.NAVIGATE);
         var label = output.toString();
-        this.addMenuItem(label, '', '', function() {
-          chrome.extension.getBackgroundPage().ChromeVoxState
-              .instance['navigateToRange'](cursors.Range.fromNode(node));
-        });
+        this.addMenuItem(label, '', '', (function() {
+          var savedNode = node;
+          return function() {
+            chrome.extension.getBackgroundPage().ChromeVoxState
+                .instance['navigateToRange'](cursors.Range.fromNode(savedNode));
+          };
+        }()));
+
         if (this.selectNext_) {
           this.activateItem(this.items_.length - 1);
           this.selectNext_ = false;
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
index 11d5143..75456ea 100644
--- a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
@@ -285,6 +285,11 @@
     onBeforeShow: function(data) {
       Oobe.getInstance().headerHidden = true;
 
+      // Reload caption image in case it was not loaded during the
+      // initialization phase.
+      $('arc-tos-logo').src =
+        'https://play.google.com/about/images/play_logo.png';
+
       this.hideOverlay();
       this.reloadPlayStore();
     },
diff --git a/chrome/browser/resources/engagement/site_engagement.html b/chrome/browser/resources/engagement/site_engagement.html
index 0125240..4a519fa 100644
--- a/chrome/browser/resources/engagement/site_engagement.html
+++ b/chrome/browser/resources/engagement/site_engagement.html
@@ -40,20 +40,28 @@
     }
 
     .origin-cell {
+      background-color: rgba(230, 230, 230, 0.5);
       min-width: 500px;
     }
 
     .score-cell {
+      background-color: rgba(230, 230, 230, 0.5);
       text-align: right;
     }
 
     .score-input {
-      background-color: transparent;
-      border: none;
+      border: 1px solid #ccc;
+      border-radius: 2px;
       text-align: right;
-      width: 40px;
+      width: 70px;
     }
 
+    .score-input:focus {
+      border: 1px solid rgb(143, 185, 252);
+      box-shadow: 0 0 2px rgb(113, 158, 206);
+      outline: none;
+    }
+    
     table tr:hover {
       background: rgb(255, 255, 187);
     }
diff --git a/chrome/browser/resources/engagement/site_engagement.js b/chrome/browser/resources/engagement/site_engagement.js
index 191872ff..bd19f11 100644
--- a/chrome/browser/resources/engagement/site_engagement.js
+++ b/chrome/browser/resources/engagement/site_engagement.js
@@ -74,6 +74,10 @@
       row.appendChild(originCell);
       row.appendChild(scoreCell);
       row.appendChild(engagementBarCell);
+
+      // Stores correspondent engagementBarCell to change it's length on
+      // scoreChange event.
+      scoreInput.barCellRef = engagementBar;
       return row;
     }
 
@@ -90,13 +94,17 @@
     }
 
     /**
-     * Sets the engagement score when a score input is changed. Also resets the
-     * update interval.
+     * Sets the engagement score when a score input is changed.
+     * Resets the length of engagement-bar-cell to match the new score.
+     * Also resets the update interval.
      * @param {string} origin The origin of the engagement score to set.
      * @param {Event} e
      */
     function handleScoreChange(origin, e) {
-      uiHandler.setSiteEngagementScoreForOrigin(origin, e.target.value);
+      var scoreInput = e.target;
+      uiHandler.setSiteEngagementScoreForOrigin(origin, scoreInput.value);
+      scoreInput.barCellRef.style.width = (scoreInput.value * 4) + 'px';
+      scoreInput.blur();
       enableAutoupdate();
     }
 
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 45db544..94ba924 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -23,13 +23,15 @@
 
 /**
  * The different sources that an NTP tile can have.
- * Note: Keep in sync with common/ntp_logging_events.h
+ * Note: Keep in sync with components/ntp_tiles/ntp_tile_source.h
  * @enum {number}
  * @const
  */
-var NTPLoggingTileSource = {
-  CLIENT: 0,
-  SERVER: 1,
+var NTPTileSource = {
+  TOP_SITES: 0,
+  SUGGESTIONS_SERVICE: 1,
+  POPULAR: 3,
+  WHITELIST: 4,
 };
 
 
@@ -83,11 +85,6 @@
  */
 var queryArgs = {};
 
-/**
- * Url to ping when suggestions have been shown.
- */
-var impressionUrl = null;
-
 
 /**
  * Log an event on the NTP.
@@ -100,7 +97,7 @@
 /**
  * Log impression of an NTP tile.
  * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileSource The source from NTPLoggingTileSource.
+ * @param {number} tileSource The source from NTPTileSource.
  */
 function logMostVisitedImpression(tileIndex, tileSource) {
   chrome.embeddedSearch.newTabPage.logMostVisitedImpression(tileIndex,
@@ -110,7 +107,7 @@
 /**
  * Log click on an NTP tile.
  * @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileSource The source from NTPLoggingTileSource.
+ * @param {number} tileSource The source from NTPTileSource.
  */
 function logMostVisitedNavigation(tileIndex, tileSource) {
   chrome.embeddedSearch.newTabPage.logMostVisitedNavigation(tileIndex,
@@ -279,11 +276,6 @@
 
   // Make sure the tiles variable contain the next tileset we may use.
   tiles = document.createElement('div');
-
-  if (impressionUrl) {
-    navigator.sendBeacon(impressionUrl);
-    impressionUrl = null;
-  }
 };
 
 
@@ -302,7 +294,7 @@
       return;
 
     data.tid = data.rid;
-    data.tileSource = NTPLoggingTileSource.CLIENT;
+    data.tileSource = NTPTileSource.TOP_SITES;
     if (!data.faviconUrl) {
       data.faviconUrl = 'chrome-search://favicon/size/16@' +
           window.devicePixelRatio + 'x/' + data.renderViewId + '/' + data.tid;
@@ -310,7 +302,7 @@
     tiles.appendChild(renderTile(data));
   } else if (args.url) {
     // If a URL is passed: a server-side suggestion.
-    args.tileSource = NTPLoggingTileSource.SERVER;
+    args.tileSource = NTPTileSource.SUGGESTIONS_SERVICE;
     // check sanity of the arguments
     if (/^javascript:/i.test(args.url) ||
         /^javascript:/i.test(args.thumbnailUrl))
@@ -383,14 +375,6 @@
   }
   tile.setAttribute('aria-label', data.title);
   tile.title = data.title;
-  if (data.impressionUrl) {
-    impressionUrl = data.impressionUrl;
-  }
-  if (data.pingUrl) {
-    tile.addEventListener('click', function(ev) {
-      navigator.sendBeacon(data.pingUrl);
-    });
-  }
 
   tile.addEventListener('click', function(ev) {
     logMostVisitedNavigation(position, data.tileSource);
diff --git a/chrome/browser/resources/md_history/app.crisper.js b/chrome/browser/resources/md_history/app.crisper.js
index 0c4b5ad..a5a250e 100644
--- a/chrome/browser/resources/md_history/app.crisper.js
+++ b/chrome/browser/resources/md_history/app.crisper.js
@@ -71,7 +71,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-Polymer({is:"history-list-container",properties:{selectedPage_:String,grouped:Boolean,groupedRange:{type:Number,observer:"groupedRangeChanged_"},queryState:Object,queryResult:Object},observers:["searchTermChanged_(queryState.searchTerm)","groupedOffsetChanged_(queryState.groupedOffset)"],listeners:{"history-list-scrolled":"closeMenu_","load-more-history":"loadMoreHistory_","toggle-menu":"toggleMenu_"},historyResult:function(info,results){this.initializeResults_(info,results);this.closeMenu_();if(info.term&&!this.queryState.incremental){Polymer.IronA11yAnnouncer.requestAvailability();this.fire("iron-announce",{text:md_history.HistoryItem.searchResultsTitle(results.length,info.term)})}var list=this.getSelectedList_();list.addNewResults(results,this.queryState.incremental,info.finished)},queryHistory:function(incremental){var queryState=this.queryState;var noResults=!this.queryResult||this.queryResult.results==null;if(queryState.queryingDisabled||!this.queryState.searchTerm&&noResults){return}var dialog=this.$.dialog.getIfExists();if(!incremental&&dialog&&dialog.open)dialog.close();this.set("queryState.querying",true);this.set("queryState.incremental",incremental);var lastVisitTime=0;if(incremental){var lastVisit=this.queryResult.results.slice(-1)[0];lastVisitTime=lastVisit?Math.floor(lastVisit.time):0}var maxResults=this.groupedRange==HistoryRange.ALL_TIME?RESULTS_PER_PAGE:0;chrome.send("queryHistory",[queryState.searchTerm,queryState.groupedOffset,queryState.range,lastVisitTime,maxResults])},historyDeleted:function(){if(this.getSelectedItemCount()>0)return;this.queryHistory(false)},getContentScrollTarget:function(){return this.getSelectedList_()},getSelectedItemCount:function(){return this.getSelectedList_().selectedPaths.size},unselectAllItems:function(count){var selectedList=this.getSelectedList_();if(selectedList)selectedList.unselectAllItems(count)},deleteSelectedWithPrompt:function(){if(!loadTimeData.getBoolean("allowDeletingHistory"))return;var browserService=md_history.BrowserService.getInstance();browserService.recordAction("RemoveSelected");if(this.queryState.searchTerm!="")browserService.recordAction("SearchResultRemove");this.$.dialog.get().showModal();this.$$(".action-button").focus()},groupedRangeChanged_:function(range,oldRange){this.selectedPage_=range==HistoryRange.ALL_TIME?"infinite-list":"grouped-list";if(oldRange==undefined)return;this.set("queryState.groupedOffset",0);if(this.queryResult.info){this.set("queryResult.results",[]);this.historyResult(this.queryResult.info,[])}this.queryHistory(false);this.fire("history-view-changed")},searchTermChanged_:function(){this.queryHistory(false);if(this.queryState.searchTerm)md_history.BrowserService.getInstance().recordAction("Search")},groupedOffsetChanged_:function(){this.queryHistory(false)},loadMoreHistory_:function(){this.queryHistory(true)},initializeResults_:function(info,results){if(results.length==0)return;var currentDate=results[0].dateRelativeDay;for(var i=0;i<results.length;i++){results[i].selected=false;results[i].readableTimestamp=info.term==""?results[i].dateTimeOfDay:results[i].dateShort;if(results[i].dateRelativeDay!=currentDate){currentDate=results[i].dateRelativeDay}}},onDialogConfirmTap_:function(){md_history.BrowserService.getInstance().recordAction("ConfirmRemoveSelected");this.getSelectedList_().deleteSelected();var dialog=assert(this.$.dialog.getIfExists());dialog.close()},onDialogCancelTap_:function(){md_history.BrowserService.getInstance().recordAction("CancelRemoveSelected");var dialog=assert(this.$.dialog.getIfExists());dialog.close()},closeMenu_:function(){var menu=this.$.sharedMenu.getIfExists();if(menu)menu.closeMenu()},toggleMenu_:function(e){var target=e.detail.target;var menu=this.$.sharedMenu.get();menu.toggleMenu(target,e.detail)},onMoreFromSiteTap_:function(){md_history.BrowserService.getInstance().recordAction("EntryMenuShowMoreFromSite");var menu=assert(this.$.sharedMenu.getIfExists());this.set("queryState.searchTerm",menu.itemData.item.domain);menu.closeMenu()},onRemoveFromHistoryTap_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryMenuRemoveFromHistory");var menu=assert(this.$.sharedMenu.getIfExists());var itemData=menu.itemData;browserService.deleteItems([itemData.item]).then(function(items){this.fire("unselect-all");this.getSelectedList_().removeItemsByPath([itemData.path]);var index=itemData.index;if(index==undefined)return;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram("HistoryPage.RemoveEntryPosition",Math.min(index,UMA_MAX_BUCKET_VALUE),UMA_MAX_BUCKET_VALUE);if(index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.RemoveEntryPositionSubset",index,UMA_MAX_SUBSET_BUCKET_VALUE)}}.bind(this));menu.closeMenu()},getSelectedList_:function(){return this.$.content.selectedItem},canDeleteHistory_:function(){return loadTimeData.getBoolean("allowDeletingHistory")}});(function(){"use strict";Polymer({is:"iron-location",properties:{path:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.pathname)}},query:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.search.slice(1))}},hash:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.hash.slice(1))}},dwellTime:{type:Number,value:2e3},urlSpaceRegex:{type:String,value:""},_urlSpaceRegExp:{computed:"_makeRegExp(urlSpaceRegex)"},_lastChangedAt:{type:Number},_initialized:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["_updateUrl(path, query, hash)"],attached:function(){this.listen(window,"hashchange","_hashChanged");this.listen(window,"location-changed","_urlChanged");this.listen(window,"popstate","_urlChanged");this.listen(document.body,"click","_globalOnClick");this._lastChangedAt=window.performance.now()-(this.dwellTime-200);this._initialized=true;this._urlChanged()},detached:function(){this.unlisten(window,"hashchange","_hashChanged");this.unlisten(window,"location-changed","_urlChanged");this.unlisten(window,"popstate","_urlChanged");this.unlisten(document.body,"click","_globalOnClick");this._initialized=false},_hashChanged:function(){this.hash=window.decodeURIComponent(window.location.hash.substring(1))},_urlChanged:function(){this._dontUpdateUrl=true;this._hashChanged();this.path=window.decodeURIComponent(window.location.pathname);this.query=window.decodeURIComponent(window.location.search.substring(1));this._dontUpdateUrl=false;this._updateUrl()},_getUrl:function(){var partiallyEncodedPath=window.encodeURI(this.path).replace(/\#/g,"%23").replace(/\?/g,"%3F");var partiallyEncodedQuery="";if(this.query){partiallyEncodedQuery="?"+window.encodeURI(this.query).replace(/\#/g,"%23")}var partiallyEncodedHash="";if(this.hash){partiallyEncodedHash="#"+window.encodeURI(this.hash)}return partiallyEncodedPath+partiallyEncodedQuery+partiallyEncodedHash},_updateUrl:function(){if(this._dontUpdateUrl||!this._initialized){return}if(this.path===window.decodeURIComponent(window.location.pathname)&&this.query===window.decodeURIComponent(window.location.search.substring(1))&&this.hash===window.decodeURIComponent(window.location.hash.substring(1))){return}var newUrl=this._getUrl();var fullNewUrl=new URL(newUrl,window.location.protocol+"//"+window.location.host).href;var now=window.performance.now();var shouldReplace=this._lastChangedAt+this.dwellTime>now;this._lastChangedAt=now;if(shouldReplace){window.history.replaceState({},"",fullNewUrl)}else{window.history.pushState({},"",fullNewUrl)}this.fire("location-changed",{},{node:window})},_globalOnClick:function(event){if(event.defaultPrevented){return}var href=this._getSameOriginLinkHref(event);if(!href){return}event.preventDefault();if(href===window.location.href){return}window.history.pushState({},"",href);this.fire("location-changed",{},{node:window})},_getSameOriginLinkHref:function(event){if(event.button!==0){return null}if(event.metaKey||event.ctrlKey){return null}var eventPath=Polymer.dom(event).path;var anchor=null;for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}if(!anchor){return null}if(anchor.target==="_blank"){return null}if((anchor.target==="_top"||anchor.target==="_parent")&&window.top!==window){return null}var href=anchor.href;var url;if(document.baseURI!=null){url=new URL(href,document.baseURI)}else{url=new URL(href)}var origin;if(window.location.origin){origin=window.location.origin}else{origin=window.location.protocol+"//"+window.location.hostname;if(window.location.port){origin+=":"+window.location.port}}if(url.origin!==origin){return null}var normalizedHref=url.pathname+url.search+url.hash;if(this._urlSpaceRegExp&&!this._urlSpaceRegExp.test(normalizedHref)){return null}var fullNormalizedHref=new URL(normalizedHref,window.location.href).href;return fullNormalizedHref},_makeRegExp:function(urlSpaceRegex){return RegExp(urlSpaceRegex)}})})();"use strict";Polymer({is:"iron-query-params",properties:{paramsString:{type:String,notify:true,observer:"paramsStringChanged"},paramsObject:{type:Object,notify:true,value:function(){return{}}},_dontReact:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["paramsObjectChanged(paramsObject.*)"],paramsStringChanged:function(){this._dontReact=true;this.paramsObject=this._decodeParams(this.paramsString);this._dontReact=false},paramsObjectChanged:function(){if(this._dontReact){return}this.paramsString=this._encodeParams(this.paramsObject)},_encodeParams:function(params){var encodedParams=[];for(var key in params){var value=params[key];if(value===""){encodedParams.push(encodeURIComponent(key))}else if(value){encodedParams.push(encodeURIComponent(key)+"="+encodeURIComponent(value.toString()))}}return encodedParams.join("&")},_decodeParams:function(paramString){var params={};paramString=(paramString||"").replace(/\+/g,"%20");var paramList=paramString.split("&");for(var i=0;i<paramList.length;i++){var param=paramList[i].split("=");if(param[0]){params[decodeURIComponent(param[0])]=decodeURIComponent(param[1]||"")}}return params}});
+Polymer({is:"history-list-container",properties:{selectedPage_:String,grouped:Boolean,groupedRange:{type:Number,observer:"groupedRangeChanged_"},queryState:Object,queryResult:Object},observers:["searchTermChanged_(queryState.searchTerm)","groupedOffsetChanged_(queryState.groupedOffset)"],listeners:{"history-list-scrolled":"closeMenu_","load-more-history":"loadMoreHistory_","toggle-menu":"toggleMenu_"},historyResult:function(info,results){this.initializeResults_(info,results);this.closeMenu_();if(info.term&&!this.queryState.incremental){Polymer.IronA11yAnnouncer.requestAvailability();this.fire("iron-announce",{text:md_history.HistoryItem.searchResultsTitle(results.length,info.term)})}var list=this.getSelectedList_();list.addNewResults(results,this.queryState.incremental,info.finished)},queryHistory:function(incremental){var queryState=this.queryState;var noResults=!this.queryResult||this.queryResult.results==null;if(queryState.queryingDisabled||!this.queryState.searchTerm&&noResults){return}var dialog=this.$.dialog.getIfExists();if(!incremental&&dialog&&dialog.open)dialog.close();this.set("queryState.querying",true);this.set("queryState.incremental",incremental);var lastVisitTime=0;if(incremental){var lastVisit=this.queryResult.results.slice(-1)[0];lastVisitTime=lastVisit?Math.floor(lastVisit.time):0}var maxResults=this.groupedRange==HistoryRange.ALL_TIME?RESULTS_PER_PAGE:0;chrome.send("queryHistory",[queryState.searchTerm,queryState.groupedOffset,queryState.range,lastVisitTime,maxResults])},historyDeleted:function(){if(this.getSelectedItemCount()>0)return;this.queryHistory(false)},getContentScrollTarget:function(){return this.getSelectedList_()},getSelectedItemCount:function(){return this.getSelectedList_().selectedPaths.size},unselectAllItems:function(count){var selectedList=this.getSelectedList_();if(selectedList)selectedList.unselectAllItems(count)},deleteSelectedWithPrompt:function(){if(!loadTimeData.getBoolean("allowDeletingHistory"))return;var browserService=md_history.BrowserService.getInstance();browserService.recordAction("RemoveSelected");if(this.queryState.searchTerm!="")browserService.recordAction("SearchResultRemove");this.$.dialog.get().showModal();this.$$(".action-button").focus()},groupedRangeChanged_:function(range,oldRange){this.selectedPage_=range==HistoryRange.ALL_TIME?"infinite-list":"grouped-list";if(oldRange==undefined)return;this.set("queryState.groupedOffset",0);if(this.queryResult.info){this.set("queryResult.results",[]);this.historyResult(this.queryResult.info,[])}this.queryHistory(false);this.fire("history-view-changed")},searchTermChanged_:function(){this.queryHistory(false);if(this.queryState.searchTerm)md_history.BrowserService.getInstance().recordAction("Search")},groupedOffsetChanged_:function(){this.queryHistory(false)},loadMoreHistory_:function(){this.queryHistory(true)},initializeResults_:function(info,results){if(results.length==0)return;var currentDate=results[0].dateRelativeDay;for(var i=0;i<results.length;i++){results[i].selected=false;results[i].readableTimestamp=info.term==""?results[i].dateTimeOfDay:results[i].dateShort;if(results[i].dateRelativeDay!=currentDate){currentDate=results[i].dateRelativeDay}}},onDialogConfirmTap_:function(){md_history.BrowserService.getInstance().recordAction("ConfirmRemoveSelected");this.getSelectedList_().deleteSelected();var dialog=assert(this.$.dialog.getIfExists());dialog.close()},onDialogCancelTap_:function(){md_history.BrowserService.getInstance().recordAction("CancelRemoveSelected");var dialog=assert(this.$.dialog.getIfExists());dialog.close()},closeMenu_:function(){var menu=this.$.sharedMenu.getIfExists();if(menu)menu.closeMenu()},toggleMenu_:function(e){var target=e.detail.target;var menu=this.$.sharedMenu.get();menu.toggleMenu(target,e.detail)},onMoreFromSiteTap_:function(){md_history.BrowserService.getInstance().recordAction("EntryMenuShowMoreFromSite");var menu=assert(this.$.sharedMenu.getIfExists());this.set("queryState.searchTerm",menu.itemData.item.domain);menu.closeMenu()},onRemoveFromHistoryTap_:function(){var browserService=md_history.BrowserService.getInstance();browserService.recordAction("EntryMenuRemoveFromHistory");var menu=assert(this.$.sharedMenu.getIfExists());var itemData=menu.itemData;browserService.deleteItems([itemData.item]).then(function(items){this.fire("unselect-all");this.getSelectedList_().removeItemsByPath([itemData.path]);var index=itemData.index;if(index==undefined)return;var browserService=md_history.BrowserService.getInstance();browserService.recordHistogram("HistoryPage.RemoveEntryPosition",Math.min(index,UMA_MAX_BUCKET_VALUE),UMA_MAX_BUCKET_VALUE);if(index<=UMA_MAX_SUBSET_BUCKET_VALUE){browserService.recordHistogram("HistoryPage.RemoveEntryPositionSubset",index,UMA_MAX_SUBSET_BUCKET_VALUE)}}.bind(this));menu.closeMenu()},getSelectedList_:function(){return this.$$("#"+this.selectedPage_)},canDeleteHistory_:function(){return loadTimeData.getBoolean("allowDeletingHistory")}});(function(){"use strict";Polymer({is:"iron-location",properties:{path:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.pathname)}},query:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.search.slice(1))}},hash:{type:String,notify:true,value:function(){return window.decodeURIComponent(window.location.hash.slice(1))}},dwellTime:{type:Number,value:2e3},urlSpaceRegex:{type:String,value:""},_urlSpaceRegExp:{computed:"_makeRegExp(urlSpaceRegex)"},_lastChangedAt:{type:Number},_initialized:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["_updateUrl(path, query, hash)"],attached:function(){this.listen(window,"hashchange","_hashChanged");this.listen(window,"location-changed","_urlChanged");this.listen(window,"popstate","_urlChanged");this.listen(document.body,"click","_globalOnClick");this._lastChangedAt=window.performance.now()-(this.dwellTime-200);this._initialized=true;this._urlChanged()},detached:function(){this.unlisten(window,"hashchange","_hashChanged");this.unlisten(window,"location-changed","_urlChanged");this.unlisten(window,"popstate","_urlChanged");this.unlisten(document.body,"click","_globalOnClick");this._initialized=false},_hashChanged:function(){this.hash=window.decodeURIComponent(window.location.hash.substring(1))},_urlChanged:function(){this._dontUpdateUrl=true;this._hashChanged();this.path=window.decodeURIComponent(window.location.pathname);this.query=window.decodeURIComponent(window.location.search.substring(1));this._dontUpdateUrl=false;this._updateUrl()},_getUrl:function(){var partiallyEncodedPath=window.encodeURI(this.path).replace(/\#/g,"%23").replace(/\?/g,"%3F");var partiallyEncodedQuery="";if(this.query){partiallyEncodedQuery="?"+window.encodeURI(this.query).replace(/\#/g,"%23")}var partiallyEncodedHash="";if(this.hash){partiallyEncodedHash="#"+window.encodeURI(this.hash)}return partiallyEncodedPath+partiallyEncodedQuery+partiallyEncodedHash},_updateUrl:function(){if(this._dontUpdateUrl||!this._initialized){return}if(this.path===window.decodeURIComponent(window.location.pathname)&&this.query===window.decodeURIComponent(window.location.search.substring(1))&&this.hash===window.decodeURIComponent(window.location.hash.substring(1))){return}var newUrl=this._getUrl();var fullNewUrl=new URL(newUrl,window.location.protocol+"//"+window.location.host).href;var now=window.performance.now();var shouldReplace=this._lastChangedAt+this.dwellTime>now;this._lastChangedAt=now;if(shouldReplace){window.history.replaceState({},"",fullNewUrl)}else{window.history.pushState({},"",fullNewUrl)}this.fire("location-changed",{},{node:window})},_globalOnClick:function(event){if(event.defaultPrevented){return}var href=this._getSameOriginLinkHref(event);if(!href){return}event.preventDefault();if(href===window.location.href){return}window.history.pushState({},"",href);this.fire("location-changed",{},{node:window})},_getSameOriginLinkHref:function(event){if(event.button!==0){return null}if(event.metaKey||event.ctrlKey){return null}var eventPath=Polymer.dom(event).path;var anchor=null;for(var i=0;i<eventPath.length;i++){var element=eventPath[i];if(element.tagName==="A"&&element.href){anchor=element;break}}if(!anchor){return null}if(anchor.target==="_blank"){return null}if((anchor.target==="_top"||anchor.target==="_parent")&&window.top!==window){return null}var href=anchor.href;var url;if(document.baseURI!=null){url=new URL(href,document.baseURI)}else{url=new URL(href)}var origin;if(window.location.origin){origin=window.location.origin}else{origin=window.location.protocol+"//"+window.location.hostname;if(window.location.port){origin+=":"+window.location.port}}if(url.origin!==origin){return null}var normalizedHref=url.pathname+url.search+url.hash;if(this._urlSpaceRegExp&&!this._urlSpaceRegExp.test(normalizedHref)){return null}var fullNormalizedHref=new URL(normalizedHref,window.location.href).href;return fullNormalizedHref},_makeRegExp:function(urlSpaceRegex){return RegExp(urlSpaceRegex)}})})();"use strict";Polymer({is:"iron-query-params",properties:{paramsString:{type:String,notify:true,observer:"paramsStringChanged"},paramsObject:{type:Object,notify:true,value:function(){return{}}},_dontReact:{type:Boolean,value:false}},hostAttributes:{hidden:true},observers:["paramsObjectChanged(paramsObject.*)"],paramsStringChanged:function(){this._dontReact=true;this.paramsObject=this._decodeParams(this.paramsString);this._dontReact=false},paramsObjectChanged:function(){if(this._dontReact){return}this.paramsString=this._encodeParams(this.paramsObject)},_encodeParams:function(params){var encodedParams=[];for(var key in params){var value=params[key];if(value===""){encodedParams.push(encodeURIComponent(key))}else if(value){encodedParams.push(encodeURIComponent(key)+"="+encodeURIComponent(value.toString()))}}return encodedParams.join("&")},_decodeParams:function(paramString){var params={};paramString=(paramString||"").replace(/\+/g,"%20");var paramList=paramString.split("&");for(var i=0;i<paramList.length;i++){var param=paramList[i].split("=");if(param[0]){params[decodeURIComponent(param[0])]=decodeURIComponent(param[1]||"")}}return params}});
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
diff --git a/chrome/browser/resources/md_history/app.vulcanized.html b/chrome/browser/resources/md_history/app.vulcanized.html
index f2efdc18..09e4008 100644
--- a/chrome/browser/resources/md_history/app.vulcanized.html
+++ b/chrome/browser/resources/md_history/app.vulcanized.html
@@ -2494,6 +2494,8 @@
 <style is="custom-style" css-build="shadow">html {
   --cr-actionable_-_cursor:  pointer;;
     --cr-focused-item-color: var(--google-grey-300);
+    
+    --cr-icon-padding: 8px;
     --cr-selectable-focus_-_background-color:  var(--cr-focused-item-color); --cr-selectable-focus_-_outline:  none;
     --cr-separator-line: 1px solid rgba(0, 0, 0, 0.06);
     --paper-checkbox-ink-size: 40px;
diff --git a/chrome/browser/resources/md_history/list_container.js b/chrome/browser/resources/md_history/list_container.js
index 395d3855..b6c1ba2 100644
--- a/chrome/browser/resources/md_history/list_container.js
+++ b/chrome/browser/resources/md_history/list_container.js
@@ -101,7 +101,7 @@
     this.queryHistory(false);
   },
 
-  /** @return {HTMLElement} */
+  /** @return {Element} */
   getContentScrollTarget: function() {
     return this.getSelectedList_();
   },
@@ -283,10 +283,10 @@
   },
 
   /**
-   * @return {HTMLElement}
+   * @return {Element}
    * @private
    */
-  getSelectedList_: function() { return this.$.content.selectedItem; },
+  getSelectedList_: function() { return this.$$('#' + this.selectedPage_); },
 
   /** @private */
   canDeleteHistory_: function() {
diff --git a/chrome/browser/resources/net_internals/export_view.html b/chrome/browser/resources/net_internals/export_view.html
index 0bc8be3..92dc081 100644
--- a/chrome/browser/resources/net_internals/export_view.html
+++ b/chrome/browser/resources/net_internals/export_view.html
@@ -1,13 +1,9 @@
 <style>
 
-.export-view-explanation-warning {
-  border: 2px solid #e00;
-}
-
 #export-view-privacy-warning {
+  border: 1px solid rgb(238, 0, 0);
+  color: rgb(238, 0, 0);
   padding: 5px;
-  color: #e00;
-  border: 1px solid #e00;
 }
 
 #export-view-save-log-file {
diff --git a/chrome/browser/resources/net_internals/export_view.js b/chrome/browser/resources/net_internals/export_view.js
index 72c5bb3c..5282998 100644
--- a/chrome/browser/resources/net_internals/export_view.js
+++ b/chrome/browser/resources/net_internals/export_view.js
@@ -29,6 +29,8 @@
     this.saveStatusText_ = $(ExportView.SAVE_STATUS_TEXT_ID);
 
     this.userCommentsTextArea_ = $(ExportView.USER_COMMENTS_TEXT_AREA_ID);
+    this.enableSaveFileButton_(false);
+    this.userCommentsTextArea_.onkeyup = this.onUserCommentsUpdated_.bind(this);
 
     // Track blob for previous log dump so it can be revoked when a new dump is
     // saved.
@@ -125,10 +127,7 @@
      */
     createLogDump_: function(callback) {
       // Get an explanation for the dump file (this is mandatory!)
-      var userComments = this.getNonEmptyUserComments_();
-      if (userComments == undefined) {
-        return;
-      }
+      var userComments = this.userCommentsTextArea_.value;
 
       this.setSaveFileStatus('Preparing data...', true);
 
@@ -154,30 +153,14 @@
      */
     setUserComments_: function(userComments) {
       this.userCommentsTextArea_.value = userComments;
+      this.onUserCommentsUpdated_();
     },
 
     /**
-     * Fetches the user comments for this dump. If none were entered, warns the
-     * user and returns undefined. Otherwise returns the comments text.
+     * User comments are updated.
      */
-    getNonEmptyUserComments_: function() {
-      var value = this.userCommentsTextArea_.value;
-
-      // Reset the class name in case we had hilighted it earlier.
-      this.userCommentsTextArea_.className = '';
-
-      // We don't accept empty explanations. We don't care what is entered, as
-      // long as there is something (a single whitespace would work).
-      if (value == '') {
-        // Put a big obnoxious red border around the text area.
-        this.userCommentsTextArea_.className =
-            'export-view-explanation-warning';
-        alert('Please fill in the text field!');
-        this.userCommentsTextArea_.focus();
-        return undefined;
-      }
-
-      return value;
+    onUserCommentsUpdated_: function() {
+      this.enableSaveFileButton_(this.userCommentsTextArea_.value != '');
     },
 
     /**
diff --git a/chrome/browser/resources/pdf/browser_api.js b/chrome/browser/resources/pdf/browser_api.js
index 6b75caf..28f17b9 100644
--- a/chrome/browser/resources/pdf/browser_api.js
+++ b/chrome/browser/resources/pdf/browser_api.js
@@ -55,28 +55,28 @@
    * @param {number} defaultZoom The default browser zoom.
    * @param {number} initialZoom The initial browser zoom
    *     upon starting the plugin.
-   * @param {boolean} manageZoom Whether to manage zoom.
+   * @param {BrowserApi.ZoomBehavior} zoomBehavior How to manage zoom.
    */
-  constructor(streamInfo, defaultZoom, initialZoom, manageZoom) {
+  constructor(streamInfo, defaultZoom, initialZoom, zoomBehavior) {
     this.streamInfo_ = streamInfo;
     this.defaultZoom_ = defaultZoom;
     this.initialZoom_ = initialZoom;
-    this.manageZoom_ = manageZoom;
+    this.zoomBehavior_ = zoomBehavior;
   }
 
   /**
    * Returns a promise to a BrowserApi.
    * @param {!Object} streamInfo The stream object pointing to the data
    *     contained in the PDF.
-   * @param {boolean} manageZoom Whether to manage zoom.
+   * @param {BrowserApi.ZoomBehavior} zoomBehavior How to manage zoom.
    */
-  static create(streamInfo, manageZoom) {
+  static create(streamInfo, zoomBehavior) {
     return Promise.all([
         lookupDefaultZoom(streamInfo),
         lookupInitialZoom(streamInfo)
     ]).then(function(zoomFactors) {
       return new BrowserApi(
-          streamInfo, zoomFactors[0], zoomFactors[1], manageZoom);
+          streamInfo, zoomFactors[0], zoomFactors[1], zoomBehavior);
     });
   }
 
@@ -103,8 +103,8 @@
    *     has been updated.
    */
   setZoom(zoom) {
-    if (!this.manageZoom_)
-      return Promise.resolve();
+    if (this.zoomBehavior_ != BrowserApi.ZoomBehavior.MANAGE)
+      return Promise.reject(new Error('Viewer does not manage browser zoom.'));
     return new Promise(function(resolve, reject) {
       chrome.tabs.setZoom(this.streamInfo_.tabId, zoom, resolve);
     }.bind(this));
@@ -127,12 +127,21 @@
   }
 
   /**
+   * Returns how to manage the zoom.
+   * @return {BrowserApi.ZoomBehavior} How to manage zoom.
+   */
+  getZoomBehavior() {
+    return this.zoomBehavior_;
+  }
+
+  /**
    * Adds an event listener to be notified when the browser zoom changes.
    * @param {function} listener The listener to be called with the new zoom
    *     factor.
    */
   addZoomEventListener(listener) {
-    if (!this.manageZoom_)
+    if (!(this.zoomBehavior_ == BrowserApi.ZoomBehavior.MANAGE ||
+          this.zoomBehavior_ == BrowserApi.ZoomBehavior.PROPAGATE_PARENT))
       return;
 
     chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) {
@@ -144,6 +153,16 @@
 };
 
 /**
+ * Enumeration of ways to manage zoom changes.
+ * @enum {number}
+ */
+BrowserApi.ZoomBehavior = {
+  NONE: 0,
+  MANAGE: 1,
+  PROPAGATE_PARENT: 2
+};
+
+/**
  * Creates a BrowserApi for an extension running as a mime handler.
  * @return {Promise<BrowserApi>} A promise to a BrowserApi instance constructed
  *     using the mimeHandlerPrivate API.
@@ -153,8 +172,11 @@
     chrome.mimeHandlerPrivate.getStreamInfo(resolve);
   }).then(function(streamInfo) {
     let promises = [];
-    let manageZoom = !streamInfo.embedded && streamInfo.tabId != -1;
+    let zoomBehavior = BrowserApi.ZoomBehavior.NONE;
     if (streamInfo.tabId != -1) {
+      zoomBehavior = streamInfo.embedded ?
+                      BrowserApi.ZoomBehavior.PROPAGATE_PARENT :
+                      BrowserApi.ZoomBehavior.MANAGE;
       promises.push(new Promise(function(resolve) {
         chrome.tabs.get(streamInfo.tabId, resolve);
       }).then(function(tab) {
@@ -162,14 +184,14 @@
           streamInfo.tabUrl = tab.url;
       }));
     }
-    if (manageZoom) {
+    if (zoomBehavior == BrowserApi.ZoomBehavior.MANAGE) {
       promises.push(new Promise(function(resolve) {
         chrome.tabs.setZoomSettings(
             streamInfo.tabId, {mode: 'manual', scope: 'per-tab'}, resolve);
       }));
     }
     return Promise.all(promises).then(
-        function() { return BrowserApi.create(streamInfo, manageZoom); });
+        function() { return BrowserApi.create(streamInfo, zoomBehavior); });
   });
 }
 
@@ -197,7 +219,9 @@
       streamInfo.tabUrl = tab.url;
       resolve();
     });
-  }).then(function() { return BrowserApi.create(streamInfo, false); });
+  }).then(function() {
+    return BrowserApi.create(streamInfo, BrowserApi.ZoomBehavior.NONE);
+  });
 }
 
 /**
diff --git a/chrome/browser/resources/pdf/pdf.js b/chrome/browser/resources/pdf/pdf.js
index 14aebcf..8db8ba16 100644
--- a/chrome/browser/resources/pdf/pdf.js
+++ b/chrome/browser/resources/pdf/pdf.js
@@ -131,13 +131,16 @@
   var shortWindow = window.innerHeight < PDFViewer.TOOLBAR_WINDOW_MIN_HEIGHT;
   var topToolbarHeight =
       (toolbarEnabled) ? PDFViewer.MATERIAL_TOOLBAR_HEIGHT : 0;
+  var defaultZoom =
+      this.browserApi_.getZoomBehavior() == BrowserApi.ZoomBehavior.MANAGE ?
+      this.browserApi_.getDefaultZoom() : 1.0;
   this.viewport_ = new Viewport(window,
                                 this.sizer_,
                                 this.viewportChanged_.bind(this),
                                 this.beforeZoom_.bind(this),
                                 this.afterZoom_.bind(this),
                                 getScrollbarWidth(),
-                                this.browserApi_.getDefaultZoom(),
+                                defaultZoom,
                                 topToolbarHeight);
 
   // Create the plugin object dynamically so we can set its src. The plugin
@@ -230,9 +233,11 @@
       new ToolbarManager(window, this.toolbar_, this.zoomToolbar_);
 
   // Set up the ZoomManager.
-  this.zoomManager_ = new ZoomManager(
-      this.viewport_, this.browserApi_.setZoom.bind(this.browserApi_),
+  this.zoomManager_ = ZoomManager.create(
+      this.browserApi_.getZoomBehavior(), this.viewport_,
+      this.browserApi_.setZoom.bind(this.browserApi_),
       this.browserApi_.getInitialZoom());
+  this.viewport_.zoomManager = this.zoomManager_;
   this.browserApi_.addZoomEventListener(
       this.zoomManager_.onBrowserZoomChange.bind(this.zoomManager_));
 
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 80a5a06..607f70ed 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -72,7 +72,8 @@
   this.beforeZoomCallback_ = beforeZoomCallback;
   this.afterZoomCallback_ = afterZoomCallback;
   this.allowedToChangeZoom_ = false;
-  this.zoom_ = 1;
+  this.internalZoom_ = 1;
+  this.zoomManager_ = new InactiveZoomManager(this, 1);
   this.documentDimensions_ = null;
   this.pageDimensions_ = [];
   this.scrollbarWidth_ = scrollbarWidth;
@@ -199,7 +200,7 @@
    *     respectively.
    */
   documentHasScrollbars: function() {
-    return this.documentNeedsScrollbars_(this.zoom_);
+    return this.documentNeedsScrollbars_(this.zoom);
   },
 
   /**
@@ -207,7 +208,7 @@
    * Helper function called when the zoomed document size changes.
    */
   contentSizeChanged_: function() {
-    var zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom_);
+    var zoomedDimensions = this.getZoomedDocumentDimensions_(this.zoom);
     if (zoomedDimensions) {
       this.sizer_.style.width = zoomedDimensions.width + 'px';
       this.sizer_.style.height = zoomedDimensions.height +
@@ -258,7 +259,7 @@
    * @type {Object} the size of the viewport excluding scrollbars.
    */
   get size() {
-    var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
+    var needsScrollbars = this.documentNeedsScrollbars_(this.zoom);
     var scrollbarWidth = needsScrollbars.vertical ? this.scrollbarWidth_ : 0;
     var scrollbarHeight = needsScrollbars.horizontal ? this.scrollbarWidth_ : 0;
     return {
@@ -271,7 +272,15 @@
    * @type {number} the zoom level of the viewport.
    */
   get zoom() {
-    return this.zoom_;
+    return this.zoomManager_.applyBrowserZoom(this.internalZoom_);
+  },
+
+  /**
+   * Set the zoom manager.
+   * @type {ZoomManager} manager the zoom manager to set.
+   */
+  set zoomManager(manager) {
+    this.zoomManager_ = manager;
   },
 
   /**
@@ -324,15 +333,15 @@
     }
     // Record the scroll position (relative to the top-left of the window).
     var currentScrollPos = {
-      x: this.position.x / this.zoom_,
-      y: this.position.y / this.zoom_
+      x: this.position.x / this.zoom,
+      y: this.position.y / this.zoom
     };
-    this.zoom_ = newZoom;
+    this.internalZoom_ = newZoom;
     this.contentSizeChanged_();
     // Scroll to the scaled scroll position.
     this.position = {
-      x: currentScrollPos.x * newZoom,
-      y: currentScrollPos.y * newZoom
+      x: currentScrollPos.x * this.zoom,
+      y: currentScrollPos.y * this.zoom
     };
   },
 
@@ -347,7 +356,7 @@
     assert(this.allowedToChangeZoom_,
         'Called Viewport.setPinchZoomInternal_ without calling ' +
         'Viewport.mightZoom_.');
-    this.zoom_ = clampScale(this.zoom_ * scaleDelta);
+    this.internalZoom_ = clampScale(this.internalZoom_ * scaleDelta);
 
     var newCenterInContent = this.frameToContent(center);
     var delta = {
@@ -357,8 +366,8 @@
 
     // Record the scroll position (relative to the pinch center).
     var currentScrollPos = {
-      x: this.position.x - delta.x * this.zoom_,
-      y: this.position.y - delta.y * this.zoom_
+      x: this.position.x - delta.x * this.zoom,
+      y: this.position.y - delta.y * this.zoom
     };
 
     this.contentSizeChanged_();
@@ -379,8 +388,8 @@
     // TODO(mcnee) Add a helper Point class to avoid duplicating operations
     // on plain {x,y} objects.
     return {
-      x: (framePoint.x + this.position.x) / this.zoom_,
-      y: (framePoint.y + this.position.y) / this.zoom_
+      x: (framePoint.x + this.position.x) / this.zoom,
+      y: (framePoint.y + this.position.y) / this.zoom
     };
   },
 
@@ -399,6 +408,29 @@
   },
 
   /**
+   * Gets notified of the browser zoom changing seperately from the
+   * internal zoom.
+   * @param {number} oldBrowserZoom the previous value of the browser zoom.
+   */
+  updateZoomFromBrowserChange: function(oldBrowserZoom) {
+    this.mightZoom_(function() {
+      // Record the scroll position (relative to the top-left of the window).
+      var oldZoom = oldBrowserZoom * this.internalZoom_;
+      var currentScrollPos = {
+        x: this.position.x / oldZoom,
+        y: this.position.y / oldZoom
+      };
+      this.contentSizeChanged_();
+      // Scroll to the scaled scroll position.
+      this.position = {
+        x: currentScrollPos.x * this.zoom,
+        y: currentScrollPos.y * this.zoom
+      };
+      this.updateViewport_();
+    }.bind(this));
+  },
+
+  /**
    * @type {number} the width of scrollbars in the viewport in pixels.
    */
   get scrollbarWidth() {
@@ -448,15 +480,15 @@
    * @return {int} the index of the most visible page.
    */
   getMostVisiblePage: function() {
-    var firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom_);
+    var firstVisiblePage = this.getPageAtY_(this.position.y / this.zoom);
     if (firstVisiblePage == this.pageDimensions_.length - 1)
       return firstVisiblePage;
 
     var viewportRect = {
-      x: this.position.x / this.zoom_,
-      y: this.position.y / this.zoom_,
-      width: this.size.width / this.zoom_,
-      height: this.size.height / this.zoom_
+      x: this.position.x / this.zoom,
+      y: this.position.y / this.zoom,
+      width: this.size.width / this.zoom,
+      height: this.size.height / this.zoom
     };
     var firstVisiblePageVisibility = getIntersectionHeight(
         this.pageDimensions_[firstVisiblePage], viewportRect) /
@@ -477,7 +509,7 @@
    * @param {Object} pageDimensions the dimensions of a given page
    * @param {boolean} widthOnly a bool indicating whether fit-to-page or
    *     fit-to-width should be computed.
-   * @return {number} the zoom to use
+   * @return {number} the internal zoom to set
    */
   computeFittingZoom_: function(pageDimensions, widthOnly) {
     // First compute the zoom without scrollbars.
@@ -528,7 +560,7 @@
       zoomHeight = windowWithScrollbars.height / pageDimensions.height;
       zoom = Math.min(zoomWidth, zoomHeight);
     }
-    return zoom;
+    return this.zoomManager_.internalZoomComponent(zoom);
   },
 
   /**
@@ -570,7 +602,7 @@
       if (scrollToTopOfPage) {
         this.position = {
           x: 0,
-          y: this.pageDimensions_[page].y * this.zoom_
+          y: this.pageDimensions_[page].y * this.zoom
         };
       }
       this.updateViewport_();
@@ -593,7 +625,7 @@
       this.fittingType_ = Viewport.FittingType.NONE;
       var nextZoom = Viewport.ZOOM_FACTORS[0];
       for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) {
-        if (Viewport.ZOOM_FACTORS[i] < this.zoom_)
+        if (Viewport.ZOOM_FACTORS[i] < this.internalZoom_)
           nextZoom = Viewport.ZOOM_FACTORS[i];
       }
       this.setZoomInternal_(nextZoom);
@@ -609,7 +641,7 @@
       this.fittingType_ = Viewport.FittingType.NONE;
       var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
       for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
-        if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
+        if (Viewport.ZOOM_FACTORS[i] > this.internalZoom_)
           nextZoom = Viewport.ZOOM_FACTORS[i];
       }
       this.setZoomInternal_(nextZoom);
@@ -632,7 +664,8 @@
           vectorDelta(e.center, this.firstPinchCenterInFrame_);
 
       var needsScrollbars = this.documentNeedsScrollbars_(
-          clampScale(this.zoom_ * scaleDelta));
+          this.zoomManager_.applyBrowserZoom(
+              clampScale(this.internalZoom_ * scaleDelta)));
 
       this.pinchCenter_ = e.center;
 
@@ -666,7 +699,7 @@
     this.oldCenterInContent =
         this.frameToContent(frameToPluginCoordinate(e.center));
 
-    var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
+    var needsScrollbars = this.documentNeedsScrollbars_(this.zoom);
     this.keepContentCentered_ = !needsScrollbars.horizontal;
     // We keep track of begining of the pinch.
     // By doing so we will be able to compute the pan distance.
@@ -710,8 +743,8 @@
       if (this.fittingType_ != Viewport.FittingType.FIT_TO_PAGE)
         toolbarOffset = this.topToolbarHeight_;
       this.position = {
-        x: dimensions.x * this.zoom_,
-        y: dimensions.y * this.zoom_ - toolbarOffset
+        x: dimensions.x * this.zoom,
+        y: dimensions.y * this.zoom - toolbarOffset
       };
       this.updateViewport_();
     }.bind(this));
@@ -778,14 +811,14 @@
     // Compute the space on the left of the document if the document fits
     // completely in the screen.
     var spaceOnLeft = (this.size.width -
-        this.documentDimensions_.width * this.zoom_) / 2;
+        this.documentDimensions_.width * this.zoom) / 2;
     spaceOnLeft = Math.max(spaceOnLeft, 0);
 
     return {
-      x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset,
-      y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset,
-      width: insetDimensions.width * this.zoom_,
-      height: insetDimensions.height * this.zoom_
+      x: x * this.zoom + spaceOnLeft - this.window_.pageXOffset,
+      y: insetDimensions.y * this.zoom - this.window_.pageYOffset,
+      width: insetDimensions.width * this.zoom,
+      height: insetDimensions.height * this.zoom
     };
   }
 };
diff --git a/chrome/browser/resources/pdf/zoom_manager.js b/chrome/browser/resources/pdf/zoom_manager.js
index 2b52c54..2508693 100644
--- a/chrome/browser/resources/pdf/zoom_manager.js
+++ b/chrome/browser/resources/pdf/zoom_manager.js
@@ -5,20 +5,109 @@
 'use strict';
 
 /**
- * A class that manages updating the browser with zoom changes.
+ * Abstract parent of classes that manage updating the browser
+ * with zoom changes and/or updating the viewer's zoom when
+ * the browser zoom changes.
  */
 class ZoomManager {
   /**
-   * Constructs a ZoomManager
+   * Constructs a ZoomManager.
+   * @param {!Viewport} viewport A Viewport for which to manage zoom.
+   * @param {number} initialZoom The initial browser zoom level.
+   */
+  constructor(viewport, initialZoom) {
+    if (this.constructor === ZoomManager) {
+      throw new TypeError('Instantiated abstract class: ZoomManager');
+    }
+    this.viewport_ = viewport;
+    this.browserZoom_ = initialZoom;
+  }
+
+  /**
+   * Creates the appropriate kind of zoom manager given the zoom behavior.
+   * @param {BrowserApi.ZoomBehavior} zoomBehavior How to manage zoom.
+   * @param {!Viewport} viewport A Viewport for which to manage zoom.
+   * @param {Function} setBrowserZoomFunction A function that sets the browser
+   *     zoom to the provided value.
+   * @param {number} initialZoom The initial browser zoom level.
+   */
+  static create(zoomBehavior, viewport, setBrowserZoomFunction, initialZoom) {
+    switch (zoomBehavior) {
+      case BrowserApi.ZoomBehavior.MANAGE:
+        return new ActiveZoomManager(
+            viewport, setBrowserZoomFunction, initialZoom);
+      case BrowserApi.ZoomBehavior.PROPAGATE_PARENT:
+        return new EmbeddedZoomManager(viewport, initialZoom);
+      default:
+        return new InactiveZoomManager(viewport, initialZoom);
+    }
+  }
+
+  /**
+   * Invoked when a browser-initiated zoom-level change occurs.
+   * @param {number} newZoom the zoom level to zoom to.
+   */
+  onBrowserZoomChange(newZoom) {}
+
+  /**
+   * Invoked when an extension-initiated zoom-level change occurs.
+   */
+  onPdfZoomChange() {}
+
+  /**
+   * Combines the internal pdf zoom and the browser zoom to
+   * produce the total zoom level for the viewer.
+   * @param {number} internalZoom the zoom level internal to the viewer.
+   * @return {number} the total zoom level.
+   */
+  applyBrowserZoom(internalZoom) {
+    return this.browserZoom_ * internalZoom;
+  }
+
+  /**
+   * Given a zoom level, return the internal zoom level needed to
+   * produce that zoom level.
+   * @param {number} totalZoom the total zoom level.
+   * @return {number} the zoom level internal to the viewer.
+   */
+  internalZoomComponent(totalZoom) {
+    return totalZoom / this.browserZoom_;
+  }
+
+  /**
+   * Returns whether two numbers are approximately equal.
+   * @param {number} a The first number.
+   * @param {number} b The second number.
+   */
+  floatingPointEquals(a, b) {
+    let MIN_ZOOM_DELTA = 0.01;
+    // If the zoom level is close enough to the current zoom level, don't
+    // change it. This avoids us getting into an infinite loop of zoom changes
+    // due to floating point error.
+    return Math.abs(a - b) <= MIN_ZOOM_DELTA;
+  }
+};
+
+/**
+ * InactiveZoomManager has no control over the browser's zoom
+ * and does not respond to browser zoom changes.
+ */
+class InactiveZoomManager extends ZoomManager {};
+
+/**
+ * ActiveZoomManager controls the browser's zoom.
+ */
+class ActiveZoomManager extends ZoomManager {
+  /**
+   * Constructs a ActiveZoomManager.
    * @param {!Viewport} viewport A Viewport for which to manage zoom.
    * @param {Function} setBrowserZoomFunction A function that sets the browser
    *     zoom to the provided value.
    * @param {number} initialZoom The initial browser zoom level.
    */
   constructor(viewport, setBrowserZoomFunction, initialZoom) {
-    this.viewport_ = viewport;
+    super(viewport, initialZoom);
     this.setBrowserZoomFunction_ = setBrowserZoomFunction;
-    this.browserZoom_ = initialZoom;
     this.changingBrowserZoom_ = null;
   }
 
@@ -69,15 +158,42 @@
   }
 
   /**
-   * Returns whether two numbers are approximately equal.
-   * @param {number} a The first number.
-   * @param {number} b The second number.
+   * Combines the internal pdf zoom and the browser zoom to
+   * produce the total zoom level for the viewer.
+   * @param {number} internalZoom the zoom level internal to the viewer.
+   * @return {number} the total zoom level.
    */
-  floatingPointEquals(a, b) {
-    let MIN_ZOOM_DELTA = 0.01;
-    // If the zoom level is close enough to the current zoom level, don't
-    // change it. This avoids us getting into an infinite loop of zoom changes
-    // due to floating point error.
-    return Math.abs(a - b) <= MIN_ZOOM_DELTA;
+  applyBrowserZoom(internalZoom) {
+    // The internal zoom and browser zoom are changed together, so the
+    // browser zoom is already applied.
+    return internalZoom;
+  }
+
+  /**
+   * Given a zoom level, return the internal zoom level needed to
+   * produce that zoom level.
+   * @param {number} totalZoom the total zoom level.
+   * @return {number} the zoom level internal to the viewer.
+   */
+  internalZoomComponent(totalZoom) {
+    // The internal zoom and browser zoom are changed together, so the
+    // internal zoom is the total zoom.
+    return totalZoom;
+  }
+};
+
+/**
+ * This EmbeddedZoomManager responds to changes in the browser zoom,
+ * but does not control the browser zoom.
+ */
+class EmbeddedZoomManager extends ZoomManager {
+  /**
+   * Invoked when a browser-initiated zoom-level change occurs.
+   * @param {number} newZoom the new browser zoom level.
+   */
+  onBrowserZoomChange(newZoom) {
+    let oldZoom = this.browserZoom_;
+    this.browserZoom_ = newZoom;
+    this.viewport_.updateZoomFromBrowserChange(oldZoom);
   }
 };
diff --git a/chrome/browser/resources/profiler/profiler.html b/chrome/browser/resources/profiler/profiler.html
index fb6c4977..cc5638f 100644
--- a/chrome/browser/resources/profiler/profiler.html
+++ b/chrome/browser/resources/profiler/profiler.html
@@ -106,12 +106,22 @@
 </style>
 </head>
 <body>
-  <b>Save:</b><button id=save-snapshots-button>Save</button>
-  <b>Restore:</b> <input type=file id=snapshot-file-loader>
-  <span id=file-load-error hidden class=errormsg></span>
-
+  <table width=100%>
+    <tr>
+      <td>
+        <b>Save:</b><button id=save-snapshots-button>Save</button>
+        <b>Restore:</b> <input type=file id=snapshot-file-loader>
+        <span id=file-load-error hidden class=errormsg></span>
+      </td>
+      <td align=right>
+        <a target="_blank"
+          href="https://sites.google.com/a/chromium.org/dev/developers/threaded-task-tracking">
+          Profiler Documentation
+        </a>
+      </td>
+    </tr>
+  </table>
   <hr>
-
   <table width=100%>
     <tr>
       <td>
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 0608e5c..d657f1c5 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -82,17 +82,14 @@
             </div>
             <div class="settings-box two-line">
               <iron-icon
-                  hidden="[[!shouldShowUpdateStatusIcon_(
-                      obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"
+                  hidden="[[!showUpdateStatus_]]"
                   icon$="[[getIcon_(
                       obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"
                   src="[[getIconSrc_(
                       obsoleteSystemInfo_, currentUpdateStatusEvent_)]]">
               </iron-icon>
               <div class="start">
-                <div id="updateStatusMessage"
-                    hidden="[[!shouldShowUpdateStatusMessage_(
-                        obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"
+                <div id="updateStatusMessage" hidden="[[!showUpdateStatus_]]"
 <if expr="not chromeos">
                     inner-h-t-m-l="[[getUpdateStatusMessage_(
                         currentUpdateStatusEvent_)]]">
@@ -111,29 +108,20 @@
                 </span>
                 <div class="secondary copyable">$i18n{aboutBrowserVersion}</div>
               </div>
-              <span id="relaunchContainer"
-<if expr="not chromeos">
-                  hidden="[[!shouldShowRelaunch_(currentUpdateStatusEvent_)]]"
-</if>
-<if expr="chromeos">
-                  hidden="[[!shouldShowRelaunch_(
-                      currentUpdateStatusEvent_, targetChannel_)]]"
-</if>
-                  class="secondary-action">
+              <span id="buttonContainer" class="secondary-action"
+                  hidden="[[!showButtonContainer_]]">
                 <paper-button id="relaunch" class="secondary-button"
-                    on-tap="onRelaunchTap_">
+                    hidden="[[!showRelaunch_]]" on-tap="onRelaunchTap_">
                   $i18n{aboutRelaunch}
                 </paper-button>
 <if expr="chromeos">
                 <paper-button id="relaunchAndPowerwash" class="secondary-button"
-                    hidden="[[!shouldShowRelaunchAndPowerwash_(
-                        currentUpdateStatusEvent_, targetChannel_)]]"
+                    hidden="[[!showRelaunchAndPowerwash_]]"
                     on-tap="onRelaunchAndPowerwashTap_">
                   $i18n{aboutRelaunchAndPowerwash}
                 </paper-button>
                 <paper-button id="checkForUpdates" class="secondary-button"
-                    hidden="[[!shouldShowCheckUpdates_(
-                        currentUpdateStatusEvent_)]]"
+                    hidden="[[!showCheckUpdates_]]"
                     on-tap="onCheckUpdatesTap_">
                   $i18n{aboutCheckForUpdates}
                 </paper-button>
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index 966bee8..4869025 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -20,7 +20,10 @@
 
 <if expr="chromeos">
     /** @private */
-    hasCheckedForUpdates_: Boolean,
+    hasCheckedForUpdates_: {
+      type: Boolean,
+      value: false,
+    },
 
     /** @private {!BrowserChannel} */
     currentChannel_: String,
@@ -42,8 +45,52 @@
         };
       },
     },
+
+    /** @private */
+    showUpdateStatus_: Boolean,
+
+    /** @private */
+    showButtonContainer_: Boolean,
+
+    /** @private */
+    showRelaunch_: Boolean,
+
+<if expr="chromeos">
+    /** @private */
+    showRelaunchAndPowerwash_: {
+      type: Boolean,
+      computed: 'computeShowRelaunchAndPowerwash_(' +
+          'currentUpdateStatusEvent_, targetChannel_)',
+    },
+
+    /** @private */
+    showCheckUpdates_: {
+      type: Boolean,
+      computed: 'computeShowCheckUpdates_(currentUpdateStatusEvent_)',
+    },
+</if>
   },
 
+  observers: [
+<if expr="not chromeos">
+    'updateShowUpdateStatus_(' +
+        'obsoleteSystemInfo_, currentUpdateStatusEvent_)',
+    'updateShowRelaunch_(currentUpdateStatusEvent_)',
+    'updateShowButtonContainer_(showRelaunch_)',
+</if>
+
+<if expr="chromeos">
+    'updateShowUpdateStatus_(' +
+        'obsoleteSystemInfo_, currentUpdateStatusEvent_,' +
+        'hasCheckedForUpdates_)',
+    'updateShowRelaunch_(currentUpdateStatusEvent_, targetChannel_,' +
+        'currentChannel_)',
+    'updateShowButtonContainer_(' +
+        'showRelaunch_, showRelaunchAndPowerwash_, showCheckUpdates_)',
+</if>
+  ],
+
+
   /** @private {?settings.AboutPageBrowserProxy} */
   aboutBrowserProxy_: null,
 
@@ -63,13 +110,9 @@
       this.targetChannel_ = e.detail;
     }.bind(this));
 
-    Promise.all([
-      this.aboutBrowserProxy_.getCurrentChannel(),
-      this.aboutBrowserProxy_.getTargetChannel(),
-    ]).then(function(channels) {
-      this.currentChannel_ = channels[0];
-      this.targetChannel_ = channels[1];
-
+    this.aboutBrowserProxy_.getChannelInfo().then(function(info) {
+      this.currentChannel_ = info.currentChannel;
+      this.targetChannel_ = info.targetChannel;
       this.startListening_();
     }.bind(this));
 
@@ -112,38 +155,45 @@
     this.lifetimeBrowserProxy_.relaunch();
   },
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  shouldShowUpdateStatusMessage_: function() {
-    return this.currentUpdateStatusEvent_.status != UpdateStatus.DISABLED &&
+  /** @private */
+  updateShowUpdateStatus_: function() {
+<if expr="chromeos">
+    // Assume the "updated" status is stale if we haven't checked yet.
+    if (this.currentUpdateStatusEvent_.status == UpdateStatus.UPDATED &&
+        !this.hasCheckedForUpdates_) {
+      this.showUpdateStatus_ = false;
+      return;
+    }
+</if>
+    this.showUpdateStatus_ =
+        this.currentUpdateStatusEvent_.status != UpdateStatus.DISABLED &&
         !this.obsoleteSystemInfo_.endOfLine;
   },
 
   /**
-   * @return {boolean}
+   * Hide the button container if all buttons are hidden, otherwise the
+   * container displayes an unwanted border (see secondary-action class).
    * @private
    */
-  shouldShowUpdateStatusIcon_: function() {
-    return this.currentUpdateStatusEvent_.status != UpdateStatus.DISABLED ||
-        this.obsoleteSystemInfo_.endOfLine;
-  },
-
-  /**
-   * @return {boolean}
-   * @private
-   */
-  shouldShowRelaunch_: function() {
-    var shouldShow = false;
+  updateShowButtonContainer_: function() {
 <if expr="not chromeos">
-    shouldShow = this.checkStatus_(UpdateStatus.NEARLY_UPDATED);
+    this.showButtonContainer_ = this.showRelaunch_;
 </if>
 <if expr="chromeos">
-    shouldShow = this.checkStatus_(UpdateStatus.NEARLY_UPDATED) &&
+    this.showButtonContainer_ = this.showRelaunch_ ||
+        this.showRelaunchAndPowerwash_ || this.showCheckUpdates_;
+</if>
+  },
+
+  /** @private */
+  updateShowRelaunch_: function() {
+<if expr="not chromeos">
+    this.showRelaunch_ = this.checkStatus_(UpdateStatus.NEARLY_UPDATED);
+</if>
+<if expr="chromeos">
+    this.showRelaunch_ = this.checkStatus_(UpdateStatus.NEARLY_UPDATED) &&
         !this.isTargetChannelMoreStable_();
 </if>
-    return shouldShow;
   },
 
   /**
@@ -265,7 +315,7 @@
    * @return {boolean}
    * @private
    */
-  shouldShowRelaunchAndPowerwash_: function() {
+  computeShowRelaunchAndPowerwash_: function() {
     return this.checkStatus_(UpdateStatus.NEARLY_UPDATED) &&
         this.isTargetChannelMoreStable_();
   },
@@ -280,7 +330,7 @@
    * @return {boolean}
    * @private
    */
-  shouldShowCheckUpdates_: function() {
+  computeShowCheckUpdates_: function() {
     return !this.hasCheckedForUpdates_ ||
         this.checkStatus_(UpdateStatus.FAILED);
   },
diff --git a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
index 22c88f89c..b1ee8af3 100644
--- a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
@@ -18,6 +18,15 @@
 
 /**
  * @typedef {{
+ *   currentChannel: string,
+ *   targetChannel: string,
+ *   canChangeChannel: boolean,
+ * }}
+ */
+var ChannelInfo;
+
+/**
+ * @typedef {{
  *   arcVersion: string,
  *   osFirmware: string,
  *   osVersion: string,
@@ -130,11 +139,8 @@
      */
     setChannel: function(channel, isPowerwashAllowed) {},
 
-    /** @return {!Promise<!BrowserChannel>} */
-    getCurrentChannel: function() {},
-
-    /** @return {!Promise<!BrowserChannel>} */
-    getTargetChannel: function() {},
+    /** @return {!Promise<!ChannelInfo>} */
+    getChannelInfo: function() {},
 
     /** @return {!Promise<!VersionInfo>} */
     getVersionInfo: function() {},
@@ -186,13 +192,8 @@
     },
 
     /** @override */
-    getCurrentChannel: function() {
-      return cr.sendWithPromise('getCurrentChannel');
-    },
-
-    /** @override */
-    getTargetChannel: function() {
-      return cr.sendWithPromise('getTargetChannel');
+    getChannelInfo: function() {
+      return cr.sendWithPromise('getChannelInfo');
     },
 
     /** @override */
diff --git a/chrome/browser/resources/settings/about_page/channel_switcher_dialog.js b/chrome/browser/resources/settings/about_page/channel_switcher_dialog.js
index 14b01f6..3cd8913f 100644
--- a/chrome/browser/resources/settings/about_page/channel_switcher_dialog.js
+++ b/chrome/browser/resources/settings/about_page/channel_switcher_dialog.js
@@ -23,6 +23,9 @@
     /** @private {!BrowserChannel} */
     currentChannel_: String,
 
+    /** @private {!BrowserChannel} */
+    targetChannel_: String,
+
     /**
      * Controls which of the two action buttons is visible.
      * @private {?{changeChannel: boolean, changeChannelAndPowerwash: boolean}}
@@ -45,11 +48,11 @@
   /** @override */
   ready: function() {
     this.browserProxy_ = settings.AboutPageBrowserProxyImpl.getInstance();
-
-    this.browserProxy_.getCurrentChannel().then(function(channel) {
-      this.currentChannel_ = channel;
-      // Pre-populate radio group with current channel.
-      this.$$('paper-radio-group').select(channel);
+    this.browserProxy_.getChannelInfo().then(function(info) {
+      this.currentChannel_ = info.currentChannel;
+      this.targetChannel_ = info.targetChannel;
+      // Pre-populate radio group with target channel.
+      this.$$('paper-radio-group').select(this.targetChannel_);
     }.bind(this));
   },
 
@@ -118,30 +121,44 @@
   onChannelSelectionChanged_: function() {
     var selectedChannel = this.$$('paper-radio-group').selected;
 
-    if (selectedChannel == this.currentChannel_) {
+    // Selected channel is the same as the target channel so only show 'cancel'.
+    if (selectedChannel == this.targetChannel_) {
       this.shouldShowButtons_ = null;
       this.warning_ = null;
       return;
     }
 
+    // Selected channel is the same as the current channel, allow the user to
+    // change without warnings.
+    if (selectedChannel == this.currentChannel_) {
+      this.updateButtons_(true, false);
+      this.warning_ = null;
+      return;
+    }
+
     if (settings.isTargetChannelMoreStable(
         this.currentChannel_, selectedChannel)) {
+      // More stable channel selected. For non managed devices, notify the user
+      // about powerwash.
       if (loadTimeData.getBoolean('aboutEnterpriseManaged')) {
         this.updateWarning_(
-            'aboutDelayedWarningTitle',
-            'aboutDelayedWarningMessage',
+            'aboutDelayedWarningTitle', 'aboutDelayedWarningMessage',
             'aboutProductTitle');
         this.updateButtons_(true, false);
       } else {
         this.updateWarning_(
-          'aboutPowerwashWarningTitle', 'aboutPowerwashWarningMessage');
+            'aboutPowerwashWarningTitle', 'aboutPowerwashWarningMessage');
         this.updateButtons_(false, true);
       }
     } else {
-      this.updateWarning_(
-        'aboutUnstableWarningTitle',
-        'aboutUnstableWarningMessage',
-        'aboutProductTitle');
+      if (selectedChannel == BrowserChannel.DEV) {
+        // Dev channel selected, warn the user.
+        this.updateWarning_(
+            'aboutUnstableWarningTitle', 'aboutUnstableWarningMessage',
+            'aboutProductTitle');
+      } else {
+        this.warning_ = null;
+      }
       this.updateButtons_(true, false);
     }
   },
diff --git a/chrome/browser/resources/settings/about_page/compiled_resources2.gyp b/chrome/browser/resources/settings/about_page/compiled_resources2.gyp
index 8e694db2..b477f45 100644
--- a/chrome/browser/resources/settings/about_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/about_page/compiled_resources2.gyp
@@ -35,5 +35,13 @@
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
+    {
+      'target_name': 'channel_switcher_dialog',
+      'dependencies': [
+        'about_page_browser_proxy',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+      ],
+      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
   ],
 }
diff --git a/chrome/browser/resources/settings/about_page/detailed_build_info.html b/chrome/browser/resources/settings/about_page/detailed_build_info.html
index 68a8ed7..5ac6d63 100644
--- a/chrome/browser/resources/settings/about_page/detailed_build_info.html
+++ b/chrome/browser/resources/settings/about_page/detailed_build_info.html
@@ -15,6 +15,10 @@
         -webkit-user-select: text;
       }
 
+      iron-icon {
+        padding: 8px;
+      }
+
       /* The command line string can contain very long substrings that
        * don't have any spaces, need to force a line break in such cases. */
       #command-line {
diff --git a/chrome/browser/resources/settings/about_page/detailed_build_info.js b/chrome/browser/resources/settings/about_page/detailed_build_info.js
index ab30170..685be3b4 100644
--- a/chrome/browser/resources/settings/about_page/detailed_build_info.js
+++ b/chrome/browser/resources/settings/about_page/detailed_build_info.js
@@ -23,12 +23,7 @@
     showChannelSwitcherDialog_: Boolean,
 
     /** @private */
-    canChangeChannel_: {
-      type: Boolean,
-      value: function() {
-        return loadTimeData.getBoolean('aboutCanChangeChannel');
-      },
-    },
+    canChangeChannel_: Boolean,
   },
 
   /** @override */
@@ -39,10 +34,19 @@
     browserProxy.getVersionInfo().then(function(versionInfo) {
       this.versionInfo_ = versionInfo;
     }.bind(this));
-    browserProxy.getCurrentChannel().then(function(channel) {
+
+    this.updateChannelInfo_();
+  },
+
+  /** @private */
+  updateChannelInfo_: function() {
+    var browserProxy = settings.AboutPageBrowserProxyImpl.getInstance();
+    browserProxy.getChannelInfo().then(function(info) {
+      // Display the target channel for the 'Currently on' message.
       this.currentlyOnChannelText_ = this.i18n(
           'aboutCurrentlyOnChannel',
-          this.i18n(settings.browserChannelToI18nId(channel)));
+          this.i18n(settings.browserChannelToI18nId(info.targetChannel)));
+      this.canChangeChannel_ = info.canChangeChannel;
     }.bind(this));
   },
 
@@ -66,6 +70,7 @@
       // previous dialog's contents are cleared.
       dialog.addEventListener('close', function() {
         this.showChannelSwitcherDialog_ = false;
+        this.updateChannelInfo_();
       }.bind(this));
     }.bind(this));
   },
diff --git a/chrome/browser/resources/settings/compiled_resources2.gyp b/chrome/browser/resources/settings/compiled_resources2.gyp
index a5cce78..74bb172d 100644
--- a/chrome/browser/resources/settings/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -25,6 +25,7 @@
       'target_name': 'global_scroll_target_behavior',
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        'route',
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
diff --git a/chrome/browser/resources/settings/device_page/display.js b/chrome/browser/resources/settings/device_page/display.js
index 0f71276..24d43749 100644
--- a/chrome/browser/resources/settings/device_page/display.js
+++ b/chrome/browser/resources/settings/device_page/display.js
@@ -158,6 +158,7 @@
     }
     this.maxModeIndex_ = this.selectedDisplay.modes.length - 1;
     this.selectedModeIndex_ = this.getSelectedModeIndex_(this.selectedDisplay);
+    this.immediateSelectedModeIndex_ = this.selectedModeIndex_;
   },
 
   /**
@@ -243,21 +244,25 @@
   },
 
   /**
-   * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay
-   * @param {number} immediateSelectedModeIndex
    * @return {string}
    * @private
    */
-  getResolutionText_: function(selectedDisplay, immediateSelectedModeIndex) {
+  getResolutionText_: function() {
     if (this.selectedDisplay.modes.length == 0) {
-      var widthStr = selectedDisplay.bounds.width.toString();
-      var heightStr = selectedDisplay.bounds.height.toString();
+      var widthStr = this.selectedDisplay.bounds.width.toString();
+      var heightStr = this.selectedDisplay.bounds.height.toString();
       return this.i18n('displayResolutionText', widthStr, heightStr);
     }
-    if (isNaN(immediateSelectedModeIndex))
-      immediateSelectedModeIndex = this.getSelectedModeIndex_(selectedDisplay);
-    var mode = selectedDisplay.modes[immediateSelectedModeIndex];
-    var best = selectedDisplay.isInternal ? mode.uiScale == 1.0 : mode.isNative;
+    // immediateSelectedModeIndex_ is bound to paper-slider.immediate-value
+    // which may not be valid, or may not have updated yet when this is called.
+    if (isNaN(this.immediateSelectedModeIndex_) ||
+        this.immediateSelectedModeIndex_ >= this.selectedDisplay.modes.length) {
+      this.immediateSelectedModeIndex_ =
+          this.getSelectedModeIndex_(this.selectedDisplay);
+    }
+    var mode = this.selectedDisplay.modes[this.immediateSelectedModeIndex_];
+    var best =
+        this.selectedDisplay.isInternal ? mode.uiScale == 1.0 : mode.isNative;
     var widthStr = mode.width.toString();
     var heightStr = mode.height.toString();
     if (best)
diff --git a/chrome/browser/resources/settings/device_page/pointers.html b/chrome/browser/resources/settings/device_page/pointers.html
index 6db7f43..fe22455 100644
--- a/chrome/browser/resources/settings/device_page/pointers.html
+++ b/chrome/browser/resources/settings/device_page/pointers.html
@@ -9,17 +9,24 @@
 <dom-module id="settings-pointers">
   <template>
     <style include="settings-shared">
-      .subsection > * {
-        -webkit-padding-end: 20px;
-        -webkit-padding-start: 56px;
+      h2 {
+        -webkit-padding-start: var(--settings-box-row-padding);
       }
 
-      .subsection h2 {
-        padding: 0 20px;
+      /* Indent layout elements under .subsection, using margin instead of
+       * padding so the borders line up. */
+      .subsection > .settings-box {
+        margin: 0 calc(
+            var(--settings-box-row-padding) + var(--settings-indent-width));
+        padding: 0;
+      }
+
+      .subsection > .list-frame {
+        -webkit-margin-start: var(--settings-indent-width);
       }
     </style>
     <div id="mouse" hidden$="[[!hasMouse]]"
-        class$="[[getSectionClass_(hasMouse, hasTouchpad)]]">
+        class$="[[getSubsectionClass_(hasMouse, hasTouchpad)]]">
       <!-- Subsection title only appears if both mouse and touchpad exist. -->
       <h2 hidden$="[[!hasTouchpad]]">$i18n{mouseTitle}</h2>
       <div class="settings-box first">
@@ -41,7 +48,7 @@
       </div>
     </div>
     <div id="touchpad" hidden$="[[!hasTouchpad]]"
-        class$="[[getSectionClass_(hasMouse, hasTouchpad)]]">
+        class$="[[getSubsectionClass_(hasMouse, hasTouchpad)]]">
       <!-- Subsection title only appears if both mouse and touchpad exist. -->
       <h2 hidden$="[[!hasMouse]]">$i18n{touchpadTitle}</h2>
       <div class="settings-box block first">
diff --git a/chrome/browser/resources/settings/device_page/pointers.js b/chrome/browser/resources/settings/device_page/pointers.js
index 51ff1ef..7ee0a91 100644
--- a/chrome/browser/resources/settings/device_page/pointers.js
+++ b/chrome/browser/resources/settings/device_page/pointers.js
@@ -49,7 +49,7 @@
   },
 
   // Mouse and touchpad sections are only subsections if they are both present.
-  getSectionClass_: function(hasMouse, hasTouchpad) {
+  getSubsectionClass_: function(hasMouse, hasTouchpad) {
     return hasMouse && hasTouchpad ? 'subsection' : '';
   },
 });
diff --git a/chrome/browser/resources/settings/global_scroll_target_behavior.html b/chrome/browser/resources/settings/global_scroll_target_behavior.html
index 71c083ae..9d3f252 100644
--- a/chrome/browser/resources/settings/global_scroll_target_behavior.html
+++ b/chrome/browser/resources/settings/global_scroll_target_behavior.html
@@ -1,2 +1,3 @@
 <link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="/route.html">
 <script src="global_scroll_target_behavior.js"></script>
diff --git a/chrome/browser/resources/settings/global_scroll_target_behavior.js b/chrome/browser/resources/settings/global_scroll_target_behavior.js
index 8062363..5a9c0a9a 100644
--- a/chrome/browser/resources/settings/global_scroll_target_behavior.js
+++ b/chrome/browser/resources/settings/global_scroll_target_behavior.js
@@ -4,15 +4,21 @@
 
 /**
  * @fileoverview |GlobalScrollTargetBehavior| allows an element to be aware of
- * the global scroll target. |scrollTarget| will be populated async by
- * |setGlobalScrollTarget|. |setGlobalScrollTarget| should only be called once.
+ * the global scroll target.
+ *
+ * |scrollTarget| will be populated async by |setGlobalScrollTarget|.
+ *
+ * |subpageScrollTarget| will be equal to the |scrollTarget|, but will only be
+ * populated when the current route is the |subpageRoute|.
+ *
+ * |setGlobalScrollTarget| should only be called once.
  */
 
 cr.define('settings', function() {
   var scrollTargetResolver = new PromiseResolver();
 
   /** @polymerBehavior */
-  var GlobalScrollTargetBehavior = {
+  var GlobalScrollTargetBehaviorImpl = {
     properties: {
       /**
        * Read only property for the scroll target.
@@ -22,12 +28,48 @@
         type: Object,
         readOnly: true,
       },
+
+      /**
+       * Read only property for the scroll target that a subpage should use.
+       * It will be set/cleared based on the current route.
+       * @type {HTMLElement}
+       */
+      subpageScrollTarget: {
+        type: Object,
+        computed: 'getActiveTarget_(scrollTarget, active_)',
+      },
+
+      /**
+       * The |subpageScrollTarget| should only be set for this route.
+       * @type {settings.Route}
+       * @private
+       */
+      subpageRoute: Object,
+
+      /** Whether the |subpageRoute| is active or not. */
+      active_: Boolean,
     },
 
     /** @override */
     attached: function() {
       scrollTargetResolver.promise.then(this._setScrollTarget.bind(this));
     },
+
+    /** @param {!settings.Route} route */
+    currentRouteChanged: function(route) {
+      this.active_ = route == this.subpageRoute;
+    },
+
+    /**
+     * Returns the target only when the route is active.
+     * @param {HTMLElement} target
+     * @param {boolean} active
+     * @return {?HTMLElement}
+     * @private
+     */
+    getActiveTarget_: function(target, active) {
+      return active ? target : null;
+    },
   };
 
   /**
@@ -39,7 +81,14 @@
   };
 
   return {
-    GlobalScrollTargetBehavior: GlobalScrollTargetBehavior,
+    GlobalScrollTargetBehaviorImpl: GlobalScrollTargetBehaviorImpl,
     setGlobalScrollTarget: setGlobalScrollTarget,
+    scrollTargetResolver: scrollTargetResolver,
   };
 });
+
+// This is done to make the closure compiler happy: it needs fully qualified
+// names when specifying an array of behaviors.
+/** @polymerBehavior */
+settings.GlobalScrollTargetBehavior =
+    [settings.RouteObserverBehavior, settings.GlobalScrollTargetBehaviorImpl];
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index 8f3f6071..75458f0 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -58,13 +58,19 @@
 
   /** @override */
   attached: function() {
-    chrome.management.onInstalled.addListener(
-        this.onExtensionAdded_.bind(this));
-    chrome.management.onEnabled.addListener(this.onExtensionAdded_.bind(this));
+    this.boundOnExtensionAdded_ = this.boundOnExtensionAdded_ ||
+        this.onExtensionAdded_.bind(this);
+    chrome.management.onInstalled.addListener(this.boundOnExtensionAdded_);
+    chrome.management.onEnabled.addListener(this.boundOnExtensionAdded_);
+
+    this.boundOnExtensionRemoved_ = this.boundOnExtensionRemoved_ ||
+        this.onExtensionRemoved_.bind(this);
     chrome.management.onUninstalled.addListener(
-        this.onExtensionRemoved_.bind(this));
-    chrome.management.onDisabled.addListener(
-        this.onExtensionDisabled_.bind(this));
+        this.boundOnExtensionRemoved_);
+
+    this.boundOnExtensionDisabled_ = this.boundOnExtensionDisabled_ ||
+        this.onExtensionDisabled_.bind(this);
+    chrome.management.onDisabled.addListener(this.boundOnExtensionDisabled_);
 
     chrome.management.getAll(this.onGetAllExtensions_.bind(this));
   },
@@ -72,16 +78,34 @@
   /** @override */
   detached: function() {
     chrome.management.onInstalled.removeListener(
-        this.onExtensionAdded_.bind(this));
+        assert(this.boundOnExtensionAdded_));
     chrome.management.onEnabled.removeListener(
-        this.onExtensionAdded_.bind(this));
+        assert(this.boundOnExtensionAdded_));
     chrome.management.onUninstalled.removeListener(
-        this.onExtensionRemoved_.bind(this));
+        assert(this.boundOnExtensionRemoved_));
     chrome.management.onDisabled.removeListener(
-        this.onExtensionDisabled_.bind(this));
+        assert(this.boundOnExtensionDisabled_));
   },
 
   /**
+   * Reference to the bound listener, such that it can be removed on detach.
+   * @private {Function}
+   */
+  boundOnExtensionAdded_: null,
+
+  /**
+   * Reference to the bound listener, such that it can be removed on detach.
+   * @private {Function}
+   */
+  boundOnExtensionRemoved_: null,
+
+  /**
+   * Reference to the bound listener, such that it can be removed on detach.
+   * @private {Function}
+   */
+  boundOnExtensionDisabled_: null,
+
+  /**
    * @param {!{detail: !CrOnc.NetworkStateProperties}} event
    * @private
    */
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index 1d52ea1..77a0bd0 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -36,7 +36,8 @@
           <template>
             <paper-checkbox class="list-item" checked="[[willAdd_(item.code)]]"
                 title$="[[item.nativeDisplayName]]"
-                on-change="onLanguageCheckboxChange_">
+                on-change="onLanguageCheckboxChange_"
+                tabindex$="[[tabIndex]]">
               [[item.displayName]]
             </paper-checkbox>
           </template>
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
index 43457ca..0f67c46 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
@@ -34,10 +34,24 @@
 
   attached: function() {
     this.$.dialog.showModal();
-    // Fire iron-resize after the list initially displays to prevent flickering.
-    setTimeout(function() {
-      this.$$('iron-list').fire('iron-resize');
-    }.bind(this));
+
+    // Prevent flashing the Cancel button's focus state.
+    this.$$('.cancel-button').blur();
+    setTimeout(this.afterShown_.bind(this));
+  },
+
+  /**
+   * Re-initializes the dialog after it is shown.
+   * @private
+   */
+  afterShown_: function() {
+    // Only fire iron-resize after the list displayed to prevent flickering.
+    this.$$('iron-list').fire('iron-resize');
+
+    // Focus the top checkbox, assuming there are languages left to enable.
+    var firstCheckbox = this.$$('iron-list paper-checkbox');
+    if (firstCheckbox)
+      firstCheckbox.focus();
   },
 
   /**
diff --git a/chrome/browser/resources/settings/languages_page/compiled_resources2.gyp b/chrome/browser/resources/settings/languages_page/compiled_resources2.gyp
index fdf6d9f7..164f93e 100644
--- a/chrome/browser/resources/settings/languages_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/languages_page/compiled_resources2.gyp
@@ -25,9 +25,10 @@
       'dependencies': [
         '../compiled_resources2.gyp:lifetime_browser_proxy',
         '../compiled_resources2.gyp:route',
-        '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu',
         '../settings_page/compiled_resources2.gyp:settings_animated_pages',
         '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-checkbox/compiled_resources2.gyp:paper-checkbox-extracted',
+        '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu',
+        '<(DEPTH)/ui/webui/resources/cr_elements/cr_expand_button/compiled_resources2.gyp:cr_expand_button',
         '<(DEPTH)/ui/webui/resources/cr_elements/cr_lazy_render/compiled_resources2.gyp:cr_lazy_render',
         '<(DEPTH)/ui/webui/resources/js/chromeos/compiled_resources2.gyp:ui_account_tweaks',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
index 1ad601dd..76cbc9e 100644
--- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
+++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -53,7 +53,8 @@
           <template>
             <div class="list-item">
               <div class="word text-elide">[[item]]</div>
-              <paper-icon-button icon="cr:clear" on-tap="onRemoveWordTap_">
+              <paper-icon-button icon="cr:clear" on-tap="onRemoveWordTap_"
+                  tabindex$="[[tabIndex]]">
               </paper-icon-button>
             </div>
           </template>
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 31eef10..d0dbab2d 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -1,3 +1,4 @@
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
@@ -70,7 +71,8 @@
     </settings-languages>
     <settings-animated-pages id="pages" section="languages">
       <neon-animatable route-path="default">
-        <div class$="settings-box first [[getLanguageListTwoLine_()]]">
+        <div class$="settings-box first [[getLanguageListTwoLine_()]]"
+            actionable on-tap="toggleExpandButton_">
           <div class="start">
             <div>$i18n{languagesListTitle}</div>
 <if expr="chromeos or is_win">
@@ -125,13 +127,14 @@
               </div>
             </template>
             <div class="list-item list-button" on-tap="onAddLanguagesTap_">
-              $i18n{addLanguages}
+              <a is="action-link">$i18n{addLanguages}</a>
             </div>
           </div>
         </iron-collapse>
 <if expr="chromeos">
-        <div id="manage-input-methods-subpage-trigger"
-              class="settings-box two-line">
+        <div id="manageInputMethodsSubpageTrigger"
+            class="settings-box two-line" actionable
+            on-tap="toggleExpandButton_">
           <div class="start">
             <div>$i18n{inputMethodsListTitle}</div>
             <div class="secondary">
@@ -149,7 +152,8 @@
                 items="[[languages.inputMethods.enabled]]">
               <div class$="list-item [[getInputMethodItemClass_(
                       item.id, languages.inputMethods.currentId)]]"
-                  on-tap="onInputMethodTap_" actionable>
+                  on-tap="onInputMethodTap_" on-keypress="onInputMethodTap_"
+                  actionable tabindex="0">
                 <div class="start">
                   <div>[[item.displayName]]</div>
                   <div class="explain-selected"
@@ -166,18 +170,19 @@
             </template>
             <div class="list-item list-button"
                 on-tap="onManageInputMethodsTap_">
-              $i18n{manageInputMethods}
+              <a is="action-link">$i18n{manageInputMethods}</a>
             </div>
           </div>
         </iron-collapse>
 </if>
 <if expr="not is_macosx">
-        <div id="spellcheck-subpage-trigger" class="settings-box two-line">
+        <div id="spellCheckSubpageTrigger"
+            class$="settings-box [[getSpellCheckListTwoLine_(
+                spellCheckSecondaryText_)]]"
+            actionable on-tap="toggleExpandButton_">
           <div class="start">
             <div>$i18n{spellCheckListTitle}</div>
-            <div class="secondary">
-              [[getSpellCheckSecondaryText_(languages.enabled.*)]]
-            </div>
+            <div class="secondary">[[spellCheckSecondaryText_]]</div>
           </div>
           <cr-expand-button expanded="{{spellCheckOpened_}}"
               alt="$i18n{spellCheckExpandA11yLabel}">
@@ -196,7 +201,7 @@
               </label>
             </template>
             <div class="list-item list-button" on-tap="onEditDictionaryTap_">
-              $i18n{manageSpellCheck}
+              <a is="action-link">$i18n{manageSpellCheck}</a>
             </div>
           </div>
         </iron-collapse>
@@ -253,7 +258,7 @@
 <if expr="chromeos">
       <template is="dom-if" route-path="/inputMethods">
         <settings-subpage
-            associated-control="[[$$('#manage-input-methods-subpage-trigger')]]"
+            associated-control="[[$$('#manageInputMethodsSubpageTrigger')]]"
             page-title="$i18n{manageInputMethodsPageTitle}">
           <settings-manage-input-methods-page languages="{{languages}}"
               language-helper="[[languageHelper]]">
@@ -264,7 +269,7 @@
 <if expr="not is_macosx">
       <template is="dom-if" route-path="/editDictionary">
         <settings-subpage
-            associated-control="[[$$('#spellcheck-subpage-trigger')]]"
+            associated-control="[[$$('#spellCheckSubpageTrigger')]]"
             page-title="$i18n{editDictionaryPageTitle}">
           <settings-edit-dictionary-page></settings-edit-dictionary-page>
         </settings-subpage>
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.js b/chrome/browser/resources/settings/languages_page/languages_page.js
index 07cbdd86..88378886 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -35,9 +35,10 @@
     languageHelper: Object,
 
     /** @private */
-    spellCheckSecondary_: {
+    spellCheckSecondaryText_: {
       type: String,
-      value: 'Placeholder, e.g. English (United States)',
+      value: '',
+      computed: 'getSpellCheckSecondaryText_(languages.enabled.*)',
     },
 
     /**
@@ -52,22 +53,6 @@
   },
 
   /**
-   * Handler for clicking a language on the main page, which selects the
-   * language as the prospective UI language on Chrome OS and Windows.
-   * @param {!Event} e The tap event.
-   */
-  onLanguageTap_: function(e) {
-    // Only change the UI language on platforms that allow it.
-    if ((!cr.isChromeOS && !cr.isWindows) || loadTimeData.getBoolean('isGuest'))
-      return;
-
-    // Set the prospective UI language. This won't take effect until a restart.
-    var tapEvent = /** @type {!{model: !{item: !LanguageState}}} */(e);
-    if (tapEvent.model.item.language.supportsUI)
-      this.languageHelper.setUILanguage(tapEvent.model.item.language.code);
-  },
-
-  /**
    * Handler for enabling or disabling spell check.
    * @param {!{target: Element, model: !{item: !LanguageState}}} e
    */
@@ -276,10 +261,12 @@
   },
 
   /**
-   * Handler for clicking an input method on the main page, which sets it as
-   * the current input method.
+   * Handler for tap and <Enter> events on an input method on the main page,
+   * which sets it as the current input method.
    * @param {!{model: !{item: !chrome.languageSettingsPrivate.InputMethod},
-   *           target: !{tagName: string}}} e
+   *           target: !{tagName: string},
+   *           type: string,
+   *           key: (string|undefined)}} e
    */
   onInputMethodTap_: function(e) {
     assert(cr.isChromeOS);
@@ -288,6 +275,10 @@
     if (e.target.tagName == 'PAPER-ICON-BUTTON')
       return;
 
+    // Ignore key presses other than <Enter>.
+    if (e.type == 'keypress' && e.key != 'Enter')
+      return;
+
     // Set the input method.
     this.languageHelper.setCurrentInputMethod(e.model.item.id);
   },
@@ -386,6 +377,14 @@
   },
 
   /**
+   * @return {string}
+   * @private
+   */
+  getSpellCheckListTwoLine_: function() {
+    return this.spellCheckSecondaryText_.length ? 'two-line' : '';
+  },
+
+  /**
    * Returns either the "selected" class, if the language matches the
    * prospective UI language, or an empty string. Languages can only be
    * selected on Chrome OS and Windows.
@@ -509,5 +508,22 @@
     settings.LifetimeBrowserProxyImpl.getInstance().restart();
 </if>
   },
+
+  /**
+   * Toggles the expand button within the element being listened to.
+   * @param {!Event} e
+   * @private
+   */
+  toggleExpandButton_: function(e) {
+    // The expand button handles toggling itself.
+    var expandButtonTag = 'CR-EXPAND-BUTTON';
+    if (e.target.tagName == expandButtonTag)
+      return;
+
+    /** @type {!CrExpandButtonElement} */
+    var expandButton = e.currentTarget.querySelector(expandButtonTag);
+    assert(expandButton);
+    expandButton.expanded = !expandButton.expanded;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
index 9aa5ecf..9b509bf5 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
@@ -80,7 +80,7 @@
       <iron-list id="passwordList"
           items="[[getFilteredPasswords_(savedPasswords, filter)]]"
           class="vertical-list list-with-header"
-          scroll-target="[[scrollTarget]]">
+          scroll-target="[[subpageScrollTarget]]">
         <template>
           <div class="list-item">
             <div class="website-column">
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
index 66e5a04..51377c7 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
@@ -55,6 +55,11 @@
       value: function() { return []; },
     },
 
+    /** @override */
+    subpageRoute: {
+      type: Object,
+      value: settings.Route.MANAGE_PASSWORDS,
+    },
 
     /**
      * The model for any password related action menus or dialogs.
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 777a0c4c3..719cf29 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -43,7 +43,7 @@
       #safeBrowsingExtendedReporting {
         align-items: center;
         display: flex;
-        min-height: var(--settings-row-min-height);       
+        min-height: var(--settings-row-min-height);
       }
 
       #metricsReportingCheckbox,
@@ -67,7 +67,7 @@
       #restart {
         flex: 1;
         text-align: end;
-      }      
+      }
     </style>
     <template is="dom-if" if="[[showClearBrowsingDataDialog_]]" restamp>
       <settings-clear-browsing-data-dialog prefs="{{prefs}}"
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
index 2cd34d3..0124a5f 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/html/icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
+<link rel="import" href="/controls/extension_controlled_indicator.html">
 <link rel="import" href="/search_engines_page/search_engine_dialog.html">
 <link rel="import" href="/search_engines_page/search_engine_entry_css.html">
 <link rel="import" href="/search_engines_page/search_engines_browser_proxy.html">
@@ -11,7 +12,7 @@
 <dom-module id="settings-search-engine-entry">
   <template>
     <style include="settings-shared search-engine-entry">
-      :host([is-default]) {
+      :host([is-default]) .list-item {
         font-weight: 500;
       }
 
@@ -24,6 +25,11 @@
       #url-column {
         flex: 4;
       }
+
+      :host(:not([show-dots_])) paper-icon-button {
+        pointer-events: none;
+        visibility: hidden;
+      }
     </style>
 
     <template is="dom-if" if="[[showEditSearchEngineDialog_]]" restamp>
@@ -52,6 +58,13 @@
             id="delete">$i18n{searchEnginesRemoveFromList}</button>
       </dialog>
     </div>
+    <template is="dom-if" if="[[engine.extension]]">
+      <extension-controlled-indicator
+          extension-id="[[engine.extension.id]]"
+          extension-name="[[engine.extension.name]]"
+          extension-can-be-disabled="[[engine.extension.canBeDisabled]]">
+      </extension-controlled-indicator>
+    </template>
   </template>
   <script src="search_engine_entry.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js
index 12f7d82..68529da 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js
@@ -13,15 +13,24 @@
     /** @type {!SearchEngine} */
     engine: Object,
 
-    /** @private {boolean} */
-    showEditSearchEngineDialog_: Boolean,
-
     /** @type {boolean} */
     isDefault: {
       reflectToAttribute: true,
       type: Boolean,
       computed: 'computeIsDefault_(engine)'
     },
+
+    /** @private {boolean} */
+    showDots_: {
+      reflectToAttribute: true,
+      type: Boolean,
+      computed: 'computeShowDots_(engine.canBeDefault,' +
+                                 'engine.canBeEdited,' +
+                                 'engine.canBeRemoved)',
+    },
+
+    /** @private {boolean} */
+    showEditSearchEngineDialog_: Boolean,
   },
 
   /** @private {settings.SearchEnginesBrowserProxy} */
@@ -32,6 +41,11 @@
     this.browserProxy_ = settings.SearchEnginesBrowserProxyImpl.getInstance();
   },
 
+  /** @private */
+  closePopupMenu_: function() {
+    this.$$('dialog[is=cr-action-menu]').close();
+  },
+
   /**
    * @return {boolean}
    * @private
@@ -40,6 +54,27 @@
     return this.engine.default;
   },
 
+  /**
+   * @param {boolean} canBeDefault
+   * @param {boolean} canBeEdited
+   * @param {boolean} canBeRemoved
+   * @return {boolean} Whether to show the dots menu.
+   * @private
+   */
+  computeShowDots_: function(canBeDefault, canBeEdited, canBeRemoved) {
+    return canBeDefault || canBeEdited || canBeRemoved;
+  },
+
+  /**
+   * @param {?string} url The icon URL if available.
+   * @return {string} A set of icon URLs.
+   * @private
+   */
+  getIconSet_: function(url) {
+    // Force default icon, if no |engine.iconURL| is available.
+    return cr.icon.getFavicon(url || '');
+  },
+
   /** @private */
   onDeleteTap_: function() {
     this.browserProxy_.removeSearchEngine(this.engine.modelIndex);
@@ -47,6 +82,13 @@
   },
 
   /** @private */
+  onDotsTap_: function() {
+    /** @type {!CrActionMenuElement} */ (
+        this.$$('dialog[is=cr-action-menu]')).showAt(
+            assert(this.$$('paper-icon-button')));
+  },
+
+  /** @private */
   onEditTap_: function() {
     this.closePopupMenu_();
 
@@ -67,26 +109,4 @@
     this.closePopupMenu_();
     this.browserProxy_.setDefaultSearchEngine(this.engine.modelIndex);
   },
-
-  /** @private */
-  closePopupMenu_: function() {
-    this.$$('dialog[is=cr-action-menu]').close();
-  },
-
-  /**
-   * @param {?string} url The icon URL if available.
-   * @return {string} A set of icon URLs.
-   * @private
-   */
-  getIconSet_: function(url) {
-    // Force default icon, if no |engine.iconURL| is available.
-    return cr.icon.getFavicon(url || '');
-  },
-
-  /** @private */
-  onDotsTap_: function() {
-    /** @type {!CrActionMenuElement} */ (
-        this.$$('dialog[is=cr-action-menu]')).showAt(
-            assert(this.$$('paper-icon-button')));
-  },
 });
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
index 8b242e9..7e12c2d 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
@@ -13,7 +13,10 @@
  *            canBeRemoved: boolean,
  *            default: boolean,
  *            displayName: string,
- *            extension: (Object|undefined),
+ *            extension: ({id: string,
+ *                         name: string,
+ *                         canBeDisabled: boolean,
+ *                         icon: string}|undefined),
  *            iconURL: (string|undefined),
  *            isOmniboxExtension: boolean,
  *            keyword: string,
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_list.html b/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
index 469207d..edbf705 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
@@ -36,7 +36,7 @@
     <div id="outer">
       <content></content>
       <div id="container" class="scroll-container">
-        <iron-list items="[[engines]]" scroll-target="[[scrollTarget]]">
+        <iron-list items="[[engines]]" scroll-target="[[subpageScrollTarget]]">
           <template>
             <settings-search-engine-entry engine="[[item]]"
                 tabindex$="[[tabIndex]]">
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_list.js b/chrome/browser/resources/settings/search_engines_page/search_engines_list.js
index 741164f..2722b1c5 100644
--- a/chrome/browser/resources/settings/search_engines_page/search_engines_list.js
+++ b/chrome/browser/resources/settings/search_engines_page/search_engines_list.js
@@ -14,5 +14,11 @@
   properties: {
     /** @type {!Array<!SearchEngine>} */
     engines: Array,
+
+    /** @override */
+    subpageRoute: {
+      type: Object,
+      value: settings.Route.SEARCH_ENGINES,
+    },
   },
 });
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index 6e4fad5..3a336106 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -69,7 +69,8 @@
       <settings-basic-page prefs="{{prefs}}"
           page-visibility="[[pageVisibility]]"
           show-android-apps="[[showAndroidApps]]"
-          on-subpage-expand="onSubpageExpand_">
+          on-subpage-expand="onSubpageExpand_"
+          in-search-mode="[[inSearchMode_]]">
       </settings-basic-page>
     </template>
     <template is="dom-if"
@@ -90,12 +91,14 @@
           showPages_.advanced, inSearchMode_, hasExpandedSection_)]]">
         <settings-advanced-page prefs="{{prefs}}"
             page-visibility="[[pageVisibility]]"
-            on-subpage-expand="onSubpageExpand_">
+            on-subpage-expand="onSubpageExpand_"
+            in-search-mode="[[inSearchMode_]]">
         </settings-advanced-page>
       </template>
     </template>
     <template is="dom-if" if="[[showPages_.about]]">
-      <settings-about-page></settings-about-page>
+      <settings-about-page in-search-mode="[[inSearchMode_]]">
+      </settings-about-page>
     </template>
     <div id="overscroll" style="padding-bottom: [[overscroll_]]px"></div>
   </template>
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 904b12f..ce15812 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -9,6 +9,19 @@
  * @polymerBehavior MainPageBehavior
  */
 var MainPageBehaviorImpl = {
+  properties: {
+    /**
+     * Whether a search operation is in progress or previous search results are
+     * being displayed.
+     * @private {boolean}
+     */
+    inSearchMode: {
+      type: Boolean,
+      value: false,
+      observer: 'inSearchModeChanged_',
+    },
+  },
+
   /** @type {?HTMLElement} The scrolling container. */
   scroller: null,
 
@@ -48,6 +61,16 @@
   },
 
   /**
+   * When exiting search mode, we need to make another attempt to scroll to
+   * the correct section, since it has just been re-rendered.
+   * @private
+   */
+  inSearchModeChanged_: function(inSearchMode) {
+    if (!inSearchMode)
+      this.tryTransitionToSection_(!settings.lastRouteChangeWasPopstate());
+  },
+
+  /**
    * If possible, transitions to the current route's section (by expanding or
    * scrolling to it). If another transition is running, finishes or cancels
    * that one, then schedules this function again. This ensures the current
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html
index 587a1fc..437de17 100644
--- a/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -15,6 +15,7 @@
 
     --settings-background-color: rgb(241, 241, 241);
     --settings-box-row-padding: 20px;
+    --settings-indent-width: 36px;
     --settings-card-max-width: 640px;
     --settings-disabled-opacity: .65;
     --settings-error-color: var(--paper-red-700);
@@ -24,7 +25,8 @@
 
     --settings-list-frame-padding: {
       -webkit-padding-end: var(--settings-box-row-padding);
-      -webkit-padding-start: 56px;
+      -webkit-padding-start: calc(
+          var(--settings-box-row-padding) + var(--settings-indent-width));
       padding-bottom: 0;
       padding-top: 0;
     }
diff --git a/chrome/browser/resources/settings/site_settings/pdf_documents.html b/chrome/browser/resources/settings/site_settings/pdf_documents.html
index 98c0c18..0ea4cf3 100644
--- a/chrome/browser/resources/settings/site_settings/pdf_documents.html
+++ b/chrome/browser/resources/settings/site_settings/pdf_documents.html
@@ -13,8 +13,7 @@
       <div class="start secondary">
         $i18n{siteSettingsPdfDifferentApplication}
       </div>
-      <settings-toggle-button id="toggle" checked="{{categoryEnabled}}"
-          on-change="onToggleChange_"
+      <settings-toggle-button id="toggle"
           pref="{{prefs.plugins.always_open_pdf_externally}}">
       </settings-toggle-button>
     </div>
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_category.html b/chrome/browser/resources/settings/site_settings/site_settings_category.html
index 2bfd900..c4d85f5 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_category.html
+++ b/chrome/browser/resources/settings/site_settings/site_settings_category.html
@@ -35,7 +35,7 @@
       <div class="settings-box">
         <div class="start">$i18n{deleteDataPostSession}</div>
         <paper-toggle-button checked="{{cookiesSessionOnly_}}"
-            disabled="[[!categoryEnabled]]" id="sessionOnlyCheckbox"
+            disabled="[[!categoryEnabled]]" id="sessionOnlyToggle"
             on-change="onChangePermissionControl_">
         </paper-toggle-button>
       </div>
@@ -48,7 +48,7 @@
           <div class="secondary">$i18n{siteSettingsFlashAskBeforeSubtitle}</div>
         </div>
         <paper-toggle-button checked="{{flashAskFirst_}}"
-            disabled="[[!categoryEnabled]]" id="flashAskCheckbox"
+            disabled="[[!categoryEnabled]]" id="flashAskToggle"
             on-change="onChangePermissionControl_">
         </paper-toggle-button>
       </div>
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_category.js b/chrome/browser/resources/settings/site_settings/site_settings_category.js
index 63cc5ace..9f637c3 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_category.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_category.js
@@ -21,7 +21,7 @@
     categoryEnabled: Boolean,
 
     /**
-     * The site that was selected by the user in the dropdown list.
+     * The site that is passed down to the site list.
      * @type {SiteException}
      */
     selectedSite: {
@@ -31,6 +31,7 @@
 
     /**
      * The description to be shown next to the slider.
+     * @private
      */
     sliderDescription_: String,
 
@@ -78,7 +79,7 @@
    * A handler for changing the default permission value for a content type.
    * @private
    */
-  onChangePermissionControl_: function(event) {
+  onChangePermissionControl_: function() {
     switch (this.category) {
       case settings.ContentSettingsTypes.BACKGROUND_SYNC:
       case settings.ContentSettingsTypes.IMAGES:
@@ -139,7 +140,8 @@
   onCategoryChanged_: function() {
     settings.SiteSettingsPrefsBrowserProxyImpl.getInstance()
         .getDefaultValueForContentType(
-            this.category).then(function(setting) {
+            this.category).then(function(defaultValue) {
+              var setting = defaultValue.setting;
               this.categoryEnabled = this.computeIsSettingEnabled(setting);
 
               // Flash only shows ALLOW or BLOCK descriptions on the slider.
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 9c934587..b50e536 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -8,6 +8,17 @@
  */
 
 /**
+ * The handler will send a police source that is similar, but not exactly the
+ * same as a ControlledBy value. If the ContentSettingProvider is omitted it
+ * should be treated as 'default'.
+ * @enum {string}
+ */
+var ContentSettingProvider = {
+  EXTENSION: 'extension',
+  PREFERENCE: 'preference',
+};
+
+/**
  * @typedef {{embeddingOrigin: string,
  *            embeddingDisplayName: string,
  *            incognito: boolean,
@@ -25,6 +36,12 @@
 var CategoryDefaultsPref;
 
 /**
+ * @typedef {{setting: string,
+ *            source: ContentSettingProvider}}
+ */
+var DefaultContentSetting;
+
+/**
  * @typedef {{location: Array<SiteException>,
  *            notifications: Array<SiteException>}}
  */
@@ -96,7 +113,7 @@
     /**
      * Gets the default value for a site settings category.
      * @param {string} contentType The name of the category to query.
-     * @return {!Promise<string>} The string value of the default setting.
+     * @return {!Promise<!DefaultContentSetting>}
      */
     getDefaultValueForContentType: function(contentType) {},
 
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js
index d1ca0aed..c3595e2 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.js
@@ -229,15 +229,15 @@
       this.updateState();
     }
 
-    setSecureOrigin(secure) {
-      this.isSecureOrigin = secure;
+    setSecure(secure) {
+      this.secure = secure;
       this.updateState();
     }
 
     updateState() {
       /** @const */ var TRANSIENT_TIMEOUT_MS = 30000;
 
-      let visible = (this.enabled && !this.isSecureOrigin);
+      let visible = (this.enabled && !this.secure);
       if (this.secureOriginTimer) {
         clearInterval(this.secureOriginTimer);
         this.secureOriginTimer = null;
@@ -269,12 +269,10 @@
 
   class Omnibox {
     constructor(contentQuadId) {
-      /** @const */ var VISIBILITY_TIMEOUT_MS = 3000;
-
       this.domUiElement = new DomUiElement('#omni-container');
       this.enabled = false;
-      this.secure = false;
-      this.visibilityTimeout = VISIBILITY_TIMEOUT_MS;
+      this.level = 0;
+      this.visibilityTimeout = 0;
       this.visibilityTimer = null;
       this.nativeState = {};
 
@@ -310,8 +308,8 @@
       this.updateState();
     }
 
-    setSecureOrigin(secure) {
-      this.secure = secure;
+    setSecurityLevel(level) {
+      this.level = level;
       this.resetVisibilityTimer();
       this.updateState();
     }
@@ -349,11 +347,11 @@
         this.setNativeVisibility(false);
         return;
       }
-
+      let secure = this.level == 2 || this.level == 3;
       document.querySelector('#omni-secure-icon').style.display =
-          (this.secure ? 'block' : 'none');
+          (secure ? 'block' : 'none');
       document.querySelector('#omni-insecure-icon').style.display =
-          (this.secure ? 'none' : 'block');
+          (secure ? 'none' : 'block');
 
       let state = 'idle';
       this.visibleAfterTransition = true;
@@ -395,6 +393,8 @@
     }
 
     setMode(mode, menuMode, fullscreen) {
+      /** @const */ var OMNIBOX_VISIBILITY_TIMEOUT_MS = 5000;
+
       this.mode = mode;
       this.menuMode = menuMode;
       this.fullscreen = fullscreen;
@@ -405,15 +405,22 @@
       // TODO(amp): Don't show controls in fullscreen once MENU mode lands.
       this.controls.setEnabled(mode == api.Mode.STANDARD && !menuMode);
       this.omnibox.setEnabled(mode == api.Mode.STANDARD && !menuMode);
+      // TODO(amp): Don't show controls in CINEMA mode once MENU mode lands.
+      this.omnibox.setVisibilityTimeout(
+          mode == api.Mode.STANDARD && !menuMode ?
+          0 : OMNIBOX_VISIBILITY_TIMEOUT_MS);
       this.secureOriginWarnings.setEnabled(mode == api.Mode.WEB_VR);
 
       api.setUiCssSize(uiRootElement.clientWidth, uiRootElement.clientHeight,
           UI_DPR);
     }
 
-    setSecureOrigin(secure) {
-      this.secureOriginWarnings.setSecureOrigin(secure);
-      this.omnibox.setSecureOrigin(secure);
+    setSecurityLevel(level) {
+      this.omnibox.setSecurityLevel(level);
+    }
+
+    setWebVRSecureOrigin(secure) {
+      this.secureOriginWarnings.setSecure(secure);
     }
 
     setReloadUiEnabled(enabled) {
@@ -432,8 +439,11 @@
     if ('mode' in dict) {
       sceneManager.setMode(dict['mode'], dict['menuMode'], dict['fullscreen']);
     }
-    if ('secureOrigin' in dict) {
-      sceneManager.setSecureOrigin(dict['secureOrigin']);
+    if ('securityLevel' in dict) {
+      sceneManager.setSecurityLevel(dict['securityLevel']);
+    }
+    if ('webVRSecureOrigin' in dict) {
+      sceneManager.setWebVRSecureOrigin(dict['webVRSecureOrigin']);
     }
     if ('enableReloadUi' in dict) {
       sceneManager.setReloadUiEnabled(dict['enableReloadUi']);
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
index 54319ea4..8da6f19 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
@@ -46,7 +46,7 @@
   for (base::DictionaryValue::Iterator iter(keys_and_digests); !iter.IsAtEnd();
        iter.Advance()) {
     uint32_t digest = 0;
-    if (iter.value().GetType() != base::Value::TYPE_STRING ||
+    if (iter.value().GetType() != base::Value::Type::STRING ||
         !iter.value().GetAsString(&digest_value) ||
         !base::StringToUint(digest_value, &digest)) {
       NOTREACHED();
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
index 2cd6635..ce8ad85 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -130,24 +130,24 @@
   static std::unique_ptr<base::Value> MakeValue(base::Value::Type value_type) {
     using base::Value;
     switch (value_type) {
-      case Value::TYPE_NULL:
+      case Value::Type::NONE:
         return Value::CreateNullValue();
-      case Value::TYPE_BOOLEAN:
+      case Value::Type::BOOLEAN:
         return std::unique_ptr<Value>(new base::FundamentalValue(false));
-      case Value::TYPE_INTEGER:
+      case Value::Type::INTEGER:
         return std::unique_ptr<Value>(new base::FundamentalValue(47));
-      case Value::TYPE_DOUBLE:
+      case Value::Type::DOUBLE:
         return std::unique_ptr<Value>(new base::FundamentalValue(0.47));
-      case Value::TYPE_STRING:
+      case Value::Type::STRING:
         return std::unique_ptr<Value>(new base::StringValue("i have a spleen"));
-      case Value::TYPE_DICTIONARY: {
+      case Value::Type::DICTIONARY: {
         std::unique_ptr<base::DictionaryValue> value(
             new base::DictionaryValue());
         value->SetInteger("twenty-two", 22);
         value->SetInteger("forty-seven", 47);
         return std::move(value);
       }
-      case Value::TYPE_LIST: {
+      case Value::Type::LIST: {
         std::unique_ptr<base::ListValue> value(new base::ListValue());
         value->AppendInteger(22);
         value->AppendInteger(47);
@@ -182,19 +182,19 @@
     // testing/gtest/include/gtest/internal/gtest-tuple.h:246:48:
     //   error: array used as initializer
     testing::Values(
-        std::tr1::make_tuple(base::Value::TYPE_NULL,
+        std::tr1::make_tuple(base::Value::Type::NONE,
                              const_cast<char*>("null")),
-        std::tr1::make_tuple(base::Value::TYPE_BOOLEAN,
+        std::tr1::make_tuple(base::Value::Type::BOOLEAN,
                              const_cast<char*>("false")),
-        std::tr1::make_tuple(base::Value::TYPE_INTEGER,
+        std::tr1::make_tuple(base::Value::Type::INTEGER,
                              const_cast<char*>("47")),
-        std::tr1::make_tuple(base::Value::TYPE_DOUBLE,
+        std::tr1::make_tuple(base::Value::Type::DOUBLE,
                              const_cast<char*>("0.47")),
-        std::tr1::make_tuple(base::Value::TYPE_STRING,
+        std::tr1::make_tuple(base::Value::Type::STRING,
                              const_cast<char*>("i have a spleen")),
-        std::tr1::make_tuple(base::Value::TYPE_DICTIONARY,
+        std::tr1::make_tuple(base::Value::Type::DICTIONARY,
             const_cast<char*>("{\"forty-seven\":47,\"twenty-two\":22}")),
-        std::tr1::make_tuple(base::Value::TYPE_LIST,
+        std::tr1::make_tuple(base::Value::Type::LIST,
                              const_cast<char*>("[22,47]"))));
 
 // Tests that no incidents are reported for relevant combinations of ValueState.
diff --git a/chrome/browser/safe_browsing/srt_fetcher_win.cc b/chrome/browser/safe_browsing/srt_fetcher_win.cc
index a7bd91a8..2711ba2 100644
--- a/chrome/browser/safe_browsing/srt_fetcher_win.cc
+++ b/chrome/browser/safe_browsing/srt_fetcher_win.cc
@@ -43,7 +43,7 @@
 #include "components/component_updater/pref_names.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/safe_browsing_db/safe_browsing_prefs.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "components/version_info/version_info.h"
@@ -253,7 +253,7 @@
       return;
     }
 
-    rappor::RapporService* rappor_service = nullptr;
+    rappor::RapporServiceImpl* rappor_service = nullptr;
     if (use_rappor)
       rappor_service = g_browser_process->rappor_service();
 
@@ -264,9 +264,9 @@
       if (base::StringToUint(uws_string, &uws_id)) {
         RecordSparseHistogram(kFoundUwsMetricName, uws_id);
         if (rappor_service) {
-          rappor_service->RecordSample(kFoundUwsMetricName,
-                                       rappor::COARSE_RAPPOR_TYPE,
-                                       base::UTF16ToUTF8(uws_string));
+          rappor_service->RecordSampleString(kFoundUwsMetricName,
+                                             rappor::COARSE_RAPPOR_TYPE,
+                                             base::UTF16ToUTF8(uws_string));
         }
       } else {
         parse_error = true;
diff --git a/chrome/browser/search/contextual_search_policy_handler_android.cc b/chrome/browser/search/contextual_search_policy_handler_android.cc
index a957b01..eb7e3f5 100644
--- a/chrome/browser/search/contextual_search_policy_handler_android.cc
+++ b/chrome/browser/search/contextual_search_policy_handler_android.cc
@@ -14,7 +14,7 @@
 
 ContextualSearchPolicyHandlerAndroid::ContextualSearchPolicyHandlerAndroid()
     : TypeCheckingPolicyHandler(key::kContextualSearchEnabled,
-                                base::Value::TYPE_BOOLEAN) {}
+                                base::Value::Type::BOOLEAN) {}
 
 ContextualSearchPolicyHandlerAndroid::~ContextualSearchPolicyHandlerAndroid() {
 }
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 963f3d9..bfa03c7 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -61,10 +61,6 @@
     {kConfigDataFilename, kLocalResource, "application/javascript"},
     {kThemeCSSFilename, kLocalResource, "text/css"},
     {"local-ntp.css", IDR_LOCAL_NTP_CSS, "text/css"},
-    {"images/close_2.png", IDR_CLOSE_2, "image/png"},
-    {"images/close_2_hover.png", IDR_CLOSE_2_H, "image/png"},
-    {"images/close_2_active.png", IDR_CLOSE_2_P, "image/png"},
-    {"images/close_2_white.png", IDR_CLOSE_2_MASK, "image/png"},
     {"images/close_3_mask.png", IDR_CLOSE_3_MASK, "image/png"},
     {"images/close_4_button.png", IDR_CLOSE_4_BUTTON, "image/png"},
     {"images/ntp_default_favicon.png", IDR_NTP_DEFAULT_FAVICON, "image/png"},
diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc
index 44f7ccdc0..479d095 100644
--- a/chrome/browser/search/search.cc
+++ b/chrome/browser/search/search.cc
@@ -182,8 +182,8 @@
   // On Chrome OS, if the session hasn't merged yet, we need to avoid loading
   // the remote NTP because that will trigger showing the merge session throttle
   // interstitial page, which can show for 5+ seconds. crbug.com/591530.
-  if (merge_session_throttling_utils::ShouldDelayRequestForProfile(profile) &&
-      merge_session_throttling_utils::ShouldDelayUrl(url)) {
+  if (merge_session_throttling_utils::ShouldDelayUrl(url) &&
+      merge_session_throttling_utils::IsSessionRestorePending(profile)) {
     return true;
   }
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/search_engines/template_url_service_unittest.cc b/chrome/browser/search_engines/template_url_service_unittest.cc
index dae3dbd..f872c83 100644
--- a/chrome/browser/search_engines/template_url_service_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_unittest.cc
@@ -1497,3 +1497,26 @@
       model()->GetTemplateURLForKeyword(ASCIIToUTF16("key1"));
   AssertTimesEqual(modified_last_visited, reloaded_url->last_visited());
 }
+
+TEST_F(TemplateURLServiceTest, LastModifiedTimeUpdate) {
+  test_util()->VerifyLoad();
+  TemplateURLData data;
+  data.SetShortName(ASCIIToUTF16("test_engine"));
+  data.SetKeyword(ASCIIToUTF16("engine_keyword"));
+  data.SetURL("http://test_engine");
+  data.safe_for_autoreplace = true;
+  TemplateURL* original_url = model()->Add(base::MakeUnique<TemplateURL>(data));
+  const base::Time original_last_modified = original_url->last_modified();
+  model()->ResetTemplateURL(original_url, ASCIIToUTF16("test_engine2"),
+                            ASCIIToUTF16("engine_keyword"),
+                            "http://test_engine");
+  TemplateURL* update_url =
+      model()->GetTemplateURLForKeyword(ASCIIToUTF16("engine_keyword"));
+  const base::Time update_last_modified = update_url->last_modified();
+  model()->SetUserSelectedDefaultSearchProvider(update_url);
+  TemplateURL* reloaded_url =
+      model()->GetTemplateURLForKeyword(ASCIIToUTF16("engine_keyword"));
+  const base::Time reloaded_last_modified = reloaded_url->last_modified();
+  EXPECT_NE(original_last_modified, reloaded_last_modified);
+  EXPECT_EQ(update_last_modified, reloaded_last_modified);
+}
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler.cc b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
index 6765576..ff6946b6 100644
--- a/chrome/browser/sessions/restore_on_startup_policy_handler.cc
+++ b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
@@ -18,7 +18,7 @@
 
 RestoreOnStartupPolicyHandler::RestoreOnStartupPolicyHandler()
     : TypeCheckingPolicyHandler(key::kRestoreOnStartup,
-                                base::Value::TYPE_INTEGER) {}
+                                base::Value::Type::INTEGER) {}
 
 RestoreOnStartupPolicyHandler::~RestoreOnStartupPolicyHandler() {
 }
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc b/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
index 737f66b..faed1f0 100644
--- a/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
+++ b/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
@@ -60,7 +60,7 @@
   EXPECT_EQ(
       l10n_util::GetStringFUTF16(IDS_POLICY_TYPE_ERROR,
                                  base::ASCIIToUTF16(base::Value::GetTypeName(
-                                     base::Value::TYPE_INTEGER))),
+                                     base::Value::Type::INTEGER))),
       errors().begin()->second);
 }
 
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index 8d0af0eb..fffcc9c 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -283,6 +283,21 @@
     }
   }
 
+  void OnPromoFocused() override {
+    base::RecordAction(
+        base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
+  }
+
+  void OnPromoChoiceMade(bool accept_promo) override {
+    if (accept_promo) {
+      base::RecordAction(
+          base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
+    } else {
+      base::RecordAction(
+          base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
+    }
+  }
+
   // A closure to be run once initialization completes.
   base::Closure continuation_;
 
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc
index dc8b50bc..1679d36 100644
--- a/chrome/browser/signin/easy_unlock_service.cc
+++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -844,7 +844,7 @@
 
 void EasyUnlockService::SetProximityAuthDevices(
     const AccountId& account_id,
-    const proximity_auth::RemoteDeviceList& remote_devices) {
+    const cryptauth::RemoteDeviceList& remote_devices) {
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery))
     return;
diff --git a/chrome/browser/signin/easy_unlock_service.h b/chrome/browser/signin/easy_unlock_service.h
index c9bac4db..2446d75a 100644
--- a/chrome/browser/signin/easy_unlock_service.h
+++ b/chrome/browser/signin/easy_unlock_service.h
@@ -18,8 +18,8 @@
 #include "chrome/browser/signin/easy_unlock_auth_attempt.h"
 #include "chrome/browser/signin/easy_unlock_metrics.h"
 #include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/screenlock_state.h"
 
 #if defined(OS_CHROMEOS)
@@ -303,7 +303,7 @@
   // are loaded for |account_id|.
   void SetProximityAuthDevices(
       const AccountId& account_id,
-      const proximity_auth::RemoteDeviceList& remote_devices);
+      const cryptauth::RemoteDeviceList& remote_devices);
 
  private:
   // A class to detect whether a bluetooth adapter is present.
diff --git a/chrome/browser/signin/easy_unlock_service_regular.cc b/chrome/browser/signin/easy_unlock_service_regular.cc
index cdc8912f..4ef32417 100644
--- a/chrome/browser/signin/easy_unlock_service_regular.cc
+++ b/chrome/browser/signin/easy_unlock_service_regular.cc
@@ -104,7 +104,7 @@
 
 void EasyUnlockServiceRegular::LoadRemoteDevices() {
   if (device_manager_->unlock_keys().empty()) {
-    SetProximityAuthDevices(GetAccountId(), proximity_auth::RemoteDeviceList());
+    SetProximityAuthDevices(GetAccountId(), cryptauth::RemoteDeviceList());
     return;
   }
 
@@ -119,7 +119,7 @@
 }
 
 void EasyUnlockServiceRegular::OnRemoteDevicesLoaded(
-    const proximity_auth::RemoteDeviceList& remote_devices) {
+    const cryptauth::RemoteDeviceList& remote_devices) {
   SetProximityAuthDevices(GetAccountId(), remote_devices);
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/signin/easy_unlock_service_regular.h b/chrome/browser/signin/easy_unlock_service_regular.h
index 40ec9e97..d530858f 100644
--- a/chrome/browser/signin/easy_unlock_service_regular.h
+++ b/chrome/browser/signin/easy_unlock_service_regular.h
@@ -72,7 +72,7 @@
 
   // Called when |remote_device_loader_| completes.
   void OnRemoteDevicesLoaded(
-      const std::vector<proximity_auth::RemoteDevice>& remote_devices);
+      const std::vector<cryptauth::RemoteDevice>& remote_devices);
 
   // EasyUnlockService implementation:
   EasyUnlockService::Type GetType() const override;
diff --git a/chrome/browser/signin/easy_unlock_service_signin_chromeos.cc b/chrome/browser/signin/easy_unlock_service_signin_chromeos.cc
index 56786bc..804f789 100644
--- a/chrome/browser/signin/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/signin/easy_unlock_service_signin_chromeos.cc
@@ -428,7 +428,7 @@
   if (devices.empty())
     return;
 
-  proximity_auth::RemoteDeviceList remote_devices;
+  cryptauth::RemoteDeviceList remote_devices;
   for (const auto& device : devices) {
     std::string decoded_public_key, decoded_psk, decoded_challenge;
     if (!base::Base64UrlDecode(device.public_key,
@@ -444,11 +444,11 @@
                     << device.public_key;
       continue;
     }
-    proximity_auth::RemoteDevice::BluetoothType bluetooth_type =
+    cryptauth::RemoteDevice::BluetoothType bluetooth_type =
         device.bluetooth_type == chromeos::EasyUnlockDeviceKeyData::BLUETOOTH_LE
-            ? proximity_auth::RemoteDevice::BLUETOOTH_LE
-            : proximity_auth::RemoteDevice::BLUETOOTH_CLASSIC;
-    proximity_auth::RemoteDevice remote_device(
+            ? cryptauth::RemoteDevice::BLUETOOTH_LE
+            : cryptauth::RemoteDevice::BLUETOOTH_CLASSIC;
+    cryptauth::RemoteDevice remote_device(
         account_id.GetUserEmail(), std::string(), decoded_public_key,
         bluetooth_type, device.bluetooth_address, decoded_psk,
         decoded_challenge);
diff --git a/chrome/browser/ssl/chrome_expect_ct_reporter_unittest.cc b/chrome/browser/ssl/chrome_expect_ct_reporter_unittest.cc
index acdb5a5..7b25db4 100644
--- a/chrome/browser/ssl/chrome_expect_ct_reporter_unittest.cc
+++ b/chrome/browser/ssl/chrome_expect_ct_reporter_unittest.cc
@@ -238,7 +238,7 @@
                          const net::SSLInfo& ssl_info) {
   std::unique_ptr<base::Value> value(base::JSONReader::Read(serialized_report));
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
 
   base::DictionaryValue* report_dict;
   ASSERT_TRUE(value->GetAsDictionary(&report_dict));
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
index 43787d9..c8fed6e 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -24,10 +24,10 @@
 #include "base/values.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_switches.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/variations/variations_associated_data.h"
+#include "content/public/common/content_switches.h"
 #include "net/base/hash_value.h"
 #include "net/base/url_util.h"
 #include "net/cert/x509_certificate.h"
@@ -301,7 +301,7 @@
   std::unique_ptr<base::Value> value(map->GetWebsiteSetting(
       url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, std::string(), NULL));
 
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY))
     value.reset(new base::DictionaryValue());
 
   base::DictionaryValue* dict;
@@ -369,7 +369,7 @@
   if (allow_localhost && net::IsLocalhost(url.host()))
     return ALLOWED;
 
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY))
     return DENIED;
 
   base::DictionaryValue* dict;  // Owned by value
@@ -445,7 +445,7 @@
   std::unique_ptr<base::Value> value(map->GetWebsiteSetting(
       url, url, CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS, std::string(), NULL));
 
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   base::DictionaryValue* dict;  // Owned by value
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
index 33ba30c5..abcb7764 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate_test.cc
@@ -20,13 +20,13 @@
 #include "chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index e9bce7b..b0f152e 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/callback_helpers.h"
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
@@ -42,6 +43,11 @@
 
 namespace {
 
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+const base::Feature kCaptivePortalInterstitial{
+    "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
 // The delay in milliseconds before displaying the SSL interstitial.
 // This can be changed in tests.
 // - If there is a name mismatch and a suggested URL available result arrives
@@ -137,8 +143,7 @@
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 bool IsCaptivePortalInterstitialEnabled() {
-  return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") ==
-         "Enabled";
+  return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
 }
 #endif
 
diff --git a/chrome/browser/supervised_user/supervised_user_creation_policy_handler.cc b/chrome/browser/supervised_user/supervised_user_creation_policy_handler.cc
new file mode 100644
index 0000000..2b912f3
--- /dev/null
+++ b/chrome/browser/supervised_user/supervised_user_creation_policy_handler.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/supervised_user/supervised_user_creation_policy_handler.h"
+
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+
+namespace policy {
+
+SupervisedUserCreationPolicyHandler::SupervisedUserCreationPolicyHandler()
+    : TypeCheckingPolicyHandler(key::kSupervisedUserCreationEnabled,
+                                base::Value::Type::BOOLEAN) {}
+
+SupervisedUserCreationPolicyHandler::~SupervisedUserCreationPolicyHandler() {}
+
+void SupervisedUserCreationPolicyHandler::ApplyPolicySettings(
+    const PolicyMap& policies,
+    PrefValueMap* prefs) {
+  // If force sign in is enabled, disable supervised user creation regardless.
+  const base::Value* force_signin_value =
+      policies.GetValue(key::kForceBrowserSignin);
+  bool is_force_signin_enabled;
+  if (force_signin_value &&
+      force_signin_value->GetAsBoolean(&is_force_signin_enabled) &&
+      is_force_signin_enabled) {
+    prefs->SetBoolean(prefs::kSupervisedUserCreationAllowed, false);
+    return;
+  }
+
+  const base::Value* creation_value = policies.GetValue(policy_name());
+  bool is_creation_enabled;
+  if (creation_value && creation_value->GetAsBoolean(&is_creation_enabled)) {
+    prefs->SetBoolean(prefs::kSupervisedUserCreationAllowed,
+                      is_creation_enabled);
+  }
+}
+
+}  // namespace policy
diff --git a/chrome/browser/supervised_user/supervised_user_creation_policy_handler.h b/chrome/browser/supervised_user/supervised_user_creation_policy_handler.h
new file mode 100644
index 0000000..daa8038
--- /dev/null
+++ b/chrome/browser/supervised_user/supervised_user_creation_policy_handler.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CREATION_POLICY_HANDLER_H_
+#define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CREATION_POLICY_HANDLER_H_
+
+#include "base/macros.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
+
+class PrefValueMap;
+
+namespace policy {
+
+class PolicyMap;
+
+class SupervisedUserCreationPolicyHandler : public TypeCheckingPolicyHandler {
+ public:
+  SupervisedUserCreationPolicyHandler();
+  ~SupervisedUserCreationPolicyHandler() override;
+
+  void ApplyPolicySettings(const PolicyMap& policies,
+                           PrefValueMap* prefs) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SupervisedUserCreationPolicyHandler);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CREATION_POLICY_HANDLER_H_
diff --git a/chrome/browser/supervised_user/supervised_user_creation_policy_handler_unittest.cc b/chrome/browser/supervised_user/supervised_user_creation_policy_handler_unittest.cc
new file mode 100644
index 0000000..6915e3a
--- /dev/null
+++ b/chrome/browser/supervised_user/supervised_user_creation_policy_handler_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/supervised_user/supervised_user_creation_policy_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+class SupervisedUserCreationPolicyHandlerTest : public ::testing::Test {
+ protected:
+  void SetUpPolicyAndApply(const char* policy_name, bool value) {
+    policies_.Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                  POLICY_SOURCE_PLATFORM,
+                  base::MakeUnique<base::FundamentalValue>(value), nullptr);
+    ApplyPolicySettings();
+  }
+
+  void ApplyPolicySettings() {
+    handler_.ApplyPolicySettings(policies_, &prefs_);
+  }
+
+  PrefValueMap* prefs() { return &prefs_; }
+
+ private:
+  PolicyMap policies_;
+  PrefValueMap prefs_;
+  SupervisedUserCreationPolicyHandler handler_;
+};
+
+TEST_F(SupervisedUserCreationPolicyHandlerTest, ForceSigninNotSet) {
+  ApplyPolicySettings();
+  EXPECT_FALSE(
+      prefs()->GetValue(prefs::kSupervisedUserCreationAllowed, nullptr));
+
+  bool value;
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, true);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_TRUE(value);
+
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, false);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_FALSE(value);
+}
+
+TEST_F(SupervisedUserCreationPolicyHandlerTest, ForceSigninDisabled) {
+  SetUpPolicyAndApply(key::kForceBrowserSignin, false);
+  EXPECT_FALSE(
+      prefs()->GetValue(prefs::kSupervisedUserCreationAllowed, nullptr));
+
+  bool value;
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, true);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_TRUE(value);
+
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, false);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_FALSE(value);
+}
+
+TEST_F(SupervisedUserCreationPolicyHandlerTest, ForceSigninEnabled) {
+  bool value;
+  SetUpPolicyAndApply(key::kForceBrowserSignin, true);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_FALSE(value);
+
+  // When force sign in is enabled, the supervised user creation will be
+  // disabled even if the policy is set to true.
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, true);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_FALSE(value);
+
+  SetUpPolicyAndApply(key::kSupervisedUserCreationEnabled, false);
+  EXPECT_TRUE(
+      prefs()->GetBoolean(prefs::kSupervisedUserCreationAllowed, &value));
+  EXPECT_FALSE(value);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/supervised_user/supervised_user_resource_throttle.cc b/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
index ad16869..a0695f62 100644
--- a/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request.h"
@@ -206,13 +205,13 @@
   if (behavior == SupervisedUserURLFilter::BLOCK)
     ShowInterstitial(url, reason);
   else if (deferred_)
-    controller()->Resume();
+    Resume();
 }
 
 void SupervisedUserResourceThrottle::OnInterstitialResult(
     bool continue_request) {
   if (continue_request)
-    controller()->Resume();
+    Resume();
   else
-    controller()->Cancel();
+    Cancel();
 }
diff --git a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
index 886c2a4..e1f2bdf 100644
--- a/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
+++ b/chrome/browser/sync/sessions/sessions_sync_manager_unittest.cc
@@ -48,6 +48,10 @@
 
 namespace {
 
+std::string TabNodeIdToTag(const std::string& machine_tag, int tab_node_id) {
+  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
+}
+
 class SessionNotificationObserver {
  public:
   SessionNotificationObserver()
@@ -128,7 +132,7 @@
   std::map<int, SyncedTabDelegate*> tab_overrides_;
   std::map<int, SessionID::id_type> tab_id_overrides_;
   const SyncedWindowDelegate* const wrapped_;
-  SessionID::id_type session_id_override_ = -1;
+  SessionID::id_type session_id_override_ = TabNodePool::kInvalidTabID;
 };
 
 class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
@@ -399,6 +403,10 @@
     return sessions_client_shim_.get();
   }
 
+  TabNodePool* GetTabPool() {
+    return &manager()->session_tracker_.local_tab_pool_;
+  }
+
   syncer::SyncPrefs* sync_prefs() { return sync_prefs_.get(); }
 
   SyncedWindowDelegatesGetter* get_synced_window_getter() {
@@ -502,40 +510,32 @@
 
 namespace {
 
+// A SyncedTabDelegate fake for testing. It simulates a normal
+// SyncedTabDelegate with a proper WebContents. For a SyncedTabDelegate without
+// a WebContents, see PlaceholderTabDelegate below.
 class SyncedTabDelegateFake : public SyncedTabDelegate {
  public:
-  SyncedTabDelegateFake()
-      : current_entry_index_(0), is_supervised_(false), sync_id_(-1) {}
+  SyncedTabDelegateFake() {}
   ~SyncedTabDelegateFake() override {}
 
+  // SyncedTabDelegate overrides.
   bool IsInitialBlankNavigation() const override {
     // This differs from NavigationControllerImpl, which has an initial blank
     // NavigationEntry.
     return GetEntryCount() == 0;
   }
   int GetCurrentEntryIndex() const override { return current_entry_index_; }
-  void set_current_entry_index(int i) {
-    current_entry_index_ = i;
-  }
-
-  void AppendEntry(std::unique_ptr<content::NavigationEntry> entry) {
-    entries_.push_back(std::move(entry));
-  }
-
   GURL GetVirtualURLAtIndex(int i) const override {
     if (static_cast<size_t>(i) >= entries_.size())
       return GURL();
     return entries_[i]->GetVirtualURL();
   }
-
   GURL GetFaviconURLAtIndex(int i) const override { return GURL(); }
-
   ui::PageTransition GetTransitionAtIndex(int i) const override {
     if (static_cast<size_t>(i) >= entries_.size())
       return ui::PAGE_TRANSITION_LINK;
     return entries_[i]->GetTransitionType();
   }
-
   void GetSerializedNavigationAtIndex(
       int i,
       sessions::SerializedNavigationEntry* serialized_entry) const override {
@@ -545,17 +545,9 @@
         sessions::ContentSerializedNavigationBuilder::FromNavigationEntry(
             i, *entries_[i]);
   }
-
   int GetEntryCount() const override { return entries_.size(); }
-
-  SessionID::id_type GetWindowId() const override {
-    return SessionID::id_type();
-  }
-
-  SessionID::id_type GetSessionId() const override {
-    return SessionID::id_type();
-  }
-
+  SessionID::id_type GetWindowId() const override { return window_id_; }
+  SessionID::id_type GetSessionId() const override { return tab_id_; }
   bool IsBeingDestroyed() const override { return false; }
   std::string GetExtensionAppId() const override { return std::string(); }
   bool ProfileIsSupervised() const override { return is_supervised_; }
@@ -564,6 +556,23 @@
   GetBlockedNavigations() const override {
     return &blocked_navigations_;
   }
+  bool IsPlaceholderTab() const override { return false; }
+  int GetSyncId() const override { return sync_id_; }
+  void SetSyncId(int sync_id) override { sync_id_ = sync_id; }
+  bool ShouldSync(SyncSessionsClient* sessions_client) override {
+    return false;
+  }
+
+  void AppendEntry(std::unique_ptr<content::NavigationEntry> entry) {
+    entries_.push_back(std::move(entry));
+  }
+
+  void set_current_entry_index(int i) { current_entry_index_ = i; }
+
+  void SetWindowId(SessionID::id_type window_id) { window_id_ = window_id; }
+
+  void SetSessionId(SessionID::id_type id) { tab_id_ = id; }
+
   void set_blocked_navigations(
       std::vector<const content::NavigationEntry*>* navs) {
     for (auto* entry : *navs) {
@@ -574,31 +583,101 @@
       blocked_navigations_.push_back(std::move(serialized_entry));
     }
   }
-  bool IsPlaceholderTab() const override { return true; }
-
-  // Session sync related methods.
-  int GetSyncId() const override { return sync_id_; }
-  void SetSyncId(int sync_id) override { sync_id_ = sync_id; }
-
-  bool ShouldSync(SyncSessionsClient* sessions_client) override {
-    return false;
-  }
 
   void reset() {
     current_entry_index_ = 0;
-    sync_id_ = -1;
+    sync_id_ = TabNodePool::kInvalidTabNodeID;
     entries_.clear();
   }
 
  private:
-  int current_entry_index_;
-  bool is_supervised_;
-  int sync_id_;
+  int current_entry_index_ = 0;
+  bool is_supervised_ = false;
+  int sync_id_ = -1;
+  SessionID::id_type tab_id_ = 0;
+  SessionID::id_type window_id_ = 0;
   std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>
       blocked_navigations_;
   std::vector<std::unique_ptr<content::NavigationEntry>> entries_;
 };
 
+// A placeholder delegate. These delegates have no WebContents, simulating a tab
+// that has been restored without bringing its state fully into memory (for
+// example on Android), or where the tab's contents have been evicted from
+// memory. See SyncedTabDelegate::IsPlaceHolderTab for more info.
+class PlaceholderTabDelegate : public SyncedTabDelegate {
+ public:
+  PlaceholderTabDelegate(SessionID::id_type session_id, int sync_id)
+      : session_id_(session_id), sync_id_(sync_id) {}
+  ~PlaceholderTabDelegate() override {}
+
+  // SyncedTabDelegate overrides.
+  SessionID::id_type GetSessionId() const override { return session_id_; }
+  int GetSyncId() const override { return sync_id_; }
+  void SetSyncId(int sync_id) override { sync_id_ = sync_id; }
+  bool IsPlaceholderTab() const override { return true; }
+
+  // Everything else is invalid to invoke as it depends on a valid WebContents.
+  SessionID::id_type GetWindowId() const override {
+    NOTREACHED();
+    return 0;
+  }
+  bool IsBeingDestroyed() const override {
+    NOTREACHED();
+    return false;
+  }
+  std::string GetExtensionAppId() const override {
+    NOTREACHED();
+    return "";
+  }
+  bool IsInitialBlankNavigation() const override {
+    NOTREACHED();
+    return false;
+  }
+  int GetCurrentEntryIndex() const override {
+    NOTREACHED();
+    return 0;
+  }
+  int GetEntryCount() const override {
+    NOTREACHED();
+    return 0;
+  }
+  GURL GetVirtualURLAtIndex(int i) const override {
+    NOTREACHED();
+    return GURL();
+  }
+  GURL GetFaviconURLAtIndex(int i) const override {
+    NOTREACHED();
+    return GURL();
+  }
+  ui::PageTransition GetTransitionAtIndex(int i) const override {
+    NOTREACHED();
+    return ui::PageTransition();
+  }
+  void GetSerializedNavigationAtIndex(
+      int i,
+      sessions::SerializedNavigationEntry* serialized_entry) const override {
+    NOTREACHED();
+  }
+  bool ProfileIsSupervised() const override {
+    NOTREACHED();
+    return false;
+  }
+  const std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>*
+  GetBlockedNavigations() const override {
+    NOTREACHED();
+    return nullptr;
+  }
+  bool ShouldSync(SyncSessionsClient* sessions_client) override {
+    NOTREACHED();
+    return false;
+  }
+
+ private:
+  SessionID::id_type session_id_;
+  int sync_id_;
+};
+
 }  // namespace
 
 static const base::Time kTime0 = base::Time::FromInternalValue(100);
@@ -953,29 +1032,37 @@
 
 // Ensure model association associates the pre-existing tabs.
 TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
-  AddTab(browser(), GURL("http://foo1"));
-  NavigateAndCommitActiveTab(GURL("http://foo2"));
-  AddTab(browser(), GURL("http://bar1"));
-  NavigateAndCommitActiveTab(GURL("http://bar2"));
-  AddTab(browser(), GURL("http://baz1"));
-  NavigateAndCommitActiveTab(GURL("http://baz2"));
+  const char kFoo1[] = "http://foo1/";
+  const char kFoo2[] = "http://foo2/";
+  const char kBar1[] = "http://bar1/";
+  const char kBar2[] = "http://bar2/";
+  const char kBaz1[] = "http://baz1/";
+  const char kBaz2[] = "http://baz2/";
   const int kRestoredTabId = 1337;
   const int kNewTabId = 2468;
 
+  // AddTab inserts at index 0, so go in reverse order (tab 3 -> tab 1).
+  AddTab(browser(), GURL(kBaz1));
+  NavigateAndCommitActiveTab(GURL(kBaz2));
+  AddTab(browser(), GURL(kBar1));
+  NavigateAndCommitActiveTab(GURL(kBar2));
+  AddTab(browser(), GURL(kFoo1));
+  NavigateAndCommitActiveTab(GURL(kFoo2));
+
   syncer::SyncDataList in;
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(in, &out);
 
-  // Should be one header add, 3 tab add/update pairs, one header update.
-  ASSERT_EQ(8U, out.size());
+  // Should be one header add, 3 tab adds, one header update.
+  ASSERT_EQ(5U, out.size());
 
   // For input, we set up:
   // * one "normal" fully loaded tab
-  // * one "frozen" tab with no WebContents and a tab_id change
-  // * one "frozen" tab with no WebContents and no tab_id change
-  sync_pb::EntitySpecifics t0_entity = out[2].sync_data().GetSpecifics();
-  sync_pb::EntitySpecifics t1_entity = out[4].sync_data().GetSpecifics();
-  sync_pb::EntitySpecifics t2_entity = out[6].sync_data().GetSpecifics();
+  // * one placeholder tab with no WebContents and a tab_id change
+  // * one placeholder tab with no WebContents and no tab_id change
+  sync_pb::EntitySpecifics t0_entity = out[1].sync_data().GetSpecifics();
+  sync_pb::EntitySpecifics t1_entity = out[2].sync_data().GetSpecifics();
+  sync_pb::EntitySpecifics t2_entity = out[3].sync_data().GetSpecifics();
   t1_entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
   in.push_back(CreateRemoteData(t0_entity));
   in.push_back(CreateRemoteData(t1_entity));
@@ -986,9 +1073,8 @@
   const std::set<const SyncedWindowDelegate*>& windows =
       manager()->synced_window_delegates_getter()->GetSyncedWindowDelegates();
   ASSERT_EQ(1U, windows.size());
-  SyncedTabDelegateFake t1_override, t2_override;
-  t1_override.SetSyncId(1);  // No WebContents by default.
-  t2_override.SetSyncId(2);  // No WebContents by default.
+  PlaceholderTabDelegate t1_override(kNewTabId, 1);
+  PlaceholderTabDelegate t2_override(t2_entity.session().tab().tab_id(), 2);
   SyncedWindowDelegateOverride window_override(*windows.begin());
   window_override.OverrideTabAt(1, &t1_override, kNewTabId);
   window_override.OverrideTabAt(2, &t2_override,
@@ -1006,50 +1092,66 @@
           new syncer::SyncErrorFactoryMock()));
 
   // There should be two changes, one for the fully associated tab, and
-  // one for the tab_id update to t1.  t2 shouldn't need to be updated.
-  ASSERT_EQ(2U, FilterOutLocalHeaderChanges(&out)->size());
+  // one each for the tab_id updates to t1 and t2.
+  ASSERT_EQ(3U, FilterOutLocalHeaderChanges(&out)->size());
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
+  EXPECT_EQ(kFoo2, out[0]
+                       .sync_data()
+                       .GetSpecifics()
+                       .session()
+                       .tab()
+                       .navigation(1)
+                       .virtual_url());
+
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
   EXPECT_EQ(kNewTabId,
             out[1].sync_data().GetSpecifics().session().tab().tab_id());
+  EXPECT_EQ(kBar2, out[1]
+                       .sync_data()
+                       .GetSpecifics()
+                       .session()
+                       .tab()
+                       .navigation(1)
+                       .virtual_url());
 
-  // Verify TabLinks.
-  SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
-  ASSERT_EQ(3U, tab_map.size());
-  int t2_tab_id = t2_entity.session().tab().tab_id();
-  EXPECT_EQ(2, tab_map.find(t2_tab_id)->second->tab_node_id());
-  EXPECT_EQ(1, tab_map.find(kNewTabId)->second->tab_node_id());
-  int t0_tab_id = out[0].sync_data().GetSpecifics().session().tab().tab_id();
-  EXPECT_EQ(0, tab_map.find(t0_tab_id)->second->tab_node_id());
-  // TODO(tim): Once bug 337057 is fixed, we can issue an OnLocalTabModified
-  // from here (using an override similar to above) to return a new tab id
-  // and verify that we don't see any node creations in the SyncChangeProcessor
-  // (similar to how SessionsSyncManagerTest.OnLocalTabModified works.)
+  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
+  EXPECT_EQ(t2_entity.session().tab().tab_id(),
+            out[2].sync_data().GetSpecifics().session().tab().tab_id());
+  EXPECT_EQ(kBaz2, out[2]
+                       .sync_data()
+                       .GetSpecifics()
+                       .session()
+                       .tab()
+                       .navigation(1)
+                       .virtual_url());
 }
 
 // Ensure model association updates the window ID for tabs whose window's ID has
 // changed.
 TEST_F(SessionsSyncManagerTest, WindowIdUpdatedOnRestore) {
+  const char kFoo1[] = "http://foo1/";
+  const char kFoo2[] = "http://foo2/";
   const int kNewWindowId = 1337;
   syncer::SyncDataList in;
   syncer::SyncChangeList out;
 
   // Set up one tab and start sync with it.
-  AddTab(browser(), GURL("http://foo1"));
-  NavigateAndCommitActiveTab(GURL("http://foo2"));
+  AddTab(browser(), GURL(kFoo1));
+  NavigateAndCommitActiveTab(GURL(kFoo2));
   InitWithSyncDataTakeOutput(in, &out);
 
-  // Should be one header add, 1 tab add/update pair, and one header update.
-  ASSERT_EQ(4U, out.size());
-  const sync_pb::EntitySpecifics t0_entity = out[2].sync_data().GetSpecifics();
+  // Should be one header add, 1 tab add, and one header update.
+  ASSERT_EQ(3U, out.size());
+  const sync_pb::EntitySpecifics t0_entity = out[1].sync_data().GetSpecifics();
+  ASSERT_TRUE(t0_entity.session().has_tab());
 
   in.push_back(CreateRemoteData(t0_entity));
   out.clear();
   manager()->StopSyncing(syncer::SESSIONS);
 
-  // SyncedTabDelegateFake is a placeholder (no WebContents) by default.
-  SyncedTabDelegateFake t0_override;
-  t0_override.SetSyncId(t0_entity.session().tab_node_id());
+  // Override the tab with a placeholder tab delegate.
+  PlaceholderTabDelegate t0_override(t0_entity.session().tab().tab_id(),
+                                     t0_entity.session().tab_node_id());
 
   // Set up the window override with the new window ID and placeholder tab.
   const std::set<const SyncedWindowDelegate*>& windows =
@@ -1078,6 +1180,13 @@
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
   EXPECT_EQ(kNewWindowId,
             out[0].sync_data().GetSpecifics().session().tab().window_id());
+  EXPECT_EQ(kFoo2, out[0]
+                       .sync_data()
+                       .GetSpecifics()
+                       .session()
+                       .tab()
+                       .navigation(1)
+                       .virtual_url());
 }
 
 // Tests MergeDataAndStartSyncing with sync data but no local data.
@@ -1140,7 +1249,8 @@
 
   syncer::SyncChangeList output;
   InitWithSyncDataTakeOutput(foreign_data, &output);
-  ASSERT_EQ(4U, output.size());
+  // Should be one header add, 1 tab add, and one header update.
+  ASSERT_EQ(3U, output.size());
 
   // Verify the local header.
   EXPECT_TRUE(output[0].IsValid());
@@ -1168,12 +1278,12 @@
   }
   EXPECT_EQ(SyncChange::ACTION_ADD, output[1].change_type());
   EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
-  EXPECT_TRUE(output[2].sync_data().GetSpecifics().session().has_tab());
+  EXPECT_TRUE(output[1].sync_data().GetSpecifics().session().has_tab());
 
   // Verify the header was updated to reflect window state.
-  EXPECT_TRUE(output[3].IsValid());
-  EXPECT_EQ(SyncChange::ACTION_UPDATE, output[3].change_type());
-  const SyncData data_2(output[3].sync_data());
+  EXPECT_TRUE(output[2].IsValid());
+  EXPECT_EQ(SyncChange::ACTION_UPDATE, output[2].change_type());
+  const SyncData data_2(output[2].sync_data());
   EXPECT_EQ(manager()->current_machine_tag(),
             syncer::SyncDataLocal(data_2).GetTag());
   const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
@@ -1216,7 +1326,9 @@
 
   syncer::SyncChangeList output1;
   InitWithSyncDataTakeOutput(foreign_data1, &output1);
-  ASSERT_EQ(4U, output1.size());
+
+  // 1 header add, one tab add, one header update.
+  ASSERT_EQ(3U, output1.size());
 
   // Add a second window to the foreign session.
   // TODO(tim): Bug 98892. Add local window too when observers are hooked up.
@@ -1307,10 +1419,10 @@
       tag, tab_nums1, &tabs));
 
   // Update associator with the session's meta node, window, and tabs.
-  manager()->UpdateTrackerWithForeignSession(meta, base::Time());
+  manager()->UpdateTrackerWithSpecifics(meta, base::Time());
   for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs.begin();
        iter != tabs.end(); ++iter) {
-    manager()->UpdateTrackerWithForeignSession(*iter, base::Time());
+    manager()->UpdateTrackerWithSpecifics(*iter, base::Time());
   }
   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
   ASSERT_EQ(1U, foreign_sessions.size());
@@ -1322,7 +1434,7 @@
   EXPECT_EQ(5U, changes.size());
   std::set<std::string> expected_tags(&tag, &tag + 1);
   for (int i = 0; i < 5; i++)
-    expected_tags.insert(TabNodePool::TabIdToTag(tag, i));
+    expected_tags.insert(TabNodeIdToTag(tag, i));
 
   for (int i = 0; i < 5; i++) {
     SCOPED_TRACE(changes[i].ToString());
@@ -1445,40 +1557,32 @@
   // committing the NavigationEntry. The first notification results in a tab
   // we don't associate although we do update the header node.  The second
   // notification triggers association of the tab, and the subsequent window
-  // update.  So we should see 4 changes at the SyncChangeProcessor.
-  ASSERT_EQ(4U, out.size());
+  // update.  So we should see 3 changes at the SyncChangeProcessor.
+  ASSERT_EQ(3U, out.size());
 
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[0].change_type());
   ASSERT_TRUE(out[0].sync_data().GetSpecifics().session().has_header());
   EXPECT_EQ(SyncChange::ACTION_ADD, out[1].change_type());
   int tab_node_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
-  EXPECT_EQ(TabNodePool::TabIdToTag(
-                manager()->current_machine_tag(), tab_node_id),
+  EXPECT_EQ(TabNodeIdToTag(manager()->current_machine_tag(), tab_node_id),
             syncer::SyncDataLocal(out[1].sync_data()).GetTag());
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
-  ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
-  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
-  ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
+  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
+  ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_header());
 
   // Verify the actual content.
   const sync_pb::SessionHeader& session_header =
-      out[3].sync_data().GetSpecifics().session().header();
+      out[2].sync_data().GetSpecifics().session().header();
   ASSERT_EQ(1, session_header.window_size());
   EXPECT_EQ(1, session_header.window(0).tab_size());
   const sync_pb::SessionTab& tab1 =
-      out[2].sync_data().GetSpecifics().session().tab();
+      out[1].sync_data().GetSpecifics().session().tab();
   ASSERT_EQ(1, tab1.navigation_size());
   EXPECT_EQ(foo1.spec(), tab1.navigation(0).virtual_url());
 
   // Verify TabNodePool integrity.
-  EXPECT_EQ(1U, manager()->local_tab_pool_.Capacity());
-  EXPECT_TRUE(manager()->local_tab_pool_.Empty());
-
-  // Verify TabLinks.
-  SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
-  ASSERT_EQ(1U, tab_map.size());
-  int tab_id = out[2].sync_data().GetSpecifics().session().tab().tab_id();
-  EXPECT_EQ(tab_node_id, tab_map.find(tab_id)->second->tab_node_id());
+  EXPECT_EQ(1U, GetTabPool()->Capacity());
+  EXPECT_TRUE(GetTabPool()->Empty());
 }
 
 // Test that receiving a session delete from sync removes the session
@@ -1622,7 +1726,8 @@
       manager()->session_tracker_.LookupSessionTab(session_tag, 2, &tab));
 
   std::set<int> tab_node_ids;
-  manager()->session_tracker_.LookupTabNodeIds(session_tag, &tab_node_ids);
+  manager()->session_tracker_.LookupForeignTabNodeIds(session_tag,
+                                                      &tab_node_ids);
   EXPECT_EQ(6U, tab_node_ids.size());
   EXPECT_TRUE(tab_node_ids.find(tab1A.tab_node_id()) != tab_node_ids.end());
   EXPECT_TRUE(tab_node_ids.find(tab1B.tab_node_id()) != tab_node_ids.end());
@@ -1638,7 +1743,8 @@
   manager()->ProcessSyncChanges(FROM_HERE, changes);
 
   tab_node_ids.clear();
-  manager()->session_tracker_.LookupTabNodeIds(session_tag, &tab_node_ids);
+  manager()->session_tracker_.LookupForeignTabNodeIds(session_tag,
+                                                      &tab_node_ids);
   EXPECT_EQ(3U, tab_node_ids.size());
   EXPECT_TRUE(tab_node_ids.find(tab1C.tab_node_id()) != tab_node_ids.end());
   EXPECT_TRUE(tab_node_ids.find(tab2A.tab_node_id()) != tab_node_ids.end());
@@ -1676,7 +1782,8 @@
   output.clear();
 
   std::set<int> tab_node_ids;
-  manager()->session_tracker_.LookupTabNodeIds(session_tag, &tab_node_ids);
+  manager()->session_tracker_.LookupForeignTabNodeIds(session_tag,
+                                                      &tab_node_ids);
   EXPECT_EQ(2U, tab_node_ids.size());
   EXPECT_TRUE(tab_node_ids.find(tab_node_id_shared) != tab_node_ids.end());
   EXPECT_TRUE(tab_node_ids.find(tab_node_id_unique) != tab_node_ids.end());
@@ -1686,7 +1793,8 @@
   manager()->ProcessSyncChanges(FROM_HERE, changes);
 
   tab_node_ids.clear();
-  manager()->session_tracker_.LookupTabNodeIds(session_tag, &tab_node_ids);
+  manager()->session_tracker_.LookupForeignTabNodeIds(session_tag,
+                                                      &tab_node_ids);
   EXPECT_EQ(1U, tab_node_ids.size());
   EXPECT_TRUE(tab_node_ids.find(tab_node_id_unique) != tab_node_ids.end());
 
@@ -1694,34 +1802,45 @@
   EXPECT_EQ(1U, output.size());
 }
 
-// TODO(shashishekhar): "Move this to TabNodePool unittests."
-TEST_F(SessionsSyncManagerTest, SaveUnassociatedNodesForReassociation) {
+TEST_F(SessionsSyncManagerTest, AssociationReusesNodes) {
   syncer::SyncChangeList changes;
-  InitWithNoSyncData();
+  AddTab(browser(), GURL("http://foo1"));
+  InitWithSyncDataTakeOutput(syncer::SyncDataList(), &changes);
+  ASSERT_EQ(3U, changes.size());  // Header add, tab add, header update.
+  ASSERT_TRUE(changes[1].sync_data().GetSpecifics().session().has_tab());
+  int tab_node_id =
+      changes[1].sync_data().GetSpecifics().session().tab_node_id();
 
-  std::string local_tag = manager()->current_machine_tag();
-  // Create a free node and then dissassociate sessions so that it ends up
-  // unassociated.
-  manager()->local_tab_pool_.GetFreeTabNode(&changes);
-
-  // Update the tab_id of the node, so that it is considered a valid
-  // unassociated node otherwise it will be mistaken for a corrupted node and
-  // will be deleted before being added to the tab node pool.
-  sync_pb::EntitySpecifics entity(changes[0].sync_data().GetSpecifics());
-  entity.mutable_session()->mutable_tab()->set_tab_id(1);
-  SyncData d = CreateRemoteData(entity);
-  syncer::SyncDataList in(&d, &d + 1);
+  // Pass back the previous tab and header nodes at association, along with a
+  // second tab node (with a rewritten tab node id).
+  syncer::SyncDataList in;
+  in.push_back(
+      CreateRemoteData(changes[2].sync_data().GetSpecifics()));  // Header node.
+  sync_pb::SessionSpecifics new_tab(
+      changes[1].sync_data().GetSpecifics().session());
+  new_tab.set_tab_node_id(tab_node_id + 1);
+  in.push_back(CreateRemoteData(new_tab));  // New tab node.
+  in.push_back(CreateRemoteData(
+      changes[1].sync_data().GetSpecifics()));  // Old tab node.
   changes.clear();
-  SessionsSyncManager manager2(GetSyncSessionsClient(), sync_prefs(),
-                               local_device(), NewDummyRouter(),
-                               base::Closure(), base::Closure());
-  syncer::SyncMergeResult result = manager2.MergeDataAndStartSyncing(
+
+  // Reassociate (with the same single tab/window open).
+  manager()->StopSyncing(syncer::SESSIONS);
+  syncer::SyncMergeResult result = manager()->MergeDataAndStartSyncing(
       syncer::SESSIONS, in, std::unique_ptr<syncer::SyncChangeProcessor>(
                                 new TestSyncProcessorStub(&changes)),
       std::unique_ptr<syncer::SyncErrorFactory>(
           new syncer::SyncErrorFactoryMock()));
   ASSERT_FALSE(result.error().IsSet());
-  EXPECT_TRUE(FilterOutLocalHeaderChanges(&changes)->empty());
+
+  // No tab entities should be deleted. The original (lower) tab node id should
+  // be reused for association.
+  FilterOutLocalHeaderChanges(&changes);
+  ASSERT_EQ(1U, changes.size());
+  EXPECT_EQ(SyncChange::ACTION_UPDATE, changes[0].change_type());
+  EXPECT_TRUE(changes[0].sync_data().GetSpecifics().session().has_tab());
+  EXPECT_EQ(tab_node_id,
+            changes[0].sync_data().GetSpecifics().session().tab_node_id());
 }
 
 TEST_F(SessionsSyncManagerTest, MergeDeletesCorruptNode) {
@@ -1729,16 +1848,19 @@
   InitWithNoSyncData();
 
   std::string local_tag = manager()->current_machine_tag();
-  int tab_node_id = manager()->local_tab_pool_.GetFreeTabNode(&changes);
-  SyncData d = CreateRemoteData(changes[0].sync_data().GetSpecifics());
+  int tab_node_id = TabNodePool::kInvalidTabNodeID;
+  GetTabPool()->GetTabNodeForTab(0, &tab_node_id);
+  sync_pb::SessionSpecifics specifics;
+  specifics.set_session_tag(local_tag);
+  specifics.set_tab_node_id(tab_node_id);
+  SyncData d = CreateRemoteData(specifics);
   syncer::SyncDataList in(&d, &d + 1);
-  changes.clear();
   TearDown();
   SetUp();
   InitWithSyncDataTakeOutput(in, &changes);
   EXPECT_EQ(1U, FilterOutLocalHeaderChanges(&changes)->size());
   EXPECT_EQ(SyncChange::ACTION_DELETE, changes[0].change_type());
-  EXPECT_EQ(TabNodePool::TabIdToTag(local_tag, tab_node_id),
+  EXPECT_EQ(TabNodeIdToTag(local_tag, tab_node_id),
             syncer::SyncDataLocal(changes[0].sync_data()).GetTag());
 }
 
@@ -1807,7 +1929,7 @@
   // Go to a sync-interesting URL.
   NavigateAndCommitActiveTab(GURL("http://foo2"));
 
-  EXPECT_EQ(3U, out.size());  // Tab add, update, and header update.
+  EXPECT_EQ(2U, out.size());  // Tab add and header update.
 
   EXPECT_TRUE(
       base::StartsWith(syncer::SyncDataLocal(out[0].sync_data()).GetTag(),
@@ -1817,18 +1939,9 @@
             out[0].sync_data().GetSpecifics().session().session_tag());
   EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
 
-  EXPECT_TRUE(
-      base::StartsWith(syncer::SyncDataLocal(out[1].sync_data()).GetTag(),
-                       manager()->current_machine_tag(),
-                       base::CompareCase::SENSITIVE));
-  EXPECT_EQ(manager()->current_machine_tag(),
-            out[1].sync_data().GetSpecifics().session().session_tag());
-  EXPECT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
+  EXPECT_TRUE(out[1].IsValid());
   EXPECT_EQ(SyncChange::ACTION_UPDATE, out[1].change_type());
-
-  EXPECT_TRUE(out[2].IsValid());
-  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[2].change_type());
-  const SyncData data(out[2].sync_data());
+  const SyncData data(out[1].sync_data());
   EXPECT_EQ(manager()->current_machine_tag(),
             syncer::SyncDataLocal(data).GetTag());
   const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
@@ -1860,17 +1973,17 @@
   AddTab(browser(), bar1);
   NavigateAndCommitActiveTab(bar2);
 
-  // One add, one update for each AddTab.
+  // One add for each AddTab.
   // One update for each NavigateAndCommit.
-  // = 6 total tab updates.
+  // = 4 total tab updates.
   // One header update corresponding to each of those.
   // = 6 total header updates.
-  // 12 total updates.
-  ASSERT_EQ(12U, out.size());
+  // 10 total updates.
+  ASSERT_EQ(10U, out.size());
 
   // Verify the tab node creations and updates to ensure the SyncProcessor
   // sees the right operations.
-  for (int i = 0; i < 12; i++) {
+  for (int i = 0; i < 10; i++) {
     SCOPED_TRACE(i);
     EXPECT_TRUE(out[i].IsValid());
     const SyncData data(out[i].sync_data());
@@ -1879,40 +1992,30 @@
                                  base::CompareCase::SENSITIVE));
     const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
     EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
-    if (i % 6 == 0) {
+    if (i % 5 == 0) {
       // First thing on an AddTab is a no-op header update for parented tab.
       EXPECT_EQ(header.SerializeAsString(),
                 data.GetSpecifics().SerializeAsString());
       EXPECT_EQ(manager()->current_machine_tag(),
                 syncer::SyncDataLocal(data).GetTag());
-    } else if (i % 6 == 1) {
-      // Next, the TabNodePool should create the tab node.
+    } else if (i % 5 == 1) {
+      // Next, the tab should be added.
       EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
-      EXPECT_EQ(TabNodePool::TabIdToTag(
-                    manager()->current_machine_tag(),
-                    data.GetSpecifics().session().tab_node_id()),
+      EXPECT_EQ(TabNodeIdToTag(manager()->current_machine_tag(),
+                               data.GetSpecifics().session().tab_node_id()),
                 syncer::SyncDataLocal(data).GetTag());
-    } else if (i % 6 == 2) {
-      // Then we see the tab update to the URL.
-      EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
-      EXPECT_EQ(TabNodePool::TabIdToTag(
-                    manager()->current_machine_tag(),
-                    data.GetSpecifics().session().tab_node_id()),
-                syncer::SyncDataLocal(data).GetTag());
-      ASSERT_TRUE(specifics.has_tab());
-    } else if (i % 6 == 3) {
+    } else if (i % 5 == 2) {
       // The header needs to be updated to reflect the new window state.
       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
       EXPECT_TRUE(specifics.has_header());
-    } else if (i % 6 == 4) {
+    } else if (i % 5 == 3) {
       // Now we move on to NavigateAndCommit.  Update the tab.
       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
-      EXPECT_EQ(TabNodePool::TabIdToTag(
-                    manager()->current_machine_tag(),
-                    data.GetSpecifics().session().tab_node_id()),
+      EXPECT_EQ(TabNodeIdToTag(manager()->current_machine_tag(),
+                               data.GetSpecifics().session().tab_node_id()),
                 syncer::SyncDataLocal(data).GetTag());
       ASSERT_TRUE(specifics.has_tab());
-    } else if (i % 6 == 5) {
+    } else if (i % 5 == 4) {
       // The header needs to be updated to reflect the new window state.
       EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
       ASSERT_TRUE(specifics.has_header());
@@ -1929,22 +2032,22 @@
   // ASSERT_TRUEs above allow us to dive in freely here.
   // Verify first tab.
   const sync_pb::SessionTab& tab1_1 =
-      out[2].sync_data().GetSpecifics().session().tab();
+      out[1].sync_data().GetSpecifics().session().tab();
   ASSERT_EQ(1, tab1_1.navigation_size());
   EXPECT_EQ(foo1.spec(), tab1_1.navigation(0).virtual_url());
   const sync_pb::SessionTab& tab1_2 =
-      out[4].sync_data().GetSpecifics().session().tab();
+      out[3].sync_data().GetSpecifics().session().tab();
   ASSERT_EQ(2, tab1_2.navigation_size());
   EXPECT_EQ(foo1.spec(), tab1_2.navigation(0).virtual_url());
   EXPECT_EQ(foo2.spec(), tab1_2.navigation(1).virtual_url());
 
   // Verify second tab.
   const sync_pb::SessionTab& tab2_1 =
-      out[8].sync_data().GetSpecifics().session().tab();
+      out[6].sync_data().GetSpecifics().session().tab();
   ASSERT_EQ(1, tab2_1.navigation_size());
   EXPECT_EQ(bar1.spec(), tab2_1.navigation(0).virtual_url());
   const sync_pb::SessionTab& tab2_2 =
-      out[10].sync_data().GetSpecifics().session().tab();
+      out[8].sync_data().GetSpecifics().session().tab();
   ASSERT_EQ(2, tab2_2.navigation_size());
   EXPECT_EQ(bar1.spec(), tab2_2.navigation(0).virtual_url());
   EXPECT_EQ(bar2.spec(), tab2_2.navigation(1).virtual_url());
@@ -1965,21 +2068,21 @@
 
   // Add an interesting tab.
   AddTab(browser(), kValidUrl);
-  // No-op header update, tab creation, tab update, header update.
-  ASSERT_EQ(4U, out.size());
+  // No-op header update, tab creation, header update.
+  ASSERT_EQ(3U, out.size());
   // The last two are the interesting updates.
-  ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_tab());
-  EXPECT_EQ(kValidUrl.spec(), out[2]
+  ASSERT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
+  EXPECT_EQ(kValidUrl.spec(), out[1]
                                   .sync_data()
                                   .GetSpecifics()
                                   .session()
                                   .tab()
                                   .navigation(0)
                                   .virtual_url());
-  ASSERT_TRUE(out[3].sync_data().GetSpecifics().session().has_header());
+  ASSERT_TRUE(out[2].sync_data().GetSpecifics().session().has_header());
   ASSERT_EQ(1,
-            out[3].sync_data().GetSpecifics().session().header().window_size());
-  ASSERT_EQ(1, out[3]
+            out[2].sync_data().GetSpecifics().session().header().window_size());
+  ASSERT_EQ(1, out[2]
                    .sync_data()
                    .GetSpecifics()
                    .session()
@@ -2015,7 +2118,7 @@
 
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
-  ASSERT_EQ(6U, out.size());
+  ASSERT_EQ(4U, out.size());  // Header creation, add two tabs, header update
 
   // Check that this machine's data is not included in the foreign windows.
   std::vector<const SyncedSession*> foreign_sessions;
@@ -2036,7 +2139,7 @@
   EXPECT_EQ(0, header_s.window_size());
 
   // Verify the tab node creations and updates with content.
-  for (int i = 1; i < 5; i++) {
+  for (int i = 1; i < 3; i++) {
     EXPECT_TRUE(out[i].IsValid());
     const SyncData data(out[i].sync_data());
     EXPECT_TRUE(base::StartsWith(syncer::SyncDataLocal(data).GetTag(),
@@ -2044,18 +2147,13 @@
                                  base::CompareCase::SENSITIVE));
     const sync_pb::SessionSpecifics& specifics(data.GetSpecifics().session());
     EXPECT_EQ(manager()->current_machine_tag(), specifics.session_tag());
-    if (i % 2 == 1) {
-      EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
-    } else {
-      EXPECT_EQ(SyncChange::ACTION_UPDATE, out[i].change_type());
-      EXPECT_TRUE(specifics.has_tab());
-    }
+    EXPECT_EQ(SyncChange::ACTION_ADD, out[i].change_type());
   }
 
   // Verify the header was updated to reflect new window state.
-  EXPECT_TRUE(out[5].IsValid());
-  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
-  const SyncData data_2(out[5].sync_data());
+  EXPECT_TRUE(out[3].IsValid());
+  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[3].change_type());
+  const SyncData data_2(out[3].sync_data());
   EXPECT_EQ(manager()->current_machine_tag(),
             syncer::SyncDataLocal(data_2).GetTag());
   const sync_pb::SessionSpecifics& specifics2(data_2.GetSpecifics().session());
@@ -2063,20 +2161,6 @@
   EXPECT_TRUE(specifics2.has_header());
   const sync_pb::SessionHeader& header_s2 = specifics2.header();
   EXPECT_EQ(1, header_s2.window_size());
-
-  // Verify TabLinks.
-  SessionsSyncManager::TabLinksMap tab_map = manager()->local_tab_map_;
-  ASSERT_EQ(2U, tab_map.size());
-  // Tabs are ordered by sessionid in tab_map, so should be able to traverse
-  // the tree based on order of tabs created
-  SessionsSyncManager::TabLinksMap::iterator iter = tab_map.begin();
-  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
-  EXPECT_EQ(GURL("http://foo1"), iter->second->tab()->GetVirtualURLAtIndex(0));
-  EXPECT_EQ(GURL("http://foo2"), iter->second->tab()->GetVirtualURLAtIndex(1));
-  iter++;
-  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
-  EXPECT_EQ(GURL("http://bar1"), iter->second->tab()->GetVirtualURLAtIndex(0));
-  EXPECT_EQ(GURL("http://bar2"), iter->second->tab()->GetVirtualURLAtIndex(1));
 }
 
 TEST_F(SessionsSyncManagerTest, ForeignSessionModifiedTime) {
@@ -2179,8 +2263,7 @@
   for (int i = 1; i < 5; i++) {
     EXPECT_EQ(SyncChange::ACTION_DELETE, output[i].change_type());
     const SyncData data(output[i].sync_data());
-    EXPECT_EQ(TabNodePool::TabIdToTag(tag1, i),
-              syncer::SyncDataLocal(data).GetTag());
+    EXPECT_EQ(TabNodeIdToTag(tag1, i), syncer::SyncDataLocal(data).GetTag());
   }
 
   ASSERT_TRUE(manager()->GetAllForeignSessions(&foreign_sessions));
@@ -2303,7 +2386,8 @@
 
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
-  ASSERT_EQ(4U, out.size());  // Header, tab ADD, tab UPDATE, header UPDATE.
+  ASSERT_EQ(3U, out.size());  // Header ADD, tab ADD, header UPDATE.
+  out.clear();
 
   // To simulate WebContents swap during prerendering, create new WebContents
   // and swap with old WebContents.
@@ -2324,24 +2408,28 @@
       old_web_contents.get());
   browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
 
-  ASSERT_EQ(9U, out.size());
-  EXPECT_EQ(SyncChange::ACTION_ADD, out[4].change_type());
-  EXPECT_EQ(SyncChange::ACTION_UPDATE, out[5].change_type());
+  ASSERT_EQ(4U, out.size());
+  EXPECT_EQ(SyncChange::ACTION_ADD, out[0].change_type());
+  out.clear();
 
-  // Navigate away.
+  // Navigate away. +1 tab updates, 1 header update.
   NavigateAndCommitActiveTab(GURL("http://bar2"));
 
   // Delete old WebContents. This should not crash.
+  // +1 no-op header update.
   old_web_contents.reset();
 
   // Try more navigations and verify output size. This can also reveal
   // bugs (leaks) on memcheck bots if the SessionSyncManager
   // didn't properly clean up the tab pool or session tracker.
+  // +1 tab updates, 1 header update.
   NavigateAndCommitActiveTab(GURL("http://bar3"));
 
+  // +1 no-op header update, tab add, header update.
   AddTab(browser(), GURL("http://bar4"));
+  // +1 tab update, header update.
   NavigateAndCommitActiveTab(GURL("http://bar5"));
-  ASSERT_EQ(19U, out.size());
+  ASSERT_EQ(10U, out.size());
 }
 
 // Test that NOTIFICATION_FOREIGN_SESSION_UPDATED is sent when processing
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.cc b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
index b1a5687..a6c13eb 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder.cc
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/tab_contents/origins_seen_service_factory.h"
 #include "components/navigation_metrics/navigation_metrics.h"
 #include "components/navigation_metrics/origins_seen_service.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_details.h"
@@ -38,7 +38,7 @@
 }
 
 void NavigationMetricsRecorder::set_rappor_service_for_testing(
-    rappor::RapporService* service) {
+    rappor::RapporServiceImpl* service) {
   rappor_service_ = service;
 }
 
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder.h b/chrome/browser/tab_contents/navigation_metrics_recorder.h
index c2fbd20..0aa6fdb 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder.h
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder.h
@@ -10,7 +10,7 @@
 #include "content/public/browser/web_contents_user_data.h"
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 class NavigationMetricsRecorder
@@ -19,7 +19,8 @@
  public:
   ~NavigationMetricsRecorder() override;
 
-  void set_rappor_service_for_testing(rappor::RapporService* rappor_service);
+  void set_rappor_service_for_testing(
+      rappor::RapporServiceImpl* rappor_service);
 
  private:
   explicit NavigationMetricsRecorder(content::WebContents* web_contents);
@@ -30,7 +31,7 @@
       const content::LoadCommittedDetails& details,
       const content::FrameNavigateParams& params) override;
 
-  rappor::RapporService* rappor_service_;
+  rappor::RapporServiceImpl* rappor_service_;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationMetricsRecorder);
 };
diff --git a/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc b/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
index 94105db..5318d98 100644
--- a/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
+++ b/chrome/browser/tab_contents/navigation_metrics_recorder_browsertest.cc
@@ -25,7 +25,7 @@
       content::WebContentsUserData<NavigationMetricsRecorder>::FromWebContents(
           web_contents);
   ASSERT_TRUE(recorder);
-  rappor::TestRapporService rappor_service;
+  rappor::TestRapporServiceImpl rappor_service;
   recorder->set_rappor_service_for_testing(&rappor_service);
 
   base::HistogramTester histograms;
diff --git a/chrome/browser/task_manager/task_manager_interface.h b/chrome/browser/task_manager/task_manager_interface.h
index 70c5956..e8faa91 100644
--- a/chrome/browser/task_manager/task_manager_interface.h
+++ b/chrome/browser/task_manager/task_manager_interface.h
@@ -117,7 +117,7 @@
   virtual const base::string16& GetTitle(TaskId task_id) const = 0;
 
   // Returns the canonicalized name of the task with |task_id| that can be used
-  // to represent this task in a Rappor sample via RapporService.
+  // to represent this task in a Rappor sample via RapporServiceImpl.
   virtual const std::string& GetTaskNameForRappor(TaskId task_id) const = 0;
 
   // Returns the name of the profile associated with the browser context of the
diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer.cc b/chrome/browser/task_profiler/task_profiler_data_serializer.cc
index 5d5e8514..6f4b52e 100644
--- a/chrome/browser/task_profiler/task_profiler_data_serializer.cc
+++ b/chrome/browser/task_profiler/task_profiler_data_serializer.cc
@@ -51,7 +51,8 @@
   LocationSnapshotToValue(birth.location, location_value.get());
   dictionary->Set(prefix + "_location", location_value.release());
 
-  dictionary->Set(prefix + "_thread", new base::StringValue(birth.thread_name));
+  dictionary->Set(prefix + "_thread",
+                  new base::StringValue(birth.sanitized_thread_name));
 }
 
 // Re-serializes the |death_data| into |dictionary|.
@@ -83,7 +84,7 @@
   DeathDataSnapshotToValue(snapshot.death_data, death_data.get());
   dictionary->Set("death_data", death_data.release());
 
-  dictionary->SetString("death_thread", snapshot.death_thread_name);
+  dictionary->SetString("death_thread", snapshot.death_sanitized_thread_name);
 }
 
 int AsChromeProcessType(
diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc b/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc
index e248f14..5ff71d6 100644
--- a/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc
+++ b/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc
@@ -54,14 +54,13 @@
     parent.location.file_name = "path/to/foo.cc";
     parent.location.function_name = "WhizBang";
     parent.location.line_number = 101;
-    parent.thread_name = "CrBrowserMain";
+    parent.sanitized_thread_name = "CrBrowserMain";
 
     tracked_objects::BirthOnThreadSnapshot child;
     child.location.file_name = "path/to/bar.cc";
     child.location.function_name = "FizzBoom";
     child.location.line_number = 433;
-    child.thread_name = "Chrome_IOThread";
-
+    child.sanitized_thread_name = "Chrome_IOThread";
 
     // Add a snapshot.
     process_data_phase.tasks.push_back(tracked_objects::TaskSnapshot());
@@ -79,7 +78,7 @@
     process_data_phase.tasks.back().death_data.freed_bytes = 1092;
     process_data_phase.tasks.back().death_data.alloc_overhead_bytes = 201;
     process_data_phase.tasks.back().death_data.max_allocated_bytes = 1500;
-    process_data_phase.tasks.back().death_thread_name =
+    process_data_phase.tasks.back().death_sanitized_thread_name =
         "WorkerPool/-1340960768";
 
     // Add a second snapshot.
@@ -98,7 +97,8 @@
     process_data_phase.tasks.back().death_data.freed_bytes = 10092;
     process_data_phase.tasks.back().death_data.alloc_overhead_bytes = 2001;
     process_data_phase.tasks.back().death_data.max_allocated_bytes = 15000;
-    process_data_phase.tasks.back().death_thread_name = "PAC thread #3";
+    process_data_phase.tasks.back().death_sanitized_thread_name =
+        "PAC thread #3";
 
     ExpectSerialization(process_data_phase, 239,
                         metrics::ProfilerEventProto::TrackedObject::RENDERER,
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 1917736a..7da11d3a 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -1096,7 +1096,7 @@
 
   for (base::DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd();
        iter.Advance()) {
-    if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    if (iter.value().IsType(base::Value::Type::DICTIONARY)) {
       const base::DictionaryValue* inner_value = NULL;
       if (iter.value().GetAsDictionary(&inner_value)) {
         for (base::DictionaryValue::Iterator inner_iter(*inner_value);
@@ -1105,7 +1105,7 @@
           std::string name;
           ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_NONE;
           if (GetScaleFactorFromManifestKey(inner_iter.key(), &scale_factor) &&
-              inner_iter.value().IsType(base::Value::TYPE_STRING) &&
+              inner_iter.value().IsType(base::Value::Type::STRING) &&
               inner_iter.value().GetAsString(&name)) {
             AddFileAtScaleToMap(iter.key(),
                                 scale_factor,
@@ -1114,7 +1114,7 @@
           }
         }
       }
-    } else if (iter.value().IsType(base::Value::TYPE_STRING)) {
+    } else if (iter.value().IsType(base::Value::Type::STRING)) {
       std::string name;
       if (iter.value().GetAsString(&name)) {
         AddFileAtScaleToMap(iter.key(),
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index 287d6ce..f31dc4e 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -99,7 +99,7 @@
 
   void LoadColorJSON(const std::string& json) {
     std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+    ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
     LoadColorDictionary(static_cast<base::DictionaryValue*>(value.get()));
   }
 
@@ -109,7 +109,7 @@
 
   void LoadTintJSON(const std::string& json) {
     std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+    ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
     LoadTintDictionary(static_cast<base::DictionaryValue*>(value.get()));
   }
 
@@ -119,7 +119,7 @@
 
   void LoadDisplayPropertiesJSON(const std::string& json) {
     std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+    ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
     LoadDisplayPropertiesDictionary(
         static_cast<base::DictionaryValue*>(value.get()));
   }
@@ -131,7 +131,7 @@
   void ParseImageNamesJSON(const std::string& json,
                            TestFilePathMap* out_file_paths) {
     std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
-    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+    ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
     ParseImageNamesDictionary(static_cast<base::DictionaryValue*>(value.get()),
                               out_file_paths);
   }
diff --git a/chrome/browser/themes/theme_service_aurax11.cc b/chrome/browser/themes/theme_service_aurax11.cc
index 7224431..00adcd147 100644
--- a/chrome/browser/themes/theme_service_aurax11.cc
+++ b/chrome/browser/themes/theme_service_aurax11.cc
@@ -46,7 +46,7 @@
 void SystemThemeX11::StartUsingTheme() {
   pref_service_->SetBoolean(prefs::kUsesSystemTheme, true);
   // Have the former theme notify its observers of change.
-  ui::NativeThemeAura::instance()->NotifyObservers();
+  ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
 }
 
 void SystemThemeX11::StopUsingTheme() {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f32a7b05..d4cf8b0 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -510,8 +510,8 @@
     "//components/net_log",
     "//components/ntp_snippets",
     "//components/ntp_tiles",
-    "//components/offline_pages",
-    "//components/offline_pages/background:background_offliner",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core/background:background_offliner",
     "//components/omnibox/browser",
     "//components/onc",
     "//components/password_manager/content/browser",
@@ -1394,8 +1394,8 @@
       "ash/system_tray_delegate_chromeos.h",
       "ash/system_tray_delegate_utils.cc",
       "ash/system_tray_delegate_utils.h",
-      "ash/volume_controller_chromeos.cc",
-      "ash/volume_controller_chromeos.h",
+      "ash/volume_controller.cc",
+      "ash/volume_controller.h",
       "ash/vpn_list_forwarder.cc",
       "ash/vpn_list_forwarder.h",
       "views/ash/chrome_browser_main_extra_parts_ash.cc",
@@ -1790,6 +1790,8 @@
         "views/passwords/manage_passwords_bubble_view.h",
         "views/passwords/manage_passwords_icon_views.cc",
         "views/passwords/manage_passwords_icon_views.h",
+        "views/payments/payment_request_dialog.cc",
+        "views/payments/payment_request_dialog.h",
         "views/process_singleton_dialog_linux.cc",
         "views/profiles/profile_indicator_icon.cc",
         "views/profiles/profile_indicator_icon.h",
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
index b2a88b5..7975c35 100644
--- a/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
+++ b/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
@@ -47,7 +47,7 @@
 
 void JSONResponseFetcher::OnJsonParseSuccess(
     std::unique_ptr<base::Value> parsed_json) {
-  if (!parsed_json->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!parsed_json->IsType(base::Value::Type::DICTIONARY)) {
     OnJsonParseError(kBadResponse);
     return;
   }
diff --git a/chrome/browser/ui/ash/ash_init.cc b/chrome/browser/ui/ash/ash_init.cc
index 5532f53..e64b95b3 100644
--- a/chrome/browser/ui/ash/ash_init.cc
+++ b/chrome/browser/ui/ash/ash_init.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/ui/ash/chrome_shell_content_state.h"
 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
 #include "chrome/browser/ui/ash/ime_controller_chromeos.h"
-#include "chrome/browser/ui/ash/volume_controller_chromeos.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/accelerometer/accelerometer_reader.h"
 #include "chromeos/chromeos_switches.h"
diff --git a/chrome/browser/ui/ash/ash_util.cc b/chrome/browser/ui/ash/ash_util.cc
index 33c69c1..4609b79 100644
--- a/chrome/browser/ui/ash/ash_util.cc
+++ b/chrome/browser/ui/ash/ash_util.cc
@@ -8,9 +8,23 @@
 #include "ash/common/wm_shell.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/ash/ash_init.h"
+#include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "ui/aura/window_event_dispatcher.h"
 
+namespace ash_util {
+
+const char* GetAshServiceName() {
+  // Under mash the ash process provides the service.
+  if (chrome::IsRunningInMash())
+    return "ash";
+
+  // Under classic ash the browser process provides the service.
+  return content::mojom::kBrowserServiceName;
+}
+
+}  // namespace ash_util
+
 namespace chrome {
 
 bool ShouldOpenAshOnStartup() {
diff --git a/chrome/browser/ui/ash/ash_util.h b/chrome/browser/ui/ash/ash_util.h
index 3cce86d..110482e 100644
--- a/chrome/browser/ui/ash/ash_util.h
+++ b/chrome/browser/ui/ash/ash_util.h
@@ -9,6 +9,15 @@
 class Accelerator;
 }  // namespace ui
 
+namespace ash_util {
+
+// Returns the name of the ash service depending on whether the browser is
+// running in classic ash or mash.
+const char* GetAshServiceName();
+
+}  // namespace ash_util
+
+// TODO(jamescook): Change this namespace to ash_util.
 namespace chrome {
 
 // Returns true if Ash should be run at startup.
diff --git a/chrome/browser/ui/ash/cast_config_client_media_router.cc b/chrome/browser/ui/ash/cast_config_client_media_router.cc
index bcb62dc..24e1001 100644
--- a/chrome/browser/ui/ash/cast_config_client_media_router.cc
+++ b/chrome/browser/ui/ash/cast_config_client_media_router.cc
@@ -24,7 +24,6 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace {
@@ -148,16 +147,9 @@
 
   // When starting up, we need to connect to ash and set ourselves as the
   // client.
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  // Connect to the CastClient interface in ash. Under mash the CastClient
-  // interface is in the ash process. In classic ash we provide it to ourself.
-  if (chrome::IsRunningInMash()) {
-    connector->ConnectToInterface("ash", &cast_config_);
-  } else {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &cast_config_);
-  }
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ConnectToInterface(ash_util::GetAshServiceName(), &cast_config_);
 
   // Register this object as the client interface implementation.
   ash::mojom::CastConfigClientAssociatedPtrInfo ptr_info;
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index e954d1ac..92a8f8c9 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
+#include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -27,8 +28,11 @@
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/sessions/core/tab_restore_service_observer.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/common/service_names.mojom.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/window_open_disposition.h"
 
 namespace {
@@ -41,7 +45,18 @@
 
 }  // namespace
 
-ChromeNewWindowClient::ChromeNewWindowClient() {}
+ChromeNewWindowClient::ChromeNewWindowClient() : binding_(this) {
+  service_manager::Connector* connector =
+      content::ServiceManagerConnection::GetForProcess()->GetConnector();
+  connector->ConnectToInterface(ash_util::GetAshServiceName(),
+                                &new_window_controller_);
+
+  // Register this object as the client interface implementation.
+  ash::mojom::NewWindowClientAssociatedPtrInfo ptr_info;
+  binding_.Bind(&ptr_info, new_window_controller_.associated_group());
+  new_window_controller_->SetClient(std::move(ptr_info));
+}
+
 ChromeNewWindowClient::~ChromeNewWindowClient() {}
 
 // TabRestoreHelper is used to restore a tab. In particular when the user
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.h b/chrome/browser/ui/ash/chrome_new_window_client.h
index d07e640..f8bcb0f 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.h
+++ b/chrome/browser/ui/ash/chrome_new_window_client.h
@@ -9,6 +9,7 @@
 
 #include "ash/public/interfaces/new_window.mojom.h"
 #include "base/macros.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 
 class ChromeNewWindowClient : public ash::mojom::NewWindowClient {
  public:
@@ -31,6 +32,11 @@
 
   std::unique_ptr<TabRestoreHelper> tab_restore_helper_;
 
+  ash::mojom::NewWindowControllerPtr new_window_controller_;
+
+  // Binds this object to the client interface.
+  mojo::AssociatedBinding<ash::mojom::NewWindowClient> binding_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeNewWindowClient);
 };
 
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index 7ce6374..dbc2194 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/common/new_window_controller.h"
 #include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "ash/wm/window_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -33,7 +33,7 @@
   Profile* profile1 = ProfileManager::GetActiveUserProfile();
   Browser* browser1 = CreateBrowser(profile1);
   // The newly created window should be created for the current active profile.
-  ash::WmShell::Get()->new_window_client()->NewWindow(false);
+  ash::WmShell::Get()->new_window_controller()->NewWindow(false);
   EXPECT_EQ(
       chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow())->profile(),
       profile1);
@@ -47,7 +47,7 @@
   Browser* browser2 = CreateBrowser(profile2);
   // The newly created window should be created for the current active window's
   // profile, which is |profile2|.
-  ash::WmShell::Get()->new_window_client()->NewWindow(false);
+  ash::WmShell::Get()->new_window_controller()->NewWindow(false);
   EXPECT_EQ(
       chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow())->profile(),
       profile2);
@@ -55,7 +55,7 @@
   // After activating |browser1|, the newly created window should be created
   // against |browser1|'s profile.
   browser1->window()->Show();
-  ash::WmShell::Get()->new_window_client()->NewWindow(false);
+  ash::WmShell::Get()->new_window_controller()->NewWindow(false);
   EXPECT_EQ(
       chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow())->profile(),
       profile1);
@@ -64,7 +64,7 @@
   // The newly created incoginito window should be created against the current
   // active |browser1|'s profile.
   browser1->window()->Show();
-  ash::WmShell::Get()->new_window_client()->NewWindow(true);
+  ash::WmShell::Get()->new_window_controller()->NewWindow(true);
   EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow())
                 ->profile()
                 ->GetOriginalProfile(),
@@ -73,7 +73,7 @@
   // The newly created incoginito window should be created against the current
   // active |browser2|'s profile.
   browser2->window()->Show();
-  ash::WmShell::Get()->new_window_client()->NewWindow(true);
+  ash::WmShell::Get()->new_window_controller()->NewWindow(true);
   EXPECT_EQ(chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow())
                 ->profile()
                 ->GetOriginalProfile(),
diff --git a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
index 965cc84..459433c 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.cc
@@ -196,7 +196,8 @@
   const ash::ShelfID shelf_id = owner_->GetShelfIDForAppID(shelf_app_id);
 
   // We are allowed to apply new deferred controller only over shortcut.
-  if (shelf_id && owner_->GetItem(shelf_id).type != ash::TYPE_APP_SHORTCUT)
+  const ash::ShelfItem* item = owner_->GetItem(shelf_id);
+  if (item && item->type != ash::TYPE_APP_SHORTCUT)
     return;
 
   ArcAppDeferredLauncherItemController* controller =
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 799faab..0c35728 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -52,14 +51,8 @@
   if (!connector)
     return false;
 
-  // Under mash the ShelfController interface is in the ash process. In classic
-  // ash we provide it to ourself.
-  if (chrome::IsRunningInMash()) {
-    connector->ConnectToInterface("ash", &shelf_controller_);
-  } else {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &shelf_controller_);
-  }
+  connector->ConnectToInterface(ash_util::GetAshServiceName(),
+                                &shelf_controller_);
   return true;
 }
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 5315d36f..e4cc0a8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -19,7 +19,6 @@
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h"
 #include "chrome/browser/ui/ash/launcher/settings_window_observer.h"
-#include "extensions/common/constants.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 
 class AccountId;
@@ -85,8 +84,8 @@
                                              const std::string& app_id,
                                              ash::ShelfItemStatus status) = 0;
 
-  // A helper to get the shelf item with the given id, which must be valid.
-  virtual const ash::ShelfItem& GetItem(ash::ShelfID id) const = 0;
+  // Returns the shelf item with the given id, or null if |id| isn't found.
+  virtual const ash::ShelfItem* GetItem(ash::ShelfID id) const = 0;
 
   // Updates the type of an item.
   virtual void SetItemType(ash::ShelfID id, ash::ShelfItemType type) = 0;
@@ -156,17 +155,10 @@
                            ash::LaunchSource source,
                            int event_flags) = 0;
 
-  // Returns the launch type of app for the specified id.
-  virtual extensions::LaunchType GetLaunchType(ash::ShelfID id) = 0;
-
   // Set the image for a specific shelf item (e.g. when set by the app).
   virtual void SetLauncherItemImage(ash::ShelfID shelf_id,
                                     const gfx::ImageSkia& image) = 0;
 
-  // Updates the launch type of the app for the specified id to |launch_type|.
-  virtual void SetLaunchType(ash::ShelfID id,
-                             extensions::LaunchType launch_type) = 0;
-
   // Notify the controller that the state of an non platform app's tabs
   // have changed,
   virtual void UpdateAppState(content::WebContents* contents,
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
index 9de260a..5f272d6c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.cc
@@ -28,10 +28,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/extensions/gfx_utils.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -67,7 +65,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -79,14 +76,7 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/extension_util.h"
-#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_resource.h"
-#include "extensions/common/manifest_handlers/icons_handler.h"
-#include "extensions/common/url_pattern.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -97,7 +87,6 @@
 #include "ui/wm/core/window_animations.h"
 
 using extensions::Extension;
-using extensions::UnloadedExtensionInfo;
 using extension_misc::kGmailAppId;
 using content::WebContents;
 
@@ -264,9 +253,8 @@
   model_->RemoveObserver(this);
   if (ash::Shell::HasInstance())
     ash::Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
-  for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
-       i != id_to_item_controller_map_.end(); ++i) {
-    int index = model_->ItemIndexByID(i->first);
+  for (const auto& pair : id_to_item_controller_map_) {
+    int index = model_->ItemIndexByID(pair.first);
     // A "browser proxy" is not known to the model and this removal does
     // therefore not need to be propagated to the model.
     if (index != -1 &&
@@ -301,32 +289,31 @@
                                ash::TYPE_APP);
 }
 
-const ash::ShelfItem& ChromeLauncherControllerImpl::GetItem(
+const ash::ShelfItem* ChromeLauncherControllerImpl::GetItem(
     ash::ShelfID id) const {
   const int index = model_->ItemIndexByID(id);
-  DCHECK_GE(index, 0);
-  return model_->items()[index];
+  if (index >= 0 && index < model_->item_count())
+    return &model_->items()[index];
+  return nullptr;
 }
 
 void ChromeLauncherControllerImpl::SetItemType(ash::ShelfID id,
                                                ash::ShelfItemType type) {
-  const int index = model_->ItemIndexByID(id);
-  DCHECK_GE(index, 0);
-  ash::ShelfItem item = model_->items()[index];
-  if (item.type != type) {
-    item.type = type;
-    model_->Set(index, item);
+  const ash::ShelfItem* item = GetItem(id);
+  if (item && item->type != type) {
+    ash::ShelfItem new_item = *item;
+    new_item.type = type;
+    model_->Set(model_->ItemIndexByID(id), new_item);
   }
 }
 
 void ChromeLauncherControllerImpl::SetItemStatus(ash::ShelfID id,
                                                  ash::ShelfItemStatus status) {
-  const int index = model_->ItemIndexByID(id);
-  DCHECK_GE(index, 0);
-  ash::ShelfItem item = model_->items()[index];
-  if (item.status != status) {
-    item.status = status;
-    model_->Set(index, item);
+  const ash::ShelfItem* item = GetItem(id);
+  if (item && item->status != status) {
+    ash::ShelfItem new_item = *item;
+    new_item.status = status;
+    model_->Set(model_->ItemIndexByID(id), new_item);
   }
 }
 
@@ -363,18 +350,11 @@
 
 void ChromeLauncherControllerImpl::Pin(ash::ShelfID id) {
   DCHECK(HasShelfIDToAppIDMapping(id));
-
-  int index = model_->ItemIndexByID(id);
-  DCHECK_GE(index, 0);
-
-  ash::ShelfItem item = model_->items()[index];
-
-  if (item.type == ash::TYPE_APP) {
-    item.type = ash::TYPE_APP_SHORTCUT;
-    model_->Set(index, item);
-  } else if (item.type != ash::TYPE_APP_SHORTCUT) {
+  const ash::ShelfItem* item = GetItem(id);
+  if (item && item->type == ash::TYPE_APP)
+    SetItemType(id, ash::TYPE_APP_SHORTCUT);
+  else if (!item || item->type != ash::TYPE_APP_SHORTCUT)
     return;
-  }
 
   SyncPinPosition(id);
 }
@@ -394,18 +374,17 @@
                                                 GetLaunchIDForShelfID(id)));
   }
 
-  if (GetItem(id).status != ash::STATUS_CLOSED || controller->locked())
+  const ash::ShelfItem* item = GetItem(id);
+  if (item && (item->status != ash::STATUS_CLOSED || controller->locked()))
     UnpinRunningAppInternal(model_->ItemIndexByID(id));
   else
     LauncherItemClosed(id);
 }
 
 bool ChromeLauncherControllerImpl::IsPinned(ash::ShelfID id) {
-  int index = model_->ItemIndexByID(id);
-  if (index < 0)
-    return false;
-  ash::ShelfItemType type = model_->items()[index].type;
-  return (type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_BROWSER_SHORTCUT);
+  const ash::ShelfItem* item = GetItem(id);
+  return item && (item->type == ash::TYPE_APP_SHORTCUT ||
+                  item->type == ash::TYPE_BROWSER_SHORTCUT);
 }
 
 void ChromeLauncherControllerImpl::TogglePinned(ash::ShelfID id) {
@@ -419,13 +398,9 @@
 }
 
 bool ChromeLauncherControllerImpl::IsPinnable(ash::ShelfID id) const {
-  int index = model_->ItemIndexByID(id);
-  if (index == -1)
-    return false;
-
-  ash::ShelfItemType type = model_->items()[index].type;
-  std::string app_id;
-  return ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_APP) &&
+  const ash::ShelfItem* item = GetItem(id);
+  return (item && (item->type == ash::TYPE_APP_SHORTCUT ||
+                   item->type == ash::TYPE_APP) &&
           model_->GetShelfItemDelegate(id)->CanPin());
 }
 
@@ -465,8 +440,8 @@
 }
 
 bool ChromeLauncherControllerImpl::IsOpen(ash::ShelfID id) {
-  const int index = model_->ItemIndexByID(id);
-  return index >= 0 && model_->items()[index].status != ash::STATUS_CLOSED;
+  const ash::ShelfItem* item = GetItem(id);
+  return item && item->status != ash::STATUS_CLOSED;
 }
 
 bool ChromeLauncherControllerImpl::IsPlatformApp(ash::ShelfID id) {
@@ -501,38 +476,15 @@
     LaunchApp(app_id, source, event_flags);
 }
 
-extensions::LaunchType ChromeLauncherControllerImpl::GetLaunchType(
-    ash::ShelfID id) {
-  const Extension* extension =
-      GetExtensionForAppID(GetAppIDForShelfID(id), profile());
-
-  // An extension can be unloaded/updated/unavailable at any time.
-  if (!extension)
-    return extensions::LAUNCH_TYPE_DEFAULT;
-
-  return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile()),
-                                   extension);
-}
-
 void ChromeLauncherControllerImpl::SetLauncherItemImage(
     ash::ShelfID shelf_id,
     const gfx::ImageSkia& image) {
-  int index = model_->ItemIndexByID(shelf_id);
-  if (index == -1)
-    return;
-  ash::ShelfItem item = model_->items()[index];
-  item.image = image;
-  model_->Set(index, item);
-}
-
-void ChromeLauncherControllerImpl::SetLaunchType(
-    ash::ShelfID id,
-    extensions::LaunchType launch_type) {
-  LauncherItemController* controller = GetLauncherItemController(id);
-  if (!controller)
-    return;
-
-  extensions::SetLaunchType(profile(), controller->app_id(), launch_type);
+  const ash::ShelfItem* item = GetItem(shelf_id);
+  if (item) {
+    ash::ShelfItem new_item = *item;
+    new_item.image = image;
+    model_->Set(model_->ItemIndexByID(shelf_id), new_item);
+  }
 }
 
 void ChromeLauncherControllerImpl::UpdateAppState(
@@ -596,22 +548,16 @@
 void ChromeLauncherControllerImpl::SetRefocusURLPatternForTest(
     ash::ShelfID id,
     const GURL& url) {
-  int index = model_->ItemIndexByID(id);
-  if (index == -1) {
-    NOTREACHED() << "Invalid launcher id";
-    return;
-  }
-
-  ash::ShelfItemType type = model_->items()[index].type;
-  if ((type == ash::TYPE_APP_SHORTCUT || type == ash::TYPE_APP) &&
-      !IsPlatformApp(id)) {
+  const ash::ShelfItem* item = GetItem(id);
+  if (item && !IsPlatformApp(id) &&
+      (item->type == ash::TYPE_APP_SHORTCUT || item->type == ash::TYPE_APP)) {
     LauncherItemController* controller = GetLauncherItemController(id);
     DCHECK(controller);
     AppShortcutLauncherItemController* app_controller =
         static_cast<AppShortcutLauncherItemController*>(controller);
     app_controller->set_refocus_url(url);
   } else {
-    NOTREACHED() << "Invalid launcher type";
+    NOTREACHED() << "Invalid launcher item or type";
   }
 }
 
@@ -687,35 +633,29 @@
 ChromeLauncherAppMenuItems ChromeLauncherControllerImpl::GetApplicationList(
     const ash::ShelfItem& item,
     int event_flags) {
-  // Make sure that there is a controller associated with the id and that the
-  // extension itself is a valid application and not a panel.
   LauncherItemController* controller = GetLauncherItemController(item.id);
-  if (!controller || !GetShelfIDForAppID(controller->app_id()))
-    return ChromeLauncherAppMenuItems();
-
-  return controller->GetApplicationList(event_flags);
+  return controller ? controller->GetApplicationList(event_flags)
+                    : ChromeLauncherAppMenuItems();
 }
 
 std::vector<content::WebContents*>
 ChromeLauncherControllerImpl::GetV1ApplicationsFromAppId(
     const std::string& app_id) {
-  ash::ShelfID id = GetShelfIDForAppID(app_id);
+  const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
   // If there is no such item pinned to the launcher, no menu gets created.
-  if (id != ash::kInvalidShelfID &&
-      GetItem(id).type == ash::TYPE_APP_SHORTCUT) {
-    LauncherItemController* controller = GetLauncherItemController(id);
-    AppShortcutLauncherItemController* app_controller =
-        static_cast<AppShortcutLauncherItemController*>(controller);
-    return app_controller->GetRunningApplications();
-  }
-  return std::vector<content::WebContents*>();
+  if (!item || item->type != ash::TYPE_APP_SHORTCUT)
+    return std::vector<content::WebContents*>();
+  LauncherItemController* controller = GetLauncherItemController(item->id);
+  AppShortcutLauncherItemController* app_controller =
+      static_cast<AppShortcutLauncherItemController*>(controller);
+  return app_controller->GetRunningApplications();
 }
 
 void ChromeLauncherControllerImpl::ActivateShellApp(const std::string& app_id,
                                                     int window_index) {
-  ash::ShelfID id = GetShelfIDForAppID(app_id);
-  if (id != ash::kInvalidShelfID && GetItem(id).type == ash::TYPE_APP) {
-    LauncherItemController* controller = GetLauncherItemController(id);
+  const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
+  if (item && item->type == ash::TYPE_APP) {
+    LauncherItemController* controller = GetLauncherItemController(item->id);
     AppWindowLauncherItemController* app_window_controller =
         static_cast<AppWindowLauncherItemController*>(controller);
     app_window_controller->ActivateIndexedApp(window_index);
@@ -781,12 +721,10 @@
 
 BrowserShortcutLauncherItemController*
 ChromeLauncherControllerImpl::GetBrowserShortcutLauncherItemController() {
-  for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
-       i != id_to_item_controller_map_.end(); ++i) {
-    int index = model_->ItemIndexByID(i->first);
-    const ash::ShelfItem& item = model_->items()[index];
-    if (item.type == ash::TYPE_BROWSER_SHORTCUT)
-      return static_cast<BrowserShortcutLauncherItemController*>(i->second);
+  for (const auto& pair : id_to_item_controller_map_) {
+    const ash::ShelfItem* item = GetItem(pair.first);
+    if (item && item->type == ash::TYPE_BROWSER_SHORTCUT)
+      return static_cast<BrowserShortcutLauncherItemController*>(pair.second);
   }
   NOTREACHED()
       << "There should be always be a BrowserShortcutLauncherItemController.";
@@ -944,10 +882,8 @@
 bool ChromeLauncherControllerImpl::IsAppPinned(const std::string& app_id) {
   const std::string shelf_app_id =
       ArcAppWindowLauncherController::GetShelfAppIdFromArcAppId(app_id);
-  for (IDToItemControllerMap::const_iterator i =
-           id_to_item_controller_map_.begin();
-       i != id_to_item_controller_map_.end(); ++i) {
-    if (IsPinned(i->first) && i->second->app_id() == shelf_app_id)
+  for (const auto& pair : id_to_item_controller_map_) {
+    if (IsPinned(pair.first) && pair.second->app_id() == shelf_app_id)
       return true;
   }
   return false;
@@ -1044,17 +980,14 @@
 
   // Find the first insertion point for running applications.
   int running_index = model_->FirstRunningAppIndex();
-  for (RunningAppListIds::iterator app_id = app_id_list->second.begin();
-       app_id != app_id_list->second.end(); ++app_id) {
-    ash::ShelfID shelf_id = GetShelfIDForAppID(*app_id);
-    if (shelf_id) {
-      int app_index = model_->ItemIndexByID(shelf_id);
+  for (const std::string& app_id : app_id_list->second) {
+    const ash::ShelfItem* item = GetItem(GetShelfIDForAppID(app_id));
+    if (item && item->type == ash::TYPE_APP) {
+      int app_index = model_->ItemIndexByID(item->id);
       DCHECK_GE(app_index, 0);
-      if (model_->items()[app_index].type == ash::TYPE_APP) {
-        if (running_index != app_index)
-          model_->Move(running_index, app_index);
-        running_index++;
-      }
+      if (running_index != app_index)
+        model_->Move(running_index, app_index);
+      running_index++;
     }
   }
 }
@@ -1116,14 +1049,9 @@
 void ChromeLauncherControllerImpl::PinRunningAppInternal(
     int index,
     ash::ShelfID shelf_id) {
+  DCHECK_EQ(GetItem(shelf_id)->type, ash::TYPE_APP);
+  SetItemType(shelf_id, ash::TYPE_APP_SHORTCUT);
   int running_index = model_->ItemIndexByID(shelf_id);
-  ash::ShelfItem item = model_->items()[running_index];
-  DCHECK_EQ(item.type, ash::TYPE_APP);
-  item.type = ash::TYPE_APP_SHORTCUT;
-  model_->Set(running_index, item);
-  // The |ShelfModel|'s weight system might reposition the item to a
-  // new index, so we get the index again.
-  running_index = model_->ItemIndexByID(shelf_id);
   if (running_index < index)
     --index;
   if (running_index != index)
@@ -1131,11 +1059,10 @@
 }
 
 void ChromeLauncherControllerImpl::UnpinRunningAppInternal(int index) {
-  DCHECK_GE(index, 0);
+  DCHECK(index >= 0 && index < model_->item_count());
   ash::ShelfItem item = model_->items()[index];
   DCHECK_EQ(item.type, ash::TYPE_APP_SHORTCUT);
-  item.type = ash::TYPE_APP;
-  model_->Set(index, item);
+  SetItemType(item.id, ash::TYPE_APP);
 }
 
 void ChromeLauncherControllerImpl::SyncPinPosition(ash::ShelfID shelf_id) {
@@ -1343,6 +1270,7 @@
 
   ash::ShelfItem item;
   item.type = shelf_item_type;
+  item.app_id = app_id;
   item.image = extensions::util::GetDefaultAppIcon();
 
   ash::ShelfItemStatus new_state = GetAppState(app_id);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
index 7c733c4..9a0888f8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
@@ -63,7 +63,7 @@
   ash::ShelfID CreateAppLauncherItem(LauncherItemController* controller,
                                      const std::string& app_id,
                                      ash::ShelfItemStatus status) override;
-  const ash::ShelfItem& GetItem(ash::ShelfID id) const override;
+  const ash::ShelfItem* GetItem(ash::ShelfID id) const override;
   void SetItemType(ash::ShelfID id, ash::ShelfItemType type) override;
   void SetItemStatus(ash::ShelfID id, ash::ShelfItemStatus status) override;
   void SetItemController(ash::ShelfID id,
@@ -83,11 +83,8 @@
   void ActivateApp(const std::string& app_id,
                    ash::LaunchSource source,
                    int event_flags) override;
-  extensions::LaunchType GetLaunchType(ash::ShelfID id) override;
   void SetLauncherItemImage(ash::ShelfID shelf_id,
                             const gfx::ImageSkia& image) override;
-  void SetLaunchType(ash::ShelfID id,
-                     extensions::LaunchType launch_type) override;
   void UpdateAppState(content::WebContents* contents,
                       AppState app_state) override;
   ash::ShelfID GetShelfIDForWebContents(
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
index d415692..49064a8 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.cc
@@ -69,10 +69,10 @@
   return ash::TYPE_UNDEFINED;
 }
 
-const ash::ShelfItem& ChromeLauncherControllerMus::GetItem(
+const ash::ShelfItem* ChromeLauncherControllerMus::GetItem(
     ash::ShelfID id) const {
   NOTIMPLEMENTED();
-  return fake_item_;
+  return nullptr;
 }
 
 void ChromeLauncherControllerMus::SetItemType(ash::ShelfID id,
@@ -149,24 +149,12 @@
   NOTIMPLEMENTED();
 }
 
-extensions::LaunchType ChromeLauncherControllerMus::GetLaunchType(
-    ash::ShelfID id) {
-  NOTIMPLEMENTED();
-  return extensions::LAUNCH_TYPE_INVALID;
-}
-
 void ChromeLauncherControllerMus::SetLauncherItemImage(
     ash::ShelfID shelf_id,
     const gfx::ImageSkia& image) {
   NOTIMPLEMENTED();
 }
 
-void ChromeLauncherControllerMus::SetLaunchType(
-    ash::ShelfID id,
-    extensions::LaunchType launch_type) {
-  NOTIMPLEMENTED();
-}
-
 void ChromeLauncherControllerMus::UpdateAppState(content::WebContents* contents,
                                                  AppState app_state) {
   NOTIMPLEMENTED();
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
index ddeacd89..dd973ba 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h
@@ -24,7 +24,7 @@
   ash::ShelfID CreateAppLauncherItem(LauncherItemController* controller,
                                      const std::string& app_id,
                                      ash::ShelfItemStatus status) override;
-  const ash::ShelfItem& GetItem(ash::ShelfID id) const override;
+  const ash::ShelfItem* GetItem(ash::ShelfID id) const override;
   void SetItemType(ash::ShelfID id, ash::ShelfItemType type) override;
   void SetItemStatus(ash::ShelfID id, ash::ShelfItemStatus status) override;
   void SetItemController(ash::ShelfID id,
@@ -44,11 +44,8 @@
   void ActivateApp(const std::string& app_id,
                    ash::LaunchSource source,
                    int event_flags) override;
-  extensions::LaunchType GetLaunchType(ash::ShelfID id) override;
   void SetLauncherItemImage(ash::ShelfID shelf_id,
                             const gfx::ImageSkia& image) override;
-  void SetLaunchType(ash::ShelfID id,
-                     extensions::LaunchType launch_type) override;
   void UpdateAppState(content::WebContents* contents,
                       AppState app_state) override;
   ash::ShelfID GetShelfIDForWebContents(
@@ -93,9 +90,6 @@
   std::map<std::string, std::unique_ptr<ChromeShelfItemDelegate>>
       app_id_to_item_delegate_;
 
-  // A fake item used for the unimplemented GetItem() override.
-  const ash::ShelfItem fake_item_;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerMus);
 };
 
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index 4d255303..499240c1 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -9,14 +9,17 @@
 #include "base/bind.h"
 #include "chrome/browser/extensions/context_menu_matcher.h"
 #include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/context_menu_params.h"
+#include "extensions/browser/extension_prefs.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
@@ -123,7 +126,7 @@
   if (command_id == MENU_OPEN_NEW) {
     if (controller()->IsPlatformApp(item().id))
       return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW);
-    switch (controller()->GetLaunchType(item().id)) {
+    switch (GetLaunchType()) {
       case extensions::LAUNCH_TYPE_PINNED:
       case extensions::LAUNCH_TYPE_REGULAR:
         return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_NEW_TAB);
@@ -142,17 +145,13 @@
 bool ExtensionLauncherContextMenu::IsCommandIdChecked(int command_id) const {
   switch (command_id) {
     case LAUNCH_TYPE_PINNED_TAB:
-      return controller()->GetLaunchType(item().id) ==
-             extensions::LAUNCH_TYPE_PINNED;
+      return GetLaunchType() == extensions::LAUNCH_TYPE_PINNED;
     case LAUNCH_TYPE_REGULAR_TAB:
-      return controller()->GetLaunchType(item().id) ==
-             extensions::LAUNCH_TYPE_REGULAR;
+      return GetLaunchType() == extensions::LAUNCH_TYPE_REGULAR;
     case LAUNCH_TYPE_WINDOW:
-      return controller()->GetLaunchType(item().id) ==
-             extensions::LAUNCH_TYPE_WINDOW;
+      return GetLaunchType() == extensions::LAUNCH_TYPE_WINDOW;
     case LAUNCH_TYPE_FULLSCREEN:
-      return controller()->GetLaunchType(item().id) ==
-             extensions::LAUNCH_TYPE_FULLSCREEN;
+      return GetLaunchType() == extensions::LAUNCH_TYPE_FULLSCREEN;
     default:
       if (command_id < MENU_ITEM_COUNT)
         return LauncherContextMenu::IsCommandIdChecked(command_id);
@@ -187,27 +186,25 @@
     return;
   switch (static_cast<MenuItem>(command_id)) {
     case LAUNCH_TYPE_PINNED_TAB:
-      controller()->SetLaunchType(item().id, extensions::LAUNCH_TYPE_PINNED);
+      SetLaunchType(extensions::LAUNCH_TYPE_PINNED);
       break;
     case LAUNCH_TYPE_REGULAR_TAB:
-      controller()->SetLaunchType(item().id, extensions::LAUNCH_TYPE_REGULAR);
+      SetLaunchType(extensions::LAUNCH_TYPE_REGULAR);
       break;
     case LAUNCH_TYPE_WINDOW: {
       extensions::LaunchType launch_type = extensions::LAUNCH_TYPE_WINDOW;
       // With bookmark apps enabled, hosted apps can only toggle between
       // LAUNCH_WINDOW and LAUNCH_REGULAR.
       if (extensions::util::IsNewBookmarkAppsEnabled()) {
-        launch_type = controller()->GetLaunchType(item().id) ==
-                              extensions::LAUNCH_TYPE_WINDOW
+        launch_type = GetLaunchType() == extensions::LAUNCH_TYPE_WINDOW
                           ? extensions::LAUNCH_TYPE_REGULAR
                           : extensions::LAUNCH_TYPE_WINDOW;
       }
-      controller()->SetLaunchType(item().id, launch_type);
+      SetLaunchType(launch_type);
       break;
     }
     case LAUNCH_TYPE_FULLSCREEN:
-      controller()->SetLaunchType(item().id,
-                                  extensions::LAUNCH_TYPE_FULLSCREEN);
+      SetLaunchType(extensions::LAUNCH_TYPE_FULLSCREEN);
       break;
     case MENU_NEW_WINDOW:
       chrome::NewEmptyWindow(controller()->profile());
@@ -222,3 +219,19 @@
       }
   }
 }
+
+extensions::LaunchType ExtensionLauncherContextMenu::GetLaunchType() const {
+  const extensions::Extension* extension =
+      GetExtensionForAppID(item().app_id, controller()->profile());
+
+  // An extension can be unloaded/updated/unavailable at any time.
+  if (!extension)
+    return extensions::LAUNCH_TYPE_DEFAULT;
+
+  return extensions::GetLaunchType(
+      extensions::ExtensionPrefs::Get(controller()->profile()), extension);
+}
+
+void ExtensionLauncherContextMenu::SetLaunchType(extensions::LaunchType type) {
+  extensions::SetLaunchType(controller()->profile(), item().app_id, type);
+}
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
index 183691fa..6f35b267 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
+#include "extensions/common/constants.h"
 
 class ChromeLauncherControllerImpl;
 
@@ -21,8 +22,7 @@
 class ContextMenuMatcher;
 }
 
-// Class for context menu which is shown for a regular extension item in the
-// shelf.
+// Context menu shown for an extension item in the shelf.
 class ExtensionLauncherContextMenu : public LauncherContextMenu {
  public:
   ExtensionLauncherContextMenu(ChromeLauncherControllerImpl* controller,
@@ -40,6 +40,10 @@
  private:
   void Init();
 
+  // Helpers to get and set the launch type for the extension item.
+  extensions::LaunchType GetLaunchType() const;
+  void SetLaunchType(extensions::LaunchType launch_type);
+
   std::unique_ptr<extensions::ContextMenuMatcher> extension_items_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionLauncherContextMenu);
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index cf32e54..aa70210 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -12,6 +12,7 @@
 #include "ash/common/strings/grit/ash_strings.h"
 #include "ash/common/wallpaper/wallpaper_delegate.h"
 #include "ash/common/wm_shell.h"
+#include "ash/common/wm_window.h"
 #include "build/build_config.h"
 #include "chrome/browser/fullscreen.h"
 #include "chrome/browser/profiles/profile.h"
@@ -178,11 +179,12 @@
 }
 
 void LauncherContextMenu::AddShelfOptionsMenu() {
-  // In fullscreen, the launcher is either hidden or autohidden depending
-  // on thethe type of fullscreen. Do not show the auto-hide menu item while in
-  // while in fullscreen because it is confusing when the preference appears
+  // In fullscreen, the launcher is either hidden or auto hidden depending
+  // on the type of fullscreen. Do not show the auto-hide menu item while in
+  // fullscreen per display because it is confusing when the preference appears
   // not to apply.
-  if (!IsFullScreenMode() &&
+  int64_t display_id = wm_shelf_->GetWindow()->GetDisplayNearestWindow().id();
+  if (!IsFullScreenMode(display_id) &&
       CanUserModifyShelfAutoHideBehavior(controller_->profile())) {
     AddCheckItemWithStringId(MENU_AUTO_HIDE,
                              IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE);
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.h b/chrome/browser/ui/ash/launcher/launcher_context_menu.h
index efdd5f80..ce10934 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.h
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.h
@@ -85,6 +85,8 @@
                            ArcLauncherContextMenuItemCheck);
   FRIEND_TEST_ALL_PREFIXES(LauncherContextMenuTest,
                            DesktopShellLauncherContextMenuVerifyCloseItem);
+  FRIEND_TEST_ALL_PREFIXES(LauncherContextMenuTest,
+                           AutohideShelfOptionOnExternalDisplay);
   FRIEND_TEST_ALL_PREFIXES(ShelfAppBrowserTest,
                            LauncherContextMenuVerifyCloseItemAppearance);
 
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
index ae8a7a1..8b7b1b5 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu_unittest.cc
@@ -5,9 +5,11 @@
 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
 
 #include "ash/common/shelf/shelf_item_types.h"
+#include "ash/common/wm_lookup.h"
 #include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_shell.h"
 #include "ash/common/wm_window.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -25,6 +27,8 @@
 #include "components/exo/shell_surface.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/views/widget/widget.h"
 
 class LauncherContextMenuTest : public ash::test::AshTestBase {
@@ -48,24 +52,26 @@
     ash::test::AshTestBase::TearDown();
   }
 
-  ash::WmShelf* GetWmShelf() {
-    return ash::WmShell::Get()
-        ->GetPrimaryRootWindow()
-        ->GetRootWindowController()
-        ->GetShelf();
+  ash::WmShelf* GetWmShelf(int64_t display_id) {
+    ash::WmRootWindowController* root_window_controller =
+        ash::WmLookup::Get()->GetRootWindowControllerWithDisplayId(display_id);
+    EXPECT_NE(nullptr, root_window_controller);
+    return root_window_controller->GetShelf();
   }
 
   LauncherContextMenu* CreateLauncherContextMenu(
-      ash::ShelfItemType shelf_item_type) {
+      ash::ShelfItemType shelf_item_type,
+      ash::WmShelf* wm_shelf) {
     ash::ShelfItem item;
     item.id = 123;  // dummy id
     item.type = shelf_item_type;
-    return LauncherContextMenu::Create(controller_.get(), &item, GetWmShelf());
+    return LauncherContextMenu::Create(controller_.get(), &item, wm_shelf);
   }
 
-  LauncherContextMenu* CreateLauncherContextMenuForDesktopShell() {
+  LauncherContextMenu* CreateLauncherContextMenuForDesktopShell(
+      ash::WmShelf* wm_shelf) {
     ash::ShelfItem* item = nullptr;
-    return LauncherContextMenu::Create(controller_.get(), item, GetWmShelf());
+    return LauncherContextMenu::Create(controller_.get(), item, wm_shelf);
   }
 
   // Creates app window and set optional Arc application id.
@@ -98,9 +104,10 @@
 // menu is disabled when Incognito mode is switched off (by a policy).
 TEST_F(LauncherContextMenuTest,
        NewIncognitoWindowMenuIsDisabledWhenIncognitoModeOff) {
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
   // Initially, "New Incognito window" should be enabled.
-  std::unique_ptr<LauncherContextMenu> menu(
-      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT));
+  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
+      ash::TYPE_BROWSER_SHORTCUT, GetWmShelf(primary_id)));
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_INCOGNITO_WINDOW));
   EXPECT_TRUE(menu->IsCommandIdEnabled(
@@ -109,7 +116,8 @@
   // Disable Incognito mode.
   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
                                       IncognitoModePrefs::DISABLED);
-  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT));
+  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT,
+                                       GetWmShelf(primary_id)));
   // The item should be disabled.
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_INCOGNITO_WINDOW));
@@ -121,9 +129,10 @@
 // menu is disabled when Incognito mode is forced (by a policy).
 TEST_F(LauncherContextMenuTest,
        NewWindowMenuIsDisabledWhenIncognitoModeForced) {
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
   // Initially, "New window" should be enabled.
-  std::unique_ptr<LauncherContextMenu> menu(
-      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT));
+  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
+      ash::TYPE_BROWSER_SHORTCUT, GetWmShelf(primary_id)));
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_WINDOW));
   EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_NEW_WINDOW));
@@ -131,7 +140,8 @@
   // Disable Incognito mode.
   IncognitoModePrefs::SetAvailability(profile()->GetPrefs(),
                                       IncognitoModePrefs::FORCED);
-  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT));
+  menu.reset(CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT,
+                                       GetWmShelf(primary_id)));
   ASSERT_TRUE(IsItemPresentInMenu(
       menu.get(), LauncherContextMenu::MENU_NEW_WINDOW));
   EXPECT_FALSE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_NEW_WINDOW));
@@ -139,8 +149,9 @@
 
 // Verifies status of contextmenu items for desktop shell.
 TEST_F(LauncherContextMenuTest, DesktopShellLauncherContextMenuItemCheck) {
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
   std::unique_ptr<LauncherContextMenu> menu(
-      CreateLauncherContextMenuForDesktopShell());
+      CreateLauncherContextMenuForDesktopShell(GetWmShelf(primary_id)));
   EXPECT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_OPEN_NEW));
   EXPECT_FALSE(IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_PIN));
@@ -163,8 +174,9 @@
 // opened.
 TEST_F(LauncherContextMenuTest,
        DesktopShellLauncherContextMenuVerifyCloseItem) {
-  std::unique_ptr<LauncherContextMenu> menu(
-      CreateLauncherContextMenu(ash::TYPE_BROWSER_SHORTCUT));
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  std::unique_ptr<LauncherContextMenu> menu(CreateLauncherContextMenu(
+      ash::TYPE_BROWSER_SHORTCUT, GetWmShelf(primary_id)));
   ASSERT_FALSE(
       IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_CLOSE));
 }
@@ -181,7 +193,8 @@
 
   ash::ShelfItem item;
   item.id = controller()->GetShelfIDForAppID(app_id);
-  ash::WmShelf* wm_shelf = GetWmShelf();
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  ash::WmShelf* wm_shelf = GetWmShelf(primary_id);
 
   std::unique_ptr<LauncherContextMenu> menu(
       new ArcLauncherContextMenu(controller(), &item, wm_shelf));
@@ -262,3 +275,31 @@
   EXPECT_TRUE(IsItemPresentInMenu(menu.get(), LauncherContextMenu::MENU_CLOSE));
   EXPECT_TRUE(menu->IsCommandIdEnabled(LauncherContextMenu::MENU_CLOSE));
 }
+
+// Tests that fullscreen which makes "Autohide shelf" option disappeared on
+// shelf is a per-display setting (crbug.com/496681).
+TEST_F(LauncherContextMenuTest, AutohideShelfOptionOnExternalDisplay) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  UpdateDisplay("940x550,940x550");
+  int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  int64_t secondary_id = display_manager()->GetSecondaryDisplay().id();
+
+  // Create a normal window on primary display.
+  views::Widget* widget = new views::Widget;
+  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
+  params.context = ash::Shell::GetPrimaryRootWindow();
+  widget->Init(params);
+  widget->Show();
+
+  widget->SetFullscreen(true);
+  std::unique_ptr<LauncherContextMenu> primary_menu(CreateLauncherContextMenu(
+      ash::TYPE_BROWSER_SHORTCUT, GetWmShelf(primary_id)));
+  std::unique_ptr<LauncherContextMenu> secondary_menu(CreateLauncherContextMenu(
+      ash::TYPE_BROWSER_SHORTCUT, GetWmShelf(secondary_id)));
+  EXPECT_FALSE(IsItemPresentInMenu(primary_menu.get(),
+                                   LauncherContextMenu::MENU_AUTO_HIDE));
+  EXPECT_TRUE(IsItemPresentInMenu(secondary_menu.get(),
+                                  LauncherContextMenu::MENU_AUTO_HIDE));
+}
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index b295cb2..69427e8 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/ash/system_tray_delegate_chromeos.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/singleton_tabs.h"
@@ -32,7 +33,6 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "extensions/browser/api/vpn_provider/vpn_service.h"
 #include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #include "net/base/escape.h"
@@ -62,16 +62,9 @@
 }  // namespace
 
 SystemTrayClient::SystemTrayClient() : binding_(this) {
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  // Connect to the SystemTray interface in ash. Under mash the SystemTray
-  // interface is in the ash process. In classic ash we provide it to ourself.
-  if (chrome::IsRunningInMash()) {
-    connector->ConnectToInterface("ash", &system_tray_);
-  } else {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &system_tray_);
-  }
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ConnectToInterface(ash_util::GetAshServiceName(), &system_tray_);
   // Register this object as the client interface implementation.
   system_tray_->SetClient(binding_.CreateInterfacePtrAndBind());
 
@@ -163,7 +156,7 @@
   int container_id = GetDialogParentContainerId();
   if (chrome::IsRunningInMash()) {
     using ui::mojom::WindowManager;
-    params.mus_properties[WindowManager::kInitialContainerId_Property] =
+    params.mus_properties[WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(container_id);
   } else {
     params.parent = ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
@@ -316,8 +309,17 @@
 }
 
 void SystemTrayClient::RequestRestartForUpdate() {
-  // We expect that UpdateEngine is in "Reboot for update" state now.
-  chrome::NotifyAndTerminate(true /* fast_path */);
+  bool component_update = false;
+  chromeos::SystemTrayDelegateChromeOS* tray =
+      chromeos::SystemTrayDelegateChromeOS::instance();
+  if (tray)
+    component_update = tray->GetFlashUpdateAvailable();
+
+  chrome::RebootPolicy reboot_policy =
+      component_update ? chrome::RebootPolicy::kForceReboot
+                       : chrome::RebootPolicy::kOptionalReboot;
+
+  chrome::NotifyAndTerminate(true /* fast_path */, reboot_policy);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 1a4022d..73f5c2e 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -109,6 +109,9 @@
 // The maximum session length limit that can be set.
 const int kSessionLengthLimitMaxMs = 24 * 60 * 60 * 1000;  // 24 hours.
 
+// A pointer so that callers can access the single class instance.
+SystemTrayDelegateChromeOS* g_instance = nullptr;
+
 void ExtractIMEInfo(const input_method::InputMethodDescriptor& ime,
                     const input_method::InputMethodUtil& util,
                     ash::IMEInfo* info) {
@@ -172,6 +175,14 @@
                  base::Unretained(this)));
 
   user_manager::UserManager::Get()->AddSessionStateObserver(this);
+
+  DCHECK(!g_instance);
+  g_instance = this;
+}
+
+// static
+SystemTrayDelegateChromeOS* SystemTrayDelegateChromeOS::instance() {
+  return g_instance;
 }
 
 void SystemTrayDelegateChromeOS::Initialize() {
@@ -221,6 +232,9 @@
 }
 
 SystemTrayDelegateChromeOS::~SystemTrayDelegateChromeOS() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+
   // Unregister PrefChangeRegistrars.
   local_state_registrar_.reset();
   user_pref_registrar_.reset();
@@ -314,6 +328,10 @@
 void SystemTrayDelegateChromeOS::GetSystemUpdateInfo(
     ash::UpdateInfo* info) const {
   GetUpdateInfo(UpgradeDetector::GetInstance(), info);
+  // If a flash component update is available, force the tray to show the user
+  // the Restart to Update dialog.
+  if (flash_update_available_)
+    info->update_required = true;
 }
 
 bool SystemTrayDelegateChromeOS::ShouldShowSettings() const {
@@ -606,6 +624,18 @@
     ash::WmShell::Get()->UpdateAfterLoginStatusChange(GetUserLoginStatus());
 }
 
+void SystemTrayDelegateChromeOS::SetFlashUpdateAvailable() {
+  flash_update_available_ = true;
+
+  ash::UpdateInfo info;
+  GetSystemUpdateInfo(&info);
+  GetSystemTrayNotifier()->NotifyUpdateRecommended(info);
+}
+
+bool SystemTrayDelegateChromeOS::GetFlashUpdateAvailable() {
+  return flash_update_available_;
+}
+
 ash::SystemTrayNotifier* SystemTrayDelegateChromeOS::GetSystemTrayNotifier() {
   return ash::WmShell::Get()->system_tray_notifier();
 }
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index db10ab44d..bb83941 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -70,6 +70,10 @@
 
   ~SystemTrayDelegateChromeOS() override;
 
+  // Access a global pointer to the single instance of the
+  // SystemTrayDelegateChromeOS class.
+  static SystemTrayDelegateChromeOS* instance();
+
   void InitializeOnAdapterReady(
       scoped_refptr<device::BluetoothAdapter> adapter);
 
@@ -123,6 +127,11 @@
 
   void UserChangedChildStatus(user_manager::User* user) override;
 
+  // This notifies the system that a flash update is now available, and so the
+  // user should reboot.
+  void SetFlashUpdateAvailable();
+  bool GetFlashUpdateAvailable();
+
  private:
   ash::SystemTrayNotifier* GetSystemTrayNotifier();
 
@@ -235,6 +244,7 @@
   std::string enterprise_realm_;
   bool should_run_bluetooth_discovery_ = false;
   bool session_started_ = false;
+  bool flash_update_available_ = false;
 
   scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
   std::unique_ptr<device::BluetoothDiscoverySession>
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc
index b061e67..8d3752fd 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc
@@ -13,6 +13,8 @@
 #include "ash/common/system/date/tray_date.h"
 #include "ash/common/system/date/tray_system_info.h"
 #include "ash/common/system/tray/system_tray.h"
+#include "ash/common/system/update/tray_update.h"
+#include "ash/common/wm_shell.h"
 #include "ash/shell.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/login_manager_test.h"
@@ -129,4 +131,29 @@
   EXPECT_EQ(base::k24HourClock, GetHourType());
 }
 
+// Test that a flash update causes the update UI to show in the system menu.
+IN_PROC_BROWSER_TEST_F(SystemTrayDelegateChromeOSTest,
+                       TestFlashUpdateTrayIcon) {
+  ash::TrayUpdate* tray_update = ash::Shell::GetInstance()
+                                     ->GetPrimarySystemTray()
+                                     ->GetTrayUpdateForTesting();
+
+  ash::UpdateInfo initial_info;
+  ash::WmShell::Get()->system_tray_delegate()->GetSystemUpdateInfo(
+      &initial_info);
+  EXPECT_FALSE(initial_info.update_required);
+
+  // When no update is pending, the item isn't visible.
+  EXPECT_FALSE(tray_update->tray_view()->visible());
+
+  chromeos::SystemTrayDelegateChromeOS::instance()->SetFlashUpdateAvailable();
+
+  ash::UpdateInfo post_info;
+  ash::WmShell::Get()->system_tray_delegate()->GetSystemUpdateInfo(&post_info);
+  EXPECT_TRUE(post_info.update_required);
+
+  // Tray item is now visible.
+  EXPECT_TRUE(tray_update->tray_view()->visible());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/ash/volume_controller.cc b/chrome/browser/ui/ash/volume_controller.cc
new file mode 100644
index 0000000..3447be83
--- /dev/null
+++ b/chrome/browser/ui/ash/volume_controller.cc
@@ -0,0 +1,93 @@
+// 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/volume_controller.h"
+
+#include "ash/public/interfaces/accelerator_controller.mojom.h"
+#include "base/command_line.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/grit/browser_resources.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/common/service_manager_connection.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+// Percent by which the volume should be changed when a volume key is pressed.
+const double kStepPercentage = 4.0;
+
+bool VolumeAdjustSoundEnabled() {
+  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kDisableVolumeAdjustSound);
+}
+
+void PlayVolumeAdjustSound() {
+  if (VolumeAdjustSoundEnabled()) {
+    chromeos::AccessibilityManager::Get()->PlayEarcon(
+        chromeos::SOUND_VOLUME_ADJUST,
+        chromeos::PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+  }
+}
+
+}  // namespace
+
+VolumeController::VolumeController() : binding_(this) {
+  // Connect to the accelerator controller interface in the ash service.
+  service_manager::Connector* connector =
+      content::ServiceManagerConnection::GetForProcess()->GetConnector();
+  ash::mojom::AcceleratorControllerPtr accelerator_controller_ptr;
+  connector->ConnectToInterface(ash_util::GetAshServiceName(),
+                                &accelerator_controller_ptr);
+
+  // Register this object as the volume controller.
+  accelerator_controller_ptr->SetVolumeController(
+      binding_.CreateInterfacePtrAndBind());
+
+  if (VolumeAdjustSoundEnabled()) {
+    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+    media::SoundsManager::Get()->Initialize(
+        chromeos::SOUND_VOLUME_ADJUST,
+        bundle.GetRawDataResource(IDR_SOUND_VOLUME_ADJUST_WAV));
+  }
+}
+
+VolumeController::~VolumeController() {}
+
+void VolumeController::VolumeMute() {
+  chromeos::CrasAudioHandler::Get()->SetOutputMute(true);
+}
+
+void VolumeController::VolumeDown() {
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+  if (audio_handler->IsOutputMuted()) {
+    audio_handler->SetOutputVolumePercent(0);
+  } else {
+    audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
+    if (audio_handler->IsOutputVolumeBelowDefaultMuteLevel())
+      audio_handler->SetOutputMute(true);
+    else
+      PlayVolumeAdjustSound();
+  }
+}
+
+void VolumeController::VolumeUp() {
+  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+  bool play_sound = false;
+  if (audio_handler->IsOutputMuted()) {
+    audio_handler->SetOutputMute(false);
+    audio_handler->AdjustOutputVolumeToAudibleLevel();
+    play_sound = true;
+  } else {
+    play_sound = audio_handler->GetOutputVolumePercent() != 100;
+    audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
+  }
+
+  if (play_sound)
+    PlayVolumeAdjustSound();
+}
diff --git a/chrome/browser/ui/ash/volume_controller.h b/chrome/browser/ui/ash/volume_controller.h
new file mode 100644
index 0000000..a3ca76eccb
--- /dev/null
+++ b/chrome/browser/ui/ash/volume_controller.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_H_
+
+#include "ash/public/interfaces/volume.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+// Controls the volume when F8-10 or a multimedia key for volume is pressed.
+// TODO(crbug.com/647781): Media accelerators like F8-F10 are broken in mash.
+// Fix this when mash supports event rewriting.
+class VolumeController : public ash::mojom::VolumeController {
+ public:
+  VolumeController();
+  ~VolumeController() override;
+
+  // Overridden from ash::mojom::VolumeClient:
+  void VolumeMute() override;
+  void VolumeDown() override;
+  void VolumeUp() override;
+
+ private:
+  mojo::Binding<ash::mojom::VolumeController> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(VolumeController);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/volume_controller_browsertest.cc b/chrome/browser/ui/ash/volume_controller_browsertest.cc
new file mode 100644
index 0000000..06db9c4
--- /dev/null
+++ b/chrome/browser/ui/ash/volume_controller_browsertest.cc
@@ -0,0 +1,247 @@
+// 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 <memory>
+#include <vector>
+
+#include "ash/common/accessibility_delegate.h"
+#include "ash/common/accessibility_types.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/ui/ash/volume_controller.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/chromeos_switches.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace {
+
+class SoundsManagerTestImpl : public media::SoundsManager {
+ public:
+  SoundsManagerTestImpl()
+      : is_sound_initialized_(chromeos::SOUND_COUNT),
+        num_play_requests_(chromeos::SOUND_COUNT) {}
+
+  ~SoundsManagerTestImpl() override {}
+
+  bool Initialize(SoundKey key, const base::StringPiece& /* data */) override {
+    is_sound_initialized_[key] = true;
+    return true;
+  }
+
+  bool Play(SoundKey key) override {
+    ++num_play_requests_[key];
+    return true;
+  }
+
+  bool Stop(SoundKey key) override { return true; }
+
+  base::TimeDelta GetDuration(SoundKey /* key */) override {
+    return base::TimeDelta();
+  }
+
+  bool is_sound_initialized(SoundKey key) const {
+    return is_sound_initialized_[key];
+  }
+
+  int num_play_requests(SoundKey key) const { return num_play_requests_[key]; }
+
+ private:
+  std::vector<bool> is_sound_initialized_;
+  std::vector<int> num_play_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(SoundsManagerTestImpl);
+};
+
+class VolumeControllerTest : public InProcessBrowserTest {
+ public:
+  VolumeControllerTest() {}
+  ~VolumeControllerTest() override {}
+
+  void SetUpOnMainThread() override {
+    volume_controller_.reset(new VolumeController());
+    audio_handler_ = chromeos::CrasAudioHandler::Get();
+  }
+
+ protected:
+  chromeos::CrasAudioHandler* audio_handler_;  // Not owned.
+  std::unique_ptr<VolumeController> volume_controller_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VolumeControllerTest);
+};
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeUpAndDown) {
+  // Set initial value as 50%
+  const int kInitVolume = 50;
+  audio_handler_->SetOutputVolumePercent(kInitVolume);
+
+  EXPECT_EQ(audio_handler_->GetOutputVolumePercent(), kInitVolume);
+
+  volume_controller_->VolumeUp();
+  EXPECT_LT(kInitVolume, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(kInitVolume, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeDown();
+  EXPECT_GT(kInitVolume, audio_handler_->GetOutputVolumePercent());
+}
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeDownToZero) {
+  // Setting to very small volume.
+  audio_handler_->SetOutputVolumePercent(1);
+
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeUp();
+  EXPECT_LT(0, audio_handler_->GetOutputVolumePercent());
+}
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeUpTo100) {
+  // Setting to almost max
+  audio_handler_->SetOutputVolumePercent(99);
+
+  volume_controller_->VolumeUp();
+  EXPECT_EQ(100, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeUp();
+  EXPECT_EQ(100, audio_handler_->GetOutputVolumePercent());
+  volume_controller_->VolumeDown();
+  EXPECT_GT(100, audio_handler_->GetOutputVolumePercent());
+}
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerTest, Mutes) {
+  ASSERT_FALSE(audio_handler_->IsOutputMuted());
+  const int initial_volume = audio_handler_->GetOutputVolumePercent();
+
+  volume_controller_->VolumeMute();
+  EXPECT_TRUE(audio_handler_->IsOutputMuted());
+
+  // Further mute buttons doesn't have effects.
+  volume_controller_->VolumeMute();
+  EXPECT_TRUE(audio_handler_->IsOutputMuted());
+
+  // Right after the volume up after set_mute recovers to original volume.
+  volume_controller_->VolumeUp();
+  EXPECT_FALSE(audio_handler_->IsOutputMuted());
+  EXPECT_EQ(initial_volume, audio_handler_->GetOutputVolumePercent());
+
+  volume_controller_->VolumeMute();
+  // After the volume down, the volume goes down to zero explicitly.
+  volume_controller_->VolumeDown();
+  EXPECT_TRUE(audio_handler_->IsOutputMuted());
+  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
+
+  // Thus, further VolumeUp doesn't recover the volume, it's just slightly
+  // bigger than 0.
+  volume_controller_->VolumeUp();
+  EXPECT_LT(0, audio_handler_->GetOutputVolumePercent());
+  EXPECT_GT(initial_volume, audio_handler_->GetOutputVolumePercent());
+}
+
+class VolumeControllerSoundsTest : public VolumeControllerTest {
+ public:
+  VolumeControllerSoundsTest() : sounds_manager_(NULL) {}
+  ~VolumeControllerSoundsTest() override {}
+
+  void SetUpInProcessBrowserTestFixture() override {
+    sounds_manager_ = new SoundsManagerTestImpl();
+    media::SoundsManager::InitializeForTesting(sounds_manager_);
+  }
+
+  void EnableSpokenFeedback(bool enabled) {
+    chromeos::AccessibilityManager* manager =
+        chromeos::AccessibilityManager::Get();
+    manager->EnableSpokenFeedback(enabled, ash::A11Y_NOTIFICATION_NONE);
+  }
+
+  bool is_sound_initialized() const {
+    return sounds_manager_->is_sound_initialized(chromeos::SOUND_VOLUME_ADJUST);
+  }
+
+  int num_play_requests() const {
+    return sounds_manager_->num_play_requests(chromeos::SOUND_VOLUME_ADJUST);
+  }
+
+ private:
+  SoundsManagerTestImpl* sounds_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(VolumeControllerSoundsTest);
+};
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsTest, Simple) {
+  audio_handler_->SetOutputVolumePercent(50);
+
+  EnableSpokenFeedback(false /* enabled */);
+  volume_controller_->VolumeUp();
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(0, num_play_requests());
+
+  EnableSpokenFeedback(true /* enabled */);
+  volume_controller_->VolumeUp();
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(2, num_play_requests());
+}
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsTest, EdgeCases) {
+  EXPECT_TRUE(is_sound_initialized());
+  EnableSpokenFeedback(true /* enabled */);
+
+  // Check that sound is played on volume up and volume down.
+  audio_handler_->SetOutputVolumePercent(50);
+  volume_controller_->VolumeUp();
+  EXPECT_EQ(1, num_play_requests());
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(2, num_play_requests());
+
+  audio_handler_->SetOutputVolumePercent(99);
+  volume_controller_->VolumeUp();
+  EXPECT_EQ(3, num_play_requests());
+
+  audio_handler_->SetOutputVolumePercent(100);
+  volume_controller_->VolumeUp();
+  EXPECT_EQ(3, num_play_requests());
+
+  // Check that sound isn't played when audio is muted.
+  audio_handler_->SetOutputVolumePercent(50);
+  volume_controller_->VolumeMute();
+  volume_controller_->VolumeDown();
+  ASSERT_TRUE(audio_handler_->IsOutputMuted());
+  EXPECT_EQ(3, num_play_requests());
+
+  // Check that audio is unmuted and sound is played.
+  volume_controller_->VolumeUp();
+  ASSERT_FALSE(audio_handler_->IsOutputMuted());
+  EXPECT_EQ(4, num_play_requests());
+}
+
+class VolumeControllerSoundsDisabledTest : public VolumeControllerSoundsTest {
+ public:
+  VolumeControllerSoundsDisabledTest() {}
+  ~VolumeControllerSoundsDisabledTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    VolumeControllerSoundsTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(chromeos::switches::kDisableVolumeAdjustSound);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VolumeControllerSoundsDisabledTest);
+};
+
+IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsDisabledTest, VolumeAdjustSounds) {
+  EXPECT_FALSE(is_sound_initialized());
+
+  // Check that sound isn't played on volume up and volume down.
+  audio_handler_->SetOutputVolumePercent(50);
+  volume_controller_->VolumeUp();
+  volume_controller_->VolumeDown();
+  EXPECT_EQ(0, num_play_requests());
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc b/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc
deleted file mode 100644
index 624cac9..0000000
--- a/chrome/browser/ui/ash/volume_controller_browsertest_chromeos.cc
+++ /dev/null
@@ -1,253 +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 <memory>
-#include <vector>
-
-#include "ash/common/accessibility_delegate.h"
-#include "ash/common/accessibility_types.h"
-#include "base/command_line.h"
-#include "base/macros.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
-#include "chrome/browser/ui/ash/volume_controller_chromeos.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/audio/chromeos_sounds.h"
-#include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/chromeos_switches.h"
-#include "media/audio/sounds/sounds_manager.h"
-#include "ui/base/accelerators/accelerator.h"
-
-namespace {
-
-class SoundsManagerTestImpl : public media::SoundsManager {
- public:
-  SoundsManagerTestImpl()
-    : is_sound_initialized_(chromeos::SOUND_COUNT),
-      num_play_requests_(chromeos::SOUND_COUNT) {
-  }
-
-  ~SoundsManagerTestImpl() override {}
-
-  bool Initialize(SoundKey key, const base::StringPiece& /* data */) override {
-    is_sound_initialized_[key] = true;
-    return true;
-  }
-
-  bool Play(SoundKey key) override {
-    ++num_play_requests_[key];
-    return true;
-  }
-
-  bool Stop(SoundKey key) override {
-    return true;
-  }
-
-  base::TimeDelta GetDuration(SoundKey /* key */) override {
-    return base::TimeDelta();
-  }
-
-  bool is_sound_initialized(SoundKey key) const {
-    return is_sound_initialized_[key];
-  }
-
-  int num_play_requests(SoundKey key) const {
-    return num_play_requests_[key];
-  }
-
- private:
-  std::vector<bool> is_sound_initialized_;
-  std::vector<int> num_play_requests_;
-
-  DISALLOW_COPY_AND_ASSIGN(SoundsManagerTestImpl);
-};
-
-class VolumeControllerTest : public InProcessBrowserTest {
- public:
-  VolumeControllerTest() {}
-  ~VolumeControllerTest() override {}
-
-  void SetUpOnMainThread() override {
-    volume_controller_.reset(new VolumeController());
-    audio_handler_ = chromeos::CrasAudioHandler::Get();
-  }
-
- protected:
-  chromeos::CrasAudioHandler* audio_handler_;  // Not owned.
-  std::unique_ptr<VolumeController> volume_controller_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(VolumeControllerTest);
-};
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeUpAndDown) {
-  // Set initial value as 50%
-  const int kInitVolume = 50;
-  audio_handler_->SetOutputVolumePercent(kInitVolume);
-
-  EXPECT_EQ(audio_handler_->GetOutputVolumePercent(), kInitVolume);
-
-  volume_controller_->VolumeUp();
-  EXPECT_LT(kInitVolume, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(kInitVolume, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeDown();
-  EXPECT_GT(kInitVolume, audio_handler_->GetOutputVolumePercent());
-}
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeDownToZero) {
-  // Setting to very small volume.
-  audio_handler_->SetOutputVolumePercent(1);
-
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeUp();
-  EXPECT_LT(0, audio_handler_->GetOutputVolumePercent());
-}
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerTest, VolumeUpTo100) {
-  // Setting to almost max
-  audio_handler_->SetOutputVolumePercent(99);
-
-  volume_controller_->VolumeUp();
-  EXPECT_EQ(100, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeUp();
-  EXPECT_EQ(100, audio_handler_->GetOutputVolumePercent());
-  volume_controller_->VolumeDown();
-  EXPECT_GT(100, audio_handler_->GetOutputVolumePercent());
-}
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerTest, Mutes) {
-  ASSERT_FALSE(audio_handler_->IsOutputMuted());
-  const int initial_volume = audio_handler_->GetOutputVolumePercent();
-
-  volume_controller_->VolumeMute();
-  EXPECT_TRUE(audio_handler_->IsOutputMuted());
-
-  // Further mute buttons doesn't have effects.
-  volume_controller_->VolumeMute();
-  EXPECT_TRUE(audio_handler_->IsOutputMuted());
-
-  // Right after the volume up after set_mute recovers to original volume.
-  volume_controller_->VolumeUp();
-  EXPECT_FALSE(audio_handler_->IsOutputMuted());
-  EXPECT_EQ(initial_volume, audio_handler_->GetOutputVolumePercent());
-
-  volume_controller_->VolumeMute();
-  // After the volume down, the volume goes down to zero explicitly.
-  volume_controller_->VolumeDown();
-  EXPECT_TRUE(audio_handler_->IsOutputMuted());
-  EXPECT_EQ(0, audio_handler_->GetOutputVolumePercent());
-
-  // Thus, further VolumeUp doesn't recover the volume, it's just slightly
-  // bigger than 0.
-  volume_controller_->VolumeUp();
-  EXPECT_LT(0, audio_handler_->GetOutputVolumePercent());
-  EXPECT_GT(initial_volume, audio_handler_->GetOutputVolumePercent());
-}
-
-class VolumeControllerSoundsTest : public VolumeControllerTest {
- public:
-  VolumeControllerSoundsTest() : sounds_manager_(NULL) {}
-  ~VolumeControllerSoundsTest() override {}
-
-  void SetUpInProcessBrowserTestFixture() override {
-    sounds_manager_ = new SoundsManagerTestImpl();
-    media::SoundsManager::InitializeForTesting(sounds_manager_);
-  }
-
-  void EnableSpokenFeedback(bool enabled) {
-    chromeos::AccessibilityManager* manager =
-        chromeos::AccessibilityManager::Get();
-    manager->EnableSpokenFeedback(enabled, ash::A11Y_NOTIFICATION_NONE);
-  }
-
-  bool is_sound_initialized() const {
-    return sounds_manager_->is_sound_initialized(chromeos::SOUND_VOLUME_ADJUST);
-  }
-
-  int num_play_requests() const {
-    return sounds_manager_->num_play_requests(chromeos::SOUND_VOLUME_ADJUST);
-  }
-
- private:
-  SoundsManagerTestImpl* sounds_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(VolumeControllerSoundsTest);
-};
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsTest, Simple) {
-  audio_handler_->SetOutputVolumePercent(50);
-
-  EnableSpokenFeedback(false /* enabled */);
-  volume_controller_->VolumeUp();
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(0, num_play_requests());
-
-  EnableSpokenFeedback(true /* enabled */);
-  volume_controller_->VolumeUp();
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(2, num_play_requests());
-}
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsTest, EdgeCases) {
-  EXPECT_TRUE(is_sound_initialized());
-  EnableSpokenFeedback(true /* enabled */);
-
-  // Check that sound is played on volume up and volume down.
-  audio_handler_->SetOutputVolumePercent(50);
-  volume_controller_->VolumeUp();
-  EXPECT_EQ(1, num_play_requests());
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(2, num_play_requests());
-
-  audio_handler_->SetOutputVolumePercent(99);
-  volume_controller_->VolumeUp();
-  EXPECT_EQ(3, num_play_requests());
-
-  audio_handler_->SetOutputVolumePercent(100);
-  volume_controller_->VolumeUp();
-  EXPECT_EQ(3, num_play_requests());
-
-  // Check that sound isn't played when audio is muted.
-  audio_handler_->SetOutputVolumePercent(50);
-  volume_controller_->VolumeMute();
-  volume_controller_->VolumeDown();
-  ASSERT_TRUE(audio_handler_->IsOutputMuted());
-  EXPECT_EQ(3, num_play_requests());
-
-  // Check that audio is unmuted and sound is played.
-  volume_controller_->VolumeUp();
-  ASSERT_FALSE(audio_handler_->IsOutputMuted());
-  EXPECT_EQ(4, num_play_requests());
-}
-
-class VolumeControllerSoundsDisabledTest : public VolumeControllerSoundsTest {
- public:
-  VolumeControllerSoundsDisabledTest() {}
-  ~VolumeControllerSoundsDisabledTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    VolumeControllerSoundsTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kDisableVolumeAdjustSound);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(VolumeControllerSoundsDisabledTest);
-};
-
-IN_PROC_BROWSER_TEST_F(VolumeControllerSoundsDisabledTest,
-                       VolumeAdjustSounds) {
-  EXPECT_FALSE(is_sound_initialized());
-
-  // Check that sound isn't played on volume up and volume down.
-  audio_handler_->SetOutputVolumePercent(50);
-  volume_controller_->VolumeUp();
-  volume_controller_->VolumeDown();
-  EXPECT_EQ(0, num_play_requests());
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.cc b/chrome/browser/ui/ash/volume_controller_chromeos.cc
deleted file mode 100644
index cc336c3..0000000
--- a/chrome/browser/ui/ash/volume_controller_chromeos.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 "chrome/browser/ui/ash/volume_controller_chromeos.h"
-
-#include "base/command_line.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
-#include "chrome/grit/browser_resources.h"
-#include "chromeos/audio/chromeos_sounds.h"
-#include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/chromeos_switches.h"
-#include "media/audio/sounds/sounds_manager.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace {
-
-// Percent by which the volume should be changed when a volume key is pressed.
-const double kStepPercentage = 4.0;
-
-bool VolumeAdjustSoundEnabled() {
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kDisableVolumeAdjustSound);
-}
-
-void PlayVolumeAdjustSound() {
-  if (VolumeAdjustSoundEnabled()) {
-    chromeos::AccessibilityManager::Get()->PlayEarcon(
-        chromeos::SOUND_VOLUME_ADJUST,
-        chromeos::PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
-  }
-}
-
-}  // namespace
-
-VolumeController::VolumeController() {
-  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  if (VolumeAdjustSoundEnabled()) {
-    media::SoundsManager::Get()->Initialize(
-        chromeos::SOUND_VOLUME_ADJUST,
-        bundle.GetRawDataResource(IDR_SOUND_VOLUME_ADJUST_WAV));
-  }
-}
-
-VolumeController::~VolumeController() {}
-
-void VolumeController::BindRequest(
-    ash::mojom::VolumeControllerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
-}
-
-void VolumeController::VolumeMute() {
-  chromeos::CrasAudioHandler::Get()->SetOutputMute(true);
-}
-
-void VolumeController::VolumeDown() {
-  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
-  if (audio_handler->IsOutputMuted()) {
-    audio_handler->SetOutputVolumePercent(0);
-  } else {
-    audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
-    if (audio_handler->IsOutputVolumeBelowDefaultMuteLevel())
-      audio_handler->SetOutputMute(true);
-    else
-      PlayVolumeAdjustSound();
-  }
-}
-
-void VolumeController::VolumeUp() {
-  chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
-  bool play_sound = false;
-  if (audio_handler->IsOutputMuted()) {
-    audio_handler->SetOutputMute(false);
-    audio_handler->AdjustOutputVolumeToAudibleLevel();
-    play_sound = true;
-  } else {
-    play_sound = audio_handler->GetOutputVolumePercent() != 100;
-    audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
-  }
-
-  if (play_sound)
-    PlayVolumeAdjustSound();
-}
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.h b/chrome/browser/ui/ash/volume_controller_chromeos.h
deleted file mode 100644
index 532b3ba..0000000
--- a/chrome/browser/ui/ash/volume_controller_chromeos.h
+++ /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.
-
-#ifndef CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_CHROMEOS_H_
-#define CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_CHROMEOS_H_
-
-#include "ash/public/interfaces/volume.mojom.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-
-// Controls the volume when F8-10 or a multimedia key for volume is pressed.
-class VolumeController : public ash::mojom::VolumeController {
- public:
-  VolumeController();
-  ~VolumeController() override;
-
-  // Binds the mojom::VolumeController interface request to this object.
-  void BindRequest(ash::mojom::VolumeControllerRequest request);
-
-  // Overridden from ash::mojom::VolumeController:
-  void VolumeMute() override;
-  void VolumeDown() override;
-  void VolumeUp() override;
-
- private:
-  mojo::BindingSet<ash::mojom::VolumeController> bindings_;
-
-  DISALLOW_COPY_AND_ASSIGN(VolumeController);
-};
-
-#endif  // CHROME_BROWSER_UI_ASH_VOLUME_CONTROLLER_CHROMEOS_H_
diff --git a/chrome/browser/ui/ash/vpn_list_forwarder.cc b/chrome/browser/ui/ash/vpn_list_forwarder.cc
index abf0e41..e3696881 100644
--- a/chrome/browser/ui/ash/vpn_list_forwarder.cc
+++ b/chrome/browser/ui/ash/vpn_list_forwarder.cc
@@ -44,14 +44,9 @@
 // Connects to the VpnList mojo interface in ash.
 ash::mojom::VpnListPtr ConnectToVpnList() {
   ash::mojom::VpnListPtr vpn_list;
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  // Under mash the VpnList interface is in the ash process. In classic ash
-  // we provide it to ourself.
-  if (chrome::IsRunningInMash())
-    connector->ConnectToInterface("ash", &vpn_list);
-  else
-    connector->ConnectToInterface("content_browser", &vpn_list);
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ConnectToInterface(ash_util::GetAshServiceName(), &vpn_list);
   return vpn_list;
 }
 
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 3177d67..773ee6b 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -156,7 +156,7 @@
   return identity_provider_.get();
 }
 
-rappor::RapporService* ChromeAutofillClient::GetRapporService() {
+rappor::RapporServiceImpl* ChromeAutofillClient::GetRapporServiceImpl() {
   return g_browser_process->rappor_service();
 }
 
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 3bc45f4..8df0fa68 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -43,7 +43,7 @@
   PrefService* GetPrefs() override;
   syncer::SyncService* GetSyncService() override;
   IdentityProvider* GetIdentityProvider() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   void ShowAutofillSettings() override;
   void ShowUnmaskPrompt(const CreditCard& card,
                         UnmaskCardReason reason,
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 10dc1ff..a3a02dbc 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -48,6 +48,10 @@
 class URLRequest;
 }
 
+namespace payments {
+class PaymentRequestImpl;
+}
+
 namespace security_state {
 struct SecurityInfo;
 }  // namespace security_state
@@ -146,6 +150,8 @@
                              const BookmarkEditor::EditDetails& details,
                              BookmarkEditor::Configuration configuration);
 
+void ShowPaymentRequestDialog(payments::PaymentRequestImpl* impl);
+
 #if defined(OS_MACOSX)
 
 // This is a class so that it can be friended from ContentSettingBubbleContents,
diff --git a/chrome/browser/ui/cocoa/applescript/apple_event_util.mm b/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
index f798950..566bd4c 100644
--- a/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
+++ b/chrome/browser/ui/cocoa/applescript/apple_event_util.mm
@@ -18,26 +18,26 @@
   NSAppleEventDescriptor* descriptor = nil;
 
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       descriptor = [NSAppleEventDescriptor
           descriptorWithTypeCode:cMissingValue];
       break;
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value;
       value->GetAsBoolean(&bool_value);
       descriptor = [NSAppleEventDescriptor descriptorWithBoolean:bool_value];
       break;
     }
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value;
       value->GetAsInteger(&int_value);
       descriptor = [NSAppleEventDescriptor descriptorWithInt32:int_value];
       break;
     }
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       value->GetAsDouble(&double_value);
       descriptor = [NSAppleEventDescriptor
@@ -47,7 +47,7 @@
       break;
     }
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string string_value;
       value->GetAsString(&string_value);
       descriptor = [NSAppleEventDescriptor descriptorWithString:
@@ -55,11 +55,11 @@
       break;
     }
 
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       NOTREACHED();
       break;
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dictionary_value;
       value->GetAsDictionary(&dictionary_value);
       descriptor = [NSAppleEventDescriptor recordDescriptor];
@@ -80,7 +80,7 @@
       break;
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list_value;
       value->GetAsList(&list_value);
       descriptor = [NSAppleEventDescriptor listDescriptor];
diff --git a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
index 541afd38..16e764eb 100644
--- a/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
+++ b/chrome/browser/ui/cocoa/autofill/card_unmask_prompt_view_bridge.mm
@@ -29,7 +29,7 @@
 #include "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/native_theme/native_theme_mac.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace {
 
@@ -653,7 +653,7 @@
 
   progressOverlayLabel_.reset([constrained_window::CreateLabel() retain]);
   NSColor* throbberBlueColor = skia::SkColorToCalibratedNSColor(
-      ui::NativeThemeMac::instance()->GetSystemColor(
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
           ui::NativeTheme::kColorId_ThrobberSpinningColor));
   [progressOverlayLabel_ setTextColor:throbberBlueColor];
   [progressOverlayView_ addSubview:progressOverlayLabel_];
diff --git a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
index 549348f..a502d8a 100644
--- a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
@@ -19,7 +19,6 @@
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_mac.h"
 
 @interface ZoomBubbleController (Private)
 - (void)performLayout;
@@ -93,7 +92,7 @@
     [window setInfoBubbleCanBecomeKeyWindow:NO];
     delegate_ = delegate;
 
-    ui::NativeTheme* nativeTheme = ui::NativeThemeMac::instance();
+    ui::NativeTheme* nativeTheme = ui::NativeTheme::GetInstanceForNativeUi();
     [[self bubble] setAlignment:info_bubble::kAlignTrailingEdgeToAnchorEdge];
     [[self bubble] setArrowLocation:info_bubble::kNoArrow];
     [[self bubble] setBackgroundColor:
@@ -232,7 +231,7 @@
   base::scoped_nsobject<NSBox> separatorView(
       [[NSBox alloc] initWithFrame:rect]);
   [separatorView setBoxType:NSBoxCustom];
-  ui::NativeTheme* nativeTheme = ui::NativeThemeMac::instance();
+  ui::NativeTheme* nativeTheme = ui::NativeTheme::GetInstanceForNativeUi();
   [separatorView setBorderColor:
       skia::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
           ui::NativeTheme::kColorId_MenuSeparatorColor))];
@@ -336,7 +335,7 @@
   NSRect bounds = [self bounds];
   NSAttributedString* title = [self attributedTitle];
   if ([self hoverState] != kHoverStateNone) {
-    ui::NativeTheme* nativeTheme = ui::NativeThemeMac::instance();
+    ui::NativeTheme* nativeTheme = ui::NativeTheme::GetInstanceForNativeUi();
     [skia::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
         ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor)) set];
     NSRectFillUsingOperation(bounds, NSCompositeSourceOver);
diff --git a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
index f5f0887..4099c11 100644
--- a/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
+++ b/chrome/browser/ui/cocoa/extensions/toolbar_actions_bar_bubble_mac.mm
@@ -22,7 +22,6 @@
 #include "ui/gfx/image/image_skia_util_mac.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_mac.h"
 
 namespace {
 BOOL g_animations_enabled = false;
@@ -86,7 +85,7 @@
     anchoredToAction_ = anchoredToAction;
     delegate_ = std::move(delegate);
 
-    ui::NativeTheme* nativeTheme = ui::NativeThemeMac::instance();
+    ui::NativeTheme* nativeTheme = ui::NativeTheme::GetInstanceForNativeUi();
     [[self bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
     [[self bubble] setArrowLocation:info_bubble::kTopRight];
     [[self bubble] setBackgroundColor:
diff --git a/chrome/browser/ui/cocoa/location_bar/OWNERS b/chrome/browser/ui/cocoa/location_bar/OWNERS
deleted file mode 100644
index 37b85af5..0000000
--- a/chrome/browser/ui/cocoa/location_bar/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-isherman@chromium.org
-rohitrao@chromium.org
-shess@chromium.org
diff --git a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.h b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.h
index e26d04b..23d0a0e 100644
--- a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.h
@@ -16,6 +16,7 @@
 // of view when a page attempts to show a popup and the popup blocker is on.
 
 @class ContentSettingAnimationState;
+@class ContentSettingBubbleController;
 class ContentSettingImageModel;
 class LocationBarViewMac;
 class Profile;
@@ -70,6 +71,9 @@
   CGFloat text_width_;
   base::scoped_nsobject<NSAttributedString> animated_text_;
 
+  // The window of the content setting bubble.
+  base::scoped_nsobject<NSWindow> bubbleWindow_;
+
   DISALLOW_COPY_AND_ASSIGN(ContentSettingDecoration);
 };
 
diff --git a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
index eb44c5e..40c69a4 100644
--- a/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/content_setting_decoration.mm
@@ -309,11 +309,19 @@
         [web_contents->GetTopLevelNativeWindow() contentView],
         model, web_contents, origin);
   } else {
-    [ContentSettingBubbleController showForModel:model
-                                     webContents:web_contents
-                                    parentWindow:[field window]
-                                      decoration:this
-                                      anchoredAt:anchor];
+    // If the bubble is already opened, close it. Otherwise, open a new bubble.
+    if (bubbleWindow_ && [bubbleWindow_ isVisible]) {
+      [bubbleWindow_ close];
+      bubbleWindow_.reset();
+    } else {
+      ContentSettingBubbleController* bubbleController =
+          [ContentSettingBubbleController showForModel:model
+                                           webContents:web_contents
+                                          parentWindow:[field window]
+                                            decoration:this
+                                            anchoredAt:anchor];
+      bubbleWindow_.reset([[bubbleController window] retain]);
+    }
   }
 
   return true;
diff --git a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
index c6f5b449..26d0e39 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_chooser_controller.mm
@@ -88,7 +88,6 @@
 #include "ui/gfx/vector_icons_public.h"
 #include "ui/native_theme/common_theme.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_mac.h"
 
 namespace {
 
@@ -222,7 +221,7 @@
 // Returns the native dialog background color.
 NSColor* GetDialogBackgroundColor() {
   return skia::SkColorToCalibratedNSColor(
-      ui::NativeThemeMac::instance()->GetSystemColor(
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
           ui::NativeTheme::kColorId_DialogBackground));
 }
 
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
index 78da7f10..21c4a85 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
@@ -29,7 +29,7 @@
 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/native_theme/native_theme_mac.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace {
 
@@ -249,13 +249,13 @@
   [promptBox_
       setBorderColor:skia::SkColorToCalibratedNSColor(
                          ui::GetSigninConfirmationPromptBarColor(
-                             ui::NativeThemeMac::instance(),
+                             ui::NativeTheme::GetInstanceForNativeUi(),
                              ui::kSigninConfirmationPromptBarBorderAlpha))];
   [promptBox_ setBorderWidth:kDialogAlertBarBorderWidth];
   [promptBox_
       setFillColor:skia::SkColorToCalibratedNSColor(
                        ui::GetSigninConfirmationPromptBarColor(
-                           ui::NativeThemeMac::instance(),
+                           ui::NativeTheme::GetInstanceForNativeUi(),
                            ui::kSigninConfirmationPromptBarBackgroundAlpha))];
   [promptBox_ setBoxType:NSBoxCustom];
   [promptBox_ setTitlePosition:NSNoTitle];
diff --git a/chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.mm b/chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.mm
index a8cb7ec..a8521c4 100644
--- a/chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.mm
+++ b/chrome/browser/ui/cocoa/screen_capture_notification_ui_cocoa.mm
@@ -25,7 +25,6 @@
 #include "ui/gfx/mac/nswindow_frame_controls.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_mac.h"
 
 const CGFloat kMinimumWidth = 460;
 const CGFloat kMaximumWidth = 1000;
@@ -238,8 +237,9 @@
 @implementation ScreenCaptureNotificationView
 
 - (void)drawRect:(NSRect)dirtyRect {
-  [skia::SkColorToSRGBNSColor(ui::NativeThemeMac::instance()->GetSystemColor(
-      ui::NativeTheme::kColorId_DialogBackground)) set];
+  [skia::SkColorToSRGBNSColor(
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+          ui::NativeTheme::kColorId_DialogBackground)) set];
   [[NSBezierPath bezierPathWithRoundedRect:[self bounds]
                                    xRadius:kWindowCornerRadius
                                    yRadius:kWindowCornerRadius] fill];
diff --git a/chrome/browser/ui/cocoa/spinner_view.mm b/chrome/browser/ui/cocoa/spinner_view.mm
index 44eaa93..afc314b 100644
--- a/chrome/browser/ui/cocoa/spinner_view.mm
+++ b/chrome/browser/ui/cocoa/spinner_view.mm
@@ -11,7 +11,6 @@
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/theme_provider.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_mac.h"
 
 namespace {
 const CGFloat kDegrees90               = (M_PI / 2);
@@ -117,9 +116,9 @@
   [shapeLayer_ setLineCap:kCALineCapRound];
   [shapeLayer_ setLineDashPattern:@[ @(kArcLength * scaleFactor) ]];
   [shapeLayer_ setFillColor:NULL];
-  ui::NativeTheme* nativeTheme = ui::NativeThemeMac::instance();
-  SkColor throbberBlueColor = nativeTheme->GetSystemColor(
-      ui::NativeTheme::kColorId_ThrobberSpinningColor);
+  SkColor throbberBlueColor =
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+          ui::NativeTheme::kColorId_ThrobberSpinningColor);
   CGColorRef blueColor = skia::CGColorCreateFromSkColor(throbberBlueColor);
   [shapeLayer_ setStrokeColor:blueColor];
   CGColorRelease(blueColor);
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h
index 7af5fc8..1881ada1 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.h
@@ -35,6 +35,7 @@
   // These are released on mouseUp:
   BOOL moveWindowOnDrag_;  // Set if the only tab of a window is dragged.
   BOOL tabWasDragged_;  // Has the tab been dragged?
+  BOOL outOfTabHorizontalDeadZone_; // Moved out of its horizontal dead zone?
   BOOL draggingWithinTabStrip_;  // Did drag stay in the current tab strip?
   BOOL chromeIsVisible_;
 
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
index 3d16831..074476a 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
@@ -17,6 +17,7 @@
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
 
+const CGFloat kHorizontalTearDistance = 10.0; // Using the same value as Views.
 const CGFloat kTearDistance = 36.0;
 const NSTimeInterval kTearDuration = 0.333;
 
@@ -171,9 +172,17 @@
   if (draggingWithinTabStrip_) {
     NSPoint thisPoint = [NSEvent mouseLocation];
     CGFloat offset = thisPoint.x - dragOrigin_.x;
-    [sourceController_ insertPlaceholderForTab:[draggedTab_ tabView]
-                                         frame:NSOffsetRect(sourceTabFrame_,
-                                                            offset, 0)];
+    // Only begin dragging the tab horizontally if it's torn out of its dead
+    // zone.
+    if (!outOfTabHorizontalDeadZone_ &&
+        fabs(offset) > kHorizontalTearDistance) {
+      outOfTabHorizontalDeadZone_ = YES;
+    }
+    if (outOfTabHorizontalDeadZone_) {
+      [sourceController_ insertPlaceholderForTab:[draggedTab_ tabView]
+                                           frame:NSOffsetRect(sourceTabFrame_,
+                                                              offset, 0)];
+    }
     // Check that we haven't pulled the tab too far to start a drag. This
     // can include either pulling it too far down, or off the side of the tab
     // strip that would cause it to no longer be fully visible.
@@ -403,6 +412,7 @@
 - (void)endDrag:(NSEvent*)event {
   // Cancel any delayed -continueDrag: requests that may still be pending.
   [NSObject cancelPreviousPerformRequestsWithTarget:self];
+  outOfTabHorizontalDeadZone_ = NO;
 
   // Special-case this to keep the logic below simpler.
   if (moveWindowOnDrag_) {
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index d348884d..ff8299c 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -38,7 +38,8 @@
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/url_formatter/elide_url.h"
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index 10e8d96..10bd07d 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -33,7 +33,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 // The hierarchy of bubble models:
@@ -176,7 +176,8 @@
   AsSubresourceFilterBubbleModel();
 
   // Sets the Rappor service used for testing.
-  void SetRapporServiceForTesting(rappor::RapporService* rappor_service) {
+  void SetRapporServiceImplForTesting(
+      rappor::RapporServiceImpl* rappor_service) {
     rappor_service_ = rappor_service;
   }
 
@@ -233,7 +234,7 @@
   void set_setting_is_managed(bool managed) {
     setting_is_managed_ = managed;
   }
-  rappor::RapporService* rappor_service() const { return rappor_service_; }
+  rappor::RapporServiceImpl* rappor_service() const { return rappor_service_; }
 
  private:
   virtual void SetTitle() = 0;
@@ -249,7 +250,7 @@
   // controlled by the user.
   bool setting_is_managed_;
   // The service used to record Rappor metrics. Can be set for testing.
-  rappor::RapporService* rappor_service_;
+  rappor::RapporServiceImpl* rappor_service_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentSettingBubbleModel);
 };
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index 03c8ca3..84cf40e 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -103,7 +103,7 @@
   replace_host.SetHostStr("example.test");
   url = url.ReplaceComponents(replace_host);
 
-  rappor::TestRapporService rappor_service;
+  rappor::TestRapporServiceImpl rappor_service;
   EXPECT_EQ(0, rappor_service.GetReportsCount());
   base::HistogramTester histograms;
   histograms.ExpectTotalCount("ContentSettings.MixedScript", 0);
@@ -121,7 +121,7 @@
           browser()->content_setting_bubble_model_delegate(),
           browser()->tab_strip_model()->GetActiveWebContents(),
           browser()->profile(), CONTENT_SETTINGS_TYPE_MIXEDSCRIPT));
-  model->SetRapporServiceForTesting(&rappor_service);
+  model->SetRapporServiceImplForTesting(&rappor_service);
   model->OnCustomLinkClicked();
 
   // Wait for reload
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index 0d91a9c1..67af850 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -443,7 +443,7 @@
     case kColorId_AlertSeverityHigh: {
       ui::NativeTheme* fallback_theme =
           color_utils::IsDark(GetTextColor(GetEntry(), NORMAL))
-              ? ui::NativeThemeAura::instance()
+              ? ui::NativeTheme::GetInstanceForNativeUi()
               : ui::NativeThemeDarkAura::instance();
       return fallback_theme->GetSystemColor(color_id);
     }
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 7d43dff..d9e9610 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -353,11 +353,11 @@
   CreateSmartBubbleFieldTrial();
   std::unique_ptr<password_manager::PasswordFormManager> test_form_manager(
       CreateFormManager());
-  password_manager::InteractionsStats stats;
-  stats.origin_domain = test_local_form().origin.GetOrigin();
-  stats.username_value = test_local_form().username_value;
-  stats.dismissal_count = kGreatDissmisalCount;
-  fetcher().set_stats({&stats});
+  std::vector<password_manager::InteractionsStats> stats(1);
+  stats[0].origin_domain = test_local_form().origin.GetOrigin();
+  stats[0].username_value = test_local_form().username_value;
+  stats[0].dismissal_count = kGreatDissmisalCount;
+  fetcher().set_stats(stats);
   test_form_manager->ProvisionallySave(
       test_local_form(),
       password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -367,7 +367,7 @@
             controller()->GetState());
   EXPECT_FALSE(controller()->opened_bubble());
   ASSERT_TRUE(controller()->GetCurrentInteractionStats());
-  EXPECT_EQ(stats, *controller()->GetCurrentInteractionStats());
+  EXPECT_EQ(stats[0], *controller()->GetCurrentInteractionStats());
 
   ExpectIconStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
   variations::testing::ClearAllVariationParams();
@@ -377,11 +377,11 @@
   CreateSmartBubbleFieldTrial();
   std::unique_ptr<password_manager::PasswordFormManager> test_form_manager(
       CreateFormManager());
-  password_manager::InteractionsStats stats;
-  stats.origin_domain = test_local_form().origin.GetOrigin();
-  stats.username_value = base::ASCIIToUTF16("not my username");
-  stats.dismissal_count = kGreatDissmisalCount;
-  fetcher().set_stats({&stats});
+  std::vector<password_manager::InteractionsStats> stats(1);
+  stats[0].origin_domain = test_local_form().origin.GetOrigin();
+  stats[0].username_value = base::ASCIIToUTF16("not my username");
+  stats[0].dismissal_count = kGreatDissmisalCount;
+  fetcher().set_stats(stats);
   test_form_manager->ProvisionallySave(
       test_local_form(),
       password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index bbc91a1..b088b96 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -88,7 +88,8 @@
 #include "chrome/installer/util/browser_distribution.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/dom_storage_context.h"
 #include "content/public/browser/notification_observer.h"
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc
index aba3a32f8..5c0f70f9 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.cc
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -30,11 +30,10 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/ssl_status.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/context_menu_params.h"
 #include "net/base/load_states.h"
-#include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -257,33 +256,6 @@
   UpdateContentRestrictions(0);
 }
 
-void CoreTabHelper::DocumentOnLoadCompletedInMainFrame() {
-  bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kAllowInsecureLocalhost);
-  if (!allow_localhost)
-    return;
-
-  content::NavigationEntry* entry =
-      web_contents()->GetController().GetLastCommittedEntry();
-  if (!entry || !net::IsLocalhost(entry->GetURL().host()))
-    return;
-
-  content::SSLStatus ssl_status = entry->GetSSL();
-  bool is_cert_error = net::IsCertStatusError(ssl_status.cert_status) &&
-                       !net::IsCertStatusMinorError(ssl_status.cert_status);
-  if (!is_cert_error)
-    return;
-
-  web_contents()->GetMainFrame()->AddMessageToConsole(
-      content::CONSOLE_MESSAGE_LEVEL_WARNING,
-      base::StringPrintf(
-          "This site does not have a valid SSL "
-          "certificate! Without SSL, your site's and "
-          "visitors' data is vulnerable to theft and "
-          "tampering. Get a valid SSL certificate before"
-          " releasing your website to the public."));
-}
-
 void CoreTabHelper::WasShown() {
   web_cache::WebCacheManager::GetInstance()->ObserveActivity(
       web_contents()->GetRenderProcessHost()->GetID());
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.h b/chrome/browser/ui/tab_contents/core_tab_helper.h
index a2186f74..43041d47 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.h
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.h
@@ -81,7 +81,6 @@
 
   // content::WebContentsObserver overrides:
   void DidStartLoading() override;
-  void DocumentOnLoadCompletedInMainFrame() override;
   void WasShown() override;
   void WebContentsDestroyed() override;
   void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
index b89a513..abd1ed6 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
@@ -573,7 +573,8 @@
     EXPECT_EQ(tab_titles[i], model.GetLabelAt(i + 5));
 }
 
-TEST_F(RecentTabsSubMenuModelTest, MaxWidth) {
+// Flaky, see crbug.com/671902.
+TEST_F(RecentTabsSubMenuModelTest, DISABLED_MaxWidth) {
   // Create 1 session with 1 window and 1 tab.
   RecentTabsBuilderTestHelper recent_tabs_builder;
   recent_tabs_builder.AddSession();
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 533d56cd..269e947 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -160,7 +160,7 @@
                                  ash::kShellWindowId_ImeWindowParentContainer);
   }
   init_params->mus_properties
-      [ui::mojom::WindowManager::kRemoveStandardFrame_Property] =
+      [ui::mojom::WindowManager::kRemoveStandardFrame_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(init_params->remove_standard_frame);
 }
 
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
index df8db2b..bcfe14f 100644
--- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/ash/ash_init.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/cast_config_client_media_router.h"
+#include "chrome/browser/ui/ash/chrome_new_window_client.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_mus.h"
 #include "chrome/browser/ui/views/ash/tab_scrubber.h"
 #include "chrome/browser/ui/views/frame/immersive_context_mus.h"
@@ -26,6 +27,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/ash/system_tray_client.h"
+#include "chrome/browser/ui/ash/volume_controller.h"
 #include "chrome/browser/ui/ash/vpn_list_forwarder.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension_factory.h"
@@ -47,7 +49,8 @@
 #if defined(OS_CHROMEOS)
   // Must be available at login screen, so initialize before profile.
   system_tray_client_ = base::MakeUnique<SystemTrayClient>();
-
+  new_window_client_ = base::MakeUnique<ChromeNewWindowClient>();
+  volume_controller_ = base::MakeUnique<VolumeController>();
   vpn_list_forwarder_ = base::MakeUnique<VpnListForwarder>();
 
   // For OS_CHROMEOS, virtual keyboard needs to be initialized before profile
@@ -85,6 +88,8 @@
 void ChromeBrowserMainExtraPartsAsh::PostMainMessageLoopRun() {
 #if defined(OS_CHROMEOS)
   vpn_list_forwarder_.reset();
+  volume_controller_.reset();
+  new_window_client_.reset();
   system_tray_client_.reset();
   cast_config_client_media_router_.reset();
 #endif
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h
index 239fa82..e16b2bb 100644
--- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.h
@@ -12,9 +12,11 @@
 
 class CastConfigClientMediaRouter;
 class ChromeLauncherControllerMus;
+class ChromeNewWindowClient;
 class ImmersiveContextMus;
 class ImmersiveHandlerFactoryMus;
 class SystemTrayClient;
+class VolumeController;
 class VpnListForwarder;
 
 class ChromeBrowserMainExtraPartsAsh : public ChromeBrowserMainExtraParts {
@@ -33,6 +35,8 @@
   std::unique_ptr<ImmersiveHandlerFactoryMus> immersive_handler_factory_;
   std::unique_ptr<ImmersiveContextMus> immersive_context_;
   std::unique_ptr<SystemTrayClient> system_tray_client_;
+  std::unique_ptr<ChromeNewWindowClient> new_window_client_;
+  std::unique_ptr<VolumeController> volume_controller_;
   std::unique_ptr<VpnListForwarder> vpn_list_forwarder_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainExtraPartsAsh);
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
index 9df20f4f..85348ed 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -56,7 +56,7 @@
     return ui::NativeThemeDarkAura::instance();
   }
 
-  return ui::NativeThemeAura::instance();
+  return ui::NativeTheme::GetInstanceForNativeUi();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc
index 74067861..3047818 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -544,6 +544,16 @@
   return ViewsDelegate::GetDialogRelatedButtonHorizontalSpacing();
 }
 
+gfx::Insets ChromeViewsDelegate::GetDialogFrameViewInsets() {
+  if (ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    // Titles are inset at the top and sides, but not at the bottom.
+    return gfx::Insets(HarmonyLayoutDelegate::kHarmonyLayoutUnit,
+                       HarmonyLayoutDelegate::kHarmonyLayoutUnit, 0,
+                       HarmonyLayoutDelegate::kHarmonyLayoutUnit);
+  }
+  return ViewsDelegate::GetDialogFrameViewInsets();
+}
+
 #if !defined(USE_ASH)
 views::Widget::InitParams::WindowOpacity
 ChromeViewsDelegate::GetOpacityForInitParams(
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index 0cc4c4cd..cea43d8 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -63,6 +63,7 @@
 
   gfx::Insets GetDialogButtonInsets() override;
   int GetDialogRelatedButtonHorizontalSpacing() override;
+  gfx::Insets GetDialogFrameViewInsets() override;
 
  private:
 #if defined(OS_WIN)
diff --git a/chrome/browser/ui/views/chrome_web_dialog_view.cc b/chrome/browser/ui/views/chrome_web_dialog_view.cc
index 90bcd5ca..93a3eec 100644
--- a/chrome/browser/ui/views/chrome_web_dialog_view.cc
+++ b/chrome/browser/ui/views/chrome_web_dialog_view.cc
@@ -65,7 +65,7 @@
   params.delegate = view;
   if (chrome::IsRunningInMash()) {
     using ui::mojom::WindowManager;
-    params.mus_properties[WindowManager::kInitialContainerId_Property] =
+    params.mus_properties[WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(container_id);
   } else {
     params.parent = ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
diff --git a/chrome/browser/ui/views/frame/browser_frame_mus.cc b/chrome/browser/ui/views/frame/browser_frame_mus.cc
index 1c8c736..47bca94 100644
--- a/chrome/browser/ui/views/frame/browser_frame_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_mus.cc
@@ -30,7 +30,7 @@
       GetWidgetParamsImpl(browser_view), &properties);
   const std::string chrome_app_id(extension_misc::kChromeAppId);
   // Indicates mash shouldn't handle immersive, rather we will.
-  properties[ui::mojom::WindowManager::kDisableImmersive_Property] =
+  properties[ui::mojom::WindowManager::kDisableImmersive_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(true);
   properties[ui::mojom::WindowManager::kAppID_Property] =
       mojo::ConvertTo<std::vector<uint8_t>>(chrome_app_id);
diff --git a/chrome/browser/ui/views/frame/browser_header_painter_ash.cc b/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
index 4096cda..45d2a559 100644
--- a/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
@@ -256,11 +256,7 @@
   if (alpha == 0)
     return;
 
-  int corner_radius =
-      (frame_->IsMaximized() || frame_->IsFullscreen())
-          ? 0
-          : ash::HeaderPainterUtil::GetTopCornerRadiusWhenRestored();
-
+  bool round_corners = !frame_->IsMaximized() && !frame_->IsFullscreen();
   gfx::ImageSkia frame_image = view_->GetFrameImage(active);
   gfx::ImageSkia frame_overlay_image = view_->GetFrameOverlayImage(active);
 
@@ -268,9 +264,12 @@
   paint.setBlendMode(SkBlendMode::kPlus);
   paint.setAlpha(alpha);
   paint.setColor(SkColorSetA(view_->GetFrameColor(active), alpha));
+  paint.setAntiAlias(round_corners);
   PaintFrameImagesInRoundRect(
       canvas, frame_image, frame_overlay_image, paint, GetPaintedBounds(),
-      corner_radius, ash::HeaderPainterUtil::GetThemeBackgroundXInset());
+      round_corners ? ash::HeaderPainterUtil::GetTopCornerRadiusWhenRestored()
+                    : 0,
+      ash::HeaderPainterUtil::GetThemeBackgroundXInset());
 }
 
 void BrowserHeaderPainterAsh::PaintTitleBar(gfx::Canvas* canvas) {
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index d31ac99..f2a776f 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1917,11 +1917,7 @@
 #if defined(USE_AURA)
     ui::NativeThemeDarkAura::instance()->NotifyObservers();
 #endif
-#if defined(OS_WIN)
-    ui::NativeThemeWin::instance()->NotifyObservers();
-#elif defined(OS_LINUX)
-    ui::NativeThemeAura::instance()->NotifyObservers();
-#endif
+    ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
   }
 
   views::View::OnThemeChanged();
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 17836ac..02a01a76 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -23,6 +23,7 @@
 #include "services/ui/public/cpp/window_tree_client.h"
 #include "services/ui/public/interfaces/window_manager.mojom.h"
 #include "ui/aura/mus/mus_util.h"
+#include "ui/aura/mus/property_converter.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/paint_context.h"
 #include "ui/compositor/paint_recorder.h"
@@ -232,9 +233,9 @@
   mash_reveal_widget_.reset(new views::Widget);
   views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_POPUP);
   std::map<std::string, std::vector<uint8_t>> window_properties;
-  window_properties
-      [ui::mojom::WindowManager::kRendererParentTitleArea_Property] =
-          mojo::ConvertTo<std::vector<uint8_t>>(true);
+  window_properties[ui::mojom::WindowManager::kRenderParentTitleArea_Property] =
+      mojo::ConvertTo<std::vector<uint8_t>>(
+          static_cast<aura::PropertyConverter::PrimitiveType>(true));
   window_properties[ui::mojom::WindowManager::kName_Property] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           std::string("ChromeImmersiveRevealWindow"));
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index cb903aea..65804a0 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
 
 #include "chrome/browser/ui/layout_constants.h"
+#include "ui/accessibility/ax_node_data.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/scoped_canvas.h"
@@ -130,6 +131,10 @@
   label_->SetBounds(label_x, 0, label_width, height());
 }
 
+void IconLabelBubbleView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  label_->GetAccessibleNodeData(node_data);
+}
+
 void IconLabelBubbleView::OnNativeThemeChanged(
     const ui::NativeTheme* native_theme) {
   label_->SetEnabledColor(GetTextColor());
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index 9d1812a68..0928b90 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -67,11 +67,12 @@
   // true if some handling was performed.
   virtual bool OnActivate(const ui::Event& event);
 
-  // views::View:
+  // views::InkDropHostView:
   gfx::Size GetPreferredSize() const override;
   void Layout() override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   bool OnKeyReleased(const ui::KeyEvent& event) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc
index 8377a67..f3dfc1a 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -124,6 +124,11 @@
   return true;
 }
 
+void LocationIconView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  IconLabelBubbleView::GetAccessibleNodeData(node_data);
+  node_data->role = ui::AX_ROLE_POP_UP_BUTTON;
+}
+
 gfx::Size LocationIconView::GetMinimumSizeForLabelText(
     const base::string16& text) const {
   views::Label label(text, font_list());
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.h b/chrome/browser/ui/views/location_bar/location_icon_view.h
index 9bc850f1f..a16501df 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.h
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.h
@@ -36,6 +36,7 @@
                       base::string16* tooltip) const override;
   SkColor GetTextColor() const override;
   bool OnActivate(const ui::Event& event) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // Whether we should show the tooltip for this icon or not.
   void set_show_tooltip(bool show_tooltip) { show_tooltip_ = show_tooltip; }
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog.cc b/chrome/browser/ui/views/payments/payment_request_dialog.cc
new file mode 100644
index 0000000..04f6d69
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_dialog.cc
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/payments/payment_request_impl.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace chrome {
+
+void ShowPaymentRequestDialog(payments::PaymentRequestImpl* impl) {
+    constrained_window::ShowWebModalDialogViews(
+        new payments::PaymentRequestDialog(impl), impl->web_contents());
+}
+
+}
+
+namespace payments {
+
+PaymentRequestDialog::PaymentRequestDialog(PaymentRequestImpl* impl)
+    : impl_(impl),
+      label_(new views::Label(base::ASCIIToUTF16("Payments dialog"))) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  SetLayoutManager(new views::FillLayout());
+  AddChildView(label_.get());
+}
+
+PaymentRequestDialog::~PaymentRequestDialog() {}
+
+ui::ModalType PaymentRequestDialog::GetModalType() const {
+  return ui::MODAL_TYPE_CHILD;
+}
+
+gfx::Size PaymentRequestDialog::GetPreferredSize() const {
+  gfx::Size ps = label_->GetPreferredSize();
+  ps.Enlarge(200, 200);
+  return ps;
+}
+
+bool PaymentRequestDialog::Cancel() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  impl_->Cancel();
+  return true;
+}
+
+}  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog.h b/chrome/browser/ui/views/payments/payment_request_dialog.h
new file mode 100644
index 0000000..6a08d698
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_dialog.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_H_
+#define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_H_
+
+#include "ui/views/controls/label.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace payments {
+
+class PaymentRequestImpl;
+
+// The dialog delegate that represents a desktop WebPayments dialog. This class
+// is responsible for displaying the view associated with the current state of
+// the WebPayments flow and managing the transition between those states.
+class PaymentRequestDialog : public views::DialogDelegateView {
+ public:
+  explicit PaymentRequestDialog(PaymentRequestImpl* impl);
+  ~PaymentRequestDialog() override;
+
+  // views::WidgetDelegate:
+  ui::ModalType GetModalType() const override;
+
+  // views::View:
+  gfx::Size GetPreferredSize() const override;
+
+  // views::DialogDelegate:
+  bool Cancel() override;
+
+ private:
+  // Non-owned reference to the PaymentRequestImpl that initiated this dialog.
+  // Since the PaymentRequestImpl object always outlives this one, the pointer
+  // should always be valid even though there is no direct ownership
+  // relationship between the two.
+  PaymentRequestImpl* impl_;
+  std::unique_ptr<views::Label> label_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestDialog);
+};
+
+}  // namespace payments
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_REQUEST_DIALOG_H_
diff --git a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
index b0ef108..30ca1bf 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/harmony/layout_delegate.h"
 #include "chrome/grit/locale_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/color_palette.h"
@@ -16,10 +17,8 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/layout_constants.h"
 
 namespace {
-const int kListPadding = 10;
 const int kIconSize = 16;
 }
 
@@ -72,7 +71,9 @@
     views::View* parent = new views::View();
     parent->SetLayoutManager(
         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
-                             views::kRelatedControlVerticalSpacing));
+                             LayoutDelegate::Get()->GetLayoutDistance(
+                                 LayoutDelegate::LayoutDistanceType::
+                                     RELATED_CONTROL_VERTICAL_SPACING)));
     parent->AddChildView(icon.release());
     parent->AddChildView(label.release());
     return parent;
@@ -106,8 +107,11 @@
 }
 
 void ToolbarActionsBarBubbleViews::Init() {
-  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0,
-                                        views::kRelatedControlVerticalSpacing));
+  LayoutDelegate* delegate = LayoutDelegate::Get();
+  SetLayoutManager(new views::BoxLayout(
+      views::BoxLayout::kVertical, 0, 0,
+      delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType::
+                                      RELATED_CONTROL_VERTICAL_SPACING)));
 
   // Add the content string.
   views::Label* content_label = new views::Label(
@@ -120,9 +124,13 @@
   AddChildView(content_label);
 
   base::string16 item_list = delegate_->GetItemListText();
+
   if (!item_list.empty()) {
     item_list_ = new views::Label(item_list);
-    item_list_->SetBorder(views::CreateEmptyBorder(0, kListPadding, 0, 0));
+    item_list_->SetBorder(views::CreateEmptyBorder(
+        0, delegate->GetLayoutDistance(LayoutDelegate::LayoutDistanceType::
+                                           RELATED_CONTROL_HORIZONTAL_SPACING),
+        0, 0));
     item_list_->SetMultiLine(true);
     item_list_->SizeToFit(width);
     item_list_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
diff --git a/chrome/browser/ui/website_settings/website_settings.cc b/chrome/browser/ui/website_settings/website_settings.cc
index 5f8c7a3..c1562185 100644
--- a/chrome/browser/ui/website_settings/website_settings.cc
+++ b/chrome/browser/ui/website_settings/website_settings.cc
@@ -50,7 +50,8 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/ssl_errors/error_info.h"
 #include "components/strings/grit/components_chromium_strings.h"
 #include "components/strings/grit/components_strings.h"
@@ -709,7 +710,7 @@
     std::unique_ptr<base::Value> value = content_settings_->GetWebsiteSetting(
         site_url_, site_url_, permission_info.type, std::string(), &info);
     DCHECK(value.get());
-    if (value->GetType() == base::Value::TYPE_INTEGER) {
+    if (value->GetType() == base::Value::Type::INTEGER) {
       permission_info.setting =
           content_settings::ValueToContentSetting(value.get());
     } else {
diff --git a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
index 0655457d..a2150d4 100644
--- a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
@@ -110,9 +110,6 @@
               &AppLaunchSplashScreenHandler::HandleNetworkConfigRequested);
 }
 
-void AppLaunchSplashScreenHandler::PrepareToShow() {
-}
-
 void AppLaunchSplashScreenHandler::Hide() {
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h
index be42663..40f68b7 100644
--- a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h
@@ -37,7 +37,6 @@
 
   // AppLaunchSplashScreenActor implementation:
   void Show(const std::string& app_id) override;
-  void PrepareToShow() override;
   void Hide() override;
   void ToggleNetworkConfig(bool visible) override;
   void UpdateAppLaunchState(AppLaunchState state) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
index 30aa0a6..9a37ebf9 100644
--- a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
@@ -46,9 +46,6 @@
     delegate_->OnActorDestroyed(this);
 }
 
-void EnableDebuggingScreenHandler::PrepareToShow() {
-}
-
 void EnableDebuggingScreenHandler::ShowWithParams() {
   ShowScreen(OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING);
 
diff --git a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h
index 469d2b0..da45fc5f 100644
--- a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h
@@ -25,7 +25,6 @@
   ~EnableDebuggingScreenHandler() override;
 
   // EnableDebuggingScreenActor implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void SetDelegate(Delegate* delegate) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index 7ea8c071..5121bfe 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -187,9 +187,6 @@
   config_ = config;
 }
 
-void EnrollmentScreenHandler::PrepareToShow() {
-}
-
 void EnrollmentScreenHandler::Show() {
   if (!page_is_ready())
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
index dfaf5c6..f9834c0 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
@@ -40,7 +40,6 @@
   // Implements EnrollmentScreenActor:
   void SetParameters(Controller* controller,
                      const policy::EnrollmentConfig& config) override;
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void ShowSigninScreen() override;
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
index a3ab879..b3092a57 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -34,9 +34,6 @@
     model_->OnViewDestroyed(this);
 }
 
-void ErrorScreenHandler::PrepareToShow() {
-}
-
 void ErrorScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
index 6199536..e63fc19 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
@@ -23,7 +23,6 @@
   ~ErrorScreenHandler() override;
 
   // ErrorView:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Bind(NetworkErrorModel& model) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
index 842aa7b..2128ca0d 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -93,9 +93,6 @@
     model_->OnViewDestroyed(this);
 }
 
-void EulaScreenHandler::PrepareToShow() {
-}
-
 void EulaScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
index a2f9276..b09cae9 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
@@ -32,7 +32,6 @@
   ~EulaScreenHandler() override;
 
   // EulaView implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Bind(EulaModel& model) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
index a93af53..8cef9a0 100644
--- a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
@@ -71,9 +71,6 @@
   BaseScreenHandler::SetBaseScreen(nullptr);
 }
 
-void HIDDetectionScreenHandler::PrepareToShow() {
-}
-
 void HIDDetectionScreenHandler::CheckIsScreenRequired(
       const base::Callback<void(bool)>& on_check_done) {
   model_->CheckIsScreenRequired(on_check_done);
diff --git a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h
index 161e3f4..5e4fc49 100644
--- a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h
@@ -29,7 +29,6 @@
   ~HIDDetectionScreenHandler() override;
 
   // HIDDetectionView implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Bind(HIDDetectionModel& model) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index 2c1c2ef..a9f516d0 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -69,9 +69,6 @@
 
 // NetworkScreenHandler, NetworkScreenActor implementation: --------------------
 
-void NetworkScreenHandler::PrepareToShow() {
-}
-
 void NetworkScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
index 1016666..ab2b3d2 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
@@ -30,7 +30,6 @@
 
  private:
   // NetworkView implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Bind(NetworkModel& model) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index bbadb13..e237c4b 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -38,9 +38,6 @@
     model_->OnViewDestroyed(this);
 }
 
-void ResetScreenHandler::PrepareToShow() {
-}
-
 void ResetScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
index fbebfb3..5efadab 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
@@ -25,7 +25,6 @@
   // ResetView implementation:
   void Bind(ResetModel& model) override;
   void Unbind() override;
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
 
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 2934a79..2b9092ec 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -94,7 +94,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/service_manager_connection.h"
-#include "content/public/common/service_names.mojom.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -296,15 +295,10 @@
   if (keyboard)
     keyboard->AddObserver(this);
 
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  if (!chrome::IsRunningInMash()) {
-    connector->ConnectToInterface(content::mojom::kBrowserServiceName,
-                                  &touch_view_manager_ptr_);
-  } else {
-    connector->ConnectToInterface("ash", &touch_view_manager_ptr_);
-  }
-
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->ConnectToInterface(ash_util::GetAshServiceName(),
+                           &touch_view_manager_ptr_);
   touch_view_manager_ptr_->AddObserver(
       touch_view_binding_.CreateInterfacePtrAndBind());
 }
@@ -1224,6 +1218,7 @@
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   if (delegate_ && !connector->IsEnterpriseManaged() &&
+      KioskAppManager::IsConsumerKioskEnabled() &&
       LoginDisplayHost::default_host()) {
     delegate_->ShowKioskEnableScreen();
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
index 2f6287a..46ccc7d 100644
--- a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
@@ -219,8 +219,6 @@
                   HandleCurrentSupervisedUserPage);
 }
 
-void SupervisedUserCreationScreenHandler::PrepareToShow() {}
-
 void SupervisedUserCreationScreenHandler::Show() {
   std::unique_ptr<base::DictionaryValue> data(new base::DictionaryValue());
   std::unique_ptr<base::ListValue> users_list(new base::ListValue());
diff --git a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
index 3a9dbd1..313eb3f 100644
--- a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
@@ -69,7 +69,6 @@
   SupervisedUserCreationScreenHandler();
   ~SupervisedUserCreationScreenHandler() override;
 
-  virtual void PrepareToShow();
   virtual void Show();
   virtual void Hide();
   virtual void SetDelegate(Delegate* delegate);
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
index 6322fa7..b48cf93 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -65,9 +65,6 @@
   }
 }
 
-void UpdateScreenHandler::PrepareToShow() {
-}
-
 void UpdateScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
index 45280366..c979a06 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
@@ -20,7 +20,6 @@
   ~UpdateScreenHandler() override;
 
   // UpdateView:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void Bind(UpdateModel& model) override;
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
index edb0aaf7..56c6d15 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -89,9 +89,6 @@
 void UserImageScreenHandler::Hide() {
 }
 
-void UserImageScreenHandler::PrepareToShow() {
-}
-
 void UserImageScreenHandler::DeclareLocalizedValues(
     ::login::LocalizedValuesBuilder* builder) {
   builder->Add("userImageScreenTitle", IDS_USER_IMAGE_SCREEN_TITLE);
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
index 9562f97..85f0392 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
@@ -37,7 +37,6 @@
   void Unbind() override;
   void Show() override;
   void Hide() override;
-  void PrepareToShow() override;
   void HideCurtain() override;
 
  private:
diff --git a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
index f70f873e..3d2d366 100644
--- a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
@@ -27,9 +27,6 @@
     delegate_->OnActorDestroyed(this);
 }
 
-void WrongHWIDScreenHandler::PrepareToShow() {
-}
-
 void WrongHWIDScreenHandler::Show() {
   if (!page_is_ready()) {
     show_on_init_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h
index fee76d82..0865850 100644
--- a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h
@@ -21,7 +21,6 @@
   ~WrongHWIDScreenHandler() override;
 
   // WrongHWIDScreenActor implementation:
-  void PrepareToShow() override;
   void Show() override;
   void Hide() override;
   void SetDelegate(Delegate* delegate) override;
diff --git a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
index 01003f6..5a7b9f2 100644
--- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
+++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+GEN('#include "base/command_line.h"');
+
 /**
  * TestFixture for kiosk app settings WebUI testing.
  * @extends {testing.Test}
@@ -17,6 +19,11 @@
    */
   browsePreload: 'chrome://extensions-frame/',
 
+  /** @override */
+  commandLineSwitches: [{
+    switchName: 'enable-consumer-kiosk',
+  }],
+
   /**
    * Mock settings data.
    * @private
@@ -233,3 +240,26 @@
   extensions.KioskAppsOverlay.setSettings(this.settings_);
   expectFalse(checkbox.disabled);
 });
+
+/**
+ * TestFixture for kiosk app settings when consumer kiosk is disabled.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function NoConsumerKioskWebUITest() {}
+
+NoConsumerKioskWebUITest.prototype = {
+  __proto__: KioskAppSettingsWebUITest.prototype,
+
+  /** @override */
+  commandLineSwitches: [],
+
+  /** @override */
+  setUp: function() {}
+};
+
+// Test kiosk app settings are not visible when consumer kiosk is disabled.
+TEST_F('NoConsumerKioskWebUITest', 'settingsHidden', function() {
+  assertEquals(this.browsePreload, document.location.href);
+  assertTrue($('add-kiosk-app').hidden);
+});
diff --git a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
index 67a9cc3..c7fc87d3 100644
--- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
+++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
@@ -19,6 +19,7 @@
 #include "base/sys_info.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -213,12 +214,23 @@
 void KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus(
     chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
   initialized_ = true;
-  is_kiosk_enabled_ = user_manager::UserManager::Get()->IsCurrentUserOwner() ||
-                      !base::SysInfo::IsRunningOnChromeOS();
-
-  is_auto_launch_enabled_ =
-      status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED ||
-      !base::SysInfo::IsRunningOnChromeOS();
+  if (KioskAppManager::IsConsumerKioskEnabled()) {
+    if (!base::SysInfo::IsRunningOnChromeOS()) {
+      // Enable everything when running on a dev box.
+      is_kiosk_enabled_ = true;
+      is_auto_launch_enabled_ = true;
+    } else {
+      // Enable consumer kiosk for owner and enable auto launch if configured.
+      is_kiosk_enabled_ =
+          ProfileHelper::IsOwnerProfile(Profile::FromWebUI(web_ui()));
+      is_auto_launch_enabled_ =
+          status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
+    }
+  } else {
+    // Otherwise, consumer kiosk is disabled.
+    is_kiosk_enabled_ = false;
+    is_auto_launch_enabled_ = false;
+  }
 
   if (is_kiosk_enabled_) {
     base::DictionaryValue kiosk_params;
diff --git a/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc
index d38be7512..4ed40ad 100644
--- a/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_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/macros.h"
 #include "chrome/browser/media/router/media_router_ui_service.h"
 #include "chrome/browser/media/router/test_helper.h"
@@ -252,15 +254,17 @@
 
   EXPECT_CALL(*action_controller, OnDialogShown());
   dialog_controller_->ShowMediaRouterDialogForPresentation(
-      base::MakeUnique<CreatePresentationConnectionRequest>(
-          RenderFrameHostId(1, 2), GURL("http://test.com"),
-          GURL("http://example.com"),
-          base::Bind(
-              &MediaRouterDialogControllerImplTest::PresentationSuccessCallback,
-              base::Unretained(this)),
-          base::Bind(
-              &MediaRouterDialogControllerImplTest::PresentationErrorCallback,
-              base::Unretained(this))));
+      std::unique_ptr<CreatePresentationConnectionRequest>(
+          new CreatePresentationConnectionRequest(
+              RenderFrameHostId(1, 2),
+              {GURL("http://test.com"), GURL("http://test2.com")},
+              GURL("http://example.com"),
+              base::Bind(&MediaRouterDialogControllerImplTest::
+                             PresentationSuccessCallback,
+                         base::Unretained(this)),
+              base::Bind(&MediaRouterDialogControllerImplTest::
+                             PresentationErrorCallback,
+                         base::Unretained(this)))));
 
   // When |dialog_controller_| is destroyed with its dialog open,
   // |action_controller| should be notified.
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 737608e6..5f8f396 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -92,6 +92,15 @@
   }
 }
 
+// Returns the first source in |sources| that can be connected to by using the
+// "Cast" button in the dialog, or an empty source if there is none.  This is
+// used by the Media Router to find such a matching route if it exists.
+MediaSource GetSourceForRouteObserver(const std::vector<MediaSource>& sources) {
+  auto source_it =
+      std::find_if(sources.begin(), sources.end(), CanConnectToMediaSource);
+  return source_it != sources.end() ? *source_it : MediaSource("");
+}
+
 }  // namespace
 
 // static
@@ -310,10 +319,15 @@
   query_result_manager_->SetSourcesForCastMode(
       MediaCastMode::DEFAULT, sources,
       presentation_request_->frame_url().GetOrigin());
-  // Register for MediaRoute updates.
-  // TODO(crbug.com/627655): Use multiple URLs.
+  // Register for MediaRoute updates.  NOTE(mfoltz): If there are multiple
+  // sources that can be connected to via the dialog, this will break.  We will
+  // need to observe multiple sources (keyed by sinks) in that case.  As this is
+  // Cast-specific for the forseeable future, it may be simpler to plumb a new
+  // observer API for this case.
+  const MediaSource source_for_route_observer =
+      GetSourceForRouteObserver(sources);
   routes_observer_.reset(new UIMediaRoutesObserver(
-      router_, sources[0].id(),
+      router_, source_for_route_observer.id(),
       base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
 
   UpdateCastModes();
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index 924c143..bfd108f 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -475,7 +475,8 @@
       "No screens found.");
   PresentationRequestCallbacks request_callbacks(expected_error);
   create_session_request_.reset(new CreatePresentationConnectionRequest(
-      RenderFrameHostId(0, 0), GURL("http://google.com/presentation"),
+      RenderFrameHostId(0, 0), {GURL("http://google.com/presentation"),
+                                GURL("http://google.com/presentation2")},
       GURL("http://google.com"),
       base::Bind(&PresentationRequestCallbacks::Success,
                  base::Unretained(&request_callbacks)),
@@ -494,7 +495,7 @@
   PresentationRequestCallbacks request_callbacks(expected_error);
   GURL presentation_url("http://google.com/presentation");
   create_session_request_.reset(new CreatePresentationConnectionRequest(
-      RenderFrameHostId(0, 0), presentation_url, GURL("http://google.com"),
+      RenderFrameHostId(0, 0), {presentation_url}, GURL("http://google.com"),
       base::Bind(&PresentationRequestCallbacks::Success,
                  base::Unretained(&request_callbacks)),
       base::Bind(&PresentationRequestCallbacks::Error,
@@ -524,7 +525,7 @@
   PresentationRequestCallbacks request_callbacks(expected_error);
   GURL presentation_url("http://google.com/presentation");
   create_session_request_.reset(new CreatePresentationConnectionRequest(
-      RenderFrameHostId(0, 0), presentation_url, GURL("http://google.com"),
+      RenderFrameHostId(0, 0), {presentation_url}, GURL("http://google.com"),
       base::Bind(&PresentationRequestCallbacks::Success,
                  base::Unretained(&request_callbacks)),
       base::Bind(&PresentationRequestCallbacks::Error,
diff --git a/chrome/browser/ui/webui/nacl_ui.cc b/chrome/browser/ui/webui/nacl_ui.cc
index f3713b2c..7fb1ea6 100644
--- a/chrome/browser/ui/webui/nacl_ui.cc
+++ b/chrome/browser/ui/webui/nacl_ui.cc
@@ -335,7 +335,7 @@
   JSONFileValueDeserializer deserializer(pnacl_json_path);
   std::string error;
   std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
-  if (!root || !root->IsType(base::Value::TYPE_DICTIONARY))
+  if (!root || !root->IsType(base::Value::Type::DICTIONARY))
     return;
 
   // Now try to get the field. This may leave version empty if the
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
index 4b25d05d..c5853e0 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/net_log/chrome_net_log.h"
@@ -330,15 +329,11 @@
 NetInternalsTest::~NetInternalsTest() {
 }
 
-void NetInternalsTest::SetUpCommandLine(base::CommandLine* command_line) {
-  WebUIBrowserTest::SetUpCommandLine(command_line);
-  // Needed to test the prerender view.
-  command_line->AppendSwitchASCII(switches::kPrerenderMode,
-                                  switches::kPrerenderModeSwitchValueEnabled);
-}
-
 void NetInternalsTest::SetUpOnMainThread() {
   WebUIBrowserTest::SetUpOnMainThread();
+  // Needed to test the prerender view.
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
   // Increase the memory allowed in a prerendered page above normal settings,
   // as debug builds use more memory and often go over the usual limit.
   Profile* profile = browser()->profile();
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h
index 2a2df5c037..fcae5dae 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h
@@ -21,7 +21,6 @@
   class MessageHandler;
 
   // InProcessBrowserTest overrides.
-  void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpOnMainThread() override;
 
   // WebUIBrowserTest implementation.
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index 62732f9..7d6a0b8 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
 #include "content/public/browser/web_ui.h"
 #include "net/base/network_change_notifier.h"
 
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
index 7452677..d79ad58 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
@@ -7,10 +7,10 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_store_types.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_store_types.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace offline_pages {
diff --git a/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc b/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc
index dbd287df..cbc56c10 100644
--- a/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc
@@ -9,8 +9,8 @@
 #include <memory>
 #include <utility>
 
+#include "ash/common/new_window_controller.h"
 #include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -178,7 +178,7 @@
 }
 
 void KeyboardHandler::HandleShowKeyboardShortcuts(const base::ListValue* args) {
-  ash::WmShell::Get()->new_window_client()->ShowKeyboardOverlay();
+  ash::WmShell::Get()->new_window_controller()->ShowKeyboardOverlay();
 }
 
 void KeyboardHandler::UpdateCapsLockOptions() const {
diff --git a/chrome/browser/ui/webui/options/core_options_handler.cc b/chrome/browser/ui/webui/options/core_options_handler.cc
index f843155..a647807 100644
--- a/chrome/browser/ui/webui/options/core_options_handler.cc
+++ b/chrome/browser/ui/webui/options/core_options_handler.cc
@@ -302,11 +302,11 @@
   }
 
   switch (value->GetType()) {
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_INTEGER:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_STRING:
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::INTEGER:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::STRING:
+    case base::Value::Type::LIST:
       pref_service->Set(pref_name, *value);
       break;
 
@@ -333,7 +333,7 @@
     return;
 
   std::string metric_string = metric;
-  if (value->IsType(base::Value::TYPE_BOOLEAN)) {
+  if (value->IsType(base::Value::Type::BOOLEAN)) {
     bool bool_value;
     CHECK(value->GetAsBoolean(&bool_value));
     metric_string += bool_value ? "_Enable" : "_Disable";
@@ -447,7 +447,7 @@
 
   // Get callback JS function name.
   const base::Value* callback;
-  if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
+  if (!args->Get(0, &callback) || !callback->IsType(base::Value::Type::STRING))
     return;
 
   base::string16 callback_function;
@@ -462,7 +462,7 @@
     if (!args->Get(i, &list_member))
       break;
 
-    if (!list_member->IsType(base::Value::TYPE_STRING))
+    if (!list_member->IsType(base::Value::Type::STRING))
       continue;
 
     std::string pref_name;
@@ -493,7 +493,7 @@
 
     // Just ignore bad pref identifiers for now.
     std::string pref_name;
-    if (!list_member->IsType(base::Value::TYPE_STRING) ||
+    if (!list_member->IsType(base::Value::Type::STRING) ||
         !list_member->GetAsString(&pref_name))
       continue;
 
@@ -545,7 +545,7 @@
 
   switch (type) {
     case TYPE_BOOLEAN:
-      if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
+      if (!value->IsType(base::Value::Type::BOOLEAN)) {
         NOTREACHED();
         return;
       }
@@ -563,13 +563,13 @@
       break;
     }
     case TYPE_DOUBLE:
-      if (!value->IsType(base::Value::TYPE_DOUBLE)) {
+      if (!value->IsType(base::Value::Type::DOUBLE)) {
         NOTREACHED();
         return;
       }
       break;
     case TYPE_STRING:
-      if (!value->IsType(base::Value::TYPE_STRING)) {
+      if (!value->IsType(base::Value::Type::STRING)) {
         NOTREACHED();
         return;
       }
@@ -594,7 +594,7 @@
       }
       temp_value = base::JSONReader::Read(json_string);
       value = temp_value.get();
-      if (!value || !value->IsType(base::Value::TYPE_LIST)) {
+      if (!value || !value->IsType(base::Value::Type::LIST)) {
         NOTREACHED();
         return;
       }
diff --git a/chrome/browser/ui/webui/options/preferences_browsertest.cc b/chrome/browser/ui/webui/options/preferences_browsertest.cc
index de784ef..1a47f6e 100644
--- a/chrome/browser/ui/webui/options/preferences_browsertest.cc
+++ b/chrome/browser/ui/webui/options/preferences_browsertest.cc
@@ -900,13 +900,13 @@
   void SetProxyPref(const std::string& name, const base::Value& value) {
     std::string type;
     switch (value.GetType()) {
-      case base::Value::TYPE_BOOLEAN:
+      case base::Value::Type::BOOLEAN:
         type = "Boolean";
         break;
-      case base::Value::TYPE_INTEGER:
+      case base::Value::Type::INTEGER:
         type = "Integer";
         break;
-      case base::Value::TYPE_STRING:
+      case base::Value::Type::STRING:
         type = "String";
         break;
       default:
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
index 0cffa21..aa1676b1e 100644
--- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -96,12 +96,6 @@
   PostCallbackError(cb);
 }
 
-bool IsIppEverywhere(const chromeos::Printer& printer) {
-  // TODO(skau): Use uri, effective_make and effective_model to determine if
-  // we should do an IPP Everywhere configuration.
-  return false;
-}
-
 std::string GetPPDPath(const chromeos::Printer& printer) {
   // TODO(skau): Consult the PPD Provider for the correct file path.
   return printer.ppd_reference().user_supplied_ppd_url;
@@ -146,7 +140,7 @@
   std::unique_ptr<chromeos::Printer> printer = prefs->GetPrinter(printer_name);
 
   // Check if configuration is viable.
-  bool ipp_everywhere = IsIppEverywhere(*printer);
+  bool ipp_everywhere = printer->IsIppEverywhere();
   std::string ppd_path = GetPPDPath(*printer);
   if (!ipp_everywhere && ppd_path.empty()) {
     HandlePrinterSetup(std::move(printer), PPD_NOT_FOUND, cb);
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 65f12f4..a904c37 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -308,8 +308,6 @@
       IDS_ABOUT_CROS_VERSION_LICENSE,
       base::ASCIIToUTF16(chrome::kChromeUIOSCreditsURL));
   html_source->AddString("aboutProductOsLicense", os_license);
-
-  html_source->AddBoolean("aboutCanChangeChannel", CanChangeChannel(profile));
   html_source->AddBoolean("aboutEnterpriseManaged", IsEnterpriseManaged());
 
   base::Time build_time = base::SysInfo::GetLsbReleaseTime();
@@ -357,11 +355,8 @@
       "getRegulatoryInfo", base::Bind(&AboutHandler::HandleGetRegulatoryInfo,
                                       base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "getCurrentChannel", base::Bind(&AboutHandler::HandleGetCurrentChannel,
-                                      base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getTargetChannel", base::Bind(&AboutHandler::HandleGetTargetChannel,
-                                     base::Unretained(this)));
+      "getChannelInfo", base::Bind(&AboutHandler::HandleGetChannelInfo,
+                                   base::Unretained(this)));
 #endif
 #if defined(OS_MACOSX)
   web_ui()->RegisterMessageCallback(
@@ -520,31 +515,35 @@
                  weak_factory_.GetWeakPtr(), callback_id));
 }
 
-void AboutHandler::HandleGetCurrentChannel(const base::ListValue* args) {
-  CHECK_EQ(1U, args->GetSize());
-  std::string callback_id;
-  CHECK(args->GetString(0, &callback_id));
-  // First argument to GetChannel() is a flag that indicates whether
-  // current channel should be returned (if true) or target channel
-  // (otherwise).
-  version_updater_->GetChannel(
-      true, base::Bind(&AboutHandler::OnGetChannelReady,
-                       weak_factory_.GetWeakPtr(), callback_id));
-}
-
-void AboutHandler::HandleGetTargetChannel(const base::ListValue* args) {
+void AboutHandler::HandleGetChannelInfo(const base::ListValue* args) {
   CHECK_EQ(1U, args->GetSize());
   std::string callback_id;
   CHECK(args->GetString(0, &callback_id));
   version_updater_->GetChannel(
-      false, base::Bind(&AboutHandler::OnGetChannelReady,
-                        weak_factory_.GetWeakPtr(), callback_id));
+      true /* get current channel */,
+      base::Bind(&AboutHandler::OnGetCurrentChannel, weak_factory_.GetWeakPtr(),
+                 callback_id));
 }
 
-void AboutHandler::OnGetChannelReady(std::string callback_id,
-                                     const std::string& channel) {
-  ResolveJavascriptCallback(base::StringValue(callback_id),
-                            base::StringValue(channel));
+void AboutHandler::OnGetCurrentChannel(std::string callback_id,
+                                       const std::string& current_channel) {
+  version_updater_->GetChannel(
+      false /* get target channel */,
+      base::Bind(&AboutHandler::OnGetTargetChannel, weak_factory_.GetWeakPtr(),
+                 callback_id, current_channel));
+}
+
+void AboutHandler::OnGetTargetChannel(std::string callback_id,
+                                      const std::string& current_channel,
+                                      const std::string& target_channel) {
+  std::unique_ptr<base::DictionaryValue> channel_info(
+      new base::DictionaryValue);
+  channel_info->SetString("currentChannel", current_channel);
+  channel_info->SetString("targetChannel", target_channel);
+  channel_info->SetBoolean("canChangeChannel",
+                           CanChangeChannel(Profile::FromWebUI(web_ui())));
+
+  ResolveJavascriptCallback(base::StringValue(callback_id), *channel_info);
 }
 
 void AboutHandler::HandleRequestUpdate(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index 6f275d0..3de70b6 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -91,20 +91,24 @@
   // Sets the release track version.
   void HandleSetChannel(const base::ListValue* args);
 
-  // Checks for and applies update, triggered by JS.
-  void HandleRequestUpdate(const base::ListValue* args);
-
   // Retrieves OS, ARC and firmware versions.
   void HandleGetVersionInfo(const base::ListValue* args);
   void OnGetVersionInfoReady(
       std::string callback_id,
       std::unique_ptr<base::DictionaryValue> version_info);
 
-  void HandleGetCurrentChannel(const base::ListValue* args);
-  void HandleGetTargetChannel(const base::ListValue* args);
-  // C++ callback for either of |HandleGetCurrentChannel| or
-  // |HandleGetTargetChannel|,
-  void OnGetChannelReady(std::string callback_id, const std::string& channel);
+  // Retrieves combined channel info.
+  void HandleGetChannelInfo(const base::ListValue* args);
+  // Callbacks for version_updater_->GetChannel calls.
+  void OnGetCurrentChannel(std::string callback_id,
+                           const std::string& current_channel);
+  void OnGetTargetChannel(std::string callback_id,
+                          const std::string& current_channel,
+                          const std::string& target_channel);
+
+  // Checks for and applies update, triggered by JS.
+  void HandleRequestUpdate(const base::ListValue* args);
+
 #endif
 
   // Checks for and applies update.
diff --git a/chrome/browser/ui/webui/settings/certificates_handler.cc b/chrome/browser/ui/webui/settings/certificates_handler.cc
index 516d476..16503d82 100644
--- a/chrome/browser/ui/webui/settings/certificates_handler.cc
+++ b/chrome/browser/ui/webui/settings/certificates_handler.cc
@@ -89,8 +89,8 @@
 
   bool operator()(const std::unique_ptr<base::Value>& a,
                   const std::unique_ptr<base::Value>& b) const {
-    DCHECK(a->GetType() == base::Value::TYPE_DICTIONARY);
-    DCHECK(b->GetType() == base::Value::TYPE_DICTIONARY);
+    DCHECK(a->GetType() == base::Value::Type::DICTIONARY);
+    DCHECK(b->GetType() == base::Value::Type::DICTIONARY);
     const base::DictionaryValue* a_dict;
     bool a_is_dictionary = a->GetAsDictionary(&a_dict);
     DCHECK(a_is_dictionary);
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index d21d63b1..ce039907 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -208,7 +208,6 @@
       printer_protocol + "://" + printer_address + "/" + printer_queue;
 
   std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
-  printer_id = printer->id();
   printer->set_display_name(printer_name);
   printer->set_description(printer_description);
   printer->set_manufacturer(printer_manufacturer);
@@ -216,23 +215,30 @@
   printer->set_uri(printer_uri);
   if (!printer_ppd_path.empty()) {
     printer->mutable_ppd_reference()->user_supplied_ppd_url = printer_ppd_path;
+    if (!ppd_provider_->CachePpd(printer->ppd_reference(),
+                                 base::FilePath(printer_ppd_path))) {
+      LOG(WARNING) << "PPD could not be stored in the cache";
+      OnAddPrinterError();
+      return;
+    }
   } else if (!printer_manufacturer.empty() && !printer_model.empty()) {
     Printer::PpdReference* ppd = printer->mutable_ppd_reference();
     ppd->effective_manufacturer = printer_manufacturer;
     ppd->effective_model = printer_model;
   }
 
-  chromeos::DebugDaemonClient* client =
-      chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
-  client->CupsAddPrinter(
-      printer_id,                               // record id
-      printer_uri,                              // uri
-      printer_ppd_path,                         // ppd location
-      printer_ppd_path.empty() ? true : false,  // ipp everywhere
-      base::Bind(&CupsPrintersHandler::OnAddedPrinter,
-                 weak_factory_.GetWeakPtr(), base::Passed(std::move(printer))),
-      base::Bind(&CupsPrintersHandler::OnAddPrinterError,
-                 weak_factory_.GetWeakPtr()));
+  if (printer->IsIppEverywhere()) {
+    AddPrinterToCups(std::move(printer), base::FilePath(), true);
+    return;
+  }
+
+  // We need to save a reference to members of printer since we transfer
+  // ownership in the bind call.
+  const Printer::PpdReference& ppd_reference = printer->ppd_reference();
+  ppd_provider_->Resolve(
+      ppd_reference,
+      base::Bind(&CupsPrintersHandler::OnPPDResolved,
+                 weak_factory_.GetWeakPtr(), base::Passed(&printer)));
 }
 
 void CupsPrintersHandler::OnAddedPrinter(std::unique_ptr<Printer> printer,
@@ -391,5 +397,34 @@
                          base::StringValue("on-printer-discovery-done"));
 }
 
+void CupsPrintersHandler::AddPrinterToCups(std::unique_ptr<Printer> printer,
+                                           const base::FilePath& ppd_path,
+                                           bool ipp_everywhere) {
+  std::string printer_id = printer->id();
+  std::string printer_uri = printer->uri();
+
+  chromeos::DebugDaemonClient* client =
+      chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+  client->CupsAddPrinter(
+      printer_id, printer_uri, ppd_path.value(), ipp_everywhere,
+      base::Bind(&CupsPrintersHandler::OnAddedPrinter,
+                 weak_factory_.GetWeakPtr(), base::Passed(&printer)),
+      base::Bind(&CupsPrintersHandler::OnAddPrinterError,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void CupsPrintersHandler::OnPPDResolved(
+    std::unique_ptr<Printer> printer,
+    printing::PpdProvider::CallbackResultCode result,
+    base::FilePath path) {
+  if (result != printing::PpdProvider::SUCCESS) {
+    // TODO(skau): Add appropriate failure modes crbug.com/670068.
+    OnAddPrinterError();
+    return;
+  }
+
+  AddPrinterToCups(std::move(printer), path, false /* never ipp everywhere */);
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index b959644..d62d8ac 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chromeos/printing/ppd_provider.h"
@@ -88,6 +89,20 @@
   void OnPrintersFound(const std::vector<Printer>& printers) override;
   void OnDiscoveryDone() override;
 
+  // Invokes debugd to add the printer to CUPS.  If |ipp_everywhere| is true,
+  // automatic configuration will be attempted  and |ppd_path| is ignored.
+  // |ppd_path| is the path to a Postscript Printer Description file that will
+  // be used to configure the printer capabilities.  This file must be in
+  // Downloads or the PPD Cache.
+  void AddPrinterToCups(std::unique_ptr<Printer> printer,
+                        const base::FilePath& ppd_path,
+                        bool ipp_everywhere);
+
+  // Callback for PpdProvider::ResolveCallback.
+  void OnPPDResolved(std::unique_ptr<Printer> printer,
+                     printing::PpdProvider::CallbackResultCode result,
+                     base::FilePath ppd_path);
+
   std::unique_ptr<chromeos::PrinterDiscoverer> printer_discoverer_;
   std::unique_ptr<chromeos::printing::PpdProvider> ppd_provider_;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
index 3fe6e66e..856e4e5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
 
+#include "ash/common/new_window_controller.h"
 #include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/values.h"
@@ -67,7 +67,7 @@
 
 void KeyboardHandler::HandleShowKeyboardShortcutsOverlay(
     const base::ListValue* args) const {
-  ash::WmShell::Get()->new_window_client()->ShowKeyboardOverlay();
+  ash::WmShell::Get()->new_window_controller()->ShowKeyboardOverlay();
 }
 
 void KeyboardHandler::UpdateShowKeys() {
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chrome/browser/ui/webui/settings/search_engines_handler.cc
index 2b51d97c..aea264e 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -28,6 +28,8 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_ui.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/management_policy.h"
 #include "extensions/common/extension.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -215,9 +217,9 @@
   dict->SetString(
       "keyword",
       table_model->GetText(index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
-  dict->SetString("url",
-                  template_url->url_ref().DisplayURL(
-                      UIThreadSearchTermsData(Profile::FromWebUI(web_ui()))));
+  Profile* profile = Profile::FromWebUI(web_ui());
+  dict->SetString("url", template_url->url_ref().DisplayURL(
+                             UIThreadSearchTermsData(profile)));
   dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
   GURL icon_url = template_url->favicon_url();
   if (icon_url.is_valid())
@@ -235,12 +237,17 @@
   if (type == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION ||
       type == TemplateURL::OMNIBOX_API_EXTENSION) {
     const extensions::Extension* extension =
-        extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
-            ->GetExtensionById(template_url->GetExtensionId(),
-                               extensions::ExtensionRegistry::EVERYTHING);
+        extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
+            template_url->GetExtensionId(),
+            extensions::ExtensionRegistry::EVERYTHING);
     if (extension) {
-      dict->Set("extension",
-                extensions::util::GetExtensionInfo(extension).release());
+      std::unique_ptr<base::DictionaryValue> ext_info =
+          extensions::util::GetExtensionInfo(extension);
+      ext_info->SetBoolean("canBeDisabled",
+                           !extensions::ExtensionSystem::Get(profile)
+                                ->management_policy()
+                                ->MustRemainEnabled(extension, nullptr));
+      dict->Set("extension", ext_info.release());
     }
   }
   return dict;
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index efbd788..9e47611 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -398,9 +398,9 @@
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile_);
 
-  std::string setting = content_settings::ContentSettingToString(
-      map->GetDefaultContentSetting(content_type, nullptr));
-  ResolveJavascriptCallback(*callback_id, base::StringValue(setting));
+  base::DictionaryValue category;
+  site_settings::GetContentCategorySetting(map, content_type, &category);
+  ResolveJavascriptCallback(*callback_id, category);
 }
 
 void SiteSettingsHandler::HandleGetExceptionList(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 8fb819a0..a1e948e 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -21,6 +21,8 @@
 namespace {
 
 const char kCallbackId[] = "test-callback-id";
+const char kSetting[] = "setting";
+const char kSource[] = "source";
 
 }
 
@@ -40,7 +42,8 @@
   content::TestWebUI* web_ui() { return &web_ui_; }
   SiteSettingsHandler* handler() { return &handler_; }
 
-  void ValidateDefault(const std::string& expected_default,
+  void ValidateDefault(const std::string& expected_setting,
+                       const std::string& expected_source,
                        size_t expected_total_calls) {
     EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
 
@@ -55,9 +58,14 @@
     ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
     ASSERT_TRUE(success);
 
-    std::string default_value;
-    ASSERT_TRUE(data.arg3()->GetAsString(&default_value));
-    EXPECT_EQ(expected_default, default_value);
+    const base::DictionaryValue* default_value = nullptr;
+    ASSERT_TRUE(data.arg3()->GetAsDictionary(&default_value));
+    std::string setting;
+    ASSERT_TRUE(default_value->GetString(kSetting, &setting));
+    EXPECT_EQ(expected_setting, setting);
+    std::string source;
+    if (default_value->GetString(kSource, &source))
+      EXPECT_EQ(expected_source, source);
   }
 
   void ValidateOrigin(
@@ -208,7 +216,7 @@
   getArgs.AppendString(kCallbackId);
   getArgs.AppendString("notifications");
   handler()->HandleGetDefaultValueForContentType(&getArgs);
-  ValidateDefault("ask", 1U);
+  ValidateDefault("ask", "default", 1U);
 
   // Set the default to 'Blocked'.
   base::ListValue setArgs;
@@ -220,7 +228,7 @@
 
   // Verify that the default has been set to 'Blocked'.
   handler()->HandleGetDefaultValueForContentType(&getArgs);
-  ValidateDefault("block", 3U);
+  ValidateDefault("block", "default", 3U);
 }
 
 TEST_F(SiteSettingsHandlerTest, Origins) {
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index 9587dbd..6d310f4 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -160,7 +160,7 @@
   EXPECT_EQ("sync.confirmation.setUserImageURL",
             web_ui()->call_data()[0]->function_name());
   EXPECT_TRUE(
-      web_ui()->call_data()[0]->arg1()->IsType(base::Value::TYPE_STRING));
+      web_ui()->call_data()[0]->arg1()->IsType(base::Value::Type::STRING));
   std::string passed_picture_url;
   EXPECT_TRUE(
       web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url));
@@ -204,7 +204,7 @@
   EXPECT_EQ("sync.confirmation.setUserImageURL",
             web_ui()->call_data()[1]->function_name());
   EXPECT_TRUE(
-      web_ui()->call_data()[1]->arg1()->IsType(base::Value::TYPE_STRING));
+      web_ui()->call_data()[1]->arg1()->IsType(base::Value::Type::STRING));
   std::string passed_picture_url;
   EXPECT_TRUE(
       web_ui()->call_data()[1]->arg1()->GetAsString(&passed_picture_url));
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index c1a6654..ef0a5fed 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -273,6 +273,20 @@
   }
 }
 
+void GetContentCategorySetting(
+    const HostContentSettingsMap* map,
+    ContentSettingsType content_type,
+    base::DictionaryValue* object) {
+  std::string provider;
+  std::string setting = content_settings::ContentSettingToString(
+      map->GetDefaultContentSetting(content_type, &provider));
+  DCHECK(!setting.empty());
+
+  object->SetString(site_settings::kSetting, setting);
+  if (provider != "default")
+    object->SetString(site_settings::kSource, provider);
+}
+
 void GetPolicyAllowedUrls(
     ContentSettingsType type,
     std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions,
diff --git a/chrome/browser/ui/webui/site_settings_helper.h b/chrome/browser/ui/webui/site_settings_helper.h
index 4bb415a..bc3b51a 100644
--- a/chrome/browser/ui/webui/site_settings_helper.h
+++ b/chrome/browser/ui/webui/site_settings_helper.h
@@ -88,6 +88,14 @@
     const std::string* filter,
     base::ListValue* exceptions);
 
+// Fills in object saying what the current settings is for the category (such as
+// enabled or blocked) and the source of that setting (such preference, policy,
+// or extension).
+void GetContentCategorySetting(
+    const HostContentSettingsMap* map,
+    ContentSettingsType content_type,
+    base::DictionaryValue* object);
+
 // Returns exceptions constructed from the policy-set allowed URLs
 // for the content settings |type| mic or camera.
 void GetPolicyAllowedUrls(
diff --git a/chrome/browser/win/settings_app_monitor.cc b/chrome/browser/win/settings_app_monitor.cc
index df7fecb6..0ef2982 100644
--- a/chrome/browser/win/settings_app_monitor.cc
+++ b/chrome/browser/win/settings_app_monitor.cc
@@ -34,6 +34,21 @@
 namespace shell_integration {
 namespace win {
 
+// Each item represent one UI element in the Settings App.
+enum class ElementType {
+  // The "Web browser" element in the "Default apps" pane.
+  DEFAULT_BROWSER,
+  // The element representing a browser in the "Choose an app" popup.
+  BROWSER_BUTTON,
+  // The button labeled "Check it out" that leaves Edge as the default browser.
+  CHECK_IT_OUT,
+  // The button labeled "Switch Anyway" that dismisses the Edge promo.
+  SWITCH_ANYWAY,
+  // Any other element.
+  UNKNOWN,
+};
+
+
 // SettingsAppMonitor::Context -------------------------------------------------
 
 // The guts of the monitor which runs on a dedicated thread in the
@@ -78,6 +93,9 @@
   // Handles the invocation of an element in the browser chooser.
   void HandleBrowserChosen(const base::string16& browser_name);
 
+  // Handles the invocation of an element in the Edge promo.
+  void HandlePromoChoiceMade(bool accept_promo);
+
   // Returns an event handler for all event types of interest.
   base::win::ScopedComPtr<IUnknown> GetEventHandler();
 
@@ -107,8 +125,8 @@
   // The event handler.
   base::win::ScopedComPtr<IUnknown> event_handler_;
 
-  // State to suppress duplicate OnAppFocused notifications.
-  bool observed_app_focused_ = false;
+  // State to suppress duplicate "focus changed" events.
+  ElementType last_focused_element_ = ElementType::UNKNOWN;
 
   // Weak pointers to the context are given to event handlers.
   base::WeakPtrFactory<Context> weak_ptr_factory_;
@@ -136,7 +154,8 @@
   // Initializes the object. Events will be dispatched back to |context| via
   // |context_runner|.
   void Initialize(scoped_refptr<base::SingleThreadTaskRunner> context_runner,
-                  const base::WeakPtr<SettingsAppMonitor::Context>& context);
+                  const base::WeakPtr<SettingsAppMonitor::Context>& context,
+                  base::win::ScopedComPtr<IUIAutomation> automation);
 
   // IUIAutomationEventHandler:
   STDMETHOD(HandleAutomationEvent)(IUIAutomationElement *sender,
@@ -152,6 +171,8 @@
   // The monitor context that owns this event handler.
   base::WeakPtr<SettingsAppMonitor::Context> context_;
 
+  base::win::ScopedComPtr<IUIAutomation> automation_;
+
   DISALLOW_COPY_AND_ASSIGN(EventHandler);
 };
 
@@ -176,45 +197,83 @@
   return base::string16(V_BSTR(var.ptr()));
 }
 
-enum class ElementType {
-  // The "Web browser" element in the "Default apps" pane.
-  DEFAULT_BROWSER,
-  // The element representing a browser in the "Choose an app" popup.
-  BROWSER_BUTTON,
-  // Any other element.
-  UNKNOWN,
-};
-
-ElementType DetectElementType(IUIAutomationElement* sender) {
-  base::string16 aid(GetCachedBstrValue(sender, UIA_AutomationIdPropertyId));
-  if (aid == L"SystemSettings_DefaultApps_Browser_Button")
-    return ElementType::DEFAULT_BROWSER;
-  if (base::MatchPattern(aid, L"SystemSettings_DefaultApps_Browser_*_Button"))
-    return ElementType::BROWSER_BUTTON;
-  return ElementType::UNKNOWN;
-}
-
 // Configures a cache request so that it includes all properties needed by
 // DetectElementType() to detect the elements of interest.
 void ConfigureCacheRequest(IUIAutomationCacheRequest* cache_request) {
   DCHECK(cache_request);
   cache_request->AddProperty(UIA_AutomationIdPropertyId);
   cache_request->AddProperty(UIA_NamePropertyId);
-#if ENABLE_DLOG
   cache_request->AddProperty(UIA_ClassNamePropertyId);
+#if DCHECK_IS_ON()
   cache_request->AddProperty(UIA_ControlTypePropertyId);
   cache_request->AddProperty(UIA_IsPeripheralPropertyId);
   cache_request->AddProperty(UIA_ProcessIdPropertyId);
   cache_request->AddProperty(UIA_ValueValuePropertyId);
   cache_request->AddProperty(UIA_RuntimeIdPropertyId);
-#endif  // ENABLE_DLOG
+#endif  // DCHECK_IS_ON()
+}
+
+// Helper function to get the parent element with class name "Flyout". Used to
+// determine the |element|'s type.
+base::string16 GetFlyoutParentAutomationId(IUIAutomation* automation,
+                                           IUIAutomationElement* element) {
+  // Create a condition that will include only elements with the right class
+  // name in the tree view.
+  base::win::ScopedVariant class_name(L"Flyout");
+  base::win::ScopedComPtr<IUIAutomationCondition> condition;
+  HRESULT result = automation->CreatePropertyCondition(
+      UIA_ClassNamePropertyId, class_name, condition.Receive());
+  if (FAILED(result))
+    return base::string16();
+
+  base::win::ScopedComPtr<IUIAutomationTreeWalker> tree_walker;
+  result = automation->CreateTreeWalker(condition.get(), tree_walker.Receive());
+  if (FAILED(result))
+    return base::string16();
+
+  base::win::ScopedComPtr<IUIAutomationCacheRequest> cache_request;
+  result = automation->CreateCacheRequest(cache_request.Receive());
+  if (FAILED(result))
+    return base::string16();
+  ConfigureCacheRequest(cache_request.get());
+
+  // From MSDN, NormalizeElementBuildCache() "Retrieves the ancestor element
+  // nearest to the specified Microsoft UI Automation element in the tree view".
+  IUIAutomationElement* flyout_element = nullptr;
+  result = tree_walker->NormalizeElementBuildCache(element, cache_request.get(),
+                                                   &flyout_element);
+  if (FAILED(result) || !flyout_element)
+    return base::string16();
+
+  return GetCachedBstrValue(flyout_element, UIA_AutomationIdPropertyId);
+}
+
+ElementType DetectElementType(IUIAutomation* automation,
+                              IUIAutomationElement* sender) {
+  DCHECK(automation);
+  DCHECK(sender);
+  base::string16 aid(GetCachedBstrValue(sender, UIA_AutomationIdPropertyId));
+  if (aid == L"SystemSettings_DefaultApps_Browser_Button")
+    return ElementType::DEFAULT_BROWSER;
+  if (aid == L"SystemSettings_DefaultApps_Browser_App0_HyperlinkButton")
+    return ElementType::SWITCH_ANYWAY;
+  if (base::MatchPattern(aid, L"SystemSettings_DefaultApps_Browser_*_Button")) {
+    // This element type depends on the automation id of one of its ancestors.
+    base::string16 automation_id =
+        GetFlyoutParentAutomationId(automation, sender);
+    if (automation_id == L"settingsFlyout")
+      return ElementType::CHECK_IT_OUT;
+    else if (automation_id == L"DefaultAppsFlyoutPresenter")
+      return ElementType::BROWSER_BUTTON;
+  }
+  return ElementType::UNKNOWN;
 }
 
 
 // Debug logging utility functions ---------------------------------------------
 
 bool GetCachedBoolValue(IUIAutomationElement* element, PROPERTYID property_id) {
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   base::win::ScopedVariant var;
 
   if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
@@ -229,14 +288,14 @@
   }
 
   return V_BOOL(var.ptr()) != 0;
-#else   // ENABLE_DLOG
+#else   // DCHECK_IS_ON()
   return false;
-#endif  // !ENABLE_DLOG
+#endif  // !DCHECK_IS_ON()
 }
 
 int32_t GetCachedInt32Value(IUIAutomationElement* element,
                             PROPERTYID property_id) {
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   base::win::ScopedVariant var;
 
   if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
@@ -251,15 +310,15 @@
   }
 
   return V_I4(var.ptr());
-#else   // ENABLE_DLOG
+#else   // DCHECK_IS_ON()
   return 0;
-#endif  // !ENABLE_DLOG
+#endif  // !DCHECK_IS_ON()
 }
 
 std::vector<int32_t> GetCachedInt32ArrayValue(IUIAutomationElement* element,
                                               PROPERTYID property_id) {
   std::vector<int32_t> values;
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   base::win::ScopedVariant var;
 
   if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
@@ -286,23 +345,23 @@
   SafeArrayAccessData(array, reinterpret_cast<void**>(&data));
   values.assign(data, data + upper_bound + 1);
   SafeArrayUnaccessData(array);
-#endif  // ENABLE_DLOG
+#endif  // DCHECK_IS_ON()
   return values;
 }
 
 std::string IntArrayToString(const std::vector<int32_t>& values) {
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   std::vector<std::string> value_strings;
   std::transform(values.begin(), values.end(),
                  std::back_inserter(value_strings), &base::IntToString);
   return base::JoinString(value_strings, ", ");
-#else   // ENABLE_DLOG
+#else   // DCHECK_IS_ON()
   return std::string();
-#endif  // !ENABLE_DLOG
+#endif  // !DCHECK_IS_ON()
 }
 
 const char* GetEventName(EVENTID event_id) {
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   switch (event_id) {
     case UIA_ToolTipOpenedEventId:
       return "UIA_ToolTipOpenedEventId";
@@ -373,12 +432,12 @@
     case UIA_TextEdit_ConversionTargetChangedEventId:
       return "UIA_TextEdit_ConversionTargetChangedEventId";
   }
-#endif  // ENABLE_DLOG
+#endif  // DCHECK_IS_ON()
   return "";
 }
 
 const char* GetControlType(long control_type) {
-#if ENABLE_DLOG
+#if DCHECK_IS_ON()
   switch (control_type) {
     case UIA_ButtonControlTypeId:
       return "UIA_ButtonControlTypeId";
@@ -463,7 +522,7 @@
     case UIA_AppBarControlTypeId:
       return "UIA_AppBarControlTypeId";
   }
-#endif  // ENABLE_DLOG
+#endif  // DCHECK_IS_ON()
   return "";
 }
 
@@ -476,9 +535,11 @@
 
 void SettingsAppMonitor::Context::EventHandler::Initialize(
     scoped_refptr<base::SingleThreadTaskRunner> context_runner,
-    const base::WeakPtr<SettingsAppMonitor::Context>& context) {
+    const base::WeakPtr<SettingsAppMonitor::Context>& context,
+    base::win::ScopedComPtr<IUIAutomation> automation) {
   context_runner_ = std::move(context_runner);
   context_ = context;
+  automation_ = automation;
 }
 
 HRESULT SettingsAppMonitor::Context::EventHandler::HandleAutomationEvent(
@@ -499,7 +560,7 @@
            << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue(
                                       sender, UIA_RuntimeIdPropertyId));
 
-  switch (DetectElementType(sender)) {
+  switch (DetectElementType(automation_.get(), sender)) {
     case ElementType::DEFAULT_BROWSER:
       context_runner_->PostTask(
           FROM_HERE,
@@ -517,6 +578,18 @@
       }
       break;
     }
+    case ElementType::SWITCH_ANYWAY:
+      context_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&SettingsAppMonitor::Context::HandlePromoChoiceMade,
+                     context_, false));
+      break;
+    case ElementType::CHECK_IT_OUT:
+      context_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&SettingsAppMonitor::Context::HandlePromoChoiceMade,
+                     context_, true));
+      break;
     case ElementType::UNKNOWN:
       break;
   }
@@ -549,6 +622,7 @@
   return S_OK;
 }
 
+
 // SettingsAppMonitor::Context -------------------------------------------------
 
 base::WeakPtr<SettingsAppMonitor::Context>
@@ -604,14 +678,18 @@
     base::win::ScopedComPtr<IUIAutomationElement> sender) {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  if (DetectElementType(sender.get()) == ElementType::DEFAULT_BROWSER) {
-    if (!observed_app_focused_) {
-      observed_app_focused_ = true;
-      monitor_runner_->PostTask(
-          FROM_HERE, base::Bind(&SettingsAppMonitor::OnAppFocused, monitor_));
-    }
-  } else {
-    observed_app_focused_ = false;
+  // Duplicate focus changed events are suppressed.
+  ElementType element_type = DetectElementType(automation_.get(), sender.get());
+  if (last_focused_element_ == element_type)
+    return;
+  last_focused_element_ = element_type;
+
+  if (element_type == ElementType::DEFAULT_BROWSER) {
+    monitor_runner_->PostTask(
+        FROM_HERE, base::Bind(&SettingsAppMonitor::OnAppFocused, monitor_));
+  } else if (element_type == ElementType::CHECK_IT_OUT) {
+    monitor_runner_->PostTask(
+        FROM_HERE, base::Bind(&SettingsAppMonitor::OnPromoFocused, monitor_));
   }
 }
 
@@ -629,6 +707,13 @@
       base::Bind(&SettingsAppMonitor::OnBrowserChosen, monitor_, browser_name));
 }
 
+void SettingsAppMonitor::Context::HandlePromoChoiceMade(bool accept_promo) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  monitor_runner_->PostTask(
+      FROM_HERE, base::Bind(&SettingsAppMonitor::OnPromoChoiceMade, monitor_,
+                            accept_promo));
+}
+
 base::win::ScopedComPtr<IUnknown>
 SettingsAppMonitor::Context::GetEventHandler() {
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -636,7 +721,8 @@
     ATL::CComObject<EventHandler>* obj = nullptr;
     HRESULT result = ATL::CComObject<EventHandler>::CreateInstance(&obj);
     if (SUCCEEDED(result)) {
-      obj->Initialize(task_runner_, weak_ptr_factory_.GetWeakPtr());
+      obj->Initialize(task_runner_, weak_ptr_factory_.GetWeakPtr(),
+                      automation_);
       obj->QueryInterface(event_handler_.Receive());
     }
   }
@@ -742,5 +828,15 @@
   delegate_->OnBrowserChosen(browser_name);
 }
 
+void SettingsAppMonitor::OnPromoFocused() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delegate_->OnPromoFocused();
+}
+
+void SettingsAppMonitor::OnPromoChoiceMade(bool accept_promo) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delegate_->OnPromoChoiceMade(accept_promo);
+}
+
 }  // namespace win
 }  // namespace shell_integration
diff --git a/chrome/browser/win/settings_app_monitor.h b/chrome/browser/win/settings_app_monitor.h
index be6548e..9e6ee68 100644
--- a/chrome/browser/win/settings_app_monitor.h
+++ b/chrome/browser/win/settings_app_monitor.h
@@ -42,6 +42,12 @@
 
     // Invoked when the user has chosen a particular Web browser.
     virtual void OnBrowserChosen(const base::string16& browser_name) = 0;
+
+    // Invoked when the Edge promo has received keyboard focus.
+    virtual void OnPromoFocused() = 0;
+
+    // Invoked when the user interacts with the Edge promo.
+    virtual void OnPromoChoiceMade(bool accept_promo) = 0;
   };
 
   // |delegate| must outlive the monitor.
@@ -56,6 +62,8 @@
   void OnAppFocused();
   void OnChooserInvoked();
   void OnBrowserChosen(const base::string16& browser_name);
+  void OnPromoFocused();
+  void OnPromoChoiceMade(bool accept_promo);
 
   base::ThreadChecker thread_checker_;
   Delegate* delegate_;
diff --git a/chrome/browser/win/titlebar_config.cc b/chrome/browser/win/titlebar_config.cc
index afd8806a..8541260 100644
--- a/chrome/browser/win/titlebar_config.cc
+++ b/chrome/browser/win/titlebar_config.cc
@@ -14,7 +14,7 @@
   // mode so that we can correctly draw the caption buttons on the left in RTL
   // mode. But they require a different style and color selection that isn't
   // currently implemented.
-  return !ui::NativeThemeWin::instance()->IsUsingHighContrastTheme() &&
+  return !ui::NativeThemeWin::IsUsingHighContrastTheme() &&
          base::CommandLine::ForCurrentProcess()->HasSwitch(
              switches::kWindows10CustomTitlebar) &&
          base::win::GetVersion() >= base::win::VERSION_WIN10;
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index c3ac4847..d85b5be 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -148,8 +148,14 @@
       ]
     }
     if (is_chromeos) {
-      sources += [ "$root_gen_dir/ui/file_manager/file_manager_resources.pak" ]
-      deps += [ "//ui/file_manager:resources" ]
+      sources += [
+        "$root_gen_dir/components/chrome_apps/chrome_apps_resources.pak",
+        "$root_gen_dir/ui/file_manager/file_manager_resources.pak",
+      ]
+      deps += [
+        "//components/chrome_apps:resources",
+        "//ui/file_manager:resources",
+      ]
     }
     if (enable_extensions) {
       sources += [
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index de6f3b74..e6858ad4 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -193,7 +193,7 @@
     "//components/nacl/common:process_type",
     "//components/nacl/common:switches",
     "//components/ntp_tiles",
-    "//components/offline_pages:switches",
+    "//components/offline_pages/core:switches",
     "//components/omnibox/common",
     "//components/password_manager/content/common:mojo_interfaces",
     "//components/password_manager/core/common",
@@ -446,6 +446,7 @@
 
   if (is_win) {
     public_deps += [
+      "//chrome/install_static:install_static_util",
       "//components/dom_distiller/core",  # Needed by chrome_content_client.cc.
       "//third_party/wtl",
     ]
@@ -669,6 +670,7 @@
   sources = [
     "conflicts/module_database_win.mojom",
     "conflicts/module_event_win.mojom",
+    "field_trial_recorder.mojom",
     "network_diagnostics.mojom",
     "resource_usage_reporter.mojom",
     "shell_handler_win.mojom",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 1a6e250a..a55a1ce 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/grit",
+  "+chrome/install_static",
   "+chromeos",  # For chromeos_switches.h
   "+components/autofill/content/common",
   "+components/autofill/core/common",
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index 20ba8876..f8eeda9e 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -26,6 +26,7 @@
 #include "build/build_config.h"
 #include "chrome/common/child_process_logging.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/crash_keys.h"
@@ -272,6 +273,13 @@
 }
 
 bool GetCommandLinePepperFlash(content::PepperPluginInfo* plugin) {
+#if defined(OS_CHROMEOS)
+  // On Chrome OS, we cannot test component flash updates reliably unless we
+  // guarantee that the component updated flash plugin will be used.
+  if (base::FeatureList::IsEnabled(features::kComponentFlashOnly))
+    return false;
+#endif  // defined(OS_CHROMEOS)
+
   const base::CommandLine::StringType flash_path =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
           switches::kPpapiFlashPath);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 34bec2b..4169a1a 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -230,6 +230,14 @@
 // Enables or disables emoji, handwriting and voice input on opt-in IME menu.
 const base::Feature kEHVInputOnImeMenu{"EmojiHandwritingVoiceInput",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables or disables flash component updates on Chrome OS.
+const base::Feature kCrosCompUpdates{"CrosCompUpdates",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enable or disable using only the component flash plugin on Chrome OS.
+const base::Feature kComponentFlashOnly{"ComponentFlashOnly",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_CHROMEOS)
 
 }  // namespace features
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 4155a53..1fc8eda 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -133,6 +133,10 @@
 extern const base::Feature kQuickUnlockPin;
 
 extern const base::Feature kEHVInputOnImeMenu;
+
+extern const base::Feature kCrosCompUpdates;
+
+extern const base::Feature kComponentFlashOnly;
 #endif  // defined(OS_CHROMEOS)
 
 // DON'T ADD RANDOM STUFF HERE. Put it in the main section above in
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index f3ef99f5..769c213e 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -36,10 +36,6 @@
 // extension API.
 const char kAllowHttpScreenCapture[] = "allow-http-screen-capture";
 
-// Enables TLS/SSL errors on localhost to be ignored (no interstitial,
-// no blocking of requests).
-const char kAllowInsecureLocalhost[] = "allow-insecure-localhost";
-
 // Don't block outdated plugins.
 const char kAllowOutdatedPlugins[]          = "allow-outdated-plugins";
 
@@ -724,22 +720,6 @@
 //   enabled: Guaranteed prerendering.
 const char kPrerenderFromOmniboxSwitchValueEnabled[] = "enabled";
 
-// Controls speculative prerendering of pages, and content prefetching. Both
-// are dispatched from <link rel=prefetch href=...> elements.
-const char kPrerenderMode[]                 = "prerender";
-
-// These are the values the kPrerenderMode switch may have, as in
-// "--prerender=disabled".
-//   disabled: No prerendering.
-const char kPrerenderModeSwitchValueDisabled[] = "disabled";
-//   enabled: Prerendering.
-const char kPrerenderModeSwitchValueEnabled[] = "enabled";
-//   prefetch: NoState Prefetch experiment.
-const char kPrerenderModeSwitchValuePrefetch[] = "prefetch";
-//   simple-load: experiment with prerendering disabled but metrics reporting
-//                enabled, to use as a reference for comparisons.
-const char kPrerenderModeSwitchValueSimpleLoad[] = "simple-load";
-
 // Use IPv6 only for privet HTTP.
 const char kPrivetIPv6Only[]                   = "privet-ipv6-only";
 
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 69bec54..7d4716c 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -34,7 +34,6 @@
 extern const char kAllowCrossOriginAuthPrompt[];
 extern const char kAllowFileAccess[];
 extern const char kAllowHttpScreenCapture[];
-extern const char kAllowInsecureLocalhost[];
 extern const char kAllowOutdatedPlugins[];
 extern const char kAllowRunningInsecureContent[];
 extern const char kAllowSilentPush[];
@@ -210,11 +209,6 @@
 extern const char kPrerenderFromOmniboxSwitchValueAuto[];
 extern const char kPrerenderFromOmniboxSwitchValueDisabled[];
 extern const char kPrerenderFromOmniboxSwitchValueEnabled[];
-extern const char kPrerenderMode[];
-extern const char kPrerenderModeSwitchValueDisabled[];
-extern const char kPrerenderModeSwitchValueEnabled[];
-extern const char kPrerenderModeSwitchValuePrefetch[];
-extern const char kPrerenderModeSwitchValueSimpleLoad[];
 extern const char kPrivetIPv6Only[];
 extern const char kProductVersion[];
 extern const char kProfileDirectory[];
diff --git a/chrome/common/cloud_print/cloud_print_helpers.cc b/chrome/common/cloud_print/cloud_print_helpers.cc
index 9a2a00a..09eef8a 100644
--- a/chrome/common/cloud_print/cloud_print_helpers.cc
+++ b/chrome/common/cloud_print/cloud_print_helpers.cc
@@ -192,7 +192,7 @@
   if (!message_value.get())
     return std::unique_ptr<base::DictionaryValue>();
 
-  if (!message_value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!message_value->IsType(base::Value::Type::DICTIONARY))
     return std::unique_ptr<base::DictionaryValue>();
 
   std::unique_ptr<base::DictionaryValue> response_dict(
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index ff0dc83..6e2eab0f 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -58,6 +58,8 @@
 
 const char kThirdPartyModulesLoaded[] = "third-party-modules-loaded";
 const char kThirdPartyModulesNotLoaded[] = "third-party-modules-not-loaded";
+
+const char kEnrolledToDomain[] = "enrolled-to-domain";
 #endif
 
 const char kInputEventFilterSendFailure[] = "input-event-filter-send-failure";
@@ -146,6 +148,7 @@
     { kHungRendererReason, kSmallSize },
     { kThirdPartyModulesLoaded, kSmallSize },
     { kThirdPartyModulesNotLoaded, kSmallSize },
+    { kEnrolledToDomain, kSmallSize },
 #endif
     { kInputEventFilterSendFailure, kSmallSize },
 #if defined(OS_CHROMEOS)
diff --git a/chrome/common/crash_keys.h b/chrome/common/crash_keys.h
index a09783d..f22d0f4 100644
--- a/chrome/common/crash_keys.h
+++ b/chrome/common/crash_keys.h
@@ -95,6 +95,9 @@
 // Third-party module crash keys are sent only on Windows.
 extern const char kThirdPartyModulesLoaded[];
 extern const char kThirdPartyModulesNotLoaded[];
+
+// Whether the machine is domain joined is only sent on Windows.
+extern const char kEnrolledToDomain[];
 #endif
 
 // Number of input event send IPC failures. Added to debug
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn
index 52c5a394..eb9fb9db 100644
--- a/chrome/common/extensions/BUILD.gn
+++ b/chrome/common/extensions/BUILD.gn
@@ -9,7 +9,7 @@
 assert(enable_extensions)
 
 json_features("extension_features_unittest") {
-  feature_class = "APIFeature"
+  feature_type = "APIFeature"
   provider_class = "UnittestFeatureProvider"
   sources = [
     "//chrome/test/data/extensions/extension_api_unittest/api_features.json",
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index b906af7..b419b51 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -232,7 +232,7 @@
 }
 
 json_features("api_features") {
-  feature_class = "APIFeature"
+  feature_type = "APIFeature"
   provider_class = "APIFeatureProvider"
   sources = [
     "../../../../extensions/common/api/_api_features.json",
@@ -241,7 +241,7 @@
 }
 
 json_features("permission_features") {
-  feature_class = "PermissionFeature"
+  feature_type = "PermissionFeature"
   provider_class = "PermissionFeatureProvider"
   sources = [
     "../../../../extensions/common/api/_permission_features.json",
@@ -250,7 +250,7 @@
 }
 
 json_features("manifest_features") {
-  feature_class = "ManifestFeature"
+  feature_type = "ManifestFeature"
   provider_class = "ManifestFeatureProvider"
   sources = [
     "../../../../extensions/common/api/_manifest_features.json",
@@ -259,7 +259,7 @@
 }
 
 json_features("behavior_features") {
-  feature_class = "BehaviorFeature"
+  feature_type = "BehaviorFeature"
   provider_class = "BehaviorFeatureProvider"
   sources = [
     "../../../../extensions/common/api/_behavior_features.json",
diff --git a/chrome/common/extensions/api/declarative_content.json b/chrome/common/extensions/api/declarative_content.json
index 9ae020e..3c5d180 100644
--- a/chrome/common/extensions/api/declarative_content.json
+++ b/chrome/common/extensions/api/declarative_content.json
@@ -84,7 +84,7 @@
       },
       {
         "id": "SetIcon",
-        "description": "Declarative event action that sets the n-<abbr title=\"device-independent pixel\">dip</abbr> square icon for the extension's $(ref:pageAction page action) or $(ref:browserAction browser action) while the corresponding conditions are met.  This action can be used without <a href=\"declare_permissions.html#host-permissions\">host permissions</a>, but the extension must have  page or browser action.<p>Exactly one of <code>imageData</code> or <code>path</code> must be specified.  Both are dictionaries mapping a number of pixels to an image representation. The image representation in <code>imageData</code> is an<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/ImageData\">ImageData</a> object, for example from a <code>&lt;canvas></code> element, while the image representation in <code>path</code> is the path to an image file relative to he extension's manifest.  If <code>scale</code> screen pixels fit into a device-independent pixel, the <code>scale * n</code> icon will be used.  If that scale is missing, another image will be resized to the needed size.",
+        "description": "Declarative event action that sets the n-<abbr title=\"device-independent pixel\">dip</abbr> square icon for the extension's $(ref:pageAction page action) or $(ref:browserAction browser action) while the corresponding conditions are met.  This action can be used without <a href=\"declare_permissions.html#host-permissions\">host permissions</a>, but the extension must have  page or browser action.<p>Exactly one of <code>imageData</code> or <code>path</code> must be specified.  Both are dictionaries mapping a number of pixels to an image representation. The image representation in <code>imageData</code> is an<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/ImageData\">ImageData</a> object, for example from a <code>&lt;canvas></code> element, while the image representation in <code>path</code> is the path to an image file relative to the extension's manifest.  If <code>scale</code> screen pixels fit into a device-independent pixel, the <code>scale * n</code> icon will be used.  If that scale is missing, another image will be resized to the needed size.",
         "type": "object",
         "properties": {
           "instanceType": {
diff --git a/chrome/common/extensions/api/input_ime/input_components_handler.cc b/chrome/common/extensions/api/input_ime/input_components_handler.cc
index 28a3d418..036a253 100644
--- a/chrome/common/extensions/api/input_ime/input_components_handler.cc
+++ b/chrome/common/extensions/api/input_ime/input_components_handler.cc
@@ -124,11 +124,11 @@
     // input_ime manifest specification.
     const base::Value* language_value = NULL;
     if (module_value->Get(keys::kLanguage, &language_value)) {
-      if (language_value->GetType() == base::Value::TYPE_STRING) {
+      if (language_value->GetType() == base::Value::Type::STRING) {
         std::string language_str;
         language_value->GetAsString(&language_str);
         languages.insert(language_str);
-      } else if (language_value->GetType() == base::Value::TYPE_LIST) {
+      } else if (language_value->GetType() == base::Value::Type::LIST) {
         const base::ListValue* language_list = NULL;
         language_value->GetAsList(&language_list);
         for (size_t j = 0; j < language_list->GetSize(); ++j) {
diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc
index 8ee8e50..7388b95e 100644
--- a/chrome/common/extensions/chrome_extensions_client.cc
+++ b/chrome/common/extensions/chrome_extensions_client.cc
@@ -5,6 +5,8 @@
 #include "chrome/common/extensions/chrome_extensions_client.h"
 
 #include <memory>
+#include <set>
+#include <string>
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -36,14 +38,9 @@
 #include "extensions/common/extension_icon_set.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/extensions_aliases.h"
-#include "extensions/common/features/api_feature.h"
-#include "extensions/common/features/behavior_feature.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_provider.h"
 #include "extensions/common/features/json_feature_provider_source.h"
-#include "extensions/common/features/manifest_feature.h"
-#include "extensions/common/features/permission_feature.h"
-#include "extensions/common/features/simple_feature.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handler.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
@@ -67,11 +64,6 @@
 
 const char kThumbsWhiteListedExtension[] = "khopmbdjffemhegeeobelklnbglcdgfh";
 
-template <class FeatureClass>
-SimpleFeature* CreateFeature() {
-  return new FeatureClass();
-}
-
 // Mirrors version_info::Channel for histograms.
 enum ChromeChannelForHistogram {
   CHANNEL_UNKNOWN,
diff --git a/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js b/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js
index 8447630..56faa35b 100644
--- a/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js
+++ b/chrome/common/extensions/docs/examples/api/webNavigation/basic/navigation_collector.js
@@ -102,7 +102,7 @@
  * * SERVER_REDIRECT: Redirected by the server via a 301/302 response.
  *
  * * FORWARD_BACK: User used the forward or back buttons to navigate through
- *   her browsing history.
+ *   their browsing history.
  *
  * @enum {string}
  */
diff --git a/chrome/common/extensions/docs/examples/apps/hello-php/lib/lightopenid/openid.php b/chrome/common/extensions/docs/examples/apps/hello-php/lib/lightopenid/openid.php
index 2f5b0ffd..4fa6cdc 100644
--- a/chrome/common/extensions/docs/examples/apps/hello-php/lib/lightopenid/openid.php
+++ b/chrome/common/extensions/docs/examples/apps/hello-php/lib/lightopenid/openid.php
@@ -104,7 +104,7 @@
         case 'identity':
             # We return claimed_id instead of identity,
             # because the developer should see the claimed identifier,
-            # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+            # i.e. what they set as identity, not the op-local identifier (which is what we verify)
             return $this->claimed_id;
         case 'trustRoot':
         case 'realm':
diff --git a/chrome/common/extensions/docs/templates/articles/extensions_index.html b/chrome/common/extensions/docs/templates/articles/extensions_index.html
index 6948d000..529a4dd3 100644
--- a/chrome/common/extensions/docs/templates/articles/extensions_index.html
+++ b/chrome/common/extensions/docs/templates/articles/extensions_index.html
@@ -61,9 +61,5 @@
 
 <p>
   <a href="http://www.youtube.com/view_play_list?p=CA101D6A85FE9D4B">Technical videos</a> <br>
-  <a href="http://www.youtube.com/view_play_list?p=38DF05697DE372B1">Developer snapshots</a> (below)
+  <a href="http://www.youtube.com/view_play_list?p=38DF05697DE372B1">Developer snapshots</a>
 </p>
-
-<div class="video-container">
-  <iframe title="YouTube video player" width="640" height="390" src="//www.youtube.com/embed/wRDPTnY3yO8?rel=0" frameborder="0" allowfullscreen=""></iframe>
-</div>
diff --git a/chrome/common/extensions/docs/templates/articles/hosting.html b/chrome/common/extensions/docs/templates/articles/hosting.html
index 9726c82..ac80368 100644
--- a/chrome/common/extensions/docs/templates/articles/hosting.html
+++ b/chrome/common/extensions/docs/templates/articles/hosting.html
@@ -13,7 +13,7 @@
 As of Chrome 44, no external installs are allowed from a path to a local .crx on Mac
 (see
 <a href="http://blog.chromium.org/2015/05/continuing-to-protect-chrome-users-from.html">Continuing to protect Chrome users from malicious extensions</a>).
-</p> 
+</p>
 
 <p>
 This page tells you how to host <code>.crx</code> files
@@ -21,9 +21,7 @@
 If you distribute your extension, app, or theme solely through the
 <a href="http://chrome.google.com/webstore">Chrome Web Store</a>,
 you don't need this page.
-Instead, consult the
-<a href="https://support.google.com/chrome_webstore/">store help</a> and
-<a href="http://code.google.com/chrome/webstore/index">developer documentation</a>.
+Instead, consult the <a href="/webstore">store developer documentation</a>.
 </p>
 
 <p>
diff --git a/chrome/common/extensions/docs/templates/intros/privacy.html b/chrome/common/extensions/docs/templates/intros/privacy.html
index 1e9b74b..b163833 100644
--- a/chrome/common/extensions/docs/templates/intros/privacy.html
+++ b/chrome/common/extensions/docs/templates/intros/privacy.html
@@ -40,8 +40,8 @@
 <p>
   Changing the value of a setting is a little bit more complex, simply because
   you first must verify that your extension can control the setting. The user
-  won't see any change to her settings if your extension toggles a setting that
-  is either locked to a specific value by enterprise policies
+  won't see any change to their settings if your extension toggles a setting
+  that is either locked to a specific value by enterprise policies
   (<code>levelOfControl</code> will be set to "not_controllable"), or if another
   extension is controlling the value (<code>levelOfControl</code> will be set to
   "controlled_by_other_extensions"). The <code>set()</code> call will succeed,
diff --git a/chrome/common/extensions/manifest_handlers/settings_overrides_handler_unittest.cc b/chrome/common/extensions/manifest_handlers/settings_overrides_handler_unittest.cc
index 735fc19a..09ff1ba5 100644
--- a/chrome/common/extensions/manifest_handlers/settings_overrides_handler_unittest.cc
+++ b/chrome/common/extensions/manifest_handlers/settings_overrides_handler_unittest.cc
@@ -85,7 +85,7 @@
   std::string error;
   std::unique_ptr<base::Value> root(json.Deserialize(NULL, &error));
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
   scoped_refptr<Extension> extension = Extension::Create(
       base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
       Manifest::INVALID_LOCATION,
@@ -134,7 +134,7 @@
   std::string error;
   std::unique_ptr<base::Value> root(json.Deserialize(NULL, &error));
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
   scoped_refptr<Extension> extension =
       Extension::Create(base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
                         Manifest::INVALID_LOCATION,
@@ -173,7 +173,7 @@
   std::string error;
   std::unique_ptr<base::Value> root(json.Deserialize(NULL, &error));
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
   scoped_refptr<Extension> extension = Extension::Create(
       base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
       Manifest::INVALID_LOCATION,
diff --git a/chrome/common/extensions/manifest_handlers/theme_handler.cc b/chrome/common/extensions/manifest_handlers/theme_handler.cc
index cdee0c3f..99106c9 100644
--- a/chrome/common/extensions/manifest_handlers/theme_handler.cc
+++ b/chrome/common/extensions/manifest_handlers/theme_handler.cc
@@ -32,12 +32,12 @@
       // The value may be a dictionary of scales and files paths.
       // Or the value may be a file path, in which case a scale
       // of 100% is assumed.
-      if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+      if (iter.value().IsType(base::Value::Type::DICTIONARY)) {
         const base::DictionaryValue* inner_value = NULL;
         if (iter.value().GetAsDictionary(&inner_value)) {
           for (base::DictionaryValue::Iterator inner_iter(*inner_value);
                !inner_iter.IsAtEnd(); inner_iter.Advance()) {
-            if (!inner_iter.value().IsType(base::Value::TYPE_STRING)) {
+            if (!inner_iter.value().IsType(base::Value::Type::STRING)) {
               *error = base::ASCIIToUTF16(errors::kInvalidThemeImages);
               return false;
             }
@@ -46,7 +46,7 @@
           *error = base::ASCIIToUTF16(errors::kInvalidThemeImages);
           return false;
         }
-      } else if (!iter.value().IsType(base::Value::TYPE_STRING)) {
+      } else if (!iter.value().IsType(base::Value::Type::STRING)) {
         *error = base::ASCIIToUTF16(errors::kInvalidThemeImages);
         return false;
       }
diff --git a/chrome/common/extensions/manifest_handlers/ui_overrides_handler_unittest.cc b/chrome/common/extensions/manifest_handlers/ui_overrides_handler_unittest.cc
index 5f75c03e..b4262b3 100644
--- a/chrome/common/extensions/manifest_handlers/ui_overrides_handler_unittest.cc
+++ b/chrome/common/extensions/manifest_handlers/ui_overrides_handler_unittest.cc
@@ -57,7 +57,7 @@
   std::string error;
   std::unique_ptr<base::Value> root(json.Deserialize(NULL, &error));
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
   scoped_refptr<Extension> extension = Extension::Create(
       base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
       Manifest::INVALID_LOCATION,
@@ -85,7 +85,7 @@
   std::string error;
   std::unique_ptr<base::Value> root(json.Deserialize(NULL, &error));
   ASSERT_TRUE(root);
-  ASSERT_TRUE(root->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(root->IsType(base::Value::Type::DICTIONARY));
   scoped_refptr<Extension> extension = Extension::Create(
       base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
       Manifest::INVALID_LOCATION,
diff --git a/chrome/common/extensions/sync_helper.cc b/chrome/common/extensions/sync_helper.cc
index 1a73e4b..5e216f91 100644
--- a/chrome/common/extensions/sync_helper.cc
+++ b/chrome/common/extensions/sync_helper.cc
@@ -21,7 +21,7 @@
 
 bool IsSyncable(const Extension* extension) {
   const Feature* feature =
-      FeatureProvider::GetBehaviorFeature(BehaviorFeature::kDoNotSync);
+      FeatureProvider::GetBehaviorFeature(behavior_feature::kDoNotSync);
   if (feature && feature->IsAvailableToExtension(extension).is_available())
     return false;
 
diff --git a/chrome/common/field_trial_recorder.mojom b/chrome/common/field_trial_recorder.mojom
new file mode 100644
index 0000000..007439d7
--- /dev/null
+++ b/chrome/common/field_trial_recorder.mojom
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome.mojom;
+
+// Records field trial activations from the renderers.
+interface FieldTrialRecorder {
+  // Indicates that a field trial has been activated.
+  FieldTrialActivated(string trial_name);
+};
diff --git a/chrome/common/importer/profile_import.mojom b/chrome/common/importer/profile_import.mojom
index 45a8757..f366a941 100644
--- a/chrome/common/importer/profile_import.mojom
+++ b/chrome/common/importer/profile_import.mojom
@@ -5,7 +5,8 @@
 module chrome.mojom;
 
 import "components/autofill/content/common/autofill_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/values.mojom";
 import "url/mojo/url.mojom";
 
 [Native]
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index 183d4e84..e9e51b13 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -61,6 +61,8 @@
 #if defined(OS_WIN)
 #include <initguid.h>
 #include "base/logging_win.h"
+#include "base/syslog_logging.h"
+#include "chrome/install_static/install_details.h"
 #endif
 
 namespace {
@@ -359,6 +361,10 @@
 #if defined(OS_WIN)
   // Enable trace control and transport through event tracing for Windows.
   logging::LogEventProvider::Initialize(kChromeTraceProviderName);
+
+  // Enable logging to the Windows Event Log.
+  logging::SetEventSourceName(base::UTF16ToASCII(
+      install_static::InstallDetails::Get().install_full_name()));
 #endif
 
   base::StatisticsRecorder::InitLogOnShutdown();
diff --git a/chrome/common/page_load_metrics/page_load_metrics_messages.h b/chrome/common/page_load_metrics/page_load_metrics_messages.h
index 7d2c6733..2ccfe94 100644
--- a/chrome/common/page_load_metrics/page_load_metrics_messages.h
+++ b/chrome/common/page_load_metrics/page_load_metrics_messages.h
@@ -15,6 +15,7 @@
 
 IPC_STRUCT_TRAITS_BEGIN(page_load_metrics::StyleSheetTiming)
   IPC_STRUCT_TRAITS_MEMBER(author_style_sheet_parse_duration_before_fcp)
+  IPC_STRUCT_TRAITS_MEMBER(update_style_duration_before_fcp)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(page_load_metrics::PageLoadTiming)
diff --git a/chrome/common/page_load_metrics/page_load_timing.cc b/chrome/common/page_load_metrics/page_load_timing.cc
index 40120a3a4..9ce7705 100644
--- a/chrome/common/page_load_metrics/page_load_timing.cc
+++ b/chrome/common/page_load_metrics/page_load_timing.cc
@@ -14,11 +14,14 @@
 
 bool StyleSheetTiming::operator==(const StyleSheetTiming& other) const {
   return author_style_sheet_parse_duration_before_fcp ==
-         other.author_style_sheet_parse_duration_before_fcp;
+             other.author_style_sheet_parse_duration_before_fcp &&
+         update_style_duration_before_fcp ==
+             other.update_style_duration_before_fcp;
 }
 
 bool StyleSheetTiming::IsEmpty() const {
-  return !author_style_sheet_parse_duration_before_fcp;
+  return !author_style_sheet_parse_duration_before_fcp &&
+         !update_style_duration_before_fcp;
 }
 
 PageLoadTiming::PageLoadTiming() {}
diff --git a/chrome/common/page_load_metrics/page_load_timing.h b/chrome/common/page_load_metrics/page_load_timing.h
index aafb5154..2f7a288 100644
--- a/chrome/common/page_load_metrics/page_load_timing.h
+++ b/chrome/common/page_load_metrics/page_load_timing.h
@@ -26,6 +26,9 @@
   // Total time spent parsing author style sheets, before the first contentful
   // paint.
   base::Optional<base::TimeDelta> author_style_sheet_parse_duration_before_fcp;
+
+  // Time spent in Document::updateStyle before FCP.
+  base::Optional<base::TimeDelta> update_style_duration_before_fcp;
 };
 
 // PageLoadTiming contains timing metrics associated with a page load. Many of
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index e7c71f43..f16b2a6 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -22,6 +22,8 @@
 const char kArcApps[] = "arc.apps";
 // A preference to store backup and restore state for Android apps.
 const char kArcBackupRestoreEnabled[] = "arc.backup_restore.enabled";
+// A preference to indicate that Android's data directory should be removed.
+const char kArcDataRemoveRequested[] = "arc.data.remove_requested";
 // A preference to keep Android apps enabled state.
 const char kArcEnabled[] = "arc.enabled";
 // A preference that indicated whether Android reported that it's compliant
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 6d4972b..d4d723e 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -21,6 +21,7 @@
 #if defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_APP_LIST)
 extern const char kArcApps[];
 extern const char kArcBackupRestoreEnabled[];
+extern const char kArcDataRemoveRequested[];
 extern const char kArcEnabled[];
 extern const char kArcPolicyCompliant[];
 extern const char kArcTermsAccepted[];
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index ff49fe9..a2d3e5f8 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -599,7 +599,3 @@
 IPC_SYNC_MESSAGE_CONTROL0_1(ChromeViewHostMsg_IsCrashReportingEnabled,
                             bool /* enabled */)
 #endif
-
-// Sent by the renderer to indicate that a fields trial has been activated.
-IPC_MESSAGE_CONTROL1(ChromeViewHostMsg_FieldTrialActivated,
-                     std::string /* name */)
diff --git a/chrome/install_static/install_details.h b/chrome/install_static/install_details.h
index 926102e..eca1f4b 100644
--- a/chrome/install_static/install_details.h
+++ b/chrome/install_static/install_details.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "chrome/install_static/install_constants.h"
+#include "chrome/install_static/install_modes.h"
 
 namespace install_static {
 
@@ -72,6 +73,13 @@
     return payload_->mode->install_suffix;
   }
 
+  // Returns the full name of the installed product (e.g. "Chrome SxS" for
+  // canary chrome).
+  std::wstring install_full_name() const {
+    return std::wstring(kProductPathName, kProductPathNameLength)
+        .append(install_suffix());
+  }
+
   const InstallConstants& mode() const { return *payload_->mode; }
 
   // The app GUID with which this mode is registered with Google Update, or an
diff --git a/chrome/install_static/install_modes.h b/chrome/install_static/install_modes.h
index 83f49e2..31b426ff 100644
--- a/chrome/install_static/install_modes.h
+++ b/chrome/install_static/install_modes.h
@@ -23,7 +23,7 @@
 
 #include <string>
 
-#include "chrome/install_static/install_details.h"
+#include "chrome/install_static/install_constants.h"
 
 // Include the brand-specific values. Each of these must define:
 // - enum InstallConstantIndex: named indices of the brand's kInstallModes
diff --git a/chrome/install_static/product_install_details.cc b/chrome/install_static/product_install_details.cc
index 81009b38..9af5b2f 100644
--- a/chrome/install_static/product_install_details.cc
+++ b/chrome/install_static/product_install_details.cc
@@ -9,6 +9,7 @@
 
 #include <algorithm>
 
+#include "chrome/install_static/install_details.h"
 #include "chrome/install_static/install_modes.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome_elf/nt_registry/nt_registry.h"
diff --git a/chrome/install_static/product_install_details_unittest.cc b/chrome/install_static/product_install_details_unittest.cc
index c0d1f82..15bb5ec2 100644
--- a/chrome/install_static/product_install_details_unittest.cc
+++ b/chrome/install_static/product_install_details_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/win/registry.h"
 #include "base/win/windows_version.h"
 #include "chrome/install_static/install_constants.h"
+#include "chrome/install_static/install_details.h"
 #include "chrome/install_static/install_modes.h"
 #include "chrome_elf/nt_registry/nt_registry.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/installer/util/master_preferences.cc b/chrome/installer/util/master_preferences.cc
index 5fa9eeb..8ec3e04 100644
--- a/chrome/installer/util/master_preferences.cc
+++ b/chrome/installer/util/master_preferences.cc
@@ -63,7 +63,7 @@
     LOG(WARNING) << "Failed to parse master prefs file: " << error;
     return NULL;
   }
-  if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!root->IsType(base::Value::Type::DICTIONARY)) {
     LOG(WARNING) << "Failed to parse master prefs file: "
                  << "Root item must be a dictionary.";
     return NULL;
diff --git a/chrome/installer/util/uninstall_metrics.cc b/chrome/installer/util/uninstall_metrics.cc
index 3be41c4..427d4f9 100644
--- a/chrome/installer/util/uninstall_metrics.cc
+++ b/chrome/installer/util/uninstall_metrics.cc
@@ -83,7 +83,7 @@
     return false;
 
   // Preferences should always have a dictionary root.
-  if (!root->IsType(base::Value::TYPE_DICTIONARY))
+  if (!root->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   return ExtractUninstallMetrics(
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index e531598..393dd41 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -11,7 +11,6 @@
 #include "base/debug/crash_logging.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chrome/renderer/chrome_mock_render_thread.cc b/chrome/renderer/chrome_mock_render_thread.cc
index e560e18..20eaa7c 100644
--- a/chrome/renderer/chrome_mock_render_thread.cc
+++ b/chrome/renderer/chrome_mock_render_thread.cc
@@ -5,13 +5,8 @@
 #include "chrome/renderer/chrome_mock_render_thread.h"
 
 #include "base/single_thread_task_runner.h"
-#include "extensions/features/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/common/extension_messages.h"
-#endif
-
 ChromeMockRenderThread::ChromeMockRenderThread() {
 }
 
@@ -27,32 +22,3 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   io_task_runner_ = task_runner;
 }
-
-bool ChromeMockRenderThread::OnMessageReceived(const IPC::Message& msg) {
-  if (content::MockRenderThread::OnMessageReceived(msg))
-    return true;
-
-  // Some messages we do special handling.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(ChromeMockRenderThread, msg)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
-                        OnOpenChannelToExtension)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-#else
-  return false;
-#endif
-}
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-void ChromeMockRenderThread::OnOpenChannelToExtension(
-    int routing_id,
-    const ExtensionMsg_ExternalConnectionInfo& info,
-    const std::string& channel_name,
-    bool include_tls_channel_id,
-    int request_id) {
-  Send(new ExtensionMsg_AssignPortId(routing_id, 0, request_id));
-}
-#endif
diff --git a/chrome/renderer/chrome_mock_render_thread.h b/chrome/renderer/chrome_mock_render_thread.h
index a7e01c2..1c16b9a 100644
--- a/chrome/renderer/chrome_mock_render_thread.h
+++ b/chrome/renderer/chrome_mock_render_thread.h
@@ -9,9 +9,6 @@
 
 #include "base/macros.h"
 #include "content/public/test/mock_render_thread.h"
-#include "extensions/features/features.h"
-
-struct ExtensionMsg_ExternalConnectionInfo;
 
 // Extends content::MockRenderThread to know about extension messages.
 class ChromeMockRenderThread : public content::MockRenderThread {
@@ -29,20 +26,6 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
 
  protected:
-  // Overrides base class implementation to add custom handling for
-  // print and extensions.
-  bool OnMessageReceived(const IPC::Message& msg) override;
-
- private:
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // The callee expects to be returned a valid channel_id.
-  void OnOpenChannelToExtension(int routing_id,
-                                const ExtensionMsg_ExternalConnectionInfo& info,
-                                const std::string& channel_name,
-                                bool include_tls_channel_id,
-                                int request_id);
-#endif
-
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeMockRenderThread);
diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
index bf5b25ee..3b96a15 100644
--- a/chrome/renderer/chrome_render_thread_observer.cc
+++ b/chrome/renderer/chrome_render_thread_observer.cc
@@ -29,6 +29,7 @@
 #include "build/build_config.h"
 #include "chrome/common/child_process_logging.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/field_trial_recorder.mojom.h"
 #include "chrome/common/media/media_resource_provider.h"
 #include "chrome/common/net/net_resource_provider.h"
 #include "chrome/common/render_messages.h"
@@ -48,6 +49,7 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_module.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "third_party/WebKit/public/web/WebCache.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
@@ -293,8 +295,10 @@
 void ChromeRenderThreadObserver::OnFieldTrialGroupFinalized(
     const std::string& trial_name,
     const std::string& group_name) {
-  content::RenderThread::Get()->Send(
-      new ChromeViewHostMsg_FieldTrialActivated(trial_name));
+  chrome::mojom::FieldTrialRecorderPtr field_trial_recorder;
+  content::RenderThread::Get()->GetRemoteInterfaces()->GetInterface(
+      &field_trial_recorder);
+  field_trial_recorder->FieldTrialActivated(trial_name);
 }
 
 void ChromeRenderThreadObserver::OnSetIsIncognitoProcess(
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
index 6f504ac..38ab2ae 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
@@ -936,7 +936,7 @@
     std::unique_ptr<V8ValueConverter> converter(V8ValueConverter::create());
     std::unique_ptr<base::Value> options_value(
         converter->FromV8Value(args[8], context()->v8_context()));
-    if (!options_value->IsType(base::Value::TYPE_NULL)) {
+    if (!options_value->IsType(base::Value::Type::NONE)) {
       options = base::DictionaryValue::From(std::move(options_value));
       if (!options) {
         args.GetIsolate()->ThrowException(v8::Exception::TypeError(
diff --git a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
index a705b61..006eda7 100644
--- a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
+++ b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
@@ -150,6 +150,10 @@
         base::TimeDelta::FromSecondsD(
             perf.authorStyleSheetParseDurationBeforeFCP());
   }
+  if (perf.updateStyleDurationBeforeFCP() > 0.0) {
+    timing.style_sheet_timing.update_style_duration_before_fcp =
+        base::TimeDelta::FromSecondsD(perf.updateStyleDurationBeforeFCP());
+  }
   return timing;
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4d1dea7..3e479c2 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -153,7 +153,7 @@
       "//components/guest_view/browser:test_support",
       "//components/infobars/core",
       "//components/network_time:network_time_test_support",
-      "//components/offline_pages:test_support",
+      "//components/offline_pages/core:test_support",
       "//components/sessions:test_support",
       "//components/web_resource:test_support",
       "//content/public/child",
@@ -383,6 +383,7 @@
       "../browser/autofill/autofill_uitest_util.cc",
       "../browser/browser_keyevents_browsertest.cc",
       "../browser/extensions/api/extension_action/browser_action_interactive_test.cc",
+      "../browser/extensions/api/notifications/notifications_apitest.cc",
       "../browser/extensions/api/omnibox/omnibox_api_interactive_test.cc",
       "../browser/extensions/api/tabs/tabs_interactive_test.cc",
       "../browser/extensions/browsertest_util.cc",
@@ -400,6 +401,7 @@
       "../browser/extensions/updater/extension_cache_fake.h",
       "../browser/extensions/window_open_interactive_apitest.cc",
       "../browser/mouseleave_browsertest.cc",
+      "../browser/notifications/message_center_notifications_browsertest.cc",
       "../browser/password_manager/password_generation_interactive_uitest.cc",
       "../browser/password_manager/password_manager_interactive_uitest.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc",
@@ -693,6 +695,13 @@
           "../browser/ui/views/bookmarks/bookmark_bar_view_test.cc",
         ]
       }
+    } else {  # ! is_chromeos
+      # Non-ChromeOS notifications tests (ChromeOS does not use cross-platform
+      # panels).
+      sources += [
+        "../browser/notifications/notification_interactive_uitest.cc",
+        "../browser/notifications/platform_notification_service_interactive_uitest.cc",
+      ]
     }
 
     if (is_win) {
@@ -746,7 +755,6 @@
 
     if (use_x11) {
       configs += [ "//build/config/linux:xtst" ]
-      data_deps += [ "//tools/xdisplaycheck" ]
     }
 
     if (enable_app_list) {
@@ -777,32 +785,16 @@
       deps += [ "//ash/test:interactive_ui_test_support" ]
     }
 
-    if (enable_notifications) {
-      sources += [
-        "../browser/extensions/api/notifications/notifications_apitest.cc",
+    if (is_android) {
+      sources -= [
+        # Android does not use the message center-based Notification system.
         "../browser/notifications/message_center_notifications_browsertest.cc",
+
+        # TODO(peter): Enable the Notification browser tests.
+        "../browser/notifications/notification_interactive_uitest.cc",
+        "../browser/notifications/platform_notification_service_interactive_uitest.cc",
       ]
-
-      # Non-ChromeOS notifications tests (ChromeOS does not use cross-platform
-      # panels).
-      if (!is_chromeos) {
-        sources += [
-          "../browser/notifications/notification_interactive_uitest.cc",
-          "../browser/notifications/platform_notification_service_interactive_uitest.cc",
-        ]
-      }
-
-      if (is_android) {
-        sources -= [
-          # Android does not use the message center-based Notification system.
-          "../browser/notifications/message_center_notifications_browsertest.cc",
-
-          # TODO(peter): Enable the Notification browser tests.
-          "../browser/notifications/notification_interactive_uitest.cc",
-          "../browser/notifications/platform_notification_service_interactive_uitest.cc",
-        ]
-      }
-    }  # enable_notifications
+    }
 
     if (!use_aura || is_chromeos) {
       sources -= [ "//ui/views/corewm/desktop_capture_controller_unittest.cc" ]
@@ -1267,6 +1259,7 @@
       "//ui/file_manager/integration_tests/",
       "//third_party/analytics/",
       "//third_party/polymer/v1_0/components-chromium/polymer/",
+      "$root_gen_dir/ui/login/login_resources.pak",
       "$root_out_dir/chromevox_test_data/",
       "$root_out_dir/content_shell.pak",
       "$root_out_dir/locales/",
@@ -1295,7 +1288,7 @@
     ]
 
     enable_multidex = true
-  } else {
+  } else {  # Not Android.
     sources += [
       # The list of sources which is only used by chrome browser tests on
       # desktop platforms.
@@ -1704,6 +1697,11 @@
       "../browser/sessions/session_restore_browsertest_chromeos.cc",
       "../browser/sessions/tab_restore_browsertest.cc",
       "../browser/site_details_browsertest.cc",
+
+      # If this list is used on Android in the future, these browser/speech/*
+      # files will probably not be applicable.
+      "../browser/speech/extension_api/tts_extension_apitest.cc",
+      "../browser/speech/speech_recognition_browsertest.cc",
       "../browser/spellchecker/spellcheck_service_browsertest.cc",
       "../browser/ssl/captive_portal_blocking_page_browsertest.cc",
       "../browser/ssl/cert_verifier_browser_test.cc",
@@ -2092,7 +2090,7 @@
         "../browser/ui/ash/shelf_browsertest.cc",
         "../browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc",
         "../browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc",
-        "../browser/ui/ash/volume_controller_browsertest_chromeos.cc",
+        "../browser/ui/ash/volume_controller_browsertest.cc",
         "../browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc",
       ]
       deps += [
@@ -2234,11 +2232,9 @@
         "../browser/chromeos/login/mixin_based_browser_test.cc",
         "../browser/chromeos/login/mixin_based_browser_test.h",
         "../browser/chromeos/login/oobe_localization_browsertest.cc",
-
-        # TODO(nkostylev) Re-enable ResourceLoaderBrowserTest.
-        #"browser/chromeos/login/resource_loader_browsertest.cc",
         "../browser/chromeos/login/proxy_auth_dialog_browsertest.cc",
         "../browser/chromeos/login/reset_browsertest.cc",
+        "../browser/chromeos/login/resource_loader_browsertest.cc",
         "../browser/chromeos/login/saml/saml_browsertest.cc",
         "../browser/chromeos/login/screens/hid_detection_screen_browsertest.cc",
         "../browser/chromeos/login/screens/mock_base_screen_delegate.cc",
@@ -2380,12 +2376,6 @@
             [ "../browser/ui/views/ime/input_ime_apitest_nonchromeos.cc" ]
       }
     }
-    if (enable_web_speech) {
-      sources += [
-        "../browser/speech/extension_api/tts_extension_apitest.cc",
-        "../browser/speech/speech_recognition_browsertest.cc",
-      ]
-    }
     if (safe_browsing_mode == 1) {
       sources += [
         "../browser/safe_browsing/permission_reporter_browsertest.cc",
@@ -2683,9 +2673,6 @@
       ]
       deps += [ "//remoting/webapp" ]
     }
-    if (use_x11) {
-      deps += [ "//tools/xdisplaycheck" ]
-    }
 
     if (use_aura) {
       if (enable_wifi_display) {
@@ -3114,7 +3101,6 @@
     "../browser/budget_service/budget_database_unittest.cc",
     "../browser/budget_service/budget_manager_unittest.cc",
     "../browser/character_encoding_unittest.cc",
-    "../browser/chrome_browser_application_mac_unittest.mm",
     "../browser/chrome_content_browser_client_unittest.cc",
     "../browser/chrome_process_singleton_win_unittest.cc",
     "../browser/command_updater_unittest.cc",
@@ -3190,6 +3176,7 @@
     "../browser/loader/chrome_navigation_data_unittest.cc",
     "../browser/loader/chrome_resource_dispatcher_host_delegate_unittest.cc",
     "../browser/logging_chrome_unittest.cc",
+    "../browser/mac/exception_processor_unittest.mm",
     "../browser/mac/keystone_glue_unittest.mm",
     "../browser/manifest/manifest_icon_downloader_unittest.cc",
     "../browser/manifest/manifest_icon_selector_unittest.cc",
@@ -3218,6 +3205,14 @@
     "../browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc",
     "../browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc",
     "../browser/net/url_info_unittest.cc",
+    "../browser/notifications/desktop_notification_profile_util_unittest.cc",
+    "../browser/notifications/notification_permission_context_unittest.cc",
+    "../browser/notifications/notification_platform_bridge_mac_unittest.mm",
+    "../browser/notifications/platform_notification_service_unittest.cc",
+    "../browser/notifications/stub_alert_dispatcher_mac.h",
+    "../browser/notifications/stub_alert_dispatcher_mac.mm",
+    "../browser/notifications/stub_notification_center_mac.h",
+    "../browser/notifications/stub_notification_center_mac.mm",
     "../browser/ntp_snippets/download_suggestions_provider_unittest.cc",
     "../browser/page_load_metrics/metrics_web_contents_observer_unittest.cc",
     "../browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc",
@@ -3231,6 +3226,7 @@
     "../browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc",
+    "../browser/page_load_metrics/user_input_tracker_unittest.cc",
     "../browser/password_manager/chrome_password_manager_client_unittest.cc",
     "../browser/password_manager/password_manager_internals_service_unittest.cc",
     "../browser/password_manager/password_store_mac_unittest.cc",
@@ -3550,8 +3546,8 @@
       ":unit_tests_java",
       "//components/gcm_driver/instance_id/android:instance_id_driver_java",
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
-      "//components/offline_pages:test_support",
-      "//components/offline_pages/background:test_support",
+      "//components/offline_pages/core:test_support",
+      "//components/offline_pages/core/background:test_support",
       "//v8:v8_external_startup_data_assets",
     ]
     deps -= [ "//third_party/libaddressinput" ]
@@ -3591,7 +3587,11 @@
       "../browser/memory/tab_manager_delegate_chromeos_unittest.cc",
       "../browser/memory/tab_manager_unittest.cc",
       "../browser/memory/tab_manager_web_contents_data_unittest.cc",
+
+      # Android does not use the Message Center notification system.
       "../browser/net/firefox_proxy_settings_unittest.cc",
+      "../browser/notifications/message_center_notifications_unittest.cc",
+      "../browser/notifications/message_center_settings_controller_unittest.cc",
       "../browser/permissions/permission_request_manager_unittest.cc",
       "../browser/platform_util_unittest.cc",
       "../browser/power_usage_monitor/power_usage_monitor_unittest.cc",
@@ -3607,6 +3607,8 @@
       # GCMDriverAndroid is not yet implemented.
       "../browser/services/gcm/gcm_profile_service_unittest.cc",
       "../browser/sessions/persistent_tab_restore_service_unittest.cc",
+      "../browser/speech/extension_api/extension_manifests_tts_unittest.cc",
+      "../browser/speech/tts_controller_unittest.cc",
       "../browser/sync/sessions/sessions_sync_manager_unittest.cc",
       "../browser/sync/sync_ui_util_unittest.cc",  # Sync setup uses native ui.
       "../browser/translate/translate_manager_render_view_host_unittest.cc",
@@ -4197,33 +4199,6 @@
       "../browser/printing/cloud_print/privet_url_fetcher_unittest.cc",
     ]
   }
-  if (enable_web_speech) {
-    sources += [
-      "../browser/speech/extension_api/extension_manifests_tts_unittest.cc",
-      "../browser/speech/tts_controller_unittest.cc",
-    ]
-  }
-  if (enable_notifications) {
-    sources += [
-      "../browser/notifications/desktop_notification_profile_util_unittest.cc",
-      "../browser/notifications/message_center_notifications_unittest.cc",
-      "../browser/notifications/message_center_settings_controller_unittest.cc",
-      "../browser/notifications/notification_permission_context_unittest.cc",
-      "../browser/notifications/notification_platform_bridge_mac_unittest.mm",
-      "../browser/notifications/platform_notification_service_unittest.cc",
-      "../browser/notifications/stub_alert_dispatcher_mac.h",
-      "../browser/notifications/stub_alert_dispatcher_mac.mm",
-      "../browser/notifications/stub_notification_center_mac.h",
-      "../browser/notifications/stub_notification_center_mac.mm",
-    ]
-    if (is_android) {
-      sources -= [
-        # Android does not use the Message Center notification system.
-        "../browser/notifications/message_center_notifications_unittest.cc",
-        "../browser/notifications/message_center_settings_controller_unittest.cc",
-      ]
-    }
-  }
 
   if (safe_browsing_mode > 0) {
     sources += [
@@ -4440,7 +4415,6 @@
   }
   if (use_x11) {
     deps += [ "//ui/events/devices" ]
-    data_deps += [ "//tools/xdisplaycheck" ]
   } else {
     sources -= [ "../browser/password_manager/password_store_x_unittest.cc" ]
   }
@@ -4879,7 +4853,10 @@
     ]
   }
   if (enable_supervised_users && !is_android && !is_chromeos) {
-    sources += [ "../browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc" ]
+    sources += [
+      "../browser/supervised_user/supervised_user_creation_policy_handler_unittest.cc",
+      "../browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc",
+    ]
   }
   if (safe_browsing_mode == 1 && enable_extensions) {
     sources += [ "../browser/extensions/blacklist_unittest.cc" ]
diff --git a/chrome/test/base/extension_js_browser_test.cc b/chrome/test/base/extension_js_browser_test.cc
index ca248f7a..48c9c9b4 100644
--- a/chrome/test/base/extension_js_browser_test.cc
+++ b/chrome/test/base/extension_js_browser_test.cc
@@ -53,7 +53,7 @@
           script);
 
   std::unique_ptr<base::Value> value_result = base::JSONReader::Read(result);
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, value_result->GetType());
+  CHECK_EQ(base::Value::Type::DICTIONARY, value_result->GetType());
   base::DictionaryValue* dict_value =
       static_cast<base::DictionaryValue*>(value_result.get());
   bool test_result;
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 483e99d..bed25a5 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -111,7 +111,7 @@
   return nullptr;
 }
 
-rappor::RapporService* TestingBrowserProcess::rappor_service() {
+rappor::RapporServiceImpl* TestingBrowserProcess::rappor_service() {
   return rappor_service_;
 }
 
@@ -221,7 +221,7 @@
 }
 
 NotificationUIManager* TestingBrowserProcess::notification_ui_manager() {
-#if defined(ENABLE_NOTIFICATIONS) && !defined(OS_ANDROID)
+#if !defined(OS_ANDROID)
   if (!notification_ui_manager_.get())
     notification_ui_manager_.reset(NotificationUIManager::Create());
   return notification_ui_manager_.get();
@@ -445,8 +445,8 @@
   subresource_filter_ruleset_service_.swap(ruleset_service);
 }
 
-void TestingBrowserProcess::SetRapporService(
-    rappor::RapporService* rappor_service) {
+void TestingBrowserProcess::SetRapporServiceImpl(
+    rappor::RapporServiceImpl* rappor_service) {
   rappor_service_ = rappor_service;
 }
 
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index 32ef1c28..af6154c8 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -66,7 +66,7 @@
   metrics_services_manager::MetricsServicesManager* GetMetricsServicesManager()
       override;
   metrics::MetricsService* metrics_service() override;
-  rappor::RapporService* rappor_service() override;
+  rappor::RapporServiceImpl* rappor_service() override;
   IOThread* io_thread() override;
   WatchDogThread* watchdog_thread() override;
   ProfileManager* profile_manager() override;
@@ -146,7 +146,7 @@
       std::unique_ptr<NotificationUIManager> notification_ui_manager);
   void SetNotificationPlatformBridge(
       std::unique_ptr<NotificationPlatformBridge> notification_platform_bridge);
-  void SetRapporService(rappor::RapporService* rappor_service);
+  void SetRapporServiceImpl(rappor::RapporServiceImpl* rappor_service);
   void SetShuttingDown(bool is_shutting_down);
   void ShutdownBrowserPolicyConnector();
 
@@ -192,7 +192,7 @@
   PrefService* local_state_;
   IOThread* io_thread_;
   net::URLRequestContextGetter* system_request_context_;
-  rappor::RapporService* rappor_service_;
+  rappor::RapporServiceImpl* rappor_service_;
 
   std::unique_ptr<BrowserProcessPlatformPart> platform_part_;
 
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index f2c32ae..de5b3d4 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -128,7 +128,7 @@
 
 #if BUILDFLAG(ANDROID_JAVA_UI)
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
 #endif
 
 using base::Time;
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 60ad11cd..28d42ab5 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -257,7 +257,7 @@
     std::string proxy_servers;
     for (size_t i = 0; i < arraysize(proxy_servers_options); ++i) {
       if (!proxy_dict->Get(proxy_servers_options[i][0], &option_value) ||
-          option_value->IsType(base::Value::TYPE_NULL)) {
+          option_value->IsType(base::Value::Type::NONE)) {
         continue;
       }
       std::string value;
@@ -277,7 +277,7 @@
 
     std::string proxy_bypass_list;
     if (proxy_dict->Get("noProxy", &option_value) &&
-        !option_value->IsType(base::Value::TYPE_NULL)) {
+        !option_value->IsType(base::Value::Type::NONE)) {
       if (!option_value->GetAsString(&proxy_bypass_list))
         return Status(kUnknownError, "'noProxy' must be a string");
     }
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
index 15753839..b74b869b 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
@@ -519,7 +519,7 @@
       "{\"method\":\"method\"}", 0, &type, &event, &response));
   ASSERT_EQ(internal::kEventMessageType, type);
   ASSERT_STREQ("method", event.method.c_str());
-  ASSERT_TRUE(event.params->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(event.params->IsType(base::Value::Type::DICTIONARY));
 }
 
 TEST(ParseInspectorMessage, EventWithParams) {
diff --git a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
index 7545980..d414ead 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
@@ -157,7 +157,7 @@
   Status status =
       internal::EvaluateScriptAndGetValue(&client, 0, std::string(), &result);
   ASSERT_EQ(kOk, status.code());
-  ASSERT_TRUE(result && result->IsType(base::Value::TYPE_NULL));
+  ASSERT_TRUE(result && result->IsType(base::Value::Type::NONE));
 }
 
 TEST(EvaluateScriptAndGetValue, Ok) {
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 16a5928..b564824 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -264,7 +264,7 @@
     if (status.IsError())
       return status;
 
-    if (!temp->IsType(base::Value::TYPE_NULL)) {
+    if (!temp->IsType(base::Value::Type::NONE)) {
       if (only_one) {
         value->reset(temp.release());
         return Status(kOk);
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index e65d81be..fb1c3bff 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -73,6 +73,13 @@
     'ChromeDriverTest.testShadowDomHover',
     'ChromeDriverTest.testMouseMoveTo',
     'ChromeDriverTest.testHoverOverElement',
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
+    'ChromeDriverTest.testWindowMaximize',
+    'ChromeDriverTest.testWindowPosition',
+    'ChromeDriverTest.testWindowSize',
+    'ChromeExtensionsCapabilityTest.testCanInspectBackgroundPage',
+    'ChromeExtensionsCapabilityTest.testCanLaunchApp',
+    'MobileEmulationCapabilityTest.testDeviceMetricsWithStandardWidth',
 ]
 _VERSION_SPECIFIC_FILTER['55'] = [
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1503
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 6dc75ca..fead5a1 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -100,6 +100,9 @@
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1503
     'BasicMouseInterfaceTest.testDraggingElementWithMouseFiresEvents',
     'BasicMouseInterfaceTest.testDraggingElementWithMouseMovesItToAnotherList',
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1625
+    'TakesScreenshotTest.*',
+    'WindowTest.*',
 ]
 
 _OS_NEGATIVE_FILTER = {}
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index 93a36f6f..5aa83bc 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -349,7 +349,7 @@
   if (!params.Get("id", &id))
     return Status(kUnknownError, "missing 'id'");
 
-  if (id->IsType(base::Value::TYPE_NULL)) {
+  if (id->IsType(base::Value::Type::NONE)) {
     session->SwitchToTopFrame();
     return Status(kOk);
   }
diff --git a/chrome/test/data/dromaeo/README.chromium b/chrome/test/data/dromaeo/README.chromium
index 8f036bf..0898bff 100644
--- a/chrome/test/data/dromaeo/README.chromium
+++ b/chrome/test/data/dromaeo/README.chromium
@@ -2,12 +2,19 @@
 
 Dromaeo was developed and released by John Resig (jresig at mozilla.com)/
 
-This benchmark was slightly modified to enable Chromium automation.
+This benchmark was slightly modified to: 
+ - Enable Chromium automation.
+ - Avoid grabbing javascript libraries from the Web.
+
 
 How to update:
 
-0) remove all the files from this dir, but keep README.chromium and LICENSE
+0) remove all the files from this dir, but keep README.chromium and LICENSE.
 1) pull new version from git://github.com/antonm/dromaeo.git into a separate
-directory;
-2) run 'make web' to build standalone version;
+directory.
+2) run 'make web' to build standalone version.
 3) copy all the files from web dir into this directory.
+4) apply patches in patches dir.
+5) Update javascript libraries to those refered in upstream (and not updated)
+   in lib/.
+
diff --git a/chrome/test/data/dromaeo/favicon.png b/chrome/test/data/dromaeo/favicon.png
index d31cc64..d772102a 100644
--- a/chrome/test/data/dromaeo/favicon.png
+++ b/chrome/test/data/dromaeo/favicon.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/htmlrunner.js b/chrome/test/data/dromaeo/htmlrunner.js
index 1fbd0fc..1365792 100644
--- a/chrome/test/data/dromaeo/htmlrunner.js
+++ b/chrome/test/data/dromaeo/htmlrunner.js
@@ -1,4 +1,4 @@
-var startTest = parent.startTest || function(){};
-var test = parent.test || function(name, fn){ fn(); };
-var endTest = parent.endTest || function(){};
-var prep = parent.prep || function(fn){ fn(); };
+var startTest = top.startTest || function(){};
+var test = top.test || function(name, fn){ fn(); };
+var endTest = top.endTest || function(){};
+var prep = top.prep || function(fn){ fn(); };
diff --git a/chrome/test/data/dromaeo/images/bg.png b/chrome/test/data/dromaeo/images/bg.png
index ce2959bd..bd659a8 100644
--- a/chrome/test/data/dromaeo/images/bg.png
+++ b/chrome/test/data/dromaeo/images/bg.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/clouds.png b/chrome/test/data/dromaeo/images/clouds.png
index c545032d..c9f7dc27 100644
--- a/chrome/test/data/dromaeo/images/clouds.png
+++ b/chrome/test/data/dromaeo/images/clouds.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/clouds2.png b/chrome/test/data/dromaeo/images/clouds2.png
index 7136d40fad..ac835dc 100644
--- a/chrome/test/data/dromaeo/images/clouds2.png
+++ b/chrome/test/data/dromaeo/images/clouds2.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino1.png b/chrome/test/data/dromaeo/images/dino1.png
index f6d8de8..2dda272 100644
--- a/chrome/test/data/dromaeo/images/dino1.png
+++ b/chrome/test/data/dromaeo/images/dino1.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino2.png b/chrome/test/data/dromaeo/images/dino2.png
index 25ee3b2..ccfa15c 100644
--- a/chrome/test/data/dromaeo/images/dino2.png
+++ b/chrome/test/data/dromaeo/images/dino2.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino3.png b/chrome/test/data/dromaeo/images/dino3.png
index a8ac5ab..a766b28 100644
--- a/chrome/test/data/dromaeo/images/dino3.png
+++ b/chrome/test/data/dromaeo/images/dino3.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino4.png b/chrome/test/data/dromaeo/images/dino4.png
index 3541000..03301d3 100644
--- a/chrome/test/data/dromaeo/images/dino4.png
+++ b/chrome/test/data/dromaeo/images/dino4.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino5.png b/chrome/test/data/dromaeo/images/dino5.png
index 522d727ac..5fd028d 100644
--- a/chrome/test/data/dromaeo/images/dino5.png
+++ b/chrome/test/data/dromaeo/images/dino5.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino6.png b/chrome/test/data/dromaeo/images/dino6.png
index ae6f821..ded8106 100644
--- a/chrome/test/data/dromaeo/images/dino6.png
+++ b/chrome/test/data/dromaeo/images/dino6.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino7.png b/chrome/test/data/dromaeo/images/dino7.png
index 02454c5..ac2021b 100644
--- a/chrome/test/data/dromaeo/images/dino7.png
+++ b/chrome/test/data/dromaeo/images/dino7.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/dino8.png b/chrome/test/data/dromaeo/images/dino8.png
index e5825aa9..a4b1fe40 100644
--- a/chrome/test/data/dromaeo/images/dino8.png
+++ b/chrome/test/data/dromaeo/images/dino8.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/left.png b/chrome/test/data/dromaeo/images/left.png
index d0d77b9..b688f2b 100644
--- a/chrome/test/data/dromaeo/images/left.png
+++ b/chrome/test/data/dromaeo/images/left.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/logo.png b/chrome/test/data/dromaeo/images/logo.png
index 09a3a24c..199fda7 100644
--- a/chrome/test/data/dromaeo/images/logo.png
+++ b/chrome/test/data/dromaeo/images/logo.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/logo2.png b/chrome/test/data/dromaeo/images/logo2.png
index c06fc10..427687f 100644
--- a/chrome/test/data/dromaeo/images/logo2.png
+++ b/chrome/test/data/dromaeo/images/logo2.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/logo3.png b/chrome/test/data/dromaeo/images/logo3.png
index ed1f1ed..306c485f 100644
--- a/chrome/test/data/dromaeo/images/logo3.png
+++ b/chrome/test/data/dromaeo/images/logo3.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/right.png b/chrome/test/data/dromaeo/images/right.png
index 05bbfb5b..154cb41 100644
--- a/chrome/test/data/dromaeo/images/right.png
+++ b/chrome/test/data/dromaeo/images/right.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/images/water.png b/chrome/test/data/dromaeo/images/water.png
index ef8541e..40a0f0d 100644
--- a/chrome/test/data/dromaeo/images/water.png
+++ b/chrome/test/data/dromaeo/images/water.png
Binary files differ
diff --git a/chrome/test/data/dromaeo/lib/dojo.js b/chrome/test/data/dromaeo/lib/dojo.js
index 31106412..ef2f2fb 100644
--- a/chrome/test/data/dromaeo/lib/dojo.js
+++ b/chrome/test/data/dromaeo/lib/dojo.js
@@ -1,8458 +1,14 @@
 /*
-	Copyright (c) 2004-2008, The Dojo Foundation All Rights Reserved.
+	Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
 	Available via Academic Free License >= 2.1 OR the modified BSD license.
 	see: http://dojotoolkit.org/license for details
 */
 
 /*
-	This is a compiled version of Dojo, built for deployment and not for
-	development. To get an editable version, please visit:
+	This is an optimized version of Dojo, built for deployment and not for
+	development. To get sources and documentation, please visit:
 
 		http://dojotoolkit.org
-
-	for documentation and information on getting the source.
 */
 
-;(function(){
-
-	/*
-	dojo, dijit, and dojox must always be the first three, and in that order.
-	djConfig.scopeMap = [
-		["dojo", "fojo"],
-		["dijit", "fijit"],
-		["dojox", "fojox"]
-	
-	]
-	*/
-
-	/**Build will replace this comment with a scoped djConfig **/
-
-	//The null below can be relaced by a build-time value used instead of djConfig.scopeMap.
-	var sMap = null;
-
-	//See if new scopes need to be defined.
-	if((sMap || (typeof djConfig != "undefined" && djConfig.scopeMap)) && (typeof window != "undefined")){
-		var scopeDef = "", scopePrefix = "", scopeSuffix = "", scopeMap = {}, scopeMapRev = {};
-		sMap = sMap || djConfig.scopeMap;
-		for(var i = 0; i < sMap.length; i++){
-			//Make local variables, then global variables that use the locals.
-			var newScope = sMap[i];
-			scopeDef += "var " + newScope[0] + " = {}; " + newScope[1] + " = " + newScope[0] + ";" + newScope[1] + "._scopeName = '" + newScope[1] + "';";
-			scopePrefix += (i == 0 ? "" : ",") + newScope[0];
-			scopeSuffix += (i == 0 ? "" : ",") + newScope[1];
-			scopeMap[newScope[0]] = newScope[1];
-			scopeMapRev[newScope[1]] = newScope[0];
-		}
-
-		eval(scopeDef + "dojo._scopeArgs = [" + scopeSuffix + "];");
-
-		dojo._scopePrefixArgs = scopePrefix;
-		dojo._scopePrefix = "(function(" + scopePrefix + "){";
-		dojo._scopeSuffix = "})(" + scopeSuffix + ")";
-		dojo._scopeMap = scopeMap;
-		dojo._scopeMapRev = scopeMapRev;
-	}
-
-/*=====
-// note:
-//		'djConfig' does not exist under 'dojo.*' so that it can be set before the
-//		'dojo' variable exists.
-// note:
-//		Setting any of these variables *after* the library has loaded does
-//		nothing at all.
-
-djConfig = {
-	// summary:
-	//		Application code can set the global 'djConfig' prior to loading
-	//		the library to override certain global settings for how dojo works.
-	//
-	// isDebug: Boolean
-	//		Defaults to `false`. If set to `true`, ensures that Dojo provides
-	//		extended debugging feedback via Firebug. If Firebug is not available
-	//		on your platform, setting `isDebug` to `true` will force Dojo to
-	//		pull in (and display) the version of Firebug Lite which is
-	//		integrated into the Dojo distribution, thereby always providing a
-	//		debugging/logging console when `isDebug` is enabled. Note that
-	//		Firebug's `console.*` methods are ALWAYS defined by Dojo. If
-	//		`isDebug` is false and you are on a platform without Firebug, these
-	//		methods will be defined as no-ops.
-	isDebug: false,
-	// debugAtAllCosts: Boolean
-	//		Defaults to `false`. If set to `true`, this triggers an alternate
-	//		mode of the package system in which dependencies are detected and
-	//		only then are resources evaluated in dependency order via
-	//		`<script>` tag inclusion. This may double-request resources and
-	//		cause problems with scripts which expect `dojo.require()` to
-	//		preform synchronously. `debugAtAllCosts` can be an invaluable
-	//		debugging aid, but when using it, ensure that all code which
-	//		depends on Dojo modules is wrapped in `dojo.addOnLoad()` handlers.
-	//		Due to the somewhat unpredictable side-effects of using
-	//		`debugAtAllCosts`, it is strongly recommended that you enable this
-	//		flag as a last resort. `debugAtAllCosts` has no effect when loading
-	//		resources across domains. For usage information, see the
-	//		[Dojo Book](http://dojotoolkit.org/book/book-dojo/part-4-meta-dojo-making-your-dojo-code-run-faster-and-better/debugging-facilities/deb)
-	debugAtAllCosts: false,
-	// locale: String
-	//		The locale to assume for loading localized resources in this page,
-	//		specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
-	//		Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`.
-	//		See the documentation for `dojo.i18n` and `dojo.requireLocalization`
-	//		for details on loading localized resources. If no locale is specified,
-	//		Dojo assumes the locale of the user agent, according to `navigator.userLanguage`
-	//		or `navigator.language` properties.
-	locale: undefined,
-	// extraLocale: Array
-	//		No default value. Specifies additional locales whose
-	//		resources should also be loaded alongside the default locale when
-	//		calls to `dojo.requireLocalization()` are processed.
-	extraLocale: undefined,
-	// baseUrl: String
-	//		The directory in which `dojo.js` is located. Under normal
-	//		conditions, Dojo auto-detects the correct location from which it
-	//		was loaded. You may need to manually configure `baseUrl` in cases
-	//		where you have renamed `dojo.js` or in which `<base>` tags confuse
-	//		some browsers (e.g. IE 6). The variable `dojo.baseUrl` is assigned
-	//		either the value of `djConfig.baseUrl` if one is provided or the
-	//		auto-detected root if not. Other modules are located relative to
-	//		this path.
-	baseUrl: undefined,
-	// modulePaths: Object
-	//		A map of module names to paths relative to `dojo.baseUrl`. The
-	//		key/value pairs correspond directly to the arguments which
-	//		`dojo.registerModulePath` accepts. Specifiying
-	//		`djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent
-	//		of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple
-	//		modules may be configured via `djConfig.modulePaths`.
-	modulePaths: {},
-	// afterOnLoad: Boolean 
-	//		Indicates Dojo was added to the page after the page load. In this case
-	//		Dojo will not wait for the page DOMContentLoad/load events and fire
-	//		its dojo.addOnLoad callbacks after making sure all outstanding
-	//		dojo.required modules have loaded.
-	afterOnLoad: false,
-	// addOnLoad: Function or Array
-	//		Adds a callback via dojo.addOnLoad. Useful when Dojo is added after
-	//		the page loads and djConfig.afterOnLoad is true. Supports the same
-	//		arguments as dojo.addOnLoad. When using a function reference, use
-	//		`djConfig.addOnLoad = function(){};`. For object with function name use
-	//		`djConfig.addOnLoad = [myObject, "functionName"];` and for object with
-	//		function reference use
-	//		`djConfig.addOnLoad = [myObject, function(){}];`
-	addOnLoad: null,
-	// require: Array
-	//		An array of module names to be loaded immediately after dojo.js has been included
-	//		in a page. 
-	require: []
-}
-=====*/
-
-(function(){
-	// firebug stubs
-
-	// if((!this["console"])||(!console["firebug"])){
-
-	if(!this["console"]){
-		this.console = {
-		};
-	}
-
-	//	Be careful to leave 'log' always at the end
-	var cn = [
-		"assert", "count", "debug", "dir", "dirxml", "error", "group",
-		"groupEnd", "info", "profile", "profileEnd", "time", "timeEnd",
-		"trace", "warn", "log" 
-	];
-	var i=0, tn;
-	while((tn=cn[i++])){
-		if(!console[tn]){
-			(function(){
-				var tcn = tn+"";
-				console[tcn] = ('log' in console) ? function(){ 
-					var a = Array.apply({}, arguments);
-					a.unshift(tcn+":");
-					console["log"](a.join(" "));
-				} : function(){}
-			})();
-		}
-	}
-
-	//TODOC:  HOW TO DOC THIS?
-	// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
-	if(typeof dojo == "undefined"){
-		this.dojo = {
-			_scopeName: "dojo",
-			_scopePrefix: "",
-			_scopePrefixArgs: "",
-			_scopeSuffix: "",
-			_scopeMap: {},
-			_scopeMapRev: {}
-		};
-	}
-
-	var d = dojo;
-
-	//Need placeholders for dijit and dojox for scoping code.
-	if(typeof dijit == "undefined"){
-		this.dijit = {_scopeName: "dijit"};
-	}
-	if(typeof dojox == "undefined"){
-		this.dojox = {_scopeName: "dojox"};
-	}
-	
-	if(!d._scopeArgs){
-		d._scopeArgs = [dojo, dijit, dojox];
-	}
-
-/*=====
-dojo.global = {
-	//	summary:
-	//		Alias for the global scope
-	//		(e.g. the window object in a browser).
-	//	description:
-	//		Refer to 'dojo.global' rather than referring to window to ensure your
-	//		code runs correctly in contexts other than web browsers (e.g. Rhino on a server).
-}
-=====*/
-	d.global = this;
-
-	d.config =/*===== djConfig = =====*/{
-		isDebug: false,
-		debugAtAllCosts: false
-	};
-
-	if(typeof djConfig != "undefined"){
-		for(var opt in djConfig){
-			d.config[opt] = djConfig[opt];
-		}
-	}
-
-	var _platforms = ["Browser", "Rhino", "Spidermonkey", "Mobile"];
-	var t;
-	while((t=_platforms.shift())){
-		d["is"+t] = false;
-	}
-
-/*=====
-	// Override locale setting, if specified
-	dojo.locale = {
-		// summary: the locale as defined by Dojo (read-only)
-	};
-=====*/
-	dojo.locale = d.config.locale;
-	
-	var rev = "$Rev: 15729 $".match(/\d+/);
-
-	dojo.version = {
-		// summary: 
-		//		version number of dojo
-		//	major: Integer
-		//		Major version. If total version is "1.2.0beta1", will be 1
-		//	minor: Integer
-		//		Minor version. If total version is "1.2.0beta1", will be 2
-		//	patch: Integer
-		//		Patch version. If total version is "1.2.0beta1", will be 0
-		//	flag: String
-		//		Descriptor flag. If total version is "1.2.0beta1", will be "beta1"
-		//	revision: Number
-		//		The SVN rev from which dojo was pulled
-		major: 1, minor: 2, patch: 2, flag: "",
-		revision: rev ? +rev[0] : 999999, //FIXME: use NaN?
-		toString: function(){
-			with(d.version){
-				return major + "." + minor + "." + patch + flag + " (" + revision + ")";	// String
-			}
-		}
-	}
-
-	// Register with the OpenAjax hub
-	if(typeof OpenAjax != "undefined"){
-		OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString());
-	}
-
-	dojo._mixin = function(/*Object*/ obj, /*Object*/ props){
-		// summary:
-		//		Adds all properties and methods of props to obj. This addition
-		//		is "prototype extension safe", so that instances of objects
-		//		will not pass along prototype defaults.
-		var tobj = {};
-		for(var x in props){
-			// the "tobj" condition avoid copying properties in "props"
-			// inherited from Object.prototype.  For example, if obj has a custom
-			// toString() method, don't overwrite it with the toString() method
-			// that props inherited from Object.prototype
-			if(tobj[x] === undefined || tobj[x] != props[x]){
-				obj[x] = props[x];
-			}
-		}
-		// IE doesn't recognize custom toStrings in for..in
-		if(d["isIE"] && props){
-			var p = props.toString;
-			if(typeof p == "function" && p != obj.toString && p != tobj.toString &&
-				p != "\nfunction toString() {\n    [native code]\n}\n"){
-					obj.toString = props.toString;
-			}
-		}
-		return obj; // Object
-	}
-
-	dojo.mixin = function(/*Object*/obj, /*Object...*/props){
-		// summary:	
-		//		Adds all properties and methods of props to obj and returns the
-		//		(now modified) obj.
-		//	description:
-		//		`dojo.mixin` can mix multiple source objects into a
-		//		destionation object which is then returned. Unlike regular
-		//		`for...in` iteration, `dojo.mixin` is also smart about avoiding
-		//		extensions which other toolkits may unwisely add to the root
-		//		object prototype
-		//	obj:
-		//		The object to mix properties into. Also the return value.
-		//	props:
-		//		One or more objects whose values are successively copied into
-		//		obj. If more than one of these objects contain the same value,
-		//		the one specified last in the function call will "win".
-		//	example:
-		//		make a shallow copy of an object
-		//	|	var copy = dojo.mixin({}, source);
-		//	example:
-		//		many class constructors often take an object which specifies
-		//		values to be configured on the object. In this case, it is
-		//		often simplest to call `dojo.mixin` on the `this` object:
-		//	|	dojo.declare("acme.Base", null, {
-		//	|		constructor: function(properties){
-		//	|			// property configuration:
-		//	|			dojo.mixin(this, properties);
-		//	|	
-		//	|			
-		//	|			//  ...
-		//	|		},
-		//	|		quip: "I wasn't born yesterday, you know - I've seen movies.",
-		//	|		// ...
-		//	|	});
-		//	|
-		//	|	// create an instance of the class and configure it
-		//	|	var b = new acme.Base({quip: "That's what it does!" });
-		//	example:
-		//		copy in properties from multiple objects
-		//	|	var flattened = dojo.mixin(
-		//	|		{
-		//	|			name: "Frylock",
-		//	|			braces: true
-		//	|		},
-		//	|		{
-		//	|			name: "Carl Brutanananadilewski"
-		//	|		}
-		//	|	);
-		//	|	
-		//	|	// will print "Carl Brutanananadilewski"
-		//	|	
-		//	|	// will print "true"
-		//	|	
-		for(var i=1, l=arguments.length; i<l; i++){
-			d._mixin(obj, arguments[i]);
-		}
-		return obj; // Object
-	}
-
-	dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){
-		var obj=context || d.global;
-		for(var i=0, p; obj && (p=parts[i]); i++){
-			if(i == 0 && this._scopeMap[p]){
-				p = this._scopeMap[p];
-			}
-			obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));
-		}
-		return obj; // mixed
-	}
-
-	dojo.setObject = function(/*String*/name, /*Object*/value, /*Object?*/context){
-		// summary: 
-		//		Set a property from a dot-separated string, such as "A.B.C"
-		//	description: 
-		//		Useful for longer api chains where you have to test each object in
-		//		the chain, or when you have an object reference in string format.
-		//		Objects are created as needed along `path`. Returns the passed
-		//		value if setting is successful or `undefined` if not.
-		//	name: 	
-		//		Path to a property, in the form "A.B.C".
-		//	context:
-		//		Optional. Object to use as root of path. Defaults to
-		//		`dojo.global`.
-		//	example:
-		//		set the value of `foo.bar.baz`, regardless of whether
-		//		intermediate objects already exist:
-		//	|	dojo.setObject("foo.bar.baz", value);
-		//	example:
-		//		without `dojo.setObject`, we often see code like this:
-		//	|	// ensure that intermediate objects are available
-		//	|	if(!obj["parent"]){ obj.parent = {}; }
-		//	|	if(!obj.parent["child"]){ obj.parent.child= {}; }
-		//	|	// now we can safely set the property
-		//	|	obj.parent.child.prop = "some value";
-		//		wheras with `dojo.setObject`, we can shorten that to:
-		//	|	dojo.setObject("parent.child.prop", "some value", obj);
-		var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context);
-		return obj && p ? (obj[p]=value) : undefined; // Object
-	}
-
-	dojo.getObject = function(/*String*/name, /*Boolean*/create, /*Object*/context){
-		// summary: 
-		//		Get a property from a dot-separated string, such as "A.B.C"
-		//	description: 
-		//		Useful for longer api chains where you have to test each object in
-		//		the chain, or when you have an object reference in string format.
-		//	name: 	
-		//		Path to an property, in the form "A.B.C".
-		//	context:
-		//		Optional. Object to use as root of path. Defaults to
-		//		'dojo.global'. Null may be passed.
-		//	create: 
-		//		Optional. Defaults to `false`. If `true`, Objects will be
-		//		created at any point along the 'path' that is undefined.
-		return d._getProp(name.split("."), create, context); // Object
-	}
-
-	dojo.exists = function(/*String*/name, /*Object?*/obj){
-		//	summary: 
-		//		determine if an object supports a given method
-		//	description: 
-		//		useful for longer api chains where you have to test each object in
-		//		the chain
-		//	name: 	
-		//		Path to an object, in the form "A.B.C".
-		//	obj:
-		//		Object to use as root of path. Defaults to
-		//		'dojo.global'. Null may be passed.
-		//	example:
-		//	|	// define an object
-		//	|	var foo = {
-		//	|		bar: { }
-		//	|	};
-		//	|
-		//	|	// search the global scope
-		//	|	dojo.exists("foo.bar"); // true
-		//	|	dojo.exists("foo.bar.baz"); // false
-		//	|
-		//	|	// search from a particular scope
-		//	|	dojo.exists("bar", foo); // true
-		//	|	dojo.exists("bar.baz", foo); // false
-		return !!d.getObject(name, false, obj); // Boolean
-	}
-
-
-	dojo["eval"] = function(/*String*/ scriptFragment){
-		//	summary: 
-		//		Perform an evaluation in the global scope. Use this rather than
-		//		calling 'eval()' directly.
-		//	description: 
-		//		Placed in a separate function to minimize size of trapped
-		//		exceptions. Calling eval() directly from some other scope may
-		//		complicate tracebacks on some platforms.
-		//	returns:
-		//		The result of the evaluation. Often `undefined`
-
-
-		// note:
-		//	 - JSC eval() takes an optional second argument which can be 'unsafe'.
-		//	 - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
-		//  	 scope object for new symbols.
-
-		// FIXME: investigate Joseph Smarr's technique for IE:
-		//		http://josephsmarr.com/2007/01/31/fixing-eval-to-use-global-scope-in-ie/
-		//	see also:
-		// 		http://trac.dojotoolkit.org/ticket/744
-		return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment); 	// Object
-	}
-
-	/*=====
-		dojo.deprecated = function(behaviour, extra, removal){
-			//	summary: 
-			//		Log a debug message to indicate that a behavior has been
-			//		deprecated.
-			//	behaviour: String
-			//		The API or behavior being deprecated. Usually in the form
-			//		of "myApp.someFunction()".
-			//	extra: String?
-			//		Text to append to the message. Often provides advice on a
-			//		new function or facility to achieve the same goal during
-			//		the deprecation period.
-			//	removal: String?
-			//		Text to indicate when in the future the behavior will be
-			//		removed. Usually a version number.
-			//	example:
-			//	|	dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0");
-		}
-
-		dojo.experimental = function(moduleName, extra){
-			//	summary: Marks code as experimental.
-			//	description: 
-			//	 	This can be used to mark a function, file, or module as
-			//	 	experimental.  Experimental code is not ready to be used, and the
-			//	 	APIs are subject to change without notice.  Experimental code may be
-			//	 	completed deleted without going through the normal deprecation
-			//	 	process.
-			//	moduleName: String
-			//	 	The name of a module, or the name of a module file or a specific
-			//	 	function
-			//	extra: String?
-			//	 	some additional message for the user
-			//	example:
-			//	|	dojo.experimental("dojo.data.Result");
-			//	example:
-			//	|	dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");
-		}
-	=====*/
-
-	//Real functions declared in dojo._firebug.firebug.
-	d.deprecated = d.experimental = function(){};
-
-})();
-// vim:ai:ts=4:noet
-
-/*
- * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains
- * all of the package loading methods.
- */
-
-(function(){
-	var d = dojo;
-
-	d.mixin(d, {
-		_loadedModules: {},
-		_inFlightCount: 0,
-		_hasResource: {},
-
-		_modulePrefixes: {
-			dojo: 	{	name: "dojo", value: "." },
-			// dojox: 	{	name: "dojox", value: "../dojox" },
-			// dijit: 	{	name: "dijit", value: "../dijit" },
-			doh: 	{	name: "doh", value: "../util/doh" },
-			tests: 	{	name: "tests", value: "tests" }
-		},
-
-		_moduleHasPrefix: function(/*String*/module){
-			// summary: checks to see if module has been established
-			var mp = this._modulePrefixes;
-			return !!(mp[module] && mp[module].value); // Boolean
-		},
-
-		_getModulePrefix: function(/*String*/module){
-			// summary: gets the prefix associated with module
-			var mp = this._modulePrefixes;
-			if(this._moduleHasPrefix(module)){
-				return mp[module].value; // String
-			}
-			return module; // String
-		},
-
-		_loadedUrls: [],
-
-		//WARNING: 
-		//		This variable is referenced by packages outside of bootstrap:
-		//		FloatingPane.js and undo/browser.js
-		_postLoad: false,
-		
-		//Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
-		_loaders: [],
-		_unloaders: [],
-		_loadNotifying: false
-	});
-
-
-		dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
-		// 	summary:
-		//		Load a Javascript module given a relative path
-		//
-		//	description:
-		//		Loads and interprets the script located at relpath, which is
-		//		relative to the script root directory.  If the script is found but
-		//		its interpretation causes a runtime exception, that exception is
-		//		not caught by us, so the caller will see it.  We return a true
-		//		value if and only if the script is found.
-		//
-		// relpath: 
-		//		A relative path to a script (no leading '/', and typically ending
-		//		in '.js').
-		// module: 
-		//		A module whose existance to check for after loading a path.  Can be
-		//		used to determine success or failure of the load.
-		// cb: 
-		//		a callback function to pass the result of evaluating the script
-
-		var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : this.baseUrl) + relpath;
-		try{
-			return !module ? this._loadUri(uri, cb) : this._loadUriAndCheck(uri, module, cb); // Boolean
-		}catch(e){
-			console.error(e);
-			return false; // Boolean
-		}
-	}
-
-	dojo._loadUri = function(/*String*/uri, /*Function?*/cb){
-		//	summary:
-		//		Loads JavaScript from a URI
-		//	description:
-		//		Reads the contents of the URI, and evaluates the contents.  This is
-		//		used to load modules as well as resource bundles. Returns true if
-		//		it succeeded. Returns false if the URI reading failed.  Throws if
-		//		the evaluation throws.
-		//	uri: a uri which points at the script to be loaded
-		//	cb: 
-		//		a callback function to process the result of evaluating the script
-		//		as an expression, typically used by the resource bundle loader to
-		//		load JSON-style resources
-
-		if(this._loadedUrls[uri]){
-			return true; // Boolean
-		}
-		var contents = this._getText(uri, true);
-		if(!contents){ return false; } // Boolean
-		this._loadedUrls[uri] = true;
-		this._loadedUrls.push(uri);
-		if(cb){
-			contents = '('+contents+')';
-		}else{
-			//Only do the scoping if no callback. If a callback is specified,
-			//it is most likely the i18n bundle stuff.
-			contents = this._scopePrefix + contents + this._scopeSuffix;
-		}
-		if(d.isMoz){ contents += "\r\n//@ sourceURL=" + uri; } // debugging assist for Firebug
-		var value = d["eval"](contents);
-		if(cb){ cb(value); }
-		return true; // Boolean
-	}
-	
-	// FIXME: probably need to add logging to this method
-	dojo._loadUriAndCheck = function(/*String*/uri, /*String*/moduleName, /*Function?*/cb){
-		// summary: calls loadUri then findModule and returns true if both succeed
-		var ok = false;
-		try{
-			ok = this._loadUri(uri, cb);
-		}catch(e){
-			console.error("failed loading " + uri + " with error: " + e);
-		}
-		return !!(ok && this._loadedModules[moduleName]); // Boolean
-	}
-
-	dojo.loaded = function(){
-		// summary:
-		//		signal fired when initial environment and package loading is
-		//		complete. You may use dojo.addOnLoad() or dojo.connect() to
-		//		this method in order to handle initialization tasks that
-		//		require the environment to be initialized. In a browser host,
-		//		declarative widgets will be constructed when this function
-		//		finishes runing.
-		this._loadNotifying = true;
-		this._postLoad = true;
-		var mll = d._loaders;
-
-		//Clear listeners so new ones can be added
-		//For other xdomain package loads after the initial load.
-		this._loaders = [];
-
-		for(var x = 0; x < mll.length; x++){
-			mll[x]();
-		}
-
-		this._loadNotifying = false;
-		
-		//Make sure nothing else got added to the onload queue
-		//after this first run. If something did, and we are not waiting for any
-		//more inflight resources, run again.
-		if(d._postLoad && d._inFlightCount == 0 && mll.length){
-			d._callLoaded();
-		}
-	}
-
-	dojo.unloaded = function(){
-		// summary:
-		//		signal fired by impending environment destruction. You may use
-		//		dojo.addOnUnload() or dojo.connect() to this method to perform
-		//		page/application cleanup methods. See dojo.addOnUnload for more info.
-		var mll = this._unloaders;
-		while(mll.length){
-			(mll.pop())();
-		}
-	}
-
-	d._onto = function(arr, obj, fn){
-		if(!fn){
-			arr.push(obj);
-		}else if(fn){
-			var func = (typeof fn == "string") ? obj[fn] : fn;
-			arr.push(function(){ func.call(obj); });
-		}
-	}
-
-	dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName){
-		// summary:
-		//		Registers a function to be triggered after the DOM has finished
-		//		loading and widgets declared in markup have been instantiated.
-		//		Images and CSS files may or may not have finished downloading when
-		//		the specified function is called.  (Note that widgets' CSS and HTML
-		//		code is guaranteed to be downloaded before said widgets are
-		//		instantiated.)
-		// example:
-		//	|	dojo.addOnLoad(functionPointer);
-		//	|	dojo.addOnLoad(object, "functionName");
-		//	|	dojo.addOnLoad(object, function(){ /* ... */});
-
-		d._onto(d._loaders, obj, functionName);
-
-		//Added for xdomain loading. dojo.addOnLoad is used to
-		//indicate callbacks after doing some dojo.require() statements.
-		//In the xdomain case, if all the requires are loaded (after initial
-		//page load), then immediately call any listeners.
-		if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){
-			d._callLoaded();
-		}
-	}
-
-	//Support calling dojo.addOnLoad via djConfig.addOnLoad. Support all the
-	//call permutations of dojo.addOnLoad. Mainly useful when dojo is added
-	//to the page after the page has loaded.
-	var dca = d.config.addOnLoad;
-	if(dca){
-		d.addOnLoad[(dca instanceof Array ? "apply" : "call")](d, dca);
-	}
-
-	dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
-		// summary:
-		//		registers a function to be triggered when the page unloads. In a browser
-		//		enviroment, the functions will be triggered during the window.onbeforeunload
-		//		event. Be careful doing work during window.onbeforeunload. onbeforeunload
-		//		can be triggered if a link to download a file is clicked, or if the link is a
-		//		javascript: link. In these cases, the onbeforeunload event fires, but the
-		//		document is not actually destroyed. So be careful about doing destructive
-		//		operations in a dojo.addOnUnload callback.
-		// example:
-		//	|	dojo.addOnUnload(functionPointer)
-		//	|	dojo.addOnUnload(object, "functionName")
-		//	|	dojo.addOnUnload(object, function(){ /* ... */});
-
-		d._onto(d._unloaders, obj, functionName);
-	}
-
-	dojo._modulesLoaded = function(){
-		if(d._postLoad){ return; }
-		if(d._inFlightCount > 0){ 
-			console.warn("files still in flight!");
-			return;
-		}
-		d._callLoaded();
-	}
-
-	dojo._callLoaded = function(){
-
-		// The "object" check is for IE, and the other opera check fixes an
-		// issue in Opera where it could not find the body element in some
-		// widget test cases.  For 0.9, maybe route all browsers through the
-		// setTimeout (need protection still for non-browser environments
-		// though). This might also help the issue with FF 2.0 and freezing
-		// issues where we try to do sync xhr while background css images are
-		// being loaded (trac #2572)? Consider for 0.9.
-		if(typeof setTimeout == "object" || (dojo.config.useXDomain && d.isOpera)){
-			if(dojo.isAIR){
-				setTimeout(function(){dojo.loaded();}, 0);
-			}else{
-				setTimeout(dojo._scopeName + ".loaded();", 0);
-			}
-		}else{
-			d.loaded();
-		}
-	}
-
-	dojo._getModuleSymbols = function(/*String*/modulename){
-		// summary:
-		//		Converts a module name in dotted JS notation to an array
-		//		representing the path in the source tree
-		var syms = modulename.split(".");
-		for(var i = syms.length; i>0; i--){
-			var parentModule = syms.slice(0, i).join(".");
-			if((i==1) && !this._moduleHasPrefix(parentModule)){		
-				// Support default module directory (sibling of dojo) for top-level modules 
-				syms[0] = "../" + syms[0];
-			}else{
-				var parentModulePath = this._getModulePrefix(parentModule);
-				if(parentModulePath != parentModule){
-					syms.splice(0, i, parentModulePath);
-					break;
-				}
-			}
-		}
-		// 
-		return syms; // Array
-	}
-
-	dojo._global_omit_module_check = false;
-
-	dojo.loadInit = function(/*Function*/init){
-		//	summary:
-		//		Executes a function that needs to be executed for the loader's dojo.requireIf
-		//		resolutions to work. This is needed mostly for the xdomain loader case where
-		//		a function needs to be executed to set up the possible values for a dojo.requireIf
-		//		call.
-		//	init:
-		//		a function reference. Executed immediately.
-		//	description: This function is mainly a marker for the xdomain loader to know parts of
-		//		code that needs be executed outside the function wrappper that is placed around modules.
-		//		The init function could be executed more than once, and it should make no assumptions
-		//		on what is loaded, or what modules are available. Only the functionality in Dojo Base
-		//		is allowed to be used. Avoid using this method. For a valid use case,
-		//		see the source for dojox.gfx.
-		init();
-	}
-
-	dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){
-		//	summary:
-		//		loads a Javascript module from the appropriate URI
-		//	moduleName:
-		//		module name to load, using periods for separators,
-		//		 e.g. "dojo.date.locale".  Module paths are de-referenced by dojo's
-		//		internal mapping of locations to names and are disambiguated by
-		//		longest prefix. See `dojo.registerModulePath()` for details on
-		//		registering new modules.
-		//	omitModuleCheck:
-		//		if `true`, omitModuleCheck skips the step of ensuring that the
-		//		loaded file actually defines the symbol it is referenced by.
-		//		For example if it called as `dojo.require("a.b.c")` and the
-		//		file located at `a/b/c.js` does not define an object `a.b.c`,
-		//		and exception will be throws whereas no exception is raised
-		//		when called as `dojo.require("a.b.c", true)`
-		//	description:
-		//		`dojo.require("A.B")` first checks to see if symbol A.B is
-		//		defined. If it is, it is simply returned (nothing to do).
-		//	
-		//		If it is not defined, it will look for `A/B.js` in the script root
-		//		directory.
-		//	
-		//		`dojo.require` throws an excpetion if it cannot find a file
-		//		to load, or if the symbol `A.B` is not defined after loading.
-		//	
-		//		It returns the object `A.B`.
-		//	
-		//		`dojo.require()` does nothing about importing symbols into
-		//		the current namespace.  It is presumed that the caller will
-		//		take care of that. For example, to import all symbols into a
-		//		local block, you might write:
-		//	
-		//		|	with (dojo.require("A.B")) {
-		//		|		...
-		//		|	}
-		//	
-		//		And to import just the leaf symbol to a local variable:
-		//	
-		//		|	var B = dojo.require("A.B");
-		//	   	|	...
-		//	returns: the required namespace object
-		omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
-
-		//Check if it is already loaded.
-		var module = this._loadedModules[moduleName];
-		if(module){
-			return module;
-		}
-
-		// convert periods to slashes
-		var relpath = this._getModuleSymbols(moduleName).join("/") + '.js';
-
-		var modArg = (!omitModuleCheck) ? moduleName : null;
-		var ok = this._loadPath(relpath, modArg);
-
-		if(!ok && !omitModuleCheck){
-			throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
-		}
-
-		// check that the symbol was defined
-		// Don't bother if we're doing xdomain (asynchronous) loading.
-		if(!omitModuleCheck && !this._isXDomain){
-			// pass in false so we can give better error
-			module = this._loadedModules[moduleName];
-			if(!module){
-				throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); 
-			}
-		}
-
-		return module;
-	}
-
-	dojo.provide = function(/*String*/ resourceName){
-		//	summary:
-		//		Each javascript source file must have at least one
-		//		`dojo.provide()` call at the top of the file, corresponding to
-		//		the file name.  For example, `js/dojo/foo.js` must have
-		//		`dojo.provide("dojo.foo");` before any calls to
-		//		`dojo.require()` are made.
-		//	description:
-		//		Each javascript source file is called a resource.  When a
-		//		resource is loaded by the browser, `dojo.provide()` registers
-		//		that it has been loaded.
-		//	
-		//		For backwards compatibility reasons, in addition to registering
-		//		the resource, `dojo.provide()` also ensures that the javascript
-		//		object for the module exists.  For example,
-		//		`dojo.provide("dojox.data.FlickrStore")`, in addition to
-		//		registering that `FlickrStore.js` is a resource for the
-		//		`dojox.data` module, will ensure that the `dojox.data`
-		//		javascript object exists, so that calls like 
-		//		`dojo.data.foo = function(){ ... }` don't fail.
-		//
-		//		In the case of a build where multiple javascript source files
-		//		are combined into one bigger file (similar to a .lib or .jar
-		//		file), that file may contain multiple dojo.provide() calls, to
-		//		note that it includes multiple resources.
-
-		//Make sure we have a string.
-		resourceName = resourceName + "";
-		return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object
-	}
-
-	//Start of old bootstrap2:
-
-	dojo.platformRequire = function(/*Object*/modMap){
-		//	summary:
-		//		require one or more modules based on which host environment
-		//		Dojo is currently operating in
-		//	description:
-		//		This method takes a "map" of arrays which one can use to
-		//		optionally load dojo modules. The map is indexed by the
-		//		possible dojo.name_ values, with two additional values:
-		//		"default" and "common". The items in the "default" array will
-		//		be loaded if none of the other items have been choosen based on
-		//		dojo.name_, set by your host environment. The items in the
-		//		"common" array will *always* be loaded, regardless of which
-		//		list is chosen.
-		//	example:
-		//		|	dojo.platformRequire({
-		//		|		browser: [
-		//		|			"foo.sample", // simple module
-		//		|			"foo.test",
-		//		|			["foo.bar.baz", true] // skip object check in _loadModule (dojo.require)
-		//		|		],
-		//		|		default: [ "foo.sample._base" ],
-		//		|		common: [ "important.module.common" ]
-		//		|	});
-
-		var common = modMap.common || [];
-		var result = common.concat(modMap[d._name] || modMap["default"] || []);
-
-		for(var x=0; x<result.length; x++){
-			var curr = result[x];
-			if(curr.constructor == Array){
-				d._loadModule.apply(d, curr);
-			}else{
-				d._loadModule(curr);
-			}
-		}
-	}
-
-	dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
-		// summary:
-		//		If the condition is true then call dojo.require() for the specified
-		//		resource
-		if(condition === true){
-			// FIXME: why do we support chained require()'s here? does the build system?
-			var args = [];
-			for(var i = 1; i < arguments.length; i++){ 
-				args.push(arguments[i]);
-			}
-			d.require.apply(d, args);
-		}
-	}
-
-	dojo.requireAfterIf = d.requireIf;
-
-	dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
-		//	summary: 
-		//		maps a module name to a path
-		//	description: 
-		//		An unregistered module is given the default path of ../[module],
-		//		relative to Dojo root. For example, module acme is mapped to
-		//		../acme.  If you want to use a different module name, use
-		//		dojo.registerModulePath. 
-		//	example:
-		//		If your dojo.js is located at this location in the web root:
-		//	|	/myapp/js/dojo/dojo/dojo.js
-		//		and your modules are located at:
-		//	|	/myapp/js/foo/bar.js
-		//	|	/myapp/js/foo/baz.js
-		//	|	/myapp/js/foo/thud/xyzzy.js
-		//		Your application can tell Dojo to locate the "foo" namespace by calling:
-		//	|	dojo.registerModulePath("foo", "../../foo");
-		//		At which point you can then use dojo.require() to load the
-		//		modules (assuming they provide() the same things which are
-		//		required). The full code might be:
-		//	|	<script type="text/javascript" 
-		//	|		src="/myapp/js/dojo/dojo/dojo.js"></script>
-		//	|	<script type="text/javascript">
-		//	|		dojo.registerModulePath("foo", "../../foo");
-		//	|		dojo.require("foo.bar");
-		//	|		dojo.require("foo.baz");
-		//	|		dojo.require("foo.thud.xyzzy");
-		//	|	</script>
-		d._modulePrefixes[module] = { name: module, value: prefix };
-	}
-
-	dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
-		// summary:
-		//		Declares translated resources and loads them if necessary, in the
-		//		same style as dojo.require.  Contents of the resource bundle are
-		//		typically strings, but may be any name/value pair, represented in
-		//		JSON format.  See also `dojo.i18n.getLocalization`.
-		//
-		// description:
-		//		Load translated resource bundles provided underneath the "nls"
-		//		directory within a package.  Translated resources may be located in
-		//		different packages throughout the source tree.  
-		//
-		//		Each directory is named for a locale as specified by RFC 3066,
-		//		(http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase.
-		//		Note that the two bundles in the example do not define all the
-		//		same variants.  For a given locale, bundles will be loaded for
-		//		that locale and all more general locales above it, including a
-		//		fallback at the root directory.  For example, a declaration for
-		//		the "de-at" locale will first load `nls/de-at/bundleone.js`,
-		//		then `nls/de/bundleone.js` and finally `nls/bundleone.js`.  The
-		//		data will be flattened into a single Object so that lookups
-		//		will follow this cascading pattern.  An optional build step can
-		//		preload the bundles to avoid data redundancy and the multiple
-		//		network hits normally required to load these resources.
-		//
-		// moduleName: 
-		//		name of the package containing the "nls" directory in which the
-		//		bundle is found
-		//
-		// bundleName: 
-		//		bundle name, i.e. the filename without the '.js' suffix
-		//
-		// locale: 
-		//		the locale to load (optional)  By default, the browser's user
-		//		locale as defined by dojo.locale
-		//
-		// availableFlatLocales: 
-		//		A comma-separated list of the available, flattened locales for this
-		//		bundle. This argument should only be set by the build process.
-		//
-		//	example:
-		//		A particular widget may define one or more resource bundles,
-		//		structured in a program as follows, where moduleName is
-		//		mycode.mywidget and bundleNames available include bundleone and
-		//		bundletwo:
-		//	|		...
-		//	|	mycode/
-		//	|		mywidget/
-		//	|			nls/
-		//	|				bundleone.js (the fallback translation, English in this example)
-		//	|				bundletwo.js (also a fallback translation)
-		//	|				de/
-		//	|					bundleone.js
-		//	|					bundletwo.js
-		//	|				de-at/
-		//	|					bundleone.js
-		//	|				en/
-		//	|					(empty; use the fallback translation)
-		//	|				en-us/
-		//	|					bundleone.js
-		//	|				en-gb/
-		//	|					bundleone.js
-		//	|				es/
-		//	|					bundleone.js
-		//	|					bundletwo.js
-		//	|				  ...etc
-		//	|				...
-		//
-
-		d.require("dojo.i18n");
-		d.i18n._requireLocalization.apply(d.hostenv, arguments);
-	};
-
-
-	var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");
-	var ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$");
-
-	dojo._Url = function(/*dojo._Url||String...*/){
-		// summary: 
-		//		Constructor to create an object representing a URL.
-		//		It is marked as private, since we might consider removing
-		//		or simplifying it.
-		// description: 
-		//		Each argument is evaluated in order relative to the next until
-		//		a canonical uri is produced. To get an absolute Uri relative to
-		//		the current document use:
-		//      	new dojo._Url(document.baseURI, url)
-
-		var n = null;
-
-		var _a = arguments;
-		var uri = [_a[0]];
-		// resolve uri components relative to each other
-		for(var i = 1; i<_a.length; i++){
-			if(!_a[i]){ continue; }
-
-			// Safari doesn't support this.constructor so we have to be explicit
-			// FIXME: Tracked (and fixed) in Webkit bug 3537.
-			//		http://bugs.webkit.org/show_bug.cgi?id=3537
-			var relobj = new d._Url(_a[i]+"");
-			var uriobj = new d._Url(uri[0]+"");
-
-			if(
-				relobj.path == "" &&
-				!relobj.scheme &&
-				!relobj.authority &&
-				!relobj.query
-			){
-				if(relobj.fragment != n){
-					uriobj.fragment = relobj.fragment;
-				}
-				relobj = uriobj;
-			}else if(!relobj.scheme){
-				relobj.scheme = uriobj.scheme;
-
-				if(!relobj.authority){
-					relobj.authority = uriobj.authority;
-
-					if(relobj.path.charAt(0) != "/"){
-						var path = uriobj.path.substring(0,
-							uriobj.path.lastIndexOf("/") + 1) + relobj.path;
-
-						var segs = path.split("/");
-						for(var j = 0; j < segs.length; j++){
-							if(segs[j] == "."){
-								// flatten "./" references
-								if(j == segs.length - 1){
-									segs[j] = "";
-								}else{
-									segs.splice(j, 1);
-									j--;
-								}
-							}else if(j > 0 && !(j == 1 && segs[0] == "") &&
-								segs[j] == ".." && segs[j-1] != ".."){
-								// flatten "../" references
-								if(j == (segs.length - 1)){
-									segs.splice(j, 1);
-									segs[j - 1] = "";
-								}else{
-									segs.splice(j - 1, 2);
-									j -= 2;
-								}
-							}
-						}
-						relobj.path = segs.join("/");
-					}
-				}
-			}
-
-			uri = [];
-			if(relobj.scheme){ 
-				uri.push(relobj.scheme, ":");
-			}
-			if(relobj.authority){
-				uri.push("//", relobj.authority);
-			}
-			uri.push(relobj.path);
-			if(relobj.query){
-				uri.push("?", relobj.query);
-			}
-			if(relobj.fragment){
-				uri.push("#", relobj.fragment);
-			}
-		}
-
-		this.uri = uri.join("");
-
-		// break the uri into its main components
-		var r = this.uri.match(ore);
-
-		this.scheme = r[2] || (r[1] ? "" : n);
-		this.authority = r[4] || (r[3] ? "" : n);
-		this.path = r[5]; // can never be undefined
-		this.query = r[7] || (r[6] ? "" : n);
-		this.fragment  = r[9] || (r[8] ? "" : n);
-
-		if(this.authority != n){
-			// server based naming authority
-			r = this.authority.match(ire);
-
-			this.user = r[3] || n;
-			this.password = r[4] || n;
-			this.host = r[6] || r[7]; // ipv6 || ipv4
-			this.port = r[9] || n;
-		}
-	}
-
-	dojo._Url.prototype.toString = function(){ return this.uri; };
-
-	dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){
-		//	summary: 
-		//		Returns a `dojo._Url` object relative to a module.
-		//	example:
-		//	|	var pngPath = dojo.moduleUrl("acme","images/small.png");
-		//	|	 // list the object properties
-		//	|	// create an image and set it's source to pngPath's value:
-		//	|	var img = document.createElement("img");
-		// 	|	// NOTE: we assign the string representation of the url object
-		//	|	img.src = pngPath.toString(); 
-		//	|	// add our image to the document
-		//	|	dojo.body().appendChild(img);
-		//	example: 
-		//		you may de-reference as far as you like down the package
-		//		hierarchy.  This is sometimes handy to avoid lenghty relative
-		//		urls or for building portable sub-packages. In this example,
-		//		the `acme.widget` and `acme.util` directories may be located
-		//		under different roots (see `dojo.registerModulePath`) but the
-		//		the modules which reference them can be unaware of their
-		//		relative locations on the filesystem:
-		//	|	// somewhere in a configuration block
-		//	|	dojo.registerModulePath("acme.widget", "../../acme/widget");
-		//	|	dojo.registerModulePath("acme.util", "../../util");
-		//	|	
-		//	|	// ...
-		//	|	
-		//	|	// code in a module using acme resources
-		//	|	var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html");
-		//	|	var dataPath = dojo.moduleUrl("acme.util","resources/data.json");
-
-		var loc = d._getModuleSymbols(module).join('/');
-		if(!loc){ return null; }
-		if(loc.lastIndexOf("/") != loc.length-1){
-			loc += "/";
-		}
-		
-		//If the path is an absolute path (starts with a / or is on another
-		//domain/xdomain) then don't add the baseUrl.
-		var colonIndex = loc.indexOf(":");
-		if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){
-			loc = d.baseUrl + loc;
-		}
-
-		return new d._Url(loc, url); // String
-	}
-})();
-
-/*=====
-dojo.isBrowser = {
-	//	example:
-	//	|	if(dojo.isBrowser){ ... }
-};
-
-dojo.isFF = {
-	//	example:
-	//	|	if(dojo.isFF > 1){ ... }
-};
-
-dojo.isIE = {
-	// example:
-	//	|	if(dojo.isIE > 6){
-	//	|		// we are IE7
-	// 	|	}
-};
-
-dojo.isSafari = {
-	//	example:
-	//	|	if(dojo.isSafari){ ... }
-	//	example: 
-	//		Detect iPhone:
-	//	|	if(dojo.isSafari && navigator.userAgent.indexOf("iPhone") != -1){ 
-	//	|		// we are iPhone. Note, iPod touch reports "iPod" above and fails this test.
-	//	|	}
-};
-
-dojo = {
-	// isBrowser: Boolean
-	//		True if the client is a web-browser
-	isBrowser: true,
-	//	isFF: Number | undefined
-	//		Version as a Number if client is FireFox. undefined otherwise. Corresponds to
-	//		major detected FireFox version (1.5, 2, 3, etc.)
-	isFF: 2,
-	//	isIE: Number | undefined
-	//		Version as a Number if client is MSIE(PC). undefined otherwise. Corresponds to
-	//		major detected IE version (6, 7, 8, etc.)
-	isIE: 6,
-	//	isKhtml: Number | undefined
-	//		Version as a Number if client is a KTHML-derived browser (Konqueror,
-	//		Safari, etc.). undefined otherwise. Corresponds to major detected version.
-	isKhtml: 0,
-	//	isMozilla: Number | undefined
-	//		Version as a Number if client is a Mozilla-based browser (Firefox,
-	//		SeaMonkey). undefined otherwise. Corresponds to major detected version.
-	isMozilla: 0,
-	//	isOpera: Number | undefined
-	//		Version as a Number if client is Opera. undefined otherwise. Corresponds to
-	//		major detected version.
-	isOpera: 0,
-	//	isSafari: Number | undefined
-	//		Version as a Number if client is Safari or iPhone. undefined otherwise.
-	isSafari: 0
-}
-=====*/
-
-if(typeof window != 'undefined'){
-	dojo.isBrowser = true;
-	dojo._name = "browser";
-
-
-	// attempt to figure out the path to dojo if it isn't set in the config
-	(function(){
-		var d = dojo;
-		// this is a scope protection closure. We set browser versions and grab
-		// the URL we were loaded from here.
-
-		// grab the node we were loaded from
-		if(document && document.getElementsByTagName){
-			var scripts = document.getElementsByTagName("script");
-			var rePkg = /dojo(\.xd)?\.js(\W|$)/i;
-			for(var i = 0; i < scripts.length; i++){
-				var src = scripts[i].getAttribute("src");
-				if(!src){ continue; }
-				var m = src.match(rePkg);
-				if(m){
-					// find out where we came from
-					if(!d.config.baseUrl){
-						d.config.baseUrl = src.substring(0, m.index);
-					}
-					// and find out if we need to modify our behavior
-					var cfg = scripts[i].getAttribute("djConfig");
-					if(cfg){
-						var cfgo = eval("({ "+cfg+" })");
-						for(var x in cfgo){
-							dojo.config[x] = cfgo[x];
-						}
-					}
-					break; // "first Dojo wins"
-				}
-			}
-		}
-		d.baseUrl = d.config.baseUrl;
-
-		// fill in the rendering support information in dojo.render.*
-		var n = navigator;
-		var dua = n.userAgent;
-		var dav = n.appVersion;
-		var tv = parseFloat(dav);
-
-		if(dua.indexOf("Opera") >= 0){ d.isOpera = tv; }
-		// safari detection derived from:
-		//		http://developer.apple.com/internet/safari/faq.html#anchor2
-		//		http://developer.apple.com/internet/safari/uamatrix.html
-		var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
-		if(index){
-			// try to grab the explicit Safari version first. If we don't get
-			// one, look for 419.3+ as the indication that we're on something
-			// "Safari 3-ish". Lastly, default to "Safari 2" handling.
-			d.isSafari = parseFloat(dav.split("Version/")[1]) ||
-				(parseFloat(dav.substr(index + 7)) > 419.3) ? 3 : 2;
-		}
-		if(dua.indexOf("AdobeAIR") >= 0){ d.isAIR = 1; }
-		if(dav.indexOf("Konqueror") >= 0 || d.isSafari){ d.isKhtml =  tv; }
-		if(dua.indexOf("Gecko") >= 0 && !d.isKhtml){ d.isMozilla = d.isMoz = tv; }
-		if(d.isMoz){
-			d.isFF = parseFloat(dua.split("Firefox/")[1]) || undefined;
-		}
-		if(document.all && !d.isOpera){
-			d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
-		}
-
-		//Workaround to get local file loads of dojo to work on IE 7
-		//by forcing to not use native xhr.
-		if(dojo.isIE && window.location.protocol === "file:"){
-			dojo.config.ieForceActiveXXhr=true;
-		}
-
-		var cm = document.compatMode;
-		d.isQuirks = cm == "BackCompat" || cm == "QuirksMode" || d.isIE < 6;
-
-		// TODO: is the HTML LANG attribute relevant?
-		d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase();
-
-		// These are in order of decreasing likelihood; this will change in time.
-		d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
-
-		d._xhrObj = function(){
-			// summary: 
-			//		does the work of portably generating a new XMLHTTPRequest
-			//		object.
-			var http = null;
-			var last_e = null;
-			if(!dojo.isIE || !dojo.config.ieForceActiveXXhr){
-				try{ http = new XMLHttpRequest(); }catch(e){}
-			}
-			if(!http){
-				for(var i=0; i<3; ++i){
-					var progid = d._XMLHTTP_PROGIDS[i];
-					try{
-						http = new ActiveXObject(progid);
-					}catch(e){
-						last_e = e;
-					}
-
-					if(http){
-						d._XMLHTTP_PROGIDS = [progid];  // so faster next time
-						break;
-					}
-				}
-			}
-
-			if(!http){
-				throw new Error("XMLHTTP not available: "+last_e);
-			}
-
-			return http; // XMLHTTPRequest instance
-		}
-
-		d._isDocumentOk = function(http){
-			var stat = http.status || 0;
-			return (stat >= 200 && stat < 300) || 	// Boolean
-				stat == 304 || 						// allow any 2XX response code
-				stat == 1223 || 						// get it out of the cache
-				(!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); // Internet Explorer mangled the status code
-		}
-
-		//See if base tag is in use.
-		//This is to fix http://trac.dojotoolkit.org/ticket/3973,
-		//but really, we need to find out how to get rid of the dojo._Url reference
-		//below and still have DOH work with the dojo.i18n test following some other
-		//test that uses the test frame to load a document (trac #2757).
-		//Opera still has problems, but perhaps a larger issue of base tag support
-		//with XHR requests (hasBase is true, but the request is still made to document
-		//path, not base path).
-		var owloc = window.location+"";
-		var base = document.getElementsByTagName("base");
-		var hasBase = (base && base.length > 0);
-
-		d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){
-			// summary: Read the contents of the specified uri and return those contents.
-			// uri:
-			//		A relative or absolute uri. If absolute, it still must be in
-			//		the same "domain" as we are.
-			// fail_ok:
-			//		Default false. If fail_ok and loading fails, return null
-			//		instead of throwing.
-			// returns: The response text. null is returned when there is a
-			//		failure and failure is okay (an exception otherwise)
-
-			// alert("_getText: " + uri);
-
-			// NOTE: must be declared before scope switches ie. this._xhrObj()
-			var http = this._xhrObj();
-
-			if(!hasBase && dojo._Url){
-				uri = (new dojo._Url(owloc, uri)).toString();
-			}
-			/*
-			
-			
-			alert(uri);
-			*/
-
-			if(d.config.cacheBust){
-				//Make sure we have a string before string methods are used on uri
-				uri += "";
-				uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,"");
-			}
-
-			http.open('GET', uri, false);
-			try{
-				http.send(null);
-				// alert(http);
-				if(!d._isDocumentOk(http)){
-					var err = Error("Unable to load "+uri+" status:"+ http.status);
-					err.status = http.status;
-					err.responseText = http.responseText;
-					throw err;
-				}
-			}catch(e){
-				if(fail_ok){ return null; } // null
-				// rethrow the exception
-				throw e;
-			}
-			return http.responseText; // String
-		}
-		
-		d._windowUnloaders = [];
-		
-		d.windowUnloaded = function(){
-			// summary:
-			//		signal fired by impending window destruction. You may use
-			//		dojo.addOnWIndowUnload() or dojo.connect() to this method to perform
-			//		page/application cleanup methods. See dojo.addOnWindowUnload for more info.
-			var mll = this._windowUnloaders;
-			while(mll.length){
-				(mll.pop())();
-			}
-		}
-
-		d.addOnWindowUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
-			// summary:
-			//		registers a function to be triggered when window.onunload fires.
-			//		Be careful trying to modify the DOM or access JavaScript properties
-			//		during this phase of page unloading: they may not always be available.
-			//		Consider dojo.addOnUnload() if you need to modify the DOM or do heavy
-			//		JavaScript work.
-			// example:
-			//	|	dojo.addOnWindowUnload(functionPointer)
-			//	|	dojo.addOnWindowUnload(object, "functionName")
-			//	|	dojo.addOnWindowUnload(object, function(){ /* ... */});
-	
-			d._onto(d._windowUnloaders, obj, functionName);
-		}
-	})();
-
-	dojo._initFired = false;
-	//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
-	dojo._loadInit = function(e){
-		dojo._initFired = true;
-		// allow multiple calls, only first one will take effect
-		// A bug in khtml calls events callbacks for document for event which isnt supported
-		// for example a created contextmenu event calls DOMContentLoaded, workaround
-		var type = (e && e.type) ? e.type.toLowerCase() : "load";
-		if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; }
-		arguments.callee.initialized = true;
-		if("_khtmlTimer" in dojo){
-			clearInterval(dojo._khtmlTimer);
-			delete dojo._khtmlTimer;
-		}
-
-		if(dojo._inFlightCount == 0){
-			dojo._modulesLoaded();
-		}
-	}
-
-	dojo._fakeLoadInit = function(){
-		dojo._loadInit({type: "load"});
-	}
-
-	if(!dojo.config.afterOnLoad){
-		//	START DOMContentLoaded
-		// Mozilla and Opera 9 expose the event we could use
-		if(document.addEventListener){
-			// NOTE: 
-			//		due to a threading issue in Firefox 2.0, we can't enable
-			//		DOMContentLoaded on that platform. For more information, see:
-			//		http://trac.dojotoolkit.org/ticket/1704
-			if(dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)){
-				document.addEventListener("DOMContentLoaded", dojo._loadInit, null);
-			}
-	
-			//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
-			//  also used for Mozilla because of trac #1640
-			window.addEventListener("load", dojo._loadInit, null);
-		}
-	
-		if(dojo.isAIR){
-			window.addEventListener("load", dojo._loadInit, null);
-		}else if(/(WebKit|khtml)/i.test(navigator.userAgent)){ // sniff
-			dojo._khtmlTimer = setInterval(function(){
-				if(/loaded|complete/.test(document.readyState)){
-					dojo._loadInit(); // call the onload handler
-				}
-			}, 10);
-		}
-		//	END DOMContentLoaded
-	}
-
-	(function(){
-		var _w = window;
-		var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){
-			// summary:
-			//		non-destructively adds the specified function to the node's
-			//		evtName handler.
-			// evtName: should be in the form "onclick" for "onclick" handlers.
-			// Make sure you pass in the "on" part.
-			var oldHandler = _w[evtName] || function(){};
-			_w[evtName] = function(){
-				fp.apply(_w, arguments);
-				oldHandler.apply(_w, arguments);
-			};
-		};
-
-		if(dojo.isIE){
-			// 	for Internet Explorer. readyState will not be achieved on init
-			// 	call, but dojo doesn't need it however, we'll include it
-			// 	because we don't know if there are other functions added that
-			// 	might.  Note that this has changed because the build process
-			// 	strips all comments -- including conditional ones.
-			if(!dojo.config.afterOnLoad){
-				document.write('<scr'+'ipt defer src="//:" '
-					+ 'onreadystatechange="if(this.readyState==\'complete\'){' + dojo._scopeName + '._loadInit();}">'
-					+ '</scr'+'ipt>'
-				);
-			}
-
-			try{
-				document.namespaces.add("v","urn:schemas-microsoft-com:vml");
-				document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
-			}catch(e){}
-		}
-
-		// FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
-		_handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); });
-		_handleNodeEvent("onunload", function() { dojo.windowUnloaded(); });
-	})();
-
-	/*
-	OpenAjax.subscribe("OpenAjax", "onload", function(){
-		if(dojo._inFlightCount == 0){
-			dojo._modulesLoaded();
-		}
-	});
-
-	OpenAjax.subscribe("OpenAjax", "onunload", function(){
-		dojo.unloaded();
-	});
-	*/
-} //if (typeof window != 'undefined')
-
-//Register any module paths set up in djConfig. Need to do this
-//in the hostenvs since hostenv_browser can read djConfig from a
-//script tag's attribute.
-(function(){
-	var mp = dojo.config["modulePaths"];
-	if(mp){
-		for(var param in mp){
-			dojo.registerModulePath(param, mp[param]);
-		}
-	}
-})();
-
-//Load debug code if necessary.
-if(dojo.config.isDebug){
-	dojo.require("dojo._firebug.firebug");
-}
-
-if(dojo.config.debugAtAllCosts){
-	dojo.config.useXDomain = true;
-	dojo.require("dojo._base._loader.loader_xd");
-	dojo.require("dojo._base._loader.loader_debug");
-	dojo.require("dojo.i18n");
-}
-
-if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.lang"] = true;
-dojo.provide("dojo._base.lang");
-
-// Crockford (ish) functions
-
-dojo.isString = function(/*anything*/ it){
-	//	summary:
-	//		Return true if it is a String
-	return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
-}
-
-dojo.isArray = function(/*anything*/ it){
-	//	summary:
-	//		Return true if it is an Array
-	return it && (it instanceof Array || typeof it == "array"); // Boolean
-}
-
-/*=====
-dojo.isFunction = function(it){
-	// summary: Return true if it is a Function
-	// it: anything
-	return; // Boolean
-}
-=====*/
-
-dojo.isFunction = (function(){
-	var _isFunction = function(/*anything*/ it){
-		return it && (typeof it == "function" || it instanceof Function); // Boolean
-	};
-
-	return dojo.isSafari ?
-		// only slow this down w/ gratuitious casting in Safari since it's what's b0rken
-		function(/*anything*/ it){
-			if(typeof it == "function" && it == "[object NodeList]"){ return false; }
-			return _isFunction(it); // Boolean
-		} : _isFunction;
-})();
-
-dojo.isObject = function(/*anything*/ it){
-	// summary: 
-	//		Returns true if it is a JavaScript object (or an Array, a Function
-	//		or null)
-	return it !== undefined &&
-		(it === null || typeof it == "object" || dojo.isArray(it) || dojo.isFunction(it)); // Boolean
-}
-
-dojo.isArrayLike = function(/*anything*/ it){
-	//	summary:
-	//		similar to dojo.isArray() but more permissive
-	//	description:
-	//		Doesn't strongly test for "arrayness".  Instead, settles for "isn't
-	//		a string or number and has a length property". Arguments objects
-	//		and DOM collections will return true when passed to
-	//		dojo.isArrayLike(), but will return false when passed to
-	//		dojo.isArray().
-	//	returns:
-	//		If it walks like a duck and quicks like a duck, return `true`
-	var d = dojo;
-	return it && it !== undefined && // Boolean
-		// keep out built-in constructors (Number, String, ...) which have length
-		// properties
-		!d.isString(it) && !d.isFunction(it) &&
-		!(it.tagName && it.tagName.toLowerCase() == 'form') &&
-		(d.isArray(it) || isFinite(it.length));
-}
-
-dojo.isAlien = function(/*anything*/ it){
-	// summary: 
-	//		Returns true if it is a built-in function or some other kind of
-	//		oddball that *should* report as a function but doesn't
-	return it && !dojo.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
-}
-
-dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){
-	// summary:
-	//		Adds all properties and methods of props to constructor's
-	//		prototype, making them available to all instances created with
-	//		constructor.
-	for(var i=1, l=arguments.length; i<l; i++){
-		dojo._mixin(constructor.prototype, arguments[i]);
-	}
-	return constructor; // Object
-}
-
-dojo._hitchArgs = function(scope, method /*,...*/){
-	var pre = dojo._toArray(arguments, 2);
-	var named = dojo.isString(method);
-	return function(){
-		// arrayify arguments
-		var args = dojo._toArray(arguments);
-		// locate our method
-		var f = named ? (scope||dojo.global)[method] : method;
-		// invoke with collected args
-		return f && f.apply(scope || this, pre.concat(args)); // mixed
- 	} // Function
-}
-
-dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){
-	//	summary: 
-	//		Returns a function that will only ever execute in the a given scope. 
-	//		This allows for easy use of object member functions
-	//		in callbacks and other places in which the "this" keyword may
-	//		otherwise not reference the expected scope. 
-	//		Any number of default positional arguments may be passed as parameters 
-	//		beyond "method".
-	//		Each of these values will be used to "placehold" (similar to curry)
-	//		for the hitched function. 
-	//	scope: 
-	//		The scope to use when method executes. If method is a string, 
-	//		scope is also the object containing method.
-	//	method:
-	//		A function to be hitched to scope, or the name of the method in
-	//		scope to be hitched.
-	//	example:
-	//	|	dojo.hitch(foo, "bar")(); 
-	//		runs foo.bar() in the scope of foo
-	//	example:
-	//	|	dojo.hitch(foo, myFunction);
-	//		returns a function that runs myFunction in the scope of foo
-	if(arguments.length > 2){
-		return dojo._hitchArgs.apply(dojo, arguments); // Function
-	}
-	if(!method){
-		method = scope;
-		scope = null;
-	}
-	if(dojo.isString(method)){
-		scope = scope || dojo.global;
-		if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
-		return function(){ return scope[method].apply(scope, arguments || []); }; // Function
-	}
-	return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function
-}
-
-/*=====
-dojo.delegate = function(obj, props){
-	//	summary:
-	//		returns a new object which "looks" to obj for properties which it
-	//		does not have a value for. Optionally takes a bag of properties to
-	//		seed the returned object with initially. 
-	//	description:
-	//		This is a small implementaton of the Boodman/Crockford delegation
-	//		pattern in JavaScript. An intermediate object constructor mediates
-	//		the prototype chain for the returned object, using it to delegate
-	//		down to obj for property lookup when object-local lookup fails.
-	//		This can be thought of similarly to ES4's "wrap", save that it does
-	//		not act on types but rather on pure objects.
-	//	obj:
-	//		The object to delegate to for properties not found directly on the
-	//		return object or in props.
-	//	props:
-	//		an object containing properties to assign to the returned object
-	//	returns:
-	//		an Object of anonymous type
-	//	example:
-	//	|	var foo = { bar: "baz" };
-	//	|	var thinger = dojo.delegate(foo, { thud: "xyzzy"});
-	//	|	thinger.bar == "baz"; // delegated to foo
-	//	|	foo.thud == undefined; // by definition
-	//	|	thinger.thud == "xyzzy"; // mixed in from props
-	//	|	foo.bar = "thonk";
-	//	|	thinger.bar == "thonk"; // still delegated to foo's bar
-}
-=====*/
-
-dojo.delegate = dojo._delegate = (function(){
-	// boodman/crockford delegation w/ cornford optimization
-	function TMP(){};
-	return function(obj, props){
-		TMP.prototype = obj;
-		var tmp = new TMP();
-		if(props){
-			dojo._mixin(tmp, props);
-		}
-		return tmp; // Object
-	}
-})();
-
-/*=====
-dojo._toArray = function(obj, offset, startWith){
-	//	summary:
-	//		Converts an array-like object (i.e. arguments, DOMCollection) to an
-	//		array. Returns a new Array with the elements of obj.
-	//	obj: Object
-	//		the object to "arrayify". We expect the object to have, at a
-	//		minimum, a length property which corresponds to integer-indexed
-	//		properties.
-	//	offset: Number?
-	//		the location in obj to start iterating from. Defaults to 0.
-	//		Optional.
-	//	startWith: Array?
-	//		An array to pack with the properties of obj. If provided,
-	//		properties in obj are appended at the end of startWith and
-	//		startWith is the returned array.
-}
-=====*/
-
-(function(){
-	var efficient = function(obj, offset, startWith){
-		return (startWith||[]).concat(Array.prototype.slice.call(obj, offset||0));
-	};
-
-	var slow = function(obj, offset, startWith){
-		var arr = startWith||[]; 
-		for(var x = offset || 0; x < obj.length; x++){ 
-			arr.push(obj[x]); 
-		} 
-		return arr;
-	};
-
-	dojo._toArray = (!dojo.isIE) ? efficient : function(obj){
-		return ((obj.item) ? slow : efficient).apply(this, arguments);
-	};
-
-})();
-
-dojo.partial = function(/*Function|String*/method /*, ...*/){
-	//	summary:
-	//		similar to hitch() except that the scope object is left to be
-	//		whatever the execution context eventually becomes.
-	//	description:
-	//		Calling dojo.partial is the functional equivalent of calling:
-	//		|	dojo.hitch(null, funcName, ...);
-	var arr = [ null ];
-	return dojo.hitch.apply(dojo, arr.concat(dojo._toArray(arguments))); // Function
-}
-
-dojo.clone = function(/*anything*/ o){
-	// summary:
-	//		Clones objects (including DOM nodes) and all children.
-	//		Warning: do not clone cyclic structures.
-	if(!o){ return o; }
-	if(dojo.isArray(o)){
-		var r = [];
-		for(var i = 0; i < o.length; ++i){
-			r.push(dojo.clone(o[i]));
-		}
-		return r; // Array
-	}
-	if(!dojo.isObject(o)){
-		return o;	/*anything*/
-	}
-	if(o.nodeType && o.cloneNode){ // isNode
-		return o.cloneNode(true); // Node
-	}
-	if(o instanceof Date){
-		return new Date(o.getTime());	// Date
-	}
-	// Generic objects
-	var r = new o.constructor(); // specific to dojo.declare()'d classes!
-	for(var i in o){
-		if(!(i in r) || r[i] != o[i]){
-			r[i] = dojo.clone(o[i]);
-		}
-	}
-	return r; // Object
-}
-
-dojo.trim = function(/*String*/ str){
-	// summary: 
-	//		trims whitespaces from both sides of the string
-	// description:
-	//		This version of trim() was selected for inclusion into the base due
-	//		to its compact size and relatively good performance (see Steven
-	//		Levithan's blog:
-	//		http://blog.stevenlevithan.com/archives/faster-trim-javascript).
-	//		The fastest but longest version of this function is located at
-	//		dojo.string.trim()
-	return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');	// String
-}
-
-}
-
-if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.declare"] = true;
-dojo.provide("dojo._base.declare");
-
-
-// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
-
-dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
-	//	summary: 
-	//		Create a feature-rich constructor from compact notation
-	//	className:
-	//		The name of the constructor (loosely, a "class")
-	//		stored in the "declaredClass" property in the created prototype
-	//	superclass:
-	//		May be null, a Function, or an Array of Functions. If an array, 
-	//		the first element is used as the prototypical ancestor and
-	//		any following Functions become mixin ancestors.
-	//	props:
-	//		An object whose properties are copied to the
-	//		created prototype.
-	//		Add an instance-initialization function by making it a property 
-	//		named "constructor".
-	//	description:
-	//		Create a constructor using a compact notation for inheritance and
-	//		prototype extension. 
-	//
-	//		All superclasses (including mixins) must be Functions (not simple Objects).
-	//
-	//		Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin 
-	//		ancestors are copied to the new class: changes to mixin prototypes will
-	//		not affect classes to which they have been mixed in.
-	//
-	//		"className" is cached in "declaredClass" property of the new class.
-	//
-	//	example:
-	//	|	dojo.declare("my.classes.bar", my.classes.foo, {
-	//	|		// properties to be added to the class prototype
-	//	|		someValue: 2,
-	//	|		// initialization function
-	//	|		constructor: function(){
-	//	|			this.myComplicatedObject = new ReallyComplicatedObject(); 
-	//	|		},
-	//	|		// other functions
-	//	|		someMethod: function(){ 
-	//	|			doStuff(); 
-	//	|		}
-	//	|	);
-
-	// process superclass argument
-	var dd = arguments.callee, mixins;
-	if(dojo.isArray(superclass)){
-		mixins = superclass;
-		superclass = mixins.shift();
-	}
-	// construct intermediate classes for mixins
-	if(mixins){
-		dojo.forEach(mixins, function(m){
-			if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
-			superclass = dd._delegate(superclass, m);
-		});
-	}
-	// create constructor
-	var ctor = dd._delegate(superclass);
-	// extend with "props"
-	props = props || {};
-	ctor.extend(props);
-	// more prototype decoration
-	dojo.extend(ctor, {declaredClass: className, _constructor: props.constructor/*, preamble: null*/});
-	// special help for IE
-	ctor.prototype.constructor = ctor;
-	// create named reference
-	return dojo.setObject(className, ctor); // Function
-};
-
-dojo.mixin(dojo.declare, {
-	_delegate: function(base, mixin){
-		var bp = (base||0).prototype, mp = (mixin||0).prototype, dd=dojo.declare;
-		// fresh constructor, fresh prototype
-		var ctor = dd._makeCtor();
-		// cache ancestry
-		dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dd._extend});
-		// chain prototypes
-		if(base){ctor.prototype = dojo._delegate(bp);}
-		// add mixin and core
-		dojo.extend(ctor, dd._core, mp||0, {_constructor: null, preamble: null});
-		// special help for IE
-		ctor.prototype.constructor = ctor;
-		// name this class for debugging
-		ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass;
-		return ctor;
-	},
-	_extend: function(props){
-		var i, fn;
-		for(i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} }
-		dojo.extend(this, props);
-	},
-	_makeCtor: function(){
-		// we have to make a function, but don't want to close over anything
-		return function(){ this._construct(arguments); };
-	},
-	_core: { 
-		_construct: function(args){
-			var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn;
-			// side-effect of = used on purpose here, lint may complain, don't try this at home
-			if(a[0]){ 
-				// FIXME: preambles for each mixin should be allowed
-				// FIXME: 
-				//		should we allow the preamble here NOT to modify the
-				//		default args, but instead to act on each mixin
-				//		independently of the class instance being constructed
-				//		(for impedence matching)?
-
-				// allow any first argument w/ a "preamble" property to act as a
-				// class preamble (not exclusive of the prototype preamble)
-				if(/*dojo.isFunction*/((fn = a[0].preamble))){ 
-					a = fn.apply(this, a) || a; 
-				}
-			} 
-			// prototype preamble
-			if((fn = c.prototype.preamble)){a = fn.apply(this, a) || a;}
-			// FIXME: 
-			//		need to provide an optional prototype-settable
-			//		"_explicitSuper" property which disables this
-			// initialize superclass
-			if(ct&&ct.apply){ct.apply(this, a);}
-			// initialize mixin
-			if(mct&&mct.apply){mct.apply(this, a);}
-			// initialize self
-			if((ii=c.prototype._constructor)){ii.apply(this, args);}
-			// post construction
-			if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ ct.apply(this, args); }
-		},
-		_findMixin: function(mixin){
-			var c = this.constructor, p, m;
-			while(c){
-				p = c.superclass;
-				m = c.mixin;
-				if(m==mixin || (m instanceof mixin.constructor)){return p;}
-				if(m && m._findMixin && (m=m._findMixin(mixin))){return m;}
-				c = p && p.constructor;
-			}
-		},
-		_findMethod: function(name, method, ptype, has){
-			// consciously trading readability for bytes and speed in this low-level method
-			var p=ptype, c, m, f;
-			do{
-				c = p.constructor;
-				m = c.mixin;
-				// find method by name in our mixin ancestor
-				if(m && (m=this._findMethod(name, method, m, has))){return m;}
-				// if we found a named method that either exactly-is or exactly-is-not 'method'
-				if((f=p[name])&&(has==(f==method))){return p;}
-				// ascend chain
-				p = c.superclass;
-			}while(p);
-			// if we couldn't find an ancestor in our primary chain, try a mixin chain
-			return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has);
-		},
-		inherited: function(name, args, newArgs){
-			// optionalize name argument
-			var a = arguments;
-			if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;}
-			a = newArgs||args;
-			var c = args.callee, p = this.constructor.prototype, fn, mp;
-			// if not an instance override
-			if(this[name] != c || p[name] == c){
-				// start from memoized prototype, or
-				// find a prototype that has property 'name' == 'c'
-				mp = (c.ctor||0).superclass || this._findMethod(name, c, p, true);
-				if(!mp){throw(this.declaredClass + ': inherited method "' + name + '" mismatch');}
-				// find a prototype that has property 'name' != 'c'
-				p = this._findMethod(name, c, mp, false);
-			}
-			// we expect 'name' to be in prototype 'p'
-			fn = p && p[name];
-			if(!fn){throw(mp.declaredClass + ': inherited method "' + name + '" not found');}
-			// if the function exists, invoke it in our scope
-			return fn.apply(this, a);
-		}
-	}
-});
-
-}
-
-if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.connect"] = true;
-dojo.provide("dojo._base.connect");
-
-
-// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
-
-// low-level delegation machinery
-dojo._listener = {
-	// create a dispatcher function
-	getDispatcher: function(){
-		// following comments pulled out-of-line to prevent cloning them 
-		// in the returned function.
-		// - indices (i) that are really in the array of listeners (ls) will 
-		//   not be in Array.prototype. This is the 'sparse array' trick
-		//   that keeps us safe from libs that take liberties with built-in 
-		//   objects
-		// - listener is invoked with current scope (this)
-		return function(){
-			var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target;
-			// return value comes from original target function
-			var r = t && t.apply(this, arguments);
-			// make local copy of listener array so it is immutable during processing
-			var lls;
-											lls = [].concat(ls);
-							
-			// invoke listeners after target function
-			for(var i in lls){
-				if(!(i in ap)){
-					lls[i].apply(this, arguments);
-				}
-			}
-			// return value comes from original target function
-			return r;
-		}
-	},
-	// add a listener to an object
-	add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
-		// Whenever 'method' is invoked, 'listener' will have the same scope.
-		// Trying to supporting a context object for the listener led to 
-		// complexity. 
-		// Non trivial to provide 'once' functionality here
-		// because listener could be the result of a dojo.hitch call,
-		// in which case two references to the same hitch target would not
-		// be equivalent. 
-		source = source || dojo.global;
-		// The source method is either null, a dispatcher, or some other function
-		var f = source[method];
-		// Ensure a dispatcher
-		if(!f||!f._listeners){
-			var d = dojo._listener.getDispatcher();
-			// original target function is special
-			d.target = f;
-			// dispatcher holds a list of listeners
-			d._listeners = []; 
-			// redirect source to dispatcher
-			f = source[method] = d;
-		}
-		// The contract is that a handle is returned that can 
-		// identify this listener for disconnect. 
-		//
-		// The type of the handle is private. Here is it implemented as Integer. 
-		// DOM event code has this same contract but handle is Function 
-		// in non-IE browsers.
-		//
-		// We could have separate lists of before and after listeners.
-		return f._listeners.push(listener) ; /*Handle*/
-	},
-	// remove a listener from an object
-	remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
-		var f = (source||dojo.global)[method];
-		// remember that handle is the index+1 (0 is not a valid handle)
-		if(f && f._listeners && handle--){
-			delete f._listeners[handle];
-		}
-	}
-};
-
-// Multiple delegation for arbitrary methods.
-
-// This unit knows nothing about DOM, 
-// but we include DOM aware 
-// documentation and dontFix
-// argument here to help the autodocs.
-// Actual DOM aware code is in event.js.
-
-dojo.connect = function(/*Object|null*/ obj, 
-						/*String*/ event, 
-						/*Object|null*/ context, 
-						/*String|Function*/ method,
-						/*Boolean*/ dontFix){
-	// summary:
-	//		Create a link that calls one function when another executes. 
-	//
-	// description:
-	//		Connects method to event, so that after event fires, method
-	//		does too. All connected functions are passed the same arguments as
-	//		the event function was initially called with. You may connect as
-	//		many methods to event as needed.
-	//
-	//		event must be a string. If obj is null, dojo.global is used.
-	//
-	//		null arguments may simply be omitted.
-	//
-	//		obj[event] can resolve to a function or undefined (null). 
-	//		If obj[event] is null, it is assigned a function.
-	//
-	//		The return value is a handle that is needed to 
-	//		remove this connection with dojo.disconnect.
-	//
-	// obj: 
-	//		The source object for the event function. 
-	//		Defaults to dojo.global if null.
-	//		If obj is a DOM node, the connection is delegated 
-	//		to the DOM event manager (unless dontFix is true).
-	//
-	// event:
-	//		String name of the event function in obj. 
-	//		I.e. identifies a property obj[event].
-	//
-	// context: 
-	//		The object that method will receive as "this".
-	//
-	//		If context is null and method is a function, then method
-	//		inherits the context of event.
-	//	
-	//		If method is a string then context must be the source 
-	//		object object for method (context[method]). If context is null,
-	//		dojo.global is used.
-	//
-	// method:
-	//		A function reference, or name of a function in context. 
-	//		The function identified by method fires after event does. 
-	//		method receives the same arguments as the event.
-	//		See context argument comments for information on method's scope.
-	//
-	// dontFix:
-	//		If obj is a DOM node, set dontFix to true to prevent delegation 
-	//		of this connection to the DOM event manager. 
-	//
-	// example:
-	//		When obj.onchange(), do ui.update():
-	//	|	dojo.connect(obj, "onchange", ui, "update");
-	//	|	dojo.connect(obj, "onchange", ui, ui.update); // same
-	//
-	// example:
-	//		Using return value for disconnect:
-	//	|	var link = dojo.connect(obj, "onchange", ui, "update");
-	//	|	...
-	//	|	dojo.disconnect(link);
-	//
-	// example:
-	//		When onglobalevent executes, watcher.handler is invoked:
-	//	|	dojo.connect(null, "onglobalevent", watcher, "handler");
-	//
-	// example:
-	//		When ob.onCustomEvent executes, customEventHandler is invoked:
-	//	|	dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
-	//	|	dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
-	//
-	// example:
-	//		When ob.onCustomEvent executes, customEventHandler is invoked
-	//		with the same scope (this):
-	//	|	dojo.connect(ob, "onCustomEvent", null, customEventHandler);
-	//	|	dojo.connect(ob, "onCustomEvent", customEventHandler); // same
-	//
-	// example:
-	//		When globalEvent executes, globalHandler is invoked
-	//		with the same scope (this):
-	//	|	dojo.connect(null, "globalEvent", null, globalHandler);
-	//	|	dojo.connect("globalEvent", globalHandler); // same
-
-	// normalize arguments
-	var a=arguments, args=[], i=0;
-	// if a[0] is a String, obj was ommited
-	args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);
-	// if the arg-after-next is a String or Function, context was NOT omitted
-	var a1 = a[i+1];
-	args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);
-	// absorb any additional arguments
-	for(var l=a.length; i<l; i++){	args.push(a[i]); }
-	// do the actual work
-	return dojo._connect.apply(this, args); /*Handle*/
-}
-
-// used by non-browser hostenvs. always overriden by event.js
-dojo._connect = function(obj, event, context, method){
-	var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); 
-	return [obj, event, h, l]; // Handle
-}
-
-dojo.disconnect = function(/*Handle*/ handle){
-	// summary:
-	//		Remove a link created by dojo.connect.
-	// description:
-	//		Removes the connection between event and the method referenced by handle.
-	// handle:
-	//		the return value of the dojo.connect call that created the connection.
-	if(handle && handle[0] !== undefined){
-		dojo._disconnect.apply(this, handle);
-		// let's not keep this reference
-		delete handle[0];
-	}
-}
-
-dojo._disconnect = function(obj, event, handle, listener){
-	listener.remove(obj, event, handle);
-}
-
-// topic publish/subscribe
-
-dojo._topics = {};
-
-dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){
-	//	summary:
-	//		Attach a listener to a named topic. The listener function is invoked whenever the
-	//		named topic is published (see: dojo.publish).
-	//		Returns a handle which is needed to unsubscribe this listener.
-	//	context:
-	//		Scope in which method will be invoked, or null for default scope.
-	//	method:
-	//		The name of a function in context, or a function reference. This is the function that
-	//		is invoked when topic is published.
-	//	example:
-	//	|	dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
-	//	|	dojo.publish("alerts", [ "read this", "hello world" ]);																	
-
-	// support for 2 argument invocation (omitting context) depends on hitch
-	return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/
-}
-
-dojo.unsubscribe = function(/*Handle*/ handle){
-	//	summary:
-	//	 	Remove a topic listener. 
-	//	handle:
-	//	 	The handle returned from a call to subscribe.
-	//	example:
-	//	|	var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
-	//	|	...
-	//	|	dojo.unsubscribe(alerter);
-	if(handle){
-		dojo._listener.remove(dojo._topics, handle[0], handle[1]);
-	}
-}
-
-dojo.publish = function(/*String*/ topic, /*Array*/ args){
-	//	summary:
-	//	 	Invoke all listener method subscribed to topic.
-	//	topic:
-	//	 	The name of the topic to publish.
-	//	args:
-	//	 	An array of arguments. The arguments will be applied 
-	//	 	to each topic subscriber (as first class parameters, via apply).
-	//	example:
-	//	|	dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
-	//	|	dojo.publish("alerts", [ "read this", "hello world" ]);	
-
-	// Note that args is an array, which is more efficient vs variable length
-	// argument list.  Ideally, var args would be implemented via Array
-	// throughout the APIs.
-	var f = dojo._topics[topic];
-	if(f){
-		f.apply(this, args||[]);
-	}
-}
-
-dojo.connectPublisher = function(	/*String*/ topic, 
-									/*Object|null*/ obj, 
-									/*String*/ event){
-	//	summary:
-	//	 	Ensure that everytime obj.event() is called, a message is published
-	//	 	on the topic. Returns a handle which can be passed to
-	//	 	dojo.disconnect() to disable subsequent automatic publication on
-	//	 	the topic.
-	//	topic:
-	//	 	The name of the topic to publish.
-	//	obj: 
-	//	 	The source object for the event function. Defaults to dojo.global
-	//	 	if null.
-	//	event:
-	//	 	The name of the event function in obj. 
-	//	 	I.e. identifies a property obj[event].
-	//	example:
-	//	|	dojo.connectPublisher("/ajax/start", dojo, "xhrGet");
-	var pf = function(){ dojo.publish(topic, arguments); }
-	return (event) ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle
-};
-
-}
-
-if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.Deferred"] = true;
-dojo.provide("dojo._base.Deferred");
-
-
-dojo.Deferred = function(/*Function?*/ canceller){
-	// summary:
-	//		Encapsulates a sequence of callbacks in response to a value that
-	//		may not yet be available.  This is modeled after the Deferred class
-	//		from Twisted <http://twistedmatrix.com>.
-	// description:
-	//		JavaScript has no threads, and even if it did, threads are hard.
-	//		Deferreds are a way of abstracting non-blocking events, such as the
-	//		final response to an XMLHttpRequest. Deferreds create a promise to
-	//		return a response a some point in the future and an easy way to
-	//		register your interest in receiving that response.
-	//
-	//		The most important methods for Deffered users are:
-	//
-	//			* addCallback(handler)
-	//			* addErrback(handler)
-	//			* callback(result)
-	//			* errback(result)
-	//
-	//		In general, when a function returns a Deferred, users then "fill
-	//		in" the second half of the contract by registering callbacks and
-	//		error handlers. You may register as many callback and errback
-	//		handlers as you like and they will be executed in the order
-	//		registered when a result is provided. Usually this result is
-	//		provided as the result of an asynchronous operation. The code
-	//		"managing" the Deferred (the code that made the promise to provide
-	//		an answer later) will use the callback() and errback() methods to
-	//		communicate with registered listeners about the result of the
-	//		operation. At this time, all registered result handlers are called
-	//		*with the most recent result value*.
-	//
-	//		Deferred callback handlers are treated as a chain, and each item in
-	//		the chain is required to return a value that will be fed into
-	//		successive handlers. The most minimal callback may be registered
-	//		like this:
-	//
-	//		|	var d = new dojo.Deferred();
-	//		|	d.addCallback(function(result){ return result; });
-	//
-	//		Perhaps the most common mistake when first using Deferreds is to
-	//		forget to return a value (in most cases, the value you were
-	//		passed).
-	//
-	//		The sequence of callbacks is internally represented as a list of
-	//		2-tuples containing the callback/errback pair.  For example, the
-	//		following call sequence:
-	//		
-	//		|	var d = new dojo.Deferred();
-	//		|	d.addCallback(myCallback);
-	//		|	d.addErrback(myErrback);
-	//		|	d.addBoth(myBoth);
-	//		|	d.addCallbacks(myCallback, myErrback);
-	//
-	//		is translated into a Deferred with the following internal
-	//		representation:
-	//
-	//		|	[
-	//		|		[myCallback, null],
-	//		|		[null, myErrback],
-	//		|		[myBoth, myBoth],
-	//		|		[myCallback, myErrback]
-	//		|	]
-	//
-	//		The Deferred also keeps track of its current status (fired).  Its
-	//		status may be one of three things:
-	//
-	//			* -1: no value yet (initial condition)
-	//			* 0: success
-	//			* 1: error
-	//	
-	//		A Deferred will be in the error state if one of the following three
-	//		conditions are met:
-	//
-	//			1. The result given to callback or errback is "instanceof" Error
-	//			2. The previous callback or errback raised an exception while
-	//			   executing
-	//			3. The previous callback or errback returned a value
-	//			   "instanceof" Error
-	//
-	//		Otherwise, the Deferred will be in the success state. The state of
-	//		the Deferred determines the next element in the callback sequence
-	//		to run.
-	//
-	//		When a callback or errback occurs with the example deferred chain,
-	//		something equivalent to the following will happen (imagine
-	//		that exceptions are caught and returned):
-	//
-	//		|	// d.callback(result) or d.errback(result)
-	//		|	if(!(result instanceof Error)){
-	//		|		result = myCallback(result);
-	//		|	}
-	//		|	if(result instanceof Error){
-	//		|		result = myErrback(result);
-	//		|	}
-	//		|	result = myBoth(result);
-	//		|	if(result instanceof Error){
-	//		|		result = myErrback(result);
-	//		|	}else{
-	//		|		result = myCallback(result);
-	//		|	}
-	//
-	//		The result is then stored away in case another step is added to the
-	//		callback sequence.	Since the Deferred already has a value
-	//		available, any new callbacks added will be called immediately.
-	//
-	//		There are two other "advanced" details about this implementation
-	//		that are useful:
-	//
-	//		Callbacks are allowed to return Deferred instances themselves, so
-	//		you can build complicated sequences of events with ease.
-	//
-	//		The creator of the Deferred may specify a canceller.  The canceller
-	//		is a function that will be called if Deferred.cancel is called
-	//		before the Deferred fires. You can use this to implement clean
-	//		aborting of an XMLHttpRequest, etc. Note that cancel will fire the
-	//		deferred with a CancelledError (unless your canceller returns
-	//		another kind of error), so the errbacks should be prepared to
-	//		handle that error for cancellable Deferreds.
-	// example:
-	//	|	var deferred = new dojo.Deferred();
-	//	|	setTimeout(function(){ deferred.callback({success: true}); }, 1000);
-	//	|	return deferred;
-	// example:
-	//		Deferred objects are often used when making code asynchronous. It
-	//		may be easiest to write functions in a synchronous manner and then
-	//		split code using a deferred to trigger a response to a long-lived
-	//		operation. For example, instead of register a callback function to
-	//		denote when a rendering operation completes, the function can
-	//		simply return a deferred:
-	//
-	//		|	// callback style:
-	//		|	function renderLotsOfData(data, callback){
-	//		|		var success = false
-	//		|		try{
-	//		|			for(var x in data){
-	//		|				renderDataitem(data[x]);
-	//		|			}
-	//		|			success = true;
-	//		|		}catch(e){ }
-	//		|		if(callback){
-	//		|			callback(success);
-	//		|		}
-	//		|	}
-	//
-	//		|	// using callback style
-	//		|	renderLotsOfData(someDataObj, function(success){
-	//		|		// handles success or failure
-	//		|		if(!success){
-	//		|			promptUserToRecover();
-	//		|		}
-	//		|	});
-	//		|	// NOTE: no way to add another callback here!!
-	// example:
-	//		Using a Deferred doesn't simplify the sending code any, but it
-	//		provides a standard interface for callers and senders alike,
-	//		providing both with a simple way to service multiple callbacks for
-	//		an operation and freeing both sides from worrying about details
-	//		such as "did this get called already?". With Deferreds, new
-	//		callbacks can be added at any time.
-	//
-	//		|	// Deferred style:
-	//		|	function renderLotsOfData(data){
-	//		|		var d = new dojo.Deferred();
-	//		|		try{
-	//		|			for(var x in data){
-	//		|				renderDataitem(data[x]);
-	//		|			}
-	//		|			d.callback(true);
-	//		|		}catch(e){ 
-	//		|			d.errback(new Error("rendering failed"));
-	//		|		}
-	//		|		return d;
-	//		|	}
-	//
-	//		|	// using Deferred style
-	//		|	renderLotsOfData(someDataObj).addErrback(function(){
-	//		|		promptUserToRecover();
-	//		|	});
-	//		|	// NOTE: addErrback and addCallback both return the Deferred
-	//		|	// again, so we could chain adding callbacks or save the
-	//		|	// deferred for later should we need to be notified again.
-	// example:
-	//		In this example, renderLotsOfData is syncrhonous and so both
-	//		versions are pretty artificial. Putting the data display on a
-	//		timeout helps show why Deferreds rock:
-	//
-	//		|	// Deferred style and async func
-	//		|	function renderLotsOfData(data){
-	//		|		var d = new dojo.Deferred();
-	//		|		setTimeout(function(){
-	//		|			try{
-	//		|				for(var x in data){
-	//		|					renderDataitem(data[x]);
-	//		|				}
-	//		|				d.callback(true);
-	//		|			}catch(e){ 
-	//		|				d.errback(new Error("rendering failed"));
-	//		|			}
-	//		|		}, 100);
-	//		|		return d;
-	//		|	}
-	//
-	//		|	// using Deferred style
-	//		|	renderLotsOfData(someDataObj).addErrback(function(){
-	//		|		promptUserToRecover();
-	//		|	});
-	//
-	//		Note that the caller doesn't have to change his code at all to
-	//		handle the asynchronous case.
-
-	this.chain = [];
-	this.id = this._nextId();
-	this.fired = -1;
-	this.paused = 0;
-	this.results = [null, null];
-	this.canceller = canceller;
-	this.silentlyCancelled = false;
-};
-
-dojo.extend(dojo.Deferred, {
-	/*
-	makeCalled: function(){
-		// summary:
-		//		returns a new, empty deferred, which is already in the called
-		//		state. Calling callback() or errback() on this deferred will
-		//		yeild an error and adding new handlers to it will result in
-		//		them being called immediately.
-		var deferred = new dojo.Deferred();
-		deferred.callback();
-		return deferred;
-	},
-
-	toString: function(){
-		var state;
-		if(this.fired == -1){
-			state = 'unfired';
-		}else{
-			state = this.fired ? 'success' : 'error';
-		}
-		return 'Deferred(' + this.id + ', ' + state + ')';
-	},
-	*/
-
-	_nextId: (function(){
-		var n = 1;
-		return function(){ return n++; };
-	})(),
-
-	cancel: function(){
-		// summary:	
-		//		Cancels a Deferred that has not yet received a value, or is
-		//		waiting on another Deferred as its value.
-		// description:
-		//		If a canceller is defined, the canceller is called. If the
-		//		canceller did not return an error, or there was no canceller,
-		//		then the errback chain is started.
-		var err;
-		if(this.fired == -1){
-			if(this.canceller){
-				err = this.canceller(this);
-			}else{
-				this.silentlyCancelled = true;
-			}
-			if(this.fired == -1){
-				if(!(err instanceof Error)){
-					var res = err;
-					err = new Error("Deferred Cancelled");
-					err.dojoType = "cancel";
-					err.cancelResult = res;
-				}
-				this.errback(err);
-			}
-		}else if(	(this.fired == 0) &&
-					(this.results[0] instanceof dojo.Deferred)
-		){
-			this.results[0].cancel();
-		}
-	},
-			
-
-	_resback: function(res){
-		// summary:
-		//		The private primitive that means either callback or errback
-		this.fired = ((res instanceof Error) ? 1 : 0);
-		this.results[this.fired] = res;
-		this._fire();
-	},
-
-	_check: function(){
-		if(this.fired != -1){
-			if(!this.silentlyCancelled){
-				throw new Error("already called!");
-			}
-			this.silentlyCancelled = false;
-			return;
-		}
-	},
-
-	callback: function(res){
-		//	summary:	
-		//		Begin the callback sequence with a non-error value.
-		
-		/*
-		callback or errback should only be called once on a given
-		Deferred.
-		*/
-		this._check();
-		this._resback(res);
-	},
-
-	errback: function(/*Error*/res){
-		//	summary: 
-		//		Begin the callback sequence with an error result.
-		this._check();
-		if(!(res instanceof Error)){
-			res = new Error(res);
-		}
-		this._resback(res);
-	},
-
-	addBoth: function(/*Function|Object*/cb, /*String?*/cbfn){
-		//	summary:
-		//		Add the same function as both a callback and an errback as the
-		//		next element on the callback sequence.This is useful for code
-		//		that you want to guarantee to run, e.g. a finalizer.
-		var enclosed = dojo.hitch.apply(dojo, arguments);
-		return this.addCallbacks(enclosed, enclosed); // dojo.Deferred
-	},
-
-	addCallback: function(/*Function|Object*/cb, /*String?*/cbfn /*...*/){
-		//	summary: 
-		//		Add a single callback to the end of the callback sequence.
-		return this.addCallbacks(dojo.hitch.apply(dojo, arguments)); // dojo.Deferred
-	},
-
-	addErrback: function(cb, cbfn){
-		//	summary: 
-		//		Add a single callback to the end of the callback sequence.
-		return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments)); // dojo.Deferred
-	},
-
-	addCallbacks: function(cb, eb){
-		// summary: 
-		//		Add separate callback and errback to the end of the callback
-		//		sequence.
-		this.chain.push([cb, eb])
-		if(this.fired >= 0){
-			this._fire();
-		}
-		return this; // dojo.Deferred
-	},
-
-	_fire: function(){
-		// summary: 
-		//		Used internally to exhaust the callback sequence when a result
-		//		is available.
-		var chain = this.chain;
-		var fired = this.fired;
-		var res = this.results[fired];
-		var self = this;
-		var cb = null;
-		while(
-			(chain.length > 0) &&
-			(this.paused == 0)
-		){
-			// Array
-			var f = chain.shift()[fired];
-			if(!f){ continue; }
-			var func = function(){
-				var ret = f(res);
-				//If no response, then use previous response.
-				if(typeof ret != "undefined"){
-					res = ret;
-				}
-				fired = ((res instanceof Error) ? 1 : 0);
-				if(res instanceof dojo.Deferred){
-					cb = function(res){
-						self._resback(res);
-						// inlined from _pause()
-						self.paused--;
-						if(
-							(self.paused == 0) && 
-							(self.fired >= 0)
-						){
-							self._fire();
-						}
-					}
-					// inlined from _unpause
-					this.paused++;
-				}
-			};
-			if(dojo.config.isDebug){
-				func.call(this);
-			}else{
-				try{
-					func.call(this);
-				}catch(err){
-					fired = 1;
-					res = err;
-				}
-			}
-		}
-		this.fired = fired;
-		this.results[fired] = res;
-		if((cb)&&(this.paused)){
-			// this is for "tail recursion" in case the dependent
-			// deferred is already fired
-			res.addBoth(cb);
-		}
-	}
-});
-
-}
-
-if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.json"] = true;
-dojo.provide("dojo._base.json");
-
-dojo.fromJson = function(/*String*/ json){
-	// summary:
-	// 		Parses a [JSON](http://json.org) string to return a JavaScript object.  Throws for invalid JSON strings.
-	// json: 
-	//		a string literal of a JSON item, for instance:
-	//			`'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
-
-	return eval("(" + json + ")"); // Object
-}
-
-dojo._escapeString = function(/*String*/str){
-	//summary:
-	//		Adds escape sequences for non-visual characters, double quote and
-	//		backslash and surrounds with double quotes to form a valid string
-	//		literal.
-	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"').
-		replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n").
-		replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string
-}
-
-dojo.toJsonIndentStr = "\t";
-dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){
-	// summary:
-	//		Returns a [JSON](http://json.org) serialization of an object.
-	//
-	// description:
-	//		Returns a [JSON](http://json.org) serialization of an object.
-	//		Note that this doesn't check for infinite recursion, so don't do that!
-	//
-	// it:
-	//		an object to be serialized. Objects may define their own
-	//		serialization via a special "__json__" or "json" function
-	//		property. If a specialized serializer has been defined, it will
-	//		be used as a fallback.
-	//
-	// prettyPrint:
-	//		if true, we indent objects and arrays to make the output prettier.
-	//		The variable dojo.toJsonIndentStr is used as the indent string 
-	//		-- to use something other than the default (tab), 
-	//		change that variable before calling dojo.toJson().
-	//
-	// _indentStr:
-	//		private variable for recursive calls when pretty printing, do not use.
-
-	if(it === undefined){
-		return "undefined";
-	}
-	var objtype = typeof it;
-	if(objtype == "number" || objtype == "boolean"){
-		return it + "";
-	}
-	if(it === null){
-		return "null";
-	}
-	if(dojo.isString(it)){ 
-		return dojo._escapeString(it); 
-	}
-	// recurse
-	var recurse = arguments.callee;
-	// short-circuit for objects that support "json" serialization
-	// if they return "self" then just pass-through...
-	var newObj;
-	_indentStr = _indentStr || "";
-	var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : "";
-	var tf = it.__json__||it.json;
-	if(dojo.isFunction(tf)){
-		newObj = tf.call(it);
-		if(it !== newObj){
-			return recurse(newObj, prettyPrint, nextIndent);
-		}
-	}
-	if(it.nodeType && it.cloneNode){ // isNode
-		// we can't seriailize DOM nodes as regular objects because they have cycles
-		// DOM nodes could be serialized with something like outerHTML, but
-		// that can be provided by users in the form of .json or .__json__ function.
-		throw new Error("Can't serialize DOM nodes");
-	}
-
-	var sep = prettyPrint ? " " : "";
-	var newLine = prettyPrint ? "\n" : "";
-
-	// array
-	if(dojo.isArray(it)){
-		var res = dojo.map(it, function(obj){
-			var val = recurse(obj, prettyPrint, nextIndent);
-			if(typeof val != "string"){
-				val = "undefined";
-			}
-			return newLine + nextIndent + val;
-		});
-		return "[" + res.join("," + sep) + newLine + _indentStr + "]";
-	}
-	/*
-	// look in the registry
-	try {
-		window.o = it;
-		newObj = dojo.json.jsonRegistry.match(it);
-		return recurse(newObj, prettyPrint, nextIndent);
-	}catch(e){
-		// 
-	}
-	// it's a function with no adapter, skip it
-	*/
-	if(objtype == "function"){
-		return null; // null
-	}
-	// generic object code path
-	var output = [], key;
-	for(key in it){
-		var keyStr, val;
-		if(typeof key == "number"){
-			keyStr = '"' + key + '"';
-		}else if(typeof key == "string"){
-			keyStr = dojo._escapeString(key);
-		}else{
-			// skip non-string or number keys
-			continue;
-		}
-		val = recurse(it[key], prettyPrint, nextIndent);
-		if(typeof val != "string"){
-			// skip non-serializable values
-			continue;
-		}
-		// FIXME: use += on Moz!!
-		//	 MOW NOTE: using += is a pain because you have to account for the dangling comma...
-		output.push(newLine + nextIndent + keyStr + ":" + sep + val);
-	}
-	return "{" + output.join("," + sep) + newLine + _indentStr + "}"; // String
-}
-
-}
-
-if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.array"] = true;
-
-dojo.provide("dojo._base.array");
-
-(function(){
-	var _getParts = function(arr, obj, cb){
-		return [ 
-			dojo.isString(arr) ? arr.split("") : arr, 
-			obj || dojo.global,
-			// FIXME: cache the anonymous functions we create here?
-			dojo.isString(cb) ? new Function("item", "index", "array", cb) : cb
-		];
-	};
-
-	dojo.mixin(dojo, {
-		indexOf: function(	/*Array*/		array, 
-							/*Object*/		value,
-							/*Integer?*/	fromIndex,
-							/*Boolean?*/	findLast){
-			// summary:
-			//		locates the first index of the provided value in the
-			//		passed array. If the value is not found, -1 is returned.
-			// description:
-			//		For details on this method, see:
-			// 			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
-
-			var step = 1, end = array.length || 0, i = 0;
-			if(findLast){
-				i = end - 1;
-				step = end = -1;
-			}
-			if(fromIndex != undefined){ i = fromIndex; }
-			if((findLast && i > end) || i < end){
-				for(; i != end; i += step){
-					if(array[i] == value){ return i; }
-				}
-			}
-			return -1;	// Number
-		},
-
-		lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){
-			// summary:
-			//		locates the last index of the provided value in the passed
-			//		array. If the value is not found, -1 is returned.
-			// description:
-			//		For details on this method, see:
-			// 			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf
-			return dojo.indexOf(array, value, fromIndex, true); // Number
-		},
-
-		forEach: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
-			//	summary:
-			//		for every item in arr, callback is invoked. Return values are ignored.
-			//	arr:
-			//		the array to iterate over. If a string, operates on individual characters.
-			//	callback:
-			//		a function is invoked with three arguments: item, index, and array
-			//	thisObject:
-			//		may be used to scope the call to callback
-			//	description:
-			//		This function corresponds to the JavaScript 1.6
-			//		Array.forEach() method. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
-			//	example:
-			//	|	// log out all members of the array:
-			//	|	dojo.forEach(
-			//	|		[ "thinger", "blah", "howdy", 10 ],
-			//	|		function(item){
-			//	|			
-			//	|		}
-			//	|	);
-			//	example:
-			//	|	// log out the members and their indexes
-			//	|	dojo.forEach(
-			//	|		[ "thinger", "blah", "howdy", 10 ],
-			//	|		function(item, idx, arr){
-			//	|			
-			//	|		}
-			//	|	);
-			//	example:
-			//	|	// use a scoped object member as the callback
-			//	|	
-			//	|	var obj = {
-			//	|		prefix: "logged via obj.callback:", 
-			//	|		callback: function(item){
-			//	|			
-			//	|		}
-			//	|	};
-			//	|	
-			//	|	// specifying the scope function executes the callback in that scope
-			//	|	dojo.forEach(
-			//	|		[ "thinger", "blah", "howdy", 10 ],
-			//	|		obj.callback,
-			//	|		obj
-			//	|	);
-			//	|	
-			//	|	// alternately, we can accomplish the same thing with dojo.hitch()
-			//	|	dojo.forEach(
-			//	|		[ "thinger", "blah", "howdy", 10 ],
-			//	|		dojo.hitch(obj, "callback")
-			//	|	);
-
-			// match the behavior of the built-in forEach WRT empty arrs
-			if(!arr || !arr.length){ return; }
-
-			// FIXME: there are several ways of handilng thisObject. Is
-			// dojo.global always the default context?
-			var _p = _getParts(arr, thisObject, callback); arr = _p[0];
-			for(var i=0,l=arr.length; i<l; ++i){ 
-				_p[2].call(_p[1], arr[i], i, arr);
-			}
-		},
-
-		_everyOrSome: function(/*Boolean*/every, /*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
-			var _p = _getParts(arr, thisObject, callback); arr = _p[0];
-			for(var i=0,l=arr.length; i<l; ++i){
-				var result = !!_p[2].call(_p[1], arr[i], i, arr);
-				if(every ^ result){
-					return result; // Boolean
-				}
-			}
-			return every; // Boolean
-		},
-
-		every: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
-			// summary:
-			//		Determines whether or not every item in arr satisfies the
-			//		condition implemented by callback.
-			// arr:
-			//		the array to iterate on. If a string, operates on individual characters.
-			// callback:
-			//		a function is invoked with three arguments: item, index,
-			//		and array and returns true if the condition is met.
-			// thisObject:
-			//		may be used to scope the call to callback
-			// description:
-			//		This function corresponds to the JavaScript 1.6
-			//		Array.every() method. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every
-			// example:
-			//	|	// returns false
-			//	|	dojo.every([1, 2, 3, 4], function(item){ return item>1; });
-			// example:
-			//	|	// returns true 
-			//	|	dojo.every([1, 2, 3, 4], function(item){ return item>0; });
-			return this._everyOrSome(true, arr, callback, thisObject); // Boolean
-		},
-
-		some: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
-			// summary:
-			//		Determines whether or not any item in arr satisfies the
-			//		condition implemented by callback.
-			// arr:
-			//		the array to iterate over. If a string, operates on individual characters.
-			// callback:
-			//		a function is invoked with three arguments: item, index,
-			//		and array and returns true if the condition is met.
-			// thisObject:
-			//		may be used to scope the call to callback
-			// description:
-			//		This function corresponds to the JavaScript 1.6
-			//		Array.some() method. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
-			// example:
-			//	|	// is true
-			//	|	dojo.some([1, 2, 3, 4], function(item){ return item>1; });
-			// example:
-			//	|	// is false
-			//	|	dojo.some([1, 2, 3, 4], function(item){ return item<1; });
-			return this._everyOrSome(false, arr, callback, thisObject); // Boolean
-		},
-
-		map: function(/*Array|String*/arr, /*Function|String*/callback, /*Function?*/thisObject){
-			// summary:
-			//		applies callback to each element of arr and returns
-			//		an Array with the results
-			// arr:
-			//		the array to iterate on. If a string, operates on
-			//		individual characters.
-			// callback:
-			//		a function is invoked with three arguments, (item, index,
-			//		array),  and returns a value
-			// thisObject:
-			//		may be used to scope the call to callback
-			// description:
-			//		This function corresponds to the JavaScript 1.6 Array.map()
-			//		method. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
-			// example:
-			//	|	// returns [2, 3, 4, 5]
-			//	|	dojo.map([1, 2, 3, 4], function(item){ return item+1 });
-
-			var _p = _getParts(arr, thisObject, callback); arr = _p[0];
-			var outArr = (arguments[3] ? (new arguments[3]()) : []);
-			for(var i=0,l=arr.length; i<l; ++i){
-				outArr.push(_p[2].call(_p[1], arr[i], i, arr));
-			}
-			return outArr; // Array
-		},
-
-		filter: function(/*Array*/arr, /*Function|String*/callback, /*Object?*/thisObject){
-			// summary:
-			//		Returns a new Array with those items from arr that match the
-			//		condition implemented by callback.
-			// arr:
-			//		the array to iterate over.
-			// callback:
-			//		a function that is invoked with three arguments (item,
-			//		index, array). The return of this function is expected to
-			//		be a boolean which determines whether the passed-in item
-			//		will be included in the returned array.
-			// thisObject:
-			//		may be used to scope the call to callback
-			// description:
-			//		This function corresponds to the JavaScript 1.6
-			//		Array.filter() method. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
-			// example:
-			//	|	// returns [2, 3, 4]
-			//	|	dojo.filter([1, 2, 3, 4], function(item){ return item>1; });
-
-			var _p = _getParts(arr, thisObject, callback); arr = _p[0];
-			var outArr = [];
-			for(var i=0,l=arr.length; i<l; ++i){
-				if(_p[2].call(_p[1], arr[i], i, arr)){
-					outArr.push(arr[i]);
-				}
-			}
-			return outArr; // Array
-		}
-	});
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.Color"] = true;
-dojo.provide("dojo._base.Color");
-
-
-
-dojo.Color = function(/*Array|String|Object*/ color){
-	// summary:
-	//		takes a named string, hex string, array of rgb or rgba values,
-	//		an object with r, g, b, and a properties, or another dojo.Color object
-	if(color){ this.setColor(color); }
-};
-
-// FIXME: there's got to be a more space-efficient way to encode or discover these!!  Use hex?
-dojo.Color.named = {
-	black:      [0,0,0],
-	silver:     [192,192,192],
-	gray:       [128,128,128],
-	white:      [255,255,255],
-	maroon:		[128,0,0],
-	red:        [255,0,0],
-	purple:		[128,0,128],
-	fuchsia:	[255,0,255],
-	green:	    [0,128,0],
-	lime:	    [0,255,0],
-	olive:		[128,128,0],
-	yellow:		[255,255,0],
-	navy:       [0,0,128],
-	blue:       [0,0,255],
-	teal:		[0,128,128],
-	aqua:		[0,255,255]
-};
-
-
-dojo.extend(dojo.Color, {
-	r: 255, g: 255, b: 255, a: 1,
-	_set: function(r, g, b, a){
-		var t = this; t.r = r; t.g = g; t.b = b; t.a = a;
-	},
-	setColor: function(/*Array|String|Object*/ color){
-		// summary:
-		//		takes a named string, hex string, array of rgb or rgba values,
-		//		an object with r, g, b, and a properties, or another dojo.Color object
-		var d = dojo;
-		if(d.isString(color)){
-			d.colorFromString(color, this);
-		}else if(d.isArray(color)){
-			d.colorFromArray(color, this);
-		}else{
-			this._set(color.r, color.g, color.b, color.a);
-			if(!(color instanceof d.Color)){ this.sanitize(); }
-		}
-		return this;	// dojo.Color
-	},
-	sanitize: function(){
-		// summary:
-		//		makes sure that the object has correct attributes
-		// description: 
-		//		the default implementation does nothing, include dojo.colors to
-		//		augment it to real checks
-		return this;	// dojo.Color
-	},
-	toRgb: function(){
-		// summary: returns 3 component array of rgb values
-		var t = this;
-		return [t.r, t.g, t.b];	// Array
-	},
-	toRgba: function(){
-		// summary: returns a 4 component array of rgba values
-		var t = this;
-		return [t.r, t.g, t.b, t.a];	// Array
-	},
-	toHex: function(){
-		// summary: returns a css color string in hexadecimal representation
-		var arr = dojo.map(["r", "g", "b"], function(x){
-			var s = this[x].toString(16);
-			return s.length < 2 ? "0" + s : s;
-		}, this);
-		return "#" + arr.join("");	// String
-	},
-	toCss: function(/*Boolean?*/ includeAlpha){
-		// summary: returns a css color string in rgb(a) representation
-		var t = this, rgb = t.r + ", " + t.g + ", " + t.b;
-		return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")";	// String
-	},
-	toString: function(){
-		// summary: returns a visual representation of the color
-		return this.toCss(true); // String
-	}
-});
-
-dojo.blendColors = function(
-	/*dojo.Color*/ start, 
-	/*dojo.Color*/ end, 
-	/*Number*/ weight,
-	/*dojo.Color?*/ obj
-){
-	// summary: 
-	//		blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend,
-	//		can reuse a previously allocated dojo.Color object for the result
-	var d = dojo, t = obj || new dojo.Color();
-	d.forEach(["r", "g", "b", "a"], function(x){
-		t[x] = start[x] + (end[x] - start[x]) * weight;
-		if(x != "a"){ t[x] = Math.round(t[x]); }
-	});
-	return t.sanitize();	// dojo.Color
-};
-
-dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
-	// summary: get rgb(a) array from css-style color declarations
-	var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);
-	return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj);	// dojo.Color
-};
-
-dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){
-	// summary: converts a hex string with a '#' prefix to a color object.
-	//	Supports 12-bit #rgb shorthand.
-	var d = dojo, t = obj || new d.Color(),
-		bits = (color.length == 4) ? 4 : 8,
-		mask = (1 << bits) - 1;
-	color = Number("0x" + color.substr(1));
-	if(isNaN(color)){
-		return null; // dojo.Color
-	}
-	d.forEach(["b", "g", "r"], function(x){
-		var c = color & mask;
-		color >>= bits;
-		t[x] = bits == 4 ? 17 * c : c;
-	});
-	t.a = 1;
-	return t;	// dojo.Color
-};
-
-dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){
-	// summary: builds a color from 1, 2, 3, or 4 element array
-	var t = obj || new dojo.Color();
-	t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3]));
-	if(isNaN(t.a)){ t.a = 1; }
-	return t.sanitize();	// dojo.Color
-};
-
-dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){
-	//	summary:
-	//		parses str for a color value.
-	//	description:
-	//		Acceptable input values for str may include arrays of any form
-	//		accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or
-	//		rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10,
-	//		10, 50)"
-	//	returns:
-	//		a dojo.Color object. If obj is passed, it will be the return value.
-	var a = dojo.Color.named[str];
-	return a && dojo.colorFromArray(a, obj) || dojo.colorFromRgb(str, obj) || dojo.colorFromHex(str, obj);
-};
-
-}
-
-if(!dojo._hasResource["dojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base"] = true;
-dojo.provide("dojo._base");
-
-
-
-
-
-
-
-
-
-}
-
-if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.window"] = true;
-dojo.provide("dojo._base.window");
-
-/*=====
-dojo.doc = {
-	// summary:
-	//		Alias for the current document. 'dojo.doc' can be modified
-	//		for temporary context shifting. Also see dojo.withDoc().
-	// description:
-	//    Refer to dojo.doc rather
-	//    than referring to 'window.document' to ensure your code runs
-	//    correctly in managed contexts.
-	// example:
-	// 	|	n.appendChild(dojo.doc.createElement('div'));
-}
-=====*/
-dojo.doc = window["document"] || null;
-
-dojo.body = function(){
-	// summary:
-	//		Return the body element of the document
-	//		return the body object associated with dojo.doc
-	// example:
-	// 	|	dojo.body().appendChild(dojo.doc.createElement('div'));
-
-	// Note: document.body is not defined for a strict xhtml document
-	// Would like to memoize this, but dojo.doc can change vi dojo.withDoc().
-	return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node
-}
-
-dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
-	// summary:
-	//		changes the behavior of many core Dojo functions that deal with
-	//		namespace and DOM lookup, changing them to work in a new global
-	//		context (e.g., an iframe). The varibles dojo.global and dojo.doc
-	//		are modified as a result of calling this function and the result of
-	//		`dojo.body()` likewise differs.
-	dojo.global = globalObject;
-	dojo.doc = globalDocument;
-};
-
-dojo._fireCallback = function(callback, context, cbArguments){
-	if(context && dojo.isString(callback)){
-		callback = context[callback];
-	}
-	return callback.apply(context, cbArguments || [ ]);
-}
-
-dojo.withGlobal = function(	/*Object*/globalObject, 
-							/*Function*/callback, 
-							/*Object?*/thisObject, 
-							/*Array?*/cbArguments){
-	// summary:
-	//		Call callback with globalObject as dojo.global and
-	//		globalObject.document as dojo.doc. If provided, globalObject
-	//		will be executed in the context of object thisObject
-	// description:
-	//		When callback() returns or throws an error, the dojo.global
-	//		and dojo.doc will be restored to its previous state.
-	var rval;
-	var oldGlob = dojo.global;
-	var oldDoc = dojo.doc;
-	try{
-		dojo.setContext(globalObject, globalObject.document);
-		rval = dojo._fireCallback(callback, thisObject, cbArguments);
-	}finally{
-		dojo.setContext(oldGlob, oldDoc);
-	}
-	return rval;
-}
-
-dojo.withDoc = function(	/*Object*/documentObject, 
-							/*Function*/callback, 
-							/*Object?*/thisObject, 
-							/*Array?*/cbArguments){
-	// summary:
-	//		Call callback with documentObject as dojo.doc. If provided,
-	//		callback will be executed in the context of object thisObject
-	// description:
-	//		When callback() returns or throws an error, the dojo.doc will
-	//		be restored to its previous state.
-	var rval;
-	var oldDoc = dojo.doc;
-	try{
-		dojo.doc = documentObject;
-		rval = dojo._fireCallback(callback, thisObject, cbArguments);
-	}finally{
-		dojo.doc = oldDoc;
-	}
-	return rval;
-};
-
-}
-
-if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.event"] = true;
-dojo.provide("dojo._base.event");
-
-
-// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
-
-(function(){
-	// DOM event listener machinery
-	var del = (dojo._event_listener = {
-		add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){
-			if(!node){return;} 
-			name = del._normalizeEventName(name);
-			fp = del._fixCallback(name, fp);
-			var oname = name;
-			if(!dojo.isIE && (name == "mouseenter" || name == "mouseleave")){
-				var ofp = fp;
-				//oname = name;
-				name = (name == "mouseenter") ? "mouseover" : "mouseout";
-				fp = function(e){
-					// check tagName to fix a FF2 bug with invalid nodes (hidden child DIV of INPUT)
-					// which causes isDecendant to return false which causes
-					// spurious, and more importantly, incorrect mouse events to fire.
-					// TODO: remove tagName check when Firefox 2 is no longer supported
-					try{ e.relatedTarget.tagName; } catch(e2){ return; }
-					if(!dojo.isDescendant(e.relatedTarget, node)){
-						// e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable.
-						return ofp.call(this, e); 
-					}
-				}
-			}
-			node.addEventListener(name, fp, false);
-			return fp; /*Handle*/
-		},
-		remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
-			// summary:
-			//		clobbers the listener from the node
-			// node:
-			//		DOM node to attach the event to
-			// event:
-			//		the name of the handler to remove the function from
-			// handle:
-			//		the handle returned from add
-			if (node){
-				event = del._normalizeEventName(event);
-				if(!dojo.isIE && (event == "mouseenter" || event == "mouseleave")){
-					event = (event == "mouseenter") ? "mouseover" : "mouseout";
-				}
-
-				node.removeEventListener(event, handle, false);
-			}
-		},
-		_normalizeEventName: function(/*String*/name){
-			// Generally, name should be lower case, unless it is special
-			// somehow (e.g. a Mozilla DOM event).
-			// Remove 'on'.
-			return name.slice(0,2) =="on" ? name.slice(2) : name;
-		},
-		_fixCallback: function(/*String*/name, fp){
-			// By default, we only invoke _fixEvent for 'keypress'
-			// If code is added to _fixEvent for other events, we have
-			// to revisit this optimization.
-			// This also applies to _fixEvent overrides for Safari and Opera
-			// below.
-			return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };
-		},
-		_fixEvent: function(evt, sender){
-			// _fixCallback only attaches us to keypress.
-			// Switch on evt.type anyway because we might 
-			// be called directly from dojo.fixEvent.
-			switch(evt.type){
-				case "keypress":
-					del._setKeyChar(evt);
-					break;
-			}
-			return evt;
-		},
-		_setKeyChar: function(evt){
-			evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
-			evt.charOrCode = evt.keyChar || evt.keyCode;
-		},
-		// For IE and Safari: some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE
-		// we map those virtual key codes to ascii here
-		// not valid for all (non-US) keyboards, so maybe we shouldn't bother
-		_punctMap: { 
-			106:42, 
-			111:47, 
-			186:59, 
-			187:43, 
-			188:44, 
-			189:45, 
-			190:46, 
-			191:47, 
-			192:96, 
-			219:91, 
-			220:92, 
-			221:93, 
-			222:39 
-		}
-	});
-
-	// DOM events
-	
-	dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
-		// summary:
-		//		normalizes properties on the event object including event
-		//		bubbling methods, keystroke normalization, and x/y positions
-		// evt: Event
-		//		native event object
-		// sender: DOMNode
-		//		node to treat as "currentTarget"
-		return del._fixEvent(evt, sender);
-	}
-
-	dojo.stopEvent = function(/*Event*/evt){
-		// summary:
-		//		prevents propagation and clobbers the default action of the
-		//		passed event
-		// evt: Event
-		//		The event object. If omitted, window.event is used on IE.
-		evt.preventDefault();
-		evt.stopPropagation();
-		// NOTE: below, this method is overridden for IE
-	}
-
-	// the default listener to use on dontFix nodes, overriden for IE
-	var node_listener = dojo._listener;
-	
-	// Unify connect and event listeners
-	dojo._connect = function(obj, event, context, method, dontFix){
-		// FIXME: need a more strict test
-		var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener);
-		// choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node
-		// we need the third option to provide leak prevention on broken browsers (IE)
-		var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid];
-		// create a listener
-		var h = l.add(obj, event, dojo.hitch(context, method));
-		// formerly, the disconnect package contained "l" directly, but if client code
-		// leaks the disconnect package (by connecting it to a node), referencing "l" 
-		// compounds the problem.
-		// instead we return a listener id, which requires custom _disconnect below.
-		// return disconnect package
-		return [ obj, event, h, lid ];
-	}
-
-	dojo._disconnect = function(obj, event, handle, listener){
-		([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);
-	}
-
-	// Constants
-
-	// Public: client code should test
-	// keyCode against these named constants, as the
-	// actual codes can vary by browser.
-	dojo.keys = {
-		// summary: definitions for common key values
-		BACKSPACE: 8,
-		TAB: 9,
-		CLEAR: 12,
-		ENTER: 13,
-		SHIFT: 16,
-		CTRL: 17,
-		ALT: 18,
-		PAUSE: 19,
-		CAPS_LOCK: 20,
-		ESCAPE: 27,
-		SPACE: 32,
-		PAGE_UP: 33,
-		PAGE_DOWN: 34,
-		END: 35,
-		HOME: 36,
-		LEFT_ARROW: 37,
-		UP_ARROW: 38,
-		RIGHT_ARROW: 39,
-		DOWN_ARROW: 40,
-		INSERT: 45,
-		DELETE: 46,
-		HELP: 47,
-		LEFT_WINDOW: 91,
-		RIGHT_WINDOW: 92,
-		SELECT: 93,
-		NUMPAD_0: 96,
-		NUMPAD_1: 97,
-		NUMPAD_2: 98,
-		NUMPAD_3: 99,
-		NUMPAD_4: 100,
-		NUMPAD_5: 101,
-		NUMPAD_6: 102,
-		NUMPAD_7: 103,
-		NUMPAD_8: 104,
-		NUMPAD_9: 105,
-		NUMPAD_MULTIPLY: 106,
-		NUMPAD_PLUS: 107,
-		NUMPAD_ENTER: 108,
-		NUMPAD_MINUS: 109,
-		NUMPAD_PERIOD: 110,
-		NUMPAD_DIVIDE: 111,
-		F1: 112,
-		F2: 113,
-		F3: 114,
-		F4: 115,
-		F5: 116,
-		F6: 117,
-		F7: 118,
-		F8: 119,
-		F9: 120,
-		F10: 121,
-		F11: 122,
-		F12: 123,
-		F13: 124,
-		F14: 125,
-		F15: 126,
-		NUM_LOCK: 144,
-		SCROLL_LOCK: 145
-	};
-	
-	// IE event normalization
-	if(dojo.isIE){ 
-		var _trySetKeyCode = function(e, code){
-			try{
-				// squelch errors when keyCode is read-only
-				// (e.g. if keyCode is ctrl or shift)
-				return (e.keyCode = code);
-			}catch(e){
-				return 0;
-			}
-		}
-
-		// by default, use the standard listener
-		var iel = dojo._listener;
-		var listenersName = dojo._ieListenersName = "_" + dojo._scopeName + "_listeners";
-		// dispatcher tracking property
-		if(!dojo.config._allow_leaks){
-			// custom listener that handles leak protection for DOM events
-			node_listener = iel = dojo._ie_listener = {
-				// support handler indirection: event handler functions are 
-				// referenced here. Event dispatchers hold only indices.
-				handlers: [],
-				// add a listener to an object
-				add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
-					source = source || dojo.global;
-					var f = source[method];
-					if(!f||!f[listenersName]){
-						var d = dojo._getIeDispatcher();
-						// original target function is special
-						d.target = f && (ieh.push(f) - 1);
-						// dispatcher holds a list of indices into handlers table
-						d[listenersName] = [];
-						// redirect source to dispatcher
-						f = source[method] = d;
-					}
-					return f[listenersName].push(ieh.push(listener) - 1) ; /*Handle*/
-				},
-				// remove a listener from an object
-				remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
-					var f = (source||dojo.global)[method], l = f && f[listenersName];
-					if(f && l && handle--){
-						delete ieh[l[handle]];
-						delete l[handle];
-					}
-				}
-			};
-			// alias used above
-			var ieh = iel.handlers;
-		}
-
-		dojo.mixin(del, {
-			add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){
-				if(!node){return;} // undefined
-				event = del._normalizeEventName(event);
-				if(event=="onkeypress"){
-					// we need to listen to onkeydown to synthesize
-					// keypress events that otherwise won't fire
-					// on IE
-					var kd = node.onkeydown;
-					if(!kd || !kd[listenersName] || !kd._stealthKeydownHandle){
-						var h = del.add(node, "onkeydown", del._stealthKeyDown);
-						kd = node.onkeydown;
-						kd._stealthKeydownHandle = h;
-						kd._stealthKeydownRefs = 1;
-					}else{
-						kd._stealthKeydownRefs++;
-					}
-				}
-				return iel.add(node, event, del._fixCallback(fp));
-			},
-			remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
-				event = del._normalizeEventName(event);
-				iel.remove(node, event, handle); 
-				if(event=="onkeypress"){
-					var kd = node.onkeydown;
-					if(--kd._stealthKeydownRefs <= 0){
-						iel.remove(node, "onkeydown", kd._stealthKeydownHandle);
-						delete kd._stealthKeydownHandle;
-					}
-				}
-			},
-			_normalizeEventName: function(/*String*/eventName){
-				// Generally, eventName should be lower case, unless it is
-				// special somehow (e.g. a Mozilla event)
-				// ensure 'on'
-				return eventName.slice(0,2) != "on" ? "on" + eventName : eventName;
-			},
-			_nop: function(){},
-			_fixEvent: function(/*Event*/evt, /*DOMNode*/sender){
-				// summary:
-				//		normalizes properties on the event object including event
-				//		bubbling methods, keystroke normalization, and x/y positions
-				// evt: native event object
-				// sender: node to treat as "currentTarget"
-				if(!evt){
-					var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window;
-					evt = w.event; 
-				}
-				if(!evt){return(evt);}
-				evt.target = evt.srcElement; 
-				evt.currentTarget = (sender || evt.srcElement); 
-				evt.layerX = evt.offsetX;
-				evt.layerY = evt.offsetY;
-				// FIXME: scroll position query is duped from dojo.html to
-				// avoid dependency on that entire module. Now that HTML is in
-				// Base, we should convert back to something similar there.
-				var se = evt.srcElement, doc = (se && se.ownerDocument) || document;
-				// DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used
-				// here rather than document.body
-				var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
-				var offset = dojo._getIeDocumentElementOffset();
-				evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;
-				evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y;
-				if(evt.type == "mouseover"){ 
-					evt.relatedTarget = evt.fromElement;
-				}
-				if(evt.type == "mouseout"){ 
-					evt.relatedTarget = evt.toElement;
-				}
-				evt.stopPropagation = del._stopPropagation;
-				evt.preventDefault = del._preventDefault;
-				return del._fixKeys(evt);
-			},
-			_fixKeys: function(evt){
-				switch(evt.type){
-					case "keypress":
-						var c = ("charCode" in evt ? evt.charCode : evt.keyCode);
-						if (c==10){
-							// CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
-							c=0;
-							evt.keyCode = 13;
-						}else if(c==13||c==27){
-							c=0; // Mozilla considers ENTER and ESC non-printable
-						}else if(c==3){
-							c=99; // Mozilla maps CTRL-BREAK to CTRL-c
-						}
-						// Mozilla sets keyCode to 0 when there is a charCode
-						// but that stops the event on IE.
-						evt.charCode = c;
-						del._setKeyChar(evt);
-						break;
-				}
-				return evt;
-			},
-			_stealthKeyDown: function(evt){
-				// IE doesn't fire keypress for most non-printable characters.
-				// other browsers do, we simulate it here.
-				var kp = evt.currentTarget.onkeypress;
-				// only works if kp exists and is a dispatcher
-				if(!kp || !kp[listenersName]){ return; }
-				// munge key/charCode
-				var k=evt.keyCode;
-				// These are Windows Virtual Key Codes
-				// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
-				var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);
-				// synthesize keypress for most unprintables and CTRL-keys
-				if(unprintable||evt.ctrlKey){
-					var c = unprintable ? 0 : k;
-					if(evt.ctrlKey){
-						if(k==3 || k==13){
-							return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively 
-						}else if(c>95 && c<106){ 
-							c -= 48; // map CTRL-[numpad 0-9] to ASCII
-						}else if((!evt.shiftKey)&&(c>=65&&c<=90)){ 
-							c += 32; // map CTRL-[A-Z] to lowercase
-						}else{ 
-							c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
-						}
-					}
-					// simulate a keypress event
-					var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
-					kp.call(evt.currentTarget, faux);
-					evt.cancelBubble = faux.cancelBubble;
-					evt.returnValue = faux.returnValue;
-					_trySetKeyCode(evt, faux.keyCode);
-				}
-			},
-			// Called in Event scope
-			_stopPropagation: function(){
-				this.cancelBubble = true; 
-			},
-			_preventDefault: function(){
-				// Setting keyCode to 0 is the only way to prevent certain keypresses (namely
-				// ctrl-combinations that correspond to menu accelerator keys).
-				// Otoh, it prevents upstream listeners from getting this information
-				// Try to split the difference here by clobbering keyCode only for ctrl 
-				// combinations. If you still need to access the key upstream, bubbledKeyCode is
-				// provided as a workaround.
-				this.bubbledKeyCode = this.keyCode;
-				if(this.ctrlKey){_trySetKeyCode(this, 0);}
-				this.returnValue = false;
-			}
-		});
-				
-		// override stopEvent for IE
-		dojo.stopEvent = function(evt){
-			evt = evt || window.event;
-			del._stopPropagation.call(evt);
-			del._preventDefault.call(evt);
-		}
-	}
-
-	del._synthesizeEvent = function(evt, props){
-			var faux = dojo.mixin({}, evt, props);
-			del._setKeyChar(faux);
-			// FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); 
-			// but it throws an error when preventDefault is invoked on Safari
-			// does Event.preventDefault not support "apply" on Safari?
-			faux.preventDefault = function(){ evt.preventDefault(); }; 
-			faux.stopPropagation = function(){ evt.stopPropagation(); }; 
-			return faux;
-	}
-	
-	// Opera event normalization
-	if(dojo.isOpera){
-		dojo.mixin(del, {
-			_fixEvent: function(evt, sender){
-				switch(evt.type){
-					case "keypress":
-						var c = evt.which;
-						if(c==3){
-							c=99; // Mozilla maps CTRL-BREAK to CTRL-c
-						}
-						// can't trap some keys at all, like INSERT and DELETE
-						// there is no differentiating info between DELETE and ".", or INSERT and "-"
-						c = ((c<41)&&(!evt.shiftKey) ? 0 : c);
-						if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){
-							// lowercase CTRL-[A-Z] keys
-							c += 32;
-						}
-						return del._synthesizeEvent(evt, { charCode: c });
-				}
-				return evt;
-			}
-		});
-	}
-
-	// Safari event normalization
-	if(dojo.isSafari){
-		del._add = del.add;
-		del._remove = del.remove;
-
-		dojo.mixin(del, {
-			add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){
-				if(!node){return;} // undefined
-				var handle = del._add(node, event, fp);
-				if(del._normalizeEventName(event) == "keypress"){
-					// we need to listen to onkeydown to synthesize
-					// keypress events that otherwise won't fire
-					// in Safari 3.1+: https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html
-					handle._stealthKeyDownHandle = del._add(node, "keydown", function(evt){
-						//A variation on the IE _stealthKeydown function
-						//Synthesize an onkeypress event, but only for unprintable characters.
-						var k=evt.keyCode;
-						// These are Windows Virtual Key Codes
-						// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
-						var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);
-						// synthesize keypress for most unprintables and CTRL-keys
-						if(unprintable||evt.ctrlKey){
-							var c = unprintable ? 0 : k;
-							if(evt.ctrlKey){
-								if(k==3 || k==13){
-									return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively 
-								}else if(c>95 && c<106){ 
-									c -= 48; // map CTRL-[numpad 0-9] to ASCII
-								}else if((!evt.shiftKey)&&(c>=65&&c<=90)){ 
-									c += 32; // map CTRL-[A-Z] to lowercase
-								}else{ 
-									c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
-								}
-							}
-							// simulate a keypress event
-							var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
-							fp.call(evt.currentTarget, faux);
-						}
-					});
-				}
-				return handle; /*Handle*/
-			},
-
-			remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
-				if(node){
-					if(handle._stealthKeyDownHandle){
-						del._remove(node, "keydown", handle._stealthKeyDownHandle);
-					}
-					del._remove(node, event, handle);
-				}
-			},
-			_fixEvent: function(evt, sender){
-				switch(evt.type){
-					case "keypress":
-						if(evt.faux){ return evt; }
-						var c = evt.charCode;
-						c = c>=32? c : 0;
-						return del._synthesizeEvent(evt, {charCode: c, faux: true});
-				}
-				return evt;
-			}
-		});
-	}
-})();
-
-if(dojo.isIE){
-	// keep this out of the closure
-	// closing over 'iel' or 'ieh' b0rks leak prevention
-	// ls[i] is an index into the master handler array
-	dojo._ieDispatcher = function(args, sender){
-		var ap=Array.prototype, h=dojo._ie_listener.handlers, c=args.callee, ls=c[dojo._ieListenersName], t=h[c.target];
-		// return value comes from original target function
-		var r = t && t.apply(sender, args);
-		// make local copy of listener array so it's immutable during processing
-		var lls = [].concat(ls);
-		// invoke listeners after target function
-		for(var i in lls){
-			if(!(i in ap)){
-				h[lls[i]].apply(sender, args);
-			}
-		}
-		return r;
-	}
-	dojo._getIeDispatcher = function(){
-		// ensure the returned function closes over nothing ("new Function" apparently doesn't close)
-		return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function
-	}
-	// keep this out of the closure to reduce RAM allocation
-	dojo._event_listener._fixCallback = function(fp){
-		var f = dojo._event_listener._fixEvent;
-		return function(e){ return fp.call(this, f(e, this)); };
-	}
-}
-
-}
-
-if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.html"] = true;
-
-dojo.provide("dojo._base.html");
-
-// FIXME: need to add unit tests for all the semi-public methods
-
-try{
-	document.execCommand("BackgroundImageCache", false, true);
-}catch(e){
-	// sane browsers don't have cache "issues"
-}
-
-// =============================
-// DOM Functions
-// =============================
-
-/*=====
-dojo.byId = function(id, doc){
-	//	summary:
-	//		Returns DOM node with matching `id` attribute or `null` 
-	//		if not found, similar to "$" function in another library.
-	//		If `id` is a DomNode, this function is a no-op.
-	//
-	//	id: String|DOMNode
-	//	 	A string to match an HTML id attribute or a reference to a DOM Node
-	//
-	//	doc: Document?
-	//		Document to work in. Defaults to the current value of
-	//		dojo.doc.  Can be used to retrieve
-	//		node references from other documents.
-=====*/
-if(dojo.isIE || dojo.isOpera){
-	dojo.byId = function(id, doc){
-		if(dojo.isString(id)){
-			var _d = doc || dojo.doc;
-			var te = _d.getElementById(id);
-			// attributes.id.value is better than just id in case the 
-			// user has a name=id inside a form
-			if(te && te.attributes.id.value == id){
-				return te;
-			}else{
-				var eles = _d.all[id];
-				if(!eles || !eles.length){ return eles; }
-				// if more than 1, choose first with the correct id
-				var i=0;
-				while((te=eles[i++])){
-					if(te.attributes.id.value == id){ return te; }
-				}
-			}
-		}else{
-			return id; // DomNode
-		}
-	}
-}else{
-	dojo.byId = function(id, doc){
-		return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
-	}
-}
-/*=====
-}
-=====*/
-
-(function(){
-	var d = dojo;
-
-	var _destroyContainer = null;
-	dojo.addOnWindowUnload(function(){
-		_destroyContainer=null; //prevent IE leak
-	});
-
-	dojo._destroyElement = function(/*String||DomNode*/node){
-		// summary:
-		//		removes node from its parent, clobbers it and all of its
-		//		children.
-		//	node:
-		//		the element to be destroyed, either as an ID or a reference
-
-		node = d.byId(node);
-		try{
-			if(!_destroyContainer || _destroyContainer.ownerDocument != node.ownerDocument){
-				_destroyContainer = node.ownerDocument.createElement("div");
-			}
-			_destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
-			// NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
-			_destroyContainer.innerHTML = ""; 
-		}catch(e){
-			/* squelch */
-		}
-	};
-
-	dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
-		//	summary:
-		//		Returns true if node is a descendant of ancestor
-		//	node: id or node reference to test
-		//	ancestor: id or node reference of potential parent to test against
-		try{
-			node = d.byId(node);
-			ancestor = d.byId(ancestor);
-			while(node){
-				if(node === ancestor){
-					return true; // Boolean
-				}
-				node = node.parentNode;
-			}
-		}catch(e){ /* squelch, return false */ }
-		return false; // Boolean
-	};
-
-	dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
-		//	summary: enable or disable selection on a node
-		//	node:
-		//		id or reference to node
-		//	selectable:
-		node = d.byId(node);
-		if(d.isMozilla){
-			node.style.MozUserSelect = selectable ? "" : "none";
-		}else if(d.isKhtml){
-			node.style.KhtmlUserSelect = selectable ? "auto" : "none";
-		}else if(d.isIE){
-			var v = (node.unselectable = selectable ? "" : "on");
-			d.query("*", node).forEach("item.unselectable = '"+v+"'");
-		}
-		//FIXME: else?  Opera?
-	};
-
-	var _insertBefore = function(/*Node*/node, /*Node*/ref){
-		ref.parentNode.insertBefore(node, ref);
-		return true;	//	boolean
-	}
-
-	var _insertAfter = function(/*Node*/node, /*Node*/ref){
-		//	summary:
-		//		Try to insert node after ref
-		var pn = ref.parentNode;
-		if(ref == pn.lastChild){
-			pn.appendChild(node);
-		}else{
-			return _insertBefore(node, ref.nextSibling);	//	boolean
-		}
-		return true;	//	boolean
-	}
-
-	dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String?|Number?*/position){
-		//	summary:
-		//		Attempt to insert node into the DOM, choosing from various positioning options.
-		//		Returns true if successful, false otherwise.
-		//	node: 
-		//		id or node reference to place relative to refNode
-		//	refNode: 
-		//		id or node reference to use as basis for placement
-		//	position:
-		//		string noting the position of node relative to refNode or a
-		//		number indicating the location in the childNodes collection of refNode. 
-		//		Accepted string values are:
-		//		* before
-		//		* after
-		//		* first
-		//		* last
-		//
-		//		"first" and "last" indicate positions as children of refNode.  position defaults
-		//		to "last" if not specified
-
-		// FIXME: need to write tests for this!!!!
-		if(!node || !refNode){
-			return false;	//	boolean 
-		}
-		node = d.byId(node);
-		refNode = d.byId(refNode);
-		if(typeof position == "number"){
-			var cn = refNode.childNodes;
-			if(!cn.length || cn.length <= position){
-				refNode.appendChild(node);
-				return true;
-			}
-			return _insertBefore(node, position <= 0 ? refNode.firstChild : cn[position]);
-		}
-		switch(position){
-			case "before":
-				return _insertBefore(node, refNode);	//	boolean
-			case "after":
-				return _insertAfter(node, refNode);		//	boolean
-			case "first":
-				if(refNode.firstChild){
-					return _insertBefore(node, refNode.firstChild);	//	boolean
-				}
-				// else fallthrough...
-			default: // aka: last
-				refNode.appendChild(node);
-				return true;	//	boolean
-		}
-	}
-
-	// Box functions will assume this model.
-	// On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
-	// Can be set to change behavior of box setters.
-	
-	// can be either:
-	//	"border-box"
-	//	"content-box" (default)
-	dojo.boxModel = "content-box";
-	
-	// We punt per-node box mode testing completely.
-	// If anybody cares, we can provide an additional (optional) unit 
-	// that overrides existing code to include per-node box sensitivity.
-
-	// Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
-	// but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
-	// IIRC, earlier versions of Opera did in fact use border-box.
-	// Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
-
-	if(d.isIE /*|| dojo.isOpera*/){
-		var _dcm = document.compatMode;
-		// client code may have to adjust if compatMode varies across iframes
-		d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE<6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support?
-	}
-
-	// =============================
-	// Style Functions
-	// =============================
-	
-	// getComputedStyle drives most of the style code.
-	// Wherever possible, reuse the returned object.
-	//
-	// API functions below that need to access computed styles accept an 
-	// optional computedStyle parameter.
-	// If this parameter is omitted, the functions will call getComputedStyle themselves.
-	// This way, calling code can access computedStyle once, and then pass the reference to 
-	// multiple API functions. 
-
-/*=====
-	dojo.getComputedStyle = function(node){
-		//	summary:
-		//		Returns a "computed style" object.
-		//
-		//	description:
-		//		Gets a "computed style" object which can be used to gather
-		//		information about the current state of the rendered node. 
-		//
-		//		Note that this may behave differently on different browsers.
-		//		Values may have different formats and value encodings across
-		//		browsers.
-		//
-		//		Note also that this method is expensive.  Wherever possible,
-		//		reuse the returned object.
-		//
-		//		Use the dojo.style() method for more consistent (pixelized)
-		//		return values.
-		//
-		//	node: DOMNode
-		//		A reference to a DOM node. Does NOT support taking an
-		//		ID string for speed reasons.
-		//	example:
-		//	|	dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
-		return; // CSS2Properties
-	}
-=====*/
-
-	// Although we normally eschew argument validation at this
-	// level, here we test argument 'node' for (duck)type.
-	// Argument node must also implement Element.  (Note: we check
-	// against HTMLElement rather than Element for interop with prototype.js)
-	// Because 'document' is the 'parentNode' of 'body'
-	// it is frequently sent to this function even 
-	// though it is not Element.
-	var gcs;
-	if(d.isSafari){
-		gcs = function(/*DomNode*/node){
-			var s;
-			if(node instanceof HTMLElement){
-				var dv = node.ownerDocument.defaultView;
-				s = dv.getComputedStyle(node, null);
-				if(!s && node.style){ 
-					node.style.display = ""; 
-					s = dv.getComputedStyle(node, null);
-				}
-			}
-			return s || {};
-		}; 
-	}else if(d.isIE){
-		gcs = function(node){
-			// IE (as of 7) doesn't expose Element like sane browsers
-			return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {};
-		};
-	}else{
-		gcs = function(node){
-			return node instanceof HTMLElement ? 
-				node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
-		};
-	}
-	dojo.getComputedStyle = gcs;
-
-	if(!d.isIE){
-		dojo._toPixelValue = function(element, value){
-			// style values can be floats, client code may want
-			// to round for integer pixels.
-			return parseFloat(value) || 0; 
-		};
-	}else{
-		dojo._toPixelValue = function(element, avalue){
-			if(!avalue){ return 0; }
-			// on IE7, medium is usually 4 pixels
-			if(avalue=="medium"){ return 4; }
-			// style values can be floats, client code may
-			// want to round this value for integer pixels.
-			if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }
-			with(element){
-				var sLeft = style.left;
-				var rsLeft = runtimeStyle.left;
-				runtimeStyle.left = currentStyle.left;
-				try{
-					// 'avalue' may be incompatible with style.left, which can cause IE to throw
-					// this has been observed for border widths using "thin", "medium", "thick" constants
-					// those particular constants could be trapped by a lookup
-					// but perhaps there are more
-					style.left = avalue;
-					avalue = style.pixelLeft;
-				}catch(e){
-					avalue = 0;
-				}
-				style.left = sLeft;
-				runtimeStyle.left = rsLeft;
-			}
-			return avalue;
-		}
-	}
-	var px = d._toPixelValue;
-
-	// FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
-	/*=====
-	dojo._getOpacity = function(node){
-			//	summary:
-			//		Returns the current opacity of the passed node as a
-			//		floating-point value between 0 and 1.
-			//	node: DomNode
-			//		a reference to a DOM node. Does NOT support taking an
-			//		ID string for speed reasons.
-			//	returns: Number between 0 and 1
-			return; // Number
-	}
-	=====*/
-
-	var astr = "DXImageTransform.Microsoft.Alpha";
-	var af = function(n, f){ 
-		try{
-			return n.filters.item(astr);
-		}catch(e){
-			return f ? {} : null;
-		}
-	}
-
-	dojo._getOpacity = d.isIE ? function(node){
-		try{
-			return af(node).Opacity / 100; // Number
-		}catch(e){
-			return 1; // Number
-		}
-	} : function(node){
-		return gcs(node).opacity;
-	};
-
-	/*=====
-	dojo._setOpacity = function(node, opacity){
-			//	summary:
-			//		set the opacity of the passed node portably. Returns the
-			//		new opacity of the node.
-			//	node: DOMNode
-			//		a reference to a DOM node. Does NOT support taking an
-			//		ID string for performance reasons.
-			//	opacity: Number
-			//		A Number between 0 and 1. 0 specifies transparent.
-			//	returns: Number between 0 and 1
-			return; // Number
-	}
-	=====*/
-
-	dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
-		var ov = opacity * 100;
-		node.style.zoom = 1.0;
-
-		// on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661),
-		//but still update the opacity value so we can get a correct reading if it is read later.
-		af(node, 1).Enabled = (opacity == 1 ? false : true);
-
-		if(!af(node)){
-			node.style.filter += " progid:"+astr+"(Opacity="+ov+")";
-		}else{
-			af(node, 1).Opacity = ov;
-		}
-
-		if(node.nodeName.toLowerCase() == "tr"){
-			d.query("> td", node).forEach(function(i){
-				d._setOpacity(i, opacity);
-			});
-		}
-		return opacity;
-	} : function(node, opacity){
-		return node.style.opacity = opacity;
-	};
-
-	var _pixelNamesCache = {
-		left: true, top: true
-	};
-	var _pixelRegExp = /margin|padding|width|height|max|min|offset/;  // |border
-	var _toStyleValue = function(node, type, value){
-		type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile!
-		if(d.isIE){
-			if(value == "auto"){
-				if(type == "height"){ return node.offsetHeight; }
-				if(type == "width"){ return node.offsetWidth; }
-			}
-			if(type == "fontweight"){
-				switch(value){
-					case 700: return "bold";
-					case 400:
-					default: return "normal";
-				}
-			}
-		}
-		if(!(type in _pixelNamesCache)){
-			_pixelNamesCache[type] = _pixelRegExp.test(type);
-		}
-		return _pixelNamesCache[type] ? px(node, value) : value;
-	}
-
-	var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
-	var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };
-	
-	// public API
-	
-	dojo.style = function(	/*DomNode|String*/ node, 
-							/*String?|Object?*/ style, 
-							/*String?*/ value){
-		//	summary:
-		//		Accesses styles on a node. If 2 arguments are
-		//		passed, acts as a getter. If 3 arguments are passed, acts
-		//		as a setter.
-		//	node:
-		//		id or reference to node to get/set style for
-		//	style:
-		//		the style property to set in DOM-accessor format
-		//		("borderWidth", not "border-width") or an object with key/value
-		//		pairs suitable for setting each property.
-		//	value:
-		//		If passed, sets value on the node for style, handling
-		//		cross-browser concerns.
-		//	example:
-		//		Passing only an ID or node returns the computed style object of
-		//		the node:
-		//	|	dojo.style("thinger");
-		//	example:
-		//		Passing a node and a style property returns the current
-		//		normalized, computed value for that property:
-		//	|	dojo.style("thinger", "opacity"); // 1 by default
-		//
-		//	example:
-		//		Passing a node, a style property, and a value changes the
-		//		current display of the node and returns the new computed value
-		//	|	dojo.style("thinger", "opacity", 0.5); // == 0.5
-		//
-		//	example:
-		//		Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
-		//	|	dojo.style("thinger", {
-		//	|		"opacity": 0.5,
-		//	|		"border": "3px solid black",
-		//	|		"height": 300
-		//	|	});
-		//
-		// 	example:
-		//		When the CSS style property is hyphenated, the JavaScript property is camelCased.
-		//		font-size becomes fontSize, and so on.
-		//	|	dojo.style("thinger",{
-		//	|		fontSize:"14pt",
-		//	|		letterSpacing:"1.2em"
-		//	|	});
-		//
-		//	example:
-		//		dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
-		//		dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
-		//	|	dojo.query(".someClassName").style("visibility","hidden");
-		//	|	// or
-		//	|	dojo.query("#baz > div").style({
-		//	|		opacity:0.75,
-		//	|		fontSize:"13pt"
-		//	|	});
-
-		var n = d.byId(node), args = arguments.length, op = (style=="opacity");
-		style = _floatAliases[style] || style;
-		if(args == 3){
-			return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
-		}
-		if(args == 2 && op){
-			return d._getOpacity(n);
-		}
-		var s = gcs(n);
-		if(args == 2 && !d.isString(style)){
-			for(var x in style){
-				d.style(node, x, style[x]);
-			}
-			return s;
-		}
-		return (args == 1) ? s : _toStyleValue(n, style, s[style]||n.style[style]); /* CSS2Properties||String||Number */
-	}
-
-	// =============================
-	// Box Functions
-	// =============================
-
-	dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
-		//	summary:
-		// 		Returns object with special values specifically useful for node
-		// 		fitting.
-		//
-		// 		* l/t = left/top padding (respectively)
-		// 		* w = the total of the left and right padding 
-		// 		* h = the total of the top and bottom padding
-		//
-		//		If 'node' has position, l/t forms the origin for child nodes. 
-		//		The w/h are used for calculating boxes.
-		//		Normally application code will not need to invoke this
-		//		directly, and will use the ...box... functions instead.
-		var 
-			s = computedStyle||gcs(n), 
-			l = px(n, s.paddingLeft), 
-			t = px(n, s.paddingTop);
-		return { 
-			l: l,
-			t: t,
-			w: l+px(n, s.paddingRight),
-			h: t+px(n, s.paddingBottom)
-		};
-	}
-
-	dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
-		//	summary:
-		//		returns an object with properties useful for noting the border
-		//		dimensions.
-		//
-		// 		* l/t = the sum of left/top border (respectively)
-		//		* w = the sum of the left and right border
-		//		* h = the sum of the top and bottom border
-		//
-		//		The w/h are used for calculating boxes.
-		//		Normally application code will not need to invoke this
-		//		directly, and will use the ...box... functions instead.
-		var 
-			ne = "none",
-			s = computedStyle||gcs(n), 
-			bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
-			bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
-		return { 
-			l: bl,
-			t: bt,
-			w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
-			h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
-		};
-	}
-
-	dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
-		//	summary:
-		//		returns object with properties useful for box fitting with
-		//		regards to padding.
-		//
-		//		* l/t = the sum of left/top padding and left/top border (respectively)
-		//		* w = the sum of the left and right padding and border
-		//		* h = the sum of the top and bottom padding and border
-		//
-		//		The w/h are used for calculating boxes.
-		//		Normally application code will not need to invoke this
-		//		directly, and will use the ...box... functions instead.
-		var 
-			s = computedStyle||gcs(n), 
-			p = d._getPadExtents(n, s),
-			b = d._getBorderExtents(n, s);
-		return { 
-			l: p.l + b.l,
-			t: p.t + b.t,
-			w: p.w + b.w,
-			h: p.h + b.h
-		};
-	}
-
-	dojo._getMarginExtents = function(n, computedStyle){
-		//	summary:
-		//		returns object with properties useful for box fitting with
-		//		regards to box margins (i.e., the outer-box).
-		//
-		//		* l/t = marginLeft, marginTop, respectively
-		//		* w = total width, margin inclusive
-		//		* h = total height, margin inclusive
-		//
-		//		The w/h are used for calculating boxes.
-		//		Normally application code will not need to invoke this
-		//		directly, and will use the ...box... functions instead.
-		var 
-			s = computedStyle||gcs(n), 
-			l = px(n, s.marginLeft),
-			t = px(n, s.marginTop),
-			r = px(n, s.marginRight),
-			b = px(n, s.marginBottom);
-		if(d.isSafari && (s.position != "absolute")){
-			// FIXME: Safari's version of the computed right margin
-			// is the space between our right edge and the right edge 
-			// of our offsetParent. 
-			// What we are looking for is the actual margin value as 
-			// determined by CSS.
-			// Hack solution is to assume left/right margins are the same.
-			r = l;
-		}
-		return { 
-			l: l,
-			t: t,
-			w: l+r,
-			h: t+b
-		};
-	}
-
-	// Box getters work in any box context because offsetWidth/clientWidth
-	// are invariant wrt box context
-	//
-	// They do *not* work for display: inline objects that have padding styles
-	// because the user agent ignores padding (it's bogus styling in any case)
-	//
-	// Be careful with IMGs because they are inline or block depending on 
-	// browser and browser mode.
-
-	// Although it would be easier to read, there are not separate versions of 
-	// _getMarginBox for each browser because:
-	// 1. the branching is not expensive
-	// 2. factoring the shared code wastes cycles (function call overhead)
-	// 3. duplicating the shared code wastes bytes
-	
-	dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
-		// summary:
-		//		returns an object that encodes the width, height, left and top
-		//		positions of the node's margin box.
-		var s = computedStyle||gcs(node), me = d._getMarginExtents(node, s);
-		var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode;
-		if(d.isMoz){
-			// Mozilla:
-			// If offsetParent has a computed overflow != visible, the offsetLeft is decreased
-			// by the parent's border.
-			// We don't want to compute the parent's style, so instead we examine node's
-			// computed left/top which is more stable.
-			var sl = parseFloat(s.left), st = parseFloat(s.top);
-			if(!isNaN(sl) && !isNaN(st)){
-				l = sl, t = st;
-			}else{
-				// If child's computed left/top are not parseable as a number (e.g. "auto"), we
-				// have no choice but to examine the parent's computed style.
-				if(p && p.style){
-					var pcs = gcs(p);
-					if(pcs.overflow != "visible"){
-						var be = d._getBorderExtents(p, pcs);
-						l += be.l, t += be.t;
-					}
-				}
-			}
-		}else if(d.isOpera){
-			// On Opera, offsetLeft includes the parent's border
-			if(p){
-				var be = d._getBorderExtents(p);
-				l -= be.l;
-				t -= be.t;
-			}
-		}
-		return { 
-			l: l, 
-			t: t, 
-			w: node.offsetWidth + me.w, 
-			h: node.offsetHeight + me.h 
-		};
-	}
-	
-	dojo._getContentBox = function(node, computedStyle){
-		// summary:
-		//		Returns an object that encodes the width, height, left and top
-		//		positions of the node's content box, irrespective of the
-		//		current box model.
-
-		// clientWidth/Height are important since the automatically account for scrollbars
-		// fallback to offsetWidth/Height for special cases (see #3378)
-		var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), be=d._getBorderExtents(node, s), w=node.clientWidth, h;
-		if(!w){
-			w=node.offsetWidth, h=node.offsetHeight;
-		}else{
-			h=node.clientHeight, be.w = be.h = 0; 
-		}
-		// On Opera, offsetLeft includes the parent's border
-		if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
-		return { 
-			l: pe.l, 
-			t: pe.t, 
-			w: w - pe.w - be.w, 
-			h: h - pe.h - be.h
-		};
-	}
-
-	dojo._getBorderBox = function(node, computedStyle){
-		var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), cb=d._getContentBox(node, s);
-		return { 
-			l: cb.l - pe.l, 
-			t: cb.t - pe.t, 
-			w: cb.w + pe.w, 
-			h: cb.h + pe.h
-		};
-	}
-
-	// Box setters depend on box context because interpretation of width/height styles
-	// vary wrt box context.
-	//
-	// The value of dojo.boxModel is used to determine box context.
-	// dojo.boxModel can be set directly to change behavior.
-	//
-	// Beware of display: inline objects that have padding styles
-	// because the user agent ignores padding (it's a bogus setup anyway)
-	//
-	// Be careful with IMGs because they are inline or block depending on 
-	// browser and browser mode.
-	// 
-	// Elements other than DIV may have special quirks, like built-in
-	// margins or padding, or values not detectable via computedStyle.
-	// In particular, margins on TABLE do not seems to appear 
-	// at all in computedStyle on Mozilla.
-	
-	dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
-		//	summary:
-		//		sets width/height/left/top in the current (native) box-model
-		//		dimentions. Uses the unit passed in u.
-		//	node: DOM Node reference. Id string not supported for performance reasons.
-		//	l: optional. left offset from parent.
-		//	t: optional. top offset from parent.
-		//	w: optional. width in current box model.
-		//	h: optional. width in current box model.
-		//	u: optional. unit measure to use for other measures. Defaults to "px".
-		u = u || "px";
-		var s = node.style;
-		if(!isNaN(l)){ s.left = l+u; }
-		if(!isNaN(t)){ s.top = t+u; }
-		if(w>=0){ s.width = w+u; }
-		if(h>=0){ s.height = h+u; }
-	}
-
-	dojo._isButtonTag = function(/*DomNode*/node) {
-		// summary:
-		//		True if the node is BUTTON or INPUT.type="button".
-		return node.tagName == "BUTTON" 
-			|| node.tagName=="INPUT" && node.getAttribute("type").toUpperCase() == "BUTTON"; // boolean
-	}
-	
-	dojo._usesBorderBox = function(/*DomNode*/node){
-		//	summary: 
-		//		True if the node uses border-box layout.
-
-		// We could test the computed style of node to see if a particular box
-		// has been specified, but there are details and we choose not to bother.
-		
-		// TABLE and BUTTON (and INPUT type=button) are always border-box by default.
-		// If you have assigned a different box to either one via CSS then
-		// box functions will break.
-		
-		var n = node.tagName;
-		return d.boxModel=="border-box" || n=="TABLE" || dojo._isButtonTag(node); // boolean
-	}
-
-	dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
-		//	summary:
-		//		Sets the size of the node's contents, irrespective of margins,
-		//		padding, or borders.
-		if(d._usesBorderBox(node)){
-			var pb = d._getPadBorderExtents(node, computedStyle);
-			if(widthPx >= 0){ widthPx += pb.w; }
-			if(heightPx >= 0){ heightPx += pb.h; }
-		}
-		d._setBox(node, NaN, NaN, widthPx, heightPx);
-	}
-
-	dojo._setMarginBox = function(/*DomNode*/node, 	/*Number?*/leftPx, /*Number?*/topPx, 
-													/*Number?*/widthPx, /*Number?*/heightPx, 
-													/*Object*/computedStyle){
-		//	summary:
-		//		sets the size of the node's margin box and placement
-		//		(left/top), irrespective of box model. Think of it as a
-		//		passthrough to dojo._setBox that handles box-model vagaries for
-		//		you.
-
-		var s = computedStyle||gcs(node);
-		// Some elements have special padding, margin, and box-model settings. 
-		// To use box functions you may need to set padding, margin explicitly.
-		// Controlling box-model is harder, in a pinch you might set dojo.boxModel.
-		var bb=d._usesBorderBox(node),
-				pb=bb ? _nilExtents : d._getPadBorderExtents(node, s);
-		if (dojo.isSafari) {
-			// on Safari (3.1.2), button nodes with no explicit size have a default margin
-			// setting an explicit size eliminates the margin.
-			// We have to swizzle the width to get correct margin reading.
-			if (dojo._isButtonTag(node)){
-				var ns = node.style;
-				if (widthPx>=0 && !ns.width) { ns.width = "4px"; }
-				if (heightPx>=0 && !ns.height) { ns.height = "4px"; }
-			}
-		}
-		var mb=d._getMarginExtents(node, s);
-		if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
-		if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
-		d._setBox(node, leftPx, topPx, widthPx, heightPx);
-	}
-	
-	var _nilExtents = { l:0, t:0, w:0, h:0 };
-
-	// public API
-	
-	dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
-		//	summary:
-		//		Getter/setter for the margin-box of node.
-		//	description: 
-		//		Returns an object in the expected format of box (regardless
-		//		if box is passed). The object might look like:
-		//			`{ l: 50, t: 200, w: 300: h: 150 }`
-		//		for a node offset from its parent 50px to the left, 200px from
-		//		the top with a margin width of 300px and a margin-height of
-		//		150px.
-		//	node:
-		//		id or reference to DOM Node to get/set box for
-		//	box:
-		//		If passed, denotes that dojo.marginBox() should
-		//		update/set the margin box for node. Box is an object in the
-		//		above format. All properties are optional if passed.
-		var n=d.byId(node), s=gcs(n), b=box;
-		return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
-	}
-
-	dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
-		//	summary:
-		//		Getter/setter for the content-box of node.
-		//	description:
-		//		Returns an object in the expected format of box (regardless if box is passed).
-		//		The object might look like:
-		//			`{ l: 50, t: 200, w: 300: h: 150 }`
-		//		for a node offset from its parent 50px to the left, 200px from
-		//		the top with a content width of 300px and a content-height of
-		//		150px. Note that the content box may have a much larger border
-		//		or margin box, depending on the box model currently in use and
-		//		CSS values set/inherited for node.
-		//	node:
-		//		id or reference to DOM Node to get/set box for
-		//	box:
-		//		If passed, denotes that dojo.contentBox() should
-		//		update/set the content box for node. Box is an object in the
-		//		above format. All properties are optional if passed.
-		var n=d.byId(node), s=gcs(n), b=box;
-		return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
-	}
-	
-	// =============================
-	// Positioning 
-	// =============================
-	
-	var _sumAncestorProperties = function(node, prop){
-		if(!(node = (node||0).parentNode)){return 0};
-		var val, retVal = 0, _b = d.body();
-		while(node && node.style){
-			if(gcs(node).position == "fixed"){
-				return 0;
-			}
-			val = node[prop];
-			if(val){
-				retVal += val - 0;
-				// opera and khtml #body & #html has the same values, we only
-				// need one value
-				if(node == _b){ break; }
-			}
-			node = node.parentNode;
-		}
-		return retVal;	//	integer
-	}
-
-	dojo._docScroll = function(){
-		var 
-			_b = d.body(),
-			_w = d.global,
-			de = d.doc.documentElement;
-		return {
-			y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
-			x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
-		};
-	};
-	
-	dojo._isBodyLtr = function(){
-		//FIXME: could check html and body tags directly instead of computed style?  need to ignore case, accept empty values
-		return !("_bodyLtr" in d) ? 
-			d._bodyLtr = gcs(d.body()).direction == "ltr" :
-			d._bodyLtr; // Boolean 
-	}
-	
-	dojo._getIeDocumentElementOffset = function(){
-		// summary
-		// The following values in IE contain an offset:
-		//     event.clientX 
-		//     event.clientY 
-		//     node.getBoundingClientRect().left
-		//     node.getBoundingClientRect().top
-		// But other position related values do not contain this offset, such as
-		// node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
-		// The offset is always (2, 2) in LTR direction. When the body is in RTL
-		// direction, the offset counts the width of left scroll bar's width.
-		// This function computes the actual offset.
-
-		//NOTE: assumes we're being called in an IE browser
-
-		var de = d.doc.documentElement;
-		//FIXME: use this instead?			var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;
-
-		return (d.isIE >= 7) ?
-			{x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
-		:
-			// IE 6.0
-			{x: d._isBodyLtr() || window.parent == window ?
-				de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, 
-				y: de.clientTop}; // Object
-	};
-	
-	dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
-		// In RTL direction, scrollLeft should be a negative value, but IE 
-		// returns a positive one. All codes using documentElement.scrollLeft
-		// must call this function to fix this error, otherwise the position
-		// will offset to right when there is a horizontal scrollbar.
-		var dd = d.doc;
-		if(d.isIE && !dojo._isBodyLtr()){
-			var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
-			return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
-		}
-		return scrollLeft; // Integer
-	}
-
-	dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){
-		//	summary:
-		//		Gets the position of the passed element relative to
-		//		the viewport (if includeScroll==false), or relative to the
-		//		document root (if includeScroll==true).
-		//
-		//		Returns an object of the form:
-		//			{ x: 100, y: 300 }
-		//		if includeScroll is passed, the x and y values will include any
-		//		document offsets that may affect the position relative to the
-		//		viewport.
-
-		// FIXME: need to decide in the brave-new-world if we're going to be
-		// margin-box or border-box.
-		var ownerDocument = node.ownerDocument;
-		var ret = {
-			x: 0,
-			y: 0
-		};
-
-		// targetBoxType == "border-box"
-		var db = d.body();
-		if(d.isIE || (d.isFF >= 3)){
-			var client = node.getBoundingClientRect();
-			var cs;
-			if(d.isFF){
-				// in FF3 you have to subract the document element margins
-				var dv = node.ownerDocument.defaultView;
-				cs=dv.getComputedStyle(db.parentNode, null);
-			}
-			var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: px(db.parentNode,cs.marginLeft), y: px(db.parentNode,cs.marginTop)};
-			ret.x = client.left - offset.x;
-			ret.y = client.top - offset.y;
-		}else{
-			if(node["offsetParent"]){
-				var endNode;
-				// in Safari, if the node is an absolutely positioned child of
-				// the body and the body has a margin the offset of the child
-				// and the body contain the body's margins, so we need to end
-				// at the body
-				// FIXME: getting contrary results to the above in latest WebKit.
-				if(d.isSafari &&
-					//(node.style.getPropertyValue("position") == "absolute") &&
-					(gcs(node).position == "absolute") &&
-					(node.parentNode == db)){
-					endNode = db;
-				}else{
-					endNode = db.parentNode;
-				}
-				// Opera seems to be double counting for some elements
-				var cs=gcs(node);
-				var n=node;
-				if(d.isOpera&&cs.position!="absolute"){
-					n=n.offsetParent;
-				}
-				ret.x -= _sumAncestorProperties(n, "scrollLeft");
-				ret.y -= _sumAncestorProperties(n, "scrollTop");
-
-				var curnode = node;
-				do{
-					var n = curnode.offsetLeft;
-					//FIXME: ugly hack to workaround the submenu in 
-					//popupmenu2 does not shown up correctly in opera. 
-					//Someone have a better workaround?
-					if(!d.isOpera || n > 0){
-						ret.x += isNaN(n) ? 0 : n;
-					}
-					var t = curnode.offsetTop;
-					ret.y += isNaN(t) ? 0 : t;
-					var cs = gcs(curnode);
-					if(curnode != node){
-						if(d.isSafari){
-							ret.x += px(curnode, cs.borderLeftWidth);
-							ret.y += px(curnode, cs.borderTopWidth);
-						}else if(d.isFF){
-							// tried left+right with differently sized left/right borders
-							// it really is 2xleft border in FF, not left+right, even in RTL!
-							ret.x += 2*px(curnode,cs.borderLeftWidth);
-							ret.y += 2*px(curnode,cs.borderTopWidth);
-						}
-					}
-					// static children in a static div in FF2 are affected by the div's border as well
-					// but offsetParent will skip this div!
-					if(d.isFF&&cs.position=="static"){
-						var parent=curnode.parentNode;
-						while(parent!=curnode.offsetParent){
-							var pcs=gcs(parent);
-							if(pcs.position=="static"){
-								ret.x += px(curnode,pcs.borderLeftWidth);
-								ret.y += px(curnode,pcs.borderTopWidth);
-							}
-							parent=parent.parentNode;
-						}
-					}
-					curnode = curnode.offsetParent;
-				}while((curnode != endNode) && curnode);
-			}else if(node.x && node.y){
-				ret.x += isNaN(node.x) ? 0 : node.x;
-				ret.y += isNaN(node.y) ? 0 : node.y;
-			}
-		}
-		// account for document scrolling
-		// if offsetParent is used, ret value already includes scroll position
-		// so we may have to actually remove that value if !includeScroll
-		if(includeScroll){
-			var scroll = d._docScroll();
-			ret.y += scroll.y;
-			ret.x += scroll.x;
-		}
-
-		return ret; // object
-	}
-
-	// FIXME: need a setter for coords or a moveTo!!
-	dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
-		//	summary:
-		//		Returns an object that measures margin box width/height and
-		//		absolute positioning data from dojo._abs().
-		//
-		//	description:
-		//		Returns an object that measures margin box width/height and
-		//		absolute positioning data from dojo._abs().
-		//		Return value will be in the form:
-		//			`{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
-		//		Does not act as a setter. If includeScroll is passed, the x and
-		//		y params are affected as one would expect in dojo._abs().
-		var n=d.byId(node), s=gcs(n), mb=d._getMarginBox(n, s);
-		var abs = d._abs(n, includeScroll);
-		mb.x = abs.x;
-		mb.y = abs.y;
-		return mb;
-	}
-
-	// =============================
-	// Element attribute Functions
-	// =============================
-
-	var ieLT8 = d.isIE < 8;
-
-	var _fixAttrName = function(/*String*/name){
-		switch(name.toLowerCase()){
-			case "tabindex":
-				// Internet Explorer will only set or remove tabindex
-				// if it is spelled "tabIndex"
-				// 
-				return ieLT8 ? "tabIndex" : "tabindex";
-			case "for": case "htmlfor":
-				// to pick up for attrib set in markup via getAttribute() IE<8 uses "htmlFor" and others use "for"
-				// get/setAttribute works in all as long use same value for both get/set
-				return ieLT8 ? "htmlFor" : "for";
-			case "class" :
-				return d.isIE ? "className" : "class";
-			default:
-				return name;
-		}
-	}
-
-	// non-deprecated HTML4 attributes with default values
-	// http://www.w3.org/TR/html401/index/attributes.html
-	// FF and Safari will return the default values if you
-	// access the attributes via a property but not
-	// via getAttribute()
-	var _attrProps = {
-		colspan: "colSpan",
-		enctype: "enctype",
-		frameborder: "frameborder",
-		method: "method",
-		rowspan: "rowSpan",
-		scrolling: "scrolling",
-		shape: "shape",
-		span: "span",
-		type: "type",
-		valuetype: "valueType"
-	}
-
-	dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
-		//	summary:
-		//		Returns true if the requested attribute is specified on the
-		//		given element, and false otherwise.
-		//	node:
-		//		id or reference to the element to check
-		//	name:
-		//		the name of the attribute
-		//	returns:
-		//		true if the requested attribute is specified on the
-		//		given element, and false otherwise
-		node = d.byId(node);
-		var fixName = _fixAttrName(name);
-		fixName = fixName == "htmlFor" ? "for" : fixName; //IE<8 uses htmlFor except in this case
-		var attr = node.getAttributeNode && node.getAttributeNode(fixName);
-		return attr ? attr.specified : false; // Boolean
-	}
-
-	var _evtHdlrMap = {
-		
-	}
-
-	var _ctr = 0;
-	var _attrId = dojo._scopeName + "attrid";
-
-	dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
-		//	summary:
-		//		Gets or sets an attribute on an HTML element.
-		//	description:
-		//		Handles normalized getting and setting of attributes on DOM
-		//		Nodes. If 2 arguments are passed, and a the second argumnt is a
-		//		string, acts as a getter.
-		//	
-		//		If a third argument is passed, or if the second argumnt is a
-		//		map of attributes, acts as a setter.
-		//
-		//		When passing functions as values, note that they will not be
-		//		directly assigned to slots on the node, but rather the default
-		//		behavior will be removed and the new behavior will be added
-		//		using `dojo.connect()`, meaning that event handler properties
-		//		will be normalized and that some caveats with regards to
-		//		non-standard behaviors for onsubmit apply. Namely that you
-		//		should cancel form submission using `dojo.stopEvent()` on the
-		//		passed event object instead of returning a boolean value from
-		//		the handler itself.
-		//	node:
-		//		id or reference to the element to get or set the attribute on
-		//	name:
-		//		the name of the attribute to get or set.
-		//	value:
-		//		The value to set for the attribute
-		//	returns:
-		//		when used as a getter, the value of the requested attribute
-		//		or null if that attribute does not have a specified or
-		//		default value;
-		//
-		//		when user as a setter, undefined
-		//
-		//	example:
-		//	|	// get the current value of the "foo" attribute on a node
-		//	|	dojo.attr(dojo.byId("nodeId"), "foo");
-		//	|	// or we can just pass the id:
-		//	|	dojo.attr("nodeId", "foo");
-		//
-		//	example:
-		//	|	// use attr() to set the tab index
-		//	|	dojo.attr("nodeId", "tabindex", 3);
-		//	|
-		//
-		//	example:
-		//	|	// set multiple values at once, including event handlers:
-		//	|	dojo.attr("formId", {
-		//	|		"foo": "bar",
-		//	|		"tabindex": -1,
-		//	|		"method": "POST",
-		//	|		"onsubmit": function(e){
-		//	|			// stop submitting the form. Note that the IE behavior
-		//	|			// of returning true or false will have no effect here
-		//	|			// since our handler is connect()ed to the built-in
-		//	|			// onsubmit behavior and so we need to use
-		//	|			// dojo.stopEvent() to ensure that the submission
-		//	|			// doesn't proceed.
-		//	|			dojo.stopEvent(e);
-		//	|
-		//	|			// submit the form with Ajax
-		//	|			dojo.xhrPost({ form: "formId" });
-		//	|		}
-		//	|	});
-
-		var args = arguments.length;
-		if(args == 2 && !d.isString(name)){
-			for(var x in name){ d.attr(node, x, name[x]); }
-			return;
-		}
-		node = d.byId(node);
-		name = _fixAttrName(name);
-		if(args == 3){
-			// FIXME:
-			//		what about when the name is "style" and value is an object?
-			//		It seems natural to pass it in to dojo.style(node,
-			//		value)...should we support this?
-			if(d.isFunction(value)){
-				// clobber if we can
-				var attrId = d.attr(node, _attrId);
-				if(!attrId){
-					attrId = _ctr++;
-					d.attr(node, _attrId, attrId);
-				}
-				if(!_evtHdlrMap[attrId]){
-					_evtHdlrMap[attrId] = {};
-				}
-				var h = _evtHdlrMap[attrId][name];
-				if(h){
-					d.disconnect(h);
-				}else{
-					try{
-						delete node[name];
-					}catch(e){}
-				}
-
-				// ensure that event objects are normalized, etc.
-				_evtHdlrMap[attrId][name] = d.connect(node, name, value);
-
-			}else if(
-				(typeof value == "boolean")|| // e.g. onsubmit, disabled
-				(name == "innerHTML")
-			){
-				node[name] = value;
-			}else if((name == "style")&&(!d.isString(value))){
-				d.style(node, value);
-			}else{
-				node.setAttribute(name, value);
-			}
-			return;
-		}else{
-			// should we access this attribute via a property or
-			// via getAttribute()?
-			var prop = _attrProps[name.toLowerCase()];
-			if(prop){
-				return node[prop];
-			}else{
-				var attrValue = node[name];
-				return (typeof attrValue == 'boolean' || typeof attrValue == 'function') ? attrValue
-					: (d.hasAttr(node, name) ? node.getAttribute(name) : null);
-			}
-		}
-	}
-
-	dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){
-		//	summary:
-		//		Removes an attribute from an HTML element.
-		//	node:
-		//		id or reference to the element to remove the attribute from
-		//	name:
-		//		the name of the attribute to remove
-		d.byId(node).removeAttribute(_fixAttrName(name));
-	}
-
-	/*
-	dojo.createElement = function(type, attrs, parent, position){
-		// TODO: need to finish this!
-	}
-	*/
-
-	// =============================
-	// (CSS) Class Functions
-	// =============================
-	var _className = "className";
-
-	dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
-		//	summary:
-		//		Returns whether or not the specified classes are a portion of the
-		//		class list currently applied to the node. 
-		return ((" "+ d.byId(node)[_className] +" ").indexOf(" "+ classStr +" ") >= 0);  // Boolean
-	};
-
-	dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){
-		//	summary:
-		//		Adds the specified classes to the end of the class list on the
-		//		passed node.
-		node = d.byId(node);
-		var cls = node[_className];
-		if((" "+ cls +" ").indexOf(" " + classStr + " ") < 0){
-			node[_className] = cls + (cls ? ' ' : '') + classStr;
-		}
-	};
-
-	dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){
-		// summary: Removes the specified classes from node.
-		node = d.byId(node);
-		var t = d.trim((" " + node[_className] + " ").replace(" " + classStr + " ", " "));
-		if(node[_className] != t){ node[_className] = t; }
-	};
-
-	dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
-		//	summary: 	
-		//		Adds a class to node if not present, or removes if present.
-		//		Pass a boolean condition if you want to explicitly add or remove.
-		//	condition:
-		//		If passed, true means to add the class, false means to remove.
-		if(condition === undefined){
-			condition = !d.hasClass(node, classStr);
-		}
-		d[condition ? "addClass" : "removeClass"](node, classStr);
-	};
-
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.NodeList"] = true;
-dojo.provide("dojo._base.NodeList");
-
-
-
-(function(){
-
-	var d = dojo;
-
-	var tnl = function(arr){
-		// decorate an array to make it look like a NodeList
-		arr.constructor = dojo.NodeList;
-		dojo._mixin(arr, dojo.NodeList.prototype);
-		return arr;
-	}
-
-	var _mapIntoDojo = function(func, alwaysThis){
-		// returns a function which, when executed in the scope of its caller,
-		// applies the passed arguments to a particular dojo.* function (named
-		// in func) and aggregates the returns. if alwaysThis is true, it
-		// always returns the scope object and not the collected returns from
-		// the Dojo method
-		return function(){
-			var _a = arguments;
-			var aa = d._toArray(_a, 0, [null]);
-			var s = this.map(function(i){
-				aa[0] = i;
-				return d[func].apply(d, aa);
-			});
-			return (alwaysThis || ( (_a.length > 1) || !d.isString(_a[0]) )) ? this : s; // String||dojo.NodeList
-		}
-	};
-
-	dojo.NodeList = function(){
-		//	summary:
-		//		dojo.NodeList is as subclass of Array which adds syntactic 
-		//		sugar for chaining, common iteration operations, animation, 
-		//		and node manipulation. NodeLists are most often returned as
-		//		the result of dojo.query() calls.
-		//	example:
-		//		create a node list from a node
-		//		|	new dojo.NodeList(dojo.byId("foo"));
-
-		return tnl(Array.apply(null, arguments));
-	}
-
-	dojo.NodeList._wrap = tnl;
-
-	dojo.extend(dojo.NodeList, {
-		// http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods
-
-		// FIXME: handle return values for #3244
-		//		http://trac.dojotoolkit.org/ticket/3244
-		
-		// FIXME:
-		//		need to wrap or implement:
-		//			join (perhaps w/ innerHTML/outerHTML overload for toString() of items?)
-		//			reduce
-		//			reduceRight
-
-		slice: function(/*===== begin, end =====*/){
-			// summary:
-			//		Returns a new NodeList, maintaining this one in place
-			// description:
-			//		This method behaves exactly like the Array.slice method
-			//		with the caveat that it returns a dojo.NodeList and not a
-			//		raw Array. For more details, see:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice
-			// begin: Integer
-			//		Can be a positive or negative integer, with positive
-			//		integers noting the offset to begin at, and negative
-			//		integers denoting an offset from the end (i.e., to the left
-			//		of the end)
-			// end: Integer?
-			//		Optional parameter to describe what position relative to
-			//		the NodeList's zero index to end the slice at. Like begin,
-			//		can be positive or negative.
-			var a = d._toArray(arguments);
-			return tnl(a.slice.apply(this, a));
-		},
-
-		splice: function(/*===== index, howmany, item =====*/){
-			// summary:
-			//		Returns a new NodeList, manipulating this NodeList based on
-			//		the arguments passed, potentially splicing in new elements
-			//		at an offset, optionally deleting elements
-			// description:
-			//		This method behaves exactly like the Array.splice method
-			//		with the caveat that it returns a dojo.NodeList and not a
-			//		raw Array. For more details, see:
-			//			<http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice>
-			// index: Integer
-			//		begin can be a positive or negative integer, with positive
-			//		integers noting the offset to begin at, and negative
-			//		integers denoting an offset from the end (i.e., to the left
-			//		of the end)
-			// howmany: Integer?
-			//		Optional parameter to describe what position relative to
-			//		the NodeList's zero index to end the slice at. Like begin,
-			//		can be positive or negative.
-			// item: Object...?
-			//		Any number of optional parameters may be passed in to be
-			//		spliced into the NodeList
-			// returns:
-			//		dojo.NodeList
-			var a = d._toArray(arguments);
-			return tnl(a.splice.apply(this, a));
-		},
-
-		concat: function(/*===== item =====*/){
-			// summary:
-			//		Returns a new NodeList comprised of items in this NodeList
-			//		as well as items passed in as parameters
-			// description:
-			//		This method behaves exactly like the Array.concat method
-			//		with the caveat that it returns a dojo.NodeList and not a
-			//		raw Array. For more details, see:
-			//			<http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat>
-			// item: Object...?
-			//		Any number of optional parameters may be passed in to be
-			//		spliced into the NodeList
-			// returns:
-			//		dojo.NodeList
-			var a = d._toArray(arguments, 0, [this]);
-			return tnl(a.concat.apply([], a));
-		},
-		
-		indexOf: function(/*Object*/ value, /*Integer?*/ fromIndex){
-			//	summary:
-			//		see dojo.indexOf(). The primary difference is that the acted-on 
-			//		array is implicitly this NodeList
-			// value:
-			//		The value to search for.
-			// fromIndex:
-			//		The loction to start searching from. Optional. Defaults to 0.
-			//	description:
-			//		For more details on the behavior of indexOf, see:
-			//			<http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
-			//	returns:
-			//		Positive Integer or 0 for a match, -1 of not found.
-			return d.indexOf(this, value, fromIndex); // Integer
-		},
-
-		lastIndexOf: function(/*===== value, fromIndex =====*/){
-			// summary:
-			//		see dojo.lastIndexOf(). The primary difference is that the
-			//		acted-on array is implicitly this NodeList
-			//	description:
-			//		For more details on the behavior of lastIndexOf, see:
-			//			<http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf>
-			// value: Object
-			//		The value to search for.
-			// fromIndex: Integer?
-			//		The loction to start searching from. Optional. Defaults to 0.
-			// returns:
-			//		Positive Integer or 0 for a match, -1 of not found.
-			return d.lastIndexOf.apply(d, d._toArray(arguments, 0, [this])); // Integer
-		},
-
-		every: function(/*Function*/callback, /*Object?*/thisObject){
-			//	summary:
-			//		see `dojo.every()` and:
-			//			<http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
-			//		Takes the same structure of arguments and returns as
-			//		dojo.every() with the caveat that the passed array is
-			//		implicitly this NodeList
-			return d.every(this, callback, thisObject); // Boolean
-		},
-
-		some: function(/*Function*/callback, /*Object?*/thisObject){
-			//	summary:
-			//		see dojo.some() and:
-			//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
-			//		Takes the same structure of arguments and returns as
-			//		dojo.some() with the caveat that the passed array is
-			//		implicitly this NodeList
-			return d.some(this, callback, thisObject); // Boolean
-		},
-
-		map: function(/*Function*/ func, /*Function?*/ obj){
-			//	summary:
-			//		see dojo.map(). The primary difference is that the acted-on
-			//		array is implicitly this NodeList and the return is a
-			//		dojo.NodeList (a subclass of Array)
-
-			return d.map(this, func, obj, d.NodeList); // dojo.NodeList
-		},
-
-		forEach: function(callback, thisObj){
-			//	summary:
-			//		see dojo.forEach(). The primary difference is that the acted-on 
-			//		array is implicitly this NodeList
-
-			d.forEach(this, callback, thisObj);
-			// non-standard return to allow easier chaining
-			return this; // dojo.NodeList 
-		},
-
-		// custom methods
-		
-		coords: function(){
-			//	summary:
-			// 		Returns the box objects all elements in a node list as
-			// 		an Array (*not* a NodeList)
-			
-			return d.map(this, d.coords); // Array
-		},
-
-		/*=====
-		attr: function(property, value){
-			//	summary:
-			//		gets or sets the DOM attribute for every element in the
-			//		NodeList
-			//	property: String
-			//		the attribute to get/set
-			//	value: String?
-			//		optional. The value to set the property to
-			//	returns:
-			//		if no value is passed, the result is an array of attribute values
-			//		If a value is passed, the return is this NodeList
-			return; // dojo.NodeList
-			return; // Array
-		},
-
-		style: function(property, value){
-			//	summary:
-			//		gets or sets the CSS property for every element in the NodeList
-			//	property: String
-			//		the CSS property to get/set, in JavaScript notation
-			//		("lineHieght" instead of "line-height") 
-			//	value: String?
-			//		optional. The value to set the property to
-			//	returns:
-			//		if no value is passed, the result is an array of strings.
-			//		If a value is passed, the return is this NodeList
-			return; // dojo.NodeList
-			return; // Array
-		},
-
-		addClass: function(className){
-			//	summary:
-			//		adds the specified class to every node in the list
-			//	className: String
-			//		the CSS class to add
-			return; // dojo.NodeList
-		},
-
-		removeClass: function(className){
-			//	summary:
-			//		removes the specified class from every node in the list
-			//	className: String
-			//		the CSS class to add
-			//	returns:
-			//		dojo.NodeList, this list
-			return; // dojo.NodeList
-		},
-
-		toggleClass: function(className, condition){
-			//	summary:
-			//		Adds a class to node if not present, or removes if present.
-			//		Pass a boolean condition if you want to explicitly add or remove.
-			//	condition: Boolean?
-			//		If passed, true means to add the class, false means to remove.
-			//	className: String
-			//		the CSS class to add
-			return; // dojo.NodeList
-		},
-
-		connect: function(methodName, objOrFunc, funcName){
-			//	summary:
-			//		attach event handlers to every item of the NodeList. Uses dojo.connect()
-			//		so event properties are normalized
-			//	methodName: String
-			//		the name of the method to attach to. For DOM events, this should be
-			//		the lower-case name of the event
-			//	objOrFunc: Object|Function|String
-			//		if 2 arguments are passed (methodName, objOrFunc), objOrFunc should
-			//		reference a function or be the name of the function in the global
-			//		namespace to attach. If 3 arguments are provided
-			//		(methodName, objOrFunc, funcName), objOrFunc must be the scope to 
-			//		locate the bound function in
-			//	funcName: String?
-			//		optional. A string naming the function in objOrFunc to bind to the
-			//		event. May also be a function reference.
-			//	example:
-			//		add an onclick handler to every button on the page
-			//		|	dojo.query("div:nth-child(odd)").connect("onclick", function(e){
-			//		|		
-			//		|	});
-			// example:
-			//		attach foo.bar() to every odd div's onmouseover
-			//		|	dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar");
-		},
-		=====*/
-		attr: _mapIntoDojo("attr"),
-		style: _mapIntoDojo("style"),
-		addClass: _mapIntoDojo("addClass", true),
-		removeClass: _mapIntoDojo("removeClass", true),
-		toggleClass: _mapIntoDojo("toggleClass", true),
-		connect: _mapIntoDojo("connect", true),
-
-		// FIXME: connectPublisher()? connectRunOnce()?
-
-		place: function(/*String||Node*/ queryOrNode, /*String*/ position){
-			//	summary:
-			//		places elements of this node list relative to the first element matched
-			//		by queryOrNode. Returns the original NodeList.
-			//	queryOrNode:
-			//		may be a string representing any valid CSS3 selector or a DOM node.
-			//		In the selector case, only the first matching element will be used 
-			//		for relative positioning.
-			//	position:
-			//		can be one of:
-			//			* "last"||"end" (default)
-			//			* "first||"start"
-			//			* "before"
-			//			* "after"
-			// 		or an offset in the childNodes property
-			var item = d.query(queryOrNode)[0];
-			return this.forEach(function(i){ d.place(i, item, position); }); // dojo.NodeList
-		},
-
-		orphan: function(/*String?*/ simpleFilter){
-			//	summary:
-			//		removes elements in this list that match the simple
-			//		filter from their parents and returns them as a new
-			//		NodeList.
-			//	simpleFilter:
-			//		single-expression CSS filter
-			//	returns:
-			//		`dojo.NodeList` containing the orpahned elements 
-			return (simpleFilter ? d._filterQueryResult(this, simpleFilter) : this). // dojo.NodeList
-				forEach("if(item.parentNode){ item.parentNode.removeChild(item); }"); 
-		},
-
-		adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
-			//	summary:
-			//		places any/all elements in queryOrListOrNode at a
-			//		position relative to the first element in this list.
-			//		Returns a dojo.NodeList of the adopted elements.
-			//	queryOrListOrNode:
-			//		a DOM node or a query string or a query result.
-			//		Represents the nodes to be adopted relative to the
-			//		first element of this NodeList.
-			//	position:
-			//		can be one of:
-			//			* "last"||"end" (default)
-			//			* "first||"start"
-			//			* "before"
-			//			* "after"
-			// 		or an offset in the childNodes property
-			var item = this[0];
-			return d.query(queryOrListOrNode).forEach(function(ai){ // dojo.NodeList
-				d.place(ai, item, position || "last"); 
-			});
-		},
-
-		// FIXME: do we need this?
-		query: function(/*String*/ queryStr){
-			//	summary:
-			//		Returns a new, flattened NodeList. Elements of the new list
-			//		satisfy the passed query but use elements of the
-			//		current NodeList as query roots.
-
-			if(!queryStr){ return this; }
-
-			// FIXME: probably slow
-			// FIXME: use map?
-			var ret = d.NodeList();
-			this.forEach(function(item){
-				// FIXME: why would we ever get undefined here?
-				ret = ret.concat(d.query(queryStr, item).filter(function(subItem){ return (subItem !== undefined); }));
-			});
-			return ret; // dojo.NodeList
-		},
-
-		filter: function(/*String*/ simpleQuery){
-			//	summary:
-			// 		"masks" the built-in javascript filter() method to support
-			//		passing a simple string filter in addition to supporting
-			//		filtering function objects.
-			//	example:
-			//		"regular" JS filter syntax as exposed in dojo.filter:
-			//		|	dojo.query("*").filter(function(item){
-			//		|		// highlight every paragraph
-			//		|		return (item.nodeName == "p");
-			//		|	}).styles("backgroundColor", "yellow");
-			// example:
-			//		the same filtering using a CSS selector
-			//		|	dojo.query("*").filter("p").styles("backgroundColor", "yellow");
-
-			var items = this;
-			var _a = arguments;
-			var r = d.NodeList();
-			var rp = function(t){ 
-				if(t !== undefined){
-					r.push(t); 
-				}
-			}
-			if(d.isString(simpleQuery)){
-				items = d._filterQueryResult(this, _a[0]);
-				if(_a.length == 1){
-					// if we only got a string query, pass back the filtered results
-					return items; // dojo.NodeList
-				}
-				// if we got a callback, run it over the filtered items
-				_a.shift();
-			}
-			// handle the (callback, [thisObject]) case
-			d.forEach(d.filter(items, _a[0], _a[1]), rp);
-			return r; // dojo.NodeList
-		},
-		
-		/*
-		// FIXME: should this be "copyTo" and include parenting info?
-		clone: function(){
-			// summary:
-			//		creates node clones of each element of this list
-			//		and returns a new list containing the clones
-		},
-		*/
-
-		addContent: function(/*String*/ content, /*String||Integer?*/ position){
-			//	summary:
-			//		add a node or some HTML as a string to every item in the list. 
-			//		Returns the original list.
-			//	description:
-			//		a copy of the HTML content is added to each item in the
-			//		list, with an optional position argument. If no position
-			//		argument is provided, the content is appended to the end of
-			//		each item.
-			//	content:
-			//		the HTML in string format to add at position to every item
-			//	position:
-			//		can be one of:
-			//			* "last"||"end" (default)
-			//			* "first||"start"
-			//			* "before"
-			//			* "after"
-			// 		or an offset in the childNodes property
-			//	example:
-			//		appends content to the end if the position is ommitted
-			//	|	dojo.query("h3 > p").addContent("hey there!");
-			//	example:
-			//		add something to the front of each element that has a "thinger" property:
-			//	|	dojo.query("[thinger]").addContent("...", "first");
-			//	example:
-			//		adds a header before each element of the list
-			//	|	dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
-			var ta = d.doc.createElement("span");
-			if(d.isString(content)){
-				ta.innerHTML = content;
-			}else{
-				ta.appendChild(content);
-			}
-			if(position === undefined){
-				position = "last";
-			}
-			var ct = (position == "first" || position == "after") ? "lastChild" : "firstChild";
-			this.forEach(function(item){
-				var tn = ta.cloneNode(true);
-				while(tn[ct]){
-					d.place(tn[ct], item, position);
-				}
-			});
-			return this; // dojo.NodeList
-		},
-
-		empty: function(){
-			//	summary:
-			//		clears all content from each node in the list
-			return this.forEach("item.innerHTML='';"); // dojo.NodeList
-
-			// FIXME: should we be checking for and/or disposing of widgets below these nodes?
-		},
-		
-		instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){
-			//	summary:
-			//		Create a new instance of a specified class, using the
-			//		specified properties and each node in the nodeList as a
-			//		srcNodeRef
-			//
-			var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass);
-			return this.forEach(function(i){
-				new c(properties||{},i);
-			}) // dojo.NodeList
-		},
-
-		at: function(/*===== index =====*/){
-			//	summary:
-			//		Returns a new NodeList comprised of items in this NodeList
-			//		at the given index or indices.
-			//	index: Integer...
-			//		One or more 0-based indices of items in the current NodeList.
-			//	returns:
-			//		dojo.NodeList
-			var nl = new dojo.NodeList();
-			dojo.forEach(arguments, function(i) { if(this[i]) { nl.push(this[i]); } }, this);
-			return nl; // dojo.NodeList
-		}
-
-	});
-
-	// syntactic sugar for DOM events
-	d.forEach([
-		"blur", "focus", "click", "keydown", "keypress", "keyup", "mousedown",
-		"mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover",
-		"mouseup", "submit", "load", "error"
-		], function(evt){
-			var _oe = "on"+evt;
-			d.NodeList.prototype[_oe] = function(a, b){
-				return this.connect(_oe, a, b);
-			}
-				// FIXME: should these events trigger publishes?
-				/*
-				return (a ? this.connect(_oe, a, b) : 
-							this.forEach(function(n){  
-								// FIXME:
-								//		listeners get buried by
-								//		addEventListener and can't be dug back
-								//		out to be triggered externally.
-								// see:
-								//		http://developer.mozilla.org/en/docs/DOM:element
-
-								
-
-								// FIXME: need synthetic event support!
-								var _e = { target: n, faux: true, type: evt };
-								// dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });
-								try{ n[evt](_e); }catch(e){  }
-								try{ n[_oe](_e); }catch(e){  }
-							})
-				);
-			}
-			*/
-		}
-	);
-
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.query"] = true;
-dojo.provide("dojo._base.query");
-
-
-/*
-	dojo.query() architectural overview:
-
-		dojo.query is a relatively full-featured CSS3 query library. It is
-		designed to take any valid CSS3 selector and return the nodes matching
-		the selector. To do this quickly, it processes queries in several
-		steps, applying caching where profitable.
-		
-		The steps (roughly in reverse order of the way they appear in the code):
-			1.) check to see if we already have a "query dispatcher"
-				- if so, use that with the given parameterization. Skip to step 4.
-			2.) attempt to determine which branch to dispatch the query to:
-				- JS (optimized DOM iteration)
-				- xpath (for browsers that support it and where it's fast)
-				- native (not available in any browser yet)
-			3.) tokenize and convert to executable "query dispatcher"
-				- this is where the lion's share of the complexity in the
-				  system lies. In the DOM version, the query dispatcher is
-				  assembled as a chain of "yes/no" test functions pertaining to
-				  a section of a simple query statement (".blah:nth-child(odd)"
-				  but not "div div", which is 2 simple statements). Individual
-				  statement dispatchers are cached (to prevent re-definition)
-				  as are entire dispatch chains (to make re-execution of the
-				  same query fast)
-				- in the xpath path, tokenization yields a concatenation of
-				  parameterized xpath selectors. As with the DOM version, both
-				  simple selector blocks and overall evaluators are cached to
-				  prevent re-defintion
-			4.) the resulting query dispatcher is called in the passed scope (by default the top-level document)
-				- for DOM queries, this results in a recursive, top-down
-				  evaluation of nodes based on each simple query section
-				- xpath queries can, thankfully, be executed in one shot
-			5.) matched nodes are pruned to ensure they are unique
-*/
-
-;(function(){
-	// define everything in a closure for compressability reasons. "d" is an
-	// alias to "dojo" since it's so frequently used. This seems a
-	// transformation that the build system could perform on a per-file basis.
-
-	////////////////////////////////////////////////////////////////////////
-	// Utility code
-	////////////////////////////////////////////////////////////////////////
-
-	var d = dojo;
-	var childNodesName = dojo.isIE ? "children" : "childNodes";
-	var caseSensitive = false;
-
-	var getQueryParts = function(query){
-		// summary: state machine for query tokenization
-		if(">~+".indexOf(query.charAt(query.length-1)) >= 0){
-			query += " *"
-		}
-		query += " "; // ensure that we terminate the state machine
-
-		var ts = function(s, e){
-			return d.trim(query.slice(s, e));
-		}
-
-		// the overall data graph of the full query, as represented by queryPart objects
-		var qparts = []; 
-		// state keeping vars
-		var inBrackets = -1;
-		var inParens = -1;
-		var inMatchFor = -1;
-		var inPseudo = -1;
-		var inClass = -1;
-		var inId = -1;
-		var inTag = -1;
-		var lc = ""; // the last character
-		var cc = ""; // the current character
-		var pStart;
-		// iteration vars
-		var x = 0; // index in the query
-		var ql = query.length;
-		var currentPart = null; // data structure representing the entire clause
-		var _cp = null; // the current pseudo or attr matcher
-
-		var endTag = function(){
-			if(inTag >= 0){
-				var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
-				currentPart[ (">~+".indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
-				inTag = -1;
-			}
-		}
-
-		var endId = function(){
-			if(inId >= 0){
-				currentPart.id = ts(inId, x).replace(/\\/g, "");
-				inId = -1;
-			}
-		}
-
-		var endClass = function(){
-			if(inClass >= 0){
-				currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, ""));
-				inClass = -1;
-			}
-		}
-
-		var endAll = function(){
-			endId(); endTag(); endClass();
-		}
-
-		for(; lc=cc, cc=query.charAt(x),x<ql; x++){
-			if(lc == "\\"){ continue; }
-			if(!currentPart){
-				// NOTE: I hate all this alloc, but it's shorter than writing tons of if's
-				pStart = x;
-				currentPart = {
-					query: null,
-					pseudos: [],
-					attrs: [],
-					classes: [],
-					tag: null,
-					oper: null,
-					id: null
-				};
-				inTag = x;
-			}
-
-			if(inBrackets >= 0){
-				// look for a the close first
-				if(cc == "]"){
-					if(!_cp.attr){
-						_cp.attr = ts(inBrackets+1, x);
-					}else{
-						_cp.matchFor = ts((inMatchFor||inBrackets+1), x);
-					}
-					var cmf = _cp.matchFor;
-					if(cmf){
-						if(	(cmf.charAt(0) == '"') || (cmf.charAt(0)  == "'") ){
-							_cp.matchFor = cmf.substring(1, cmf.length-1);
-						}
-					}
-					currentPart.attrs.push(_cp);
-					_cp = null; // necessaray?
-					inBrackets = inMatchFor = -1;
-				}else if(cc == "="){
-					var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
-					_cp.type = addToCc+cc;
-					_cp.attr = ts(inBrackets+1, x-addToCc.length);
-					inMatchFor = x+1;
-				}
-				// now look for other clause parts
-			}else if(inParens >= 0){
-				if(cc == ")"){
-					if(inPseudo >= 0){
-						_cp.value = ts(inParens+1, x);
-					}
-					inPseudo = inParens = -1;
-				}
-			}else if(cc == "#"){
-				endAll();
-				inId = x+1;
-			}else if(cc == "."){
-				endAll();
-				inClass = x;
-			}else if(cc == ":"){
-				endAll();
-				inPseudo = x;
-			}else if(cc == "["){
-				endAll();
-				inBrackets = x;
-				_cp = {
-					/*=====
-					attr: null, type: null, matchFor: null
-					=====*/
-				};
-			}else if(cc == "("){
-				if(inPseudo >= 0){
-					_cp = { 
-						name: ts(inPseudo+1, x), 
-						value: null
-					}
-					currentPart.pseudos.push(_cp);
-				}
-				inParens = x;
-			}else if(cc == " " && lc != cc){
-				// note that we expect the string to be " " terminated
-				endAll();
-				if(inPseudo >= 0){
-					currentPart.pseudos.push({ name: ts(inPseudo+1, x) });
-				}
-				currentPart.hasLoops = (	
-						currentPart.pseudos.length || 
-						currentPart.attrs.length || 
-						currentPart.classes.length	);
-				currentPart.query = ts(pStart, x);
-				currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
-				if(currentPart.tag){ // FIXME: not valid in case-sensitive documents
-					currentPart.tag = currentPart.tag.toUpperCase();
-				}
-				qparts.push(currentPart);
-				currentPart = null;
-			}
-		}
-		return qparts;
-	};
-	
-
-	////////////////////////////////////////////////////////////////////////
-	// XPath query code
-	////////////////////////////////////////////////////////////////////////
-
-	// this array is a lookup used to generate an attribute matching function.
-	// There is a similar lookup/generator list for the DOM branch with similar
-	// calling semantics.
-	var xPathAttrs = {
-		"*=": function(attr, value){
-			return "[contains(@"+attr+", '"+ value +"')]";
-		},
-		"^=": function(attr, value){
-			return "[starts-with(@"+attr+", '"+ value +"')]";
-		},
-		"$=": function(attr, value){
-			return "[substring(@"+attr+", string-length(@"+attr+")-"+(value.length-1)+")='"+value+"']";
-		},
-		"~=": function(attr, value){
-			return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
-		},
-		"|=": function(attr, value){
-			return "[contains(concat(' ',@"+attr+",' '), ' "+ value +"-')]";
-		},
-		"=": function(attr, value){
-			return "[@"+attr+"='"+ value +"']";
-		}
-	};
-
-	// takes a list of attribute searches, the overall query, a function to
-	// generate a default matcher, and a closure-bound method for providing a
-	// matching function that generates whatever type of yes/no distinguisher
-	// the query method needs. The method is a bit tortured and hard to read
-	// because it needs to be used in both the XPath and DOM branches.
-	var handleAttrs = function(	attrList, 
-								query, 
-								getDefault, 
-								handleMatch){
-		d.forEach(query.attrs, function(attr){
-			var matcher;
-			// type, attr, matchFor
-			if(attr.type && attrList[attr.type]){
-				matcher = attrList[attr.type](attr.attr, attr.matchFor);
-			}else if(attr.attr.length){
-				matcher = getDefault(attr.attr);
-			}
-			if(matcher){ handleMatch(matcher); }
-		});
-	}
-
-	var buildPath = function(query){
-		var xpath = ".";
-		var qparts = getQueryParts(d.trim(query));
-		while(qparts.length){
-			var tqp = qparts.shift();
-			var prefix;
-			var postfix = "";
-			if(tqp.oper == ">"){
-				prefix = "/";
-				// prefix = "/child::*";
-				tqp = qparts.shift();
-			}else if(tqp.oper == "~"){
-				prefix = "/following-sibling::"; // get element following siblings
-				tqp = qparts.shift();
-			}else if(tqp.oper == "+"){
-				// FIXME: 
-				//		fails when selecting subsequent siblings by node type
-				//		because the position() checks the position in the list
-				//		of matching elements and not the localized siblings
-				prefix = "/following-sibling::";
-				postfix = "[position()=1]";
-				tqp = qparts.shift();
-			}else{
-				prefix = "//";
-				// prefix = "/descendant::*"
-			}
-
-			// get the tag name (if any)
-
-			xpath += prefix + tqp.tag + postfix;
-			
-			// check to see if it's got an id. Needs to come first in xpath.
-			if(tqp.id){
-				xpath += "[@id='"+tqp.id+"'][1]";
-			}
-
-			d.forEach(tqp.classes, function(cn){
-				var cnl = cn.length;
-				var padding = " ";
-				if(cn.charAt(cnl-1) == "*"){
-					padding = ""; cn = cn.substr(0, cnl-1);
-				}
-				xpath += 
-					"[contains(concat(' ',@class,' '), ' "+
-					cn + padding + "')]";
-			});
-
-			handleAttrs(xPathAttrs, tqp, 
-				function(condition){
-						return "[@"+condition+"]";
-				},
-				function(matcher){
-					xpath += matcher;
-				}
-			);
-
-			// FIXME: need to implement pseudo-class checks!!
-		};
-		return xpath;
-	};
-
-	var _xpathFuncCache = {};
-	var getXPathFunc = function(path){
-		if(_xpathFuncCache[path]){
-			return _xpathFuncCache[path];
-		}
-
-		var doc = d.doc;
-		// don't need to memoize. The closure scope handles it for us.
-		var xpath = buildPath(path);
-
-		var tf = function(parent){
-			// XPath query strings are memoized.
-
-			var ret = [];
-			var xpathResult;
-			var tdoc = doc;
-			if(parent){
-				tdoc = (parent.nodeType == 9) ? parent : parent.ownerDocument;
-			}
-			try{
-				xpathResult = tdoc.evaluate(xpath, parent, null, 
-												// XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
-												XPathResult.ANY_TYPE, null);
-			}catch(e){
-				
-				
-			}
-			var result = xpathResult.iterateNext();
-			while(result){
-				ret.push(result);
-				result = xpathResult.iterateNext();
-			}
-			return ret;
-		}
-		return _xpathFuncCache[path] = tf;
-	};
-
-	/*
-	d.xPathMatch = function(query){
-		// XPath based DOM query system. Handles a small subset of CSS
-		// selectors, subset is identical to the non-XPath version of this
-		// function. 
-
-		return getXPathFunc(query)();
-	}
-	*/
-
-	////////////////////////////////////////////////////////////////////////
-	// DOM query code
-	////////////////////////////////////////////////////////////////////////
-
-	var _filtersCache = {};
-	var _simpleFiltersCache = {};
-
-	// the basic building block of the yes/no chaining system. agree(f1, f2)
-	// generates a new function which returns the boolean results of both of
-	// the passed functions to a single logical-anded result.
-	var agree = function(first, second){
-		if(!first){ return second; }
-		if(!second){ return first; }
-
-		return function(){
-			return first.apply(window, arguments) && second.apply(window, arguments);
-		}
-	}
-
-	var _childElements = function(root){
-		var ret = [];
-		var te, x = 0, tret = root[childNodesName];
-		while((te = tret[x++])){
-			if(te.nodeType == 1){ ret.push(te); }
-		}
-		return ret;
-	}
-
-	var _nextSiblings = function(root, single){
-		var ret = [];
-		var te = root;
-		while(te = te.nextSibling){
-			if(te.nodeType == 1){
-				ret.push(te);
-				if(single){ break; }
-			}
-		}
-		return ret;
-	}
-
-	// FIXME:
-	//		we need to re-write the way "~" and "+" selectors are handled since
-	//		the left-hand selector simply modifies the right (which is the
-	//		actual search selector). We need to locate on search selector
-	//		instead of modifier to speed up these searches.
-
-	var _filterDown = function(element, queryParts, matchArr, idx){
-		// NOTE:
-		//		in the fast path! this function is called recursively and for
-		//		every run of a query.
-		var nidx = idx+1;
-		var isFinal = (queryParts.length == nidx);
-		var tqp = queryParts[idx];
-
-		// see if we can constrain our next level to direct children
-		if(tqp.oper){
-			// find some eligable children to search
-			var ecn = (tqp.oper == ">") ? 
-				_childElements(element) :
-				_nextSiblings(element, (tqp.oper == "+"));
-
-			if(!ecn || !ecn.length){
-				return;
-			}
-			nidx++;
-			isFinal = (queryParts.length == nidx);
-			// kinda janky, too much array alloc
-			var tf = getFilterFunc(queryParts[idx+1]);
-			// for(var x=ecn.length-1, te; x>=0, te=ecn[x]; x--){
-			for(var x=0, ecnl=ecn.length, te; x<ecnl, te=ecn[x]; x++){
-				if(tf(te)){
-					if(isFinal){
-						matchArr.push(te);
-					}else{
-						_filterDown(te, queryParts, matchArr, nidx);
-					}
-				}
-				/*
-				if(x==0){
-					break;
-				}
-				*/
-			}
-		}
-
-		// otherwise, keep going down, unless we'er at the end
-		var candidates = getElementsFunc(tqp)(element);
-		if(isFinal){
-			while(candidates.length){
-				matchArr.push(candidates.shift());
-			}
-			/*
-			candidates.unshift(0, matchArr.length-1);
-			matchArr.splice.apply(matchArr, candidates);
-			*/
-		}else{
-			// if we're not yet at the bottom, keep going!
-			while(candidates.length){
-				_filterDown(candidates.shift(), queryParts, matchArr, nidx);
-			}
-		}
-	}
-
-	var filterDown = function(elements, queryParts){
-		var ret = [];
-
-		// for every root, get the elements that match the descendant selector
-		// for(var x=elements.length-1, te; x>=0, te=elements[x]; x--){
-		var x = elements.length - 1, te;
-		while((te = elements[x--])){
-			_filterDown(te, queryParts, ret, 0);
-		}
-		return ret;
-	}
-
-	var getFilterFunc = function(q){
-		// note: query can't have spaces!
-		if(_filtersCache[q.query]){
-			return _filtersCache[q.query];
-		}
-		var ff = null;
-
-		// does it have a tagName component?
-		if(q.tag){
-			if(q.tag == "*"){
-				ff = agree(ff, 
-					function(elem){
-						return (elem.nodeType == 1);
-					}
-				);
-			}else{
-				// tag name match
-				ff = agree(ff, 
-					function(elem){
-						return (
-							(elem.nodeType == 1) &&
-							(q[ caseSensitive ? "otag" : "tag" ] == elem.tagName)
-							// (q.tag == elem.tagName.toLowerCase())
-						);
-						// return isTn;
-					}
-				);
-			}
-		}
-
-		// does the node have an ID?
-		if(q.id){
-			ff = agree(ff, 
-				function(elem){
-					return (
-						(elem.nodeType == 1) &&
-						(elem.id == q.id)
-					);
-				}
-			);
-		}
-
-		if(q.hasLoops){
-			// if we have other query param parts, make sure we add them to the
-			// filter chain
-			ff = agree(ff, getSimpleFilterFunc(q));
-		}
-
-		return _filtersCache[q.query] = ff;
-	}
-
-	var getNodeIndex = function(node){
-		// NOTE: 
-		//		we could have a more accurate caching mechanism by invalidating
-		//		caches after the query has finished, but I think that'd lead to
-		//		significantly more cache churn than the cache would provide
-		//		value for in the common case. Generally, we're more
-		//		conservative (and therefore, more accurate) than jQuery and
-		//		DomQuery WRT node node indexes, but there may be corner cases
-		//		in which we fall down.  How much we care about them is TBD.
-
-		var pn = node.parentNode;
-		var pnc = pn.childNodes;
-
-		// check to see if we can trust the cache. If not, re-key the whole
-		// thing and return our node match from that.
-
-		var nidx = -1;
-		var child = pn.firstChild;
-		if(!child){
-			return nidx;
-		}
-
-		var ci = node["__cachedIndex"];
-		var cl = pn["__cachedLength"];
-
-		// only handle cache building if we've gone out of sync
-		if(((typeof cl == "number")&&(cl != pnc.length))||(typeof ci != "number")){
-			// rip though the whole set, building cache indexes as we go
-			pn["__cachedLength"] = pnc.length;
-			var idx = 1;
-			do{
-				// we only assign indexes for nodes with nodeType == 1, as per:
-				//		http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
-				// only elements are counted in the search order, and they
-				// begin at 1 for the first child's index
-
-				if(child === node){
-					nidx = idx;
-				}
-				if(child.nodeType == 1){
-					child["__cachedIndex"] = idx;
-					idx++;
-				}
-				child = child.nextSibling;
-			}while(child);
-		}else{
-			// NOTE: 
-			//		could be incorrect in some cases (node swaps involving the
-			//		passed node, etc.), but we ignore those due to the relative
-			//		unlikelihood of that occuring
-			nidx = ci;
-		}
-		return nidx;
-	}
-
-	var firedCount = 0;
-
-	var blank = "";
-	var _getAttr = function(elem, attr){
-		if(attr == "class"){
-			return elem.className || blank;
-		}
-		if(attr == "for"){
-			return elem.htmlFor || blank;
-		}
-		if(attr == "style"){
-			return elem.style.cssText || blank;
-		}
-		return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
-	}
-
-	var attrs = {
-		"*=": function(attr, value){
-			return function(elem){
-				// E[foo*="bar"]
-				//		an E element whose "foo" attribute value contains
-				//		the substring "bar"
-				return (_getAttr(elem, attr).indexOf(value)>=0);
-			}
-		},
-		"^=": function(attr, value){
-			// E[foo^="bar"]
-			//		an E element whose "foo" attribute value begins exactly
-			//		with the string "bar"
-			return function(elem){
-				return (_getAttr(elem, attr).indexOf(value)==0);
-			}
-		},
-		"$=": function(attr, value){
-			// E[foo$="bar"]	
-			//		an E element whose "foo" attribute value ends exactly
-			//		with the string "bar"
-			var tval = " "+value;
-			return function(elem){
-				var ea = " "+_getAttr(elem, attr);
-				return (ea.lastIndexOf(value)==(ea.length-value.length));
-			}
-		},
-		"~=": function(attr, value){
-			// E[foo~="bar"]	
-			//		an E element whose "foo" attribute value is a list of
-			//		space-separated values, one of which is exactly equal
-			//		to "bar"
-
-			// return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
-			var tval = " "+value+" ";
-			return function(elem){
-				var ea = " "+_getAttr(elem, attr)+" ";
-				return (ea.indexOf(tval)>=0);
-			}
-		},
-		"|=": function(attr, value){
-			// E[hreflang|="en"]
-			//		an E element whose "hreflang" attribute has a
-			//		hyphen-separated list of values beginning (from the
-			//		left) with "en"
-			var valueDash = " "+value+"-";
-			return function(elem){
-				var ea = " "+(elem.getAttribute(attr, 2) || "");
-				return (
-					(ea == value) ||
-					(ea.indexOf(valueDash)==0)
-				);
-			}
-		},
-		"=": function(attr, value){
-			return function(elem){
-				return (_getAttr(elem, attr) == value);
-			}
-		}
-	};
-
-	var pseudos = {
-		"checked": function(name, condition){
-			return function(elem){
-				return !!d.attr(elem, "checked");
-			}
-		},
-		"first-child": function(name, condition){
-			return function(elem){
-				if(elem.nodeType != 1){ return false; }
-				// check to see if any of the previous siblings are elements
-				var fc = elem.previousSibling;
-				while(fc && (fc.nodeType != 1)){
-					fc = fc.previousSibling;
-				}
-				return (!fc);
-			}
-		},
-		"last-child": function(name, condition){
-			return function(elem){
-				if(elem.nodeType != 1){ return false; }
-				// check to see if any of the next siblings are elements
-				var nc = elem.nextSibling;
-				while(nc && (nc.nodeType != 1)){
-					nc = nc.nextSibling;
-				}
-				return (!nc);
-			}
-		},
-		"empty": function(name, condition){
-			return function(elem){
-				// DomQuery and jQuery get this wrong, oddly enough.
-				// The CSS 3 selectors spec is pretty explicit about
-				// it, too.
-				var cn = elem.childNodes;
-				var cnl = elem.childNodes.length;
-				// if(!cnl){ return true; }
-				for(var x=cnl-1; x >= 0; x--){
-					var nt = cn[x].nodeType;
-					if((nt == 1)||(nt == 3)){ return false; }
-				}
-				return true;
-			}
-		},
-		"contains": function(name, condition){
-			return function(elem){
-				// FIXME: I dislike this version of "contains", as
-				// whimsical attribute could set it off. An inner-text
-				// based version might be more accurate, but since
-				// jQuery and DomQuery also potentially get this wrong,
-				// I'm leaving it for now.
-				if(condition.charAt(0)=='"' || condition.charAt(0)=="'"){//remove quote
-					condition=condition.substr(1,condition.length-2);
-				}
-				return (elem.innerHTML.indexOf(condition) >= 0);
-			}
-		},
-		"not": function(name, condition){
-			var ntf = getFilterFunc(getQueryParts(condition)[0]);
-			return function(elem){
-				return (!ntf(elem));
-			}
-		},
-		"nth-child": function(name, condition){
-			var pi = parseInt;
-			if(condition == "odd"){
-				condition = "2n+1";
-			}else if(condition == "even"){
-				condition = "2n";
-			}
-			if(condition.indexOf("n") != -1){
-				var tparts = condition.split("n", 2);
-				var pred = tparts[0] ? (tparts[0]=='-'?-1:pi(tparts[0])) : 1;
-				var idx = tparts[1] ? pi(tparts[1]) : 0;
-				var lb = 0, ub = -1;
-				if(pred>0){
-					if(idx<0){
-						idx = (idx % pred) && (pred + (idx % pred));
-					}else if(idx>0){
-						if(idx >= pred){
-							lb = idx - idx % pred;
-						}
-						idx = idx % pred;
-					}
-				}else if(pred<0){
-					pred *= -1;
-					if(idx>0){
-						ub = idx;
-						idx = idx % pred;
-					} //idx has to be greater than 0 when pred is negative; shall we throw an error here?
-				}
-				if(pred>0){
-					return function(elem){
-						var i=getNodeIndex(elem);
-						return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
-					}
-				}else{
-					condition=idx;
-				}
-			}
-			//if(condition.indexOf("n") == -1){
-			var ncount = pi(condition);
-			return function(elem){
-				return (getNodeIndex(elem) == ncount);
-			}
-		}
-	};
-
-	var defaultGetter = (d.isIE) ? function(cond){
-		var clc = cond.toLowerCase();
-		return function(elem){
-			return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
-		}
-	} : function(cond){
-		return function(elem){
-			return (elem && elem.getAttribute && elem.hasAttribute(cond));
-		}
-	};
-
-	var getSimpleFilterFunc = function(query){
-
-		var fcHit = (_simpleFiltersCache[query.query]||_filtersCache[query.query]);
-		if(fcHit){ return fcHit; }
-
-		var ff = null;
-
-		// the only case where we'll need the tag name is if we came from an ID query
-		if(query.id){ // do we have an ID component?
-			if(query.tag != "*"){
-				ff = agree(ff, function(elem){
-					return (elem.tagName == query[ caseSensitive ? "otag" : "tag" ]);
-				});
-			}
-		}
-
-		// if there's a class in our query, generate a match function for it
-		d.forEach(query.classes, function(cname, idx, arr){
-			// get the class name
-			var isWildcard = cname.charAt(cname.length-1) == "*";
-			if(isWildcard){
-				cname = cname.substr(0, cname.length-1);
-			}
-			// I dislike the regex thing, even if memozied in a cache, but it's VERY short
-			var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
-			ff = agree(ff, function(elem){
-				return re.test(elem.className);
-			});
-			ff.count = idx;
-		});
-
-		d.forEach(query.pseudos, function(pseudo){
-			if(pseudos[pseudo.name]){
-				ff = agree(ff, pseudos[pseudo.name](pseudo.name, pseudo.value));
-			}
-		});
-
-		handleAttrs(attrs, query, defaultGetter,
-			function(tmatcher){ ff = agree(ff, tmatcher); }
-		);
-		if(!ff){
-			ff = function(){ return true; };
-		}
-		return _simpleFiltersCache[query.query] = ff;
-	}
-
-	var _getElementsFuncCache = { };
-
-	var getElementsFunc = function(query, root){
-		var fHit = _getElementsFuncCache[query.query];
-		if(fHit){ return fHit; }
-
-		// NOTE: this function is in the fast path! not memoized!!!
-
-		// the query doesn't contain any spaces, so there's only so many
-		// things it could be
-
-		if(query.id && !query.hasLoops && !query.tag){
-			// ID-only query. Easy.
-			return _getElementsFuncCache[query.query] = function(root){
-				// FIXME: if root != document, check for parenting!
-				return [ d.byId(query.id) ];
-			}
-		}
-
-		var filterFunc = getSimpleFilterFunc(query);
-
-		var retFunc;
-		if(query.tag && query.id && !query.hasLoops){
-			// we got a filtered ID search (e.g., "h4#thinger")
-			retFunc = function(root){
-				var te = d.byId(query.id, (root.ownerDocument||root)); //root itself may be a document
-				if(filterFunc(te)){
-					return [ te ];
-				}
-			}
-		}else{
-			var tret;
-
-			if(!query.hasLoops){
-				// it's just a plain-ol elements-by-tag-name query from the root
-				retFunc = function(root){
-					var ret = [];
-					var te, x=0, tret = root.getElementsByTagName(query[ caseSensitive ? "otag" : "tag"]);
-					while((te = tret[x++])){
-						ret.push(te);
-					}
-					return ret;
-				}
-			}else{
-				retFunc = function(root){
-					var ret = [];
-					var te, x = 0, tret = root.getElementsByTagName(query[ caseSensitive ? "otag" : "tag"]);
-					while((te = tret[x++])){
-						if(filterFunc(te)){
-							ret.push(te);
-						}
-					}
-					return ret;
-				}
-			}
-		}
-		return _getElementsFuncCache[query.query] = retFunc;
-	}
-
-	var _partsCache = {};
-
-	////////////////////////////////////////////////////////////////////////
-	// the query runner
-	////////////////////////////////////////////////////////////////////////
-
-	// this is the second level of spliting, from full-length queries (e.g.,
-	// "div.foo .bar") into simple query expressions (e.g., ["div.foo",
-	// ".bar"])
-	var _queryFuncCache = {
-		"*": d.isIE ? 
-			function(root){ 
-					return root.all;
-			} : 
-			function(root){
-				 return root.getElementsByTagName("*");
-			},
-		"~": _nextSiblings,
-		"+": function(root){ return _nextSiblings(root, true); },
-		">": _childElements
-	};
-
-	var getStepQueryFunc = function(query){
-		// if it's trivial, get a fast-path dispatcher
-		var qparts = getQueryParts(d.trim(query));
-		// if(query[query.length-1] == ">"){ query += " *"; }
-		if(qparts.length == 1){
-			var tt = getElementsFunc(qparts[0]);
-			tt.nozip = true; // FIXME: is this right? Shouldn't this be wrapped in a closure to mark the return?
-			return tt;
-		}
-
-		// otherwise, break it up and return a runner that iterates over the parts recursively
-		var sqf = function(root){
-			var localQueryParts = qparts.slice(0); // clone the src arr
-			var candidates;
-			if(localQueryParts[0].oper == ">"){ // FIXME: what if it's + or ~?
-				candidates = [ root ];
-				// root = document;
-			}else{
-				candidates = getElementsFunc(localQueryParts.shift())(root);
-			}
-			return filterDown(candidates, localQueryParts);
-		}
-		return sqf;
-	}
-
-	// a specialized method that implements our primoridal "query optimizer".
-	// This allows us to dispatch queries to the fastest subsystem we can get.
-	var _getQueryFunc = (
-		// NOTE: 
-		//		XPath on the Webkit is slower than it's DOM iteration for most
-		//		test cases
-		// FIXME: 
-		//		we should try to capture some runtime speed data for each query
-		//		function to determine on the fly if we should stick w/ the
-		//		potentially optimized variant or if we should try something
-		//		new.
-		(document["evaluate"] && !d.isSafari) ? 
-		function(query, root){
-			// has xpath support that's faster than DOM
-			var qparts = query.split(" ");
-			// can we handle it?
-			if(	(!caseSensitive) && // not strictly necessaray, but simplifies lots of stuff
-				(document["evaluate"]) &&
-				(query.indexOf(":") == -1) &&
-				(query.indexOf("+") == -1) // skip direct sibling matches. See line ~344
-			){
-				// dojo.debug(query);
-				// should we handle it?
-
-				// kind of a lame heuristic, but it works
-				if(	
-					// a "div div div" style query
-					((qparts.length > 2)&&(query.indexOf(">") == -1))||
-					// or something else with moderate complexity. kinda janky
-					(qparts.length > 3)||
-					(query.indexOf("[")>=0)||
-					// or if it's a ".thinger" query
-					((1 == qparts.length)&&(0 <= query.indexOf(".")))
-
-				){
-					// use get and cache a xpath runner for this selector
-					return getXPathFunc(query);
-				}
-			}
-
-			// fallthrough
-			return getStepQueryFunc(query);
-		} : getStepQueryFunc
-	);
-	// uncomment to disable XPath for testing and tuning the DOM path
-	// _getQueryFunc = getStepQueryFunc;
-
-	// FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything
-
-	// uncomment to disable DOM queries for testing and tuning XPath
-	// _getQueryFunc = getXPathFunc;
-
-	// this is the primary caching for full-query results. The query dispatcher
-	// functions are generated here and then pickled for hash lookup in the
-	// future
-	var getQueryFunc = function(query){
-		// return a cached version if one is available
-		var qcz = query.charAt(0);
-		if(d.doc["querySelectorAll"] && 
-			( (!d.isSafari) || (d.isSafari > 3.1) ) && // see #5832
-			// as per CSS 3, we can't currently start w/ combinator:
-			//		http://www.w3.org/TR/css3-selectors/#w3cselgrammar
-			(">+~".indexOf(qcz) == -1)
-		){
-			return function(root){
-				var r = root.querySelectorAll(query);
-				r.nozip = true; // skip expensive duplication checks and just wrap in a NodeList
-				return r;
-			};
-		}
-		if(_queryFuncCache[query]){ return _queryFuncCache[query]; }
-		if(0 > query.indexOf(",")){
-			// if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
-			return _queryFuncCache[query] = _getQueryFunc(query);
-		}else{
-			// if it's a complex query, break it up into it's constituent parts
-			// and return a dispatcher that will merge the parts when run
-
-			// var parts = query.split(", ");
-			var parts = query.split(/\s*,\s*/);
-			var tf = function(root){
-				var pindex = 0; // avoid array alloc for every invocation
-				var ret = [];
-				var tp;
-				while((tp = parts[pindex++])){
-					ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root));
-				}
-				return ret;
-			}
-			// ...cache and return
-			return _queryFuncCache[query] = tf;
-		}
-	}
-
-	// FIXME: 
-	//		Dean's Base2 uses a system whereby queries themselves note if
-	//		they'll need duplicate filtering. We need to get on that plan!!
-
-	// attempt to efficiently determine if an item in a list is a dupe,
-	// returning a list of "uniques", hopefully in doucment order
-	var _zipIdx = 0;
-	var _zip = function(arr){
-		if(arr && arr.nozip){ return d.NodeList._wrap(arr); }
-		var ret = new d.NodeList();
-		if(!arr){ return ret; }
-		if(arr[0]){
-			ret.push(arr[0]);
-		}
-		if(arr.length < 2){ return ret; }
-
-		_zipIdx++;
-		
-		// we have to fork here for IE and XML docs because we can't set
-		// expandos on their nodes (apparently). *sigh*
-		if(d.isIE && caseSensitive){
-			var szidx = _zipIdx+"";
-			arr[0].setAttribute("_zipIdx", szidx);
-			for(var x = 1, te; te = arr[x]; x++){
-				if(arr[x].getAttribute("_zipIdx") != szidx){ 
-					ret.push(te);
-				}
-				te.setAttribute("_zipIdx", szidx);
-			}
-		}else{
-			arr[0]["_zipIdx"] = _zipIdx;
-			for(var x = 1, te; te = arr[x]; x++){
-				if(arr[x]["_zipIdx"] != _zipIdx){ 
-					ret.push(te);
-				}
-				te["_zipIdx"] = _zipIdx;
-			}
-		}
-		// FIXME: should we consider stripping these properties?
-		return ret;
-	}
-
-	// the main executor
-	d.query = function(/*String*/ query, /*String|DOMNode?*/ root){
-		//	summary:
-		//		Returns nodes which match the given CSS3 selector, searching the
-		//		entire document by default but optionally taking a node to scope
-		//		the search by. Returns an instance of dojo.NodeList.
-		//	description:
-		//		dojo.query() is the swiss army knife of DOM node manipulation in
-		//		Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
-		//		"$" function, dojo.query provides robust, high-performance
-		//		CSS-based node selector support with the option of scoping searches
-		//		to a particular sub-tree of a document.
-		//
-		//		Supported Selectors:
-		//		--------------------
-		//
-		//		dojo.query() supports a rich set of CSS3 selectors, including:
-		//
-		//			* class selectors (e.g., `.foo`)
-		//			* node type selectors like `span`
-		//			* ` ` descendant selectors
-		//			* `>` child element selectors 
-		//			* `#foo` style ID selectors
-		//			* `*` universal selector
-		//			* `~`, the immediately preceeded-by sibling selector
-		//			* `+`, the preceeded-by sibling selector
-		//			* attribute queries:
-		//			|	* `[foo]` attribute presence selector
-		//			|	* `[foo='bar']` attribute value exact match
-		//			|	* `[foo~='bar']` attribute value list item match
-		//			|	* `[foo^='bar']` attribute start match
-		//			|	* `[foo$='bar']` attribute end match
-		//			|	* `[foo*='bar']` attribute substring match
-		//			* `:first-child`, `:last-child` positional selectors
-		//			* `:empty` content emtpy selector
-		//			* `:empty` content emtpy selector
-		//			* `:checked` pseudo selector
-		//			* `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
-		//			* `:nth-child(even)`, `:nth-child(odd)` positional selectors
-		//			* `:not(...)` negation pseudo selectors
-		//
-		//		Any legal combination of these selectors will work with
-		//		`dojo.query()`, including compound selectors ("," delimited).
-		//		Very complex and useful searches can be constructed with this
-		//		palette of selectors and when combined with functions for
-		//		maniplation presented by dojo.NodeList, many types of DOM
-		//		manipulation operations become very straightforward.
-		//		
-		//		Unsupported Selectors:
-		//		----------------------
-		//
-		//		While dojo.query handles many CSS3 selectors, some fall outside of
-		//		what's resaonable for a programmatic node querying engine to
-		//		handle. Currently unsupported selectors include:
-		//		
-		//			* namespace-differentiated selectors of any form
-		//			* all `::` pseduo-element selectors
-		//			* certain pseduo-selectors which don't get a lot of day-to-day use:
-		//			|	* `:root`, `:lang()`, `:target`, `:focus`
-		//			* all visual and state selectors:
-		//			|	* `:root`, `:active`, `:hover`, `:visisted`, `:link`,
-		//				  `:enabled`, `:disabled`
-		//			* `:*-of-type` pseudo selectors
-		//		
-		//		dojo.query and XML Documents:
-		//		-----------------------------
-		//		
-		//		`dojo.query` currently only supports searching XML documents
-		//		whose tags and attributes are 100% lower-case. This is a known
-		//		limitation and will [be addressed soon](http://trac.dojotoolkit.org/ticket/3866)
-		//		Non-selector Queries:
-		//		---------------------
-		//
-		//		If something other than a String is passed for the query,
-		//		`dojo.query` will return a new `dojo.NodeList` constructed from
-		//		that parameter alone and all further processing will stop. This
-		//		means that if you have a reference to a node or NodeList, you
-		//		can quickly construct a new NodeList from the original by
-		//		calling `dojo.query(node)` or `dojo.query(list)`.
-		//
-		//	query:
-		//		The CSS3 expression to match against. For details on the syntax of
-		//		CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
-		//	root:
-		//		A DOMNode (or node id) to scope the search from. Optional.
-		//	returns: dojo.NodeList
-		//		An instance of `dojo.NodeList`. Many methods are available on
-		//		NodeLists for searching, iterating, manipulating, and handling
-		//		events on the matched nodes in the returned list.
-		//	example:
-		//		search the entire document for elements with the class "foo":
-		//	|	dojo.query(".foo");
-		//		these elements will match:
-		//	|	<span class="foo"></span>
-		//	|	<span class="foo bar"></span>
-		//	|	<p class="thud foo"></p>
-		//	example:
-		//		search the entire document for elements with the classes "foo" *and* "bar":
-		//	|	dojo.query(".foo.bar");
-		//		these elements will match:
-		//	|	<span class="foo bar"></span>
-		//		while these will not:
-		//	|	<span class="foo"></span>
-		//	|	<p class="thud foo"></p>
-		//	example:
-		//		find `<span>` elements which are descendants of paragraphs and
-		//		which have a "highlighted" class:
-		//	|	dojo.query("p span.highlighted");
-		//		the innermost span in this fragment matches:
-		//	|	<p class="foo">
-		//	|		<span>...
-		//	|			<span class="highlighted foo bar">...</span>
-		//	|		</span>
-		//	|	</p>
-		//	example:
-		//		set an "odd" class on all odd table rows inside of the table
-		//		`#tabular_data`, using the `>` (direct child) selector to avoid
-		//		affecting any nested tables:
-		//	|	dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
-		//	example:
-		//		remove all elements with the class "error" from the document
-		//		and store them in a list:
-		//	|	var errors = dojo.query(".error").orphan();
-		//	example:
-		//		add an onclick handler to every submit button in the document
-		//		which causes the form to be sent via Ajax instead:
-		//	|	dojo.query("input[type='submit']").onclick(function(e){
-		//	|		dojo.stopEvent(e); // prevent sending the form
-		//	|		var btn = e.target;
-		//	|		dojo.xhrPost({
-		//	|			form: btn.form,
-		//	|			load: function(data){
-		//	|				// replace the form with the response
-		//	|				var div = dojo.doc.createElement("div");
-		//	|				dojo.place(div, btn.form, "after");
-		//	|				div.innerHTML = data;
-		//	|				dojo.style(btn.form, "display", "none");
-		//	|			}
-		//	|		});
-		//	|	});
-
-
-		// NOTE: elementsById is not currently supported
-		// NOTE: ignores xpath-ish queries for now
-
-		if(query.constructor == d.NodeList){
-			return query;
-		}
-		if(!d.isString(query)){
-			return new d.NodeList(query); // dojo.NodeList
-		}
-		if(d.isString(root)){
-			root = d.byId(root);
-		}
-
-		root = root||d.doc;
-		var od = root.ownerDocument||root.documentElement;
-		caseSensitive = (root.contentType && root.contentType=="application/xml") || (!!od) && (d.isIE ? od.xml : (root.xmlVersion||od.xmlVersion));
-		return _zip(getQueryFunc(query)(root)); // dojo.NodeList
-	}
-
-	/*
-	// exposing this was a mistake
-	d.query.attrs = attrs;
-	*/
-	// exposing this because new pseudo matches are only executed through the
-	// DOM query path (never through the xpath optimizing branch)
-	d.query.pseudos = pseudos;
-
-	// one-off function for filtering a NodeList based on a simple selector
-	d._filterQueryResult = function(nodeList, simpleFilter){
-		var tnl = new d.NodeList();
-		var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; };
-		for(var x = 0, te; te = nodeList[x]; x++){
-			if(ff(te)){ tnl.push(te); }
-		}
-		return tnl;
-	}
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.xhr"] = true;
-dojo.provide("dojo._base.xhr");
-
-
-
-
-
-(function(){
-	var _d = dojo;
-	function setValue(/*Object*/obj, /*String*/name, /*String*/value){
-		//summary:
-		//		For the named property in object, set the value. If a value
-		//		already exists and it is a string, convert the value to be an
-		//		array of values.
-		var val = obj[name];
-		if(_d.isString(val)){
-			obj[name] = [val, value];
-		}else if(_d.isArray(val)){
-			val.push(value);
-		}else{
-			obj[name] = value;
-		}
-	}
-
-	dojo.formToObject = function(/*DOMNode||String*/ formNode){
-		// summary:
-		//		dojo.formToObject returns the values encoded in an HTML form as
-		//		string properties in an object which it then returns. Disabled form
-		//		elements, buttons, and other non-value form elements are skipped.
-		//		Multi-select elements are returned as an array of string values.
-		// description:
-		//		This form:
-		//
-		//		|	<form id="test_form">
-		//		|		<input type="text" name="blah" value="blah">
-		//		|		<input type="text" name="no_value" value="blah" disabled>
-		//		|		<input type="button" name="no_value2" value="blah">
-		//		|		<select type="select" multiple name="multi" size="5">
-		//		|			<option value="blah">blah</option>
-		//		|			<option value="thud" selected>thud</option>
-		//		|			<option value="thonk" selected>thonk</option>
-		//		|		</select>
-		//		|	</form>
-		//
-		//		yields this object structure as the result of a call to
-		//		formToObject():
-		//
-		//		|	{ 
-		//		|		blah: "blah",
-		//		|		multi: [
-		//		|			"thud",
-		//		|			"thonk"
-		//		|		]
-		//		|	};
-
-		var ret = {};
-		var exclude = "file|submit|image|reset|button|";
-		_d.forEach(dojo.byId(formNode).elements, function(item){
-			var _in = item.name;
-			var type = (item.type||"").toLowerCase();
-			if(_in && type && exclude.indexOf(type) == -1 && !item.disabled){
-				if(type == "radio" || type == "checkbox"){
-					if(item.checked){ setValue(ret, _in, item.value); }
-				}else if(item.multiple){
-					ret[_in] = [];
-					_d.query("option", item).forEach(function(opt){
-						if(opt.selected){
-							setValue(ret, _in, opt.value);
-						}
-					});
-				}else{ 
-					setValue(ret, _in, item.value);
-					if(type == "image"){
-						ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
-					}
-				}
-			}
-		});
-		return ret; // Object
-	}
-
-	dojo.objectToQuery = function(/*Object*/ map){
-		//	summary:
-		//		takes a name/value mapping object and returns a string representing
-		//		a URL-encoded version of that object.
-		//	example:
-		//		this object:
-		//
-		//		|	{ 
-		//		|		blah: "blah",
-		//		|		multi: [
-		//		|			"thud",
-		//		|			"thonk"
-		//		|		]
-		//		|	};
-		//
-		//	yields the following query string:
-		//	
-		//	|	"blah=blah&multi=thud&multi=thonk"
-
-		// FIXME: need to implement encodeAscii!!
-		var enc = encodeURIComponent;
-		var pairs = [];
-		var backstop = {};
-		for(var name in map){
-			var value = map[name];
-			if(value != backstop[name]){
-				var assign = enc(name) + "=";
-				if(_d.isArray(value)){
-					for(var i=0; i < value.length; i++){
-						pairs.push(assign + enc(value[i]));
-					}
-				}else{
-					pairs.push(assign + enc(value));
-				}
-			}
-		}
-		return pairs.join("&"); // String
-	}
-
-	dojo.formToQuery = function(/*DOMNode||String*/ formNode){
-		// summary:
-		//		Returns a URL-encoded string representing the form passed as either a
-		//		node or string ID identifying the form to serialize
-		return _d.objectToQuery(_d.formToObject(formNode)); // String
-	}
-
-	dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){
-		// summary:
-		//		return a serialized JSON string from a form node or string
-		//		ID identifying the form to serialize
-		return _d.toJson(_d.formToObject(formNode), prettyPrint); // String
-	}
-
-	dojo.queryToObject = function(/*String*/ str){
-		// summary:
-		//		returns an object representing a de-serialized query section of a
-		//		URL. Query keys with multiple values are returned in an array.
-		// description:
-		//		This string:
-		//
-		//	|		"foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
-		//		
-		//		results in this object structure:
-		//
-		//	|		{
-		//	|			foo: [ "bar", "baz" ],
-		//	|			thinger: " spaces =blah",
-		//	|			zonk: "blarg"
-		//	|		}
-		//	
-		//		Note that spaces and other urlencoded entities are correctly
-		//		handled.
-
-		// FIXME: should we grab the URL string if we're not passed one?
-		var ret = {};
-		var qp = str.split("&");
-		var dec = decodeURIComponent;
-		_d.forEach(qp, function(item){
-			if(item.length){
-				var parts = item.split("=");
-				var name = dec(parts.shift());
-				var val = dec(parts.join("="));
-				if(_d.isString(ret[name])){
-					ret[name] = [ret[name]];
-				}
-				if(_d.isArray(ret[name])){
-					ret[name].push(val);
-				}else{
-					ret[name] = val;
-				}
-			}
-		});
-		return ret; // Object
-	}
-
-	/*
-		from refactor.txt:
-
-		all bind() replacement APIs take the following argument structure:
-
-			{
-				url: "blah.html",
-
-				// all below are optional, but must be supported in some form by
-				// every IO API
-				timeout: 1000, // milliseconds
-				handleAs: "text", // replaces the always-wrong "mimetype"
-				content: { 
-					key: "value"
-				},
-
-				// browser-specific, MAY be unsupported
-				sync: true, // defaults to false
-				form: dojo.byId("someForm") 
-			}
-	*/
-
-	// need to block async callbacks from snatching this thread as the result
-	// of an async callback might call another sync XHR, this hangs khtml forever
-	// must checked by watchInFlight()
-
-	dojo._blockAsync = false;
-
-	dojo._contentHandlers = {
-		"text": function(xhr){ return xhr.responseText; },
-		"json": function(xhr){
-			return _d.fromJson(xhr.responseText || null);
-		},
-		"json-comment-filtered": function(xhr){ 
-			// NOTE: the json-comment-filtered option was implemented to prevent
-			// "JavaScript Hijacking", but it is less secure than standard JSON. Use
-			// standard JSON instead. JSON prefixing can be used to subvert hijacking.
-			if(!dojo.config.useCommentedJson){
-				console.warn("Consider using the standard mimetype:application/json."
-					+ " json-commenting can introduce security issues. To"
-					+ " decrease the chances of hijacking, use the standard the 'json' handler and"
-					+ " prefix your json with: {}&&\n"
-					+ "Use djConfig.useCommentedJson=true to turn off this message.");
-			}
-
-			var value = xhr.responseText;
-			var cStartIdx = value.indexOf("\/*");
-			var cEndIdx = value.lastIndexOf("*\/");
-			if(cStartIdx == -1 || cEndIdx == -1){
-				throw new Error("JSON was not comment filtered");
-			}
-			return _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
-		},
-		"javascript": function(xhr){ 
-			// FIXME: try Moz and IE specific eval variants?
-			return _d.eval(xhr.responseText);
-		},
-		"xml": function(xhr){ 
-			var result = xhr.responseXML;
-			if(_d.isIE && (!result || result.documentElement == null)){
-				_d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){
-					try{
-						var dom = new ActiveXObject(prefix + ".XMLDOM");
-						dom.async = false;
-						dom.loadXML(xhr.responseText);
-						result = dom;
-					}catch(e){ /* Not available. Squelch and try next one. */ }
-				});
-			}
-			return result; // DOMDocument
-		}
-	};
-
-	dojo._contentHandlers["json-comment-optional"] = function(xhr){
-		var handlers = _d._contentHandlers;
-		if(xhr.responseText && xhr.responseText.indexOf("\/*") != -1){
-			return handlers["json-comment-filtered"](xhr);
-		}else{
-			return handlers["json"](xhr);
-		}
-	};
-
-	/*=====
-	dojo.__IoArgs = function(){
-		//	url: String
-		//		URL to server endpoint.
-		//	content: Object?
-		//		Contains properties with string values. These
-		//		properties will be serialized as name1=value2 and
-		//		passed in the request.
-		//	timeout: Integer?
-		//		Milliseconds to wait for the response. If this time
-		//		passes, the then error callbacks are called.
-		//	form: DOMNode?
-		//		DOM node for a form. Used to extract the form values
-		//		and send to the server.
-		//	preventCache: Boolean?
-		//		Default is false. If true, then a
-		//		"dojo.preventCache" parameter is sent in the request
-		//		with a value that changes with each request
-		//		(timestamp). Useful only with GET-type requests.
-		//	handleAs: String?
-		//		Acceptable values depend on the type of IO
-		//		transport (see specific IO calls for more information).
-		//	load: Function?
-		//		function(response, ioArgs){} response is of type Object, ioArgs
-		//		is of type dojo.__IoCallbackArgs.  This function will be
-		//		called on a successful HTTP response code.
-		//	error: Function?
-		//		function(response, ioArgs){} response is of type Object, ioArgs
-		//		is of type dojo.__IoCallbackArgs. This function will
-		//		be called when the request fails due to a network or server error, the url
-		//		is invalid, etc. It will also be called if the load or handle callback throws an
-		//		exception, unless djConfig.isDebug is true.  This allows deployed applications
-		//		to continue to run even when a logic error happens in the callback, while making
-		//		it easier to troubleshoot while in debug mode.
-		//	handle: Function?
-		//		function(response, ioArgs){} response is of type Object, ioArgs
-		//		is of type dojo.__IoCallbackArgs.  This function will
-		//		be called at the end of every request, whether or not an error occurs.
-		this.url = url;
-		this.content = content;
-		this.timeout = timeout;
-		this.form = form;
-		this.preventCache = preventCache;
-		this.handleAs = handleAs;
-		this.load = load;
-		this.error = error;
-		this.handle = handle;
-	}
-	=====*/
-
-	/*=====
-	dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){
-		//	args: Object
-		//		the original object argument to the IO call.
-		//	xhr: XMLHttpRequest
-		//		For XMLHttpRequest calls only, the
-		//		XMLHttpRequest object that was used for the
-		//		request.
-		//	url: String
-		//		The final URL used for the call. Many times it
-		//		will be different than the original args.url
-		//		value.
-		//	query: String
-		//		For non-GET requests, the
-		//		name1=value1&name2=value2 parameters sent up in
-		//		the request.
-		//	handleAs: String
-		//		The final indicator on how the response will be
-		//		handled.
-		//	id: String
-		//		For dojo.io.script calls only, the internal
-		//		script ID used for the request.
-		//	canDelete: Boolean
-		//		For dojo.io.script calls only, indicates
-		//		whether the script tag that represents the
-		//		request can be deleted after callbacks have
-		//		been called. Used internally to know when
-		//		cleanup can happen on JSONP-type requests.
-		//	json: Object
-		//		For dojo.io.script calls only: holds the JSON
-		//		response for JSONP-type requests. Used
-		//		internally to hold on to the JSON responses.
-		//		You should not need to access it directly --
-		//		the same object should be passed to the success
-		//		callbacks directly.
-		this.args = args;
-		this.xhr = xhr;
-		this.url = url;
-		this.query = query;
-		this.handleAs = handleAs;
-		this.id = id;
-		this.canDelete = canDelete;
-		this.json = json;
-	}
-	=====*/
-
-
-
-	dojo._ioSetArgs = function(/*dojo.__IoArgs*/args,
-			/*Function*/canceller,
-			/*Function*/okHandler,
-			/*Function*/errHandler){
-		//	summary: 
-		//		sets up the Deferred and ioArgs property on the Deferred so it
-		//		can be used in an io call.
-		//	args:
-		//		The args object passed into the public io call. Recognized properties on
-		//		the args object are:
-		//	canceller:
-		//		The canceller function used for the Deferred object. The function
-		//		will receive one argument, the Deferred object that is related to the
-		//		canceller.
-		//	okHandler:
-		//		The first OK callback to be registered with Deferred. It has the opportunity
-		//		to transform the OK response. It will receive one argument -- the Deferred
-		//		object returned from this function.
-		//	errHandler:
-		//		The first error callback to be registered with Deferred. It has the opportunity
-		//		to do cleanup on an error. It will receive two arguments: error (the 
-		//		Error object) and dfd, the Deferred object returned from this function.
-
-		var ioArgs = {args: args, url: args.url};
-
-		//Get values from form if requestd.
-		var formObject = null;
-		if(args.form){ 
-			var form = _d.byId(args.form);
-			//IE requires going through getAttributeNode instead of just getAttribute in some form cases, 
-			//so use it for all.  See #2844
-			var actnNode = form.getAttributeNode("action");
-			ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); 
-			formObject = _d.formToObject(form);
-		}
-
-		// set up the query params
-		var miArgs = [{}];
-	
-		if(formObject){
-			// potentially over-ride url-provided params w/ form values
-			miArgs.push(formObject);
-		}
-		if(args.content){
-			// stuff in content over-rides what's set by form
-			miArgs.push(args.content);
-		}
-		if(args.preventCache){
-			miArgs.push({"dojo.preventCache": new Date().valueOf()});
-		}
-		ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));
-	
-		// .. and the real work of getting the deferred in order, etc.
-		ioArgs.handleAs = args.handleAs || "text";
-		var d = new _d.Deferred(canceller);
-		d.addCallbacks(okHandler, function(error){
-			return errHandler(error, d);
-		});
-
-		//Support specifying load, error and handle callback functions from the args.
-		//For those callbacks, the "this" object will be the args object.
-		//The callbacks will get the deferred result value as the
-		//first argument and the ioArgs object as the second argument.
-		var ld = args.load;
-		if(ld && _d.isFunction(ld)){
-			d.addCallback(function(value){
-				return ld.call(args, value, ioArgs);
-			});
-		}
-		var err = args.error;
-		if(err && _d.isFunction(err)){
-			d.addErrback(function(value){
-				return err.call(args, value, ioArgs);
-			});
-		}
-		var handle = args.handle;
-		if(handle && _d.isFunction(handle)){
-			d.addBoth(function(value){
-				return handle.call(args, value, ioArgs);
-			});
-		}
-		
-		d.ioArgs = ioArgs;
-	
-		// FIXME: need to wire up the xhr object's abort method to something
-		// analagous in the Deferred
-		return d;
-	}
-
-	var _deferredCancel = function(/*Deferred*/dfd){
-		//summary: canceller function for dojo._ioSetArgs call.
-		
-		dfd.canceled = true;
-		var xhr = dfd.ioArgs.xhr;
-		var _at = typeof xhr.abort;
-		if(_at == "function" || _at == "object" || _at == "unknown"){
-			xhr.abort();
-		}
-		var err = dfd.ioArgs.error;
-		if(!err){
-			err = new Error("xhr cancelled");
-			err.dojoType="cancel";
-		}
-		return err;
-	}
-	var _deferredOk = function(/*Deferred*/dfd){
-		//summary: okHandler function for dojo._ioSetArgs call.
-
-		var ret = _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
-		return (typeof ret == "undefined") ? null : ret;
-	}
-	var _deferError = function(/*Error*/error, /*Deferred*/dfd){
-		//summary: errHandler function for dojo._ioSetArgs call.
-
-		
-		return error;
-	}
-
-	// avoid setting a timer per request. It degrades performance on IE
-	// something fierece if we don't use unified loops.
-	var _inFlightIntvl = null;
-	var _inFlight = [];
-	var _watchInFlight = function(){
-		//summary: 
-		//		internal method that checks each inflight XMLHttpRequest to see
-		//		if it has completed or if the timeout situation applies.
-		
-		var now = (new Date()).getTime();
-		// make sure sync calls stay thread safe, if this callback is called
-		// during a sync call and this results in another sync call before the
-		// first sync call ends the browser hangs
-		if(!_d._blockAsync){
-			// we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
-			// note: the second clause is an assigment on purpose, lint may complain
-			for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){
-				var dfd = tif.dfd;
-				var func = function(){
-					if(!dfd || dfd.canceled || !tif.validCheck(dfd)){
-						_inFlight.splice(i--, 1); 
-					}else if(tif.ioCheck(dfd)){
-						_inFlight.splice(i--, 1);
-						tif.resHandle(dfd);
-					}else if(dfd.startTime){
-						//did we timeout?
-						if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){
-							_inFlight.splice(i--, 1);
-							var err = new Error("timeout exceeded");
-							err.dojoType = "timeout";
-							dfd.errback(err);
-							//Cancel the request so the io module can do appropriate cleanup.
-							dfd.cancel();
-						}
-					}
-				};
-				if(dojo.config.isDebug){
-					func.call(this);
-				}else{
-					try{
-						func.call(this);
-					}catch(e){
-						dfd.errback(e);
-					}
-				}
-			}
-		}
-
-		if(!_inFlight.length){
-			clearInterval(_inFlightIntvl);
-			_inFlightIntvl = null;
-			return;
-		}
-
-	}
-
-	dojo._ioCancelAll = function(){
-		//summary: Cancels all pending IO requests, regardless of IO type
-		//(xhr, script, iframe).
-		try{
-			_d.forEach(_inFlight, function(i){
-				try{
-					i.dfd.cancel();
-				}catch(e){/*squelch*/}
-			});
-		}catch(e){/*squelch*/}
-	}
-
-	//Automatically call cancel all io calls on unload
-	//in IE for trac issue #2357.
-	if(_d.isIE){
-		_d.addOnWindowUnload(_d._ioCancelAll);
-	}
-
-	_d._ioWatch = function(/*Deferred*/dfd,
-		/*Function*/validCheck,
-		/*Function*/ioCheck,
-		/*Function*/resHandle){
-		//summary: watches the io request represented by dfd to see if it completes.
-		//dfd:
-		//		The Deferred object to watch.
-		//validCheck:
-		//		Function used to check if the IO request is still valid. Gets the dfd
-		//		object as its only argument.
-		//ioCheck:
-		//		Function used to check if basic IO call worked. Gets the dfd
-		//		object as its only argument.
-		//resHandle:
-		//		Function used to process response. Gets the dfd
-		//		object as its only argument.
-		if(dfd.ioArgs.args.timeout){
-			dfd.startTime = (new Date()).getTime();
-		}
-		_inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
-		if(!_inFlightIntvl){
-			_inFlightIntvl = setInterval(_watchInFlight, 50);
-		}
-		_watchInFlight(); // handle sync requests
-	}
-
-	var _defaultContentType = "application/x-www-form-urlencoded";
-
-	var _validCheck = function(/*Deferred*/dfd){
-		return dfd.ioArgs.xhr.readyState; //boolean
-	}
-	var _ioCheck = function(/*Deferred*/dfd){
-		return 4 == dfd.ioArgs.xhr.readyState; //boolean
-	}
-	var _resHandle = function(/*Deferred*/dfd){
-		var xhr = dfd.ioArgs.xhr;
-		if(_d._isDocumentOk(xhr)){
-			dfd.callback(dfd);
-		}else{
-			var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
-			err.status = xhr.status;
-			err.responseText = xhr.responseText;
-			dfd.errback(err);
-		}
-	}
-
-	dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
-		//summary: Adds query params discovered by the io deferred construction to the URL.
-		//Only use this for operations which are fundamentally GET-type operations.
-		if(ioArgs.query.length){
-			ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
-			ioArgs.query = null;
-		}		
-	}
-
-	/*=====
-	dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
-		constructor: function(){
-			//	summary:
-			//		In addition to the properties listed for the dojo._IoArgs type,
-			//		the following properties are allowed for dojo.xhr* methods.
-			//	handleAs: String?
-			//		Acceptable values are: text (default), json, json-comment-optional,
-			//		json-comment-filtered, javascript, xml
-			//	sync: Boolean?
-			//		false is default. Indicates whether the request should
-			//		be a synchronous (blocking) request.
-			//	headers: Object?
-			//		Additional HTTP headers to send in the request.
-			this.handleAs = handleAs;
-			this.sync = sync;
-			this.headers = headers;
-		}
-	});
-	=====*/
-
-	dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
-		//	summary:
-		//		Sends an HTTP request with the given method.
-		//	description:
-		//		Sends an HTTP request with the given method.
-		//		See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
-		//		for those HTTP methods. There are also methods for "raw" PUT and POST methods
-		//		via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
-		//	method:
-		//		HTTP method to be used, such as GET, POST, PUT, DELETE.  Should be uppercase.
-		//	hasBody:
-		//		If the request has an HTTP body, then pass true for hasBody.
-
-		//Make the Deferred object for this xhr request.
-		var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);
-
-		//Pass the args to _xhrObj, to allow xhr iframe proxy interceptions.
-		dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args);
-
-		if(hasBody){
-			if("postData" in args){
-				dfd.ioArgs.query = args.postData;
-			}else if("putData" in args){
-				dfd.ioArgs.query = args.putData;
-			}
-		}else{
-			_d._ioAddQueryToUrl(dfd.ioArgs);
-		}
-
-		// IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).
-		// workaround for IE6's apply() "issues"
-		var ioArgs = dfd.ioArgs;
-		var xhr = ioArgs.xhr;
-		xhr.open(method, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
-		if(args.headers){
-			for(var hdr in args.headers){
-				if(hdr.toLowerCase() === "content-type" && !args.contentType){
-					args.contentType = args.headers[hdr];
-				}else{
-					xhr.setRequestHeader(hdr, args.headers[hdr]);
-				}
-			}
-		}
-		// FIXME: is this appropriate for all content types?
-		xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
-		if(!args.headers || !args.headers["X-Requested-With"]){
-			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-		}
-		// FIXME: set other headers here!
-		if(dojo.config.isDebug){
-			xhr.send(ioArgs.query);
-		}else{
-			try{
-				xhr.send(ioArgs.query);
-			}catch(e){
-				dfd.ioArgs.error = e;
-				dfd.cancel();
-			}
-		}
-		_d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);
-		xhr = null;
-		return dfd; // dojo.Deferred
-	}
-
-	dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
-		//	summary: 
-		//		Sends an HTTP GET request to the server.
-		return _d.xhr("GET", args); // dojo.Deferred
-	}
-
-	dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
-		//	summary:
-		//		Sends an HTTP POST request to the server. In addtion to the properties
-		//		listed for the dojo.__XhrArgs type, the following property is allowed:
-		//	postData:
-		//		String. Send raw data in the body of the POST request.
-		return _d.xhr("POST", args, true); // dojo.Deferred
-	}
-
-	dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
-		//	summary:
-		//		Sends an HTTP PUT request to the server. In addtion to the properties
-		//		listed for the dojo.__XhrArgs type, the following property is allowed:
-		//	putData:
-		//		String. Send raw data in the body of the PUT request.
-		return _d.xhr("PUT", args, true); // dojo.Deferred
-	}
-
-	dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
-		//	summary:
-		//		Sends an HTTP DELETE request to the server.
-		return _d.xhr("DELETE", args); //dojo.Deferred
-	}
-
-	/*
-	dojo.wrapForm = function(formNode){
-		//summary:
-		//		A replacement for FormBind, but not implemented yet.
-
-		// FIXME: need to think harder about what extensions to this we might
-		// want. What should we allow folks to do w/ this? What events to
-		// set/send?
-		throw new Error("dojo.wrapForm not yet implemented");
-	}
-	*/
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.fx"] = true;
-dojo.provide("dojo._base.fx");
-
-
-
-
-
-
-/*
-	Animation losely package based on Dan Pupius' work, contributed under CLA: 
-		http://pupius.co.uk/js/Toolkit.Drawing.js
-*/
-(function(){ 
-
-	var d = dojo;
-	
-	dojo._Line = function(/*int*/ start, /*int*/ end){
-		//	summary:
-		//		dojo._Line is the object used to generate values from a start value
-		//		to an end value
-		//	start: int
-		//		Beginning value for range
-		//	end: int
-		//		Ending value for range
-		this.start = start;
-		this.end = end;
-		this.getValue = function(/*float*/ n){
-			//	summary: returns the point on the line
-			//	n: a floating point number greater than 0 and less than 1
-			return ((this.end - this.start) * n) + this.start; // Decimal
-		}
-	}
-	
-	d.declare("dojo._Animation", null, {
-		//	summary
-		//		A generic animation class that fires callbacks into its handlers
-		//		object at various states. Nearly all dojo animation functions
-		//		return an instance of this method, usually without calling the
-		//		.play() method beforehand. Therefore, you will likely need to
-		//		call .play() on instances of dojo._Animation when one is
-		//		returned.
-		constructor: function(/*Object*/ args){
-			d.mixin(this, args);
-			if(d.isArray(this.curve)){
-				/* curve: Array
-					pId: a */
-				this.curve = new d._Line(this.curve[0], this.curve[1]);
-			}
-		},
-		
-		// duration: Integer
-		//	The time in milliseonds the animation will take to run
-		duration: 350,
-	
-	/*=====
-		// curve: dojo._Line||Array
-		//	A two element array of start and end values, or a dojo._Line instance to be
-		//	used in the Animation. 
-		curve: null,
-	
-		// easing: Function
-		//	A Function to adjust the acceleration (or deceleration) of the progress 
-		//	across a dojo._Line
-		easing: null,
-	=====*/
-	
-		// repeat: Integer
-		//	The number of times to loop the animation
-		repeat: 0,
-	
-		// rate: Integer
-		//	the time in milliseconds to wait before advancing to next frame 
-		//	(used as a fps timer: rate/1000 = fps)
-		rate: 10 /* 100 fps */,
-	
-	/*===== 
-		// delay: Integer
-		// 	The time in milliseconds to wait before starting animation after it has been .play()'ed
-		delay: null,
-	
-		// events
-		//
-		// beforeBegin: Event
-		//	Synthetic event fired before a dojo._Animation begins playing (synchronous)
-		beforeBegin: null,
-	
-		// onBegin: Event
-		//	Synthetic event fired as a dojo._Animation begins playing (useful?)
-		onBegin: null,
-	
-		// onAnimate: Event
-		//	Synthetic event fired at each interval of a dojo._Animation
-		onAnimate: null,
-	
-		// onEnd: Event
-		//	Synthetic event fired after the final frame of a dojo._Animation
-		onEnd: null,
-	
-		// onPlay: Event
-		//	Synthetic event fired any time a dojo._Animation is play()'ed
-		onPlay: null,
-	
-		// onPause: Event
-		//	Synthetic event fired when a dojo._Animation is paused
-		onPause: null,
-	
-		// onStop: Event
-		//	Synthetic event fires when a dojo._Animation is stopped
-		onStop: null,
-	
-	=====*/
-	
-		_percent: 0,
-		_startRepeatCount: 0,
-	
-		_fire: function(/*Event*/ evt, /*Array?*/ args){
-			//	summary:
-			//		Convenience function.  Fire event "evt" and pass it the
-			//		arguments specified in "args".
-			//	evt:
-			//		The event to fire.
-			//	args:
-			//		The arguments to pass to the event.
-			if(this[evt]){
-				if(dojo.config.isDebug){
-					this[evt].apply(this, args||[]);
-				}else{
-					try{
-						this[evt].apply(this, args||[]);
-					}catch(e){
-						// squelch and log because we shouldn't allow exceptions in
-						// synthetic event handlers to cause the internal timer to run
-						// amuck, potentially pegging the CPU. I'm not a fan of this
-						// squelch, but hopefully logging will make it clear what's
-						// going on
-						console.error("exception in animation handler for:", evt);
-						console.error(e);
-					}
-				}
-			}
-			return this; // dojo._Animation
-		},
-
-		play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
-			// summary:
-			//		Start the animation.
-			// delay:
-			//		How many milliseconds to delay before starting.
-			// gotoStart:
-			//		If true, starts the animation from the beginning; otherwise,
-			//		starts it from its current position.
-			var _t = this;
-			if(gotoStart){
-				_t._stopTimer();
-				_t._active = _t._paused = false;
-				_t._percent = 0;
-			}else if(_t._active && !_t._paused){
-				return _t; // dojo._Animation
-			}
-	
-			_t._fire("beforeBegin");
-	
-			var de = delay||_t.delay;
-			var _p = dojo.hitch(_t, "_play", gotoStart);
-			if(de > 0){
-				setTimeout(_p, de);
-				return _t; // dojo._Animation
-			}
-			_p();
-			return _t;
-		},
-	
-		_play: function(gotoStart){
-			var _t = this;
-			_t._startTime = new Date().valueOf();
-			if(_t._paused){
-				_t._startTime -= _t.duration * _t._percent;
-			}
-			_t._endTime = _t._startTime + _t.duration;
-	
-			_t._active = true;
-			_t._paused = false;
-	
-			var value = _t.curve.getValue(_t._percent);
-			if(!_t._percent){
-				if(!_t._startRepeatCount){
-					_t._startRepeatCount = _t.repeat;
-				}
-				_t._fire("onBegin", [value]);
-			}
-	
-			_t._fire("onPlay", [value]);
-	
-			_t._cycle();
-			return _t; // dojo._Animation
-		},
-	
-		pause: function(){
-			// summary: Pauses a running animation.
-			this._stopTimer();
-			if(!this._active){ return this; /*dojo._Animation*/ }
-			this._paused = true;
-			this._fire("onPause", [this.curve.getValue(this._percent)]);
-			return this; // dojo._Animation
-		},
-	
-		gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
-			//	summary:
-			//		Sets the progress of the animation.
-			//	percent:
-			//		A percentage in decimal notation (between and including 0.0 and 1.0).
-			//	andPlay:
-			//		If true, play the animation after setting the progress.
-			this._stopTimer();
-			this._active = this._paused = true;
-			this._percent = percent;
-			if(andPlay){ this.play(); }
-			return this; // dojo._Animation
-		},
-	
-		stop: function(/*boolean?*/ gotoEnd){
-			// summary: Stops a running animation.
-			// gotoEnd: If true, the animation will end.
-			if(!this._timer){ return this; /* dojo._Animation */ }
-			this._stopTimer();
-			if(gotoEnd){
-				this._percent = 1;
-			}
-			this._fire("onStop", [this.curve.getValue(this._percent)]);
-			this._active = this._paused = false;
-			return this; // dojo._Animation
-		},
-	
-		status: function(){
-			// summary: Returns a string token representation of the status of
-			//			the animation, one of: "paused", "playing", "stopped"
-			if(this._active){
-				return this._paused ? "paused" : "playing"; // String
-			}
-			return "stopped"; // String
-		},
-	
-		_cycle: function(){
-			var _t = this;
-			if(_t._active){
-				var curr = new Date().valueOf();
-				var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);
-	
-				if(step >= 1){
-					step = 1;
-				}
-				_t._percent = step;
-	
-				// Perform easing
-				if(_t.easing){
-					step = _t.easing(step);
-				}
-	
-				_t._fire("onAnimate", [_t.curve.getValue(step)]);
-	
-				if(_t._percent < 1){
-					_t._startTimer();
-				}else{
-					_t._active = false;
-	
-					if(_t.repeat > 0){
-						_t.repeat--;
-						_t.play(null, true);
-					}else if(_t.repeat == -1){
-						_t.play(null, true);
-					}else{
-						if(_t._startRepeatCount){
-							_t.repeat = _t._startRepeatCount;
-							_t._startRepeatCount = 0;
-						}
-					}
-					_t._percent = 0;
-					_t._fire("onEnd");
-					_t._stopTimer();
-				}
-			}
-			return _t; // dojo._Animation
-		}
-	});
-
-	var ctr = 0;
-	var _globalTimerList = [];
-	var runner = {
-		run: function(){ }
-	};
-	var timer = null;
-	dojo._Animation.prototype._startTimer = function(){
-		// this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);
-		if(!this._timer){
-			this._timer = d.connect(runner, "run", this, "_cycle");
-			ctr++;
-		}
-		if(!timer){
-			timer = setInterval(d.hitch(runner, "run"), this.rate);
-		}
-	};
-
-	dojo._Animation.prototype._stopTimer = function(){
-		if(this._timer){
-			d.disconnect(this._timer);
-			this._timer = null;
-			ctr--;
-		}
-		if(ctr <= 0){
-			clearInterval(timer);
-			timer = null;
-			ctr = 0;
-		}
-	};
-
-	var _makeFadeable = (d.isIE) ? function(node){
-		// only set the zoom if the "tickle" value would be the same as the
-		// default
-		var ns = node.style;
-		// don't set the width to auto if it didn't already cascade that way.
-		// We don't want to f anyones designs
-		if(!ns.width.length && d.style(node, "width") == "auto"){
-			ns.width = "auto";
-		}
-	} : function(){};
-
-	dojo._fade = function(/*Object*/ args){
-		//	summary: 
-		//		Returns an animation that will fade the node defined by
-		//		args.node from the start to end values passed (args.start
-		//		args.end) (end is mandatory, start is optional)
-
-		args.node = d.byId(args.node);
-		var fArgs = d.mixin({ properties: {} }, args);
-		var props = (fArgs.properties.opacity = {});
-		props.start = !("start" in fArgs) ?
-			function(){ 
-				return Number(d.style(fArgs.node, "opacity")); 
-			} : fArgs.start;
-		props.end = fArgs.end;
-
-		var anim = d.animateProperty(fArgs);
-		d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));
-
-		return anim; // dojo._Animation
-	}
-
-	/*=====
-	dojo.__FadeArgs = function(node, duration, easing){
-		// 	node: DOMNode|String
-		//		The node referenced in the animation
-		//	duration: Integer?
-		//		Duration of the animation in milliseconds.
-		//	easing: Function?
-		//		An easing function.
-		this.node = node;
-		this.duration = duration;
-		this.easing = easing;
-	}
-	=====*/
-
-	dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){
-		// summary: 
-		//		Returns an animation that will fade node defined in 'args' from
-		//		its current opacity to fully opaque.
-		return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation
-	}
-
-	dojo.fadeOut = function(/*dojo.__FadeArgs*/  args){
-		// summary: 
-		//		Returns an animation that will fade node defined in 'args'
-		//		from its current opacity to fully transparent.
-		return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation
-	}
-
-	dojo._defaultEasing = function(/*Decimal?*/ n){
-		// summary: The default easing function for dojo._Animation(s)
-		return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);
-	}
-
-	var PropLine = function(properties){
-		// PropLine is an internal class which is used to model the values of
-		// an a group of CSS properties across an animation lifecycle. In
-		// particular, the "getValue" function handles getting interpolated
-		// values between start and end for a particular CSS value.
-		this._properties = properties;
-		for(var p in properties){
-			var prop = properties[p];
-			if(prop.start instanceof d.Color){
-				// create a reusable temp color object to keep intermediate results
-				prop.tempColor = new d.Color();
-			}
-		}
-		this.getValue = function(r){
-			var ret = {};
-			for(var p in this._properties){
-				var prop = this._properties[p];
-				var start = prop.start;
-				if(start instanceof d.Color){
-					ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();
-				}else if(!d.isArray(start)){
-					ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");
-				}
-			}
-			return ret;
-		}
-	}
-
-	/*=====
-	dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], {
-		// Properties: Object?
-		//	A hash map of style properties to Objects describing the transition,
-		//	such as the properties of dojo._Line with an additional 'unit' property
-		properties: {}
-		
-		//TODOC: add event callbacks
-	});
-	=====*/
-
-	dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){
-		//	summary: 
-		//		Returns an animation that will transition the properties of
-		//		node defined in 'args' depending how they are defined in
-		//		'args.properties'
-		//
-		// description:
-		//		dojo.animateProperty is the foundation of most dojo.fx
-		//		animations. It takes an object of "properties" corresponding to
-		//		style properties, and animates them in parallel over a set
-		//		duration.
-		//	
-		// 	example:
-		//		A simple animation that changes the width of the specified node.
-		//	|	dojo.animateProperty({ 
-		//	|		node: "nodeId",
-		//	|		properties: { width: 400 },
-		//	|	}).play();
-		//		Dojo figures out the start value for the width and converts the
-		//		integer specified for the width to the more expressive but
-		//		verbose form `{ width: { end: '400', units: 'px' } }` which you
-		//		can also specify directly
-		//
-		// 	example:
-		//		Animate width, height, and padding over 2 seconds... the
-		//		pedantic way:
-		//	|	dojo.animateProperty({ node: node, duration:2000,
-		//	|		properties: {
-		//	|			width: { start: '200', end: '400', unit:"px" },
-		//	|			height: { start:'200', end: '400', unit:"px" },
-		//	|			paddingTop: { start:'5', end:'50', unit:"px" } 
-		//	|		}
-		//	|	}).play();
-		//		Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties
-		//		are written using "mixed case", as the hyphen is illegal as an object key.
-		//		
-		// 	example:
-		//		Plug in a different easing function and register a callback for
-		//		when the animation ends. Easing functions accept values between
-		//		zero and one and return a value on that basis. In this case, an
-		//		exponential-in curve.
-		//	|	dojo.animateProperty({ 
-		//	|		node: "nodeId",
-		//	|		// dojo figures out the start value
-		//	|		properties: { width: { end: 400 } },
-		//	|		easing: function(n){
-		//	|			return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
-		//	|		},
-		//	|		onEnd: function(){
-		//	|			// called when the animation finishes
-		//	|		}
-		//	|	}).play(500); // delay playing half a second
-		//
-		//	example:
-		//		Like all `dojo._Animation`s, animateProperty returns a handle to the
-		//		Animation instance, which fires the events common to Dojo FX. Use `dojo.connect`
-		//		to access these events outside of the Animation definiton:
-		//	|	var anim = dojo.animateProperty({
-		//	|		node:"someId",
-		//	|		properties:{
-		//	|			width:400, height:500
-		//	|		}
-		//	|	});
-		//	|	dojo.connect(anim,"onEnd", function(){
-		//	|		
-		//	|	});
-		//	|	// play the animation now:
-		//	|	anim.play();
-		
-		args.node = d.byId(args.node);
-		if(!args.easing){ args.easing = d._defaultEasing; }
-
-		var anim = new d._Animation(args);
-		d.connect(anim, "beforeBegin", anim, function(){
-			var pm = {};
-			for(var p in this.properties){
-				// Make shallow copy of properties into pm because we overwrite
-				// some values below. In particular if start/end are functions
-				// we don't want to overwrite them or the functions won't be
-				// called if the animation is reused.
-				if(p == "width" || p == "height"){
-					this.node.display = "block";
-				}
-				var prop = this.properties[p];
-				prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop }));
-
-				if(d.isFunction(prop.start)){
-					prop.start = prop.start();
-				}
-				if(d.isFunction(prop.end)){
-					prop.end = prop.end();
-				}
-				var isColor = (p.toLowerCase().indexOf("color") >= 0);
-				function getStyle(node, p){
-					// dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:
-					var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];
-					if(v !== undefined){ return v; }
-					v = d.style(node, p);
-					return (p=="opacity") ? Number(v) : (isColor ? v : parseFloat(v));
-				}
-				if(!("end" in prop)){
-					prop.end = getStyle(this.node, p);
-				}else if(!("start" in prop)){
-					prop.start = getStyle(this.node, p);
-				}
-
-				if(isColor){
-					prop.start = new d.Color(prop.start);
-					prop.end = new d.Color(prop.end);
-				}else{
-					prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);
-				}
-			}
-			this.curve = new PropLine(pm);
-		});
-		d.connect(anim, "onAnimate", d.hitch(d, "style", anim.node));
-		return anim; // dojo._Animation
-	}
-
-	dojo.anim = function(	/*DOMNode|String*/ 	node, 
-							/*Object*/ 			properties, 
-							/*Integer?*/		duration, 
-							/*Function?*/		easing, 
-							/*Function?*/		onEnd,
-							/*Integer?*/		delay){
-		//	summary:
-		//		A simpler interface to `dojo.animateProperty()`, also returns
-		//		an instance of `dojo._Animation` but begins the animation
-		//		immediately, unlike nearly every other Dojo animation API.
-		//	description:
-		//		`dojo.anim` is a simpler (but somewhat less powerful) version
-		//		of `dojo.animateProperty`.  It uses defaults for many basic properties
-		//		and allows for positional parameters to be used in place of the
-		//		packed "property bag" which is used for other Dojo animation
-		//		methods.
-		//
-		//		The `dojo._Animation` object returned from `dojo.anim` will be
-		//		already playing when it is returned from this function, so
-		//		calling play() on it again is (usually) a no-op.
-		//	node:
-		//		a DOM node or the id of a node to animate CSS properties on
-		//	duration:
-		//		The number of milliseconds over which the animation
-		//		should run. Defaults to the global animation default duration
-		//		(350ms).
-		//	easing:
-		//		An easing function over which to calculate acceleration
-		//		and deceleration of the animation through its duration.
-		//		A default easing algorithm is provided, but you may
-		//		plug in any you wish. A large selection of easing algorithms
-		//		are available in `dojo.fx.easing`.
-		//	onEnd:
-		//		A function to be called when the animation finishes
-		//		running.
-		//	delay:
-		//		The number of milliseconds to delay beginning the
-		//		animation by. The default is 0.
-		//	example:
-		//		Fade out a node
-		//	|	dojo.anim("id", { opacity: 0 });
-		//	example:
-		//		Fade out a node over a full second
-		//	|	dojo.anim("id", { opacity: 0 }, 1000);
-		return d.animateProperty({ 
-			node: node,
-			duration: duration||d._Animation.prototype.duration,
-			properties: properties,
-			easing: easing,
-			onEnd: onEnd 
-		}).play(delay||0);
-	}
-})();
-
-}
-
-if(!dojo._hasResource["dojo._base.browser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo._base.browser"] = true;
-dojo.provide("dojo._base.browser");
-
-
-
-
-
-
-
-
-
-//Need this to be the last code segment in base, so do not place any
-//dojo.requireIf calls in this file. Otherwise, due to how the build system
-//puts all requireIf dependencies after the current file, the require calls
-//could be called before all of base is defined.
-if(dojo.config.require){
-	dojo.forEach(dojo.config.require, "dojo['require'](item);");
-}
-
-}
-
-	//INSERT dojo.i18n._preloadLocalizations HERE
-
-	if(dojo.config.afterOnLoad && dojo.isBrowser){
-		//Dojo is being added to the page after page load, so just trigger
-		//the init sequence after a timeout. Using a timeout so the rest of this
-		//script gets evaluated properly. This work needs to happen after the
-		//dojo.config.require work done in dojo._base.
-		window.setTimeout(dojo._fakeLoadInit, 1000);
-	}
-
-})();
-
+(function(){var _1=null;if((_1||(typeof djConfig!="undefined"&&djConfig.scopeMap))&&(typeof window!="undefined")){var _2="",_3="",_4="",_5={},_6={};_1=_1||djConfig.scopeMap;for(var i=0;i<_1.length;i++){var _7=_1[i];_2+="var "+_7[0]+" = {}; "+_7[1]+" = "+_7[0]+";"+_7[1]+"._scopeName = '"+_7[1]+"';";_3+=(i==0?"":",")+_7[0];_4+=(i==0?"":",")+_7[1];_5[_7[0]]=_7[1];_6[_7[1]]=_7[0];}eval(_2+"dojo._scopeArgs = ["+_4+"];");dojo._scopePrefixArgs=_3;dojo._scopePrefix="(function("+_3+"){";dojo._scopeSuffix="})("+_4+")";dojo._scopeMap=_5;dojo._scopeMapRev=_6;}(function(){if(typeof this["loadFirebugConsole"]=="function"){this["loadFirebugConsole"]();}else{this.console=this.console||{};var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","profile","profileEnd","time","timeEnd","trace","warn","log"];var i=0,tn;while((tn=cn[i++])){if(!console[tn]){(function(){var _8=tn+"";console[_8]=("log" in console)?function(){var a=Array.apply({},arguments);a.unshift(_8+":");console["log"](a.join(" "));}:function(){};console[_8]._fake=true;})();}}}if(typeof dojo=="undefined"){dojo={_scopeName:"dojo",_scopePrefix:"",_scopePrefixArgs:"",_scopeSuffix:"",_scopeMap:{},_scopeMapRev:{}};}var d=dojo;if(typeof dijit=="undefined"){dijit={_scopeName:"dijit"};}if(typeof dojox=="undefined"){dojox={_scopeName:"dojox"};}if(!d._scopeArgs){d._scopeArgs=[dojo,dijit,dojox];}d.global=this;d.config={isDebug:false,debugAtAllCosts:false};var _9=typeof djConfig!="undefined"?djConfig:typeof dojoConfig!="undefined"?dojoConfig:null;if(_9){for(var c in _9){d.config[c]=_9[c];}}dojo.locale=d.config.locale;var _a="$Rev: 24595 $".match(/\d+/);dojo.version={major:1,minor:6,patch:1,flag:"",revision:_a?+_a[0]:NaN,toString:function(){with(d.version){return major+"."+minor+"."+patch+flag+" ("+revision+")";}}};if(typeof OpenAjax!="undefined"){OpenAjax.hub.registerLibrary(dojo._scopeName,"http://dojotoolkit.org",d.version.toString());}var _b,_c,_d={};for(var i in {toString:1}){_b=[];break;}dojo._extraNames=_b=_b||["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];_c=_b.length;dojo._mixin=function(_e,_f){var _10,s,i;for(_10 in _f){s=_f[_10];if(!(_10 in _e)||(_e[_10]!==s&&(!(_10 in _d)||_d[_10]!==s))){_e[_10]=s;}}if(_c&&_f){for(i=0;i<_c;++i){_10=_b[i];s=_f[_10];if(!(_10 in _e)||(_e[_10]!==s&&(!(_10 in _d)||_d[_10]!==s))){_e[_10]=s;}}}return _e;};dojo.mixin=function(obj,_11){if(!obj){obj={};}for(var i=1,l=arguments.length;i<l;i++){d._mixin(obj,arguments[i]);}return obj;};dojo._getProp=function(_12,_13,_14){var obj=_14||d.global;for(var i=0,p;obj&&(p=_12[i]);i++){if(i==0&&d._scopeMap[p]){p=d._scopeMap[p];}obj=(p in obj?obj[p]:(_13?obj[p]={}:undefined));}return obj;};dojo.setObject=function(_15,_16,_17){var _18=_15.split("."),p=_18.pop(),obj=d._getProp(_18,true,_17);return obj&&p?(obj[p]=_16):undefined;};dojo.getObject=function(_19,_1a,_1b){return d._getProp(_19.split("."),_1a,_1b);};dojo.exists=function(_1c,obj){return d.getObject(_1c,false,obj)!==undefined;};dojo["eval"]=function(_1d){return d.global.eval?d.global.eval(_1d):eval(_1d);};d.deprecated=d.experimental=function(){};})();(function(){var d=dojo,_1e;d.mixin(d,{_loadedModules:{},_inFlightCount:0,_hasResource:{},_modulePrefixes:{dojo:{name:"dojo",value:"."},doh:{name:"doh",value:"../util/doh"},tests:{name:"tests",value:"tests"}},_moduleHasPrefix:function(_1f){var mp=d._modulePrefixes;return !!(mp[_1f]&&mp[_1f].value);},_getModulePrefix:function(_20){var mp=d._modulePrefixes;if(d._moduleHasPrefix(_20)){return mp[_20].value;}return _20;},_loadedUrls:[],_postLoad:false,_loaders:[],_unloaders:[],_loadNotifying:false});dojo._loadPath=function(_21,_22,cb){var uri=((_21.charAt(0)=="/"||_21.match(/^\w+:/))?"":d.baseUrl)+_21;try{_1e=_22;return !_22?d._loadUri(uri,cb):d._loadUriAndCheck(uri,_22,cb);}catch(e){console.error(e);return false;}finally{_1e=null;}};dojo._loadUri=function(uri,cb){if(d._loadedUrls[uri]){return true;}d._inFlightCount++;var _23=d._getText(uri,true);if(_23){d._loadedUrls[uri]=true;d._loadedUrls.push(uri);if(cb){_23=/^define\(/.test(_23)?_23:"("+_23+")";}else{_23=d._scopePrefix+_23+d._scopeSuffix;}if(!d.isIE){_23+="\r\n//@ sourceURL="+uri;}var _24=d["eval"](_23);if(cb){cb(_24);}}if(--d._inFlightCount==0&&d._postLoad&&d._loaders.length){setTimeout(function(){if(d._inFlightCount==0){d._callLoaded();}},0);}return !!_23;};dojo._loadUriAndCheck=function(uri,_25,cb){var ok=false;try{ok=d._loadUri(uri,cb);}catch(e){console.error("failed loading "+uri+" with error: "+e);}return !!(ok&&d._loadedModules[_25]);};dojo.loaded=function(){d._loadNotifying=true;d._postLoad=true;var mll=d._loaders;d._loaders=[];for(var x=0;x<mll.length;x++){mll[x]();}d._loadNotifying=false;if(d._postLoad&&d._inFlightCount==0&&mll.length){d._callLoaded();}};dojo.unloaded=function(){var mll=d._unloaders;while(mll.length){(mll.pop())();}};d._onto=function(arr,obj,fn){if(!fn){arr.push(obj);}else{if(fn){var _26=(typeof fn=="string")?obj[fn]:fn;arr.push(function(){_26.call(obj);});}}};dojo.ready=dojo.addOnLoad=function(obj,_27){d._onto(d._loaders,obj,_27);if(d._postLoad&&d._inFlightCount==0&&!d._loadNotifying){d._callLoaded();}};var dca=d.config.addOnLoad;if(dca){d.addOnLoad[(dca instanceof Array?"apply":"call")](d,dca);}dojo._modulesLoaded=function(){if(d._postLoad){return;}if(d._inFlightCount>0){console.warn("files still in flight!");return;}d._callLoaded();};dojo._callLoaded=function(){if(typeof setTimeout=="object"||(d.config.useXDomain&&d.isOpera)){setTimeout(d.isAIR?function(){d.loaded();}:d._scopeName+".loaded();",0);}else{d.loaded();}};dojo._getModuleSymbols=function(_28){var _29=_28.split(".");for(var i=_29.length;i>0;i--){var _2a=_29.slice(0,i).join(".");if(i==1&&!d._moduleHasPrefix(_2a)){_29[0]="../"+_29[0];}else{var _2b=d._getModulePrefix(_2a);if(_2b!=_2a){_29.splice(0,i,_2b);break;}}}return _29;};dojo._global_omit_module_check=false;dojo.loadInit=function(_2c){_2c();};dojo._loadModule=dojo.require=function(_2d,_2e){_2e=d._global_omit_module_check||_2e;var _2f=d._loadedModules[_2d];if(_2f){return _2f;}var _30=d._getModuleSymbols(_2d).join("/")+".js";var _31=!_2e?_2d:null;var ok=d._loadPath(_30,_31);if(!ok&&!_2e){throw new Error("Could not load '"+_2d+"'; last tried '"+_30+"'");}if(!_2e&&!d._isXDomain){_2f=d._loadedModules[_2d];if(!_2f){throw new Error("symbol '"+_2d+"' is not defined after loading '"+_30+"'");}}return _2f;};dojo.provide=function(_32){_32=_32+"";return (d._loadedModules[_32]=d.getObject(_32,true));};dojo.platformRequire=function(_33){var _34=_33.common||[];var _35=_34.concat(_33[d._name]||_33["default"]||[]);for(var x=0;x<_35.length;x++){var _36=_35[x];if(_36.constructor==Array){d._loadModule.apply(d,_36);}else{d._loadModule(_36);}}};dojo.requireIf=function(_37,_38){if(_37===true){var _39=[];for(var i=1;i<arguments.length;i++){_39.push(arguments[i]);}d.require.apply(d,_39);}};dojo.requireAfterIf=d.requireIf;dojo.registerModulePath=function(_3a,_3b){d._modulePrefixes[_3a]={name:_3a,value:_3b};};dojo.requireLocalization=function(_3c,_3d,_3e,_3f){d.require("dojo.i18n");d.i18n._requireLocalization.apply(d.hostenv,arguments);};var ore=new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),ire=new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$");dojo._Url=function(){var n=null,_40=arguments,uri=[_40[0]];for(var i=1;i<_40.length;i++){if(!_40[i]){continue;}var _41=new d._Url(_40[i]+""),_42=new d._Url(uri[0]+"");if(_41.path==""&&!_41.scheme&&!_41.authority&&!_41.query){if(_41.fragment!=n){_42.fragment=_41.fragment;}_41=_42;}else{if(!_41.scheme){_41.scheme=_42.scheme;if(!_41.authority){_41.authority=_42.authority;if(_41.path.charAt(0)!="/"){var _43=_42.path.substring(0,_42.path.lastIndexOf("/")+1)+_41.path;var _44=_43.split("/");for(var j=0;j<_44.length;j++){if(_44[j]=="."){if(j==_44.length-1){_44[j]="";}else{_44.splice(j,1);j--;}}else{if(j>0&&!(j==1&&_44[0]=="")&&_44[j]==".."&&_44[j-1]!=".."){if(j==(_44.length-1)){_44.splice(j,1);_44[j-1]="";}else{_44.splice(j-1,2);j-=2;}}}}_41.path=_44.join("/");}}}}uri=[];if(_41.scheme){uri.push(_41.scheme,":");}if(_41.authority){uri.push("//",_41.authority);}uri.push(_41.path);if(_41.query){uri.push("?",_41.query);}if(_41.fragment){uri.push("#",_41.fragment);}}this.uri=uri.join("");var r=this.uri.match(ore);this.scheme=r[2]||(r[1]?"":n);this.authority=r[4]||(r[3]?"":n);this.path=r[5];this.query=r[7]||(r[6]?"":n);this.fragment=r[9]||(r[8]?"":n);if(this.authority!=n){r=this.authority.match(ire);this.user=r[3]||n;this.password=r[4]||n;this.host=r[6]||r[7];this.port=r[9]||n;}};dojo._Url.prototype.toString=function(){return this.uri;};dojo.moduleUrl=function(_45,url){var loc=d._getModuleSymbols(_45).join("/");if(!loc){return null;}if(loc.lastIndexOf("/")!=loc.length-1){loc+="/";}var _46=loc.indexOf(":");if(loc.charAt(0)!="/"&&(_46==-1||_46>loc.indexOf("/"))){loc=d.baseUrl+loc;}return new d._Url(loc,url);};})();if(typeof window!="undefined"){dojo.isBrowser=true;dojo._name="browser";(function(){var d=dojo;if(document&&document.getElementsByTagName){var _47=document.getElementsByTagName("script");var _48=/dojo(\.xd)?\.js(\W|$)/i;for(var i=0;i<_47.length;i++){var src=_47[i].getAttribute("src");if(!src){continue;}var m=src.match(_48);if(m){if(!d.config.baseUrl){d.config.baseUrl=src.substring(0,m.index);}var cfg=(_47[i].getAttribute("djConfig")||_47[i].getAttribute("data-dojo-config"));if(cfg){var _49=eval("({ "+cfg+" })");for(var x in _49){dojo.config[x]=_49[x];}}break;}}}d.baseUrl=d.config.baseUrl;var n=navigator;var dua=n.userAgent,dav=n.appVersion,tv=parseFloat(dav);if(dua.indexOf("Opera")>=0){d.isOpera=tv;}if(dua.indexOf("AdobeAIR")>=0){d.isAIR=1;}d.isKhtml=(dav.indexOf("Konqueror")>=0)?tv:0;d.isWebKit=parseFloat(dua.split("WebKit/")[1])||undefined;d.isChrome=parseFloat(dua.split("Chrome/")[1])||undefined;d.isMac=dav.indexOf("Macintosh")>=0;var _4a=Math.max(dav.indexOf("WebKit"),dav.indexOf("Safari"),0);if(_4a&&!dojo.isChrome){d.isSafari=parseFloat(dav.split("Version/")[1]);if(!d.isSafari||parseFloat(dav.substr(_4a+7))<=419.3){d.isSafari=2;}}if(dua.indexOf("Gecko")>=0&&!d.isKhtml&&!d.isWebKit){d.isMozilla=d.isMoz=tv;}if(d.isMoz){d.isFF=parseFloat(dua.split("Firefox/")[1]||dua.split("Minefield/")[1])||undefined;}if(document.all&&!d.isOpera){d.isIE=parseFloat(dav.split("MSIE ")[1])||undefined;var _4b=document.documentMode;if(_4b&&_4b!=5&&Math.floor(d.isIE)!=_4b){d.isIE=_4b;}}if(dojo.isIE&&window.location.protocol==="file:"){dojo.config.ieForceActiveXXhr=true;}d.isQuirks=document.compatMode=="BackCompat";d.locale=dojo.config.locale||(d.isIE?n.userLanguage:n.language).toLowerCase();d._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];d._xhrObj=function(){var _4c,_4d;if(!dojo.isIE||!dojo.config.ieForceActiveXXhr){try{_4c=new XMLHttpRequest();}catch(e){}}if(!_4c){for(var i=0;i<3;++i){var _4e=d._XMLHTTP_PROGIDS[i];try{_4c=new ActiveXObject(_4e);}catch(e){_4d=e;}if(_4c){d._XMLHTTP_PROGIDS=[_4e];break;}}}if(!_4c){throw new Error("XMLHTTP not available: "+_4d);}return _4c;};d._isDocumentOk=function(_4f){var _50=_4f.status||0,lp=location.protocol;return (_50>=200&&_50<300)||_50==304||_50==1223||(!_50&&(lp=="file:"||lp=="chrome:"||lp=="chrome-extension:"||lp=="app:"));};var _51=window.location+"";var _52=document.getElementsByTagName("base");var _53=(_52&&_52.length>0);d._getText=function(uri,_54){var _55=d._xhrObj();if(!_53&&dojo._Url){uri=(new dojo._Url(_51,uri)).toString();}if(d.config.cacheBust){uri+="";uri+=(uri.indexOf("?")==-1?"?":"&")+String(d.config.cacheBust).replace(/\W+/g,"");}_55.open("GET",uri,false);try{_55.send(null);if(!d._isDocumentOk(_55)){var err=Error("Unable to load "+uri+" status:"+_55.status);err.status=_55.status;err.responseText=_55.responseText;throw err;}}catch(e){if(_54){return null;}throw e;}return _55.responseText;};var _56=window;var _57=function(_58,fp){var _59=_56.attachEvent||_56.addEventListener;_58=_56.attachEvent?_58:_58.substring(2);_59(_58,function(){fp.apply(_56,arguments);},false);};d._windowUnloaders=[];d.windowUnloaded=function(){var mll=d._windowUnloaders;while(mll.length){(mll.pop())();}d=null;};var _5a=0;d.addOnWindowUnload=function(obj,_5b){d._onto(d._windowUnloaders,obj,_5b);if(!_5a){_5a=1;_57("onunload",d.windowUnloaded);}};var _5c=0;d.addOnUnload=function(obj,_5d){d._onto(d._unloaders,obj,_5d);if(!_5c){_5c=1;_57("onbeforeunload",dojo.unloaded);}};})();dojo._initFired=false;dojo._loadInit=function(e){if(dojo._scrollIntervalId){clearInterval(dojo._scrollIntervalId);dojo._scrollIntervalId=0;}if(!dojo._initFired){dojo._initFired=true;if(!dojo.config.afterOnLoad&&window.detachEvent){window.detachEvent("onload",dojo._loadInit);}if(dojo._inFlightCount==0){dojo._modulesLoaded();}}};if(!dojo.config.afterOnLoad){if(document.addEventListener){document.addEventListener("DOMContentLoaded",dojo._loadInit,false);window.addEventListener("load",dojo._loadInit,false);}else{if(window.attachEvent){window.attachEvent("onload",dojo._loadInit);if(!dojo.config.skipIeDomLoaded&&self===self.top){dojo._scrollIntervalId=setInterval(function(){try{if(document.body){document.documentElement.doScroll("left");dojo._loadInit();}}catch(e){}},30);}}}}if(dojo.isIE){try{(function(){document.namespaces.add("v","urn:schemas-microsoft-com:vml");var _5e=["*","group","roundrect","oval","shape","rect","imagedata","path","textpath","text"],i=0,l=1,s=document.createStyleSheet();if(dojo.isIE>=8){i=1;l=_5e.length;}for(;i<l;++i){s.addRule("v\\:"+_5e[i],"behavior:url(#default#VML); display:inline-block");}})();}catch(e){}}}(function(){var mp=dojo.config["modulePaths"];if(mp){for(var _5f in mp){dojo.registerModulePath(_5f,mp[_5f]);}}})();if(dojo.config.isDebug){dojo.require("dojo._firebug.firebug");}if(dojo.config.debugAtAllCosts){dojo.require("dojo._base._loader.loader_debug");dojo.require("dojo.i18n");}if(!dojo._hasResource["dojo._base.lang"]){dojo._hasResource["dojo._base.lang"]=true;dojo.provide("dojo._base.lang");(function(){var d=dojo,_60=Object.prototype.toString;dojo.isString=function(it){return (typeof it=="string"||it instanceof String);};dojo.isArray=function(it){return it&&(it instanceof Array||typeof it=="array");};dojo.isFunction=function(it){return _60.call(it)==="[object Function]";};dojo.isObject=function(it){return it!==undefined&&(it===null||typeof it=="object"||d.isArray(it)||d.isFunction(it));};dojo.isArrayLike=function(it){return it&&it!==undefined&&!d.isString(it)&&!d.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(d.isArray(it)||isFinite(it.length));};dojo.isAlien=function(it){return it&&!d.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it));};dojo.extend=function(_61,_62){for(var i=1,l=arguments.length;i<l;i++){d._mixin(_61.prototype,arguments[i]);}return _61;};dojo._hitchArgs=function(_63,_64){var pre=d._toArray(arguments,2);var _65=d.isString(_64);return function(){var _66=d._toArray(arguments);var f=_65?(_63||d.global)[_64]:_64;return f&&f.apply(_63||this,pre.concat(_66));};};dojo.hitch=function(_67,_68){if(arguments.length>2){return d._hitchArgs.apply(d,arguments);}if(!_68){_68=_67;_67=null;}if(d.isString(_68)){_67=_67||d.global;if(!_67[_68]){throw (["dojo.hitch: scope[\"",_68,"\"] is null (scope=\"",_67,"\")"].join(""));}return function(){return _67[_68].apply(_67,arguments||[]);};}return !_67?_68:function(){return _68.apply(_67,arguments||[]);};};dojo.delegate=dojo._delegate=(function(){function TMP(){};return function(obj,_69){TMP.prototype=obj;var tmp=new TMP();TMP.prototype=null;if(_69){d._mixin(tmp,_69);}return tmp;};})();var _6a=function(obj,_6b,_6c){return (_6c||[]).concat(Array.prototype.slice.call(obj,_6b||0));};var _6d=function(obj,_6e,_6f){var arr=_6f||[];for(var x=_6e||0;x<obj.length;x++){arr.push(obj[x]);}return arr;};dojo._toArray=d.isIE?function(obj){return ((obj.item)?_6d:_6a).apply(this,arguments);}:_6a;dojo.partial=function(_70){var arr=[null];return d.hitch.apply(d,arr.concat(d._toArray(arguments)));};var _71=d._extraNames,_72=_71.length,_73={};dojo.clone=function(o){if(!o||typeof o!="object"||d.isFunction(o)){return o;}if(o.nodeType&&"cloneNode" in o){return o.cloneNode(true);}if(o instanceof Date){return new Date(o.getTime());}if(o instanceof RegExp){return new RegExp(o);}var r,i,l,s,_74;if(d.isArray(o)){r=[];for(i=0,l=o.length;i<l;++i){if(i in o){r.push(d.clone(o[i]));}}}else{r=o.constructor?new o.constructor():{};}for(_74 in o){s=o[_74];if(!(_74 in r)||(r[_74]!==s&&(!(_74 in _73)||_73[_74]!==s))){r[_74]=d.clone(s);}}if(_72){for(i=0;i<_72;++i){_74=_71[i];s=o[_74];if(!(_74 in r)||(r[_74]!==s&&(!(_74 in _73)||_73[_74]!==s))){r[_74]=s;}}}return r;};dojo.trim=String.prototype.trim?function(str){return str.trim();}:function(str){return str.replace(/^\s\s*/,"").replace(/\s\s*$/,"");};var _75=/\{([^\}]+)\}/g;dojo.replace=function(_76,map,_77){return _76.replace(_77||_75,d.isFunction(map)?map:function(_78,k){return d.getObject(k,false,map);});};})();}if(!dojo._hasResource["dojo._base.array"]){dojo._hasResource["dojo._base.array"]=true;dojo.provide("dojo._base.array");(function(){var _79=function(arr,obj,cb){return [(typeof arr=="string")?arr.split(""):arr,obj||dojo.global,(typeof cb=="string")?new Function("item","index","array",cb):cb];};var _7a=function(_7b,arr,_7c,_7d){var _7e=_79(arr,_7d,_7c);arr=_7e[0];for(var i=0,l=arr.length;i<l;++i){var _7f=!!_7e[2].call(_7e[1],arr[i],i,arr);if(_7b^_7f){return _7f;}}return _7b;};dojo.mixin(dojo,{indexOf:function(_80,_81,_82,_83){var _84=1,end=_80.length||0,i=0;if(_83){i=end-1;_84=end=-1;}if(_82!=undefined){i=_82;}if((_83&&i>end)||i<end){for(;i!=end;i+=_84){if(_80[i]==_81){return i;}}}return -1;},lastIndexOf:function(_85,_86,_87){return dojo.indexOf(_85,_86,_87,true);},forEach:function(arr,_88,_89){if(!arr||!arr.length){return;}var _8a=_79(arr,_89,_88);arr=_8a[0];for(var i=0,l=arr.length;i<l;++i){_8a[2].call(_8a[1],arr[i],i,arr);}},every:function(arr,_8b,_8c){return _7a(true,arr,_8b,_8c);},some:function(arr,_8d,_8e){return _7a(false,arr,_8d,_8e);},map:function(arr,_8f,_90){var _91=_79(arr,_90,_8f);arr=_91[0];var _92=(arguments[3]?(new arguments[3]()):[]);for(var i=0,l=arr.length;i<l;++i){_92.push(_91[2].call(_91[1],arr[i],i,arr));}return _92;},filter:function(arr,_93,_94){var _95=_79(arr,_94,_93);arr=_95[0];var _96=[];for(var i=0,l=arr.length;i<l;++i){if(_95[2].call(_95[1],arr[i],i,arr)){_96.push(arr[i]);}}return _96;}});})();}if(!dojo._hasResource["dojo._base.declare"]){dojo._hasResource["dojo._base.declare"]=true;dojo.provide("dojo._base.declare");(function(){var d=dojo,mix=d._mixin,op=Object.prototype,_97=op.toString,_98=new Function,_99=0,_9a="constructor";function err(msg,cls){throw new Error("declare"+(cls?" "+cls:"")+": "+msg);};function _9b(_9c,_9d){var _9e=[],_9f=[{cls:0,refs:[]}],_a0={},_a1=1,l=_9c.length,i=0,j,lin,_a2,top,_a3,rec,_a4,_a5;for(;i<l;++i){_a2=_9c[i];if(!_a2){err("mixin #"+i+" is unknown. Did you use dojo.require to pull it in?",_9d);}else{if(_97.call(_a2)!="[object Function]"){err("mixin #"+i+" is not a callable constructor.",_9d);}}lin=_a2._meta?_a2._meta.bases:[_a2];top=0;for(j=lin.length-1;j>=0;--j){_a3=lin[j].prototype;if(!_a3.hasOwnProperty("declaredClass")){_a3.declaredClass="uniqName_"+(_99++);}_a4=_a3.declaredClass;if(!_a0.hasOwnProperty(_a4)){_a0[_a4]={count:0,refs:[],cls:lin[j]};++_a1;}rec=_a0[_a4];if(top&&top!==rec){rec.refs.push(top);++top.count;}top=rec;}++top.count;_9f[0].refs.push(top);}while(_9f.length){top=_9f.pop();_9e.push(top.cls);--_a1;while(_a5=top.refs,_a5.length==1){top=_a5[0];if(!top||--top.count){top=0;break;}_9e.push(top.cls);--_a1;}if(top){for(i=0,l=_a5.length;i<l;++i){top=_a5[i];if(!--top.count){_9f.push(top);}}}}if(_a1){err("can't build consistent linearization",_9d);}_a2=_9c[0];_9e[0]=_a2?_a2._meta&&_a2===_9e[_9e.length-_a2._meta.bases.length]?_a2._meta.bases.length:1:0;return _9e;};function _a6(_a7,a,f){var _a8,_a9,_aa,_ab,_ac,_ad,_ae,opf,pos,_af=this._inherited=this._inherited||{};if(typeof _a7=="string"){_a8=_a7;_a7=a;a=f;}f=0;_ab=_a7.callee;_a8=_a8||_ab.nom;if(!_a8){err("can't deduce a name to call inherited()",this.declaredClass);}_ac=this.constructor._meta;_aa=_ac.bases;pos=_af.p;if(_a8!=_9a){if(_af.c!==_ab){pos=0;_ad=_aa[0];_ac=_ad._meta;if(_ac.hidden[_a8]!==_ab){_a9=_ac.chains;if(_a9&&typeof _a9[_a8]=="string"){err("calling chained method with inherited: "+_a8,this.declaredClass);}do{_ac=_ad._meta;_ae=_ad.prototype;if(_ac&&(_ae[_a8]===_ab&&_ae.hasOwnProperty(_a8)||_ac.hidden[_a8]===_ab)){break;}}while(_ad=_aa[++pos]);pos=_ad?pos:-1;}}_ad=_aa[++pos];if(_ad){_ae=_ad.prototype;if(_ad._meta&&_ae.hasOwnProperty(_a8)){f=_ae[_a8];}else{opf=op[_a8];do{_ae=_ad.prototype;f=_ae[_a8];if(f&&(_ad._meta?_ae.hasOwnProperty(_a8):f!==opf)){break;}}while(_ad=_aa[++pos]);}}f=_ad&&f||op[_a8];}else{if(_af.c!==_ab){pos=0;_ac=_aa[0]._meta;if(_ac&&_ac.ctor!==_ab){_a9=_ac.chains;if(!_a9||_a9.constructor!=="manual"){err("calling chained constructor with inherited",this.declaredClass);}while(_ad=_aa[++pos]){_ac=_ad._meta;if(_ac&&_ac.ctor===_ab){break;}}pos=_ad?pos:-1;}}while(_ad=_aa[++pos]){_ac=_ad._meta;f=_ac?_ac.ctor:_ad;if(f){break;}}f=_ad&&f;}_af.c=f;_af.p=pos;if(f){return a===true?f:f.apply(this,a||_a7);}};function _b0(_b1,_b2){if(typeof _b1=="string"){return this.inherited(_b1,_b2,true);}return this.inherited(_b1,true);};function _b3(cls){var _b4=this.constructor._meta.bases;for(var i=0,l=_b4.length;i<l;++i){if(_b4[i]===cls){return true;}}return this instanceof cls;};function _b5(_b6,_b7){var _b8,i=0,l=d._extraNames.length;for(_b8 in _b7){if(_b8!=_9a&&_b7.hasOwnProperty(_b8)){_b6[_b8]=_b7[_b8];}}for(;i<l;++i){_b8=d._extraNames[i];if(_b8!=_9a&&_b7.hasOwnProperty(_b8)){_b6[_b8]=_b7[_b8];}}};function _b9(_ba,_bb){var _bc,t,i=0,l=d._extraNames.length;for(_bc in _bb){t=_bb[_bc];if((t!==op[_bc]||!(_bc in op))&&_bc!=_9a){if(_97.call(t)=="[object Function]"){t.nom=_bc;}_ba[_bc]=t;}}for(;i<l;++i){_bc=d._extraNames[i];t=_bb[_bc];if((t!==op[_bc]||!(_bc in op))&&_bc!=_9a){if(_97.call(t)=="[object Function]"){t.nom=_bc;}_ba[_bc]=t;}}return _ba;};function _bd(_be){_b9(this.prototype,_be);return this;};function _bf(_c0,_c1){return function(){var a=arguments,_c2=a,a0=a[0],f,i,m,l=_c0.length,_c3;if(!(this instanceof a.callee)){return _c4(a);}if(_c1&&(a0&&a0.preamble||this.preamble)){_c3=new Array(_c0.length);_c3[0]=a;for(i=0;;){a0=a[0];if(a0){f=a0.preamble;if(f){a=f.apply(this,a)||a;}}f=_c0[i].prototype;f=f.hasOwnProperty("preamble")&&f.preamble;if(f){a=f.apply(this,a)||a;}if(++i==l){break;}_c3[i]=a;}}for(i=l-1;i>=0;--i){f=_c0[i];m=f._meta;f=m?m.ctor:f;if(f){f.apply(this,_c3?_c3[i]:a);}}f=this.postscript;if(f){f.apply(this,_c2);}};};function _c5(_c6,_c7){return function(){var a=arguments,t=a,a0=a[0],f;if(!(this instanceof a.callee)){return _c4(a);}if(_c7){if(a0){f=a0.preamble;if(f){t=f.apply(this,t)||t;}}f=this.preamble;if(f){f.apply(this,t);}}if(_c6){_c6.apply(this,a);}f=this.postscript;if(f){f.apply(this,a);}};};function _c8(_c9){return function(){var a=arguments,i=0,f,m;if(!(this instanceof a.callee)){return _c4(a);}for(;f=_c9[i];++i){m=f._meta;f=m?m.ctor:f;if(f){f.apply(this,a);break;}}f=this.postscript;if(f){f.apply(this,a);}};};function _ca(_cb,_cc,_cd){return function(){var b,m,f,i=0,_ce=1;if(_cd){i=_cc.length-1;_ce=-1;}for(;b=_cc[i];i+=_ce){m=b._meta;f=(m?m.hidden:b.prototype)[_cb];if(f){f.apply(this,arguments);}}};};function _cf(_d0){_98.prototype=_d0.prototype;var t=new _98;_98.prototype=null;return t;};function _c4(_d1){var _d2=_d1.callee,t=_cf(_d2);_d2.apply(t,_d1);return t;};d.declare=function(_d3,_d4,_d5){if(typeof _d3!="string"){_d5=_d4;_d4=_d3;_d3="";}_d5=_d5||{};var _d6,i,t,_d7,_d8,_d9,_da,_db=1,_dc=_d4;if(_97.call(_d4)=="[object Array]"){_d9=_9b(_d4,_d3);t=_d9[0];_db=_d9.length-t;_d4=_d9[_db];}else{_d9=[0];if(_d4){if(_97.call(_d4)=="[object Function]"){t=_d4._meta;_d9=_d9.concat(t?t.bases:_d4);}else{err("base class is not a callable constructor.",_d3);}}else{if(_d4!==null){err("unknown base class. Did you use dojo.require to pull it in?",_d3);}}}if(_d4){for(i=_db-1;;--i){_d6=_cf(_d4);if(!i){break;}t=_d9[i];(t._meta?_b5:mix)(_d6,t.prototype);_d7=new Function;_d7.superclass=_d4;_d7.prototype=_d6;_d4=_d6.constructor=_d7;}}else{_d6={};}_b9(_d6,_d5);t=_d5.constructor;if(t!==op.constructor){t.nom=_9a;_d6.constructor=t;}for(i=_db-1;i;--i){t=_d9[i]._meta;if(t&&t.chains){_da=mix(_da||{},t.chains);}}if(_d6["-chains-"]){_da=mix(_da||{},_d6["-chains-"]);}t=!_da||!_da.hasOwnProperty(_9a);_d9[0]=_d7=(_da&&_da.constructor==="manual")?_c8(_d9):(_d9.length==1?_c5(_d5.constructor,t):_bf(_d9,t));_d7._meta={bases:_d9,hidden:_d5,chains:_da,parents:_dc,ctor:_d5.constructor};_d7.superclass=_d4&&_d4.prototype;_d7.extend=_bd;_d7.prototype=_d6;_d6.constructor=_d7;_d6.getInherited=_b0;_d6.inherited=_a6;_d6.isInstanceOf=_b3;if(_d3){_d6.declaredClass=_d3;d.setObject(_d3,_d7);}if(_da){for(_d8 in _da){if(_d6[_d8]&&typeof _da[_d8]=="string"&&_d8!=_9a){t=_d6[_d8]=_ca(_d8,_d9,_da[_d8]==="after");t.nom=_d8;}}}return _d7;};d.safeMixin=_b9;})();}if(!dojo._hasResource["dojo._base.connect"]){dojo._hasResource["dojo._base.connect"]=true;dojo.provide("dojo._base.connect");dojo._listener={getDispatcher:function(){return function(){var ap=Array.prototype,c=arguments.callee,ls=c._listeners,t=c.target,r=t&&t.apply(this,arguments),i,lls=[].concat(ls);for(i in lls){if(!(i in ap)){lls[i].apply(this,arguments);}}return r;};},add:function(_dd,_de,_df){_dd=_dd||dojo.global;var f=_dd[_de];if(!f||!f._listeners){var d=dojo._listener.getDispatcher();d.target=f;d._listeners=[];f=_dd[_de]=d;}return f._listeners.push(_df);},remove:function(_e0,_e1,_e2){var f=(_e0||dojo.global)[_e1];if(f&&f._listeners&&_e2--){delete f._listeners[_e2];}}};dojo.connect=function(obj,_e3,_e4,_e5,_e6){var a=arguments,_e7=[],i=0;_e7.push(dojo.isString(a[0])?null:a[i++],a[i++]);var a1=a[i+1];_e7.push(dojo.isString(a1)||dojo.isFunction(a1)?a[i++]:null,a[i++]);for(var l=a.length;i<l;i++){_e7.push(a[i]);}return dojo._connect.apply(this,_e7);};dojo._connect=function(obj,_e8,_e9,_ea){var l=dojo._listener,h=l.add(obj,_e8,dojo.hitch(_e9,_ea));return [obj,_e8,h,l];};dojo.disconnect=function(_eb){if(_eb&&_eb[0]!==undefined){dojo._disconnect.apply(this,_eb);delete _eb[0];}};dojo._disconnect=function(obj,_ec,_ed,_ee){_ee.remove(obj,_ec,_ed);};dojo._topics={};dojo.subscribe=function(_ef,_f0,_f1){return [_ef,dojo._listener.add(dojo._topics,_ef,dojo.hitch(_f0,_f1))];};dojo.unsubscribe=function(_f2){if(_f2){dojo._listener.remove(dojo._topics,_f2[0],_f2[1]);}};dojo.publish=function(_f3,_f4){var f=dojo._topics[_f3];if(f){f.apply(this,_f4||[]);}};dojo.connectPublisher=function(_f5,obj,_f6){var pf=function(){dojo.publish(_f5,arguments);};return _f6?dojo.connect(obj,_f6,pf):dojo.connect(obj,pf);};}if(!dojo._hasResource["dojo._base.Deferred"]){dojo._hasResource["dojo._base.Deferred"]=true;dojo.provide("dojo._base.Deferred");(function(){var _f7=function(){};var _f8=Object.freeze||function(){};dojo.Deferred=function(_f9){var _fa,_fb,_fc,_fd,_fe;var _ff=(this.promise={});function _100(_101){if(_fb){throw new Error("This deferred has already been resolved");}_fa=_101;_fb=true;_102();};function _102(){var _103;while(!_103&&_fe){var _104=_fe;_fe=_fe.next;if((_103=(_104.progress==_f7))){_fb=false;}var func=(_fc?_104.error:_104.resolved);if(func){try{var _105=func(_fa);if(_105&&typeof _105.then==="function"){_105.then(dojo.hitch(_104.deferred,"resolve"),dojo.hitch(_104.deferred,"reject"));continue;}var _106=_103&&_105===undefined;if(_103&&!_106){_fc=_105 instanceof Error;}_104.deferred[_106&&_fc?"reject":"resolve"](_106?_fa:_105);}catch(e){_104.deferred.reject(e);}}else{if(_fc){_104.deferred.reject(_fa);}else{_104.deferred.resolve(_fa);}}}};this.resolve=this.callback=function(_107){this.fired=0;this.results=[_107,null];_100(_107);};this.reject=this.errback=function(_108){_fc=true;this.fired=1;_100(_108);this.results=[null,_108];if(!_108||_108.log!==false){(dojo.config.deferredOnError||function(x){console.error(x);})(_108);}};this.progress=function(_109){var _10a=_fe;while(_10a){var _10b=_10a.progress;_10b&&_10b(_109);_10a=_10a.next;}};this.addCallbacks=function(_10c,_10d){this.then(_10c,_10d,_f7);return this;};this.then=_ff.then=function(_10e,_10f,_110){var _111=_110==_f7?this:new dojo.Deferred(_ff.cancel);var _112={resolved:_10e,error:_10f,progress:_110,deferred:_111};if(_fe){_fd=_fd.next=_112;}else{_fe=_fd=_112;}if(_fb){_102();}return _111.promise;};var _113=this;this.cancel=_ff.cancel=function(){if(!_fb){var _114=_f9&&_f9(_113);if(!_fb){if(!(_114 instanceof Error)){_114=new Error(_114);}_114.log=false;_113.reject(_114);}}};_f8(_ff);};dojo.extend(dojo.Deferred,{addCallback:function(_115){return this.addCallbacks(dojo.hitch.apply(dojo,arguments));},addErrback:function(_116){return this.addCallbacks(null,dojo.hitch.apply(dojo,arguments));},addBoth:function(_117){var _118=dojo.hitch.apply(dojo,arguments);return this.addCallbacks(_118,_118);},fired:-1});})();dojo.when=function(_119,_11a,_11b,_11c){if(_119&&typeof _119.then==="function"){return _119.then(_11a,_11b,_11c);}return _11a(_119);};}if(!dojo._hasResource["dojo._base.json"]){dojo._hasResource["dojo._base.json"]=true;dojo.provide("dojo._base.json");dojo.fromJson=function(json){return eval("("+json+")");};dojo._escapeString=function(str){return ("\""+str.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r");};dojo.toJsonIndentStr="\t";dojo.toJson=function(it,_11d,_11e){if(it===undefined){return "undefined";}var _11f=typeof it;if(_11f=="number"||_11f=="boolean"){return it+"";}if(it===null){return "null";}if(dojo.isString(it)){return dojo._escapeString(it);}var _120=arguments.callee;var _121;_11e=_11e||"";var _122=_11d?_11e+dojo.toJsonIndentStr:"";var tf=it.__json__||it.json;if(dojo.isFunction(tf)){_121=tf.call(it);if(it!==_121){return _120(_121,_11d,_122);}}if(it.nodeType&&it.cloneNode){throw new Error("Can't serialize DOM nodes");}var sep=_11d?" ":"";var _123=_11d?"\n":"";if(dojo.isArray(it)){var res=dojo.map(it,function(obj){var val=_120(obj,_11d,_122);if(typeof val!="string"){val="undefined";}return _123+_122+val;});return "["+res.join(","+sep)+_123+_11e+"]";}if(_11f=="function"){return null;}var _124=[],key;for(key in it){var _125,val;if(typeof key=="number"){_125="\""+key+"\"";}else{if(typeof key=="string"){_125=dojo._escapeString(key);}else{continue;}}val=_120(it[key],_11d,_122);if(typeof val!="string"){continue;}_124.push(_123+_122+_125+":"+sep+val);}return "{"+_124.join(","+sep)+_123+_11e+"}";};}if(!dojo._hasResource["dojo._base.Color"]){dojo._hasResource["dojo._base.Color"]=true;dojo.provide("dojo._base.Color");(function(){var d=dojo;dojo.Color=function(_126){if(_126){this.setColor(_126);}};dojo.Color.named={black:[0,0,0],silver:[192,192,192],gray:[128,128,128],white:[255,255,255],maroon:[128,0,0],red:[255,0,0],purple:[128,0,128],fuchsia:[255,0,255],green:[0,128,0],lime:[0,255,0],olive:[128,128,0],yellow:[255,255,0],navy:[0,0,128],blue:[0,0,255],teal:[0,128,128],aqua:[0,255,255],transparent:d.config.transparentColor||[255,255,255]};dojo.extend(dojo.Color,{r:255,g:255,b:255,a:1,_set:function(r,g,b,a){var t=this;t.r=r;t.g=g;t.b=b;t.a=a;},setColor:function(_127){if(d.isString(_127)){d.colorFromString(_127,this);}else{if(d.isArray(_127)){d.colorFromArray(_127,this);}else{this._set(_127.r,_127.g,_127.b,_127.a);if(!(_127 instanceof d.Color)){this.sanitize();}}}return this;},sanitize:function(){return this;},toRgb:function(){var t=this;return [t.r,t.g,t.b];},toRgba:function(){var t=this;return [t.r,t.g,t.b,t.a];},toHex:function(){var arr=d.map(["r","g","b"],function(x){var s=this[x].toString(16);return s.length<2?"0"+s:s;},this);return "#"+arr.join("");},toCss:function(_128){var t=this,rgb=t.r+", "+t.g+", "+t.b;return (_128?"rgba("+rgb+", "+t.a:"rgb("+rgb)+")";},toString:function(){return this.toCss(true);}});dojo.blendColors=function(_129,end,_12a,obj){var t=obj||new d.Color();d.forEach(["r","g","b","a"],function(x){t[x]=_129[x]+(end[x]-_129[x])*_12a;if(x!="a"){t[x]=Math.round(t[x]);}});return t.sanitize();};dojo.colorFromRgb=function(_12b,obj){var m=_12b.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);return m&&dojo.colorFromArray(m[1].split(/\s*,\s*/),obj);};dojo.colorFromHex=function(_12c,obj){var t=obj||new d.Color(),bits=(_12c.length==4)?4:8,mask=(1<<bits)-1;_12c=Number("0x"+_12c.substr(1));if(isNaN(_12c)){return null;}d.forEach(["b","g","r"],function(x){var c=_12c&mask;_12c>>=bits;t[x]=bits==4?17*c:c;});t.a=1;return t;};dojo.colorFromArray=function(a,obj){var t=obj||new d.Color();t._set(Number(a[0]),Number(a[1]),Number(a[2]),Number(a[3]));if(isNaN(t.a)){t.a=1;}return t.sanitize();};dojo.colorFromString=function(str,obj){var a=d.Color.named[str];return a&&d.colorFromArray(a,obj)||d.colorFromRgb(str,obj)||d.colorFromHex(str,obj);};})();}if(!dojo._hasResource["dojo._base.window"]){dojo._hasResource["dojo._base.window"]=true;dojo.provide("dojo._base.window");dojo.doc=window["document"]||null;dojo.body=function(){return dojo.doc.body||dojo.doc.getElementsByTagName("body")[0];};dojo.setContext=function(_12d,_12e){dojo.global=_12d;dojo.doc=_12e;};dojo.withGlobal=function(_12f,_130,_131,_132){var _133=dojo.global;try{dojo.global=_12f;return dojo.withDoc.call(null,_12f.document,_130,_131,_132);}finally{dojo.global=_133;}};dojo.withDoc=function(_134,_135,_136,_137){var _138=dojo.doc,_139=dojo._bodyLtr,oldQ=dojo.isQuirks;try{dojo.doc=_134;delete dojo._bodyLtr;dojo.isQuirks=dojo.doc.compatMode=="BackCompat";if(_136&&typeof _135=="string"){_135=_136[_135];}return _135.apply(_136,_137||[]);}finally{dojo.doc=_138;delete dojo._bodyLtr;if(_139!==undefined){dojo._bodyLtr=_139;}dojo.isQuirks=oldQ;}};}if(!dojo._hasResource["dojo._base.event"]){dojo._hasResource["dojo._base.event"]=true;dojo.provide("dojo._base.event");(function(){var del=(dojo._event_listener={add:function(node,name,fp){if(!node){return;}name=del._normalizeEventName(name);fp=del._fixCallback(name,fp);if(!dojo.isIE&&(name=="mouseenter"||name=="mouseleave")){var ofp=fp;name=(name=="mouseenter")?"mouseover":"mouseout";fp=function(e){if(!dojo.isDescendant(e.relatedTarget,node)){return ofp.call(this,e);}};}node.addEventListener(name,fp,false);return fp;},remove:function(node,_13a,_13b){if(node){_13a=del._normalizeEventName(_13a);if(!dojo.isIE&&(_13a=="mouseenter"||_13a=="mouseleave")){_13a=(_13a=="mouseenter")?"mouseover":"mouseout";}node.removeEventListener(_13a,_13b,false);}},_normalizeEventName:function(name){return name.slice(0,2)=="on"?name.slice(2):name;},_fixCallback:function(name,fp){return name!="keypress"?fp:function(e){return fp.call(this,del._fixEvent(e,this));};},_fixEvent:function(evt,_13c){switch(evt.type){case "keypress":del._setKeyChar(evt);break;}return evt;},_setKeyChar:function(evt){evt.keyChar=evt.charCode>=32?String.fromCharCode(evt.charCode):"";evt.charOrCode=evt.keyChar||evt.keyCode;},_punctMap:{106:42,111:47,186:59,187:43,188:44,189:45,190:46,191:47,192:96,219:91,220:92,221:93,222:39}});dojo.fixEvent=function(evt,_13d){return del._fixEvent(evt,_13d);};dojo.stopEvent=function(evt){evt.preventDefault();evt.stopPropagation();};var _13e=dojo._listener;dojo._connect=function(obj,_13f,_140,_141,_142){var _143=obj&&(obj.nodeType||obj.attachEvent||obj.addEventListener);var lid=_143?(_142?2:1):0,l=[dojo._listener,del,_13e][lid];var h=l.add(obj,_13f,dojo.hitch(_140,_141));return [obj,_13f,h,lid];};dojo._disconnect=function(obj,_144,_145,_146){([dojo._listener,del,_13e][_146]).remove(obj,_144,_145);};dojo.keys={BACKSPACE:8,TAB:9,CLEAR:12,ENTER:13,SHIFT:16,CTRL:17,ALT:18,META:dojo.isSafari?91:224,PAUSE:19,CAPS_LOCK:20,ESCAPE:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,HELP:47,LEFT_WINDOW:91,RIGHT_WINDOW:92,SELECT:93,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,NUMPAD_MULTIPLY:106,NUMPAD_PLUS:107,NUMPAD_ENTER:108,NUMPAD_MINUS:109,NUMPAD_PERIOD:110,NUMPAD_DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,F13:124,F14:125,F15:126,NUM_LOCK:144,SCROLL_LOCK:145,copyKey:dojo.isMac&&!dojo.isAIR?(dojo.isSafari?91:224):17};var _147=dojo.isMac?"metaKey":"ctrlKey";dojo.isCopyKey=function(e){return e[_147];};if(dojo.isIE<9||(dojo.isIE&&dojo.isQuirks)){dojo.mouseButtons={LEFT:1,MIDDLE:4,RIGHT:2,isButton:function(e,_148){return e.button&_148;},isLeft:function(e){return e.button&1;},isMiddle:function(e){return e.button&4;},isRight:function(e){return e.button&2;}};}else{dojo.mouseButtons={LEFT:0,MIDDLE:1,RIGHT:2,isButton:function(e,_149){return e.button==_149;},isLeft:function(e){return e.button==0;},isMiddle:function(e){return e.button==1;},isRight:function(e){return e.button==2;}};}if(dojo.isIE){var _14a=function(e,code){try{return (e.keyCode=code);}catch(e){return 0;}};var iel=dojo._listener;var _14b=(dojo._ieListenersName="_"+dojo._scopeName+"_listeners");if(!dojo.config._allow_leaks){_13e=iel=dojo._ie_listener={handlers:[],add:function(_14c,_14d,_14e){_14c=_14c||dojo.global;var f=_14c[_14d];if(!f||!f[_14b]){var d=dojo._getIeDispatcher();d.target=f&&(ieh.push(f)-1);d[_14b]=[];f=_14c[_14d]=d;}return f[_14b].push(ieh.push(_14e)-1);},remove:function(_14f,_150,_151){var f=(_14f||dojo.global)[_150],l=f&&f[_14b];if(f&&l&&_151--){delete ieh[l[_151]];delete l[_151];}}};var ieh=iel.handlers;}dojo.mixin(del,{add:function(node,_152,fp){if(!node){return;}_152=del._normalizeEventName(_152);if(_152=="onkeypress"){var kd=node.onkeydown;if(!kd||!kd[_14b]||!kd._stealthKeydownHandle){var h=del.add(node,"onkeydown",del._stealthKeyDown);kd=node.onkeydown;kd._stealthKeydownHandle=h;kd._stealthKeydownRefs=1;}else{kd._stealthKeydownRefs++;}}return iel.add(node,_152,del._fixCallback(fp));},remove:function(node,_153,_154){_153=del._normalizeEventName(_153);iel.remove(node,_153,_154);if(_153=="onkeypress"){var kd=node.onkeydown;if(--kd._stealthKeydownRefs<=0){iel.remove(node,"onkeydown",kd._stealthKeydownHandle);delete kd._stealthKeydownHandle;}}},_normalizeEventName:function(_155){return _155.slice(0,2)!="on"?"on"+_155:_155;},_nop:function(){},_fixEvent:function(evt,_156){if(!evt){var w=_156&&(_156.ownerDocument||_156.document||_156).parentWindow||window;evt=w.event;}if(!evt){return (evt);}evt.target=evt.srcElement;evt.currentTarget=(_156||evt.srcElement);evt.layerX=evt.offsetX;evt.layerY=evt.offsetY;var se=evt.srcElement,doc=(se&&se.ownerDocument)||document;var _157=((dojo.isIE<6)||(doc["compatMode"]=="BackCompat"))?doc.body:doc.documentElement;var _158=dojo._getIeDocumentElementOffset();evt.pageX=evt.clientX+dojo._fixIeBiDiScrollLeft(_157.scrollLeft||0)-_158.x;evt.pageY=evt.clientY+(_157.scrollTop||0)-_158.y;if(evt.type=="mouseover"){evt.relatedTarget=evt.fromElement;}if(evt.type=="mouseout"){evt.relatedTarget=evt.toElement;}if(dojo.isIE<9||dojo.isQuirks){evt.stopPropagation=del._stopPropagation;evt.preventDefault=del._preventDefault;}return del._fixKeys(evt);},_fixKeys:function(evt){switch(evt.type){case "keypress":var c=("charCode" in evt?evt.charCode:evt.keyCode);if(c==10){c=0;evt.keyCode=13;}else{if(c==13||c==27){c=0;}else{if(c==3){c=99;}}}evt.charCode=c;del._setKeyChar(evt);break;}return evt;},_stealthKeyDown:function(evt){var kp=evt.currentTarget.onkeypress;if(!kp||!kp[_14b]){return;}var k=evt.keyCode;var _159=(k!=13||(dojo.isIE>=9&&!dojo.isQuirks))&&k!=32&&k!=27&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);if(_159||evt.ctrlKey){var c=_159?0:k;if(evt.ctrlKey){if(k==3||k==13){return;}else{if(c>95&&c<106){c-=48;}else{if((!evt.shiftKey)&&(c>=65&&c<=90)){c+=32;}else{c=del._punctMap[c]||c;}}}}var faux=del._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c});kp.call(evt.currentTarget,faux);if(dojo.isIE<9||(dojo.isIE&&dojo.isQuirks)){evt.cancelBubble=faux.cancelBubble;}evt.returnValue=faux.returnValue;_14a(evt,faux.keyCode);}},_stopPropagation:function(){this.cancelBubble=true;},_preventDefault:function(){this.bubbledKeyCode=this.keyCode;if(this.ctrlKey){_14a(this,0);}this.returnValue=false;}});dojo.stopEvent=(dojo.isIE<9||dojo.isQuirks)?function(evt){evt=evt||window.event;del._stopPropagation.call(evt);del._preventDefault.call(evt);}:dojo.stopEvent;}del._synthesizeEvent=function(evt,_15a){var faux=dojo.mixin({},evt,_15a);del._setKeyChar(faux);faux.preventDefault=function(){evt.preventDefault();};faux.stopPropagation=function(){evt.stopPropagation();};return faux;};if(dojo.isOpera){dojo.mixin(del,{_fixEvent:function(evt,_15b){switch(evt.type){case "keypress":var c=evt.which;if(c==3){c=99;}c=c<41&&!evt.shiftKey?0:c;if(evt.ctrlKey&&!evt.shiftKey&&c>=65&&c<=90){c+=32;}return del._synthesizeEvent(evt,{charCode:c});}return evt;}});}if(dojo.isWebKit){del._add=del.add;del._remove=del.remove;dojo.mixin(del,{add:function(node,_15c,fp){if(!node){return;}var _15d=del._add(node,_15c,fp);if(del._normalizeEventName(_15c)=="keypress"){_15d._stealthKeyDownHandle=del._add(node,"keydown",function(evt){var k=evt.keyCode;var _15e=k!=13&&k!=32&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);if(_15e||evt.ctrlKey){var c=_15e?0:k;if(evt.ctrlKey){if(k==3||k==13){return;}else{if(c>95&&c<106){c-=48;}else{if(!evt.shiftKey&&c>=65&&c<=90){c+=32;}else{c=del._punctMap[c]||c;}}}}var faux=del._synthesizeEvent(evt,{type:"keypress",faux:true,charCode:c});fp.call(evt.currentTarget,faux);}});}return _15d;},remove:function(node,_15f,_160){if(node){if(_160._stealthKeyDownHandle){del._remove(node,"keydown",_160._stealthKeyDownHandle);}del._remove(node,_15f,_160);}},_fixEvent:function(evt,_161){switch(evt.type){case "keypress":if(evt.faux){return evt;}var c=evt.charCode;c=c>=32?c:0;return del._synthesizeEvent(evt,{charCode:c,faux:true});}return evt;}});}})();if(dojo.isIE){dojo._ieDispatcher=function(args,_162){var ap=Array.prototype,h=dojo._ie_listener.handlers,c=args.callee,ls=c[dojo._ieListenersName],t=h[c.target];var r=t&&t.apply(_162,args);var lls=[].concat(ls);for(var i in lls){var f=h[lls[i]];if(!(i in ap)&&f){f.apply(_162,args);}}return r;};dojo._getIeDispatcher=function(){return new Function(dojo._scopeName+"._ieDispatcher(arguments, this)");};dojo._event_listener._fixCallback=function(fp){var f=dojo._event_listener._fixEvent;return function(e){return fp.call(this,f(e,this));};};}}if(!dojo._hasResource["dojo._base.html"]){dojo._hasResource["dojo._base.html"]=true;dojo.provide("dojo._base.html");try{document.execCommand("BackgroundImageCache",false,true);}catch(e){}if(dojo.isIE){dojo.byId=function(id,doc){if(typeof id!="string"){return id;}var _163=doc||dojo.doc,te=_163.getElementById(id);if(te&&(te.attributes.id.value==id||te.id==id)){return te;}else{var eles=_163.all[id];if(!eles||eles.nodeName){eles=[eles];}var i=0;while((te=eles[i++])){if((te.attributes&&te.attributes.id&&te.attributes.id.value==id)||te.id==id){return te;}}}};}else{dojo.byId=function(id,doc){return ((typeof id=="string")?(doc||dojo.doc).getElementById(id):id)||null;};}(function(){var d=dojo;var byId=d.byId;var _164=null,_165;d.addOnWindowUnload(function(){_164=null;});dojo._destroyElement=dojo.destroy=function(node){node=byId(node);try{var doc=node.ownerDocument;if(!_164||_165!=doc){_164=doc.createElement("div");_165=doc;}_164.appendChild(node.parentNode?node.parentNode.removeChild(node):node);_164.innerHTML="";}catch(e){}};dojo.isDescendant=function(node,_166){try{node=byId(node);_166=byId(_166);while(node){if(node==_166){return true;}node=node.parentNode;}}catch(e){}return false;};dojo.setSelectable=function(node,_167){node=byId(node);if(d.isMozilla){node.style.MozUserSelect=_167?"":"none";}else{if(d.isKhtml||d.isWebKit){node.style.KhtmlUserSelect=_167?"auto":"none";}else{if(d.isIE){var v=(node.unselectable=_167?"":"on");d.query("*",node).forEach("item.unselectable = '"+v+"'");}}}};var _168=function(node,ref){var _169=ref.parentNode;if(_169){_169.insertBefore(node,ref);}};var _16a=function(node,ref){var _16b=ref.parentNode;if(_16b){if(_16b.lastChild==ref){_16b.appendChild(node);}else{_16b.insertBefore(node,ref.nextSibling);}}};dojo.place=function(node,_16c,_16d){_16c=byId(_16c);if(typeof node=="string"){node=/^\s*</.test(node)?d._toDom(node,_16c.ownerDocument):byId(node);}if(typeof _16d=="number"){var cn=_16c.childNodes;if(!cn.length||cn.length<=_16d){_16c.appendChild(node);}else{_168(node,cn[_16d<0?0:_16d]);}}else{switch(_16d){case "before":_168(node,_16c);break;case "after":_16a(node,_16c);break;case "replace":_16c.parentNode.replaceChild(node,_16c);break;case "only":d.empty(_16c);_16c.appendChild(node);break;case "first":if(_16c.firstChild){_168(node,_16c.firstChild);break;}default:_16c.appendChild(node);}}return node;};dojo.boxModel="content-box";if(d.isIE){d.boxModel=document.compatMode=="BackCompat"?"border-box":"content-box";}var gcs;if(d.isWebKit){gcs=function(node){var s;if(node.nodeType==1){var dv=node.ownerDocument.defaultView;s=dv.getComputedStyle(node,null);if(!s&&node.style){node.style.display="";s=dv.getComputedStyle(node,null);}}return s||{};};}else{if(d.isIE){gcs=function(node){return node.nodeType==1?node.currentStyle:{};};}else{gcs=function(node){return node.nodeType==1?node.ownerDocument.defaultView.getComputedStyle(node,null):{};};}}dojo.getComputedStyle=gcs;if(!d.isIE){d._toPixelValue=function(_16e,_16f){return parseFloat(_16f)||0;};}else{d._toPixelValue=function(_170,_171){if(!_171){return 0;}if(_171=="medium"){return 4;}if(_171.slice&&_171.slice(-2)=="px"){return parseFloat(_171);}with(_170){var _172=style.left;var _173=runtimeStyle.left;runtimeStyle.left=currentStyle.left;try{style.left=_171;_171=style.pixelLeft;}catch(e){_171=0;}style.left=_172;runtimeStyle.left=_173;}return _171;};}var px=d._toPixelValue;var astr="DXImageTransform.Microsoft.Alpha";var af=function(n,f){try{return n.filters.item(astr);}catch(e){return f?{}:null;}};dojo._getOpacity=d.isIE<9?function(node){try{return af(node).Opacity/100;}catch(e){return 1;}}:function(node){return gcs(node).opacity;};dojo._setOpacity=d.isIE<9?function(node,_174){var ov=_174*100,_175=_174==1;node.style.zoom=_175?"":1;if(!af(node)){if(_175){return _174;}node.style.filter+=" progid:"+astr+"(Opacity="+ov+")";}else{af(node,1).Opacity=ov;}af(node,1).Enabled=!_175;if(node.nodeName.toLowerCase()=="tr"){d.query("> td",node).forEach(function(i){d._setOpacity(i,_174);});}return _174;}:function(node,_176){return node.style.opacity=_176;};var _177={left:true,top:true};var _178=/margin|padding|width|height|max|min|offset/;var _179=function(node,type,_17a){type=type.toLowerCase();if(d.isIE){if(_17a=="auto"){if(type=="height"){return node.offsetHeight;}if(type=="width"){return node.offsetWidth;}}if(type=="fontweight"){switch(_17a){case 700:return "bold";case 400:default:return "normal";}}}if(!(type in _177)){_177[type]=_178.test(type);}return _177[type]?px(node,_17a):_17a;};var _17b=d.isIE?"styleFloat":"cssFloat",_17c={"cssFloat":_17b,"styleFloat":_17b,"float":_17b};dojo.style=function(node,_17d,_17e){var n=byId(node),args=arguments.length,op=(_17d=="opacity");_17d=_17c[_17d]||_17d;if(args==3){return op?d._setOpacity(n,_17e):n.style[_17d]=_17e;}if(args==2&&op){return d._getOpacity(n);}var s=gcs(n);if(args==2&&typeof _17d!="string"){for(var x in _17d){d.style(node,x,_17d[x]);}return s;}return (args==1)?s:_179(n,_17d,s[_17d]||n.style[_17d]);};dojo._getPadExtents=function(n,_17f){var s=_17f||gcs(n),l=px(n,s.paddingLeft),t=px(n,s.paddingTop);return {l:l,t:t,w:l+px(n,s.paddingRight),h:t+px(n,s.paddingBottom)};};dojo._getBorderExtents=function(n,_180){var ne="none",s=_180||gcs(n),bl=(s.borderLeftStyle!=ne?px(n,s.borderLeftWidth):0),bt=(s.borderTopStyle!=ne?px(n,s.borderTopWidth):0);return {l:bl,t:bt,w:bl+(s.borderRightStyle!=ne?px(n,s.borderRightWidth):0),h:bt+(s.borderBottomStyle!=ne?px(n,s.borderBottomWidth):0)};};dojo._getPadBorderExtents=function(n,_181){var s=_181||gcs(n),p=d._getPadExtents(n,s),b=d._getBorderExtents(n,s);return {l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h};};dojo._getMarginExtents=function(n,_182){var s=_182||gcs(n),l=px(n,s.marginLeft),t=px(n,s.marginTop),r=px(n,s.marginRight),b=px(n,s.marginBottom);if(d.isWebKit&&(s.position!="absolute")){r=l;}return {l:l,t:t,w:l+r,h:t+b};};dojo._getMarginBox=function(node,_183){var s=_183||gcs(node),me=d._getMarginExtents(node,s);var l=node.offsetLeft-me.l,t=node.offsetTop-me.t,p=node.parentNode;if(d.isMoz){var sl=parseFloat(s.left),st=parseFloat(s.top);if(!isNaN(sl)&&!isNaN(st)){l=sl,t=st;}else{if(p&&p.style){var pcs=gcs(p);if(pcs.overflow!="visible"){var be=d._getBorderExtents(p,pcs);l+=be.l,t+=be.t;}}}}else{if(d.isOpera||(d.isIE>7&&!d.isQuirks)){if(p){be=d._getBorderExtents(p);l-=be.l;t-=be.t;}}}return {l:l,t:t,w:node.offsetWidth+me.w,h:node.offsetHeight+me.h};};dojo._getMarginSize=function(node,_184){node=byId(node);var me=d._getMarginExtents(node,_184||gcs(node));var size=node.getBoundingClientRect();return {w:(size.right-size.left)+me.w,h:(size.bottom-size.top)+me.h};};dojo._getContentBox=function(node,_185){var s=_185||gcs(node),pe=d._getPadExtents(node,s),be=d._getBorderExtents(node,s),w=node.clientWidth,h;if(!w){w=node.offsetWidth,h=node.offsetHeight;}else{h=node.clientHeight,be.w=be.h=0;}if(d.isOpera){pe.l+=be.l;pe.t+=be.t;}return {l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h};};dojo._getBorderBox=function(node,_186){var s=_186||gcs(node),pe=d._getPadExtents(node,s),cb=d._getContentBox(node,s);return {l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h};};dojo._setBox=function(node,l,t,w,h,u){u=u||"px";var s=node.style;if(!isNaN(l)){s.left=l+u;}if(!isNaN(t)){s.top=t+u;}if(w>=0){s.width=w+u;}if(h>=0){s.height=h+u;}};dojo._isButtonTag=function(node){return node.tagName=="BUTTON"||node.tagName=="INPUT"&&(node.getAttribute("type")||"").toUpperCase()=="BUTTON";};dojo._usesBorderBox=function(node){var n=node.tagName;return d.boxModel=="border-box"||n=="TABLE"||d._isButtonTag(node);};dojo._setContentSize=function(node,_187,_188,_189){if(d._usesBorderBox(node)){var pb=d._getPadBorderExtents(node,_189);if(_187>=0){_187+=pb.w;}if(_188>=0){_188+=pb.h;}}d._setBox(node,NaN,NaN,_187,_188);};dojo._setMarginBox=function(node,_18a,_18b,_18c,_18d,_18e){var s=_18e||gcs(node),bb=d._usesBorderBox(node),pb=bb?_18f:d._getPadBorderExtents(node,s);if(d.isWebKit){if(d._isButtonTag(node)){var ns=node.style;if(_18c>=0&&!ns.width){ns.width="4px";}if(_18d>=0&&!ns.height){ns.height="4px";}}}var mb=d._getMarginExtents(node,s);if(_18c>=0){_18c=Math.max(_18c-pb.w-mb.w,0);}if(_18d>=0){_18d=Math.max(_18d-pb.h-mb.h,0);}d._setBox(node,_18a,_18b,_18c,_18d);};var _18f={l:0,t:0,w:0,h:0};dojo.marginBox=function(node,box){var n=byId(node),s=gcs(n),b=box;return !b?d._getMarginBox(n,s):d._setMarginBox(n,b.l,b.t,b.w,b.h,s);};dojo.contentBox=function(node,box){var n=byId(node),s=gcs(n),b=box;return !b?d._getContentBox(n,s):d._setContentSize(n,b.w,b.h,s);};var _190=function(node,prop){if(!(node=(node||0).parentNode)){return 0;}var val,_191=0,_192=d.body();while(node&&node.style){if(gcs(node).position=="fixed"){return 0;}val=node[prop];if(val){_191+=val-0;if(node==_192){break;}}node=node.parentNode;}return _191;};dojo._docScroll=function(){var n=d.global;return "pageXOffset" in n?{x:n.pageXOffset,y:n.pageYOffset}:(n=d.isQuirks?d.doc.body:d.doc.documentElement,{x:d._fixIeBiDiScrollLeft(n.scrollLeft||0),y:n.scrollTop||0});};dojo._isBodyLtr=function(){return "_bodyLtr" in d?d._bodyLtr:d._bodyLtr=(d.body().dir||d.doc.documentElement.dir||"ltr").toLowerCase()=="ltr";};dojo._getIeDocumentElementOffset=function(){var de=d.doc.documentElement;if(d.isIE<8){var r=de.getBoundingClientRect();var l=r.left,t=r.top;if(d.isIE<7){l+=de.clientLeft;t+=de.clientTop;}return {x:l<0?0:l,y:t<0?0:t};}else{return {x:0,y:0};}};dojo._fixIeBiDiScrollLeft=function(_193){var ie=d.isIE;if(ie&&!d._isBodyLtr()){var qk=d.isQuirks,de=qk?d.doc.body:d.doc.documentElement;if(ie==6&&!qk&&d.global.frameElement&&de.scrollHeight>de.clientHeight){_193+=de.clientLeft;}return (ie<8||qk)?(_193+de.clientWidth-de.scrollWidth):-_193;}return _193;};dojo._abs=dojo.position=function(node,_194){node=byId(node);var db=d.body(),dh=db.parentNode,ret=node.getBoundingClientRect();ret={x:ret.left,y:ret.top,w:ret.right-ret.left,h:ret.bottom-ret.top};if(d.isIE){var _195=d._getIeDocumentElementOffset();ret.x-=_195.x+(d.isQuirks?db.clientLeft+db.offsetLeft:0);ret.y-=_195.y+(d.isQuirks?db.clientTop+db.offsetTop:0);}else{if(d.isFF==3){var cs=gcs(dh);ret.x-=px(dh,cs.marginLeft)+px(dh,cs.borderLeftWidth);ret.y-=px(dh,cs.marginTop)+px(dh,cs.borderTopWidth);}}if(_194){var _196=d._docScroll();ret.x+=_196.x;ret.y+=_196.y;}return ret;};dojo.coords=function(node,_197){var n=byId(node),s=gcs(n),mb=d._getMarginBox(n,s);var abs=d.position(n,_197);mb.x=abs.x;mb.y=abs.y;return mb;};var _198={"class":"className","for":"htmlFor",tabindex:"tabIndex",readonly:"readOnly",colspan:"colSpan",frameborder:"frameBorder",rowspan:"rowSpan",valuetype:"valueType"},_199={classname:"class",htmlfor:"for",tabindex:"tabIndex",readonly:"readOnly"},_19a={innerHTML:1,className:1,htmlFor:d.isIE,value:1};var _19b=function(name){return _199[name.toLowerCase()]||name;};var _19c=function(node,name){var attr=node.getAttributeNode&&node.getAttributeNode(name);return attr&&attr.specified;};dojo.hasAttr=function(node,name){var lc=name.toLowerCase();return _19a[_198[lc]||name]||_19c(byId(node),_199[lc]||name);};var _19d={},_19e=0,_19f=dojo._scopeName+"attrid",_1a0={col:1,colgroup:1,table:1,tbody:1,tfoot:1,thead:1,tr:1,title:1};dojo.attr=function(node,name,_1a1){node=byId(node);var args=arguments.length,prop;if(args==2&&typeof name!="string"){for(var x in name){d.attr(node,x,name[x]);}return node;}var lc=name.toLowerCase(),_1a2=_198[lc]||name,_1a3=_19a[_1a2],_1a4=_199[lc]||name;if(args==3){do{if(_1a2=="style"&&typeof _1a1!="string"){d.style(node,_1a1);break;}if(_1a2=="innerHTML"){if(d.isIE&&node.tagName.toLowerCase() in _1a0){d.empty(node);node.appendChild(d._toDom(_1a1,node.ownerDocument));}else{node[_1a2]=_1a1;}break;}if(d.isFunction(_1a1)){var _1a5=d.attr(node,_19f);if(!_1a5){_1a5=_19e++;d.attr(node,_19f,_1a5);}if(!_19d[_1a5]){_19d[_1a5]={};}var h=_19d[_1a5][_1a2];if(h){d.disconnect(h);}else{try{delete node[_1a2];}catch(e){}}_19d[_1a5][_1a2]=d.connect(node,_1a2,_1a1);break;}if(_1a3||typeof _1a1=="boolean"){node[_1a2]=_1a1;break;}node.setAttribute(_1a4,_1a1);}while(false);return node;}_1a1=node[_1a2];if(_1a3&&typeof _1a1!="undefined"){return _1a1;}if(_1a2!="href"&&(typeof _1a1=="boolean"||d.isFunction(_1a1))){return _1a1;}return _19c(node,_1a4)?node.getAttribute(_1a4):null;};dojo.removeAttr=function(node,name){byId(node).removeAttribute(_19b(name));};dojo.getNodeProp=function(node,name){node=byId(node);var lc=name.toLowerCase(),_1a6=_198[lc]||name;if((_1a6 in node)&&_1a6!="href"){return node[_1a6];}var _1a7=_199[lc]||name;return _19c(node,_1a7)?node.getAttribute(_1a7):null;};dojo.create=function(tag,_1a8,_1a9,pos){var doc=d.doc;if(_1a9){_1a9=byId(_1a9);doc=_1a9.ownerDocument;}if(typeof tag=="string"){tag=doc.createElement(tag);}if(_1a8){d.attr(tag,_1a8);}if(_1a9){d.place(tag,_1a9,pos);}return tag;};d.empty=d.isIE?function(node){node=byId(node);for(var c;c=node.lastChild;){d.destroy(c);}}:function(node){byId(node).innerHTML="";};var _1aa={option:["select"],tbody:["table"],thead:["table"],tfoot:["table"],tr:["table","tbody"],td:["table","tbody","tr"],th:["table","thead","tr"],legend:["fieldset"],caption:["table"],colgroup:["table"],col:["table","colgroup"],li:["ul"]},_1ab=/<\s*([\w\:]+)/,_1ac={},_1ad=0,_1ae="__"+d._scopeName+"ToDomId";for(var _1af in _1aa){if(_1aa.hasOwnProperty(_1af)){var tw=_1aa[_1af];tw.pre=_1af=="option"?"<select multiple=\"multiple\">":"<"+tw.join("><")+">";tw.post="</"+tw.reverse().join("></")+">";}}d._toDom=function(frag,doc){doc=doc||d.doc;var _1b0=doc[_1ae];if(!_1b0){doc[_1ae]=_1b0=++_1ad+"";_1ac[_1b0]=doc.createElement("div");}frag+="";var _1b1=frag.match(_1ab),tag=_1b1?_1b1[1].toLowerCase():"",_1b2=_1ac[_1b0],wrap,i,fc,df;if(_1b1&&_1aa[tag]){wrap=_1aa[tag];_1b2.innerHTML=wrap.pre+frag+wrap.post;for(i=wrap.length;i;--i){_1b2=_1b2.firstChild;}}else{_1b2.innerHTML=frag;}if(_1b2.childNodes.length==1){return _1b2.removeChild(_1b2.firstChild);}df=doc.createDocumentFragment();while(fc=_1b2.firstChild){df.appendChild(fc);}return df;};var _1b3="className";dojo.hasClass=function(node,_1b4){return ((" "+byId(node)[_1b3]+" ").indexOf(" "+_1b4+" ")>=0);};var _1b5=/\s+/,a1=[""],_1b6={},_1b7=function(s){if(typeof s=="string"||s instanceof String){if(s.indexOf(" ")<0){a1[0]=s;return a1;}else{return s.split(_1b5);}}return s||"";};dojo.addClass=function(node,_1b8){node=byId(node);_1b8=_1b7(_1b8);var cls=node[_1b3],_1b9;cls=cls?" "+cls+" ":" ";_1b9=cls.length;for(var i=0,len=_1b8.length,c;i<len;++i){c=_1b8[i];if(c&&cls.indexOf(" "+c+" ")<0){cls+=c+" ";}}if(_1b9<cls.length){node[_1b3]=cls.substr(1,cls.length-2);}};dojo.removeClass=function(node,_1ba){node=byId(node);var cls;if(_1ba!==undefined){_1ba=_1b7(_1ba);cls=" "+node[_1b3]+" ";for(var i=0,len=_1ba.length;i<len;++i){cls=cls.replace(" "+_1ba[i]+" "," ");}cls=d.trim(cls);}else{cls="";}if(node[_1b3]!=cls){node[_1b3]=cls;}};dojo.replaceClass=function(node,_1bb,_1bc){node=byId(node);_1b6.className=node.className;dojo.removeClass(_1b6,_1bc);dojo.addClass(_1b6,_1bb);if(node.className!==_1b6.className){node.className=_1b6.className;}};dojo.toggleClass=function(node,_1bd,_1be){if(_1be===undefined){_1be=!d.hasClass(node,_1bd);}d[_1be?"addClass":"removeClass"](node,_1bd);};})();}if(!dojo._hasResource["dojo._base.NodeList"]){dojo._hasResource["dojo._base.NodeList"]=true;dojo.provide("dojo._base.NodeList");(function(){var d=dojo;var ap=Array.prototype,aps=ap.slice,apc=ap.concat;var tnl=function(a,_1bf,_1c0){if(!a.sort){a=aps.call(a,0);}var ctor=_1c0||this._NodeListCtor||d._NodeListCtor;a.constructor=ctor;dojo._mixin(a,ctor.prototype);a._NodeListCtor=ctor;return _1bf?a._stash(_1bf):a;};var _1c1=function(f,a,o){a=[0].concat(aps.call(a,0));o=o||d.global;return function(node){a[0]=node;return f.apply(o,a);};};var _1c2=function(f,o){return function(){this.forEach(_1c1(f,arguments,o));return this;};};var _1c3=function(f,o){return function(){return this.map(_1c1(f,arguments,o));};};var _1c4=function(f,o){return function(){return this.filter(_1c1(f,arguments,o));};};var _1c5=function(f,g,o){return function(){var a=arguments,body=_1c1(f,a,o);if(g.call(o||d.global,a)){return this.map(body);}this.forEach(body);return this;};};var _1c6=function(a){return a.length==1&&(typeof a[0]=="string");};var _1c7=function(node){var p=node.parentNode;if(p){p.removeChild(node);}};dojo.NodeList=function(){return tnl(Array.apply(null,arguments));};d._NodeListCtor=d.NodeList;var nl=d.NodeList,nlp=nl.prototype;nl._wrap=nlp._wrap=tnl;nl._adaptAsMap=_1c3;nl._adaptAsForEach=_1c2;nl._adaptAsFilter=_1c4;nl._adaptWithCondition=_1c5;d.forEach(["slice","splice"],function(name){var f=ap[name];nlp[name]=function(){return this._wrap(f.apply(this,arguments),name=="slice"?this:null);};});d.forEach(["indexOf","lastIndexOf","every","some"],function(name){var f=d[name];nlp[name]=function(){return f.apply(d,[this].concat(aps.call(arguments,0)));};});d.forEach(["attr","style"],function(name){nlp[name]=_1c5(d[name],_1c6);});d.forEach(["connect","addClass","removeClass","replaceClass","toggleClass","empty","removeAttr"],function(name){nlp[name]=_1c2(d[name]);});dojo.extend(dojo.NodeList,{_normalize:function(_1c8,_1c9){var _1ca=_1c8.parse===true?true:false;if(typeof _1c8.template=="string"){var _1cb=_1c8.templateFunc||(dojo.string&&dojo.string.substitute);_1c8=_1cb?_1cb(_1c8.template,_1c8):_1c8;}var type=(typeof _1c8);if(type=="string"||type=="number"){_1c8=dojo._toDom(_1c8,(_1c9&&_1c9.ownerDocument));if(_1c8.nodeType==11){_1c8=dojo._toArray(_1c8.childNodes);}else{_1c8=[_1c8];}}else{if(!dojo.isArrayLike(_1c8)){_1c8=[_1c8];}else{if(!dojo.isArray(_1c8)){_1c8=dojo._toArray(_1c8);}}}if(_1ca){_1c8._runParse=true;}return _1c8;},_cloneNode:function(node){return node.cloneNode(true);},_place:function(ary,_1cc,_1cd,_1ce){if(_1cc.nodeType!=1&&_1cd=="only"){return;}var _1cf=_1cc,_1d0;var _1d1=ary.length;for(var i=_1d1-1;i>=0;i--){var node=(_1ce?this._cloneNode(ary[i]):ary[i]);if(ary._runParse&&dojo.parser&&dojo.parser.parse){if(!_1d0){_1d0=_1cf.ownerDocument.createElement("div");}_1d0.appendChild(node);dojo.parser.parse(_1d0);node=_1d0.firstChild;while(_1d0.firstChild){_1d0.removeChild(_1d0.firstChild);}}if(i==_1d1-1){dojo.place(node,_1cf,_1cd);}else{_1cf.parentNode.insertBefore(node,_1cf);}_1cf=node;}},_stash:function(_1d2){this._parent=_1d2;return this;},end:function(){if(this._parent){return this._parent;}else{return new this._NodeListCtor();}},concat:function(item){var t=d.isArray(this)?this:aps.call(this,0),m=d.map(arguments,function(a){return a&&!d.isArray(a)&&(typeof NodeList!="undefined"&&a.constructor===NodeList||a.constructor===this._NodeListCtor)?aps.call(a,0):a;});return this._wrap(apc.apply(t,m),this);},map:function(func,obj){return this._wrap(d.map(this,func,obj),this);},forEach:function(_1d3,_1d4){d.forEach(this,_1d3,_1d4);return this;},coords:_1c3(d.coords),position:_1c3(d.position),place:function(_1d5,_1d6){var item=d.query(_1d5)[0];return this.forEach(function(node){d.place(node,item,_1d6);});},orphan:function(_1d7){return (_1d7?d._filterQueryResult(this,_1d7):this).forEach(_1c7);},adopt:function(_1d8,_1d9){return d.query(_1d8).place(this[0],_1d9)._stash(this);},query:function(_1da){if(!_1da){return this;}var ret=this.map(function(node){return d.query(_1da,node).filter(function(_1db){return _1db!==undefined;});});return this._wrap(apc.apply([],ret),this);},filter:function(_1dc){var a=arguments,_1dd=this,_1de=0;if(typeof _1dc=="string"){_1dd=d._filterQueryResult(this,a[0]);if(a.length==1){return _1dd._stash(this);}_1de=1;}return this._wrap(d.filter(_1dd,a[_1de],a[_1de+1]),this);},addContent:function(_1df,_1e0){_1df=this._normalize(_1df,this[0]);for(var i=0,node;(node=this[i]);i++){this._place(_1df,node,_1e0,i>0);}return this;},instantiate:function(_1e1,_1e2){var c=d.isFunction(_1e1)?_1e1:d.getObject(_1e1);_1e2=_1e2||{};return this.forEach(function(node){new c(_1e2,node);});},at:function(){var t=new this._NodeListCtor();d.forEach(arguments,function(i){if(i<0){i=this.length+i;}if(this[i]){t.push(this[i]);}},this);return t._stash(this);}});nl.events=["blur","focus","change","click","error","keydown","keypress","keyup","load","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","submit"];d.forEach(nl.events,function(evt){var _1e3="on"+evt;nlp[_1e3]=function(a,b){return this.connect(_1e3,a,b);};});})();}if(!dojo._hasResource["dojo._base.query"]){dojo._hasResource["dojo._base.query"]=true;(function(){var _1e4=function(d){var trim=d.trim;var each=d.forEach;var qlc=(d._NodeListCtor=d.NodeList);var _1e5=function(){return d.doc;};var _1e6=((d.isWebKit||d.isMozilla)&&((_1e5().compatMode)=="BackCompat"));var _1e7=!!_1e5().firstChild["children"]?"children":"childNodes";var _1e8=">~+";var _1e9=false;var _1ea=function(){return true;};var _1eb=function(_1ec){if(_1e8.indexOf(_1ec.slice(-1))>=0){_1ec+=" * ";}else{_1ec+=" ";}var ts=function(s,e){return trim(_1ec.slice(s,e));};var _1ed=[];var _1ee=-1,_1ef=-1,_1f0=-1,_1f1=-1,_1f2=-1,inId=-1,_1f3=-1,lc="",cc="",_1f4;var x=0,ql=_1ec.length,_1f5=null,_1f6=null;var _1f7=function(){if(_1f3>=0){var tv=(_1f3==x)?null:ts(_1f3,x);_1f5[(_1e8.indexOf(tv)<0)?"tag":"oper"]=tv;_1f3=-1;}};var _1f8=function(){if(inId>=0){_1f5.id=ts(inId,x).replace(/\\/g,"");inId=-1;}};var _1f9=function(){if(_1f2>=0){_1f5.classes.push(ts(_1f2+1,x).replace(/\\/g,""));_1f2=-1;}};var _1fa=function(){_1f8();_1f7();_1f9();};var _1fb=function(){_1fa();if(_1f1>=0){_1f5.pseudos.push({name:ts(_1f1+1,x)});}_1f5.loops=(_1f5.pseudos.length||_1f5.attrs.length||_1f5.classes.length);_1f5.oquery=_1f5.query=ts(_1f4,x);_1f5.otag=_1f5.tag=(_1f5["oper"])?null:(_1f5.tag||"*");if(_1f5.tag){_1f5.tag=_1f5.tag.toUpperCase();}if(_1ed.length&&(_1ed[_1ed.length-1].oper)){_1f5.infixOper=_1ed.pop();_1f5.query=_1f5.infixOper.query+" "+_1f5.query;}_1ed.push(_1f5);_1f5=null;};for(;lc=cc,cc=_1ec.charAt(x),x<ql;x++){if(lc=="\\"){continue;}if(!_1f5){_1f4=x;_1f5={query:null,pseudos:[],attrs:[],classes:[],tag:null,oper:null,id:null,getTag:function(){return (_1e9)?this.otag:this.tag;}};_1f3=x;}if(_1ee>=0){if(cc=="]"){if(!_1f6.attr){_1f6.attr=ts(_1ee+1,x);}else{_1f6.matchFor=ts((_1f0||_1ee+1),x);}var cmf=_1f6.matchFor;if(cmf){if((cmf.charAt(0)=="\"")||(cmf.charAt(0)=="'")){_1f6.matchFor=cmf.slice(1,-1);}}_1f5.attrs.push(_1f6);_1f6=null;_1ee=_1f0=-1;}else{if(cc=="="){var _1fc=("|~^$*".indexOf(lc)>=0)?lc:"";_1f6.type=_1fc+cc;_1f6.attr=ts(_1ee+1,x-_1fc.length);_1f0=x+1;}}}else{if(_1ef>=0){if(cc==")"){if(_1f1>=0){_1f6.value=ts(_1ef+1,x);}_1f1=_1ef=-1;}}else{if(cc=="#"){_1fa();inId=x+1;}else{if(cc=="."){_1fa();_1f2=x;}else{if(cc==":"){_1fa();_1f1=x;}else{if(cc=="["){_1fa();_1ee=x;_1f6={};}else{if(cc=="("){if(_1f1>=0){_1f6={name:ts(_1f1+1,x),value:null};_1f5.pseudos.push(_1f6);}_1ef=x;}else{if((cc==" ")&&(lc!=cc)){_1fb();}}}}}}}}}return _1ed;};var _1fd=function(_1fe,_1ff){if(!_1fe){return _1ff;}if(!_1ff){return _1fe;}return function(){return _1fe.apply(window,arguments)&&_1ff.apply(window,arguments);};};var _200=function(i,arr){var r=arr||[];if(i){r.push(i);}return r;};var _201=function(n){return (1==n.nodeType);};var _202="";var _203=function(elem,attr){if(!elem){return _202;}if(attr=="class"){return elem.className||_202;}if(attr=="for"){return elem.htmlFor||_202;}if(attr=="style"){return elem.style.cssText||_202;}return (_1e9?elem.getAttribute(attr):elem.getAttribute(attr,2))||_202;};var _204={"*=":function(attr,_205){return function(elem){return (_203(elem,attr).indexOf(_205)>=0);};},"^=":function(attr,_206){return function(elem){return (_203(elem,attr).indexOf(_206)==0);};},"$=":function(attr,_207){var tval=" "+_207;return function(elem){var ea=" "+_203(elem,attr);return (ea.lastIndexOf(_207)==(ea.length-_207.length));};},"~=":function(attr,_208){var tval=" "+_208+" ";return function(elem){var ea=" "+_203(elem,attr)+" ";return (ea.indexOf(tval)>=0);};},"|=":function(attr,_209){var _20a=" "+_209+"-";return function(elem){var ea=" "+_203(elem,attr);return ((ea==_209)||(ea.indexOf(_20a)==0));};},"=":function(attr,_20b){return function(elem){return (_203(elem,attr)==_20b);};}};var _20c=(typeof _1e5().firstChild.nextElementSibling=="undefined");var _20d=!_20c?"nextElementSibling":"nextSibling";var _20e=!_20c?"previousElementSibling":"previousSibling";var _20f=(_20c?_201:_1ea);var _210=function(node){while(node=node[_20e]){if(_20f(node)){return false;}}return true;};var _211=function(node){while(node=node[_20d]){if(_20f(node)){return false;}}return true;};var _212=function(node){var root=node.parentNode;var i=0,tret=root[_1e7],ci=(node["_i"]||-1),cl=(root["_l"]||-1);if(!tret){return -1;}var l=tret.length;if(cl==l&&ci>=0&&cl>=0){return ci;}root["_l"]=l;ci=-1;for(var te=root["firstElementChild"]||root["firstChild"];te;te=te[_20d]){if(_20f(te)){te["_i"]=++i;if(node===te){ci=i;}}}return ci;};var _213=function(elem){return !((_212(elem))%2);};var _214=function(elem){return ((_212(elem))%2);};var _215={"checked":function(name,_216){return function(elem){return !!("checked" in elem?elem.checked:elem.selected);};},"first-child":function(){return _210;},"last-child":function(){return _211;},"only-child":function(name,_217){return function(node){if(!_210(node)){return false;}if(!_211(node)){return false;}return true;};},"empty":function(name,_218){return function(elem){var cn=elem.childNodes;var cnl=elem.childNodes.length;for(var x=cnl-1;x>=0;x--){var nt=cn[x].nodeType;if((nt===1)||(nt==3)){return false;}}return true;};},"contains":function(name,_219){var cz=_219.charAt(0);if(cz=="\""||cz=="'"){_219=_219.slice(1,-1);}return function(elem){return (elem.innerHTML.indexOf(_219)>=0);};},"not":function(name,_21a){var p=_1eb(_21a)[0];var _21b={el:1};if(p.tag!="*"){_21b.tag=1;}if(!p.classes.length){_21b.classes=1;}var ntf=_21c(p,_21b);return function(elem){return (!ntf(elem));};},"nth-child":function(name,_21d){var pi=parseInt;if(_21d=="odd"){return _214;}else{if(_21d=="even"){return _213;}}if(_21d.indexOf("n")!=-1){var _21e=_21d.split("n",2);var pred=_21e[0]?((_21e[0]=="-")?-1:pi(_21e[0])):1;var idx=_21e[1]?pi(_21e[1]):0;var lb=0,ub=-1;if(pred>0){if(idx<0){idx=(idx%pred)&&(pred+(idx%pred));}else{if(idx>0){if(idx>=pred){lb=idx-idx%pred;}idx=idx%pred;}}}else{if(pred<0){pred*=-1;if(idx>0){ub=idx;idx=idx%pred;}}}if(pred>0){return function(elem){var i=_212(elem);return (i>=lb)&&(ub<0||i<=ub)&&((i%pred)==idx);};}else{_21d=idx;}}var _21f=pi(_21d);return function(elem){return (_212(elem)==_21f);};}};var _220=(d.isIE<9||(dojo.isIE&&dojo.isQuirks))?function(cond){var clc=cond.toLowerCase();if(clc=="class"){cond="className";}return function(elem){return (_1e9?elem.getAttribute(cond):elem[cond]||elem[clc]);};}:function(cond){return function(elem){return (elem&&elem.getAttribute&&elem.hasAttribute(cond));};};var _21c=function(_221,_222){if(!_221){return _1ea;}_222=_222||{};var ff=null;if(!("el" in _222)){ff=_1fd(ff,_201);}if(!("tag" in _222)){if(_221.tag!="*"){ff=_1fd(ff,function(elem){return (elem&&(elem.tagName==_221.getTag()));});}}if(!("classes" in _222)){each(_221.classes,function(_223,idx,arr){var re=new RegExp("(?:^|\\s)"+_223+"(?:\\s|$)");ff=_1fd(ff,function(elem){return re.test(elem.className);});ff.count=idx;});}if(!("pseudos" in _222)){each(_221.pseudos,function(_224){var pn=_224.name;if(_215[pn]){ff=_1fd(ff,_215[pn](pn,_224.value));}});}if(!("attrs" in _222)){each(_221.attrs,function(attr){var _225;var a=attr.attr;if(attr.type&&_204[attr.type]){_225=_204[attr.type](a,attr.matchFor);}else{if(a.length){_225=_220(a);}}if(_225){ff=_1fd(ff,_225);}});}if(!("id" in _222)){if(_221.id){ff=_1fd(ff,function(elem){return (!!elem&&(elem.id==_221.id));});}}if(!ff){if(!("default" in _222)){ff=_1ea;}}return ff;};var _226=function(_227){return function(node,ret,bag){while(node=node[_20d]){if(_20c&&(!_201(node))){continue;}if((!bag||_228(node,bag))&&_227(node)){ret.push(node);}break;}return ret;};};var _229=function(_22a){return function(root,ret,bag){var te=root[_20d];while(te){if(_20f(te)){if(bag&&!_228(te,bag)){break;}if(_22a(te)){ret.push(te);}}te=te[_20d];}return ret;};};var _22b=function(_22c){_22c=_22c||_1ea;return function(root,ret,bag){var te,x=0,tret=root[_1e7];while(te=tret[x++]){if(_20f(te)&&(!bag||_228(te,bag))&&(_22c(te,x))){ret.push(te);}}return ret;};};var _22d=function(node,root){var pn=node.parentNode;while(pn){if(pn==root){break;}pn=pn.parentNode;}return !!pn;};var _22e={};var _22f=function(_230){var _231=_22e[_230.query];if(_231){return _231;}var io=_230.infixOper;var oper=(io?io.oper:"");var _232=_21c(_230,{el:1});var qt=_230.tag;var _233=("*"==qt);var ecs=_1e5()["getElementsByClassName"];if(!oper){if(_230.id){_232=(!_230.loops&&_233)?_1ea:_21c(_230,{el:1,id:1});_231=function(root,arr){var te=d.byId(_230.id,(root.ownerDocument||root));if(!te||!_232(te)){return;}if(9==root.nodeType){return _200(te,arr);}else{if(_22d(te,root)){return _200(te,arr);}}};}else{if(ecs&&/\{\s*\[native code\]\s*\}/.test(String(ecs))&&_230.classes.length&&!_1e6){_232=_21c(_230,{el:1,classes:1,id:1});var _234=_230.classes.join(" ");_231=function(root,arr,bag){var ret=_200(0,arr),te,x=0;var tret=root.getElementsByClassName(_234);while((te=tret[x++])){if(_232(te,root)&&_228(te,bag)){ret.push(te);}}return ret;};}else{if(!_233&&!_230.loops){_231=function(root,arr,bag){var ret=_200(0,arr),te,x=0;var tret=root.getElementsByTagName(_230.getTag());while((te=tret[x++])){if(_228(te,bag)){ret.push(te);}}return ret;};}else{_232=_21c(_230,{el:1,tag:1,id:1});_231=function(root,arr,bag){var ret=_200(0,arr),te,x=0;var tret=root.getElementsByTagName(_230.getTag());while((te=tret[x++])){if(_232(te,root)&&_228(te,bag)){ret.push(te);}}return ret;};}}}}else{var _235={el:1};if(_233){_235.tag=1;}_232=_21c(_230,_235);if("+"==oper){_231=_226(_232);}else{if("~"==oper){_231=_229(_232);}else{if(">"==oper){_231=_22b(_232);}}}}return _22e[_230.query]=_231;};var _236=function(root,_237){var _238=_200(root),qp,x,te,qpl=_237.length,bag,ret;for(var i=0;i<qpl;i++){ret=[];qp=_237[i];x=_238.length-1;if(x>0){bag={};ret.nozip=true;}var gef=_22f(qp);for(var j=0;(te=_238[j]);j++){gef(te,ret,bag);}if(!ret.length){break;}_238=ret;}return ret;};var _239={},_23a={};var _23b=function(_23c){var _23d=_1eb(trim(_23c));if(_23d.length==1){var tef=_22f(_23d[0]);return function(root){var r=tef(root,new qlc());if(r){r.nozip=true;}return r;};}return function(root){return _236(root,_23d);};};var nua=navigator.userAgent;var wk="WebKit/";var _23e=(d.isWebKit&&(nua.indexOf(wk)>0)&&(parseFloat(nua.split(wk)[1])>528));var _23f=d.isIE?"commentStrip":"nozip";var qsa="querySelectorAll";var _240=(!!_1e5()[qsa]&&(!d.isSafari||(d.isSafari>3.1)||_23e));var _241=/n\+\d|([^ ])?([>~+])([^ =])?/g;var _242=function(_243,pre,ch,post){return ch?(pre?pre+" ":"")+ch+(post?" "+post:""):_243;};var _244=function(_245,_246){_245=_245.replace(_241,_242);if(_240){var _247=_23a[_245];if(_247&&!_246){return _247;}}var _248=_239[_245];if(_248){return _248;}var qcz=_245.charAt(0);var _249=(-1==_245.indexOf(" "));if((_245.indexOf("#")>=0)&&(_249)){_246=true;}var _24a=(_240&&(!_246)&&(_1e8.indexOf(qcz)==-1)&&(!d.isIE||(_245.indexOf(":")==-1))&&(!(_1e6&&(_245.indexOf(".")>=0)))&&(_245.indexOf(":contains")==-1)&&(_245.indexOf(":checked")==-1)&&(_245.indexOf("|=")==-1));if(_24a){var tq=(_1e8.indexOf(_245.charAt(_245.length-1))>=0)?(_245+" *"):_245;return _23a[_245]=function(root){try{if(!((9==root.nodeType)||_249)){throw "";}var r=root[qsa](tq);r[_23f]=true;return r;}catch(e){return _244(_245,true)(root);}};}else{var _24b=_245.split(/\s*,\s*/);return _239[_245]=((_24b.length<2)?_23b(_245):function(root){var _24c=0,ret=[],tp;while((tp=_24b[_24c++])){ret=ret.concat(_23b(tp)(root));}return ret;});}};var _24d=0;var _24e=d.isIE?function(node){if(_1e9){return (node.getAttribute("_uid")||node.setAttribute("_uid",++_24d)||_24d);}else{return node.uniqueID;}}:function(node){return (node._uid||(node._uid=++_24d));};var _228=function(node,bag){if(!bag){return 1;}var id=_24e(node);if(!bag[id]){return bag[id]=1;}return 0;};var _24f="_zipIdx";var _250=function(arr){if(arr&&arr.nozip){return (qlc._wrap)?qlc._wrap(arr):arr;}var ret=new qlc();if(!arr||!arr.length){return ret;}if(arr[0]){ret.push(arr[0]);}if(arr.length<2){return ret;}_24d++;if(d.isIE&&_1e9){var _251=_24d+"";arr[0].setAttribute(_24f,_251);for(var x=1,te;te=arr[x];x++){if(arr[x].getAttribute(_24f)!=_251){ret.push(te);}te.setAttribute(_24f,_251);}}else{if(d.isIE&&arr.commentStrip){try{for(var x=1,te;te=arr[x];x++){if(_201(te)){ret.push(te);}}}catch(e){}}else{if(arr[0]){arr[0][_24f]=_24d;}for(var x=1,te;te=arr[x];x++){if(arr[x][_24f]!=_24d){ret.push(te);}te[_24f]=_24d;}}}return ret;};d.query=function(_252,root){qlc=d._NodeListCtor;if(!_252){return new qlc();}if(_252.constructor==qlc){return _252;}if(typeof _252!="string"){return new qlc(_252);}if(typeof root=="string"){root=d.byId(root);if(!root){return new qlc();}}root=root||_1e5();var od=root.ownerDocument||root.documentElement;_1e9=(root.contentType&&root.contentType=="application/xml")||(d.isOpera&&(root.doctype||od.toString()=="[object XMLDocument]"))||(!!od)&&(d.isIE?od.xml:(root.xmlVersion||od.xmlVersion));var r=_244(_252)(root);if(r&&r.nozip&&!qlc._wrap){return r;}return _250(r);};d.query.pseudos=_215;d._filterQueryResult=function(_253,_254,root){var _255=new d._NodeListCtor(),_256=_1eb(_254),_257=(_256.length==1&&!/[^\w#\.]/.test(_254))?_21c(_256[0]):function(node){return dojo.query(_254,root).indexOf(node)!=-1;};for(var x=0,te;te=_253[x];x++){if(_257(te)){_255.push(te);}}return _255;};};var _258=function(){acme={trim:function(str){str=str.replace(/^\s+/,"");for(var i=str.length-1;i>=0;i--){if(/\S/.test(str.charAt(i))){str=str.substring(0,i+1);break;}}return str;},forEach:function(arr,_259,_25a){if(!arr||!arr.length){return;}for(var i=0,l=arr.length;i<l;++i){_259.call(_25a||window,arr[i],i,arr);}},byId:function(id,doc){if(typeof id=="string"){return (doc||document).getElementById(id);}else{return id;}},doc:document,NodeList:Array};var n=navigator;var dua=n.userAgent;var dav=n.appVersion;var tv=parseFloat(dav);acme.isOpera=(dua.indexOf("Opera")>=0)?tv:undefined;acme.isKhtml=(dav.indexOf("Konqueror")>=0)?tv:undefined;acme.isWebKit=parseFloat(dua.split("WebKit/")[1])||undefined;acme.isChrome=parseFloat(dua.split("Chrome/")[1])||undefined;var _25b=Math.max(dav.indexOf("WebKit"),dav.indexOf("Safari"),0);if(_25b&&!acme.isChrome){acme.isSafari=parseFloat(dav.split("Version/")[1]);if(!acme.isSafari||parseFloat(dav.substr(_25b+7))<=419.3){acme.isSafari=2;}}if(document.all&&!acme.isOpera){acme.isIE=parseFloat(dav.split("MSIE ")[1])||undefined;}Array._wrap=function(arr){return arr;};return acme;};if(this["dojo"]){dojo.provide("dojo._base.query");_1e4(this["queryPortability"]||this["acme"]||dojo);}else{_1e4(this["queryPortability"]||this["acme"]||_258());}})();}if(!dojo._hasResource["dojo._base.xhr"]){dojo._hasResource["dojo._base.xhr"]=true;dojo.provide("dojo._base.xhr");(function(){var _25c=dojo,cfg=_25c.config;function _25d(obj,name,_25e){if(_25e===null){return;}var val=obj[name];if(typeof val=="string"){obj[name]=[val,_25e];}else{if(_25c.isArray(val)){val.push(_25e);}else{obj[name]=_25e;}}};dojo.fieldToObject=function(_25f){var ret=null;var item=_25c.byId(_25f);if(item){var _260=item.name;var type=(item.type||"").toLowerCase();if(_260&&type&&!item.disabled){if(type=="radio"||type=="checkbox"){if(item.checked){ret=item.value;}}else{if(item.multiple){ret=[];_25c.query("option",item).forEach(function(opt){if(opt.selected){ret.push(opt.value);}});}else{ret=item.value;}}}}return ret;};dojo.formToObject=function(_261){var ret={};var _262="file|submit|image|reset|button|";_25c.forEach(dojo.byId(_261).elements,function(item){var _263=item.name;var type=(item.type||"").toLowerCase();if(_263&&type&&_262.indexOf(type)==-1&&!item.disabled){_25d(ret,_263,_25c.fieldToObject(item));if(type=="image"){ret[_263+".x"]=ret[_263+".y"]=ret[_263].x=ret[_263].y=0;}}});return ret;};dojo.objectToQuery=function(map){var enc=encodeURIComponent;var _264=[];var _265={};for(var name in map){var _266=map[name];if(_266!=_265[name]){var _267=enc(name)+"=";if(_25c.isArray(_266)){for(var i=0;i<_266.length;i++){_264.push(_267+enc(_266[i]));}}else{_264.push(_267+enc(_266));}}}return _264.join("&");};dojo.formToQuery=function(_268){return _25c.objectToQuery(_25c.formToObject(_268));};dojo.formToJson=function(_269,_26a){return _25c.toJson(_25c.formToObject(_269),_26a);};dojo.queryToObject=function(str){var ret={};var qp=str.split("&");var dec=decodeURIComponent;_25c.forEach(qp,function(item){if(item.length){var _26b=item.split("=");var name=dec(_26b.shift());var val=dec(_26b.join("="));if(typeof ret[name]=="string"){ret[name]=[ret[name]];}if(_25c.isArray(ret[name])){ret[name].push(val);}else{ret[name]=val;}}});return ret;};dojo._blockAsync=false;var _26c=_25c._contentHandlers=dojo.contentHandlers={text:function(xhr){return xhr.responseText;},json:function(xhr){return _25c.fromJson(xhr.responseText||null);},"json-comment-filtered":function(xhr){if(!dojo.config.useCommentedJson){console.warn("Consider using the standard mimetype:application/json."+" json-commenting can introduce security issues. To"+" decrease the chances of hijacking, use the standard the 'json' handler and"+" prefix your json with: {}&&\n"+"Use djConfig.useCommentedJson=true to turn off this message.");}var _26d=xhr.responseText;var _26e=_26d.indexOf("/*");var _26f=_26d.lastIndexOf("*/");if(_26e==-1||_26f==-1){throw new Error("JSON was not comment filtered");}return _25c.fromJson(_26d.substring(_26e+2,_26f));},javascript:function(xhr){return _25c.eval(xhr.responseText);},xml:function(xhr){var _270=xhr.responseXML;if(_25c.isIE&&(!_270||!_270.documentElement)){var ms=function(n){return "MSXML"+n+".DOMDocument";};var dp=["Microsoft.XMLDOM",ms(6),ms(4),ms(3),ms(2)];_25c.some(dp,function(p){try{var dom=new ActiveXObject(p);dom.async=false;dom.loadXML(xhr.responseText);_270=dom;}catch(e){return false;}return true;});}return _270;},"json-comment-optional":function(xhr){if(xhr.responseText&&/^[^{\[]*\/\*/.test(xhr.responseText)){return _26c["json-comment-filtered"](xhr);}else{return _26c["json"](xhr);}}};dojo._ioSetArgs=function(args,_271,_272,_273){var _274={args:args,url:args.url};var _275=null;if(args.form){var form=_25c.byId(args.form);var _276=form.getAttributeNode("action");_274.url=_274.url||(_276?_276.value:null);_275=_25c.formToObject(form);}var _277=[{}];if(_275){_277.push(_275);}if(args.content){_277.push(args.content);}if(args.preventCache){_277.push({"dojo.preventCache":new Date().valueOf()});}_274.query=_25c.objectToQuery(_25c.mixin.apply(null,_277));_274.handleAs=args.handleAs||"text";var d=new _25c.Deferred(_271);d.addCallbacks(_272,function(_278){return _273(_278,d);});var ld=args.load;if(ld&&_25c.isFunction(ld)){d.addCallback(function(_279){return ld.call(args,_279,_274);});}var err=args.error;if(err&&_25c.isFunction(err)){d.addErrback(function(_27a){return err.call(args,_27a,_274);});}var _27b=args.handle;if(_27b&&_25c.isFunction(_27b)){d.addBoth(function(_27c){return _27b.call(args,_27c,_274);});}if(cfg.ioPublish&&_25c.publish&&_274.args.ioPublish!==false){d.addCallbacks(function(res){_25c.publish("/dojo/io/load",[d,res]);return res;},function(res){_25c.publish("/dojo/io/error",[d,res]);return res;});d.addBoth(function(res){_25c.publish("/dojo/io/done",[d,res]);return res;});}d.ioArgs=_274;return d;};var _27d=function(dfd){dfd.canceled=true;var xhr=dfd.ioArgs.xhr;var _27e=typeof xhr.abort;if(_27e=="function"||_27e=="object"||_27e=="unknown"){xhr.abort();}var err=dfd.ioArgs.error;if(!err){err=new Error("xhr cancelled");err.dojoType="cancel";}return err;};var _27f=function(dfd){var ret=_26c[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);return ret===undefined?null:ret;};var _280=function(_281,dfd){if(!dfd.ioArgs.args.failOk){console.error(_281);}return _281;};var _282=null;var _283=[];var _284=0;var _285=function(dfd){if(_284<=0){_284=0;if(cfg.ioPublish&&_25c.publish&&(!dfd||dfd&&dfd.ioArgs.args.ioPublish!==false)){_25c.publish("/dojo/io/stop");}}};var _286=function(){var now=(new Date()).getTime();if(!_25c._blockAsync){for(var i=0,tif;i<_283.length&&(tif=_283[i]);i++){var dfd=tif.dfd;var func=function(){if(!dfd||dfd.canceled||!tif.validCheck(dfd)){_283.splice(i--,1);_284-=1;}else{if(tif.ioCheck(dfd)){_283.splice(i--,1);tif.resHandle(dfd);_284-=1;}else{if(dfd.startTime){if(dfd.startTime+(dfd.ioArgs.args.timeout||0)<now){_283.splice(i--,1);var err=new Error("timeout exceeded");err.dojoType="timeout";dfd.errback(err);dfd.cancel();_284-=1;}}}}};if(dojo.config.debugAtAllCosts){func.call(this);}else{try{func.call(this);}catch(e){dfd.errback(e);}}}}_285(dfd);if(!_283.length){clearInterval(_282);_282=null;return;}};dojo._ioCancelAll=function(){try{_25c.forEach(_283,function(i){try{i.dfd.cancel();}catch(e){}});}catch(e){}};if(_25c.isIE){_25c.addOnWindowUnload(_25c._ioCancelAll);}_25c._ioNotifyStart=function(dfd){if(cfg.ioPublish&&_25c.publish&&dfd.ioArgs.args.ioPublish!==false){if(!_284){_25c.publish("/dojo/io/start");}_284+=1;_25c.publish("/dojo/io/send",[dfd]);}};_25c._ioWatch=function(dfd,_287,_288,_289){var args=dfd.ioArgs.args;if(args.timeout){dfd.startTime=(new Date()).getTime();}_283.push({dfd:dfd,validCheck:_287,ioCheck:_288,resHandle:_289});if(!_282){_282=setInterval(_286,50);}if(args.sync){_286();}};var _28a="application/x-www-form-urlencoded";var _28b=function(dfd){return dfd.ioArgs.xhr.readyState;};var _28c=function(dfd){return 4==dfd.ioArgs.xhr.readyState;};var _28d=function(dfd){var xhr=dfd.ioArgs.xhr;if(_25c._isDocumentOk(xhr)){dfd.callback(dfd);}else{var err=new Error("Unable to load "+dfd.ioArgs.url+" status:"+xhr.status);err.status=xhr.status;err.responseText=xhr.responseText;dfd.errback(err);}};dojo._ioAddQueryToUrl=function(_28e){if(_28e.query.length){_28e.url+=(_28e.url.indexOf("?")==-1?"?":"&")+_28e.query;_28e.query=null;}};dojo.xhr=function(_28f,args,_290){var dfd=_25c._ioSetArgs(args,_27d,_27f,_280);var _291=dfd.ioArgs;var xhr=_291.xhr=_25c._xhrObj(_291.args);if(!xhr){dfd.cancel();return dfd;}if("postData" in args){_291.query=args.postData;}else{if("putData" in args){_291.query=args.putData;}else{if("rawBody" in args){_291.query=args.rawBody;}else{if((arguments.length>2&&!_290)||"POST|PUT".indexOf(_28f.toUpperCase())==-1){_25c._ioAddQueryToUrl(_291);}}}}xhr.open(_28f,_291.url,args.sync!==true,args.user||undefined,args.password||undefined);if(args.headers){for(var hdr in args.headers){if(hdr.toLowerCase()==="content-type"&&!args.contentType){args.contentType=args.headers[hdr];}else{if(args.headers[hdr]){xhr.setRequestHeader(hdr,args.headers[hdr]);}}}}xhr.setRequestHeader("Content-Type",args.contentType||_28a);if(!args.headers||!("X-Requested-With" in args.headers)){xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");}_25c._ioNotifyStart(dfd);if(dojo.config.debugAtAllCosts){xhr.send(_291.query);}else{try{xhr.send(_291.query);}catch(e){_291.error=e;dfd.cancel();}}_25c._ioWatch(dfd,_28b,_28c,_28d);xhr=null;return dfd;};dojo.xhrGet=function(args){return _25c.xhr("GET",args);};dojo.rawXhrPost=dojo.xhrPost=function(args){return _25c.xhr("POST",args,true);};dojo.rawXhrPut=dojo.xhrPut=function(args){return _25c.xhr("PUT",args,true);};dojo.xhrDelete=function(args){return _25c.xhr("DELETE",args);};})();}if(!dojo._hasResource["dojo._base.fx"]){dojo._hasResource["dojo._base.fx"]=true;dojo.provide("dojo._base.fx");(function(){var d=dojo;var _292=d._mixin;dojo._Line=function(_293,end){this.start=_293;this.end=end;};dojo._Line.prototype.getValue=function(n){return ((this.end-this.start)*n)+this.start;};dojo.Animation=function(args){_292(this,args);if(d.isArray(this.curve)){this.curve=new d._Line(this.curve[0],this.curve[1]);}};d._Animation=d.Animation;d.extend(dojo.Animation,{duration:350,repeat:0,rate:20,_percent:0,_startRepeatCount:0,_getStep:function(){var _294=this._percent,_295=this.easing;return _295?_295(_294):_294;},_fire:function(evt,args){var a=args||[];if(this[evt]){if(d.config.debugAtAllCosts){this[evt].apply(this,a);}else{try{this[evt].apply(this,a);}catch(e){console.error("exception in animation handler for:",evt);console.error(e);}}}return this;},play:function(_296,_297){var _298=this;if(_298._delayTimer){_298._clearTimer();}if(_297){_298._stopTimer();_298._active=_298._paused=false;_298._percent=0;}else{if(_298._active&&!_298._paused){return _298;}}_298._fire("beforeBegin",[_298.node]);var de=_296||_298.delay,_299=dojo.hitch(_298,"_play",_297);if(de>0){_298._delayTimer=setTimeout(_299,de);return _298;}_299();return _298;},_play:function(_29a){var _29b=this;if(_29b._delayTimer){_29b._clearTimer();}_29b._startTime=new Date().valueOf();if(_29b._paused){_29b._startTime-=_29b.duration*_29b._percent;}_29b._active=true;_29b._paused=false;var _29c=_29b.curve.getValue(_29b._getStep());if(!_29b._percent){if(!_29b._startRepeatCount){_29b._startRepeatCount=_29b.repeat;}_29b._fire("onBegin",[_29c]);}_29b._fire("onPlay",[_29c]);_29b._cycle();return _29b;},pause:function(){var _29d=this;if(_29d._delayTimer){_29d._clearTimer();}_29d._stopTimer();if(!_29d._active){return _29d;}_29d._paused=true;_29d._fire("onPause",[_29d.curve.getValue(_29d._getStep())]);return _29d;},gotoPercent:function(_29e,_29f){var _2a0=this;_2a0._stopTimer();_2a0._active=_2a0._paused=true;_2a0._percent=_29e;if(_29f){_2a0.play();}return _2a0;},stop:function(_2a1){var _2a2=this;if(_2a2._delayTimer){_2a2._clearTimer();}if(!_2a2._timer){return _2a2;}_2a2._stopTimer();if(_2a1){_2a2._percent=1;}_2a2._fire("onStop",[_2a2.curve.getValue(_2a2._getStep())]);_2a2._active=_2a2._paused=false;return _2a2;},status:function(){if(this._active){return this._paused?"paused":"playing";}return "stopped";},_cycle:function(){var _2a3=this;if(_2a3._active){var curr=new Date().valueOf();var step=(curr-_2a3._startTime)/(_2a3.duration);if(step>=1){step=1;}_2a3._percent=step;if(_2a3.easing){step=_2a3.easing(step);}_2a3._fire("onAnimate",[_2a3.curve.getValue(step)]);if(_2a3._percent<1){_2a3._startTimer();}else{_2a3._active=false;if(_2a3.repeat>0){_2a3.repeat--;_2a3.play(null,true);}else{if(_2a3.repeat==-1){_2a3.play(null,true);}else{if(_2a3._startRepeatCount){_2a3.repeat=_2a3._startRepeatCount;_2a3._startRepeatCount=0;}}}_2a3._percent=0;_2a3._fire("onEnd",[_2a3.node]);!_2a3.repeat&&_2a3._stopTimer();}}return _2a3;},_clearTimer:function(){clearTimeout(this._delayTimer);delete this._delayTimer;}});var ctr=0,_2a4=null,_2a5={run:function(){}};d.extend(d.Animation,{_startTimer:function(){if(!this._timer){this._timer=d.connect(_2a5,"run",this,"_cycle");ctr++;}if(!_2a4){_2a4=setInterval(d.hitch(_2a5,"run"),this.rate);}},_stopTimer:function(){if(this._timer){d.disconnect(this._timer);this._timer=null;ctr--;}if(ctr<=0){clearInterval(_2a4);_2a4=null;ctr=0;}}});var _2a6=d.isIE?function(node){var ns=node.style;if(!ns.width.length&&d.style(node,"width")=="auto"){ns.width="auto";}}:function(){};dojo._fade=function(args){args.node=d.byId(args.node);var _2a7=_292({properties:{}},args),_2a8=(_2a7.properties.opacity={});_2a8.start=!("start" in _2a7)?function(){return +d.style(_2a7.node,"opacity")||0;}:_2a7.start;_2a8.end=_2a7.end;var anim=d.animateProperty(_2a7);d.connect(anim,"beforeBegin",d.partial(_2a6,_2a7.node));return anim;};dojo.fadeIn=function(args){return d._fade(_292({end:1},args));};dojo.fadeOut=function(args){return d._fade(_292({end:0},args));};dojo._defaultEasing=function(n){return 0.5+((Math.sin((n+1.5)*Math.PI))/2);};var _2a9=function(_2aa){this._properties=_2aa;for(var p in _2aa){var prop=_2aa[p];if(prop.start instanceof d.Color){prop.tempColor=new d.Color();}}};_2a9.prototype.getValue=function(r){var ret={};for(var p in this._properties){var prop=this._properties[p],_2ab=prop.start;if(_2ab instanceof d.Color){ret[p]=d.blendColors(_2ab,prop.end,r,prop.tempColor).toCss();}else{if(!d.isArray(_2ab)){ret[p]=((prop.end-_2ab)*r)+_2ab+(p!="opacity"?prop.units||"px":0);}}}return ret;};dojo.animateProperty=function(args){var n=args.node=d.byId(args.node);if(!args.easing){args.easing=d._defaultEasing;}var anim=new d.Animation(args);d.connect(anim,"beforeBegin",anim,function(){var pm={};for(var p in this.properties){if(p=="width"||p=="height"){this.node.display="block";}var prop=this.properties[p];if(d.isFunction(prop)){prop=prop(n);}prop=pm[p]=_292({},(d.isObject(prop)?prop:{end:prop}));if(d.isFunction(prop.start)){prop.start=prop.start(n);}if(d.isFunction(prop.end)){prop.end=prop.end(n);}var _2ac=(p.toLowerCase().indexOf("color")>=0);function _2ad(node,p){var v={height:node.offsetHeight,width:node.offsetWidth}[p];if(v!==undefined){return v;}v=d.style(node,p);return (p=="opacity")?+v:(_2ac?v:parseFloat(v));};if(!("end" in prop)){prop.end=_2ad(n,p);}else{if(!("start" in prop)){prop.start=_2ad(n,p);}}if(_2ac){prop.start=new d.Color(prop.start);prop.end=new d.Color(prop.end);}else{prop.start=(p=="opacity")?+prop.start:parseFloat(prop.start);}}this.curve=new _2a9(pm);});d.connect(anim,"onAnimate",d.hitch(d,"style",anim.node));return anim;};dojo.anim=function(node,_2ae,_2af,_2b0,_2b1,_2b2){return d.animateProperty({node:node,duration:_2af||d.Animation.prototype.duration,properties:_2ae,easing:_2b0,onEnd:_2b1}).play(_2b2||0);};})();}if(!dojo._hasResource["dojo._base.browser"]){dojo._hasResource["dojo._base.browser"]=true;dojo.provide("dojo._base.browser");dojo.forEach(dojo.config.require,function(i){dojo["require"](i);});}if(!dojo._hasResource["dojo._base"]){dojo._hasResource["dojo._base"]=true;dojo.provide("dojo._base");}if(dojo.isBrowser&&(document.readyState==="complete"||dojo.config.afterOnLoad)){window.setTimeout(dojo._loadInit,100);}})();
diff --git a/chrome/test/data/dromaeo/lib/jquery.1.10.2.js b/chrome/test/data/dromaeo/lib/jquery.1.10.2.js
new file mode 100644
index 0000000..c5c6482
--- /dev/null
+++ b/chrome/test/data/dromaeo/lib/jquery.1.10.2.js
@@ -0,0 +1,9789 @@
+/*!
+ * jQuery JavaScript Library v1.10.2
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-07-03T13:48Z
+ */
+(function( window, undefined ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//"use strict";
+var
+	// The deferred used on DOM ready
+	readyList,
+
+	// A central reference to the root jQuery(document)
+	rootjQuery,
+
+	// Support: IE<10
+	// For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
+	core_strundefined = typeof undefined,
+
+	// Use the correct document accordingly with window argument (sandbox)
+	location = window.location,
+	document = window.document,
+	docElem = document.documentElement,
+
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+
+	// Map over the $ in case of overwrite
+	_$ = window.$,
+
+	// [[Class]] -> type pairs
+	class2type = {},
+
+	// List of deleted data cache ids, so we can reuse them
+	core_deletedIds = [],
+
+	core_version = "1.10.2",
+
+	// Save a reference to some core methods
+	core_concat = core_deletedIds.concat,
+	core_push = core_deletedIds.push,
+	core_slice = core_deletedIds.slice,
+	core_indexOf = core_deletedIds.indexOf,
+	core_toString = class2type.toString,
+	core_hasOwn = class2type.hasOwnProperty,
+	core_trim = core_version.trim,
+
+	// Define a local copy of jQuery
+	jQuery = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		return new jQuery.fn.init( selector, context, rootjQuery );
+	},
+
+	// Used for matching numbers
+	core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+
+	// Used for splitting on whitespace
+	core_rnotwhite = /\S+/g,
+
+	// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+	// A simple way to check for HTML strings
+	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+	// Strict HTML recognition (#11290: must start with <)
+	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+	// Match a standalone tag
+	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+	// JSON RegExp
+	rvalidchars = /^[\],:{}\s]*$/,
+	rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+	rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+	rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
+
+	// Matches dashed string for camelizing
+	rmsPrefix = /^-ms-/,
+	rdashAlpha = /-([\da-z])/gi,
+
+	// Used by jQuery.camelCase as callback to replace()
+	fcamelCase = function( all, letter ) {
+		return letter.toUpperCase();
+	},
+
+	// The ready event handler
+	completed = function( event ) {
+
+		// readyState === "complete" is good enough for us to call the dom ready in oldIE
+		if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+			detach();
+			jQuery.ready();
+		}
+	},
+	// Clean-up method for dom ready events
+	detach = function() {
+		if ( document.addEventListener ) {
+			document.removeEventListener( "DOMContentLoaded", completed, false );
+			window.removeEventListener( "load", completed, false );
+
+		} else {
+			document.detachEvent( "onreadystatechange", completed );
+			window.detachEvent( "onload", completed );
+		}
+	};
+
+jQuery.fn = jQuery.prototype = {
+	// The current version of jQuery being used
+	jquery: core_version,
+
+	constructor: jQuery,
+	init: function( selector, context, rootjQuery ) {
+		var match, elem;
+
+		// HANDLE: $(""), $(null), $(undefined), $(false)
+		if ( !selector ) {
+			return this;
+		}
+
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+				// Assume that strings that start and end with <> are HTML and skip the regex check
+				match = [ null, selector, null ];
+
+			} else {
+				match = rquickExpr.exec( selector );
+			}
+
+			// Match html or make sure no context is specified for #id
+			if ( match && (match[1] || !context) ) {
+
+				// HANDLE: $(html) -> $(array)
+				if ( match[1] ) {
+					context = context instanceof jQuery ? context[0] : context;
+
+					// scripts is true for back-compat
+					jQuery.merge( this, jQuery.parseHTML(
+						match[1],
+						context && context.nodeType ? context.ownerDocument || context : document,
+						true
+					) );
+
+					// HANDLE: $(html, props)
+					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+						for ( match in context ) {
+							// Properties of context are called as methods if possible
+							if ( jQuery.isFunction( this[ match ] ) ) {
+								this[ match ]( context[ match ] );
+
+							// ...and otherwise set as attributes
+							} else {
+								this.attr( match, context[ match ] );
+							}
+						}
+					}
+
+					return this;
+
+				// HANDLE: $(#id)
+				} else {
+					elem = document.getElementById( match[2] );
+
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE and Opera return items
+						// by name instead of ID
+						if ( elem.id !== match[2] ) {
+							return rootjQuery.find( selector );
+						}
+
+						// Otherwise, we inject the element directly into the jQuery object
+						this.length = 1;
+						this[0] = elem;
+					}
+
+					this.context = document;
+					this.selector = selector;
+					return this;
+				}
+
+			// HANDLE: $(expr, $(...))
+			} else if ( !context || context.jquery ) {
+				return ( context || rootjQuery ).find( selector );
+
+			// HANDLE: $(expr, context)
+			// (which is just equivalent to: $(context).find(expr)
+			} else {
+				return this.constructor( context ).find( selector );
+			}
+
+		// HANDLE: $(DOMElement)
+		} else if ( selector.nodeType ) {
+			this.context = this[0] = selector;
+			this.length = 1;
+			return this;
+
+		// HANDLE: $(function)
+		// Shortcut for document ready
+		} else if ( jQuery.isFunction( selector ) ) {
+			return rootjQuery.ready( selector );
+		}
+
+		if ( selector.selector !== undefined ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return jQuery.makeArray( selector, this );
+	},
+
+	// Start with an empty selector
+	selector: "",
+
+	// The default length of a jQuery object is 0
+	length: 0,
+
+	toArray: function() {
+		return core_slice.call( this );
+	},
+
+	// Get the Nth element in the matched element set OR
+	// Get the whole matched element set as a clean array
+	get: function( num ) {
+		return num == null ?
+
+			// Return a 'clean' array
+			this.toArray() :
+
+			// Return just the object
+			( num < 0 ? this[ this.length + num ] : this[ num ] );
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems ) {
+
+		// Build a new jQuery matched element set
+		var ret = jQuery.merge( this.constructor(), elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+		ret.context = this.context;
+
+		// Return the newly-formed element set
+		return ret;
+	},
+
+	// Execute a callback for every element in the matched set.
+	// (You can seed the arguments with an array of args, but this is
+	// only used internally.)
+	each: function( callback, args ) {
+		return jQuery.each( this, callback, args );
+	},
+
+	ready: function( fn ) {
+		// Add the callback
+		jQuery.ready.promise().done( fn );
+
+		return this;
+	},
+
+	slice: function() {
+		return this.pushStack( core_slice.apply( this, arguments ) );
+	},
+
+	first: function() {
+		return this.eq( 0 );
+	},
+
+	last: function() {
+		return this.eq( -1 );
+	},
+
+	eq: function( i ) {
+		var len = this.length,
+			j = +i + ( i < 0 ? len : 0 );
+		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function( elem, i ) {
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	end: function() {
+		return this.prevObject || this.constructor(null);
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: core_push,
+	sort: [].sort,
+	splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+	var src, copyIsArray, copy, name, options, clone,
+		target = arguments[0] || {},
+		i = 1,
+		length = arguments.length,
+		deep = false;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+
+	// Handle case when target is a string or something (possible in deep copy)
+	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+		target = {};
+	}
+
+	// extend jQuery itself if only one argument is passed
+	if ( length === i ) {
+		target = this;
+		--i;
+	}
+
+	for ( ; i < length; i++ ) {
+		// Only deal with non-null/undefined values
+		if ( (options = arguments[ i ]) != null ) {
+			// Extend the base object
+			for ( name in options ) {
+				src = target[ name ];
+				copy = options[ name ];
+
+				// Prevent never-ending loop
+				if ( target === copy ) {
+					continue;
+				}
+
+				// Recurse if we're merging plain objects or arrays
+				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+					if ( copyIsArray ) {
+						copyIsArray = false;
+						clone = src && jQuery.isArray(src) ? src : [];
+
+					} else {
+						clone = src && jQuery.isPlainObject(src) ? src : {};
+					}
+
+					// Never move original objects, clone them
+					target[ name ] = jQuery.extend( deep, clone, copy );
+
+				// Don't bring in undefined values
+				} else if ( copy !== undefined ) {
+					target[ name ] = copy;
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};
+
+jQuery.extend({
+	// Unique for each copy of jQuery on the page
+	// Non-digits removed to match rinlinejQuery
+	expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
+	noConflict: function( deep ) {
+		if ( window.$ === jQuery ) {
+			window.$ = _$;
+		}
+
+		if ( deep && window.jQuery === jQuery ) {
+			window.jQuery = _jQuery;
+		}
+
+		return jQuery;
+	},
+
+	// Is the DOM ready to be used? Set to true once it occurs.
+	isReady: false,
+
+	// A counter to track how many items to wait for before
+	// the ready event fires. See #6781
+	readyWait: 1,
+
+	// Hold (or release) the ready event
+	holdReady: function( hold ) {
+		if ( hold ) {
+			jQuery.readyWait++;
+		} else {
+			jQuery.ready( true );
+		}
+	},
+
+	// Handle when the DOM is ready
+	ready: function( wait ) {
+
+		// Abort if there are pending holds or we're already ready
+		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+			return;
+		}
+
+		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+		if ( !document.body ) {
+			return setTimeout( jQuery.ready );
+		}
+
+		// Remember that the DOM is ready
+		jQuery.isReady = true;
+
+		// If a normal DOM Ready event fired, decrement, and wait if need be
+		if ( wait !== true && --jQuery.readyWait > 0 ) {
+			return;
+		}
+
+		// If there are functions bound, to execute
+		readyList.resolveWith( document, [ jQuery ] );
+
+		// Trigger any bound ready events
+		if ( jQuery.fn.trigger ) {
+			jQuery( document ).trigger("ready").off("ready");
+		}
+	},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return jQuery.type(obj) === "function";
+	},
+
+	isArray: Array.isArray || function( obj ) {
+		return jQuery.type(obj) === "array";
+	},
+
+	isWindow: function( obj ) {
+		/* jshint eqeqeq: false */
+		return obj != null && obj == obj.window;
+	},
+
+	isNumeric: function( obj ) {
+		return !isNaN( parseFloat(obj) ) && isFinite( obj );
+	},
+
+	type: function( obj ) {
+		if ( obj == null ) {
+			return String( obj );
+		}
+		return typeof obj === "object" || typeof obj === "function" ?
+			class2type[ core_toString.call(obj) ] || "object" :
+			typeof obj;
+	},
+
+	isPlainObject: function( obj ) {
+		var key;
+
+		// Must be an Object.
+		// Because of IE, we also have to check the presence of the constructor property.
+		// Make sure that DOM nodes and window objects don't pass through, as well
+		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+			return false;
+		}
+
+		try {
+			// Not own constructor property must be Object
+			if ( obj.constructor &&
+				!core_hasOwn.call(obj, "constructor") &&
+				!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+				return false;
+			}
+		} catch ( e ) {
+			// IE8,9 Will throw exceptions on certain host objects #9897
+			return false;
+		}
+
+		// Support: IE<9
+		// Handle iteration over inherited properties before own properties.
+		if ( jQuery.support.ownLast ) {
+			for ( key in obj ) {
+				return core_hasOwn.call( obj, key );
+			}
+		}
+
+		// Own properties are enumerated firstly, so to speed up,
+		// if last one is own, then all properties are own.
+		for ( key in obj ) {}
+
+		return key === undefined || core_hasOwn.call( obj, key );
+	},
+
+	isEmptyObject: function( obj ) {
+		var name;
+		for ( name in obj ) {
+			return false;
+		}
+		return true;
+	},
+
+	error: function( msg ) {
+		throw new Error( msg );
+	},
+
+	// data: string of html
+	// context (optional): If specified, the fragment will be created in this context, defaults to document
+	// keepScripts (optional): If true, will include scripts passed in the html string
+	parseHTML: function( data, context, keepScripts ) {
+		if ( !data || typeof data !== "string" ) {
+			return null;
+		}
+		if ( typeof context === "boolean" ) {
+			keepScripts = context;
+			context = false;
+		}
+		context = context || document;
+
+		var parsed = rsingleTag.exec( data ),
+			scripts = !keepScripts && [];
+
+		// Single tag
+		if ( parsed ) {
+			return [ context.createElement( parsed[1] ) ];
+		}
+
+		parsed = jQuery.buildFragment( [ data ], context, scripts );
+		if ( scripts ) {
+			jQuery( scripts ).remove();
+		}
+		return jQuery.merge( [], parsed.childNodes );
+	},
+
+	parseJSON: function( data ) {
+		// Attempt to parse using the native JSON parser first
+		if ( window.JSON && window.JSON.parse ) {
+			return window.JSON.parse( data );
+		}
+
+		if ( data === null ) {
+			return data;
+		}
+
+		if ( typeof data === "string" ) {
+
+			// Make sure leading/trailing whitespace is removed (IE can't handle it)
+			data = jQuery.trim( data );
+
+			if ( data ) {
+				// Make sure the incoming data is actual JSON
+				// Logic borrowed from http://json.org/json2.js
+				if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+					.replace( rvalidtokens, "]" )
+					.replace( rvalidbraces, "")) ) {
+
+					return ( new Function( "return " + data ) )();
+				}
+			}
+		}
+
+		jQuery.error( "Invalid JSON: " + data );
+	},
+
+	// Cross-browser xml parsing
+	parseXML: function( data ) {
+		var xml, tmp;
+		if ( !data || typeof data !== "string" ) {
+			return null;
+		}
+		try {
+			if ( window.DOMParser ) { // Standard
+				tmp = new DOMParser();
+				xml = tmp.parseFromString( data , "text/xml" );
+			} else { // IE
+				xml = new ActiveXObject( "Microsoft.XMLDOM" );
+				xml.async = "false";
+				xml.loadXML( data );
+			}
+		} catch( e ) {
+			xml = undefined;
+		}
+		if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+			jQuery.error( "Invalid XML: " + data );
+		}
+		return xml;
+	},
+
+	noop: function() {},
+
+	// Evaluates a script in a global context
+	// Workarounds based on findings by Jim Driscoll
+	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+	globalEval: function( data ) {
+		if ( data && jQuery.trim( data ) ) {
+			// We use execScript on Internet Explorer
+			// We use an anonymous function so that context is window
+			// rather than jQuery in Firefox
+			( window.execScript || function( data ) {
+				window[ "eval" ].call( window, data );
+			} )( data );
+		}
+	},
+
+	// Convert dashed to camelCase; used by the css and data modules
+	// Microsoft forgot to hump their vendor prefix (#9572)
+	camelCase: function( string ) {
+		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+	},
+
+	nodeName: function( elem, name ) {
+		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+	},
+
+	// args is for internal usage only
+	each: function( obj, callback, args ) {
+		var value,
+			i = 0,
+			length = obj.length,
+			isArray = isArraylike( obj );
+
+		if ( args ) {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+
+		// A special, fast, case for the most common use of each
+		} else {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+		}
+
+		return obj;
+	},
+
+	// Use native String.trim function wherever possible
+	trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+		function( text ) {
+			return text == null ?
+				"" :
+				core_trim.call( text );
+		} :
+
+		// Otherwise use our own trimming functionality
+		function( text ) {
+			return text == null ?
+				"" :
+				( text + "" ).replace( rtrim, "" );
+		},
+
+	// results is for internal usage only
+	makeArray: function( arr, results ) {
+		var ret = results || [];
+
+		if ( arr != null ) {
+			if ( isArraylike( Object(arr) ) ) {
+				jQuery.merge( ret,
+					typeof arr === "string" ?
+					[ arr ] : arr
+				);
+			} else {
+				core_push.call( ret, arr );
+			}
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, arr, i ) {
+		var len;
+
+		if ( arr ) {
+			if ( core_indexOf ) {
+				return core_indexOf.call( arr, elem, i );
+			}
+
+			len = arr.length;
+			i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+			for ( ; i < len; i++ ) {
+				// Skip accessing in sparse arrays
+				if ( i in arr && arr[ i ] === elem ) {
+					return i;
+				}
+			}
+		}
+
+		return -1;
+	},
+
+	merge: function( first, second ) {
+		var l = second.length,
+			i = first.length,
+			j = 0;
+
+		if ( typeof l === "number" ) {
+			for ( ; j < l; j++ ) {
+				first[ i++ ] = second[ j ];
+			}
+		} else {
+			while ( second[j] !== undefined ) {
+				first[ i++ ] = second[ j++ ];
+			}
+		}
+
+		first.length = i;
+
+		return first;
+	},
+
+	grep: function( elems, callback, inv ) {
+		var retVal,
+			ret = [],
+			i = 0,
+			length = elems.length;
+		inv = !!inv;
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( ; i < length; i++ ) {
+			retVal = !!callback( elems[ i ], i );
+			if ( inv !== retVal ) {
+				ret.push( elems[ i ] );
+			}
+		}
+
+		return ret;
+	},
+
+	// arg is for internal usage only
+	map: function( elems, callback, arg ) {
+		var value,
+			i = 0,
+			length = elems.length,
+			isArray = isArraylike( elems ),
+			ret = [];
+
+		// Go through the array, translating each of the items to their
+		if ( isArray ) {
+			for ( ; i < length; i++ ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+
+		// Go through every key on the object,
+		} else {
+			for ( i in elems ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+		}
+
+		// Flatten any nested arrays
+		return core_concat.apply( [], ret );
+	},
+
+	// A global GUID counter for objects
+	guid: 1,
+
+	// Bind a function to a context, optionally partially applying any
+	// arguments.
+	proxy: function( fn, context ) {
+		var args, proxy, tmp;
+
+		if ( typeof context === "string" ) {
+			tmp = fn[ context ];
+			context = fn;
+			fn = tmp;
+		}
+
+		// Quick check to determine if target is callable, in the spec
+		// this throws a TypeError, but we will just return undefined.
+		if ( !jQuery.isFunction( fn ) ) {
+			return undefined;
+		}
+
+		// Simulated bind
+		args = core_slice.call( arguments, 2 );
+		proxy = function() {
+			return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+		};
+
+		// Set the guid of unique handler to the same of original handler, so it can be removed
+		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+		return proxy;
+	},
+
+	// Multifunctional method to get and set values of a collection
+	// The value/s can optionally be executed if it's a function
+	access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+		var i = 0,
+			length = elems.length,
+			bulk = key == null;
+
+		// Sets many values
+		if ( jQuery.type( key ) === "object" ) {
+			chainable = true;
+			for ( i in key ) {
+				jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+			}
+
+		// Sets one value
+		} else if ( value !== undefined ) {
+			chainable = true;
+
+			if ( !jQuery.isFunction( value ) ) {
+				raw = true;
+			}
+
+			if ( bulk ) {
+				// Bulk operations run against the entire set
+				if ( raw ) {
+					fn.call( elems, value );
+					fn = null;
+
+				// ...except when executing function values
+				} else {
+					bulk = fn;
+					fn = function( elem, key, value ) {
+						return bulk.call( jQuery( elem ), value );
+					};
+				}
+			}
+
+			if ( fn ) {
+				for ( ; i < length; i++ ) {
+					fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+				}
+			}
+		}
+
+		return chainable ?
+			elems :
+
+			// Gets
+			bulk ?
+				fn.call( elems ) :
+				length ? fn( elems[0], key ) : emptyGet;
+	},
+
+	now: function() {
+		return ( new Date() ).getTime();
+	},
+
+	// A method for quickly swapping in/out CSS properties to get correct calculations.
+	// Note: this method belongs to the css module but it's needed here for the support module.
+	// If support gets modularized, this method should be moved back to the css module.
+	swap: function( elem, options, callback, args ) {
+		var ret, name,
+			old = {};
+
+		// Remember the old values, and insert the new ones
+		for ( name in options ) {
+			old[ name ] = elem.style[ name ];
+			elem.style[ name ] = options[ name ];
+		}
+
+		ret = callback.apply( elem, args || [] );
+
+		// Revert the old values
+		for ( name in options ) {
+			elem.style[ name ] = old[ name ];
+		}
+
+		return ret;
+	}
+});
+
+jQuery.ready.promise = function( obj ) {
+	if ( !readyList ) {
+
+		readyList = jQuery.Deferred();
+
+		// Catch cases where $(document).ready() is called after the browser event has already occurred.
+		// we once tried to use readyState "interactive" here, but it caused issues like the one
+		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+		if ( document.readyState === "complete" ) {
+			// Handle it asynchronously to allow scripts the opportunity to delay ready
+			setTimeout( jQuery.ready );
+
+		// Standards-based browsers support DOMContentLoaded
+		} else if ( document.addEventListener ) {
+			// Use the handy event callback
+			document.addEventListener( "DOMContentLoaded", completed, false );
+
+			// A fallback to window.onload, that will always work
+			window.addEventListener( "load", completed, false );
+
+		// If IE event model is used
+		} else {
+			// Ensure firing before onload, maybe late but safe also for iframes
+			document.attachEvent( "onreadystatechange", completed );
+
+			// A fallback to window.onload, that will always work
+			window.attachEvent( "onload", completed );
+
+			// If IE and not a frame
+			// continually check to see if the document is ready
+			var top = false;
+
+			try {
+				top = window.frameElement == null && document.documentElement;
+			} catch(e) {}
+
+			if ( top && top.doScroll ) {
+				(function doScrollCheck() {
+					if ( !jQuery.isReady ) {
+
+						try {
+							// Use the trick by Diego Perini
+							// http://javascript.nwbox.com/IEContentLoaded/
+							top.doScroll("left");
+						} catch(e) {
+							return setTimeout( doScrollCheck, 50 );
+						}
+
+						// detach all dom ready events
+						detach();
+
+						// and execute any waiting functions
+						jQuery.ready();
+					}
+				})();
+			}
+		}
+	}
+	return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+	var length = obj.length,
+		type = jQuery.type( obj );
+
+	if ( jQuery.isWindow( obj ) ) {
+		return false;
+	}
+
+	if ( obj.nodeType === 1 && length ) {
+		return true;
+	}
+
+	return type === "array" || type !== "function" &&
+		( length === 0 ||
+		typeof length === "number" && length > 0 && ( length - 1 ) in obj );
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+/*!
+ * Sizzle CSS Selector Engine v1.10.2
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-07-03
+ */
+(function( window, undefined ) {
+
+var i,
+	support,
+	cachedruns,
+	Expr,
+	getText,
+	isXML,
+	compile,
+	outermostContext,
+	sortInput,
+
+	// Local document vars
+	setDocument,
+	document,
+	docElem,
+	documentIsHTML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+
+	// Instance-specific data
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+	hasDuplicate = false,
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+		return 0;
+	},
+
+	// General-purpose constants
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	// Instance methods
+	hasOwn = ({}).hasOwnProperty,
+	arr = [],
+	pop = arr.pop,
+	push_native = arr.push,
+	push = arr.push,
+	slice = arr.slice,
+	// Use a stripped-down indexOf if we can't use a native one
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+	// Regular expressions
+
+	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+	whitespace = "[\\x20\\t\\r\\n\\f]",
+	// http://www.w3.org/TR/css3-syntax/#characters
+	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+	// Loosely modeled on CSS identifier characters
+	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+	identifier = characterEncoding.replace( "w", "w#" ),
+
+	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+		"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+	// Prefer arguments quoted,
+	//   then not containing pseudos/brackets,
+	//   then attribute selectors/non-parenthetical expressions,
+	//   then anything else
+	// These preferences are here to reduce the number of selectors
+	//   needing tokenize in the PSEUDO preFilter
+	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+	rsibling = new RegExp( whitespace + "*[+~]" ),
+	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
+
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+		"ATTR": new RegExp( "^" + attributes ),
+		"PSEUDO": new RegExp( "^" + pseudos ),
+		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+		// For use in libraries implementing .is()
+		// We use this for POS matching in `select`
+		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+	},
+
+	rnative = /^[^{]+\{\s*\[native \w/,
+
+	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rescape = /'|\\/g,
+
+	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+	funescape = function( _, escaped, escapedWhitespace ) {
+		var high = "0x" + escaped - 0x10000;
+		// NaN means non-codepoint
+		// Support: Firefox
+		// Workaround erroneous numeric interpretation of +"0x"
+		return high !== high || escapedWhitespace ?
+			escaped :
+			// BMP codepoint
+			high < 0 ?
+				String.fromCharCode( high + 0x10000 ) :
+				// Supplemental Plane codepoint (surrogate pair)
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+// Optimize for push.apply( _, NodeList )
+try {
+	push.apply(
+		(arr = slice.call( preferredDoc.childNodes )),
+		preferredDoc.childNodes
+	);
+	// Support: Android<4.0
+	// Detect silently failing push.apply
+	arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+	push = { apply: arr.length ?
+
+		// Leverage slice if possible
+		function( target, els ) {
+			push_native.apply( target, slice.call(els) );
+		} :
+
+		// Support: IE<9
+		// Otherwise append directly
+		function( target, els ) {
+			var j = target.length,
+				i = 0;
+			// Can't trust NodeList.length
+			while ( (target[j++] = els[i++]) ) {}
+			target.length = j - 1;
+		}
+	};
+}
+
+function Sizzle( selector, context, results, seed ) {
+	var match, elem, m, nodeType,
+		// QSA vars
+		i, groups, old, nid, newContext, newSelector;
+
+	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+		setDocument( context );
+	}
+
+	context = context || document;
+	results = results || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( documentIsHTML && !seed ) {
+
+		// Shortcuts
+		if ( (match = rquickExpr.exec( selector )) ) {
+			// Speed-up: Sizzle("#ID")
+			if ( (m = match[1]) ) {
+				if ( nodeType === 9 ) {
+					elem = context.getElementById( m );
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE, Opera, and Webkit return items
+						// by name instead of ID
+						if ( elem.id === m ) {
+							results.push( elem );
+							return results;
+						}
+					} else {
+						return results;
+					}
+				} else {
+					// Context is not a document
+					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+						contains( context, elem ) && elem.id === m ) {
+						results.push( elem );
+						return results;
+					}
+				}
+
+			// Speed-up: Sizzle("TAG")
+			} else if ( match[2] ) {
+				push.apply( results, context.getElementsByTagName( selector ) );
+				return results;
+
+			// Speed-up: Sizzle(".CLASS")
+			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+				push.apply( results, context.getElementsByClassName( m ) );
+				return results;
+			}
+		}
+
+		// QSA path
+		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			nid = old = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && selector;
+
+			// qSA works strangely on Element-rooted queries
+			// We can work around this by specifying an extra ID on the root
+			// and working up from there (Thanks to Andrew Dupont for the technique)
+			// IE 8 doesn't work on object elements
+			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+				groups = tokenize( selector );
+
+				if ( (old = context.getAttribute("id")) ) {
+					nid = old.replace( rescape, "\\$&" );
+				} else {
+					context.setAttribute( "id", nid );
+				}
+				nid = "[id='" + nid + "'] ";
+
+				i = groups.length;
+				while ( i-- ) {
+					groups[i] = nid + toSelector( groups[i] );
+				}
+				newContext = rsibling.test( selector ) && context.parentNode || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results,
+						newContext.querySelectorAll( newSelector )
+					);
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	// All others
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *	deleting the oldest entry
+ */
+function createCache() {
+	var keys = [];
+
+	function cache( key, value ) {
+		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+		if ( keys.push( key += " " ) > Expr.cacheLength ) {
+			// Only keep the most recent entries
+			delete cache[ keys.shift() ];
+		}
+		return (cache[ key ] = value);
+	}
+	return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+	fn[ expando ] = true;
+	return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+	var div = document.createElement("div");
+
+	try {
+		return !!fn( div );
+	} catch (e) {
+		return false;
+	} finally {
+		// Remove from its parent by default
+		if ( div.parentNode ) {
+			div.parentNode.removeChild( div );
+		}
+		// release memory in IE
+		div = null;
+	}
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+	var arr = attrs.split("|"),
+		i = attrs.length;
+
+	while ( i-- ) {
+		Expr.attrHandle[ arr[i] ] = handler;
+	}
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+			( ~b.sourceIndex || MAX_NEGATIVE ) -
+			( ~a.sourceIndex || MAX_NEGATIVE );
+
+	// Use IE sourceIndex if available on both nodes
+	if ( diff ) {
+		return diff;
+	}
+
+	// Check if b follows a
+	if ( cur ) {
+		while ( (cur = cur.nextSibling) ) {
+			if ( cur === b ) {
+				return -1;
+			}
+		}
+	}
+
+	return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return name === "input" && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return (name === "input" || name === "button") && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+	return markFunction(function( argument ) {
+		argument = +argument;
+		return markFunction(function( seed, matches ) {
+			var j,
+				matchIndexes = fn( [], seed.length, argument ),
+				i = matchIndexes.length;
+
+			// Match elements found at the specified indexes
+			while ( i-- ) {
+				if ( seed[ (j = matchIndexes[i]) ] ) {
+					seed[j] = !(matches[j] = seed[j]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * Detect xml
+ * @param {Element|Object} elem An element or a document
+ */
+isXML = Sizzle.isXML = function( elem ) {
+	// documentElement is verified for cases where it doesn't yet exist
+	// (such as loading iframes in IE - #4833)
+	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+	var doc = node ? node.ownerDocument || node : preferredDoc,
+		parent = doc.defaultView;
+
+	// If no document and documentElement is available, return
+	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+		return document;
+	}
+
+	// Set our document
+	document = doc;
+	docElem = doc.documentElement;
+
+	// Support tests
+	documentIsHTML = !isXML( doc );
+
+	// Support: IE>8
+	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
+	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+	// IE6-8 do not support the defaultView property so parent will be undefined
+	if ( parent && parent.attachEvent && parent !== parent.top ) {
+		parent.attachEvent( "onbeforeunload", function() {
+			setDocument();
+		});
+	}
+
+	/* Attributes
+	---------------------------------------------------------------------- */
+
+	// Support: IE<8
+	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+	support.attributes = assert(function( div ) {
+		div.className = "i";
+		return !div.getAttribute("className");
+	});
+
+	/* getElement(s)By*
+	---------------------------------------------------------------------- */
+
+	// Check if getElementsByTagName("*") returns only elements
+	support.getElementsByTagName = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	// Check if getElementsByClassName can be trusted
+	support.getElementsByClassName = assert(function( div ) {
+		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+		// Support: Safari<4
+		// Catch class over-caching
+		div.firstChild.className = "i";
+		// Support: Opera<10
+		// Catch gEBCN failure to find non-leading classes
+		return div.getElementsByClassName("i").length === 2;
+	});
+
+	// Support: IE<10
+	// Check if getElementById returns elements by name
+	// The broken getElementById methods don't pick up programatically-set names,
+	// so use a roundabout getElementsByName test
+	support.getById = assert(function( div ) {
+		docElem.appendChild( div ).id = expando;
+		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+	});
+
+	// ID find and filter
+	if ( support.getById ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+				var m = context.getElementById( id );
+				// Check parentNode to catch when Blackberry 4.6 returns
+				// nodes that are no longer in the document #6963
+				return m && m.parentNode ? [m] : [];
+			}
+		};
+		Expr.filter["ID"] = function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				return elem.getAttribute("id") === attrId;
+			};
+		};
+	} else {
+		// Support: IE6/7
+		// getElementById is not reliable as a find shortcut
+		delete Expr.find["ID"];
+
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	// Tag
+	Expr.find["TAG"] = support.getElementsByTagName ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				results = context.getElementsByTagName( tag );
+
+			// Filter out possible comments
+			if ( tag === "*" ) {
+				while ( (elem = results[i++]) ) {
+					if ( elem.nodeType === 1 ) {
+						tmp.push( elem );
+					}
+				}
+
+				return tmp;
+			}
+			return results;
+		};
+
+	// Class
+	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	/* QSA/matchesSelector
+	---------------------------------------------------------------------- */
+
+	// QSA and matchesSelector support
+
+	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+	rbuggyMatches = [];
+
+	// qSa(:focus) reports false when true (Chrome 21)
+	// We allow this because of a bug in IE8/9 that throws an error
+	// whenever `document.activeElement` is accessed on an iframe
+	// So, we allow :focus to pass through QSA all the time to avoid the IE error
+	// See http://bugs.jquery.com/ticket/13378
+	rbuggyQSA = [];
+
+	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+		// Build QSA regex
+		// Regex strategy adopted from Diego Perini
+		assert(function( div ) {
+			// Select is set to empty string on purpose
+			// This is to test IE's treatment of not explicitly
+			// setting a boolean content attribute,
+			// since its presence should be enough
+			// http://bugs.jquery.com/ticket/12359
+			div.innerHTML = "<select><option selected=''></option></select>";
+
+			// Support: IE8
+			// Boolean attributes and "value" are not treated correctly
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+			}
+
+			// Webkit/Opera - :checked should return selected option elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":checked").length ) {
+				rbuggyQSA.push(":checked");
+			}
+		});
+
+		assert(function( div ) {
+
+			// Support: Opera 10-12/IE8
+			// ^= $= *= and empty values
+			// Should not select anything
+			// Support: Windows 8 Native Apps
+			// The type attribute is restricted during .innerHTML assignment
+			var input = doc.createElement("input");
+			input.setAttribute( "type", "hidden" );
+			div.appendChild( input ).setAttribute( "t", "" );
+
+			if ( div.querySelectorAll("[t^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+			}
+
+			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":enabled").length ) {
+				rbuggyQSA.push( ":enabled", ":disabled" );
+			}
+
+			// Opera 10-11 does not throw on post-comma invalid pseudos
+			div.querySelectorAll("*,:x");
+			rbuggyQSA.push(",.*:");
+		});
+	}
+
+	if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.oMatchesSelector ||
+		docElem.msMatchesSelector) )) ) {
+
+		assert(function( div ) {
+			// Check to see if it's possible to do matchesSelector
+			// on a disconnected node (IE 9)
+			support.disconnectedMatch = matches.call( div, "div" );
+
+			// This should fail with an exception
+			// Gecko does not error, returns false instead
+			matches.call( div, "[s!='']:x" );
+			rbuggyMatches.push( "!=", pseudos );
+		});
+	}
+
+	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+	/* Contains
+	---------------------------------------------------------------------- */
+
+	// Element contains another
+	// Purposefully does not implement inclusive descendent
+	// As in, an element does not contain itself
+	contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
+		function( a, b ) {
+			var adown = a.nodeType === 9 ? a.documentElement : a,
+				bup = b && b.parentNode;
+			return a === bup || !!( bup && bup.nodeType === 1 && (
+				adown.contains ?
+					adown.contains( bup ) :
+					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+			));
+		} :
+		function( a, b ) {
+			if ( b ) {
+				while ( (b = b.parentNode) ) {
+					if ( b === a ) {
+						return true;
+					}
+				}
+			}
+			return false;
+		};
+
+	/* Sorting
+	---------------------------------------------------------------------- */
+
+	// Document order sorting
+	sortOrder = docElem.compareDocumentPosition ?
+	function( a, b ) {
+
+		// Flag for duplicate removal
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
+
+		if ( compare ) {
+			// Disconnected nodes
+			if ( compare & 1 ||
+				(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+				// Choose the first element that is related to our preferred document
+				if ( a === doc || contains(preferredDoc, a) ) {
+					return -1;
+				}
+				if ( b === doc || contains(preferredDoc, b) ) {
+					return 1;
+				}
+
+				// Maintain original order
+				return sortInput ?
+					( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+					0;
+			}
+
+			return compare & 4 ? -1 : 1;
+		}
+
+		// Not directly comparable, sort on existence of method
+		return a.compareDocumentPosition ? -1 : 1;
+	} :
+	function( a, b ) {
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		// Exit early if the nodes are identical
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+
+		// Parentless nodes are either documents or disconnected
+		} else if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+
+		// If the nodes are siblings, we can do a quick check
+		} else if ( aup === bup ) {
+			return siblingCheck( a, b );
+		}
+
+		// Otherwise we need full lists of their ancestors for comparison
+		cur = a;
+		while ( (cur = cur.parentNode) ) {
+			ap.unshift( cur );
+		}
+		cur = b;
+		while ( (cur = cur.parentNode) ) {
+			bp.unshift( cur );
+		}
+
+		// Walk down the tree looking for a discrepancy
+		while ( ap[i] === bp[i] ) {
+			i++;
+		}
+
+		return i ?
+			// Do a sibling check if the nodes have a common ancestor
+			siblingCheck( ap[i], bp[i] ) :
+
+			// Otherwise nodes in our document sort first
+			ap[i] === preferredDoc ? -1 :
+			bp[i] === preferredDoc ? 1 :
+			0;
+	};
+
+	return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	// Make sure that attribute selectors are quoted
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	if ( support.matchesSelector && documentIsHTML &&
+		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+		try {
+			var ret = matches.call( elem, expr );
+
+			// IE 9's matchesSelector returns false on disconnected nodes
+			if ( ret || support.disconnectedMatch ||
+					// As well, disconnected nodes are said to be in a document
+					// fragment in IE 9
+					elem.document && elem.document.nodeType !== 11 ) {
+				return ret;
+			}
+		} catch(e) {}
+	}
+
+	return Sizzle( expr, document, null, [elem] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+	// Set document vars if needed
+	if ( ( context.ownerDocument || context ) !== document ) {
+		setDocument( context );
+	}
+	return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	var fn = Expr.attrHandle[ name.toLowerCase() ],
+		// Don't get fooled by Object.prototype properties (jQuery #13807)
+		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+			fn( elem, name, !documentIsHTML ) :
+			undefined;
+
+	return val === undefined ?
+		support.attributes || !documentIsHTML ?
+			elem.getAttribute( name ) :
+			(val = elem.getAttributeNode(name)) && val.specified ?
+				val.value :
+				null :
+		val;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		j = 0,
+		i = 0;
+
+	// Unless we *know* we can detect duplicates, assume their presence
+	hasDuplicate = !support.detectDuplicates;
+	sortInput = !support.sortStable && results.slice( 0 );
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		while ( (elem = results[i++]) ) {
+			if ( elem === results[ i ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+	var node,
+		ret = "",
+		i = 0,
+		nodeType = elem.nodeType;
+
+	if ( !nodeType ) {
+		// If no nodeType, this is expected to be an array
+		for ( ; (node = elem[i]); i++ ) {
+			// Do not traverse comment nodes
+			ret += getText( node );
+		}
+	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+		// Use textContent for elements
+		// innerText usage removed for consistency of new lines (see #11153)
+		if ( typeof elem.textContent === "string" ) {
+			return elem.textContent;
+		} else {
+			// Traverse its children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				ret += getText( elem );
+			}
+		}
+	} else if ( nodeType === 3 || nodeType === 4 ) {
+		return elem.nodeValue;
+	}
+	// Do not include comment or processing instruction nodes
+
+	return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+	// Can be adjusted by the user
+	cacheLength: 50,
+
+	createPseudo: markFunction,
+
+	match: matchExpr,
+
+	attrHandle: {},
+
+	find: {},
+
+	relative: {
+		">": { dir: "parentNode", first: true },
+		" ": { dir: "parentNode" },
+		"+": { dir: "previousSibling", first: true },
+		"~": { dir: "previousSibling" }
+	},
+
+	preFilter: {
+		"ATTR": function( match ) {
+			match[1] = match[1].replace( runescape, funescape );
+
+			// Move the given value to match[3] whether quoted or unquoted
+			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+			if ( match[2] === "~=" ) {
+				match[3] = " " + match[3] + " ";
+			}
+
+			return match.slice( 0, 4 );
+		},
+
+		"CHILD": function( match ) {
+			/* matches from matchExpr["CHILD"]
+				1 type (only|nth|...)
+				2 what (child|of-type)
+				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+				4 xn-component of xn+y argument ([+-]?\d*n|)
+				5 sign of xn-component
+				6 x of xn-component
+				7 sign of y-component
+				8 y of y-component
+			*/
+			match[1] = match[1].toLowerCase();
+
+			if ( match[1].slice( 0, 3 ) === "nth" ) {
+				// nth-* requires argument
+				if ( !match[3] ) {
+					Sizzle.error( match[0] );
+				}
+
+				// numeric x and y parameters for Expr.filter.CHILD
+				// remember that false/true cast respectively to 0/1
+				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+			// other types prohibit arguments
+			} else if ( match[3] ) {
+				Sizzle.error( match[0] );
+			}
+
+			return match;
+		},
+
+		"PSEUDO": function( match ) {
+			var excess,
+				unquoted = !match[5] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			// Accept quoted arguments as-is
+			if ( match[3] && match[4] !== undefined ) {
+				match[2] = match[4];
+
+			// Strip excess characters from unquoted arguments
+			} else if ( unquoted && rpseudo.test( unquoted ) &&
+				// Get excess from tokenize (recursively)
+				(excess = tokenize( unquoted, true )) &&
+				// advance to the next closing parenthesis
+				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+				// excess is a negative index
+				match[0] = match[0].slice( 0, excess );
+				match[2] = unquoted.slice( 0, excess );
+			}
+
+			// Return only captures needed by the pseudo filter method (type and argument)
+			return match.slice( 0, 3 );
+		}
+	},
+
+	filter: {
+
+		"TAG": function( nodeNameSelector ) {
+			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+			return nodeNameSelector === "*" ?
+				function() { return true; } :
+				function( elem ) {
+					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+				};
+		},
+
+		"CLASS": function( className ) {
+			var pattern = classCache[ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+				});
+		},
+
+		"ATTR": function( name, operator, check ) {
+			return function( elem ) {
+				var result = Sizzle.attr( elem, name );
+
+				if ( result == null ) {
+					return operator === "!=";
+				}
+				if ( !operator ) {
+					return true;
+				}
+
+				result += "";
+
+				return operator === "=" ? result === check :
+					operator === "!=" ? result !== check :
+					operator === "^=" ? check && result.indexOf( check ) === 0 :
+					operator === "*=" ? check && result.indexOf( check ) > -1 :
+					operator === "$=" ? check && result.slice( -check.length ) === check :
+					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+					false;
+			};
+		},
+
+		"CHILD": function( type, what, argument, first, last ) {
+			var simple = type.slice( 0, 3 ) !== "nth",
+				forward = type.slice( -4 ) !== "last",
+				ofType = what === "of-type";
+
+			return first === 1 && last === 0 ?
+
+				// Shortcut for :nth-*(n)
+				function( elem ) {
+					return !!elem.parentNode;
+				} :
+
+				function( elem, context, xml ) {
+					var cache, outerCache, node, diff, nodeIndex, start,
+						dir = simple !== forward ? "nextSibling" : "previousSibling",
+						parent = elem.parentNode,
+						name = ofType && elem.nodeName.toLowerCase(),
+						useCache = !xml && !ofType;
+
+					if ( parent ) {
+
+						// :(first|last|only)-(child|of-type)
+						if ( simple ) {
+							while ( dir ) {
+								node = elem;
+								while ( (node = node[ dir ]) ) {
+									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+										return false;
+									}
+								}
+								// Reverse direction for :only-* (if we haven't yet done so)
+								start = dir = type === "only" && !start && "nextSibling";
+							}
+							return true;
+						}
+
+						start = [ forward ? parent.firstChild : parent.lastChild ];
+
+						// non-xml :nth-child(...) stores cache data on `parent`
+						if ( forward && useCache ) {
+							// Seek `elem` from a previously-cached index
+							outerCache = parent[ expando ] || (parent[ expando ] = {});
+							cache = outerCache[ type ] || [];
+							nodeIndex = cache[0] === dirruns && cache[1];
+							diff = cache[0] === dirruns && cache[2];
+							node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+								// Fallback to seeking `elem` from the start
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								// When found, cache indexes on `parent` and break
+								if ( node.nodeType === 1 && ++diff && node === elem ) {
+									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+									break;
+								}
+							}
+
+						// Use previously-cached element index if available
+						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+							diff = cache[1];
+
+						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+						} else {
+							// Use the same loop as above to seek `elem` from the start
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+									// Cache the index of each encountered element
+									if ( useCache ) {
+										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+									}
+
+									if ( node === elem ) {
+										break;
+									}
+								}
+							}
+						}
+
+						// Incorporate the offset, then check against cycle size
+						diff -= last;
+						return diff === first || ( diff % first === 0 && diff / first >= 0 );
+					}
+				};
+		},
+
+		"PSEUDO": function( pseudo, argument ) {
+			// pseudo-class names are case-insensitive
+			// http://www.w3.org/TR/selectors/#pseudo-classes
+			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+			// Remember that setFilters inherits from pseudos
+			var args,
+				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+					Sizzle.error( "unsupported pseudo: " + pseudo );
+
+			// The user may use createPseudo to indicate that
+			// arguments are needed to create the filter function
+			// just as Sizzle does
+			if ( fn[ expando ] ) {
+				return fn( argument );
+			}
+
+			// But maintain support for old signatures
+			if ( fn.length > 1 ) {
+				args = [ pseudo, pseudo, "", argument ];
+				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+					markFunction(function( seed, matches ) {
+						var idx,
+							matched = fn( seed, argument ),
+							i = matched.length;
+						while ( i-- ) {
+							idx = indexOf.call( seed, matched[i] );
+							seed[ idx ] = !( matches[ idx ] = matched[i] );
+						}
+					}) :
+					function( elem ) {
+						return fn( elem, 0, args );
+					};
+			}
+
+			return fn;
+		}
+	},
+
+	pseudos: {
+		// Potentially complex pseudos
+		"not": markFunction(function( selector ) {
+			// Trim the selector passed to compile
+			// to avoid treating leading and trailing
+			// spaces as combinators
+			var input = [],
+				results = [],
+				matcher = compile( selector.replace( rtrim, "$1" ) );
+
+			return matcher[ expando ] ?
+				markFunction(function( seed, matches, context, xml ) {
+					var elem,
+						unmatched = matcher( seed, null, xml, [] ),
+						i = seed.length;
+
+					// Match elements unmatched by `matcher`
+					while ( i-- ) {
+						if ( (elem = unmatched[i]) ) {
+							seed[i] = !(matches[i] = elem);
+						}
+					}
+				}) :
+				function( elem, context, xml ) {
+					input[0] = elem;
+					matcher( input, null, xml, results );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			return function( elem ) {
+				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+			};
+		}),
+
+		// "Whether an element is represented by a :lang() selector
+		// is based solely on the element's language value
+		// being equal to the identifier C,
+		// or beginning with the identifier C immediately followed by "-".
+		// The matching of C against the element's language value is performed case-insensitively.
+		// The identifier C does not have to be a valid language name."
+		// http://www.w3.org/TR/selectors/#lang-pseudo
+		"lang": markFunction( function( lang ) {
+			// lang value must be a valid identifier
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsHTML ?
+						elem.lang :
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+						elemLang = elemLang.toLowerCase();
+						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+					}
+				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+				return false;
+			};
+		}),
+
+		// Miscellaneous
+		"target": function( elem ) {
+			var hash = window.location && window.location.hash;
+			return hash && hash.slice( 1 ) === elem.id;
+		},
+
+		"root": function( elem ) {
+			return elem === docElem;
+		},
+
+		"focus": function( elem ) {
+			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+		},
+
+		// Boolean properties
+		"enabled": function( elem ) {
+			return elem.disabled === false;
+		},
+
+		"disabled": function( elem ) {
+			return elem.disabled === true;
+		},
+
+		"checked": function( elem ) {
+			// In CSS3, :checked should return both checked and selected elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			var nodeName = elem.nodeName.toLowerCase();
+			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+		},
+
+		"selected": function( elem ) {
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		// Contents
+		"empty": function( elem ) {
+			// http://www.w3.org/TR/selectors/#empty-pseudo
+			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+			//   not comment, processing instructions, or others
+			// Thanks to Diego Perini for the nodeName shortcut
+			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+					return false;
+				}
+			}
+			return true;
+		},
+
+		"parent": function( elem ) {
+			return !Expr.pseudos["empty"]( elem );
+		},
+
+		// Element/input types
+		"header": function( elem ) {
+			return rheader.test( elem.nodeName );
+		},
+
+		"input": function( elem ) {
+			return rinputs.test( elem.nodeName );
+		},
+
+		"button": function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && elem.type === "button" || name === "button";
+		},
+
+		"text": function( elem ) {
+			var attr;
+			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+			// use getAttribute instead to test this case
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+		},
+
+		// Position-in-collection
+		"first": createPositionalPseudo(function() {
+			return [ 0 ];
+		}),
+
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
+			return [ length - 1 ];
+		}),
+
+		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			return [ argument < 0 ? argument + length : argument ];
+		}),
+
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 0;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 1;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; --i >= 0; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; ++i < length; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		})
+	}
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+	Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+	Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+function tokenize( selector, parseOnly ) {
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ selector + " " ];
+
+	if ( cached ) {
+		return parseOnly ? 0 : cached.slice( 0 );
+	}
+
+	soFar = selector;
+	groups = [];
+	preFilters = Expr.preFilter;
+
+	while ( soFar ) {
+
+		// Comma and first run
+		if ( !matched || (match = rcomma.exec( soFar )) ) {
+			if ( match ) {
+				// Don't consume trailing commas as valid
+				soFar = soFar.slice( match[0].length ) || soFar;
+			}
+			groups.push( tokens = [] );
+		}
+
+		matched = false;
+
+		// Combinators
+		if ( (match = rcombinators.exec( soFar )) ) {
+			matched = match.shift();
+			tokens.push({
+				value: matched,
+				// Cast descendant combinators to space
+				type: match[0].replace( rtrim, " " )
+			});
+			soFar = soFar.slice( matched.length );
+		}
+
+		// Filters
+		for ( type in Expr.filter ) {
+			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+				(match = preFilters[ type ]( match ))) ) {
+				matched = match.shift();
+				tokens.push({
+					value: matched,
+					type: type,
+					matches: match
+				});
+				soFar = soFar.slice( matched.length );
+			}
+		}
+
+		if ( !matched ) {
+			break;
+		}
+	}
+
+	// Return the length of the invalid excess
+	// if we're just parsing
+	// Otherwise, throw an error or return tokens
+	return parseOnly ?
+		soFar.length :
+		soFar ?
+			Sizzle.error( selector ) :
+			// Cache the tokens
+			tokenCache( selector, groups ).slice( 0 );
+}
+
+function toSelector( tokens ) {
+	var i = 0,
+		len = tokens.length,
+		selector = "";
+	for ( ; i < len; i++ ) {
+		selector += tokens[i].value;
+	}
+	return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+	var dir = combinator.dir,
+		checkNonElements = base && dir === "parentNode",
+		doneName = done++;
+
+	return combinator.first ?
+		// Check against closest ancestor/preceding element
+		function( elem, context, xml ) {
+			while ( (elem = elem[ dir ]) ) {
+				if ( elem.nodeType === 1 || checkNonElements ) {
+					return matcher( elem, context, xml );
+				}
+			}
+		} :
+
+		// Check against all ancestor/preceding elements
+		function( elem, context, xml ) {
+			var data, cache, outerCache,
+				dirkey = dirruns + " " + doneName;
+
+			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+			if ( xml ) {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						if ( matcher( elem, context, xml ) ) {
+							return true;
+						}
+					}
+				}
+			} else {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						outerCache = elem[ expando ] || (elem[ expando ] = {});
+						if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+							if ( (data = cache[1]) === true || data === cachedruns ) {
+								return data === true;
+							}
+						} else {
+							cache = outerCache[ dir ] = [ dirkey ];
+							cache[1] = matcher( elem, context, xml ) || cachedruns;
+							if ( cache[1] === true ) {
+								return true;
+							}
+						}
+					}
+				}
+			}
+		};
+}
+
+function elementMatcher( matchers ) {
+	return matchers.length > 1 ?
+		function( elem, context, xml ) {
+			var i = matchers.length;
+			while ( i-- ) {
+				if ( !matchers[i]( elem, context, xml ) ) {
+					return false;
+				}
+			}
+			return true;
+		} :
+		matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+	var elem,
+		newUnmatched = [],
+		i = 0,
+		len = unmatched.length,
+		mapped = map != null;
+
+	for ( ; i < len; i++ ) {
+		if ( (elem = unmatched[i]) ) {
+			if ( !filter || filter( elem, context, xml ) ) {
+				newUnmatched.push( elem );
+				if ( mapped ) {
+					map.push( i );
+				}
+			}
+		}
+	}
+
+	return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+	if ( postFilter && !postFilter[ expando ] ) {
+		postFilter = setMatcher( postFilter );
+	}
+	if ( postFinder && !postFinder[ expando ] ) {
+		postFinder = setMatcher( postFinder, postSelector );
+	}
+	return markFunction(function( seed, results, context, xml ) {
+		var temp, i, elem,
+			preMap = [],
+			postMap = [],
+			preexisting = results.length,
+
+			// Get initial elements from seed or context
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+			// Prefilter to get matcher input, preserving a map for seed-results synchronization
+			matcherIn = preFilter && ( seed || !selector ) ?
+				condense( elems, preMap, preFilter, context, xml ) :
+				elems,
+
+			matcherOut = matcher ?
+				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+					// ...intermediate processing is necessary
+					[] :
+
+					// ...otherwise use results directly
+					results :
+				matcherIn;
+
+		// Find primary matches
+		if ( matcher ) {
+			matcher( matcherIn, matcherOut, context, xml );
+		}
+
+		// Apply postFilter
+		if ( postFilter ) {
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
+
+			// Un-match failing elements by moving them back to matcherIn
+			i = temp.length;
+			while ( i-- ) {
+				if ( (elem = temp[i]) ) {
+					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+				}
+			}
+		}
+
+		if ( seed ) {
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							// Restore matcherIn since elem is not yet a final match
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				// Move matched elements from seed to results to keep them synchronized
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		// Add elements to results, through postFinder if defined
+		} else {
+			matcherOut = condense(
+				matcherOut === results ?
+					matcherOut.splice( preexisting, matcherOut.length ) :
+					matcherOut
+			);
+			if ( postFinder ) {
+				postFinder( null, results, matcherOut, xml );
+			} else {
+				push.apply( results, matcherOut );
+			}
+		}
+	});
+}
+
+function matcherFromTokens( tokens ) {
+	var checkContext, matcher, j,
+		len = tokens.length,
+		leadingRelative = Expr.relative[ tokens[0].type ],
+		implicitRelative = leadingRelative || Expr.relative[" "],
+		i = leadingRelative ? 1 : 0,
+
+		// The foundational matcher ensures that elements are reachable from top-level context(s)
+		matchContext = addCombinator( function( elem ) {
+			return elem === checkContext;
+		}, implicitRelative, true ),
+		matchAnyContext = addCombinator( function( elem ) {
+			return indexOf.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	for ( ; i < len; i++ ) {
+		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+		} else {
+			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+			// Return special upon seeing a positional matcher
+			if ( matcher[ expando ] ) {
+				// Find the next relative operator (if any) for proper handling
+				j = ++i;
+				for ( ; j < len; j++ ) {
+					if ( Expr.relative[ tokens[j].type ] ) {
+						break;
+					}
+				}
+				return setMatcher(
+					i > 1 && elementMatcher( matchers ),
+					i > 1 && toSelector(
+						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
+						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+					).replace( rtrim, "$1" ),
+					matcher,
+					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+					j < len && toSelector( tokens )
+				);
+			}
+			matchers.push( matcher );
+		}
+	}
+
+	return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+	// A counter to specify which element is currently being matched
+	var matcherCachedRuns = 0,
+		bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, expandContext ) {
+			var elem, j, matcher,
+				setMatched = [],
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				outermost = expandContext != null,
+				contextBackup = outermostContext,
+				// We must always have either seed elements or context
+				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+				// Use integer dirruns iff this is the outermost matcher
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+				cachedruns = matcherCachedRuns;
+			}
+
+			// Add elements passing elementMatchers directly to results
+			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+			for ( ; (elem = elems[i]) != null; i++ ) {
+				if ( byElement && elem ) {
+					j = 0;
+					while ( (matcher = elementMatchers[j++]) ) {
+						if ( matcher( elem, context, xml ) ) {
+							results.push( elem );
+							break;
+						}
+					}
+					if ( outermost ) {
+						dirruns = dirrunsUnique;
+						cachedruns = ++matcherCachedRuns;
+					}
+				}
+
+				// Track unmatched elements for set filters
+				if ( bySet ) {
+					// They will have gone through all possible matchers
+					if ( (elem = !matcher && elem) ) {
+						matchedCount--;
+					}
+
+					// Lengthen the array for every element, matched or not
+					if ( seed ) {
+						unmatched.push( elem );
+					}
+				}
+			}
+
+			// Apply set filters to unmatched elements
+			matchedCount += i;
+			if ( bySet && i !== matchedCount ) {
+				j = 0;
+				while ( (matcher = setMatchers[j++]) ) {
+					matcher( unmatched, setMatched, context, xml );
+				}
+
+				if ( seed ) {
+					// Reintegrate element matches to eliminate the need for sorting
+					if ( matchedCount > 0 ) {
+						while ( i-- ) {
+							if ( !(unmatched[i] || setMatched[i]) ) {
+								setMatched[i] = pop.call( results );
+							}
+						}
+					}
+
+					// Discard index placeholder values to get only actual matches
+					setMatched = condense( setMatched );
+				}
+
+				// Add matches to results
+				push.apply( results, setMatched );
+
+				// Seedless set matches succeeding multiple successful matchers stipulate sorting
+				if ( outermost && !seed && setMatched.length > 0 &&
+					( matchedCount + setMatchers.length ) > 1 ) {
+
+					Sizzle.uniqueSort( results );
+				}
+			}
+
+			// Override manipulation of globals by nested matchers
+			if ( outermost ) {
+				dirruns = dirrunsUnique;
+				outermostContext = contextBackup;
+			}
+
+			return unmatched;
+		};
+
+	return bySet ?
+		markFunction( superMatcher ) :
+		superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+	var i,
+		setMatchers = [],
+		elementMatchers = [],
+		cached = compilerCache[ selector + " " ];
+
+	if ( !cached ) {
+		// Generate a function of recursive functions that can be used to check each element
+		if ( !group ) {
+			group = tokenize( selector );
+		}
+		i = group.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( group[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		// Cache the compiled function
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+	}
+	return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function select( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		match = tokenize( selector );
+
+	if ( !seed ) {
+		// Try to minimize operations if there is only one group
+		if ( match.length === 1 ) {
+
+			// Take a shortcut and set the context if the root selector is an ID
+			tokens = match[0] = match[0].slice( 0 );
+			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+					support.getById && context.nodeType === 9 && documentIsHTML &&
+					Expr.relative[ tokens[1].type ] ) {
+
+				context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+				if ( !context ) {
+					return results;
+				}
+				selector = selector.slice( tokens.shift().value.length );
+			}
+
+			// Fetch a seed set for right-to-left matching
+			i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+			while ( i-- ) {
+				token = tokens[i];
+
+				// Abort if we hit a combinator
+				if ( Expr.relative[ (type = token.type) ] ) {
+					break;
+				}
+				if ( (find = Expr.find[ type ]) ) {
+					// Search, expanding context for leading sibling combinators
+					if ( (seed = find(
+						token.matches[0].replace( runescape, funescape ),
+						rsibling.test( tokens[0].type ) && context.parentNode || context
+					)) ) {
+
+						// If seed is empty or no tokens remain, we can return early
+						tokens.splice( i, 1 );
+						selector = seed.length && toSelector( tokens );
+						if ( !selector ) {
+							push.apply( results, seed );
+							return results;
+						}
+
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	// Compile and execute a filtering function
+	// Provide `match` to avoid retokenization if we modified the selector above
+	compile( selector, match )(
+		seed,
+		context,
+		!documentIsHTML,
+		results,
+		rsibling.test( selector )
+	);
+	return results;
+}
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+	// Should return 1, but returns 4 (following)
+	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+	div.innerHTML = "<a href='#'></a>";
+	return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+		if ( !isXML ) {
+			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+		}
+	});
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+	div.innerHTML = "<input/>";
+	div.firstChild.setAttribute( "value", "" );
+	return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+	addHandle( "value", function( elem, name, isXML ) {
+		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+			return elem.defaultValue;
+		}
+	});
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+	return div.getAttribute("disabled") == null;
+}) ) {
+	addHandle( booleans, function( elem, name, isXML ) {
+		var val;
+		if ( !isXML ) {
+			return (val = elem.getAttributeNode( name )) && val.specified ?
+				val.value :
+				elem[ name ] === true ? name.toLowerCase() : null;
+		}
+	});
+}
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+	var object = optionsCache[ options ] = {};
+	jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+		object[ flag ] = true;
+	});
+	return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *	options: an optional list of space-separated options that will change how
+ *			the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *	once:			will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *	memory:			will keep track of previous values and will call any callback added
+ *					after the list has been fired right away with the latest "memorized"
+ *					values (like a Deferred)
+ *
+ *	unique:			will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *	stopOnFalse:	interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+	// Convert options from String-formatted to Object-formatted if needed
+	// (we check in cache first)
+	options = typeof options === "string" ?
+		( optionsCache[ options ] || createOptions( options ) ) :
+		jQuery.extend( {}, options );
+
+	var // Flag to know if list is currently firing
+		firing,
+		// Last fire value (for non-forgettable lists)
+		memory,
+		// Flag to know if list was already fired
+		fired,
+		// End of the loop when firing
+		firingLength,
+		// Index of currently firing callback (modified by remove if needed)
+		firingIndex,
+		// First callback to fire (used internally by add and fireWith)
+		firingStart,
+		// Actual callback list
+		list = [],
+		// Stack of fire calls for repeatable lists
+		stack = !options.once && [],
+		// Fire callbacks
+		fire = function( data ) {
+			memory = options.memory && data;
+			fired = true;
+			firingIndex = firingStart || 0;
+			firingStart = 0;
+			firingLength = list.length;
+			firing = true;
+			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+					memory = false; // To prevent further calls using add
+					break;
+				}
+			}
+			firing = false;
+			if ( list ) {
+				if ( stack ) {
+					if ( stack.length ) {
+						fire( stack.shift() );
+					}
+				} else if ( memory ) {
+					list = [];
+				} else {
+					self.disable();
+				}
+			}
+		},
+		// Actual Callbacks object
+		self = {
+			// Add a callback or a collection of callbacks to the list
+			add: function() {
+				if ( list ) {
+					// First, we save the current length
+					var start = list.length;
+					(function add( args ) {
+						jQuery.each( args, function( _, arg ) {
+							var type = jQuery.type( arg );
+							if ( type === "function" ) {
+								if ( !options.unique || !self.has( arg ) ) {
+									list.push( arg );
+								}
+							} else if ( arg && arg.length && type !== "string" ) {
+								// Inspect recursively
+								add( arg );
+							}
+						});
+					})( arguments );
+					// Do we need to add the callbacks to the
+					// current firing batch?
+					if ( firing ) {
+						firingLength = list.length;
+					// With memory, if we're not firing then
+					// we should call right away
+					} else if ( memory ) {
+						firingStart = start;
+						fire( memory );
+					}
+				}
+				return this;
+			},
+			// Remove a callback from the list
+			remove: function() {
+				if ( list ) {
+					jQuery.each( arguments, function( _, arg ) {
+						var index;
+						while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+							list.splice( index, 1 );
+							// Handle firing indexes
+							if ( firing ) {
+								if ( index <= firingLength ) {
+									firingLength--;
+								}
+								if ( index <= firingIndex ) {
+									firingIndex--;
+								}
+							}
+						}
+					});
+				}
+				return this;
+			},
+			// Check if a given callback is in the list.
+			// If no argument is given, return whether or not list has callbacks attached.
+			has: function( fn ) {
+				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+			},
+			// Remove all callbacks from the list
+			empty: function() {
+				list = [];
+				firingLength = 0;
+				return this;
+			},
+			// Have the list do nothing anymore
+			disable: function() {
+				list = stack = memory = undefined;
+				return this;
+			},
+			// Is it disabled?
+			disabled: function() {
+				return !list;
+			},
+			// Lock the list in its current state
+			lock: function() {
+				stack = undefined;
+				if ( !memory ) {
+					self.disable();
+				}
+				return this;
+			},
+			// Is it locked?
+			locked: function() {
+				return !stack;
+			},
+			// Call all callbacks with the given context and arguments
+			fireWith: function( context, args ) {
+				if ( list && ( !fired || stack ) ) {
+					args = args || [];
+					args = [ context, args.slice ? args.slice() : args ];
+					if ( firing ) {
+						stack.push( args );
+					} else {
+						fire( args );
+					}
+				}
+				return this;
+			},
+			// Call all the callbacks with the given arguments
+			fire: function() {
+				self.fireWith( this, arguments );
+				return this;
+			},
+			// To know if the callbacks have already been called at least once
+			fired: function() {
+				return !!fired;
+			}
+		};
+
+	return self;
+};
+jQuery.extend({
+
+	Deferred: function( func ) {
+		var tuples = [
+				// action, add listener, listener list, final state
+				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+				[ "notify", "progress", jQuery.Callbacks("memory") ]
+			],
+			state = "pending",
+			promise = {
+				state: function() {
+					return state;
+				},
+				always: function() {
+					deferred.done( arguments ).fail( arguments );
+					return this;
+				},
+				then: function( /* fnDone, fnFail, fnProgress */ ) {
+					var fns = arguments;
+					return jQuery.Deferred(function( newDefer ) {
+						jQuery.each( tuples, function( i, tuple ) {
+							var action = tuple[ 0 ],
+								fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+							// deferred[ done | fail | progress ] for forwarding actions to newDefer
+							deferred[ tuple[1] ](function() {
+								var returned = fn && fn.apply( this, arguments );
+								if ( returned && jQuery.isFunction( returned.promise ) ) {
+									returned.promise()
+										.done( newDefer.resolve )
+										.fail( newDefer.reject )
+										.progress( newDefer.notify );
+								} else {
+									newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+								}
+							});
+						});
+						fns = null;
+					}).promise();
+				},
+				// Get a promise for this deferred
+				// If obj is provided, the promise aspect is added to the object
+				promise: function( obj ) {
+					return obj != null ? jQuery.extend( obj, promise ) : promise;
+				}
+			},
+			deferred = {};
+
+		// Keep pipe for back-compat
+		promise.pipe = promise.then;
+
+		// Add list-specific methods
+		jQuery.each( tuples, function( i, tuple ) {
+			var list = tuple[ 2 ],
+				stateString = tuple[ 3 ];
+
+			// promise[ done | fail | progress ] = list.add
+			promise[ tuple[1] ] = list.add;
+
+			// Handle state
+			if ( stateString ) {
+				list.add(function() {
+					// state = [ resolved | rejected ]
+					state = stateString;
+
+				// [ reject_list | resolve_list ].disable; progress_list.lock
+				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+			}
+
+			// deferred[ resolve | reject | notify ]
+			deferred[ tuple[0] ] = function() {
+				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+				return this;
+			};
+			deferred[ tuple[0] + "With" ] = list.fireWith;
+		});
+
+		// Make the deferred a promise
+		promise.promise( deferred );
+
+		// Call given func if any
+		if ( func ) {
+			func.call( deferred, deferred );
+		}
+
+		// All done!
+		return deferred;
+	},
+
+	// Deferred helper
+	when: function( subordinate /* , ..., subordinateN */ ) {
+		var i = 0,
+			resolveValues = core_slice.call( arguments ),
+			length = resolveValues.length,
+
+			// the count of uncompleted subordinates
+			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+			// Update function for both resolve and progress values
+			updateFunc = function( i, contexts, values ) {
+				return function( value ) {
+					contexts[ i ] = this;
+					values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+					if( values === progressValues ) {
+						deferred.notifyWith( contexts, values );
+					} else if ( !( --remaining ) ) {
+						deferred.resolveWith( contexts, values );
+					}
+				};
+			},
+
+			progressValues, progressContexts, resolveContexts;
+
+		// add listeners to Deferred subordinates; treat others as resolved
+		if ( length > 1 ) {
+			progressValues = new Array( length );
+			progressContexts = new Array( length );
+			resolveContexts = new Array( length );
+			for ( ; i < length; i++ ) {
+				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+					resolveValues[ i ].promise()
+						.done( updateFunc( i, resolveContexts, resolveValues ) )
+						.fail( deferred.reject )
+						.progress( updateFunc( i, progressContexts, progressValues ) );
+				} else {
+					--remaining;
+				}
+			}
+		}
+
+		// if we're not waiting on anything, resolve the master
+		if ( !remaining ) {
+			deferred.resolveWith( resolveContexts, resolveValues );
+		}
+
+		return deferred.promise();
+	}
+});
+jQuery.support = (function( support ) {
+
+	var all, a, input, select, fragment, opt, eventName, isSupported, i,
+		div = document.createElement("div");
+
+	// Setup
+	div.setAttribute( "className", "t" );
+	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+	// Finish early in limited (non-browser) environments
+	all = div.getElementsByTagName("*") || [];
+	a = div.getElementsByTagName("a")[ 0 ];
+	if ( !a || !a.style || !all.length ) {
+		return support;
+	}
+
+	// First batch of tests
+	select = document.createElement("select");
+	opt = select.appendChild( document.createElement("option") );
+	input = div.getElementsByTagName("input")[ 0 ];
+
+	a.style.cssText = "top:1px;float:left;opacity:.5";
+
+	// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+	support.getSetAttribute = div.className !== "t";
+
+	// IE strips leading whitespace when .innerHTML is used
+	support.leadingWhitespace = div.firstChild.nodeType === 3;
+
+	// Make sure that tbody elements aren't automatically inserted
+	// IE will insert them into empty tables
+	support.tbody = !div.getElementsByTagName("tbody").length;
+
+	// Make sure that link elements get serialized correctly by innerHTML
+	// This requires a wrapper element in IE
+	support.htmlSerialize = !!div.getElementsByTagName("link").length;
+
+	// Get the style information from getAttribute
+	// (IE uses .cssText instead)
+	support.style = /top/.test( a.getAttribute("style") );
+
+	// Make sure that URLs aren't manipulated
+	// (IE normalizes it by default)
+	support.hrefNormalized = a.getAttribute("href") === "/a";
+
+	// Make sure that element opacity exists
+	// (IE uses filter instead)
+	// Use a regex to work around a WebKit issue. See #5145
+	support.opacity = /^0.5/.test( a.style.opacity );
+
+	// Verify style float existence
+	// (IE uses styleFloat instead of cssFloat)
+	support.cssFloat = !!a.style.cssFloat;
+
+	// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+	support.checkOn = !!input.value;
+
+	// Make sure that a selected-by-default option has a working selected property.
+	// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+	support.optSelected = opt.selected;
+
+	// Tests for enctype support on a form (#6743)
+	support.enctype = !!document.createElement("form").enctype;
+
+	// Makes sure cloning an html5 element does not cause problems
+	// Where outerHTML is undefined, this still works
+	support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>";
+
+	// Will be defined later
+	support.inlineBlockNeedsLayout = false;
+	support.shrinkWrapBlocks = false;
+	support.pixelPosition = false;
+	support.deleteExpando = true;
+	support.noCloneEvent = true;
+	support.reliableMarginRight = true;
+	support.boxSizingReliable = true;
+
+	// Make sure checked status is properly cloned
+	input.checked = true;
+	support.noCloneChecked = input.cloneNode( true ).checked;
+
+	// Make sure that the options inside disabled selects aren't marked as disabled
+	// (WebKit marks them as disabled)
+	select.disabled = true;
+	support.optDisabled = !opt.disabled;
+
+	// Support: IE<9
+	try {
+		delete div.test;
+	} catch( e ) {
+		support.deleteExpando = false;
+	}
+
+	// Check if we can trust getAttribute("value")
+	input = document.createElement("input");
+	input.setAttribute( "value", "" );
+	support.input = input.getAttribute( "value" ) === "";
+
+	// Check if an input maintains its value after becoming a radio
+	input.value = "t";
+	input.setAttribute( "type", "radio" );
+	support.radioValue = input.value === "t";
+
+	// #11217 - WebKit loses check when the name is after the checked attribute
+	input.setAttribute( "checked", "t" );
+	input.setAttribute( "name", "t" );
+
+	fragment = document.createDocumentFragment();
+	fragment.appendChild( input );
+
+	// Check if a disconnected checkbox will retain its checked
+	// value of true after appended to the DOM (IE6/7)
+	support.appendChecked = input.checked;
+
+	// WebKit doesn't clone checked state correctly in fragments
+	support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+	// Support: IE<9
+	// Opera does not clone events (and typeof div.attachEvent === undefined).
+	// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+	if ( div.attachEvent ) {
+		div.attachEvent( "onclick", function() {
+			support.noCloneEvent = false;
+		});
+
+		div.cloneNode( true ).click();
+	}
+
+	// Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
+	// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+	for ( i in { submit: true, change: true, focusin: true }) {
+		div.setAttribute( eventName = "on" + i, "t" );
+
+		support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
+	}
+
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	// Support: IE<9
+	// Iteration over object's inherited properties before its own.
+	for ( i in jQuery( support ) ) {
+		break;
+	}
+	support.ownLast = i !== "0";
+
+	// Run tests that need a body at doc ready
+	jQuery(function() {
+		var container, marginDiv, tds,
+			divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
+			body = document.getElementsByTagName("body")[0];
+
+		if ( !body ) {
+			// Return for frameset docs that don't have a body
+			return;
+		}
+
+		container = document.createElement("div");
+		container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+
+		body.appendChild( container ).appendChild( div );
+
+		// Support: IE8
+		// Check if table cells still have offsetWidth/Height when they are set
+		// to display:none and there are still other visible table cells in a
+		// table row; if so, offsetWidth/Height are not reliable for use when
+		// determining if an element has been hidden directly using
+		// display:none (it is still safe to use offsets if a parent element is
+		// hidden; don safety goggles and see bug #4512 for more information).
+		div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+		tds = div.getElementsByTagName("td");
+		tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+		isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+		tds[ 0 ].style.display = "";
+		tds[ 1 ].style.display = "none";
+
+		// Support: IE8
+		// Check if empty table cells still have offsetWidth/Height
+		support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+		// Check box-sizing and margin behavior.
+		div.innerHTML = "";
+		div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+
+		// Workaround failing boxSizing test due to offsetWidth returning wrong value
+		// with some non-1 values of body zoom, ticket #13543
+		jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
+			support.boxSizing = div.offsetWidth === 4;
+		});
+
+		// Use window.getComputedStyle because jsdom on node.js will break without it.
+		if ( window.getComputedStyle ) {
+			support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+			support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+			// Check if div with explicit width and no margin-right incorrectly
+			// gets computed margin-right based on width of container. (#3333)
+			// Fails in WebKit before Feb 2011 nightlies
+			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+			marginDiv = div.appendChild( document.createElement("div") );
+			marginDiv.style.cssText = div.style.cssText = divReset;
+			marginDiv.style.marginRight = marginDiv.style.width = "0";
+			div.style.width = "1px";
+
+			support.reliableMarginRight =
+				!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+		}
+
+		if ( typeof div.style.zoom !== core_strundefined ) {
+			// Support: IE<8
+			// Check if natively block-level elements act like inline-block
+			// elements when setting their display to 'inline' and giving
+			// them layout
+			div.innerHTML = "";
+			div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+			support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+			// Support: IE6
+			// Check if elements with layout shrink-wrap their children
+			div.style.display = "block";
+			div.innerHTML = "<div></div>";
+			div.firstChild.style.width = "5px";
+			support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+			if ( support.inlineBlockNeedsLayout ) {
+				// Prevent IE 6 from affecting layout for positioned elements #11048
+				// Prevent IE from shrinking the body in IE 7 mode #12869
+				// Support: IE<8
+				body.style.zoom = 1;
+			}
+		}
+
+		body.removeChild( container );
+
+		// Null elements to avoid leaks in IE
+		container = div = tds = marginDiv = null;
+	});
+
+	// Null elements to avoid leaks in IE
+	all = select = fragment = opt = a = input = null;
+
+	return support;
+})({});
+
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+	rmultiDash = /([A-Z])/g;
+
+function internalData( elem, name, data, pvt /* Internal Use Only */ ){
+	if ( !jQuery.acceptData( elem ) ) {
+		return;
+	}
+
+	var ret, thisCache,
+		internalKey = jQuery.expando,
+
+		// We have to handle DOM nodes and JS objects differently because IE6-7
+		// can't GC object references properly across the DOM-JS boundary
+		isNode = elem.nodeType,
+
+		// Only DOM nodes need the global jQuery cache; JS object data is
+		// attached directly to the object so GC can occur automatically
+		cache = isNode ? jQuery.cache : elem,
+
+		// Only defining an ID for JS objects if its cache already exists allows
+		// the code to shortcut on the same path as a DOM node with no cache
+		id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+	// Avoid doing any more work than we need to when trying to get data on an
+	// object that has no data at all
+	if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
+		return;
+	}
+
+	if ( !id ) {
+		// Only DOM nodes need a new unique ID for each element since their data
+		// ends up in the global cache
+		if ( isNode ) {
+			id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;
+		} else {
+			id = internalKey;
+		}
+	}
+
+	if ( !cache[ id ] ) {
+		// Avoid exposing jQuery metadata on plain JS objects when the object
+		// is serialized using JSON.stringify
+		cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+	}
+
+	// An object can be passed to jQuery.data instead of a key/value pair; this gets
+	// shallow copied over onto the existing cache
+	if ( typeof name === "object" || typeof name === "function" ) {
+		if ( pvt ) {
+			cache[ id ] = jQuery.extend( cache[ id ], name );
+		} else {
+			cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+		}
+	}
+
+	thisCache = cache[ id ];
+
+	// jQuery data() is stored in a separate object inside the object's internal data
+	// cache in order to avoid key collisions between internal data and user-defined
+	// data.
+	if ( !pvt ) {
+		if ( !thisCache.data ) {
+			thisCache.data = {};
+		}
+
+		thisCache = thisCache.data;
+	}
+
+	if ( data !== undefined ) {
+		thisCache[ jQuery.camelCase( name ) ] = data;
+	}
+
+	// Check for both converted-to-camel and non-converted data property names
+	// If a data property was specified
+	if ( typeof name === "string" ) {
+
+		// First Try to find as-is property data
+		ret = thisCache[ name ];
+
+		// Test for null|undefined property data
+		if ( ret == null ) {
+
+			// Try to find the camelCased property
+			ret = thisCache[ jQuery.camelCase( name ) ];
+		}
+	} else {
+		ret = thisCache;
+	}
+
+	return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+	if ( !jQuery.acceptData( elem ) ) {
+		return;
+	}
+
+	var thisCache, i,
+		isNode = elem.nodeType,
+
+		// See jQuery.data for more information
+		cache = isNode ? jQuery.cache : elem,
+		id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+	// If there is already no cache entry for this object, there is no
+	// purpose in continuing
+	if ( !cache[ id ] ) {
+		return;
+	}
+
+	if ( name ) {
+
+		thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+		if ( thisCache ) {
+
+			// Support array or space separated string names for data keys
+			if ( !jQuery.isArray( name ) ) {
+
+				// try the string as a key before any manipulation
+				if ( name in thisCache ) {
+					name = [ name ];
+				} else {
+
+					// split the camel cased version by spaces unless a key with the spaces exists
+					name = jQuery.camelCase( name );
+					if ( name in thisCache ) {
+						name = [ name ];
+					} else {
+						name = name.split(" ");
+					}
+				}
+			} else {
+				// If "name" is an array of keys...
+				// When data is initially created, via ("key", "val") signature,
+				// keys will be converted to camelCase.
+				// Since there is no way to tell _how_ a key was added, remove
+				// both plain key and camelCase key. #12786
+				// This will only penalize the array argument path.
+				name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+			}
+
+			i = name.length;
+			while ( i-- ) {
+				delete thisCache[ name[i] ];
+			}
+
+			// If there is no data left in the cache, we want to continue
+			// and let the cache object itself get destroyed
+			if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
+				return;
+			}
+		}
+	}
+
+	// See jQuery.data for more information
+	if ( !pvt ) {
+		delete cache[ id ].data;
+
+		// Don't destroy the parent cache unless the internal data object
+		// had been the only thing left in it
+		if ( !isEmptyDataObject( cache[ id ] ) ) {
+			return;
+		}
+	}
+
+	// Destroy the cache
+	if ( isNode ) {
+		jQuery.cleanData( [ elem ], true );
+
+	// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+	/* jshint eqeqeq: false */
+	} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+		/* jshint eqeqeq: true */
+		delete cache[ id ];
+
+	// When all else fails, null
+	} else {
+		cache[ id ] = null;
+	}
+}
+
+jQuery.extend({
+	cache: {},
+
+	// The following elements throw uncatchable exceptions if you
+	// attempt to add expando properties to them.
+	noData: {
+		"applet": true,
+		"embed": true,
+		// Ban all objects except for Flash (which handle expandos)
+		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+	},
+
+	hasData: function( elem ) {
+		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+		return !!elem && !isEmptyDataObject( elem );
+	},
+
+	data: function( elem, name, data ) {
+		return internalData( elem, name, data );
+	},
+
+	removeData: function( elem, name ) {
+		return internalRemoveData( elem, name );
+	},
+
+	// For internal use only.
+	_data: function( elem, name, data ) {
+		return internalData( elem, name, data, true );
+	},
+
+	_removeData: function( elem, name ) {
+		return internalRemoveData( elem, name, true );
+	},
+
+	// A method for determining if a DOM node can handle the data expando
+	acceptData: function( elem ) {
+		// Do not set data on non-element because it will not be cleared (#8335).
+		if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
+			return false;
+		}
+
+		var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+		// nodes accept data unless otherwise specified; rejection can be conditional
+		return !noData || noData !== true && elem.getAttribute("classid") === noData;
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ) {
+		var attrs, name,
+			data = null,
+			i = 0,
+			elem = this[0];
+
+		// Special expections of .data basically thwart jQuery.access,
+		// so implement the relevant behavior ourselves
+
+		// Gets all values
+		if ( key === undefined ) {
+			if ( this.length ) {
+				data = jQuery.data( elem );
+
+				if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+					attrs = elem.attributes;
+					for ( ; i < attrs.length; i++ ) {
+						name = attrs[i].name;
+
+						if ( name.indexOf("data-") === 0 ) {
+							name = jQuery.camelCase( name.slice(5) );
+
+							dataAttr( elem, name, data[ name ] );
+						}
+					}
+					jQuery._data( elem, "parsedAttrs", true );
+				}
+			}
+
+			return data;
+		}
+
+		// Sets multiple values
+		if ( typeof key === "object" ) {
+			return this.each(function() {
+				jQuery.data( this, key );
+			});
+		}
+
+		return arguments.length > 1 ?
+
+			// Sets one value
+			this.each(function() {
+				jQuery.data( this, key, value );
+			}) :
+
+			// Gets one value
+			// Try to fetch any internally stored data first
+			elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
+	},
+
+	removeData: function( key ) {
+		return this.each(function() {
+			jQuery.removeData( this, key );
+		});
+	}
+});
+
+function dataAttr( elem, key, data ) {
+	// If nothing was found internally, try to fetch any
+	// data from the HTML5 data-* attribute
+	if ( data === undefined && elem.nodeType === 1 ) {
+
+		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+		data = elem.getAttribute( name );
+
+		if ( typeof data === "string" ) {
+			try {
+				data = data === "true" ? true :
+					data === "false" ? false :
+					data === "null" ? null :
+					// Only convert to a number if it doesn't change the string
+					+data + "" === data ? +data :
+					rbrace.test( data ) ? jQuery.parseJSON( data ) :
+						data;
+			} catch( e ) {}
+
+			// Make sure we set the data so it isn't changed later
+			jQuery.data( elem, key, data );
+
+		} else {
+			data = undefined;
+		}
+	}
+
+	return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+	var name;
+	for ( name in obj ) {
+
+		// if the public data object is empty, the private is still empty
+		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+			continue;
+		}
+		if ( name !== "toJSON" ) {
+			return false;
+		}
+	}
+
+	return true;
+}
+jQuery.extend({
+	queue: function( elem, type, data ) {
+		var queue;
+
+		if ( elem ) {
+			type = ( type || "fx" ) + "queue";
+			queue = jQuery._data( elem, type );
+
+			// Speed up dequeue by getting out quickly if this is just a lookup
+			if ( data ) {
+				if ( !queue || jQuery.isArray(data) ) {
+					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+				} else {
+					queue.push( data );
+				}
+			}
+			return queue || [];
+		}
+	},
+
+	dequeue: function( elem, type ) {
+		type = type || "fx";
+
+		var queue = jQuery.queue( elem, type ),
+			startLength = queue.length,
+			fn = queue.shift(),
+			hooks = jQuery._queueHooks( elem, type ),
+			next = function() {
+				jQuery.dequeue( elem, type );
+			};
+
+		// If the fx queue is dequeued, always remove the progress sentinel
+		if ( fn === "inprogress" ) {
+			fn = queue.shift();
+			startLength--;
+		}
+
+		if ( fn ) {
+
+			// Add a progress sentinel to prevent the fx queue from being
+			// automatically dequeued
+			if ( type === "fx" ) {
+				queue.unshift( "inprogress" );
+			}
+
+			// clear up the last queue stop function
+			delete hooks.stop;
+			fn.call( elem, next, hooks );
+		}
+
+		if ( !startLength && hooks ) {
+			hooks.empty.fire();
+		}
+	},
+
+	// not intended for public consumption - generates a queueHooks object, or returns the current one
+	_queueHooks: function( elem, type ) {
+		var key = type + "queueHooks";
+		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+			empty: jQuery.Callbacks("once memory").add(function() {
+				jQuery._removeData( elem, type + "queue" );
+				jQuery._removeData( elem, key );
+			})
+		});
+	}
+});
+
+jQuery.fn.extend({
+	queue: function( type, data ) {
+		var setter = 2;
+
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+			setter--;
+		}
+
+		if ( arguments.length < setter ) {
+			return jQuery.queue( this[0], type );
+		}
+
+		return data === undefined ?
+			this :
+			this.each(function() {
+				var queue = jQuery.queue( this, type, data );
+
+				// ensure a hooks for this queue
+				jQuery._queueHooks( this, type );
+
+				if ( type === "fx" && queue[0] !== "inprogress" ) {
+					jQuery.dequeue( this, type );
+				}
+			});
+	},
+	dequeue: function( type ) {
+		return this.each(function() {
+			jQuery.dequeue( this, type );
+		});
+	},
+	// Based off of the plugin by Clint Helfers, with permission.
+	// http://blindsignals.com/index.php/2009/07/jquery-delay/
+	delay: function( time, type ) {
+		time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+		type = type || "fx";
+
+		return this.queue( type, function( next, hooks ) {
+			var timeout = setTimeout( next, time );
+			hooks.stop = function() {
+				clearTimeout( timeout );
+			};
+		});
+	},
+	clearQueue: function( type ) {
+		return this.queue( type || "fx", [] );
+	},
+	// Get a promise resolved when queues of a certain type
+	// are emptied (fx is the type by default)
+	promise: function( type, obj ) {
+		var tmp,
+			count = 1,
+			defer = jQuery.Deferred(),
+			elements = this,
+			i = this.length,
+			resolve = function() {
+				if ( !( --count ) ) {
+					defer.resolveWith( elements, [ elements ] );
+				}
+			};
+
+		if ( typeof type !== "string" ) {
+			obj = type;
+			type = undefined;
+		}
+		type = type || "fx";
+
+		while( i-- ) {
+			tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+			if ( tmp && tmp.empty ) {
+				count++;
+				tmp.empty.add( resolve );
+			}
+		}
+		resolve();
+		return defer.promise( obj );
+	}
+});
+var nodeHook, boolHook,
+	rclass = /[\t\r\n\f]/g,
+	rreturn = /\r/g,
+	rfocusable = /^(?:input|select|textarea|button|object)$/i,
+	rclickable = /^(?:a|area)$/i,
+	ruseDefault = /^(?:checked|selected)$/i,
+	getSetAttribute = jQuery.support.getSetAttribute,
+	getSetInput = jQuery.support.input;
+
+jQuery.fn.extend({
+	attr: function( name, value ) {
+		return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+	},
+
+	removeAttr: function( name ) {
+		return this.each(function() {
+			jQuery.removeAttr( this, name );
+		});
+	},
+
+	prop: function( name, value ) {
+		return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+	},
+
+	removeProp: function( name ) {
+		name = jQuery.propFix[ name ] || name;
+		return this.each(function() {
+			// try/catch handles cases where IE balks (such as removing a property on window)
+			try {
+				this[ name ] = undefined;
+				delete this[ name ];
+			} catch( e ) {}
+		});
+	},
+
+	addClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = typeof value === "string" && value;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).addClass( value.call( this, j, this.className ) );
+			});
+		}
+
+		if ( proceed ) {
+			// The disjunction here is for better compressibility (see removeClass)
+			classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					" "
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+							cur += clazz + " ";
+						}
+					}
+					elem.className = jQuery.trim( cur );
+
+				}
+			}
+		}
+
+		return this;
+	},
+
+	removeClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = arguments.length === 0 || typeof value === "string" && value;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+			});
+		}
+		if ( proceed ) {
+			classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				// This expression is here for better compressibility (see addClass)
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					""
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						// Remove *all* instances
+						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+							cur = cur.replace( " " + clazz + " ", " " );
+						}
+					}
+					elem.className = value ? jQuery.trim( cur ) : "";
+				}
+			}
+		}
+
+		return this;
+	},
+
+	toggleClass: function( value, stateVal ) {
+		var type = typeof value;
+
+		if ( typeof stateVal === "boolean" && type === "string" ) {
+			return stateVal ? this.addClass( value ) : this.removeClass( value );
+		}
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+			});
+		}
+
+		return this.each(function() {
+			if ( type === "string" ) {
+				// toggle individual class names
+				var className,
+					i = 0,
+					self = jQuery( this ),
+					classNames = value.match( core_rnotwhite ) || [];
+
+				while ( (className = classNames[ i++ ]) ) {
+					// check each className given, space separated list
+					if ( self.hasClass( className ) ) {
+						self.removeClass( className );
+					} else {
+						self.addClass( className );
+					}
+				}
+
+			// Toggle whole class name
+			} else if ( type === core_strundefined || type === "boolean" ) {
+				if ( this.className ) {
+					// store className if set
+					jQuery._data( this, "__className__", this.className );
+				}
+
+				// If the element has a class name or if we're passed "false",
+				// then remove the whole classname (if there was one, the above saved it).
+				// Otherwise bring back whatever was previously saved (if anything),
+				// falling back to the empty string if nothing was stored.
+				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+			}
+		});
+	},
+
+	hasClass: function( selector ) {
+		var className = " " + selector + " ",
+			i = 0,
+			l = this.length;
+		for ( ; i < l; i++ ) {
+			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+				return true;
+			}
+		}
+
+		return false;
+	},
+
+	val: function( value ) {
+		var ret, hooks, isFunction,
+			elem = this[0];
+
+		if ( !arguments.length ) {
+			if ( elem ) {
+				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+					return ret;
+				}
+
+				ret = elem.value;
+
+				return typeof ret === "string" ?
+					// handle most common string cases
+					ret.replace(rreturn, "") :
+					// handle cases where value is null/undef or number
+					ret == null ? "" : ret;
+			}
+
+			return;
+		}
+
+		isFunction = jQuery.isFunction( value );
+
+		return this.each(function( i ) {
+			var val;
+
+			if ( this.nodeType !== 1 ) {
+				return;
+			}
+
+			if ( isFunction ) {
+				val = value.call( this, i, jQuery( this ).val() );
+			} else {
+				val = value;
+			}
+
+			// Treat null/undefined as ""; convert numbers to string
+			if ( val == null ) {
+				val = "";
+			} else if ( typeof val === "number" ) {
+				val += "";
+			} else if ( jQuery.isArray( val ) ) {
+				val = jQuery.map(val, function ( value ) {
+					return value == null ? "" : value + "";
+				});
+			}
+
+			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+			// If set returns undefined, fall back to normal setting
+			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+				this.value = val;
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	valHooks: {
+		option: {
+			get: function( elem ) {
+				// Use proper attribute retrieval(#6932, #12072)
+				var val = jQuery.find.attr( elem, "value" );
+				return val != null ?
+					val :
+					elem.text;
+			}
+		},
+		select: {
+			get: function( elem ) {
+				var value, option,
+					options = elem.options,
+					index = elem.selectedIndex,
+					one = elem.type === "select-one" || index < 0,
+					values = one ? null : [],
+					max = one ? index + 1 : options.length,
+					i = index < 0 ?
+						max :
+						one ? index : 0;
+
+				// Loop through all the selected options
+				for ( ; i < max; i++ ) {
+					option = options[ i ];
+
+					// oldIE doesn't update selected after form reset (#2551)
+					if ( ( option.selected || i === index ) &&
+							// Don't return options that are disabled or in a disabled optgroup
+							( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+						// Get the specific value for the option
+						value = jQuery( option ).val();
+
+						// We don't need an array for one selects
+						if ( one ) {
+							return value;
+						}
+
+						// Multi-Selects return an array
+						values.push( value );
+					}
+				}
+
+				return values;
+			},
+
+			set: function( elem, value ) {
+				var optionSet, option,
+					options = elem.options,
+					values = jQuery.makeArray( value ),
+					i = options.length;
+
+				while ( i-- ) {
+					option = options[ i ];
+					if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
+						optionSet = true;
+					}
+				}
+
+				// force browsers to behave consistently when non-matching value is set
+				if ( !optionSet ) {
+					elem.selectedIndex = -1;
+				}
+				return values;
+			}
+		}
+	},
+
+	attr: function( elem, name, value ) {
+		var hooks, ret,
+			nType = elem.nodeType;
+
+		// don't get/set attributes on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		// Fallback to prop when attributes are not supported
+		if ( typeof elem.getAttribute === core_strundefined ) {
+			return jQuery.prop( elem, name, value );
+		}
+
+		// All attributes are lowercase
+		// Grab necessary hook if one is defined
+		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+			name = name.toLowerCase();
+			hooks = jQuery.attrHooks[ name ] ||
+				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+		}
+
+		if ( value !== undefined ) {
+
+			if ( value === null ) {
+				jQuery.removeAttr( elem, name );
+
+			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				elem.setAttribute( name, value + "" );
+				return value;
+			}
+
+		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+			return ret;
+
+		} else {
+			ret = jQuery.find.attr( elem, name );
+
+			// Non-existent attributes return null, we normalize to undefined
+			return ret == null ?
+				undefined :
+				ret;
+		}
+	},
+
+	removeAttr: function( elem, value ) {
+		var name, propName,
+			i = 0,
+			attrNames = value && value.match( core_rnotwhite );
+
+		if ( attrNames && elem.nodeType === 1 ) {
+			while ( (name = attrNames[i++]) ) {
+				propName = jQuery.propFix[ name ] || name;
+
+				// Boolean attributes get special treatment (#10870)
+				if ( jQuery.expr.match.bool.test( name ) ) {
+					// Set corresponding property to false
+					if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+						elem[ propName ] = false;
+					// Support: IE<9
+					// Also clear defaultChecked/defaultSelected (if appropriate)
+					} else {
+						elem[ jQuery.camelCase( "default-" + name ) ] =
+							elem[ propName ] = false;
+					}
+
+				// See #9699 for explanation of this approach (setting first, then removal)
+				} else {
+					jQuery.attr( elem, name, "" );
+				}
+
+				elem.removeAttribute( getSetAttribute ? name : propName );
+			}
+		}
+	},
+
+	attrHooks: {
+		type: {
+			set: function( elem, value ) {
+				if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+					// Setting the type on a radio button after the value resets the value in IE6-9
+					// Reset value to default in case type is set after value during creation
+					var val = elem.value;
+					elem.setAttribute( "type", value );
+					if ( val ) {
+						elem.value = val;
+					}
+					return value;
+				}
+			}
+		}
+	},
+
+	propFix: {
+		"for": "htmlFor",
+		"class": "className"
+	},
+
+	prop: function( elem, name, value ) {
+		var ret, hooks, notxml,
+			nType = elem.nodeType;
+
+		// don't get/set properties on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+		if ( notxml ) {
+			// Fix name and attach hooks
+			name = jQuery.propFix[ name ] || name;
+			hooks = jQuery.propHooks[ name ];
+		}
+
+		if ( value !== undefined ) {
+			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+				ret :
+				( elem[ name ] = value );
+
+		} else {
+			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+				ret :
+				elem[ name ];
+		}
+	},
+
+	propHooks: {
+		tabIndex: {
+			get: function( elem ) {
+				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+				// Use proper attribute retrieval(#12072)
+				var tabindex = jQuery.find.attr( elem, "tabindex" );
+
+				return tabindex ?
+					parseInt( tabindex, 10 ) :
+					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+						0 :
+						-1;
+			}
+		}
+	}
+});
+
+// Hooks for boolean attributes
+boolHook = {
+	set: function( elem, value, name ) {
+		if ( value === false ) {
+			// Remove boolean attributes when set to false
+			jQuery.removeAttr( elem, name );
+		} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+			// IE<8 needs the *property* name
+			elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+
+		// Use defaultChecked and defaultSelected for oldIE
+		} else {
+			elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+		}
+
+		return name;
+	}
+};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+	var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+
+	jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
+		function( elem, name, isXML ) {
+			var fn = jQuery.expr.attrHandle[ name ],
+				ret = isXML ?
+					undefined :
+					/* jshint eqeqeq: false */
+					(jQuery.expr.attrHandle[ name ] = undefined) !=
+						getter( elem, name, isXML ) ?
+
+						name.toLowerCase() :
+						null;
+			jQuery.expr.attrHandle[ name ] = fn;
+			return ret;
+		} :
+		function( elem, name, isXML ) {
+			return isXML ?
+				undefined :
+				elem[ jQuery.camelCase( "default-" + name ) ] ?
+					name.toLowerCase() :
+					null;
+		};
+});
+
+// fix oldIE attroperties
+if ( !getSetInput || !getSetAttribute ) {
+	jQuery.attrHooks.value = {
+		set: function( elem, value, name ) {
+			if ( jQuery.nodeName( elem, "input" ) ) {
+				// Does not return so that setAttribute is also used
+				elem.defaultValue = value;
+			} else {
+				// Use nodeHook if defined (#1954); otherwise setAttribute is fine
+				return nodeHook && nodeHook.set( elem, value, name );
+			}
+		}
+	};
+}
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+	// Use this for any attribute in IE6/7
+	// This fixes almost every IE6/7 issue
+	nodeHook = {
+		set: function( elem, value, name ) {
+			// Set the existing or create a new attribute node
+			var ret = elem.getAttributeNode( name );
+			if ( !ret ) {
+				elem.setAttributeNode(
+					(ret = elem.ownerDocument.createAttribute( name ))
+				);
+			}
+
+			ret.value = value += "";
+
+			// Break association with cloned elements by also using setAttribute (#9646)
+			return name === "value" || value === elem.getAttribute( name ) ?
+				value :
+				undefined;
+		}
+	};
+	jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords =
+		// Some attributes are constructed with empty-string values when not defined
+		function( elem, name, isXML ) {
+			var ret;
+			return isXML ?
+				undefined :
+				(ret = elem.getAttributeNode( name )) && ret.value !== "" ?
+					ret.value :
+					null;
+		};
+	jQuery.valHooks.button = {
+		get: function( elem, name ) {
+			var ret = elem.getAttributeNode( name );
+			return ret && ret.specified ?
+				ret.value :
+				undefined;
+		},
+		set: nodeHook.set
+	};
+
+	// Set contenteditable to false on removals(#10429)
+	// Setting to empty string throws an error as an invalid value
+	jQuery.attrHooks.contenteditable = {
+		set: function( elem, value, name ) {
+			nodeHook.set( elem, value === "" ? false : value, name );
+		}
+	};
+
+	// Set width and height to auto instead of 0 on empty string( Bug #8150 )
+	// This is for removals
+	jQuery.each([ "width", "height" ], function( i, name ) {
+		jQuery.attrHooks[ name ] = {
+			set: function( elem, value ) {
+				if ( value === "" ) {
+					elem.setAttribute( name, "auto" );
+					return value;
+				}
+			}
+		};
+	});
+}
+
+
+// Some attributes require a special call on IE
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !jQuery.support.hrefNormalized ) {
+	// href/src property should get the full normalized URL (#10299/#12915)
+	jQuery.each([ "href", "src" ], function( i, name ) {
+		jQuery.propHooks[ name ] = {
+			get: function( elem ) {
+				return elem.getAttribute( name, 4 );
+			}
+		};
+	});
+}
+
+if ( !jQuery.support.style ) {
+	jQuery.attrHooks.style = {
+		get: function( elem ) {
+			// Return undefined in the case of empty string
+			// Note: IE uppercases css property names, but if we were to .toLowerCase()
+			// .cssText, that would destroy case senstitivity in URL's, like in "background"
+			return elem.style.cssText || undefined;
+		},
+		set: function( elem, value ) {
+			return ( elem.style.cssText = value + "" );
+		}
+	};
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+	jQuery.propHooks.selected = {
+		get: function( elem ) {
+			var parent = elem.parentNode;
+
+			if ( parent ) {
+				parent.selectedIndex;
+
+				// Make sure that it also works with optgroups, see #5701
+				if ( parent.parentNode ) {
+					parent.parentNode.selectedIndex;
+				}
+			}
+			return null;
+		}
+	};
+}
+
+jQuery.each([
+	"tabIndex",
+	"readOnly",
+	"maxLength",
+	"cellSpacing",
+	"cellPadding",
+	"rowSpan",
+	"colSpan",
+	"useMap",
+	"frameBorder",
+	"contentEditable"
+], function() {
+	jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+	jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+	jQuery.valHooks[ this ] = {
+		set: function( elem, value ) {
+			if ( jQuery.isArray( value ) ) {
+				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+			}
+		}
+	};
+	if ( !jQuery.support.checkOn ) {
+		jQuery.valHooks[ this ].get = function( elem ) {
+			// Support: Webkit
+			// "" is returned instead of "on" if a value isn't specified
+			return elem.getAttribute("value") === null ? "on" : elem.value;
+		};
+	}
+});
+var rformElems = /^(?:input|select|textarea)$/i,
+	rkeyEvent = /^key/,
+	rmouseEvent = /^(?:mouse|contextmenu)|click/,
+	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+	return true;
+}
+
+function returnFalse() {
+	return false;
+}
+
+function safeActiveElement() {
+	try {
+		return document.activeElement;
+	} catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+	global: {},
+
+	add: function( elem, types, handler, data, selector ) {
+		var tmp, events, t, handleObjIn,
+			special, eventHandle, handleObj,
+			handlers, type, namespaces, origType,
+			elemData = jQuery._data( elem );
+
+		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+		if ( !elemData ) {
+			return;
+		}
+
+		// Caller can pass in an object of custom data in lieu of the handler
+		if ( handler.handler ) {
+			handleObjIn = handler;
+			handler = handleObjIn.handler;
+			selector = handleObjIn.selector;
+		}
+
+		// Make sure that the handler has a unique ID, used to find/remove it later
+		if ( !handler.guid ) {
+			handler.guid = jQuery.guid++;
+		}
+
+		// Init the element's event structure and main handler, if this is the first
+		if ( !(events = elemData.events) ) {
+			events = elemData.events = {};
+		}
+		if ( !(eventHandle = elemData.handle) ) {
+			eventHandle = elemData.handle = function( e ) {
+				// Discard the second event of a jQuery.event.trigger() and
+				// when an event is called after a page has unloaded
+				return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+					undefined;
+			};
+			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+			eventHandle.elem = elem;
+		}
+
+		// Handle multiple events separated by a space
+		types = ( types || "" ).match( core_rnotwhite ) || [""];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// There *must* be a type, no attaching namespace-only handlers
+			if ( !type ) {
+				continue;
+			}
+
+			// If event changes its type, use the special event handlers for the changed type
+			special = jQuery.event.special[ type ] || {};
+
+			// If selector defined, determine special event api type, otherwise given type
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+
+			// Update special based on newly reset type
+			special = jQuery.event.special[ type ] || {};
+
+			// handleObj is passed to all event handlers
+			handleObj = jQuery.extend({
+				type: type,
+				origType: origType,
+				data: data,
+				handler: handler,
+				guid: handler.guid,
+				selector: selector,
+				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+				namespace: namespaces.join(".")
+			}, handleObjIn );
+
+			// Init the event handler queue if we're the first
+			if ( !(handlers = events[ type ]) ) {
+				handlers = events[ type ] = [];
+				handlers.delegateCount = 0;
+
+				// Only use addEventListener/attachEvent if the special events handler returns false
+				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+					// Bind the global event handler to the element
+					if ( elem.addEventListener ) {
+						elem.addEventListener( type, eventHandle, false );
+
+					} else if ( elem.attachEvent ) {
+						elem.attachEvent( "on" + type, eventHandle );
+					}
+				}
+			}
+
+			if ( special.add ) {
+				special.add.call( elem, handleObj );
+
+				if ( !handleObj.handler.guid ) {
+					handleObj.handler.guid = handler.guid;
+				}
+			}
+
+			// Add to the element's handler list, delegates in front
+			if ( selector ) {
+				handlers.splice( handlers.delegateCount++, 0, handleObj );
+			} else {
+				handlers.push( handleObj );
+			}
+
+			// Keep track of which events have ever been used, for event optimization
+			jQuery.event.global[ type ] = true;
+		}
+
+		// Nullify elem to prevent memory leaks in IE
+		elem = null;
+	},
+
+	// Detach an event or set of events from an element
+	remove: function( elem, types, handler, selector, mappedTypes ) {
+		var j, handleObj, tmp,
+			origCount, t, events,
+			special, handlers, type,
+			namespaces, origType,
+			elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+		if ( !elemData || !(events = elemData.events) ) {
+			return;
+		}
+
+		// Once for each type.namespace in types; type may be omitted
+		types = ( types || "" ).match( core_rnotwhite ) || [""];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// Unbind all events (on this namespace, if provided) for the element
+			if ( !type ) {
+				for ( type in events ) {
+					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+				}
+				continue;
+			}
+
+			special = jQuery.event.special[ type ] || {};
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+			handlers = events[ type ] || [];
+			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+			// Remove matching events
+			origCount = j = handlers.length;
+			while ( j-- ) {
+				handleObj = handlers[ j ];
+
+				if ( ( mappedTypes || origType === handleObj.origType ) &&
+					( !handler || handler.guid === handleObj.guid ) &&
+					( !tmp || tmp.test( handleObj.namespace ) ) &&
+					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+					handlers.splice( j, 1 );
+
+					if ( handleObj.selector ) {
+						handlers.delegateCount--;
+					}
+					if ( special.remove ) {
+						special.remove.call( elem, handleObj );
+					}
+				}
+			}
+
+			// Remove generic event handler if we removed something and no more handlers exist
+			// (avoids potential for endless recursion during removal of special event handlers)
+			if ( origCount && !handlers.length ) {
+				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+					jQuery.removeEvent( elem, type, elemData.handle );
+				}
+
+				delete events[ type ];
+			}
+		}
+
+		// Remove the expando if it's no longer used
+		if ( jQuery.isEmptyObject( events ) ) {
+			delete elemData.handle;
+
+			// removeData also checks for emptiness and clears the expando if empty
+			// so use it instead of delete
+			jQuery._removeData( elem, "events" );
+		}
+	},
+
+	trigger: function( event, data, elem, onlyHandlers ) {
+		var handle, ontype, cur,
+			bubbleType, special, tmp, i,
+			eventPath = [ elem || document ],
+			type = core_hasOwn.call( event, "type" ) ? event.type : event,
+			namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+		cur = tmp = elem = elem || document;
+
+		// Don't do events on text and comment nodes
+		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+			return;
+		}
+
+		// focus/blur morphs to focusin/out; ensure we're not firing them right now
+		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+			return;
+		}
+
+		if ( type.indexOf(".") >= 0 ) {
+			// Namespaced trigger; create a regexp to match event type in handle()
+			namespaces = type.split(".");
+			type = namespaces.shift();
+			namespaces.sort();
+		}
+		ontype = type.indexOf(":") < 0 && "on" + type;
+
+		// Caller can pass in a jQuery.Event object, Object, or just an event type string
+		event = event[ jQuery.expando ] ?
+			event :
+			new jQuery.Event( type, typeof event === "object" && event );
+
+		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+		event.isTrigger = onlyHandlers ? 2 : 3;
+		event.namespace = namespaces.join(".");
+		event.namespace_re = event.namespace ?
+			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+			null;
+
+		// Clean up the event in case it is being reused
+		event.result = undefined;
+		if ( !event.target ) {
+			event.target = elem;
+		}
+
+		// Clone any incoming data and prepend the event, creating the handler arg list
+		data = data == null ?
+			[ event ] :
+			jQuery.makeArray( data, [ event ] );
+
+		// Allow special events to draw outside the lines
+		special = jQuery.event.special[ type ] || {};
+		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+			return;
+		}
+
+		// Determine event propagation path in advance, per W3C events spec (#9951)
+		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+			bubbleType = special.delegateType || type;
+			if ( !rfocusMorph.test( bubbleType + type ) ) {
+				cur = cur.parentNode;
+			}
+			for ( ; cur; cur = cur.parentNode ) {
+				eventPath.push( cur );
+				tmp = cur;
+			}
+
+			// Only add window if we got to document (e.g., not plain obj or detached DOM)
+			if ( tmp === (elem.ownerDocument || document) ) {
+				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+			}
+		}
+
+		// Fire handlers on the event path
+		i = 0;
+		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+			event.type = i > 1 ?
+				bubbleType :
+				special.bindType || type;
+
+			// jQuery handler
+			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+			if ( handle ) {
+				handle.apply( cur, data );
+			}
+
+			// Native handler
+			handle = ontype && cur[ ontype ];
+			if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+				event.preventDefault();
+			}
+		}
+		event.type = type;
+
+		// If nobody prevented the default action, do it now
+		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+				jQuery.acceptData( elem ) ) {
+
+				// Call a native DOM method on the target with the same name name as the event.
+				// Can't use an .isFunction() check here because IE6/7 fails that test.
+				// Don't do default actions on window, that's where global variables be (#6170)
+				if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+					// Don't re-trigger an onFOO event when we call its FOO() method
+					tmp = elem[ ontype ];
+
+					if ( tmp ) {
+						elem[ ontype ] = null;
+					}
+
+					// Prevent re-triggering of the same event, since we already bubbled it above
+					jQuery.event.triggered = type;
+					try {
+						elem[ type ]();
+					} catch ( e ) {
+						// IE<9 dies on focus/blur to hidden element (#1486,#12518)
+						// only reproducible on winXP IE8 native, not IE9 in IE8 mode
+					}
+					jQuery.event.triggered = undefined;
+
+					if ( tmp ) {
+						elem[ ontype ] = tmp;
+					}
+				}
+			}
+		}
+
+		return event.result;
+	},
+
+	dispatch: function( event ) {
+
+		// Make a writable jQuery.Event from the native event object
+		event = jQuery.event.fix( event );
+
+		var i, ret, handleObj, matched, j,
+			handlerQueue = [],
+			args = core_slice.call( arguments ),
+			handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+			special = jQuery.event.special[ event.type ] || {};
+
+		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+		args[0] = event;
+		event.delegateTarget = this;
+
+		// Call the preDispatch hook for the mapped type, and let it bail if desired
+		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+			return;
+		}
+
+		// Determine handlers
+		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+		// Run delegates first; they may want to stop propagation beneath us
+		i = 0;
+		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+			event.currentTarget = matched.elem;
+
+			j = 0;
+			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+				// Triggered event must either 1) have no namespace, or
+				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+					event.handleObj = handleObj;
+					event.data = handleObj.data;
+
+					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+							.apply( matched.elem, args );
+
+					if ( ret !== undefined ) {
+						if ( (event.result = ret) === false ) {
+							event.preventDefault();
+							event.stopPropagation();
+						}
+					}
+				}
+			}
+		}
+
+		// Call the postDispatch hook for the mapped type
+		if ( special.postDispatch ) {
+			special.postDispatch.call( this, event );
+		}
+
+		return event.result;
+	},
+
+	handlers: function( event, handlers ) {
+		var sel, handleObj, matches, i,
+			handlerQueue = [],
+			delegateCount = handlers.delegateCount,
+			cur = event.target;
+
+		// Find delegate handlers
+		// Black-hole SVG <use> instance trees (#13180)
+		// Avoid non-left-click bubbling in Firefox (#3861)
+		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+			/* jshint eqeqeq: false */
+			for ( ; cur != this; cur = cur.parentNode || this ) {
+				/* jshint eqeqeq: true */
+
+				// Don't check non-elements (#13208)
+				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+				if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
+					matches = [];
+					for ( i = 0; i < delegateCount; i++ ) {
+						handleObj = handlers[ i ];
+
+						// Don't conflict with Object.prototype properties (#13203)
+						sel = handleObj.selector + " ";
+
+						if ( matches[ sel ] === undefined ) {
+							matches[ sel ] = handleObj.needsContext ?
+								jQuery( sel, this ).index( cur ) >= 0 :
+								jQuery.find( sel, this, null, [ cur ] ).length;
+						}
+						if ( matches[ sel ] ) {
+							matches.push( handleObj );
+						}
+					}
+					if ( matches.length ) {
+						handlerQueue.push({ elem: cur, handlers: matches });
+					}
+				}
+			}
+		}
+
+		// Add the remaining (directly-bound) handlers
+		if ( delegateCount < handlers.length ) {
+			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+		}
+
+		return handlerQueue;
+	},
+
+	fix: function( event ) {
+		if ( event[ jQuery.expando ] ) {
+			return event;
+		}
+
+		// Create a writable copy of the event object and normalize some properties
+		var i, prop, copy,
+			type = event.type,
+			originalEvent = event,
+			fixHook = this.fixHooks[ type ];
+
+		if ( !fixHook ) {
+			this.fixHooks[ type ] = fixHook =
+				rmouseEvent.test( type ) ? this.mouseHooks :
+				rkeyEvent.test( type ) ? this.keyHooks :
+				{};
+		}
+		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+		event = new jQuery.Event( originalEvent );
+
+		i = copy.length;
+		while ( i-- ) {
+			prop = copy[ i ];
+			event[ prop ] = originalEvent[ prop ];
+		}
+
+		// Support: IE<9
+		// Fix target property (#1925)
+		if ( !event.target ) {
+			event.target = originalEvent.srcElement || document;
+		}
+
+		// Support: Chrome 23+, Safari?
+		// Target should not be a text node (#504, #13143)
+		if ( event.target.nodeType === 3 ) {
+			event.target = event.target.parentNode;
+		}
+
+		// Support: IE<9
+		// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+		event.metaKey = !!event.metaKey;
+
+		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+	},
+
+	// Includes some event props shared by KeyEvent and MouseEvent
+	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+	fixHooks: {},
+
+	keyHooks: {
+		props: "char charCode key keyCode".split(" "),
+		filter: function( event, original ) {
+
+			// Add which for key events
+			if ( event.which == null ) {
+				event.which = original.charCode != null ? original.charCode : original.keyCode;
+			}
+
+			return event;
+		}
+	},
+
+	mouseHooks: {
+		props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+		filter: function( event, original ) {
+			var body, eventDoc, doc,
+				button = original.button,
+				fromElement = original.fromElement;
+
+			// Calculate pageX/Y if missing and clientX/Y available
+			if ( event.pageX == null && original.clientX != null ) {
+				eventDoc = event.target.ownerDocument || document;
+				doc = eventDoc.documentElement;
+				body = eventDoc.body;
+
+				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+			}
+
+			// Add relatedTarget, if necessary
+			if ( !event.relatedTarget && fromElement ) {
+				event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+			}
+
+			// Add which for click: 1 === left; 2 === middle; 3 === right
+			// Note: button is not normalized, so don't use it
+			if ( !event.which && button !== undefined ) {
+				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+			}
+
+			return event;
+		}
+	},
+
+	special: {
+		load: {
+			// Prevent triggered image.load events from bubbling to window.load
+			noBubble: true
+		},
+		focus: {
+			// Fire native event if possible so blur/focus sequence is correct
+			trigger: function() {
+				if ( this !== safeActiveElement() && this.focus ) {
+					try {
+						this.focus();
+						return false;
+					} catch ( e ) {
+						// Support: IE<9
+						// If we error on focus to hidden element (#1486, #12518),
+						// let .trigger() run the handlers
+					}
+				}
+			},
+			delegateType: "focusin"
+		},
+		blur: {
+			trigger: function() {
+				if ( this === safeActiveElement() && this.blur ) {
+					this.blur();
+					return false;
+				}
+			},
+			delegateType: "focusout"
+		},
+		click: {
+			// For checkbox, fire native event so checked state will be right
+			trigger: function() {
+				if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+					this.click();
+					return false;
+				}
+			},
+
+			// For cross-browser consistency, don't fire native .click() on links
+			_default: function( event ) {
+				return jQuery.nodeName( event.target, "a" );
+			}
+		},
+
+		beforeunload: {
+			postDispatch: function( event ) {
+
+				// Even when returnValue equals to undefined Firefox will still show alert
+				if ( event.result !== undefined ) {
+					event.originalEvent.returnValue = event.result;
+				}
+			}
+		}
+	},
+
+	simulate: function( type, elem, event, bubble ) {
+		// Piggyback on a donor event to simulate a different one.
+		// Fake originalEvent to avoid donor's stopPropagation, but if the
+		// simulated event prevents default then we do the same on the donor.
+		var e = jQuery.extend(
+			new jQuery.Event(),
+			event,
+			{
+				type: type,
+				isSimulated: true,
+				originalEvent: {}
+			}
+		);
+		if ( bubble ) {
+			jQuery.event.trigger( e, null, elem );
+		} else {
+			jQuery.event.dispatch.call( elem, e );
+		}
+		if ( e.isDefaultPrevented() ) {
+			event.preventDefault();
+		}
+	}
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+	function( elem, type, handle ) {
+		if ( elem.removeEventListener ) {
+			elem.removeEventListener( type, handle, false );
+		}
+	} :
+	function( elem, type, handle ) {
+		var name = "on" + type;
+
+		if ( elem.detachEvent ) {
+
+			// #8545, #7054, preventing memory leaks for custom events in IE6-8
+			// detachEvent needed property on element, by name of that event, to properly expose it to GC
+			if ( typeof elem[ name ] === core_strundefined ) {
+				elem[ name ] = null;
+			}
+
+			elem.detachEvent( name, handle );
+		}
+	};
+
+jQuery.Event = function( src, props ) {
+	// Allow instantiation without the 'new' keyword
+	if ( !(this instanceof jQuery.Event) ) {
+		return new jQuery.Event( src, props );
+	}
+
+	// Event object
+	if ( src && src.type ) {
+		this.originalEvent = src;
+		this.type = src.type;
+
+		// Events bubbling up the document may have been marked as prevented
+		// by a handler lower down the tree; reflect the correct value.
+		this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+			src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+	// Event type
+	} else {
+		this.type = src;
+	}
+
+	// Put explicitly provided properties onto the event object
+	if ( props ) {
+		jQuery.extend( this, props );
+	}
+
+	// Create a timestamp if incoming event doesn't have one
+	this.timeStamp = src && src.timeStamp || jQuery.now();
+
+	// Mark it as fixed
+	this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+	isDefaultPrevented: returnFalse,
+	isPropagationStopped: returnFalse,
+	isImmediatePropagationStopped: returnFalse,
+
+	preventDefault: function() {
+		var e = this.originalEvent;
+
+		this.isDefaultPrevented = returnTrue;
+		if ( !e ) {
+			return;
+		}
+
+		// If preventDefault exists, run it on the original event
+		if ( e.preventDefault ) {
+			e.preventDefault();
+
+		// Support: IE
+		// Otherwise set the returnValue property of the original event to false
+		} else {
+			e.returnValue = false;
+		}
+	},
+	stopPropagation: function() {
+		var e = this.originalEvent;
+
+		this.isPropagationStopped = returnTrue;
+		if ( !e ) {
+			return;
+		}
+		// If stopPropagation exists, run it on the original event
+		if ( e.stopPropagation ) {
+			e.stopPropagation();
+		}
+
+		// Support: IE
+		// Set the cancelBubble property of the original event to true
+		e.cancelBubble = true;
+	},
+	stopImmediatePropagation: function() {
+		this.isImmediatePropagationStopped = returnTrue;
+		this.stopPropagation();
+	}
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+	mouseenter: "mouseover",
+	mouseleave: "mouseout"
+}, function( orig, fix ) {
+	jQuery.event.special[ orig ] = {
+		delegateType: fix,
+		bindType: fix,
+
+		handle: function( event ) {
+			var ret,
+				target = this,
+				related = event.relatedTarget,
+				handleObj = event.handleObj;
+
+			// For mousenter/leave call the handler if related is outside the target.
+			// NB: No relatedTarget if the mouse left/entered the browser window
+			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+				event.type = handleObj.origType;
+				ret = handleObj.handler.apply( this, arguments );
+				event.type = fix;
+			}
+			return ret;
+		}
+	};
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+	jQuery.event.special.submit = {
+		setup: function() {
+			// Only need this for delegated form submit events
+			if ( jQuery.nodeName( this, "form" ) ) {
+				return false;
+			}
+
+			// Lazy-add a submit handler when a descendant form may potentially be submitted
+			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+				// Node name check avoids a VML-related crash in IE (#9807)
+				var elem = e.target,
+					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+				if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+					jQuery.event.add( form, "submit._submit", function( event ) {
+						event._submit_bubble = true;
+					});
+					jQuery._data( form, "submitBubbles", true );
+				}
+			});
+			// return undefined since we don't need an event listener
+		},
+
+		postDispatch: function( event ) {
+			// If form was submitted by the user, bubble the event up the tree
+			if ( event._submit_bubble ) {
+				delete event._submit_bubble;
+				if ( this.parentNode && !event.isTrigger ) {
+					jQuery.event.simulate( "submit", this.parentNode, event, true );
+				}
+			}
+		},
+
+		teardown: function() {
+			// Only need this for delegated form submit events
+			if ( jQuery.nodeName( this, "form" ) ) {
+				return false;
+			}
+
+			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+			jQuery.event.remove( this, "._submit" );
+		}
+	};
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+	jQuery.event.special.change = {
+
+		setup: function() {
+
+			if ( rformElems.test( this.nodeName ) ) {
+				// IE doesn't fire change on a check/radio until blur; trigger it on click
+				// after a propertychange. Eat the blur-change in special.change.handle.
+				// This still fires onchange a second time for check/radio after blur.
+				if ( this.type === "checkbox" || this.type === "radio" ) {
+					jQuery.event.add( this, "propertychange._change", function( event ) {
+						if ( event.originalEvent.propertyName === "checked" ) {
+							this._just_changed = true;
+						}
+					});
+					jQuery.event.add( this, "click._change", function( event ) {
+						if ( this._just_changed && !event.isTrigger ) {
+							this._just_changed = false;
+						}
+						// Allow triggered, simulated change events (#11500)
+						jQuery.event.simulate( "change", this, event, true );
+					});
+				}
+				return false;
+			}
+			// Delegated event; lazy-add a change handler on descendant inputs
+			jQuery.event.add( this, "beforeactivate._change", function( e ) {
+				var elem = e.target;
+
+				if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+					jQuery.event.add( elem, "change._change", function( event ) {
+						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+							jQuery.event.simulate( "change", this.parentNode, event, true );
+						}
+					});
+					jQuery._data( elem, "changeBubbles", true );
+				}
+			});
+		},
+
+		handle: function( event ) {
+			var elem = event.target;
+
+			// Swallow native change events from checkbox/radio, we already triggered them above
+			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+				return event.handleObj.handler.apply( this, arguments );
+			}
+		},
+
+		teardown: function() {
+			jQuery.event.remove( this, "._change" );
+
+			return !rformElems.test( this.nodeName );
+		}
+	};
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+		// Attach a single capturing handler while someone wants focusin/focusout
+		var attaches = 0,
+			handler = function( event ) {
+				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+			};
+
+		jQuery.event.special[ fix ] = {
+			setup: function() {
+				if ( attaches++ === 0 ) {
+					document.addEventListener( orig, handler, true );
+				}
+			},
+			teardown: function() {
+				if ( --attaches === 0 ) {
+					document.removeEventListener( orig, handler, true );
+				}
+			}
+		};
+	});
+}
+
+jQuery.fn.extend({
+
+	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+		var type, origFn;
+
+		// Types can be a map of types/handlers
+		if ( typeof types === "object" ) {
+			// ( types-Object, selector, data )
+			if ( typeof selector !== "string" ) {
+				// ( types-Object, data )
+				data = data || selector;
+				selector = undefined;
+			}
+			for ( type in types ) {
+				this.on( type, selector, data, types[ type ], one );
+			}
+			return this;
+		}
+
+		if ( data == null && fn == null ) {
+			// ( types, fn )
+			fn = selector;
+			data = selector = undefined;
+		} else if ( fn == null ) {
+			if ( typeof selector === "string" ) {
+				// ( types, selector, fn )
+				fn = data;
+				data = undefined;
+			} else {
+				// ( types, data, fn )
+				fn = data;
+				data = selector;
+				selector = undefined;
+			}
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		} else if ( !fn ) {
+			return this;
+		}
+
+		if ( one === 1 ) {
+			origFn = fn;
+			fn = function( event ) {
+				// Can use an empty set, since event contains the info
+				jQuery().off( event );
+				return origFn.apply( this, arguments );
+			};
+			// Use same guid so caller can remove using origFn
+			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+		}
+		return this.each( function() {
+			jQuery.event.add( this, types, fn, data, selector );
+		});
+	},
+	one: function( types, selector, data, fn ) {
+		return this.on( types, selector, data, fn, 1 );
+	},
+	off: function( types, selector, fn ) {
+		var handleObj, type;
+		if ( types && types.preventDefault && types.handleObj ) {
+			// ( event )  dispatched jQuery.Event
+			handleObj = types.handleObj;
+			jQuery( types.delegateTarget ).off(
+				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+				handleObj.selector,
+				handleObj.handler
+			);
+			return this;
+		}
+		if ( typeof types === "object" ) {
+			// ( types-object [, selector] )
+			for ( type in types ) {
+				this.off( type, selector, types[ type ] );
+			}
+			return this;
+		}
+		if ( selector === false || typeof selector === "function" ) {
+			// ( types [, fn] )
+			fn = selector;
+			selector = undefined;
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		}
+		return this.each(function() {
+			jQuery.event.remove( this, types, fn, selector );
+		});
+	},
+
+	trigger: function( type, data ) {
+		return this.each(function() {
+			jQuery.event.trigger( type, data, this );
+		});
+	},
+	triggerHandler: function( type, data ) {
+		var elem = this[0];
+		if ( elem ) {
+			return jQuery.event.trigger( type, data, elem, true );
+		}
+	}
+});
+var isSimple = /^.[^:#\[\.,]*$/,
+	rparentsprev = /^(?:parents|prev(?:Until|All))/,
+	rneedsContext = jQuery.expr.match.needsContext,
+	// methods guaranteed to produce a unique set when starting from a unique set
+	guaranteedUnique = {
+		children: true,
+		contents: true,
+		next: true,
+		prev: true
+	};
+
+jQuery.fn.extend({
+	find: function( selector ) {
+		var i,
+			ret = [],
+			self = this,
+			len = self.length;
+
+		if ( typeof selector !== "string" ) {
+			return this.pushStack( jQuery( selector ).filter(function() {
+				for ( i = 0; i < len; i++ ) {
+					if ( jQuery.contains( self[ i ], this ) ) {
+						return true;
+					}
+				}
+			}) );
+		}
+
+		for ( i = 0; i < len; i++ ) {
+			jQuery.find( selector, self[ i ], ret );
+		}
+
+		// Needed because $( selector, context ) becomes $( context ).find( selector )
+		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+		ret.selector = this.selector ? this.selector + " " + selector : selector;
+		return ret;
+	},
+
+	has: function( target ) {
+		var i,
+			targets = jQuery( target, this ),
+			len = targets.length;
+
+		return this.filter(function() {
+			for ( i = 0; i < len; i++ ) {
+				if ( jQuery.contains( this, targets[i] ) ) {
+					return true;
+				}
+			}
+		});
+	},
+
+	not: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], true) );
+	},
+
+	filter: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], false) );
+	},
+
+	is: function( selector ) {
+		return !!winnow(
+			this,
+
+			// If this is a positional/relative selector, check membership in the returned set
+			// so $("p:first").is("p:last") won't return true for a doc with two "p".
+			typeof selector === "string" && rneedsContext.test( selector ) ?
+				jQuery( selector ) :
+				selector || [],
+			false
+		).length;
+	},
+
+	closest: function( selectors, context ) {
+		var cur,
+			i = 0,
+			l = this.length,
+			ret = [],
+			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+				jQuery( selectors, context || this.context ) :
+				0;
+
+		for ( ; i < l; i++ ) {
+			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+				// Always skip document fragments
+				if ( cur.nodeType < 11 && (pos ?
+					pos.index(cur) > -1 :
+
+					// Don't pass non-elements to Sizzle
+					cur.nodeType === 1 &&
+						jQuery.find.matchesSelector(cur, selectors)) ) {
+
+					cur = ret.push( cur );
+					break;
+				}
+			}
+		}
+
+		return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	index: function( elem ) {
+
+		// No argument, return index in parent
+		if ( !elem ) {
+			return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
+		}
+
+		// index in selector
+		if ( typeof elem === "string" ) {
+			return jQuery.inArray( this[0], jQuery( elem ) );
+		}
+
+		// Locate the position of the desired element
+		return jQuery.inArray(
+			// If it receives a jQuery object, the first element is used
+			elem.jquery ? elem[0] : elem, this );
+	},
+
+	add: function( selector, context ) {
+		var set = typeof selector === "string" ?
+				jQuery( selector, context ) :
+				jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+			all = jQuery.merge( this.get(), set );
+
+		return this.pushStack( jQuery.unique(all) );
+	},
+
+	addBack: function( selector ) {
+		return this.add( selector == null ?
+			this.prevObject : this.prevObject.filter(selector)
+		);
+	}
+});
+
+function sibling( cur, dir ) {
+	do {
+		cur = cur[ dir ];
+	} while ( cur && cur.nodeType !== 1 );
+
+	return cur;
+}
+
+jQuery.each({
+	parent: function( elem ) {
+		var parent = elem.parentNode;
+		return parent && parent.nodeType !== 11 ? parent : null;
+	},
+	parents: function( elem ) {
+		return jQuery.dir( elem, "parentNode" );
+	},
+	parentsUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "parentNode", until );
+	},
+	next: function( elem ) {
+		return sibling( elem, "nextSibling" );
+	},
+	prev: function( elem ) {
+		return sibling( elem, "previousSibling" );
+	},
+	nextAll: function( elem ) {
+		return jQuery.dir( elem, "nextSibling" );
+	},
+	prevAll: function( elem ) {
+		return jQuery.dir( elem, "previousSibling" );
+	},
+	nextUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "nextSibling", until );
+	},
+	prevUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "previousSibling", until );
+	},
+	siblings: function( elem ) {
+		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+	},
+	children: function( elem ) {
+		return jQuery.sibling( elem.firstChild );
+	},
+	contents: function( elem ) {
+		return jQuery.nodeName( elem, "iframe" ) ?
+			elem.contentDocument || elem.contentWindow.document :
+			jQuery.merge( [], elem.childNodes );
+	}
+}, function( name, fn ) {
+	jQuery.fn[ name ] = function( until, selector ) {
+		var ret = jQuery.map( this, fn, until );
+
+		if ( name.slice( -5 ) !== "Until" ) {
+			selector = until;
+		}
+
+		if ( selector && typeof selector === "string" ) {
+			ret = jQuery.filter( selector, ret );
+		}
+
+		if ( this.length > 1 ) {
+			// Remove duplicates
+			if ( !guaranteedUnique[ name ] ) {
+				ret = jQuery.unique( ret );
+			}
+
+			// Reverse order for parents* and prev-derivatives
+			if ( rparentsprev.test( name ) ) {
+				ret = ret.reverse();
+			}
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+jQuery.extend({
+	filter: function( expr, elems, not ) {
+		var elem = elems[ 0 ];
+
+		if ( not ) {
+			expr = ":not(" + expr + ")";
+		}
+
+		return elems.length === 1 && elem.nodeType === 1 ?
+			jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+			jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+				return elem.nodeType === 1;
+			}));
+	},
+
+	dir: function( elem, dir, until ) {
+		var matched = [],
+			cur = elem[ dir ];
+
+		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+			if ( cur.nodeType === 1 ) {
+				matched.push( cur );
+			}
+			cur = cur[dir];
+		}
+		return matched;
+	},
+
+	sibling: function( n, elem ) {
+		var r = [];
+
+		for ( ; n; n = n.nextSibling ) {
+			if ( n.nodeType === 1 && n !== elem ) {
+				r.push( n );
+			}
+		}
+
+		return r;
+	}
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+	if ( jQuery.isFunction( qualifier ) ) {
+		return jQuery.grep( elements, function( elem, i ) {
+			/* jshint -W018 */
+			return !!qualifier.call( elem, i, elem ) !== not;
+		});
+
+	}
+
+	if ( qualifier.nodeType ) {
+		return jQuery.grep( elements, function( elem ) {
+			return ( elem === qualifier ) !== not;
+		});
+
+	}
+
+	if ( typeof qualifier === "string" ) {
+		if ( isSimple.test( qualifier ) ) {
+			return jQuery.filter( qualifier, elements, not );
+		}
+
+		qualifier = jQuery.filter( qualifier, elements );
+	}
+
+	return jQuery.grep( elements, function( elem ) {
+		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
+	});
+}
+function createSafeFragment( document ) {
+	var list = nodeNames.split( "|" ),
+		safeFrag = document.createDocumentFragment();
+
+	if ( safeFrag.createElement ) {
+		while ( list.length ) {
+			safeFrag.createElement(
+				list.pop()
+			);
+		}
+	}
+	return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+		"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+	rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+	rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+	rleadingWhitespace = /^\s+/,
+	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+	rtagName = /<([\w:]+)/,
+	rtbody = /<tbody/i,
+	rhtml = /<|&#?\w+;/,
+	rnoInnerhtml = /<(?:script|style|link)/i,
+	manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
+	// checked="checked" or checked
+	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+	rscriptType = /^$|\/(?:java|ecma)script/i,
+	rscriptTypeMasked = /^true\/(.*)/,
+	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+	// We have to close these tags to support XHTML (#13200)
+	wrapMap = {
+		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+		legend: [ 1, "<fieldset>", "</fieldset>" ],
+		area: [ 1, "<map>", "</map>" ],
+		param: [ 1, "<object>", "</object>" ],
+		thead: [ 1, "<table>", "</table>" ],
+		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+		// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+		// unless wrapped in a div with non-breaking characters in front of it.
+		_default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>"  ]
+	},
+	safeFragment = createSafeFragment( document ),
+	fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+jQuery.fn.extend({
+	text: function( value ) {
+		return jQuery.access( this, function( value ) {
+			return value === undefined ?
+				jQuery.text( this ) :
+				this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+		}, null, value, arguments.length );
+	},
+
+	append: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.appendChild( elem );
+			}
+		});
+	},
+
+	prepend: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.insertBefore( elem, target.firstChild );
+			}
+		});
+	},
+
+	before: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this );
+			}
+		});
+	},
+
+	after: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this.nextSibling );
+			}
+		});
+	},
+
+	// keepData is for internal use only--do not document
+	remove: function( selector, keepData ) {
+		var elem,
+			elems = selector ? jQuery.filter( selector, this ) : this,
+			i = 0;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+
+			if ( !keepData && elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem ) );
+			}
+
+			if ( elem.parentNode ) {
+				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+					setGlobalEval( getAll( elem, "script" ) );
+				}
+				elem.parentNode.removeChild( elem );
+			}
+		}
+
+		return this;
+	},
+
+	empty: function() {
+		var elem,
+			i = 0;
+
+		for ( ; (elem = this[i]) != null; i++ ) {
+			// Remove element nodes and prevent memory leaks
+			if ( elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem, false ) );
+			}
+
+			// Remove any remaining nodes
+			while ( elem.firstChild ) {
+				elem.removeChild( elem.firstChild );
+			}
+
+			// If this is a select, ensure that it displays empty (#12336)
+			// Support: IE<9
+			if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
+				elem.options.length = 0;
+			}
+		}
+
+		return this;
+	},
+
+	clone: function( dataAndEvents, deepDataAndEvents ) {
+		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+		return this.map( function () {
+			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+		});
+	},
+
+	html: function( value ) {
+		return jQuery.access( this, function( value ) {
+			var elem = this[0] || {},
+				i = 0,
+				l = this.length;
+
+			if ( value === undefined ) {
+				return elem.nodeType === 1 ?
+					elem.innerHTML.replace( rinlinejQuery, "" ) :
+					undefined;
+			}
+
+			// See if we can take a shortcut and just use innerHTML
+			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+				( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
+				( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+				!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+				value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+				try {
+					for (; i < l; i++ ) {
+						// Remove element nodes and prevent memory leaks
+						elem = this[i] || {};
+						if ( elem.nodeType === 1 ) {
+							jQuery.cleanData( getAll( elem, false ) );
+							elem.innerHTML = value;
+						}
+					}
+
+					elem = 0;
+
+				// If using innerHTML throws an exception, use the fallback method
+				} catch(e) {}
+			}
+
+			if ( elem ) {
+				this.empty().append( value );
+			}
+		}, null, value, arguments.length );
+	},
+
+	replaceWith: function() {
+		var
+			// Snapshot the DOM in case .domManip sweeps something relevant into its fragment
+			args = jQuery.map( this, function( elem ) {
+				return [ elem.nextSibling, elem.parentNode ];
+			}),
+			i = 0;
+
+		// Make the changes, replacing each context element with the new content
+		this.domManip( arguments, function( elem ) {
+			var next = args[ i++ ],
+				parent = args[ i++ ];
+
+			if ( parent ) {
+				// Don't use the snapshot next if it has moved (#13810)
+				if ( next && next.parentNode !== parent ) {
+					next = this.nextSibling;
+				}
+				jQuery( this ).remove();
+				parent.insertBefore( elem, next );
+			}
+		// Allow new content to include elements from the context set
+		}, true );
+
+		// Force removal if there was no new content (e.g., from empty arguments)
+		return i ? this : this.remove();
+	},
+
+	detach: function( selector ) {
+		return this.remove( selector, true );
+	},
+
+	domManip: function( args, callback, allowIntersection ) {
+
+		// Flatten any nested arrays
+		args = core_concat.apply( [], args );
+
+		var first, node, hasScripts,
+			scripts, doc, fragment,
+			i = 0,
+			l = this.length,
+			set = this,
+			iNoClone = l - 1,
+			value = args[0],
+			isFunction = jQuery.isFunction( value );
+
+		// We can't cloneNode fragments that contain checked, in WebKit
+		if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
+			return this.each(function( index ) {
+				var self = set.eq( index );
+				if ( isFunction ) {
+					args[0] = value.call( this, index, self.html() );
+				}
+				self.domManip( args, callback, allowIntersection );
+			});
+		}
+
+		if ( l ) {
+			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this );
+			first = fragment.firstChild;
+
+			if ( fragment.childNodes.length === 1 ) {
+				fragment = first;
+			}
+
+			if ( first ) {
+				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+				hasScripts = scripts.length;
+
+				// Use the original fragment for the last item instead of the first because it can end up
+				// being emptied incorrectly in certain situations (#8070).
+				for ( ; i < l; i++ ) {
+					node = fragment;
+
+					if ( i !== iNoClone ) {
+						node = jQuery.clone( node, true, true );
+
+						// Keep references to cloned scripts for later restoration
+						if ( hasScripts ) {
+							jQuery.merge( scripts, getAll( node, "script" ) );
+						}
+					}
+
+					callback.call( this[i], node, i );
+				}
+
+				if ( hasScripts ) {
+					doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+					// Reenable scripts
+					jQuery.map( scripts, restoreScript );
+
+					// Evaluate executable scripts on first document insertion
+					for ( i = 0; i < hasScripts; i++ ) {
+						node = scripts[ i ];
+						if ( rscriptType.test( node.type || "" ) &&
+							!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+							if ( node.src ) {
+								// Hope ajax is available...
+								jQuery._evalUrl( node.src );
+							} else {
+								jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
+							}
+						}
+					}
+				}
+
+				// Fix #11809: Avoid leaking memory
+				fragment = first = null;
+			}
+		}
+
+		return this;
+	}
+});
+
+// Support: IE<8
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+	return jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ?
+
+		elem.getElementsByTagName("tbody")[0] ||
+			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+		elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+	elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
+	return elem;
+}
+function restoreScript( elem ) {
+	var match = rscriptTypeMasked.exec( elem.type );
+	if ( match ) {
+		elem.type = match[1];
+	} else {
+		elem.removeAttribute("type");
+	}
+	return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+	var elem,
+		i = 0;
+	for ( ; (elem = elems[i]) != null; i++ ) {
+		jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
+	}
+}
+
+function cloneCopyEvent( src, dest ) {
+
+	if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+		return;
+	}
+
+	var type, i, l,
+		oldData = jQuery._data( src ),
+		curData = jQuery._data( dest, oldData ),
+		events = oldData.events;
+
+	if ( events ) {
+		delete curData.handle;
+		curData.events = {};
+
+		for ( type in events ) {
+			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+				jQuery.event.add( dest, type, events[ type ][ i ] );
+			}
+		}
+	}
+
+	// make the cloned public data object a copy from the original
+	if ( curData.data ) {
+		curData.data = jQuery.extend( {}, curData.data );
+	}
+}
+
+function fixCloneNodeIssues( src, dest ) {
+	var nodeName, e, data;
+
+	// We do not need to do anything for non-Elements
+	if ( dest.nodeType !== 1 ) {
+		return;
+	}
+
+	nodeName = dest.nodeName.toLowerCase();
+
+	// IE6-8 copies events bound via attachEvent when using cloneNode.
+	if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
+		data = jQuery._data( dest );
+
+		for ( e in data.events ) {
+			jQuery.removeEvent( dest, e, data.handle );
+		}
+
+		// Event data gets referenced instead of copied if the expando gets copied too
+		dest.removeAttribute( jQuery.expando );
+	}
+
+	// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
+	if ( nodeName === "script" && dest.text !== src.text ) {
+		disableScript( dest ).text = src.text;
+		restoreScript( dest );
+
+	// IE6-10 improperly clones children of object elements using classid.
+	// IE10 throws NoModificationAllowedError if parent is null, #12132.
+	} else if ( nodeName === "object" ) {
+		if ( dest.parentNode ) {
+			dest.outerHTML = src.outerHTML;
+		}
+
+		// This path appears unavoidable for IE9. When cloning an object
+		// element in IE9, the outerHTML strategy above is not sufficient.
+		// If the src has innerHTML and the destination does not,
+		// copy the src.innerHTML into the dest.innerHTML. #10324
+		if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
+			dest.innerHTML = src.innerHTML;
+		}
+
+	} else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
+		// IE6-8 fails to persist the checked state of a cloned checkbox
+		// or radio button. Worse, IE6-7 fail to give the cloned element
+		// a checked appearance if the defaultChecked value isn't also set
+
+		dest.defaultChecked = dest.checked = src.checked;
+
+		// IE6-7 get confused and end up setting the value of a cloned
+		// checkbox/radio button to an empty string instead of "on"
+		if ( dest.value !== src.value ) {
+			dest.value = src.value;
+		}
+
+	// IE6-8 fails to return the selected option to the default selected
+	// state when cloning options
+	} else if ( nodeName === "option" ) {
+		dest.defaultSelected = dest.selected = src.defaultSelected;
+
+	// IE6-8 fails to set the defaultValue to the correct value when
+	// cloning other types of input fields
+	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+		dest.defaultValue = src.defaultValue;
+	}
+}
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function( name, original ) {
+	jQuery.fn[ name ] = function( selector ) {
+		var elems,
+			i = 0,
+			ret = [],
+			insert = jQuery( selector ),
+			last = insert.length - 1;
+
+		for ( ; i <= last; i++ ) {
+			elems = i === last ? this : this.clone(true);
+			jQuery( insert[i] )[ original ]( elems );
+
+			// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
+			core_push.apply( ret, elems.get() );
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+function getAll( context, tag ) {
+	var elems, elem,
+		i = 0,
+		found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
+			typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
+			undefined;
+
+	if ( !found ) {
+		for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
+			if ( !tag || jQuery.nodeName( elem, tag ) ) {
+				found.push( elem );
+			} else {
+				jQuery.merge( found, getAll( elem, tag ) );
+			}
+		}
+	}
+
+	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+		jQuery.merge( [ context ], found ) :
+		found;
+}
+
+// Used in buildFragment, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+	if ( manipulation_rcheckableType.test( elem.type ) ) {
+		elem.defaultChecked = elem.checked;
+	}
+}
+
+jQuery.extend({
+	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+		var destElements, node, clone, i, srcElements,
+			inPage = jQuery.contains( elem.ownerDocument, elem );
+
+		if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+			clone = elem.cloneNode( true );
+
+		// IE<=8 does not properly clone detached, unknown element nodes
+		} else {
+			fragmentDiv.innerHTML = elem.outerHTML;
+			fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+		}
+
+		if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+				(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+
+			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+			destElements = getAll( clone );
+			srcElements = getAll( elem );
+
+			// Fix all IE cloning issues
+			for ( i = 0; (node = srcElements[i]) != null; ++i ) {
+				// Ensure that the destination node is not null; Fixes #9587
+				if ( destElements[i] ) {
+					fixCloneNodeIssues( node, destElements[i] );
+				}
+			}
+		}
+
+		// Copy the events from the original to the clone
+		if ( dataAndEvents ) {
+			if ( deepDataAndEvents ) {
+				srcElements = srcElements || getAll( elem );
+				destElements = destElements || getAll( clone );
+
+				for ( i = 0; (node = srcElements[i]) != null; i++ ) {
+					cloneCopyEvent( node, destElements[i] );
+				}
+			} else {
+				cloneCopyEvent( elem, clone );
+			}
+		}
+
+		// Preserve script evaluation history
+		destElements = getAll( clone, "script" );
+		if ( destElements.length > 0 ) {
+			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+		}
+
+		destElements = srcElements = node = null;
+
+		// Return the cloned set
+		return clone;
+	},
+
+	buildFragment: function( elems, context, scripts, selection ) {
+		var j, elem, contains,
+			tmp, tag, tbody, wrap,
+			l = elems.length,
+
+			// Ensure a safe fragment
+			safe = createSafeFragment( context ),
+
+			nodes = [],
+			i = 0;
+
+		for ( ; i < l; i++ ) {
+			elem = elems[ i ];
+
+			if ( elem || elem === 0 ) {
+
+				// Add nodes directly
+				if ( jQuery.type( elem ) === "object" ) {
+					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+				// Convert non-html into a text node
+				} else if ( !rhtml.test( elem ) ) {
+					nodes.push( context.createTextNode( elem ) );
+
+				// Convert html into DOM nodes
+				} else {
+					tmp = tmp || safe.appendChild( context.createElement("div") );
+
+					// Deserialize a standard representation
+					tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+					wrap = wrapMap[ tag ] || wrapMap._default;
+
+					tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
+
+					// Descend through wrappers to the right content
+					j = wrap[0];
+					while ( j-- ) {
+						tmp = tmp.lastChild;
+					}
+
+					// Manually add leading whitespace removed by IE
+					if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+						nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
+					}
+
+					// Remove IE's autoinserted <tbody> from table fragments
+					if ( !jQuery.support.tbody ) {
+
+						// String was a <table>, *may* have spurious <tbody>
+						elem = tag === "table" && !rtbody.test( elem ) ?
+							tmp.firstChild :
+
+							// String was a bare <thead> or <tfoot>
+							wrap[1] === "<table>" && !rtbody.test( elem ) ?
+								tmp :
+								0;
+
+						j = elem && elem.childNodes.length;
+						while ( j-- ) {
+							if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
+								elem.removeChild( tbody );
+							}
+						}
+					}
+
+					jQuery.merge( nodes, tmp.childNodes );
+
+					// Fix #12392 for WebKit and IE > 9
+					tmp.textContent = "";
+
+					// Fix #12392 for oldIE
+					while ( tmp.firstChild ) {
+						tmp.removeChild( tmp.firstChild );
+					}
+
+					// Remember the top-level container for proper cleanup
+					tmp = safe.lastChild;
+				}
+			}
+		}
+
+		// Fix #11356: Clear elements from fragment
+		if ( tmp ) {
+			safe.removeChild( tmp );
+		}
+
+		// Reset defaultChecked for any radios and checkboxes
+		// about to be appended to the DOM in IE 6/7 (#8060)
+		if ( !jQuery.support.appendChecked ) {
+			jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+		}
+
+		i = 0;
+		while ( (elem = nodes[ i++ ]) ) {
+
+			// #4087 - If origin and destination elements are the same, and this is
+			// that element, do not do anything
+			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+				continue;
+			}
+
+			contains = jQuery.contains( elem.ownerDocument, elem );
+
+			// Append to fragment
+			tmp = getAll( safe.appendChild( elem ), "script" );
+
+			// Preserve script evaluation history
+			if ( contains ) {
+				setGlobalEval( tmp );
+			}
+
+			// Capture executables
+			if ( scripts ) {
+				j = 0;
+				while ( (elem = tmp[ j++ ]) ) {
+					if ( rscriptType.test( elem.type || "" ) ) {
+						scripts.push( elem );
+					}
+				}
+			}
+		}
+
+		tmp = null;
+
+		return safe;
+	},
+
+	cleanData: function( elems, /* internal */ acceptData ) {
+		var elem, type, id, data,
+			i = 0,
+			internalKey = jQuery.expando,
+			cache = jQuery.cache,
+			deleteExpando = jQuery.support.deleteExpando,
+			special = jQuery.event.special;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+
+			if ( acceptData || jQuery.acceptData( elem ) ) {
+
+				id = elem[ internalKey ];
+				data = id && cache[ id ];
+
+				if ( data ) {
+					if ( data.events ) {
+						for ( type in data.events ) {
+							if ( special[ type ] ) {
+								jQuery.event.remove( elem, type );
+
+							// This is a shortcut to avoid jQuery.event.remove's overhead
+							} else {
+								jQuery.removeEvent( elem, type, data.handle );
+							}
+						}
+					}
+
+					// Remove cache only if it was not already removed by jQuery.event.remove
+					if ( cache[ id ] ) {
+
+						delete cache[ id ];
+
+						// IE does not allow us to delete expando properties from nodes,
+						// nor does it have a removeAttribute function on Document nodes;
+						// we must handle all of these cases
+						if ( deleteExpando ) {
+							delete elem[ internalKey ];
+
+						} else if ( typeof elem.removeAttribute !== core_strundefined ) {
+							elem.removeAttribute( internalKey );
+
+						} else {
+							elem[ internalKey ] = null;
+						}
+
+						core_deletedIds.push( id );
+					}
+				}
+			}
+		}
+	},
+
+	_evalUrl: function( url ) {
+		return jQuery.ajax({
+			url: url,
+			type: "GET",
+			dataType: "script",
+			async: false,
+			global: false,
+			"throws": true
+		});
+	}
+});
+jQuery.fn.extend({
+	wrapAll: function( html ) {
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function(i) {
+				jQuery(this).wrapAll( html.call(this, i) );
+			});
+		}
+
+		if ( this[0] ) {
+			// The elements to wrap the target around
+			var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+			if ( this[0].parentNode ) {
+				wrap.insertBefore( this[0] );
+			}
+
+			wrap.map(function() {
+				var elem = this;
+
+				while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+					elem = elem.firstChild;
+				}
+
+				return elem;
+			}).append( this );
+		}
+
+		return this;
+	},
+
+	wrapInner: function( html ) {
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function(i) {
+				jQuery(this).wrapInner( html.call(this, i) );
+			});
+		}
+
+		return this.each(function() {
+			var self = jQuery( this ),
+				contents = self.contents();
+
+			if ( contents.length ) {
+				contents.wrapAll( html );
+
+			} else {
+				self.append( html );
+			}
+		});
+	},
+
+	wrap: function( html ) {
+		var isFunction = jQuery.isFunction( html );
+
+		return this.each(function(i) {
+			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+		});
+	},
+
+	unwrap: function() {
+		return this.parent().each(function() {
+			if ( !jQuery.nodeName( this, "body" ) ) {
+				jQuery( this ).replaceWith( this.childNodes );
+			}
+		}).end();
+	}
+});
+var iframe, getStyles, curCSS,
+	ralpha = /alpha\([^)]*\)/i,
+	ropacity = /opacity\s*=\s*([^)]*)/,
+	rposition = /^(top|right|bottom|left)$/,
+	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+	rmargin = /^margin/,
+	rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+	rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
+	elemdisplay = { BODY: "block" },
+
+	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+	cssNormalTransform = {
+		letterSpacing: 0,
+		fontWeight: 400
+	},
+
+	cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+	// shortcut for names that are not vendor prefixed
+	if ( name in style ) {
+		return name;
+	}
+
+	// check for vendor prefixed names
+	var capName = name.charAt(0).toUpperCase() + name.slice(1),
+		origName = name,
+		i = cssPrefixes.length;
+
+	while ( i-- ) {
+		name = cssPrefixes[ i ] + capName;
+		if ( name in style ) {
+			return name;
+		}
+	}
+
+	return origName;
+}
+
+function isHidden( elem, el ) {
+	// isHidden might be called from jQuery#filter function;
+	// in that case, element will be second argument
+	elem = el || elem;
+	return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+	var display, elem, hidden,
+		values = [],
+		index = 0,
+		length = elements.length;
+
+	for ( ; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+
+		values[ index ] = jQuery._data( elem, "olddisplay" );
+		display = elem.style.display;
+		if ( show ) {
+			// Reset the inline display of this element to learn if it is
+			// being hidden by cascaded rules or not
+			if ( !values[ index ] && display === "none" ) {
+				elem.style.display = "";
+			}
+
+			// Set elements which have been overridden with display: none
+			// in a stylesheet to whatever the default browser style is
+			// for such an element
+			if ( elem.style.display === "" && isHidden( elem ) ) {
+				values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+			}
+		} else {
+
+			if ( !values[ index ] ) {
+				hidden = isHidden( elem );
+
+				if ( display && display !== "none" || !hidden ) {
+					jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+				}
+			}
+		}
+	}
+
+	// Set the display of most of the elements in a second loop
+	// to avoid the constant reflow
+	for ( index = 0; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+			elem.style.display = show ? values[ index ] || "" : "none";
+		}
+	}
+
+	return elements;
+}
+
+jQuery.fn.extend({
+	css: function( name, value ) {
+		return jQuery.access( this, function( elem, name, value ) {
+			var len, styles,
+				map = {},
+				i = 0;
+
+			if ( jQuery.isArray( name ) ) {
+				styles = getStyles( elem );
+				len = name.length;
+
+				for ( ; i < len; i++ ) {
+					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+				}
+
+				return map;
+			}
+
+			return value !== undefined ?
+				jQuery.style( elem, name, value ) :
+				jQuery.css( elem, name );
+		}, name, value, arguments.length > 1 );
+	},
+	show: function() {
+		return showHide( this, true );
+	},
+	hide: function() {
+		return showHide( this );
+	},
+	toggle: function( state ) {
+		if ( typeof state === "boolean" ) {
+			return state ? this.show() : this.hide();
+		}
+
+		return this.each(function() {
+			if ( isHidden( this ) ) {
+				jQuery( this ).show();
+			} else {
+				jQuery( this ).hide();
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	// Add in style property hooks for overriding the default
+	// behavior of getting and setting a style property
+	cssHooks: {
+		opacity: {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// We should always get a number back from opacity
+					var ret = curCSS( elem, "opacity" );
+					return ret === "" ? "1" : ret;
+				}
+			}
+		}
+	},
+
+	// Don't automatically add "px" to these possibly-unitless properties
+	cssNumber: {
+		"columnCount": true,
+		"fillOpacity": true,
+		"fontWeight": true,
+		"lineHeight": true,
+		"opacity": true,
+		"order": true,
+		"orphans": true,
+		"widows": true,
+		"zIndex": true,
+		"zoom": true
+	},
+
+	// Add in properties whose names you wish to fix before
+	// setting or getting the value
+	cssProps: {
+		// normalize float css property
+		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+	},
+
+	// Get and set the style property on a DOM Node
+	style: function( elem, name, value, extra ) {
+		// Don't set styles on text and comment nodes
+		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+			return;
+		}
+
+		// Make sure that we're working with the right name
+		var ret, type, hooks,
+			origName = jQuery.camelCase( name ),
+			style = elem.style;
+
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// Check if we're setting a value
+		if ( value !== undefined ) {
+			type = typeof value;
+
+			// convert relative number strings (+= or -=) to relative numbers. #7345
+			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+				// Fixes bug #9237
+				type = "number";
+			}
+
+			// Make sure that NaN and null values aren't set. See: #7116
+			if ( value == null || type === "number" && isNaN( value ) ) {
+				return;
+			}
+
+			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+				value += "px";
+			}
+
+			// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
+			// but it would mean to define eight (for every problematic property) identical functions
+			if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
+				style[ name ] = "inherit";
+			}
+
+			// If a hook was provided, use that value, otherwise just set the specified value
+			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+
+				// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+				// Fixes bug #5509
+				try {
+					style[ name ] = value;
+				} catch(e) {}
+			}
+
+		} else {
+			// If a hook was provided get the non-computed value from there
+			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+				return ret;
+			}
+
+			// Otherwise just get the value from the style object
+			return style[ name ];
+		}
+	},
+
+	css: function( elem, name, extra, styles ) {
+		var num, val, hooks,
+			origName = jQuery.camelCase( name );
+
+		// Make sure that we're working with the right name
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// If a hook was provided get the computed value from there
+		if ( hooks && "get" in hooks ) {
+			val = hooks.get( elem, true, extra );
+		}
+
+		// Otherwise, if a way to get the computed value exists, use that
+		if ( val === undefined ) {
+			val = curCSS( elem, name, styles );
+		}
+
+		//convert "normal" to computed value
+		if ( val === "normal" && name in cssNormalTransform ) {
+			val = cssNormalTransform[ name ];
+		}
+
+		// Return, converting to number if forced or a qualifier was provided and val looks numeric
+		if ( extra === "" || extra ) {
+			num = parseFloat( val );
+			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+		}
+		return val;
+	}
+});
+
+// NOTE: we've included the "window" in window.getComputedStyle
+// because jsdom on node.js will break without it.
+if ( window.getComputedStyle ) {
+	getStyles = function( elem ) {
+		return window.getComputedStyle( elem, null );
+	};
+
+	curCSS = function( elem, name, _computed ) {
+		var width, minWidth, maxWidth,
+			computed = _computed || getStyles( elem ),
+
+			// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+			ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
+			style = elem.style;
+
+		if ( computed ) {
+
+			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+				ret = jQuery.style( elem, name );
+			}
+
+			// A tribute to the "awesome hack by Dean Edwards"
+			// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+			// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+			// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+			if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+				// Remember the original values
+				width = style.width;
+				minWidth = style.minWidth;
+				maxWidth = style.maxWidth;
+
+				// Put in the new values to get a computed value out
+				style.minWidth = style.maxWidth = style.width = ret;
+				ret = computed.width;
+
+				// Revert the changed values
+				style.width = width;
+				style.minWidth = minWidth;
+				style.maxWidth = maxWidth;
+			}
+		}
+
+		return ret;
+	};
+} else if ( document.documentElement.currentStyle ) {
+	getStyles = function( elem ) {
+		return elem.currentStyle;
+	};
+
+	curCSS = function( elem, name, _computed ) {
+		var left, rs, rsLeft,
+			computed = _computed || getStyles( elem ),
+			ret = computed ? computed[ name ] : undefined,
+			style = elem.style;
+
+		// Avoid setting ret to empty string here
+		// so we don't default to auto
+		if ( ret == null && style && style[ name ] ) {
+			ret = style[ name ];
+		}
+
+		// From the awesome hack by Dean Edwards
+		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+		// If we're not dealing with a regular pixel number
+		// but a number that has a weird ending, we need to convert it to pixels
+		// but not position css attributes, as those are proportional to the parent element instead
+		// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+		if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+			// Remember the original values
+			left = style.left;
+			rs = elem.runtimeStyle;
+			rsLeft = rs && rs.left;
+
+			// Put in the new values to get a computed value out
+			if ( rsLeft ) {
+				rs.left = elem.currentStyle.left;
+			}
+			style.left = name === "fontSize" ? "1em" : ret;
+			ret = style.pixelLeft + "px";
+
+			// Revert the changed values
+			style.left = left;
+			if ( rsLeft ) {
+				rs.left = rsLeft;
+			}
+		}
+
+		return ret === "" ? "auto" : ret;
+	};
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+	var matches = rnumsplit.exec( value );
+	return matches ?
+		// Guard against undefined "subtract", e.g., when used as in cssHooks
+		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+		value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+	var i = extra === ( isBorderBox ? "border" : "content" ) ?
+		// If we already have the right measurement, avoid augmentation
+		4 :
+		// Otherwise initialize for horizontal or vertical properties
+		name === "width" ? 1 : 0,
+
+		val = 0;
+
+	for ( ; i < 4; i += 2 ) {
+		// both box models exclude margin, so add it if we want it
+		if ( extra === "margin" ) {
+			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+		}
+
+		if ( isBorderBox ) {
+			// border-box includes padding, so remove it if we want content
+			if ( extra === "content" ) {
+				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+			}
+
+			// at this point, extra isn't border nor margin, so remove border
+			if ( extra !== "margin" ) {
+				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		} else {
+			// at this point, extra isn't content, so add padding
+			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+			// at this point, extra isn't content nor padding, so add border
+			if ( extra !== "padding" ) {
+				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		}
+	}
+
+	return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+	// Start with offset property, which is equivalent to the border-box value
+	var valueIsBorderBox = true,
+		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+		styles = getStyles( elem ),
+		isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+	// some non-html elements return undefined for offsetWidth, so check for null/undefined
+	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+	if ( val <= 0 || val == null ) {
+		// Fall back to computed then uncomputed css if necessary
+		val = curCSS( elem, name, styles );
+		if ( val < 0 || val == null ) {
+			val = elem.style[ name ];
+		}
+
+		// Computed unit is not pixels. Stop here and return.
+		if ( rnumnonpx.test(val) ) {
+			return val;
+		}
+
+		// we need the check for style in case a browser which returns unreliable values
+		// for getComputedStyle silently falls back to the reliable elem.style
+		valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+		// Normalize "", auto, and prepare for extra
+		val = parseFloat( val ) || 0;
+	}
+
+	// use the active box-sizing model to add/subtract irrelevant styles
+	return ( val +
+		augmentWidthOrHeight(
+			elem,
+			name,
+			extra || ( isBorderBox ? "border" : "content" ),
+			valueIsBorderBox,
+			styles
+		)
+	) + "px";
+}
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+	var doc = document,
+		display = elemdisplay[ nodeName ];
+
+	if ( !display ) {
+		display = actualDisplay( nodeName, doc );
+
+		// If the simple way fails, read from inside an iframe
+		if ( display === "none" || !display ) {
+			// Use the already-created iframe if possible
+			iframe = ( iframe ||
+				jQuery("<iframe frameborder='0' width='0' height='0'/>")
+				.css( "cssText", "display:block !important" )
+			).appendTo( doc.documentElement );
+
+			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+			doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
+			doc.write("<!doctype html><html><body>");
+			doc.close();
+
+			display = actualDisplay( nodeName, doc );
+			iframe.detach();
+		}
+
+		// Store the correct default display
+		elemdisplay[ nodeName ] = display;
+	}
+
+	return display;
+}
+
+// Called ONLY from within css_defaultDisplay
+function actualDisplay( name, doc ) {
+	var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+		display = jQuery.css( elem[0], "display" );
+	elem.remove();
+	return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+	jQuery.cssHooks[ name ] = {
+		get: function( elem, computed, extra ) {
+			if ( computed ) {
+				// certain elements can have dimension info if we invisibly show them
+				// however, it must have a current display style that would benefit from this
+				return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
+					jQuery.swap( elem, cssShow, function() {
+						return getWidthOrHeight( elem, name, extra );
+					}) :
+					getWidthOrHeight( elem, name, extra );
+			}
+		},
+
+		set: function( elem, value, extra ) {
+			var styles = extra && getStyles( elem );
+			return setPositiveNumber( elem, value, extra ?
+				augmentWidthOrHeight(
+					elem,
+					name,
+					extra,
+					jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+					styles
+				) : 0
+			);
+		}
+	};
+});
+
+if ( !jQuery.support.opacity ) {
+	jQuery.cssHooks.opacity = {
+		get: function( elem, computed ) {
+			// IE uses filters for opacity
+			return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+				( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+				computed ? "1" : "";
+		},
+
+		set: function( elem, value ) {
+			var style = elem.style,
+				currentStyle = elem.currentStyle,
+				opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+				filter = currentStyle && currentStyle.filter || style.filter || "";
+
+			// IE has trouble with opacity if it does not have layout
+			// Force it by setting the zoom level
+			style.zoom = 1;
+
+			// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+			// if value === "", then remove inline opacity #12685
+			if ( ( value >= 1 || value === "" ) &&
+					jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+					style.removeAttribute ) {
+
+				// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+				// if "filter:" is present at all, clearType is disabled, we want to avoid this
+				// style.removeAttribute is IE Only, but so apparently is this code path...
+				style.removeAttribute( "filter" );
+
+				// if there is no filter style applied in a css rule or unset inline opacity, we are done
+				if ( value === "" || currentStyle && !currentStyle.filter ) {
+					return;
+				}
+			}
+
+			// otherwise, set new filter values
+			style.filter = ralpha.test( filter ) ?
+				filter.replace( ralpha, opacity ) :
+				filter + " " + opacity;
+		}
+	};
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+	if ( !jQuery.support.reliableMarginRight ) {
+		jQuery.cssHooks.marginRight = {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+					// Work around by temporarily setting element display to inline-block
+					return jQuery.swap( elem, { "display": "inline-block" },
+						curCSS, [ elem, "marginRight" ] );
+				}
+			}
+		};
+	}
+
+	// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+	// getComputedStyle returns percent when specified for top/left/bottom/right
+	// rather than make the css module depend on the offset module, we just check for it here
+	if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+		jQuery.each( [ "top", "left" ], function( i, prop ) {
+			jQuery.cssHooks[ prop ] = {
+				get: function( elem, computed ) {
+					if ( computed ) {
+						computed = curCSS( elem, prop );
+						// if curCSS returns percentage, fallback to offset
+						return rnumnonpx.test( computed ) ?
+							jQuery( elem ).position()[ prop ] + "px" :
+							computed;
+					}
+				}
+			};
+		});
+	}
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	jQuery.expr.filters.hidden = function( elem ) {
+		// Support: Opera <= 12.12
+		// Opera reports offsetWidths and offsetHeights less than zero on some elements
+		return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
+			(!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+	};
+
+	jQuery.expr.filters.visible = function( elem ) {
+		return !jQuery.expr.filters.hidden( elem );
+	};
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+	margin: "",
+	padding: "",
+	border: "Width"
+}, function( prefix, suffix ) {
+	jQuery.cssHooks[ prefix + suffix ] = {
+		expand: function( value ) {
+			var i = 0,
+				expanded = {},
+
+				// assumes a single number if not a string
+				parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+			for ( ; i < 4; i++ ) {
+				expanded[ prefix + cssExpand[ i ] + suffix ] =
+					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+			}
+
+			return expanded;
+		}
+	};
+
+	if ( !rmargin.test( prefix ) ) {
+		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+	}
+});
+var r20 = /%20/g,
+	rbracket = /\[\]$/,
+	rCRLF = /\r?\n/g,
+	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+jQuery.fn.extend({
+	serialize: function() {
+		return jQuery.param( this.serializeArray() );
+	},
+	serializeArray: function() {
+		return this.map(function(){
+			// Can add propHook for "elements" to filter or add form elements
+			var elements = jQuery.prop( this, "elements" );
+			return elements ? jQuery.makeArray( elements ) : this;
+		})
+		.filter(function(){
+			var type = this.type;
+			// Use .is(":disabled") so that fieldset[disabled] works
+			return this.name && !jQuery( this ).is( ":disabled" ) &&
+				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+				( this.checked || !manipulation_rcheckableType.test( type ) );
+		})
+		.map(function( i, elem ){
+			var val = jQuery( this ).val();
+
+			return val == null ?
+				null :
+				jQuery.isArray( val ) ?
+					jQuery.map( val, function( val ){
+						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+					}) :
+					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+		}).get();
+	}
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+	var prefix,
+		s = [],
+		add = function( key, value ) {
+			// If value is a function, invoke it and return its value
+			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+		};
+
+	// Set traditional to true for jQuery <= 1.3.2 behavior.
+	if ( traditional === undefined ) {
+		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+	}
+
+	// If an array was passed in, assume that it is an array of form elements.
+	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+		// Serialize the form elements
+		jQuery.each( a, function() {
+			add( this.name, this.value );
+		});
+
+	} else {
+		// If traditional, encode the "old" way (the way 1.3.2 or older
+		// did it), otherwise encode params recursively.
+		for ( prefix in a ) {
+			buildParams( prefix, a[ prefix ], traditional, add );
+		}
+	}
+
+	// Return the resulting serialization
+	return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+	var name;
+
+	if ( jQuery.isArray( obj ) ) {
+		// Serialize array item.
+		jQuery.each( obj, function( i, v ) {
+			if ( traditional || rbracket.test( prefix ) ) {
+				// Treat each array item as a scalar.
+				add( prefix, v );
+
+			} else {
+				// Item is non-scalar (array or object), encode its numeric index.
+				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+			}
+		});
+
+	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
+		// Serialize object item.
+		for ( name in obj ) {
+			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+		}
+
+	} else {
+		// Serialize scalar item.
+		add( prefix, obj );
+	}
+}
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+	// Handle event binding
+	jQuery.fn[ name ] = function( data, fn ) {
+		return arguments.length > 0 ?
+			this.on( name, null, data, fn ) :
+			this.trigger( name );
+	};
+});
+
+jQuery.fn.extend({
+	hover: function( fnOver, fnOut ) {
+		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+	},
+
+	bind: function( types, data, fn ) {
+		return this.on( types, null, data, fn );
+	},
+	unbind: function( types, fn ) {
+		return this.off( types, null, fn );
+	},
+
+	delegate: function( selector, types, data, fn ) {
+		return this.on( types, selector, data, fn );
+	},
+	undelegate: function( selector, types, fn ) {
+		// ( namespace ) or ( selector, types [, fn] )
+		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+	}
+});
+var
+	// Document location
+	ajaxLocParts,
+	ajaxLocation,
+	ajax_nonce = jQuery.now(),
+
+	ajax_rquery = /\?/,
+	rhash = /#.*$/,
+	rts = /([?&])_=[^&]*/,
+	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+	// #7653, #8125, #8152: local protocol detection
+	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+	rnoContent = /^(?:GET|HEAD)$/,
+	rprotocol = /^\/\//,
+	rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+	// Keep a copy of the old load method
+	_load = jQuery.fn.load,
+
+	/* Prefilters
+	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+	 * 2) These are called:
+	 *    - BEFORE asking for a transport
+	 *    - AFTER param serialization (s.data is a string if s.processData is true)
+	 * 3) key is the dataType
+	 * 4) the catchall symbol "*" can be used
+	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+	 */
+	prefilters = {},
+
+	/* Transports bindings
+	 * 1) key is the dataType
+	 * 2) the catchall symbol "*" can be used
+	 * 3) selection will start with transport dataType and THEN go to "*" if needed
+	 */
+	transports = {},
+
+	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+	allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+	ajaxLocation = location.href;
+} catch( e ) {
+	// Use the href attribute of an A element
+	// since IE will modify it given document.location
+	ajaxLocation = document.createElement( "a" );
+	ajaxLocation.href = "";
+	ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+	// dataTypeExpression is optional and defaults to "*"
+	return function( dataTypeExpression, func ) {
+
+		if ( typeof dataTypeExpression !== "string" ) {
+			func = dataTypeExpression;
+			dataTypeExpression = "*";
+		}
+
+		var dataType,
+			i = 0,
+			dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
+
+		if ( jQuery.isFunction( func ) ) {
+			// For each dataType in the dataTypeExpression
+			while ( (dataType = dataTypes[i++]) ) {
+				// Prepend if requested
+				if ( dataType[0] === "+" ) {
+					dataType = dataType.slice( 1 ) || "*";
+					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+				// Otherwise append
+				} else {
+					(structure[ dataType ] = structure[ dataType ] || []).push( func );
+				}
+			}
+		}
+	};
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+	var inspected = {},
+		seekingTransport = ( structure === transports );
+
+	function inspect( dataType ) {
+		var selected;
+		inspected[ dataType ] = true;
+		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+			if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+				options.dataTypes.unshift( dataTypeOrTransport );
+				inspect( dataTypeOrTransport );
+				return false;
+			} else if ( seekingTransport ) {
+				return !( selected = dataTypeOrTransport );
+			}
+		});
+		return selected;
+	}
+
+	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+	var deep, key,
+		flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+	for ( key in src ) {
+		if ( src[ key ] !== undefined ) {
+			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+		}
+	}
+	if ( deep ) {
+		jQuery.extend( true, target, deep );
+	}
+
+	return target;
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+	if ( typeof url !== "string" && _load ) {
+		return _load.apply( this, arguments );
+	}
+
+	var selector, response, type,
+		self = this,
+		off = url.indexOf(" ");
+
+	if ( off >= 0 ) {
+		selector = url.slice( off, url.length );
+		url = url.slice( 0, off );
+	}
+
+	// If it's a function
+	if ( jQuery.isFunction( params ) ) {
+
+		// We assume that it's the callback
+		callback = params;
+		params = undefined;
+
+	// Otherwise, build a param string
+	} else if ( params && typeof params === "object" ) {
+		type = "POST";
+	}
+
+	// If we have elements to modify, make the request
+	if ( self.length > 0 ) {
+		jQuery.ajax({
+			url: url,
+
+			// if "type" variable is undefined, then "GET" method will be used
+			type: type,
+			dataType: "html",
+			data: params
+		}).done(function( responseText ) {
+
+			// Save response for use in complete callback
+			response = arguments;
+
+			self.html( selector ?
+
+				// If a selector was specified, locate the right elements in a dummy div
+				// Exclude scripts to avoid IE 'Permission Denied' errors
+				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+				// Otherwise use the full result
+				responseText );
+
+		}).complete( callback && function( jqXHR, status ) {
+			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+		});
+	}
+
+	return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
+	jQuery.fn[ type ] = function( fn ){
+		return this.on( type, fn );
+	};
+});
+
+jQuery.extend({
+
+	// Counter for holding the number of active queries
+	active: 0,
+
+	// Last-Modified header cache for next request
+	lastModified: {},
+	etag: {},
+
+	ajaxSettings: {
+		url: ajaxLocation,
+		type: "GET",
+		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+		global: true,
+		processData: true,
+		async: true,
+		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+		/*
+		timeout: 0,
+		data: null,
+		dataType: null,
+		username: null,
+		password: null,
+		cache: null,
+		throws: false,
+		traditional: false,
+		headers: {},
+		*/
+
+		accepts: {
+			"*": allTypes,
+			text: "text/plain",
+			html: "text/html",
+			xml: "application/xml, text/xml",
+			json: "application/json, text/javascript"
+		},
+
+		contents: {
+			xml: /xml/,
+			html: /html/,
+			json: /json/
+		},
+
+		responseFields: {
+			xml: "responseXML",
+			text: "responseText",
+			json: "responseJSON"
+		},
+
+		// Data converters
+		// Keys separate source (or catchall "*") and destination types with a single space
+		converters: {
+
+			// Convert anything to text
+			"* text": String,
+
+			// Text to html (true = no transformation)
+			"text html": true,
+
+			// Evaluate text as a json expression
+			"text json": jQuery.parseJSON,
+
+			// Parse text as xml
+			"text xml": jQuery.parseXML
+		},
+
+		// For options that shouldn't be deep extended:
+		// you can add your own custom options here if
+		// and when you create one that shouldn't be
+		// deep extended (see ajaxExtend)
+		flatOptions: {
+			url: true,
+			context: true
+		}
+	},
+
+	// Creates a full fledged settings object into target
+	// with both ajaxSettings and settings fields.
+	// If target is omitted, writes into ajaxSettings.
+	ajaxSetup: function( target, settings ) {
+		return settings ?
+
+			// Building a settings object
+			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+			// Extending ajaxSettings
+			ajaxExtend( jQuery.ajaxSettings, target );
+	},
+
+	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+	ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+	// Main method
+	ajax: function( url, options ) {
+
+		// If url is an object, simulate pre-1.5 signature
+		if ( typeof url === "object" ) {
+			options = url;
+			url = undefined;
+		}
+
+		// Force options to be an object
+		options = options || {};
+
+		var // Cross-domain detection vars
+			parts,
+			// Loop variable
+			i,
+			// URL without anti-cache param
+			cacheURL,
+			// Response headers as string
+			responseHeadersString,
+			// timeout handle
+			timeoutTimer,
+
+			// To know if global events are to be dispatched
+			fireGlobals,
+
+			transport,
+			// Response headers
+			responseHeaders,
+			// Create the final options object
+			s = jQuery.ajaxSetup( {}, options ),
+			// Callbacks context
+			callbackContext = s.context || s,
+			// Context for global events is callbackContext if it is a DOM node or jQuery collection
+			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+				jQuery( callbackContext ) :
+				jQuery.event,
+			// Deferreds
+			deferred = jQuery.Deferred(),
+			completeDeferred = jQuery.Callbacks("once memory"),
+			// Status-dependent callbacks
+			statusCode = s.statusCode || {},
+			// Headers (they are sent all at once)
+			requestHeaders = {},
+			requestHeadersNames = {},
+			// The jqXHR state
+			state = 0,
+			// Default abort message
+			strAbort = "canceled",
+			// Fake xhr
+			jqXHR = {
+				readyState: 0,
+
+				// Builds headers hashtable if needed
+				getResponseHeader: function( key ) {
+					var match;
+					if ( state === 2 ) {
+						if ( !responseHeaders ) {
+							responseHeaders = {};
+							while ( (match = rheaders.exec( responseHeadersString )) ) {
+								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+							}
+						}
+						match = responseHeaders[ key.toLowerCase() ];
+					}
+					return match == null ? null : match;
+				},
+
+				// Raw string
+				getAllResponseHeaders: function() {
+					return state === 2 ? responseHeadersString : null;
+				},
+
+				// Caches the header
+				setRequestHeader: function( name, value ) {
+					var lname = name.toLowerCase();
+					if ( !state ) {
+						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+						requestHeaders[ name ] = value;
+					}
+					return this;
+				},
+
+				// Overrides response content-type header
+				overrideMimeType: function( type ) {
+					if ( !state ) {
+						s.mimeType = type;
+					}
+					return this;
+				},
+
+				// Status-dependent callbacks
+				statusCode: function( map ) {
+					var code;
+					if ( map ) {
+						if ( state < 2 ) {
+							for ( code in map ) {
+								// Lazy-add the new callback in a way that preserves old ones
+								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+							}
+						} else {
+							// Execute the appropriate callbacks
+							jqXHR.always( map[ jqXHR.status ] );
+						}
+					}
+					return this;
+				},
+
+				// Cancel the request
+				abort: function( statusText ) {
+					var finalText = statusText || strAbort;
+					if ( transport ) {
+						transport.abort( finalText );
+					}
+					done( 0, finalText );
+					return this;
+				}
+			};
+
+		// Attach deferreds
+		deferred.promise( jqXHR ).complete = completeDeferred.add;
+		jqXHR.success = jqXHR.done;
+		jqXHR.error = jqXHR.fail;
+
+		// Remove hash character (#7531: and string promotion)
+		// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+		// Handle falsy url in the settings object (#10093: consistency with old signature)
+		// We also use the url parameter if available
+		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+		// Alias method option to type as per ticket #12004
+		s.type = options.method || options.type || s.method || s.type;
+
+		// Extract dataTypes list
+		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
+
+		// A cross-domain request is in order when we have a protocol:host:port mismatch
+		if ( s.crossDomain == null ) {
+			parts = rurl.exec( s.url.toLowerCase() );
+			s.crossDomain = !!( parts &&
+				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+			);
+		}
+
+		// Convert data if not already a string
+		if ( s.data && s.processData && typeof s.data !== "string" ) {
+			s.data = jQuery.param( s.data, s.traditional );
+		}
+
+		// Apply prefilters
+		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+		// If request was aborted inside a prefilter, stop there
+		if ( state === 2 ) {
+			return jqXHR;
+		}
+
+		// We can fire global events as of now if asked to
+		fireGlobals = s.global;
+
+		// Watch for a new set of requests
+		if ( fireGlobals && jQuery.active++ === 0 ) {
+			jQuery.event.trigger("ajaxStart");
+		}
+
+		// Uppercase the type
+		s.type = s.type.toUpperCase();
+
+		// Determine if request has content
+		s.hasContent = !rnoContent.test( s.type );
+
+		// Save the URL in case we're toying with the If-Modified-Since
+		// and/or If-None-Match header later on
+		cacheURL = s.url;
+
+		// More options handling for requests with no content
+		if ( !s.hasContent ) {
+
+			// If data is available, append data to url
+			if ( s.data ) {
+				cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+				// #9682: remove data so that it's not used in an eventual retry
+				delete s.data;
+			}
+
+			// Add anti-cache in url if needed
+			if ( s.cache === false ) {
+				s.url = rts.test( cacheURL ) ?
+
+					// If there is already a '_' parameter, set its value
+					cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
+
+					// Otherwise add one to the end
+					cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
+			}
+		}
+
+		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+		if ( s.ifModified ) {
+			if ( jQuery.lastModified[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+			}
+			if ( jQuery.etag[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+			}
+		}
+
+		// Set the correct header, if data is being sent
+		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+			jqXHR.setRequestHeader( "Content-Type", s.contentType );
+		}
+
+		// Set the Accepts header for the server, depending on the dataType
+		jqXHR.setRequestHeader(
+			"Accept",
+			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+				s.accepts[ "*" ]
+		);
+
+		// Check for headers option
+		for ( i in s.headers ) {
+			jqXHR.setRequestHeader( i, s.headers[ i ] );
+		}
+
+		// Allow custom headers/mimetypes and early abort
+		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+			// Abort if not done already and return
+			return jqXHR.abort();
+		}
+
+		// aborting is no longer a cancellation
+		strAbort = "abort";
+
+		// Install callbacks on deferreds
+		for ( i in { success: 1, error: 1, complete: 1 } ) {
+			jqXHR[ i ]( s[ i ] );
+		}
+
+		// Get transport
+		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+		// If no transport, we auto-abort
+		if ( !transport ) {
+			done( -1, "No Transport" );
+		} else {
+			jqXHR.readyState = 1;
+
+			// Send global event
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+			}
+			// Timeout
+			if ( s.async && s.timeout > 0 ) {
+				timeoutTimer = setTimeout(function() {
+					jqXHR.abort("timeout");
+				}, s.timeout );
+			}
+
+			try {
+				state = 1;
+				transport.send( requestHeaders, done );
+			} catch ( e ) {
+				// Propagate exception as error if not done
+				if ( state < 2 ) {
+					done( -1, e );
+				// Simply rethrow otherwise
+				} else {
+					throw e;
+				}
+			}
+		}
+
+		// Callback for when everything is done
+		function done( status, nativeStatusText, responses, headers ) {
+			var isSuccess, success, error, response, modified,
+				statusText = nativeStatusText;
+
+			// Called once
+			if ( state === 2 ) {
+				return;
+			}
+
+			// State is "done" now
+			state = 2;
+
+			// Clear timeout if it exists
+			if ( timeoutTimer ) {
+				clearTimeout( timeoutTimer );
+			}
+
+			// Dereference transport for early garbage collection
+			// (no matter how long the jqXHR object will be used)
+			transport = undefined;
+
+			// Cache response headers
+			responseHeadersString = headers || "";
+
+			// Set readyState
+			jqXHR.readyState = status > 0 ? 4 : 0;
+
+			// Determine if successful
+			isSuccess = status >= 200 && status < 300 || status === 304;
+
+			// Get response data
+			if ( responses ) {
+				response = ajaxHandleResponses( s, jqXHR, responses );
+			}
+
+			// Convert no matter what (that way responseXXX fields are always set)
+			response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+			// If successful, handle type chaining
+			if ( isSuccess ) {
+
+				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+				if ( s.ifModified ) {
+					modified = jqXHR.getResponseHeader("Last-Modified");
+					if ( modified ) {
+						jQuery.lastModified[ cacheURL ] = modified;
+					}
+					modified = jqXHR.getResponseHeader("etag");
+					if ( modified ) {
+						jQuery.etag[ cacheURL ] = modified;
+					}
+				}
+
+				// if no content
+				if ( status === 204 || s.type === "HEAD" ) {
+					statusText = "nocontent";
+
+				// if not modified
+				} else if ( status === 304 ) {
+					statusText = "notmodified";
+
+				// If we have data, let's convert it
+				} else {
+					statusText = response.state;
+					success = response.data;
+					error = response.error;
+					isSuccess = !error;
+				}
+			} else {
+				// We extract error from statusText
+				// then normalize statusText and status for non-aborts
+				error = statusText;
+				if ( status || !statusText ) {
+					statusText = "error";
+					if ( status < 0 ) {
+						status = 0;
+					}
+				}
+			}
+
+			// Set data for the fake xhr object
+			jqXHR.status = status;
+			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+			// Success/Error
+			if ( isSuccess ) {
+				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+			} else {
+				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+			}
+
+			// Status-dependent callbacks
+			jqXHR.statusCode( statusCode );
+			statusCode = undefined;
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+					[ jqXHR, s, isSuccess ? success : error ] );
+			}
+
+			// Complete
+			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+				// Handle the global AJAX counter
+				if ( !( --jQuery.active ) ) {
+					jQuery.event.trigger("ajaxStop");
+				}
+			}
+		}
+
+		return jqXHR;
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get( url, data, callback, "json" );
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get( url, undefined, callback, "script" );
+	}
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+	jQuery[ method ] = function( url, data, callback, type ) {
+		// shift arguments if data argument was omitted
+		if ( jQuery.isFunction( data ) ) {
+			type = type || callback;
+			callback = data;
+			data = undefined;
+		}
+
+		return jQuery.ajax({
+			url: url,
+			type: method,
+			dataType: type,
+			data: data,
+			success: callback
+		});
+	};
+});
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+	var firstDataType, ct, finalDataType, type,
+		contents = s.contents,
+		dataTypes = s.dataTypes;
+
+	// Remove auto dataType and get content-type in the process
+	while( dataTypes[ 0 ] === "*" ) {
+		dataTypes.shift();
+		if ( ct === undefined ) {
+			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+		}
+	}
+
+	// Check if we're dealing with a known content-type
+	if ( ct ) {
+		for ( type in contents ) {
+			if ( contents[ type ] && contents[ type ].test( ct ) ) {
+				dataTypes.unshift( type );
+				break;
+			}
+		}
+	}
+
+	// Check to see if we have a response for the expected dataType
+	if ( dataTypes[ 0 ] in responses ) {
+		finalDataType = dataTypes[ 0 ];
+	} else {
+		// Try convertible dataTypes
+		for ( type in responses ) {
+			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+				finalDataType = type;
+				break;
+			}
+			if ( !firstDataType ) {
+				firstDataType = type;
+			}
+		}
+		// Or just use first one
+		finalDataType = finalDataType || firstDataType;
+	}
+
+	// If we found a dataType
+	// We add the dataType to the list if needed
+	// and return the corresponding response
+	if ( finalDataType ) {
+		if ( finalDataType !== dataTypes[ 0 ] ) {
+			dataTypes.unshift( finalDataType );
+		}
+		return responses[ finalDataType ];
+	}
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+	var conv2, current, conv, tmp, prev,
+		converters = {},
+		// Work with a copy of dataTypes in case we need to modify it for conversion
+		dataTypes = s.dataTypes.slice();
+
+	// Create converters map with lowercased keys
+	if ( dataTypes[ 1 ] ) {
+		for ( conv in s.converters ) {
+			converters[ conv.toLowerCase() ] = s.converters[ conv ];
+		}
+	}
+
+	current = dataTypes.shift();
+
+	// Convert to each sequential dataType
+	while ( current ) {
+
+		if ( s.responseFields[ current ] ) {
+			jqXHR[ s.responseFields[ current ] ] = response;
+		}
+
+		// Apply the dataFilter if provided
+		if ( !prev && isSuccess && s.dataFilter ) {
+			response = s.dataFilter( response, s.dataType );
+		}
+
+		prev = current;
+		current = dataTypes.shift();
+
+		if ( current ) {
+
+			// There's only work to do if current dataType is non-auto
+			if ( current === "*" ) {
+
+				current = prev;
+
+			// Convert response if prev dataType is non-auto and differs from current
+			} else if ( prev !== "*" && prev !== current ) {
+
+				// Seek a direct converter
+				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+				// If none found, seek a pair
+				if ( !conv ) {
+					for ( conv2 in converters ) {
+
+						// If conv2 outputs current
+						tmp = conv2.split( " " );
+						if ( tmp[ 1 ] === current ) {
+
+							// If prev can be converted to accepted input
+							conv = converters[ prev + " " + tmp[ 0 ] ] ||
+								converters[ "* " + tmp[ 0 ] ];
+							if ( conv ) {
+								// Condense equivalence converters
+								if ( conv === true ) {
+									conv = converters[ conv2 ];
+
+								// Otherwise, insert the intermediate dataType
+								} else if ( converters[ conv2 ] !== true ) {
+									current = tmp[ 0 ];
+									dataTypes.unshift( tmp[ 1 ] );
+								}
+								break;
+							}
+						}
+					}
+				}
+
+				// Apply converter (if not an equivalence)
+				if ( conv !== true ) {
+
+					// Unless errors are allowed to bubble, catch and return them
+					if ( conv && s[ "throws" ] ) {
+						response = conv( response );
+					} else {
+						try {
+							response = conv( response );
+						} catch ( e ) {
+							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return { state: "success", data: response };
+}
+// Install script dataType
+jQuery.ajaxSetup({
+	accepts: {
+		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+	},
+	contents: {
+		script: /(?:java|ecma)script/
+	},
+	converters: {
+		"text script": function( text ) {
+			jQuery.globalEval( text );
+			return text;
+		}
+	}
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+	if ( s.cache === undefined ) {
+		s.cache = false;
+	}
+	if ( s.crossDomain ) {
+		s.type = "GET";
+		s.global = false;
+	}
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+	// This transport only deals with cross domain requests
+	if ( s.crossDomain ) {
+
+		var script,
+			head = document.head || jQuery("head")[0] || document.documentElement;
+
+		return {
+
+			send: function( _, callback ) {
+
+				script = document.createElement("script");
+
+				script.async = true;
+
+				if ( s.scriptCharset ) {
+					script.charset = s.scriptCharset;
+				}
+
+				script.src = s.url;
+
+				// Attach handlers for all browsers
+				script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+						// Handle memory leak in IE
+						script.onload = script.onreadystatechange = null;
+
+						// Remove the script
+						if ( script.parentNode ) {
+							script.parentNode.removeChild( script );
+						}
+
+						// Dereference the script
+						script = null;
+
+						// Callback if not abort
+						if ( !isAbort ) {
+							callback( 200, "success" );
+						}
+					}
+				};
+
+				// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+				// Use native DOM manipulation to avoid our domManip AJAX trickery
+				head.insertBefore( script, head.firstChild );
+			},
+
+			abort: function() {
+				if ( script ) {
+					script.onload( undefined, true );
+				}
+			}
+		};
+	}
+});
+var oldCallbacks = [],
+	rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+	jsonp: "callback",
+	jsonpCallback: function() {
+		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
+		this[ callback ] = true;
+		return callback;
+	}
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+	var callbackName, overwritten, responseContainer,
+		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+			"url" :
+			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+		);
+
+	// Handle iff the expected data type is "jsonp" or we have a parameter to set
+	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+		// Get callback name, remembering preexisting value associated with it
+		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+			s.jsonpCallback() :
+			s.jsonpCallback;
+
+		// Insert callback into url or form data
+		if ( jsonProp ) {
+			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+		} else if ( s.jsonp !== false ) {
+			s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+		}
+
+		// Use data converter to retrieve json after script execution
+		s.converters["script json"] = function() {
+			if ( !responseContainer ) {
+				jQuery.error( callbackName + " was not called" );
+			}
+			return responseContainer[ 0 ];
+		};
+
+		// force json dataType
+		s.dataTypes[ 0 ] = "json";
+
+		// Install callback
+		overwritten = window[ callbackName ];
+		window[ callbackName ] = function() {
+			responseContainer = arguments;
+		};
+
+		// Clean-up function (fires after converters)
+		jqXHR.always(function() {
+			// Restore preexisting value
+			window[ callbackName ] = overwritten;
+
+			// Save back as free
+			if ( s[ callbackName ] ) {
+				// make sure that re-using the options doesn't screw things around
+				s.jsonpCallback = originalSettings.jsonpCallback;
+
+				// save the callback name for future use
+				oldCallbacks.push( callbackName );
+			}
+
+			// Call if it was a function and we have a response
+			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+				overwritten( responseContainer[ 0 ] );
+			}
+
+			responseContainer = overwritten = undefined;
+		});
+
+		// Delegate to script
+		return "script";
+	}
+});
+var xhrCallbacks, xhrSupported,
+	xhrId = 0,
+	// #5280: Internet Explorer will keep connections alive if we don't abort on unload
+	xhrOnUnloadAbort = window.ActiveXObject && function() {
+		// Abort all pending requests
+		var key;
+		for ( key in xhrCallbacks ) {
+			xhrCallbacks[ key ]( undefined, true );
+		}
+	};
+
+// Functions to create xhrs
+function createStandardXHR() {
+	try {
+		return new window.XMLHttpRequest();
+	} catch( e ) {}
+}
+
+function createActiveXHR() {
+	try {
+		return new window.ActiveXObject("Microsoft.XMLHTTP");
+	} catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+	/* Microsoft failed to properly
+	 * implement the XMLHttpRequest in IE7 (can't request local files),
+	 * so we use the ActiveXObject when it is available
+	 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+	 * we need a fallback.
+	 */
+	function() {
+		return !this.isLocal && createStandardXHR() || createActiveXHR();
+	} :
+	// For all other browsers, use the standard XMLHttpRequest object
+	createStandardXHR;
+
+// Determine support properties
+xhrSupported = jQuery.ajaxSettings.xhr();
+jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+xhrSupported = jQuery.support.ajax = !!xhrSupported;
+
+// Create transport if the browser can provide an xhr
+if ( xhrSupported ) {
+
+	jQuery.ajaxTransport(function( s ) {
+		// Cross domain only allowed if supported through XMLHttpRequest
+		if ( !s.crossDomain || jQuery.support.cors ) {
+
+			var callback;
+
+			return {
+				send: function( headers, complete ) {
+
+					// Get a new xhr
+					var handle, i,
+						xhr = s.xhr();
+
+					// Open the socket
+					// Passing null username, generates a login popup on Opera (#2865)
+					if ( s.username ) {
+						xhr.open( s.type, s.url, s.async, s.username, s.password );
+					} else {
+						xhr.open( s.type, s.url, s.async );
+					}
+
+					// Apply custom fields if provided
+					if ( s.xhrFields ) {
+						for ( i in s.xhrFields ) {
+							xhr[ i ] = s.xhrFields[ i ];
+						}
+					}
+
+					// Override mime type if needed
+					if ( s.mimeType && xhr.overrideMimeType ) {
+						xhr.overrideMimeType( s.mimeType );
+					}
+
+					// X-Requested-With header
+					// For cross-domain requests, seeing as conditions for a preflight are
+					// akin to a jigsaw puzzle, we simply never set it to be sure.
+					// (it can always be set on a per-request basis or even using ajaxSetup)
+					// For same-domain requests, won't change header if already provided.
+					if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+						headers["X-Requested-With"] = "XMLHttpRequest";
+					}
+
+					// Need an extra try/catch for cross domain requests in Firefox 3
+					try {
+						for ( i in headers ) {
+							xhr.setRequestHeader( i, headers[ i ] );
+						}
+					} catch( err ) {}
+
+					// Do send the request
+					// This may raise an exception which is actually
+					// handled in jQuery.ajax (so no try/catch here)
+					xhr.send( ( s.hasContent && s.data ) || null );
+
+					// Listener
+					callback = function( _, isAbort ) {
+						var status, responseHeaders, statusText, responses;
+
+						// Firefox throws exceptions when accessing properties
+						// of an xhr when a network error occurred
+						// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+						try {
+
+							// Was never called and is aborted or complete
+							if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+								// Only called once
+								callback = undefined;
+
+								// Do not keep as active anymore
+								if ( handle ) {
+									xhr.onreadystatechange = jQuery.noop;
+									if ( xhrOnUnloadAbort ) {
+										delete xhrCallbacks[ handle ];
+									}
+								}
+
+								// If it's an abort
+								if ( isAbort ) {
+									// Abort it manually if needed
+									if ( xhr.readyState !== 4 ) {
+										xhr.abort();
+									}
+								} else {
+									responses = {};
+									status = xhr.status;
+									responseHeaders = xhr.getAllResponseHeaders();
+
+									// When requesting binary data, IE6-9 will throw an exception
+									// on any attempt to access responseText (#11426)
+									if ( typeof xhr.responseText === "string" ) {
+										responses.text = xhr.responseText;
+									}
+
+									// Firefox throws an exception when accessing
+									// statusText for faulty cross-domain requests
+									try {
+										statusText = xhr.statusText;
+									} catch( e ) {
+										// We normalize with Webkit giving an empty statusText
+										statusText = "";
+									}
+
+									// Filter status for non standard behaviors
+
+									// If the request is local and we have data: assume a success
+									// (success with no data won't get notified, that's the best we
+									// can do given current implementations)
+									if ( !status && s.isLocal && !s.crossDomain ) {
+										status = responses.text ? 200 : 404;
+									// IE - #1450: sometimes returns 1223 when it should be 204
+									} else if ( status === 1223 ) {
+										status = 204;
+									}
+								}
+							}
+						} catch( firefoxAccessException ) {
+							if ( !isAbort ) {
+								complete( -1, firefoxAccessException );
+							}
+						}
+
+						// Call complete if needed
+						if ( responses ) {
+							complete( status, statusText, responses, responseHeaders );
+						}
+					};
+
+					if ( !s.async ) {
+						// if we're in sync mode we fire the callback
+						callback();
+					} else if ( xhr.readyState === 4 ) {
+						// (IE6 & IE7) if it's in cache and has been
+						// retrieved directly we need to fire the callback
+						setTimeout( callback );
+					} else {
+						handle = ++xhrId;
+						if ( xhrOnUnloadAbort ) {
+							// Create the active xhrs callbacks list if needed
+							// and attach the unload handler
+							if ( !xhrCallbacks ) {
+								xhrCallbacks = {};
+								jQuery( window ).unload( xhrOnUnloadAbort );
+							}
+							// Add to list of active xhrs callbacks
+							xhrCallbacks[ handle ] = callback;
+						}
+						xhr.onreadystatechange = callback;
+					}
+				},
+
+				abort: function() {
+					if ( callback ) {
+						callback( undefined, true );
+					}
+				}
+			};
+		}
+	});
+}
+var fxNow, timerId,
+	rfxtypes = /^(?:toggle|show|hide)$/,
+	rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+	rrun = /queueHooks$/,
+	animationPrefilters = [ defaultPrefilter ],
+	tweeners = {
+		"*": [function( prop, value ) {
+			var tween = this.createTween( prop, value ),
+				target = tween.cur(),
+				parts = rfxnum.exec( value ),
+				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+				// Starting value computation is required for potential unit mismatches
+				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+				scale = 1,
+				maxIterations = 20;
+
+			if ( start && start[ 3 ] !== unit ) {
+				// Trust units reported by jQuery.css
+				unit = unit || start[ 3 ];
+
+				// Make sure we update the tween properties later on
+				parts = parts || [];
+
+				// Iteratively approximate from a nonzero starting point
+				start = +target || 1;
+
+				do {
+					// If previous iteration zeroed out, double until we get *something*
+					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
+					scale = scale || ".5";
+
+					// Adjust and apply
+					start = start / scale;
+					jQuery.style( tween.elem, prop, start + unit );
+
+				// Update scale, tolerating zero or NaN from tween.cur()
+				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+			}
+
+			// Update tween properties
+			if ( parts ) {
+				start = tween.start = +start || +target || 0;
+				tween.unit = unit;
+				// If a +=/-= token was provided, we're doing a relative animation
+				tween.end = parts[ 1 ] ?
+					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+					+parts[ 2 ];
+			}
+
+			return tween;
+		}]
+	};
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+	setTimeout(function() {
+		fxNow = undefined;
+	});
+	return ( fxNow = jQuery.now() );
+}
+
+function createTween( value, prop, animation ) {
+	var tween,
+		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+		index = 0,
+		length = collection.length;
+	for ( ; index < length; index++ ) {
+		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+			// we're done with this property
+			return tween;
+		}
+	}
+}
+
+function Animation( elem, properties, options ) {
+	var result,
+		stopped,
+		index = 0,
+		length = animationPrefilters.length,
+		deferred = jQuery.Deferred().always( function() {
+			// don't match elem in the :animated selector
+			delete tick.elem;
+		}),
+		tick = function() {
+			if ( stopped ) {
+				return false;
+			}
+			var currentTime = fxNow || createFxNow(),
+				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+				temp = remaining / animation.duration || 0,
+				percent = 1 - temp,
+				index = 0,
+				length = animation.tweens.length;
+
+			for ( ; index < length ; index++ ) {
+				animation.tweens[ index ].run( percent );
+			}
+
+			deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+			if ( percent < 1 && length ) {
+				return remaining;
+			} else {
+				deferred.resolveWith( elem, [ animation ] );
+				return false;
+			}
+		},
+		animation = deferred.promise({
+			elem: elem,
+			props: jQuery.extend( {}, properties ),
+			opts: jQuery.extend( true, { specialEasing: {} }, options ),
+			originalProperties: properties,
+			originalOptions: options,
+			startTime: fxNow || createFxNow(),
+			duration: options.duration,
+			tweens: [],
+			createTween: function( prop, end ) {
+				var tween = jQuery.Tween( elem, animation.opts, prop, end,
+						animation.opts.specialEasing[ prop ] || animation.opts.easing );
+				animation.tweens.push( tween );
+				return tween;
+			},
+			stop: function( gotoEnd ) {
+				var index = 0,
+					// if we are going to the end, we want to run all the tweens
+					// otherwise we skip this part
+					length = gotoEnd ? animation.tweens.length : 0;
+				if ( stopped ) {
+					return this;
+				}
+				stopped = true;
+				for ( ; index < length ; index++ ) {
+					animation.tweens[ index ].run( 1 );
+				}
+
+				// resolve when we played the last frame
+				// otherwise, reject
+				if ( gotoEnd ) {
+					deferred.resolveWith( elem, [ animation, gotoEnd ] );
+				} else {
+					deferred.rejectWith( elem, [ animation, gotoEnd ] );
+				}
+				return this;
+			}
+		}),
+		props = animation.props;
+
+	propFilter( props, animation.opts.specialEasing );
+
+	for ( ; index < length ; index++ ) {
+		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+		if ( result ) {
+			return result;
+		}
+	}
+
+	jQuery.map( props, createTween, animation );
+
+	if ( jQuery.isFunction( animation.opts.start ) ) {
+		animation.opts.start.call( elem, animation );
+	}
+
+	jQuery.fx.timer(
+		jQuery.extend( tick, {
+			elem: elem,
+			anim: animation,
+			queue: animation.opts.queue
+		})
+	);
+
+	// attach callbacks from options
+	return animation.progress( animation.opts.progress )
+		.done( animation.opts.done, animation.opts.complete )
+		.fail( animation.opts.fail )
+		.always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+	var index, name, easing, value, hooks;
+
+	// camelCase, specialEasing and expand cssHook pass
+	for ( index in props ) {
+		name = jQuery.camelCase( index );
+		easing = specialEasing[ name ];
+		value = props[ index ];
+		if ( jQuery.isArray( value ) ) {
+			easing = value[ 1 ];
+			value = props[ index ] = value[ 0 ];
+		}
+
+		if ( index !== name ) {
+			props[ name ] = value;
+			delete props[ index ];
+		}
+
+		hooks = jQuery.cssHooks[ name ];
+		if ( hooks && "expand" in hooks ) {
+			value = hooks.expand( value );
+			delete props[ name ];
+
+			// not quite $.extend, this wont overwrite keys already present.
+			// also - reusing 'index' from above because we have the correct "name"
+			for ( index in value ) {
+				if ( !( index in props ) ) {
+					props[ index ] = value[ index ];
+					specialEasing[ index ] = easing;
+				}
+			}
+		} else {
+			specialEasing[ name ] = easing;
+		}
+	}
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+	tweener: function( props, callback ) {
+		if ( jQuery.isFunction( props ) ) {
+			callback = props;
+			props = [ "*" ];
+		} else {
+			props = props.split(" ");
+		}
+
+		var prop,
+			index = 0,
+			length = props.length;
+
+		for ( ; index < length ; index++ ) {
+			prop = props[ index ];
+			tweeners[ prop ] = tweeners[ prop ] || [];
+			tweeners[ prop ].unshift( callback );
+		}
+	},
+
+	prefilter: function( callback, prepend ) {
+		if ( prepend ) {
+			animationPrefilters.unshift( callback );
+		} else {
+			animationPrefilters.push( callback );
+		}
+	}
+});
+
+function defaultPrefilter( elem, props, opts ) {
+	/* jshint validthis: true */
+	var prop, value, toggle, tween, hooks, oldfire,
+		anim = this,
+		orig = {},
+		style = elem.style,
+		hidden = elem.nodeType && isHidden( elem ),
+		dataShow = jQuery._data( elem, "fxshow" );
+
+	// handle queue: false promises
+	if ( !opts.queue ) {
+		hooks = jQuery._queueHooks( elem, "fx" );
+		if ( hooks.unqueued == null ) {
+			hooks.unqueued = 0;
+			oldfire = hooks.empty.fire;
+			hooks.empty.fire = function() {
+				if ( !hooks.unqueued ) {
+					oldfire();
+				}
+			};
+		}
+		hooks.unqueued++;
+
+		anim.always(function() {
+			// doing this makes sure that the complete handler will be called
+			// before this completes
+			anim.always(function() {
+				hooks.unqueued--;
+				if ( !jQuery.queue( elem, "fx" ).length ) {
+					hooks.empty.fire();
+				}
+			});
+		});
+	}
+
+	// height/width overflow pass
+	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+		// Make sure that nothing sneaks out
+		// Record all 3 overflow attributes because IE does not
+		// change the overflow attribute when overflowX and
+		// overflowY are set to the same value
+		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+		// Set display property to inline-block for height/width
+		// animations on inline elements that are having width/height animated
+		if ( jQuery.css( elem, "display" ) === "inline" &&
+				jQuery.css( elem, "float" ) === "none" ) {
+
+			// inline-level elements accept inline-block;
+			// block-level elements need to be inline with layout
+			if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+				style.display = "inline-block";
+
+			} else {
+				style.zoom = 1;
+			}
+		}
+	}
+
+	if ( opts.overflow ) {
+		style.overflow = "hidden";
+		if ( !jQuery.support.shrinkWrapBlocks ) {
+			anim.always(function() {
+				style.overflow = opts.overflow[ 0 ];
+				style.overflowX = opts.overflow[ 1 ];
+				style.overflowY = opts.overflow[ 2 ];
+			});
+		}
+	}
+
+
+	// show/hide pass
+	for ( prop in props ) {
+		value = props[ prop ];
+		if ( rfxtypes.exec( value ) ) {
+			delete props[ prop ];
+			toggle = toggle || value === "toggle";
+			if ( value === ( hidden ? "hide" : "show" ) ) {
+				continue;
+			}
+			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+		}
+	}
+
+	if ( !jQuery.isEmptyObject( orig ) ) {
+		if ( dataShow ) {
+			if ( "hidden" in dataShow ) {
+				hidden = dataShow.hidden;
+			}
+		} else {
+			dataShow = jQuery._data( elem, "fxshow", {} );
+		}
+
+		// store state if its toggle - enables .stop().toggle() to "reverse"
+		if ( toggle ) {
+			dataShow.hidden = !hidden;
+		}
+		if ( hidden ) {
+			jQuery( elem ).show();
+		} else {
+			anim.done(function() {
+				jQuery( elem ).hide();
+			});
+		}
+		anim.done(function() {
+			var prop;
+			jQuery._removeData( elem, "fxshow" );
+			for ( prop in orig ) {
+				jQuery.style( elem, prop, orig[ prop ] );
+			}
+		});
+		for ( prop in orig ) {
+			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+			if ( !( prop in dataShow ) ) {
+				dataShow[ prop ] = tween.start;
+				if ( hidden ) {
+					tween.end = tween.start;
+					tween.start = prop === "width" || prop === "height" ? 1 : 0;
+				}
+			}
+		}
+	}
+}
+
+function Tween( elem, options, prop, end, easing ) {
+	return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+	constructor: Tween,
+	init: function( elem, options, prop, end, easing, unit ) {
+		this.elem = elem;
+		this.prop = prop;
+		this.easing = easing || "swing";
+		this.options = options;
+		this.start = this.now = this.cur();
+		this.end = end;
+		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+	},
+	cur: function() {
+		var hooks = Tween.propHooks[ this.prop ];
+
+		return hooks && hooks.get ?
+			hooks.get( this ) :
+			Tween.propHooks._default.get( this );
+	},
+	run: function( percent ) {
+		var eased,
+			hooks = Tween.propHooks[ this.prop ];
+
+		if ( this.options.duration ) {
+			this.pos = eased = jQuery.easing[ this.easing ](
+				percent, this.options.duration * percent, 0, 1, this.options.duration
+			);
+		} else {
+			this.pos = eased = percent;
+		}
+		this.now = ( this.end - this.start ) * eased + this.start;
+
+		if ( this.options.step ) {
+			this.options.step.call( this.elem, this.now, this );
+		}
+
+		if ( hooks && hooks.set ) {
+			hooks.set( this );
+		} else {
+			Tween.propHooks._default.set( this );
+		}
+		return this;
+	}
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+	_default: {
+		get: function( tween ) {
+			var result;
+
+			if ( tween.elem[ tween.prop ] != null &&
+				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+				return tween.elem[ tween.prop ];
+			}
+
+			// passing an empty string as a 3rd parameter to .css will automatically
+			// attempt a parseFloat and fallback to a string if the parse fails
+			// so, simple values such as "10px" are parsed to Float.
+			// complex values such as "rotate(1rad)" are returned as is.
+			result = jQuery.css( tween.elem, tween.prop, "" );
+			// Empty strings, null, undefined and "auto" are converted to 0.
+			return !result || result === "auto" ? 0 : result;
+		},
+		set: function( tween ) {
+			// use step hook for back compat - use cssHook if its there - use .style if its
+			// available and use plain properties where available
+			if ( jQuery.fx.step[ tween.prop ] ) {
+				jQuery.fx.step[ tween.prop ]( tween );
+			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+			} else {
+				tween.elem[ tween.prop ] = tween.now;
+			}
+		}
+	}
+};
+
+// Support: IE <=9
+// Panic based approach to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+	set: function( tween ) {
+		if ( tween.elem.nodeType && tween.elem.parentNode ) {
+			tween.elem[ tween.prop ] = tween.now;
+		}
+	}
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+	var cssFn = jQuery.fn[ name ];
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return speed == null || typeof speed === "boolean" ?
+			cssFn.apply( this, arguments ) :
+			this.animate( genFx( name, true ), speed, easing, callback );
+	};
+});
+
+jQuery.fn.extend({
+	fadeTo: function( speed, to, easing, callback ) {
+
+		// show any hidden elements after setting opacity to 0
+		return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+			// animate to the value specified
+			.end().animate({ opacity: to }, speed, easing, callback );
+	},
+	animate: function( prop, speed, easing, callback ) {
+		var empty = jQuery.isEmptyObject( prop ),
+			optall = jQuery.speed( speed, easing, callback ),
+			doAnimation = function() {
+				// Operate on a copy of prop so per-property easing won't be lost
+				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+				// Empty animations, or finishing resolves immediately
+				if ( empty || jQuery._data( this, "finish" ) ) {
+					anim.stop( true );
+				}
+			};
+			doAnimation.finish = doAnimation;
+
+		return empty || optall.queue === false ?
+			this.each( doAnimation ) :
+			this.queue( optall.queue, doAnimation );
+	},
+	stop: function( type, clearQueue, gotoEnd ) {
+		var stopQueue = function( hooks ) {
+			var stop = hooks.stop;
+			delete hooks.stop;
+			stop( gotoEnd );
+		};
+
+		if ( typeof type !== "string" ) {
+			gotoEnd = clearQueue;
+			clearQueue = type;
+			type = undefined;
+		}
+		if ( clearQueue && type !== false ) {
+			this.queue( type || "fx", [] );
+		}
+
+		return this.each(function() {
+			var dequeue = true,
+				index = type != null && type + "queueHooks",
+				timers = jQuery.timers,
+				data = jQuery._data( this );
+
+			if ( index ) {
+				if ( data[ index ] && data[ index ].stop ) {
+					stopQueue( data[ index ] );
+				}
+			} else {
+				for ( index in data ) {
+					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+						stopQueue( data[ index ] );
+					}
+				}
+			}
+
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+					timers[ index ].anim.stop( gotoEnd );
+					dequeue = false;
+					timers.splice( index, 1 );
+				}
+			}
+
+			// start the next in the queue if the last step wasn't forced
+			// timers currently will call their complete callbacks, which will dequeue
+			// but only if they were gotoEnd
+			if ( dequeue || !gotoEnd ) {
+				jQuery.dequeue( this, type );
+			}
+		});
+	},
+	finish: function( type ) {
+		if ( type !== false ) {
+			type = type || "fx";
+		}
+		return this.each(function() {
+			var index,
+				data = jQuery._data( this ),
+				queue = data[ type + "queue" ],
+				hooks = data[ type + "queueHooks" ],
+				timers = jQuery.timers,
+				length = queue ? queue.length : 0;
+
+			// enable finishing flag on private data
+			data.finish = true;
+
+			// empty the queue first
+			jQuery.queue( this, type, [] );
+
+			if ( hooks && hooks.stop ) {
+				hooks.stop.call( this, true );
+			}
+
+			// look for any active animations, and finish them
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+					timers[ index ].anim.stop( true );
+					timers.splice( index, 1 );
+				}
+			}
+
+			// look for any animations in the old queue and finish them
+			for ( index = 0; index < length; index++ ) {
+				if ( queue[ index ] && queue[ index ].finish ) {
+					queue[ index ].finish.call( this );
+				}
+			}
+
+			// turn off finishing flag
+			delete data.finish;
+		});
+	}
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+	var which,
+		attrs = { height: type },
+		i = 0;
+
+	// if we include width, step value is 1 to do all cssExpand values,
+	// if we don't include width, step value is 2 to skip over Left and Right
+	includeWidth = includeWidth? 1 : 0;
+	for( ; i < 4 ; i += 2 - includeWidth ) {
+		which = cssExpand[ i ];
+		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+	}
+
+	if ( includeWidth ) {
+		attrs.opacity = attrs.width = type;
+	}
+
+	return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+	slideDown: genFx("show"),
+	slideUp: genFx("hide"),
+	slideToggle: genFx("toggle"),
+	fadeIn: { opacity: "show" },
+	fadeOut: { opacity: "hide" },
+	fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return this.animate( props, speed, easing, callback );
+	};
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+		complete: fn || !fn && easing ||
+			jQuery.isFunction( speed ) && speed,
+		duration: speed,
+		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+	};
+
+	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+	// normalize opt.queue - true/undefined/null -> "fx"
+	if ( opt.queue == null || opt.queue === true ) {
+		opt.queue = "fx";
+	}
+
+	// Queueing
+	opt.old = opt.complete;
+
+	opt.complete = function() {
+		if ( jQuery.isFunction( opt.old ) ) {
+			opt.old.call( this );
+		}
+
+		if ( opt.queue ) {
+			jQuery.dequeue( this, opt.queue );
+		}
+	};
+
+	return opt;
+};
+
+jQuery.easing = {
+	linear: function( p ) {
+		return p;
+	},
+	swing: function( p ) {
+		return 0.5 - Math.cos( p*Math.PI ) / 2;
+	}
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+	var timer,
+		timers = jQuery.timers,
+		i = 0;
+
+	fxNow = jQuery.now();
+
+	for ( ; i < timers.length; i++ ) {
+		timer = timers[ i ];
+		// Checks the timer has not already been removed
+		if ( !timer() && timers[ i ] === timer ) {
+			timers.splice( i--, 1 );
+		}
+	}
+
+	if ( !timers.length ) {
+		jQuery.fx.stop();
+	}
+	fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+	if ( timer() && jQuery.timers.push( timer ) ) {
+		jQuery.fx.start();
+	}
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+	if ( !timerId ) {
+		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+	}
+};
+
+jQuery.fx.stop = function() {
+	clearInterval( timerId );
+	timerId = null;
+};
+
+jQuery.fx.speeds = {
+	slow: 600,
+	fast: 200,
+	// Default speed
+	_default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	jQuery.expr.filters.animated = function( elem ) {
+		return jQuery.grep(jQuery.timers, function( fn ) {
+			return elem === fn.elem;
+		}).length;
+	};
+}
+jQuery.fn.offset = function( options ) {
+	if ( arguments.length ) {
+		return options === undefined ?
+			this :
+			this.each(function( i ) {
+				jQuery.offset.setOffset( this, options, i );
+			});
+	}
+
+	var docElem, win,
+		box = { top: 0, left: 0 },
+		elem = this[ 0 ],
+		doc = elem && elem.ownerDocument;
+
+	if ( !doc ) {
+		return;
+	}
+
+	docElem = doc.documentElement;
+
+	// Make sure it's not a disconnected DOM node
+	if ( !jQuery.contains( docElem, elem ) ) {
+		return box;
+	}
+
+	// If we don't have gBCR, just use 0,0 rather than error
+	// BlackBerry 5, iOS 3 (original iPhone)
+	if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+		box = elem.getBoundingClientRect();
+	}
+	win = getWindow( doc );
+	return {
+		top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
+		left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+	};
+};
+
+jQuery.offset = {
+
+	setOffset: function( elem, options, i ) {
+		var position = jQuery.css( elem, "position" );
+
+		// set position first, in-case top/left are set even on static elem
+		if ( position === "static" ) {
+			elem.style.position = "relative";
+		}
+
+		var curElem = jQuery( elem ),
+			curOffset = curElem.offset(),
+			curCSSTop = jQuery.css( elem, "top" ),
+			curCSSLeft = jQuery.css( elem, "left" ),
+			calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+			props = {}, curPosition = {}, curTop, curLeft;
+
+		// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+		if ( calculatePosition ) {
+			curPosition = curElem.position();
+			curTop = curPosition.top;
+			curLeft = curPosition.left;
+		} else {
+			curTop = parseFloat( curCSSTop ) || 0;
+			curLeft = parseFloat( curCSSLeft ) || 0;
+		}
+
+		if ( jQuery.isFunction( options ) ) {
+			options = options.call( elem, i, curOffset );
+		}
+
+		if ( options.top != null ) {
+			props.top = ( options.top - curOffset.top ) + curTop;
+		}
+		if ( options.left != null ) {
+			props.left = ( options.left - curOffset.left ) + curLeft;
+		}
+
+		if ( "using" in options ) {
+			options.using.call( elem, props );
+		} else {
+			curElem.css( props );
+		}
+	}
+};
+
+
+jQuery.fn.extend({
+
+	position: function() {
+		if ( !this[ 0 ] ) {
+			return;
+		}
+
+		var offsetParent, offset,
+			parentOffset = { top: 0, left: 0 },
+			elem = this[ 0 ];
+
+		// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
+		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+			// we assume that getBoundingClientRect is available when computed position is fixed
+			offset = elem.getBoundingClientRect();
+		} else {
+			// Get *real* offsetParent
+			offsetParent = this.offsetParent();
+
+			// Get correct offsets
+			offset = this.offset();
+			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+				parentOffset = offsetParent.offset();
+			}
+
+			// Add offsetParent borders
+			parentOffset.top  += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+		}
+
+		// Subtract parent offsets and element margins
+		// note: when an element has margin: auto the offsetLeft and marginLeft
+		// are the same in Safari causing offset.left to incorrectly be 0
+		return {
+			top:  offset.top  - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+		};
+	},
+
+	offsetParent: function() {
+		return this.map(function() {
+			var offsetParent = this.offsetParent || docElem;
+			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
+				offsetParent = offsetParent.offsetParent;
+			}
+			return offsetParent || docElem;
+		});
+	}
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+	var top = /Y/.test( prop );
+
+	jQuery.fn[ method ] = function( val ) {
+		return jQuery.access( this, function( elem, method, val ) {
+			var win = getWindow( elem );
+
+			if ( val === undefined ) {
+				return win ? (prop in win) ? win[ prop ] :
+					win.document.documentElement[ method ] :
+					elem[ method ];
+			}
+
+			if ( win ) {
+				win.scrollTo(
+					!top ? val : jQuery( win ).scrollLeft(),
+					top ? val : jQuery( win ).scrollTop()
+				);
+
+			} else {
+				elem[ method ] = val;
+			}
+		}, method, val, arguments.length, null );
+	};
+});
+
+function getWindow( elem ) {
+	return jQuery.isWindow( elem ) ?
+		elem :
+		elem.nodeType === 9 ?
+			elem.defaultView || elem.parentWindow :
+			false;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+		// margin is only for outerHeight, outerWidth
+		jQuery.fn[ funcName ] = function( margin, value ) {
+			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+			return jQuery.access( this, function( elem, type, value ) {
+				var doc;
+
+				if ( jQuery.isWindow( elem ) ) {
+					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+					// isn't a whole lot we can do. See pull request at this URL for discussion:
+					// https://github.com/jquery/jquery/pull/764
+					return elem.document.documentElement[ "client" + name ];
+				}
+
+				// Get document width or height
+				if ( elem.nodeType === 9 ) {
+					doc = elem.documentElement;
+
+					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+					// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+					return Math.max(
+						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+						elem.body[ "offset" + name ], doc[ "offset" + name ],
+						doc[ "client" + name ]
+					);
+				}
+
+				return value === undefined ?
+					// Get width or height on the element, requesting but not forcing parseFloat
+					jQuery.css( elem, type, extra ) :
+
+					// Set width or height on the element
+					jQuery.style( elem, type, value, extra );
+			}, type, chainable ? margin : undefined, chainable, null );
+		};
+	});
+});
+// Limit scope pollution from any deprecated API
+// (function() {
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+	return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+// })();
+if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+	// Expose jQuery as module.exports in loaders that implement the Node
+	// module pattern (including browserify). Do not create the global, since
+	// the user will be storing it themselves locally, and globals are frowned
+	// upon in the Node module world.
+	module.exports = jQuery;
+} else {
+	// Otherwise expose jQuery to the global object as usual
+	window.jQuery = window.$ = jQuery;
+
+	// Register as a named AMD module, since jQuery can be concatenated with other
+	// files that may use define, but not via a proper concatenation script that
+	// understands anonymous AMD modules. A named AMD is safest and most robust
+	// way to register. Lowercase jquery is used because AMD module names are
+	// derived from file names, and jQuery is normally delivered in a lowercase
+	// file name. Do this after creating the global so that if an AMD module wants
+	// to call noConflict to hide this version of jQuery, it will work.
+	if ( typeof define === "function" && define.amd ) {
+		define( "jquery", [], function () { return jQuery; } );
+	}
+}
+
+})( window );
diff --git a/chrome/test/data/dromaeo/lib/jquery.2.0.3.js b/chrome/test/data/dromaeo/lib/jquery.2.0.3.js
new file mode 100644
index 0000000..ebc6c187
--- /dev/null
+++ b/chrome/test/data/dromaeo/lib/jquery.2.0.3.js
@@ -0,0 +1,8829 @@
+/*!
+ * jQuery JavaScript Library v2.0.3
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-07-03T13:30Z
+ */
+(function( window, undefined ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//"use strict";
+var
+	// A central reference to the root jQuery(document)
+	rootjQuery,
+
+	// The deferred used on DOM ready
+	readyList,
+
+	// Support: IE9
+	// For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
+	core_strundefined = typeof undefined,
+
+	// Use the correct document accordingly with window argument (sandbox)
+	location = window.location,
+	document = window.document,
+	docElem = document.documentElement,
+
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+
+	// Map over the $ in case of overwrite
+	_$ = window.$,
+
+	// [[Class]] -> type pairs
+	class2type = {},
+
+	// List of deleted data cache ids, so we can reuse them
+	core_deletedIds = [],
+
+	core_version = "2.0.3",
+
+	// Save a reference to some core methods
+	core_concat = core_deletedIds.concat,
+	core_push = core_deletedIds.push,
+	core_slice = core_deletedIds.slice,
+	core_indexOf = core_deletedIds.indexOf,
+	core_toString = class2type.toString,
+	core_hasOwn = class2type.hasOwnProperty,
+	core_trim = core_version.trim,
+
+	// Define a local copy of jQuery
+	jQuery = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		return new jQuery.fn.init( selector, context, rootjQuery );
+	},
+
+	// Used for matching numbers
+	core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+
+	// Used for splitting on whitespace
+	core_rnotwhite = /\S+/g,
+
+	// A simple way to check for HTML strings
+	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+	// Strict HTML recognition (#11290: must start with <)
+	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+	// Match a standalone tag
+	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+	// Matches dashed string for camelizing
+	rmsPrefix = /^-ms-/,
+	rdashAlpha = /-([\da-z])/gi,
+
+	// Used by jQuery.camelCase as callback to replace()
+	fcamelCase = function( all, letter ) {
+		return letter.toUpperCase();
+	},
+
+	// The ready event handler and self cleanup method
+	completed = function() {
+		document.removeEventListener( "DOMContentLoaded", completed, false );
+		window.removeEventListener( "load", completed, false );
+		jQuery.ready();
+	};
+
+jQuery.fn = jQuery.prototype = {
+	// The current version of jQuery being used
+	jquery: core_version,
+
+	constructor: jQuery,
+	init: function( selector, context, rootjQuery ) {
+		var match, elem;
+
+		// HANDLE: $(""), $(null), $(undefined), $(false)
+		if ( !selector ) {
+			return this;
+		}
+
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+				// Assume that strings that start and end with <> are HTML and skip the regex check
+				match = [ null, selector, null ];
+
+			} else {
+				match = rquickExpr.exec( selector );
+			}
+
+			// Match html or make sure no context is specified for #id
+			if ( match && (match[1] || !context) ) {
+
+				// HANDLE: $(html) -> $(array)
+				if ( match[1] ) {
+					context = context instanceof jQuery ? context[0] : context;
+
+					// scripts is true for back-compat
+					jQuery.merge( this, jQuery.parseHTML(
+						match[1],
+						context && context.nodeType ? context.ownerDocument || context : document,
+						true
+					) );
+
+					// HANDLE: $(html, props)
+					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+						for ( match in context ) {
+							// Properties of context are called as methods if possible
+							if ( jQuery.isFunction( this[ match ] ) ) {
+								this[ match ]( context[ match ] );
+
+							// ...and otherwise set as attributes
+							} else {
+								this.attr( match, context[ match ] );
+							}
+						}
+					}
+
+					return this;
+
+				// HANDLE: $(#id)
+				} else {
+					elem = document.getElementById( match[2] );
+
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Inject the element directly into the jQuery object
+						this.length = 1;
+						this[0] = elem;
+					}
+
+					this.context = document;
+					this.selector = selector;
+					return this;
+				}
+
+			// HANDLE: $(expr, $(...))
+			} else if ( !context || context.jquery ) {
+				return ( context || rootjQuery ).find( selector );
+
+			// HANDLE: $(expr, context)
+			// (which is just equivalent to: $(context).find(expr)
+			} else {
+				return this.constructor( context ).find( selector );
+			}
+
+		// HANDLE: $(DOMElement)
+		} else if ( selector.nodeType ) {
+			this.context = this[0] = selector;
+			this.length = 1;
+			return this;
+
+		// HANDLE: $(function)
+		// Shortcut for document ready
+		} else if ( jQuery.isFunction( selector ) ) {
+			return rootjQuery.ready( selector );
+		}
+
+		if ( selector.selector !== undefined ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return jQuery.makeArray( selector, this );
+	},
+
+	// Start with an empty selector
+	selector: "",
+
+	// The default length of a jQuery object is 0
+	length: 0,
+
+	toArray: function() {
+		return core_slice.call( this );
+	},
+
+	// Get the Nth element in the matched element set OR
+	// Get the whole matched element set as a clean array
+	get: function( num ) {
+		return num == null ?
+
+			// Return a 'clean' array
+			this.toArray() :
+
+			// Return just the object
+			( num < 0 ? this[ this.length + num ] : this[ num ] );
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems ) {
+
+		// Build a new jQuery matched element set
+		var ret = jQuery.merge( this.constructor(), elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+		ret.context = this.context;
+
+		// Return the newly-formed element set
+		return ret;
+	},
+
+	// Execute a callback for every element in the matched set.
+	// (You can seed the arguments with an array of args, but this is
+	// only used internally.)
+	each: function( callback, args ) {
+		return jQuery.each( this, callback, args );
+	},
+
+	ready: function( fn ) {
+		// Add the callback
+		jQuery.ready.promise().done( fn );
+
+		return this;
+	},
+
+	slice: function() {
+		return this.pushStack( core_slice.apply( this, arguments ) );
+	},
+
+	first: function() {
+		return this.eq( 0 );
+	},
+
+	last: function() {
+		return this.eq( -1 );
+	},
+
+	eq: function( i ) {
+		var len = this.length,
+			j = +i + ( i < 0 ? len : 0 );
+		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function( elem, i ) {
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	end: function() {
+		return this.prevObject || this.constructor(null);
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: core_push,
+	sort: [].sort,
+	splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+	var options, name, src, copy, copyIsArray, clone,
+		target = arguments[0] || {},
+		i = 1,
+		length = arguments.length,
+		deep = false;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+
+	// Handle case when target is a string or something (possible in deep copy)
+	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+		target = {};
+	}
+
+	// extend jQuery itself if only one argument is passed
+	if ( length === i ) {
+		target = this;
+		--i;
+	}
+
+	for ( ; i < length; i++ ) {
+		// Only deal with non-null/undefined values
+		if ( (options = arguments[ i ]) != null ) {
+			// Extend the base object
+			for ( name in options ) {
+				src = target[ name ];
+				copy = options[ name ];
+
+				// Prevent never-ending loop
+				if ( target === copy ) {
+					continue;
+				}
+
+				// Recurse if we're merging plain objects or arrays
+				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+					if ( copyIsArray ) {
+						copyIsArray = false;
+						clone = src && jQuery.isArray(src) ? src : [];
+
+					} else {
+						clone = src && jQuery.isPlainObject(src) ? src : {};
+					}
+
+					// Never move original objects, clone them
+					target[ name ] = jQuery.extend( deep, clone, copy );
+
+				// Don't bring in undefined values
+				} else if ( copy !== undefined ) {
+					target[ name ] = copy;
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};
+
+jQuery.extend({
+	// Unique for each copy of jQuery on the page
+	expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
+	noConflict: function( deep ) {
+		if ( window.$ === jQuery ) {
+			window.$ = _$;
+		}
+
+		if ( deep && window.jQuery === jQuery ) {
+			window.jQuery = _jQuery;
+		}
+
+		return jQuery;
+	},
+
+	// Is the DOM ready to be used? Set to true once it occurs.
+	isReady: false,
+
+	// A counter to track how many items to wait for before
+	// the ready event fires. See #6781
+	readyWait: 1,
+
+	// Hold (or release) the ready event
+	holdReady: function( hold ) {
+		if ( hold ) {
+			jQuery.readyWait++;
+		} else {
+			jQuery.ready( true );
+		}
+	},
+
+	// Handle when the DOM is ready
+	ready: function( wait ) {
+
+		// Abort if there are pending holds or we're already ready
+		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+			return;
+		}
+
+		// Remember that the DOM is ready
+		jQuery.isReady = true;
+
+		// If a normal DOM Ready event fired, decrement, and wait if need be
+		if ( wait !== true && --jQuery.readyWait > 0 ) {
+			return;
+		}
+
+		// If there are functions bound, to execute
+		readyList.resolveWith( document, [ jQuery ] );
+
+		// Trigger any bound ready events
+		if ( jQuery.fn.trigger ) {
+			jQuery( document ).trigger("ready").off("ready");
+		}
+	},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return jQuery.type(obj) === "function";
+	},
+
+	isArray: Array.isArray,
+
+	isWindow: function( obj ) {
+		return obj != null && obj === obj.window;
+	},
+
+	isNumeric: function( obj ) {
+		return !isNaN( parseFloat(obj) ) && isFinite( obj );
+	},
+
+	type: function( obj ) {
+		if ( obj == null ) {
+			return String( obj );
+		}
+		// Support: Safari <= 5.1 (functionish RegExp)
+		return typeof obj === "object" || typeof obj === "function" ?
+			class2type[ core_toString.call(obj) ] || "object" :
+			typeof obj;
+	},
+
+	isPlainObject: function( obj ) {
+		// Not plain objects:
+		// - Any object or value whose internal [[Class]] property is not "[object Object]"
+		// - DOM nodes
+		// - window
+		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+			return false;
+		}
+
+		// Support: Firefox <20
+		// The try/catch suppresses exceptions thrown when attempting to access
+		// the "constructor" property of certain host objects, ie. |window.location|
+		// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
+		try {
+			if ( obj.constructor &&
+					!core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+				return false;
+			}
+		} catch ( e ) {
+			return false;
+		}
+
+		// If the function hasn't returned already, we're confident that
+		// |obj| is a plain object, created by {} or constructed with new Object
+		return true;
+	},
+
+	isEmptyObject: function( obj ) {
+		var name;
+		for ( name in obj ) {
+			return false;
+		}
+		return true;
+	},
+
+	error: function( msg ) {
+		throw new Error( msg );
+	},
+
+	// data: string of html
+	// context (optional): If specified, the fragment will be created in this context, defaults to document
+	// keepScripts (optional): If true, will include scripts passed in the html string
+	parseHTML: function( data, context, keepScripts ) {
+		if ( !data || typeof data !== "string" ) {
+			return null;
+		}
+		if ( typeof context === "boolean" ) {
+			keepScripts = context;
+			context = false;
+		}
+		context = context || document;
+
+		var parsed = rsingleTag.exec( data ),
+			scripts = !keepScripts && [];
+
+		// Single tag
+		if ( parsed ) {
+			return [ context.createElement( parsed[1] ) ];
+		}
+
+		parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+		if ( scripts ) {
+			jQuery( scripts ).remove();
+		}
+
+		return jQuery.merge( [], parsed.childNodes );
+	},
+
+	parseJSON: JSON.parse,
+
+	// Cross-browser xml parsing
+	parseXML: function( data ) {
+		var xml, tmp;
+		if ( !data || typeof data !== "string" ) {
+			return null;
+		}
+
+		// Support: IE9
+		try {
+			tmp = new DOMParser();
+			xml = tmp.parseFromString( data , "text/xml" );
+		} catch ( e ) {
+			xml = undefined;
+		}
+
+		if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
+			jQuery.error( "Invalid XML: " + data );
+		}
+		return xml;
+	},
+
+	noop: function() {},
+
+	// Evaluates a script in a global context
+	globalEval: function( code ) {
+		var script,
+				indirect = eval;
+
+		code = jQuery.trim( code );
+
+		if ( code ) {
+			// If the code includes a valid, prologue position
+			// strict mode pragma, execute code by injecting a
+			// script tag into the document.
+			if ( code.indexOf("use strict") === 1 ) {
+				script = document.createElement("script");
+				script.text = code;
+				document.head.appendChild( script ).parentNode.removeChild( script );
+			} else {
+			// Otherwise, avoid the DOM node creation, insertion
+			// and removal by using an indirect global eval
+				indirect( code );
+			}
+		}
+	},
+
+	// Convert dashed to camelCase; used by the css and data modules
+	// Microsoft forgot to hump their vendor prefix (#9572)
+	camelCase: function( string ) {
+		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+	},
+
+	nodeName: function( elem, name ) {
+		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+	},
+
+	// args is for internal usage only
+	each: function( obj, callback, args ) {
+		var value,
+			i = 0,
+			length = obj.length,
+			isArray = isArraylike( obj );
+
+		if ( args ) {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+
+		// A special, fast, case for the most common use of each
+		} else {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+		}
+
+		return obj;
+	},
+
+	trim: function( text ) {
+		return text == null ? "" : core_trim.call( text );
+	},
+
+	// results is for internal usage only
+	makeArray: function( arr, results ) {
+		var ret = results || [];
+
+		if ( arr != null ) {
+			if ( isArraylike( Object(arr) ) ) {
+				jQuery.merge( ret,
+					typeof arr === "string" ?
+					[ arr ] : arr
+				);
+			} else {
+				core_push.call( ret, arr );
+			}
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, arr, i ) {
+		return arr == null ? -1 : core_indexOf.call( arr, elem, i );
+	},
+
+	merge: function( first, second ) {
+		var l = second.length,
+			i = first.length,
+			j = 0;
+
+		if ( typeof l === "number" ) {
+			for ( ; j < l; j++ ) {
+				first[ i++ ] = second[ j ];
+			}
+		} else {
+			while ( second[j] !== undefined ) {
+				first[ i++ ] = second[ j++ ];
+			}
+		}
+
+		first.length = i;
+
+		return first;
+	},
+
+	grep: function( elems, callback, inv ) {
+		var retVal,
+			ret = [],
+			i = 0,
+			length = elems.length;
+		inv = !!inv;
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( ; i < length; i++ ) {
+			retVal = !!callback( elems[ i ], i );
+			if ( inv !== retVal ) {
+				ret.push( elems[ i ] );
+			}
+		}
+
+		return ret;
+	},
+
+	// arg is for internal usage only
+	map: function( elems, callback, arg ) {
+		var value,
+			i = 0,
+			length = elems.length,
+			isArray = isArraylike( elems ),
+			ret = [];
+
+		// Go through the array, translating each of the items to their
+		if ( isArray ) {
+			for ( ; i < length; i++ ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+
+		// Go through every key on the object,
+		} else {
+			for ( i in elems ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+		}
+
+		// Flatten any nested arrays
+		return core_concat.apply( [], ret );
+	},
+
+	// A global GUID counter for objects
+	guid: 1,
+
+	// Bind a function to a context, optionally partially applying any
+	// arguments.
+	proxy: function( fn, context ) {
+		var tmp, args, proxy;
+
+		if ( typeof context === "string" ) {
+			tmp = fn[ context ];
+			context = fn;
+			fn = tmp;
+		}
+
+		// Quick check to determine if target is callable, in the spec
+		// this throws a TypeError, but we will just return undefined.
+		if ( !jQuery.isFunction( fn ) ) {
+			return undefined;
+		}
+
+		// Simulated bind
+		args = core_slice.call( arguments, 2 );
+		proxy = function() {
+			return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+		};
+
+		// Set the guid of unique handler to the same of original handler, so it can be removed
+		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+		return proxy;
+	},
+
+	// Multifunctional method to get and set values of a collection
+	// The value/s can optionally be executed if it's a function
+	access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+		var i = 0,
+			length = elems.length,
+			bulk = key == null;
+
+		// Sets many values
+		if ( jQuery.type( key ) === "object" ) {
+			chainable = true;
+			for ( i in key ) {
+				jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+			}
+
+		// Sets one value
+		} else if ( value !== undefined ) {
+			chainable = true;
+
+			if ( !jQuery.isFunction( value ) ) {
+				raw = true;
+			}
+
+			if ( bulk ) {
+				// Bulk operations run against the entire set
+				if ( raw ) {
+					fn.call( elems, value );
+					fn = null;
+
+				// ...except when executing function values
+				} else {
+					bulk = fn;
+					fn = function( elem, key, value ) {
+						return bulk.call( jQuery( elem ), value );
+					};
+				}
+			}
+
+			if ( fn ) {
+				for ( ; i < length; i++ ) {
+					fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+				}
+			}
+		}
+
+		return chainable ?
+			elems :
+
+			// Gets
+			bulk ?
+				fn.call( elems ) :
+				length ? fn( elems[0], key ) : emptyGet;
+	},
+
+	now: Date.now,
+
+	// A method for quickly swapping in/out CSS properties to get correct calculations.
+	// Note: this method belongs to the css module but it's needed here for the support module.
+	// If support gets modularized, this method should be moved back to the css module.
+	swap: function( elem, options, callback, args ) {
+		var ret, name,
+			old = {};
+
+		// Remember the old values, and insert the new ones
+		for ( name in options ) {
+			old[ name ] = elem.style[ name ];
+			elem.style[ name ] = options[ name ];
+		}
+
+		ret = callback.apply( elem, args || [] );
+
+		// Revert the old values
+		for ( name in options ) {
+			elem.style[ name ] = old[ name ];
+		}
+
+		return ret;
+	}
+});
+
+jQuery.ready.promise = function( obj ) {
+	if ( !readyList ) {
+
+		readyList = jQuery.Deferred();
+
+		// Catch cases where $(document).ready() is called after the browser event has already occurred.
+		// we once tried to use readyState "interactive" here, but it caused issues like the one
+		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+		if ( document.readyState === "complete" ) {
+			// Handle it asynchronously to allow scripts the opportunity to delay ready
+			setTimeout( jQuery.ready );
+
+		} else {
+
+			// Use the handy event callback
+			document.addEventListener( "DOMContentLoaded", completed, false );
+
+			// A fallback to window.onload, that will always work
+			window.addEventListener( "load", completed, false );
+		}
+	}
+	return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+	var length = obj.length,
+		type = jQuery.type( obj );
+
+	if ( jQuery.isWindow( obj ) ) {
+		return false;
+	}
+
+	if ( obj.nodeType === 1 && length ) {
+		return true;
+	}
+
+	return type === "array" || type !== "function" &&
+		( length === 0 ||
+		typeof length === "number" && length > 0 && ( length - 1 ) in obj );
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+/*!
+ * Sizzle CSS Selector Engine v1.9.4-pre
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-06-03
+ */
+(function( window, undefined ) {
+
+var i,
+	support,
+	cachedruns,
+	Expr,
+	getText,
+	isXML,
+	compile,
+	outermostContext,
+	sortInput,
+
+	// Local document vars
+	setDocument,
+	document,
+	docElem,
+	documentIsHTML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+
+	// Instance-specific data
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+	hasDuplicate = false,
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+		return 0;
+	},
+
+	// General-purpose constants
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	// Instance methods
+	hasOwn = ({}).hasOwnProperty,
+	arr = [],
+	pop = arr.pop,
+	push_native = arr.push,
+	push = arr.push,
+	slice = arr.slice,
+	// Use a stripped-down indexOf if we can't use a native one
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+	// Regular expressions
+
+	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+	whitespace = "[\\x20\\t\\r\\n\\f]",
+	// http://www.w3.org/TR/css3-syntax/#characters
+	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+	// Loosely modeled on CSS identifier characters
+	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+	identifier = characterEncoding.replace( "w", "w#" ),
+
+	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+		"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+	// Prefer arguments quoted,
+	//   then not containing pseudos/brackets,
+	//   then attribute selectors/non-parenthetical expressions,
+	//   then anything else
+	// These preferences are here to reduce the number of selectors
+	//   needing tokenize in the PSEUDO preFilter
+	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+	rsibling = new RegExp( whitespace + "*[+~]" ),
+	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
+
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+		"ATTR": new RegExp( "^" + attributes ),
+		"PSEUDO": new RegExp( "^" + pseudos ),
+		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+		// For use in libraries implementing .is()
+		// We use this for POS matching in `select`
+		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+	},
+
+	rnative = /^[^{]+\{\s*\[native \w/,
+
+	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rescape = /'|\\/g,
+
+	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+	funescape = function( _, escaped, escapedWhitespace ) {
+		var high = "0x" + escaped - 0x10000;
+		// NaN means non-codepoint
+		// Support: Firefox
+		// Workaround erroneous numeric interpretation of +"0x"
+		return high !== high || escapedWhitespace ?
+			escaped :
+			// BMP codepoint
+			high < 0 ?
+				String.fromCharCode( high + 0x10000 ) :
+				// Supplemental Plane codepoint (surrogate pair)
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+// Optimize for push.apply( _, NodeList )
+try {
+	push.apply(
+		(arr = slice.call( preferredDoc.childNodes )),
+		preferredDoc.childNodes
+	);
+	// Support: Android<4.0
+	// Detect silently failing push.apply
+	arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+	push = { apply: arr.length ?
+
+		// Leverage slice if possible
+		function( target, els ) {
+			push_native.apply( target, slice.call(els) );
+		} :
+
+		// Support: IE<9
+		// Otherwise append directly
+		function( target, els ) {
+			var j = target.length,
+				i = 0;
+			// Can't trust NodeList.length
+			while ( (target[j++] = els[i++]) ) {}
+			target.length = j - 1;
+		}
+	};
+}
+
+function Sizzle( selector, context, results, seed ) {
+	var match, elem, m, nodeType,
+		// QSA vars
+		i, groups, old, nid, newContext, newSelector;
+
+	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+		setDocument( context );
+	}
+
+	context = context || document;
+	results = results || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( documentIsHTML && !seed ) {
+
+		// Shortcuts
+		if ( (match = rquickExpr.exec( selector )) ) {
+			// Speed-up: Sizzle("#ID")
+			if ( (m = match[1]) ) {
+				if ( nodeType === 9 ) {
+					elem = context.getElementById( m );
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE, Opera, and Webkit return items
+						// by name instead of ID
+						if ( elem.id === m ) {
+							results.push( elem );
+							return results;
+						}
+					} else {
+						return results;
+					}
+				} else {
+					// Context is not a document
+					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+						contains( context, elem ) && elem.id === m ) {
+						results.push( elem );
+						return results;
+					}
+				}
+
+			// Speed-up: Sizzle("TAG")
+			} else if ( match[2] ) {
+				push.apply( results, context.getElementsByTagName( selector ) );
+				return results;
+
+			// Speed-up: Sizzle(".CLASS")
+			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+				push.apply( results, context.getElementsByClassName( m ) );
+				return results;
+			}
+		}
+
+		// QSA path
+		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			nid = old = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && selector;
+
+			// qSA works strangely on Element-rooted queries
+			// We can work around this by specifying an extra ID on the root
+			// and working up from there (Thanks to Andrew Dupont for the technique)
+			// IE 8 doesn't work on object elements
+			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+				groups = tokenize( selector );
+
+				if ( (old = context.getAttribute("id")) ) {
+					nid = old.replace( rescape, "\\$&" );
+				} else {
+					context.setAttribute( "id", nid );
+				}
+				nid = "[id='" + nid + "'] ";
+
+				i = groups.length;
+				while ( i-- ) {
+					groups[i] = nid + toSelector( groups[i] );
+				}
+				newContext = rsibling.test( selector ) && context.parentNode || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results,
+						newContext.querySelectorAll( newSelector )
+					);
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	// All others
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *	deleting the oldest entry
+ */
+function createCache() {
+	var keys = [];
+
+	function cache( key, value ) {
+		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+		if ( keys.push( key += " " ) > Expr.cacheLength ) {
+			// Only keep the most recent entries
+			delete cache[ keys.shift() ];
+		}
+		return (cache[ key ] = value);
+	}
+	return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+	fn[ expando ] = true;
+	return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+	var div = document.createElement("div");
+
+	try {
+		return !!fn( div );
+	} catch (e) {
+		return false;
+	} finally {
+		// Remove from its parent by default
+		if ( div.parentNode ) {
+			div.parentNode.removeChild( div );
+		}
+		// release memory in IE
+		div = null;
+	}
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+	var arr = attrs.split("|"),
+		i = attrs.length;
+
+	while ( i-- ) {
+		Expr.attrHandle[ arr[i] ] = handler;
+	}
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+			( ~b.sourceIndex || MAX_NEGATIVE ) -
+			( ~a.sourceIndex || MAX_NEGATIVE );
+
+	// Use IE sourceIndex if available on both nodes
+	if ( diff ) {
+		return diff;
+	}
+
+	// Check if b follows a
+	if ( cur ) {
+		while ( (cur = cur.nextSibling) ) {
+			if ( cur === b ) {
+				return -1;
+			}
+		}
+	}
+
+	return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return name === "input" && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return (name === "input" || name === "button") && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+	return markFunction(function( argument ) {
+		argument = +argument;
+		return markFunction(function( seed, matches ) {
+			var j,
+				matchIndexes = fn( [], seed.length, argument ),
+				i = matchIndexes.length;
+
+			// Match elements found at the specified indexes
+			while ( i-- ) {
+				if ( seed[ (j = matchIndexes[i]) ] ) {
+					seed[j] = !(matches[j] = seed[j]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * Detect xml
+ * @param {Element|Object} elem An element or a document
+ */
+isXML = Sizzle.isXML = function( elem ) {
+	// documentElement is verified for cases where it doesn't yet exist
+	// (such as loading iframes in IE - #4833)
+	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+	var doc = node ? node.ownerDocument || node : preferredDoc,
+		parent = doc.defaultView;
+
+	// If no document and documentElement is available, return
+	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+		return document;
+	}
+
+	// Set our document
+	document = doc;
+	docElem = doc.documentElement;
+
+	// Support tests
+	documentIsHTML = !isXML( doc );
+
+	// Support: IE>8
+	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
+	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+	// IE6-8 do not support the defaultView property so parent will be undefined
+	if ( parent && parent.attachEvent && parent !== parent.top ) {
+		parent.attachEvent( "onbeforeunload", function() {
+			setDocument();
+		});
+	}
+
+	/* Attributes
+	---------------------------------------------------------------------- */
+
+	// Support: IE<8
+	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+	support.attributes = assert(function( div ) {
+		div.className = "i";
+		return !div.getAttribute("className");
+	});
+
+	/* getElement(s)By*
+	---------------------------------------------------------------------- */
+
+	// Check if getElementsByTagName("*") returns only elements
+	support.getElementsByTagName = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	// Check if getElementsByClassName can be trusted
+	support.getElementsByClassName = assert(function( div ) {
+		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+		// Support: Safari<4
+		// Catch class over-caching
+		div.firstChild.className = "i";
+		// Support: Opera<10
+		// Catch gEBCN failure to find non-leading classes
+		return div.getElementsByClassName("i").length === 2;
+	});
+
+	// Support: IE<10
+	// Check if getElementById returns elements by name
+	// The broken getElementById methods don't pick up programatically-set names,
+	// so use a roundabout getElementsByName test
+	support.getById = assert(function( div ) {
+		docElem.appendChild( div ).id = expando;
+		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+	});
+
+	// ID find and filter
+	if ( support.getById ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+				var m = context.getElementById( id );
+				// Check parentNode to catch when Blackberry 4.6 returns
+				// nodes that are no longer in the document #6963
+				return m && m.parentNode ? [m] : [];
+			}
+		};
+		Expr.filter["ID"] = function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				return elem.getAttribute("id") === attrId;
+			};
+		};
+	} else {
+		// Support: IE6/7
+		// getElementById is not reliable as a find shortcut
+		delete Expr.find["ID"];
+
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	// Tag
+	Expr.find["TAG"] = support.getElementsByTagName ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				results = context.getElementsByTagName( tag );
+
+			// Filter out possible comments
+			if ( tag === "*" ) {
+				while ( (elem = results[i++]) ) {
+					if ( elem.nodeType === 1 ) {
+						tmp.push( elem );
+					}
+				}
+
+				return tmp;
+			}
+			return results;
+		};
+
+	// Class
+	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	/* QSA/matchesSelector
+	---------------------------------------------------------------------- */
+
+	// QSA and matchesSelector support
+
+	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+	rbuggyMatches = [];
+
+	// qSa(:focus) reports false when true (Chrome 21)
+	// We allow this because of a bug in IE8/9 that throws an error
+	// whenever `document.activeElement` is accessed on an iframe
+	// So, we allow :focus to pass through QSA all the time to avoid the IE error
+	// See http://bugs.jquery.com/ticket/13378
+	rbuggyQSA = [];
+
+	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+		// Build QSA regex
+		// Regex strategy adopted from Diego Perini
+		assert(function( div ) {
+			// Select is set to empty string on purpose
+			// This is to test IE's treatment of not explicitly
+			// setting a boolean content attribute,
+			// since its presence should be enough
+			// http://bugs.jquery.com/ticket/12359
+			div.innerHTML = "<select><option selected=''></option></select>";
+
+			// Support: IE8
+			// Boolean attributes and "value" are not treated correctly
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+			}
+
+			// Webkit/Opera - :checked should return selected option elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":checked").length ) {
+				rbuggyQSA.push(":checked");
+			}
+		});
+
+		assert(function( div ) {
+
+			// Support: Opera 10-12/IE8
+			// ^= $= *= and empty values
+			// Should not select anything
+			// Support: Windows 8 Native Apps
+			// The type attribute is restricted during .innerHTML assignment
+			var input = doc.createElement("input");
+			input.setAttribute( "type", "hidden" );
+			div.appendChild( input ).setAttribute( "t", "" );
+
+			if ( div.querySelectorAll("[t^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+			}
+
+			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":enabled").length ) {
+				rbuggyQSA.push( ":enabled", ":disabled" );
+			}
+
+			// Opera 10-11 does not throw on post-comma invalid pseudos
+			div.querySelectorAll("*,:x");
+			rbuggyQSA.push(",.*:");
+		});
+	}
+
+	if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.oMatchesSelector ||
+		docElem.msMatchesSelector) )) ) {
+
+		assert(function( div ) {
+			// Check to see if it's possible to do matchesSelector
+			// on a disconnected node (IE 9)
+			support.disconnectedMatch = matches.call( div, "div" );
+
+			// This should fail with an exception
+			// Gecko does not error, returns false instead
+			matches.call( div, "[s!='']:x" );
+			rbuggyMatches.push( "!=", pseudos );
+		});
+	}
+
+	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+	/* Contains
+	---------------------------------------------------------------------- */
+
+	// Element contains another
+	// Purposefully does not implement inclusive descendent
+	// As in, an element does not contain itself
+	contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
+		function( a, b ) {
+			var adown = a.nodeType === 9 ? a.documentElement : a,
+				bup = b && b.parentNode;
+			return a === bup || !!( bup && bup.nodeType === 1 && (
+				adown.contains ?
+					adown.contains( bup ) :
+					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+			));
+		} :
+		function( a, b ) {
+			if ( b ) {
+				while ( (b = b.parentNode) ) {
+					if ( b === a ) {
+						return true;
+					}
+				}
+			}
+			return false;
+		};
+
+	/* Sorting
+	---------------------------------------------------------------------- */
+
+	// Document order sorting
+	sortOrder = docElem.compareDocumentPosition ?
+	function( a, b ) {
+
+		// Flag for duplicate removal
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
+
+		if ( compare ) {
+			// Disconnected nodes
+			if ( compare & 1 ||
+				(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+				// Choose the first element that is related to our preferred document
+				if ( a === doc || contains(preferredDoc, a) ) {
+					return -1;
+				}
+				if ( b === doc || contains(preferredDoc, b) ) {
+					return 1;
+				}
+
+				// Maintain original order
+				return sortInput ?
+					( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+					0;
+			}
+
+			return compare & 4 ? -1 : 1;
+		}
+
+		// Not directly comparable, sort on existence of method
+		return a.compareDocumentPosition ? -1 : 1;
+	} :
+	function( a, b ) {
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		// Exit early if the nodes are identical
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+
+		// Parentless nodes are either documents or disconnected
+		} else if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+
+		// If the nodes are siblings, we can do a quick check
+		} else if ( aup === bup ) {
+			return siblingCheck( a, b );
+		}
+
+		// Otherwise we need full lists of their ancestors for comparison
+		cur = a;
+		while ( (cur = cur.parentNode) ) {
+			ap.unshift( cur );
+		}
+		cur = b;
+		while ( (cur = cur.parentNode) ) {
+			bp.unshift( cur );
+		}
+
+		// Walk down the tree looking for a discrepancy
+		while ( ap[i] === bp[i] ) {
+			i++;
+		}
+
+		return i ?
+			// Do a sibling check if the nodes have a common ancestor
+			siblingCheck( ap[i], bp[i] ) :
+
+			// Otherwise nodes in our document sort first
+			ap[i] === preferredDoc ? -1 :
+			bp[i] === preferredDoc ? 1 :
+			0;
+	};
+
+	return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	// Make sure that attribute selectors are quoted
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	if ( support.matchesSelector && documentIsHTML &&
+		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+		try {
+			var ret = matches.call( elem, expr );
+
+			// IE 9's matchesSelector returns false on disconnected nodes
+			if ( ret || support.disconnectedMatch ||
+					// As well, disconnected nodes are said to be in a document
+					// fragment in IE 9
+					elem.document && elem.document.nodeType !== 11 ) {
+				return ret;
+			}
+		} catch(e) {}
+	}
+
+	return Sizzle( expr, document, null, [elem] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+	// Set document vars if needed
+	if ( ( context.ownerDocument || context ) !== document ) {
+		setDocument( context );
+	}
+	return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	var fn = Expr.attrHandle[ name.toLowerCase() ],
+		// Don't get fooled by Object.prototype properties (jQuery #13807)
+		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+			fn( elem, name, !documentIsHTML ) :
+			undefined;
+
+	return val === undefined ?
+		support.attributes || !documentIsHTML ?
+			elem.getAttribute( name ) :
+			(val = elem.getAttributeNode(name)) && val.specified ?
+				val.value :
+				null :
+		val;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		j = 0,
+		i = 0;
+
+	// Unless we *know* we can detect duplicates, assume their presence
+	hasDuplicate = !support.detectDuplicates;
+	sortInput = !support.sortStable && results.slice( 0 );
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		while ( (elem = results[i++]) ) {
+			if ( elem === results[ i ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+	var node,
+		ret = "",
+		i = 0,
+		nodeType = elem.nodeType;
+
+	if ( !nodeType ) {
+		// If no nodeType, this is expected to be an array
+		for ( ; (node = elem[i]); i++ ) {
+			// Do not traverse comment nodes
+			ret += getText( node );
+		}
+	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+		// Use textContent for elements
+		// innerText usage removed for consistency of new lines (see #11153)
+		if ( typeof elem.textContent === "string" ) {
+			return elem.textContent;
+		} else {
+			// Traverse its children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				ret += getText( elem );
+			}
+		}
+	} else if ( nodeType === 3 || nodeType === 4 ) {
+		return elem.nodeValue;
+	}
+	// Do not include comment or processing instruction nodes
+
+	return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+	// Can be adjusted by the user
+	cacheLength: 50,
+
+	createPseudo: markFunction,
+
+	match: matchExpr,
+
+	attrHandle: {},
+
+	find: {},
+
+	relative: {
+		">": { dir: "parentNode", first: true },
+		" ": { dir: "parentNode" },
+		"+": { dir: "previousSibling", first: true },
+		"~": { dir: "previousSibling" }
+	},
+
+	preFilter: {
+		"ATTR": function( match ) {
+			match[1] = match[1].replace( runescape, funescape );
+
+			// Move the given value to match[3] whether quoted or unquoted
+			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+			if ( match[2] === "~=" ) {
+				match[3] = " " + match[3] + " ";
+			}
+
+			return match.slice( 0, 4 );
+		},
+
+		"CHILD": function( match ) {
+			/* matches from matchExpr["CHILD"]
+				1 type (only|nth|...)
+				2 what (child|of-type)
+				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+				4 xn-component of xn+y argument ([+-]?\d*n|)
+				5 sign of xn-component
+				6 x of xn-component
+				7 sign of y-component
+				8 y of y-component
+			*/
+			match[1] = match[1].toLowerCase();
+
+			if ( match[1].slice( 0, 3 ) === "nth" ) {
+				// nth-* requires argument
+				if ( !match[3] ) {
+					Sizzle.error( match[0] );
+				}
+
+				// numeric x and y parameters for Expr.filter.CHILD
+				// remember that false/true cast respectively to 0/1
+				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+			// other types prohibit arguments
+			} else if ( match[3] ) {
+				Sizzle.error( match[0] );
+			}
+
+			return match;
+		},
+
+		"PSEUDO": function( match ) {
+			var excess,
+				unquoted = !match[5] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			// Accept quoted arguments as-is
+			if ( match[3] && match[4] !== undefined ) {
+				match[2] = match[4];
+
+			// Strip excess characters from unquoted arguments
+			} else if ( unquoted && rpseudo.test( unquoted ) &&
+				// Get excess from tokenize (recursively)
+				(excess = tokenize( unquoted, true )) &&
+				// advance to the next closing parenthesis
+				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+				// excess is a negative index
+				match[0] = match[0].slice( 0, excess );
+				match[2] = unquoted.slice( 0, excess );
+			}
+
+			// Return only captures needed by the pseudo filter method (type and argument)
+			return match.slice( 0, 3 );
+		}
+	},
+
+	filter: {
+
+		"TAG": function( nodeNameSelector ) {
+			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+			return nodeNameSelector === "*" ?
+				function() { return true; } :
+				function( elem ) {
+					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+				};
+		},
+
+		"CLASS": function( className ) {
+			var pattern = classCache[ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+				});
+		},
+
+		"ATTR": function( name, operator, check ) {
+			return function( elem ) {
+				var result = Sizzle.attr( elem, name );
+
+				if ( result == null ) {
+					return operator === "!=";
+				}
+				if ( !operator ) {
+					return true;
+				}
+
+				result += "";
+
+				return operator === "=" ? result === check :
+					operator === "!=" ? result !== check :
+					operator === "^=" ? check && result.indexOf( check ) === 0 :
+					operator === "*=" ? check && result.indexOf( check ) > -1 :
+					operator === "$=" ? check && result.slice( -check.length ) === check :
+					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+					false;
+			};
+		},
+
+		"CHILD": function( type, what, argument, first, last ) {
+			var simple = type.slice( 0, 3 ) !== "nth",
+				forward = type.slice( -4 ) !== "last",
+				ofType = what === "of-type";
+
+			return first === 1 && last === 0 ?
+
+				// Shortcut for :nth-*(n)
+				function( elem ) {
+					return !!elem.parentNode;
+				} :
+
+				function( elem, context, xml ) {
+					var cache, outerCache, node, diff, nodeIndex, start,
+						dir = simple !== forward ? "nextSibling" : "previousSibling",
+						parent = elem.parentNode,
+						name = ofType && elem.nodeName.toLowerCase(),
+						useCache = !xml && !ofType;
+
+					if ( parent ) {
+
+						// :(first|last|only)-(child|of-type)
+						if ( simple ) {
+							while ( dir ) {
+								node = elem;
+								while ( (node = node[ dir ]) ) {
+									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+										return false;
+									}
+								}
+								// Reverse direction for :only-* (if we haven't yet done so)
+								start = dir = type === "only" && !start && "nextSibling";
+							}
+							return true;
+						}
+
+						start = [ forward ? parent.firstChild : parent.lastChild ];
+
+						// non-xml :nth-child(...) stores cache data on `parent`
+						if ( forward && useCache ) {
+							// Seek `elem` from a previously-cached index
+							outerCache = parent[ expando ] || (parent[ expando ] = {});
+							cache = outerCache[ type ] || [];
+							nodeIndex = cache[0] === dirruns && cache[1];
+							diff = cache[0] === dirruns && cache[2];
+							node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+								// Fallback to seeking `elem` from the start
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								// When found, cache indexes on `parent` and break
+								if ( node.nodeType === 1 && ++diff && node === elem ) {
+									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+									break;
+								}
+							}
+
+						// Use previously-cached element index if available
+						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+							diff = cache[1];
+
+						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+						} else {
+							// Use the same loop as above to seek `elem` from the start
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+									// Cache the index of each encountered element
+									if ( useCache ) {
+										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+									}
+
+									if ( node === elem ) {
+										break;
+									}
+								}
+							}
+						}
+
+						// Incorporate the offset, then check against cycle size
+						diff -= last;
+						return diff === first || ( diff % first === 0 && diff / first >= 0 );
+					}
+				};
+		},
+
+		"PSEUDO": function( pseudo, argument ) {
+			// pseudo-class names are case-insensitive
+			// http://www.w3.org/TR/selectors/#pseudo-classes
+			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+			// Remember that setFilters inherits from pseudos
+			var args,
+				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+					Sizzle.error( "unsupported pseudo: " + pseudo );
+
+			// The user may use createPseudo to indicate that
+			// arguments are needed to create the filter function
+			// just as Sizzle does
+			if ( fn[ expando ] ) {
+				return fn( argument );
+			}
+
+			// But maintain support for old signatures
+			if ( fn.length > 1 ) {
+				args = [ pseudo, pseudo, "", argument ];
+				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+					markFunction(function( seed, matches ) {
+						var idx,
+							matched = fn( seed, argument ),
+							i = matched.length;
+						while ( i-- ) {
+							idx = indexOf.call( seed, matched[i] );
+							seed[ idx ] = !( matches[ idx ] = matched[i] );
+						}
+					}) :
+					function( elem ) {
+						return fn( elem, 0, args );
+					};
+			}
+
+			return fn;
+		}
+	},
+
+	pseudos: {
+		// Potentially complex pseudos
+		"not": markFunction(function( selector ) {
+			// Trim the selector passed to compile
+			// to avoid treating leading and trailing
+			// spaces as combinators
+			var input = [],
+				results = [],
+				matcher = compile( selector.replace( rtrim, "$1" ) );
+
+			return matcher[ expando ] ?
+				markFunction(function( seed, matches, context, xml ) {
+					var elem,
+						unmatched = matcher( seed, null, xml, [] ),
+						i = seed.length;
+
+					// Match elements unmatched by `matcher`
+					while ( i-- ) {
+						if ( (elem = unmatched[i]) ) {
+							seed[i] = !(matches[i] = elem);
+						}
+					}
+				}) :
+				function( elem, context, xml ) {
+					input[0] = elem;
+					matcher( input, null, xml, results );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			return function( elem ) {
+				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+			};
+		}),
+
+		// "Whether an element is represented by a :lang() selector
+		// is based solely on the element's language value
+		// being equal to the identifier C,
+		// or beginning with the identifier C immediately followed by "-".
+		// The matching of C against the element's language value is performed case-insensitively.
+		// The identifier C does not have to be a valid language name."
+		// http://www.w3.org/TR/selectors/#lang-pseudo
+		"lang": markFunction( function( lang ) {
+			// lang value must be a valid identifier
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsHTML ?
+						elem.lang :
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+						elemLang = elemLang.toLowerCase();
+						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+					}
+				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+				return false;
+			};
+		}),
+
+		// Miscellaneous
+		"target": function( elem ) {
+			var hash = window.location && window.location.hash;
+			return hash && hash.slice( 1 ) === elem.id;
+		},
+
+		"root": function( elem ) {
+			return elem === docElem;
+		},
+
+		"focus": function( elem ) {
+			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+		},
+
+		// Boolean properties
+		"enabled": function( elem ) {
+			return elem.disabled === false;
+		},
+
+		"disabled": function( elem ) {
+			return elem.disabled === true;
+		},
+
+		"checked": function( elem ) {
+			// In CSS3, :checked should return both checked and selected elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			var nodeName = elem.nodeName.toLowerCase();
+			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+		},
+
+		"selected": function( elem ) {
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		// Contents
+		"empty": function( elem ) {
+			// http://www.w3.org/TR/selectors/#empty-pseudo
+			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+			//   not comment, processing instructions, or others
+			// Thanks to Diego Perini for the nodeName shortcut
+			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+					return false;
+				}
+			}
+			return true;
+		},
+
+		"parent": function( elem ) {
+			return !Expr.pseudos["empty"]( elem );
+		},
+
+		// Element/input types
+		"header": function( elem ) {
+			return rheader.test( elem.nodeName );
+		},
+
+		"input": function( elem ) {
+			return rinputs.test( elem.nodeName );
+		},
+
+		"button": function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && elem.type === "button" || name === "button";
+		},
+
+		"text": function( elem ) {
+			var attr;
+			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+			// use getAttribute instead to test this case
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+		},
+
+		// Position-in-collection
+		"first": createPositionalPseudo(function() {
+			return [ 0 ];
+		}),
+
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
+			return [ length - 1 ];
+		}),
+
+		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			return [ argument < 0 ? argument + length : argument ];
+		}),
+
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 0;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 1;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; --i >= 0; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; ++i < length; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		})
+	}
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+	Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+	Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+function tokenize( selector, parseOnly ) {
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ selector + " " ];
+
+	if ( cached ) {
+		return parseOnly ? 0 : cached.slice( 0 );
+	}
+
+	soFar = selector;
+	groups = [];
+	preFilters = Expr.preFilter;
+
+	while ( soFar ) {
+
+		// Comma and first run
+		if ( !matched || (match = rcomma.exec( soFar )) ) {
+			if ( match ) {
+				// Don't consume trailing commas as valid
+				soFar = soFar.slice( match[0].length ) || soFar;
+			}
+			groups.push( tokens = [] );
+		}
+
+		matched = false;
+
+		// Combinators
+		if ( (match = rcombinators.exec( soFar )) ) {
+			matched = match.shift();
+			tokens.push({
+				value: matched,
+				// Cast descendant combinators to space
+				type: match[0].replace( rtrim, " " )
+			});
+			soFar = soFar.slice( matched.length );
+		}
+
+		// Filters
+		for ( type in Expr.filter ) {
+			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+				(match = preFilters[ type ]( match ))) ) {
+				matched = match.shift();
+				tokens.push({
+					value: matched,
+					type: type,
+					matches: match
+				});
+				soFar = soFar.slice( matched.length );
+			}
+		}
+
+		if ( !matched ) {
+			break;
+		}
+	}
+
+	// Return the length of the invalid excess
+	// if we're just parsing
+	// Otherwise, throw an error or return tokens
+	return parseOnly ?
+		soFar.length :
+		soFar ?
+			Sizzle.error( selector ) :
+			// Cache the tokens
+			tokenCache( selector, groups ).slice( 0 );
+}
+
+function toSelector( tokens ) {
+	var i = 0,
+		len = tokens.length,
+		selector = "";
+	for ( ; i < len; i++ ) {
+		selector += tokens[i].value;
+	}
+	return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+	var dir = combinator.dir,
+		checkNonElements = base && dir === "parentNode",
+		doneName = done++;
+
+	return combinator.first ?
+		// Check against closest ancestor/preceding element
+		function( elem, context, xml ) {
+			while ( (elem = elem[ dir ]) ) {
+				if ( elem.nodeType === 1 || checkNonElements ) {
+					return matcher( elem, context, xml );
+				}
+			}
+		} :
+
+		// Check against all ancestor/preceding elements
+		function( elem, context, xml ) {
+			var data, cache, outerCache,
+				dirkey = dirruns + " " + doneName;
+
+			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+			if ( xml ) {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						if ( matcher( elem, context, xml ) ) {
+							return true;
+						}
+					}
+				}
+			} else {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						outerCache = elem[ expando ] || (elem[ expando ] = {});
+						if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+							if ( (data = cache[1]) === true || data === cachedruns ) {
+								return data === true;
+							}
+						} else {
+							cache = outerCache[ dir ] = [ dirkey ];
+							cache[1] = matcher( elem, context, xml ) || cachedruns;
+							if ( cache[1] === true ) {
+								return true;
+							}
+						}
+					}
+				}
+			}
+		};
+}
+
+function elementMatcher( matchers ) {
+	return matchers.length > 1 ?
+		function( elem, context, xml ) {
+			var i = matchers.length;
+			while ( i-- ) {
+				if ( !matchers[i]( elem, context, xml ) ) {
+					return false;
+				}
+			}
+			return true;
+		} :
+		matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+	var elem,
+		newUnmatched = [],
+		i = 0,
+		len = unmatched.length,
+		mapped = map != null;
+
+	for ( ; i < len; i++ ) {
+		if ( (elem = unmatched[i]) ) {
+			if ( !filter || filter( elem, context, xml ) ) {
+				newUnmatched.push( elem );
+				if ( mapped ) {
+					map.push( i );
+				}
+			}
+		}
+	}
+
+	return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+	if ( postFilter && !postFilter[ expando ] ) {
+		postFilter = setMatcher( postFilter );
+	}
+	if ( postFinder && !postFinder[ expando ] ) {
+		postFinder = setMatcher( postFinder, postSelector );
+	}
+	return markFunction(function( seed, results, context, xml ) {
+		var temp, i, elem,
+			preMap = [],
+			postMap = [],
+			preexisting = results.length,
+
+			// Get initial elements from seed or context
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+			// Prefilter to get matcher input, preserving a map for seed-results synchronization
+			matcherIn = preFilter && ( seed || !selector ) ?
+				condense( elems, preMap, preFilter, context, xml ) :
+				elems,
+
+			matcherOut = matcher ?
+				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+					// ...intermediate processing is necessary
+					[] :
+
+					// ...otherwise use results directly
+					results :
+				matcherIn;
+
+		// Find primary matches
+		if ( matcher ) {
+			matcher( matcherIn, matcherOut, context, xml );
+		}
+
+		// Apply postFilter
+		if ( postFilter ) {
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
+
+			// Un-match failing elements by moving them back to matcherIn
+			i = temp.length;
+			while ( i-- ) {
+				if ( (elem = temp[i]) ) {
+					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+				}
+			}
+		}
+
+		if ( seed ) {
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							// Restore matcherIn since elem is not yet a final match
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				// Move matched elements from seed to results to keep them synchronized
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		// Add elements to results, through postFinder if defined
+		} else {
+			matcherOut = condense(
+				matcherOut === results ?
+					matcherOut.splice( preexisting, matcherOut.length ) :
+					matcherOut
+			);
+			if ( postFinder ) {
+				postFinder( null, results, matcherOut, xml );
+			} else {
+				push.apply( results, matcherOut );
+			}
+		}
+	});
+}
+
+function matcherFromTokens( tokens ) {
+	var checkContext, matcher, j,
+		len = tokens.length,
+		leadingRelative = Expr.relative[ tokens[0].type ],
+		implicitRelative = leadingRelative || Expr.relative[" "],
+		i = leadingRelative ? 1 : 0,
+
+		// The foundational matcher ensures that elements are reachable from top-level context(s)
+		matchContext = addCombinator( function( elem ) {
+			return elem === checkContext;
+		}, implicitRelative, true ),
+		matchAnyContext = addCombinator( function( elem ) {
+			return indexOf.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	for ( ; i < len; i++ ) {
+		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+		} else {
+			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+			// Return special upon seeing a positional matcher
+			if ( matcher[ expando ] ) {
+				// Find the next relative operator (if any) for proper handling
+				j = ++i;
+				for ( ; j < len; j++ ) {
+					if ( Expr.relative[ tokens[j].type ] ) {
+						break;
+					}
+				}
+				return setMatcher(
+					i > 1 && elementMatcher( matchers ),
+					i > 1 && toSelector(
+						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
+						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+					).replace( rtrim, "$1" ),
+					matcher,
+					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+					j < len && toSelector( tokens )
+				);
+			}
+			matchers.push( matcher );
+		}
+	}
+
+	return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+	// A counter to specify which element is currently being matched
+	var matcherCachedRuns = 0,
+		bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, expandContext ) {
+			var elem, j, matcher,
+				setMatched = [],
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				outermost = expandContext != null,
+				contextBackup = outermostContext,
+				// We must always have either seed elements or context
+				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+				// Use integer dirruns iff this is the outermost matcher
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+				cachedruns = matcherCachedRuns;
+			}
+
+			// Add elements passing elementMatchers directly to results
+			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+			for ( ; (elem = elems[i]) != null; i++ ) {
+				if ( byElement && elem ) {
+					j = 0;
+					while ( (matcher = elementMatchers[j++]) ) {
+						if ( matcher( elem, context, xml ) ) {
+							results.push( elem );
+							break;
+						}
+					}
+					if ( outermost ) {
+						dirruns = dirrunsUnique;
+						cachedruns = ++matcherCachedRuns;
+					}
+				}
+
+				// Track unmatched elements for set filters
+				if ( bySet ) {
+					// They will have gone through all possible matchers
+					if ( (elem = !matcher && elem) ) {
+						matchedCount--;
+					}
+
+					// Lengthen the array for every element, matched or not
+					if ( seed ) {
+						unmatched.push( elem );
+					}
+				}
+			}
+
+			// Apply set filters to unmatched elements
+			matchedCount += i;
+			if ( bySet && i !== matchedCount ) {
+				j = 0;
+				while ( (matcher = setMatchers[j++]) ) {
+					matcher( unmatched, setMatched, context, xml );
+				}
+
+				if ( seed ) {
+					// Reintegrate element matches to eliminate the need for sorting
+					if ( matchedCount > 0 ) {
+						while ( i-- ) {
+							if ( !(unmatched[i] || setMatched[i]) ) {
+								setMatched[i] = pop.call( results );
+							}
+						}
+					}
+
+					// Discard index placeholder values to get only actual matches
+					setMatched = condense( setMatched );
+				}
+
+				// Add matches to results
+				push.apply( results, setMatched );
+
+				// Seedless set matches succeeding multiple successful matchers stipulate sorting
+				if ( outermost && !seed && setMatched.length > 0 &&
+					( matchedCount + setMatchers.length ) > 1 ) {
+
+					Sizzle.uniqueSort( results );
+				}
+			}
+
+			// Override manipulation of globals by nested matchers
+			if ( outermost ) {
+				dirruns = dirrunsUnique;
+				outermostContext = contextBackup;
+			}
+
+			return unmatched;
+		};
+
+	return bySet ?
+		markFunction( superMatcher ) :
+		superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+	var i,
+		setMatchers = [],
+		elementMatchers = [],
+		cached = compilerCache[ selector + " " ];
+
+	if ( !cached ) {
+		// Generate a function of recursive functions that can be used to check each element
+		if ( !group ) {
+			group = tokenize( selector );
+		}
+		i = group.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( group[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		// Cache the compiled function
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+	}
+	return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function select( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		match = tokenize( selector );
+
+	if ( !seed ) {
+		// Try to minimize operations if there is only one group
+		if ( match.length === 1 ) {
+
+			// Take a shortcut and set the context if the root selector is an ID
+			tokens = match[0] = match[0].slice( 0 );
+			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+					support.getById && context.nodeType === 9 && documentIsHTML &&
+					Expr.relative[ tokens[1].type ] ) {
+
+				context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+				if ( !context ) {
+					return results;
+				}
+				selector = selector.slice( tokens.shift().value.length );
+			}
+
+			// Fetch a seed set for right-to-left matching
+			i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+			while ( i-- ) {
+				token = tokens[i];
+
+				// Abort if we hit a combinator
+				if ( Expr.relative[ (type = token.type) ] ) {
+					break;
+				}
+				if ( (find = Expr.find[ type ]) ) {
+					// Search, expanding context for leading sibling combinators
+					if ( (seed = find(
+						token.matches[0].replace( runescape, funescape ),
+						rsibling.test( tokens[0].type ) && context.parentNode || context
+					)) ) {
+
+						// If seed is empty or no tokens remain, we can return early
+						tokens.splice( i, 1 );
+						selector = seed.length && toSelector( tokens );
+						if ( !selector ) {
+							push.apply( results, seed );
+							return results;
+						}
+
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	// Compile and execute a filtering function
+	// Provide `match` to avoid retokenization if we modified the selector above
+	compile( selector, match )(
+		seed,
+		context,
+		!documentIsHTML,
+		results,
+		rsibling.test( selector )
+	);
+	return results;
+}
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+	// Should return 1, but returns 4 (following)
+	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+	div.innerHTML = "<a href='#'></a>";
+	return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+		if ( !isXML ) {
+			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+		}
+	});
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+	div.innerHTML = "<input/>";
+	div.firstChild.setAttribute( "value", "" );
+	return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+	addHandle( "value", function( elem, name, isXML ) {
+		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+			return elem.defaultValue;
+		}
+	});
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+	return div.getAttribute("disabled") == null;
+}) ) {
+	addHandle( booleans, function( elem, name, isXML ) {
+		var val;
+		if ( !isXML ) {
+			return (val = elem.getAttributeNode( name )) && val.specified ?
+				val.value :
+				elem[ name ] === true ? name.toLowerCase() : null;
+		}
+	});
+}
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+	var object = optionsCache[ options ] = {};
+	jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+		object[ flag ] = true;
+	});
+	return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *	options: an optional list of space-separated options that will change how
+ *			the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *	once:			will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *	memory:			will keep track of previous values and will call any callback added
+ *					after the list has been fired right away with the latest "memorized"
+ *					values (like a Deferred)
+ *
+ *	unique:			will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *	stopOnFalse:	interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+	// Convert options from String-formatted to Object-formatted if needed
+	// (we check in cache first)
+	options = typeof options === "string" ?
+		( optionsCache[ options ] || createOptions( options ) ) :
+		jQuery.extend( {}, options );
+
+	var // Last fire value (for non-forgettable lists)
+		memory,
+		// Flag to know if list was already fired
+		fired,
+		// Flag to know if list is currently firing
+		firing,
+		// First callback to fire (used internally by add and fireWith)
+		firingStart,
+		// End of the loop when firing
+		firingLength,
+		// Index of currently firing callback (modified by remove if needed)
+		firingIndex,
+		// Actual callback list
+		list = [],
+		// Stack of fire calls for repeatable lists
+		stack = !options.once && [],
+		// Fire callbacks
+		fire = function( data ) {
+			memory = options.memory && data;
+			fired = true;
+			firingIndex = firingStart || 0;
+			firingStart = 0;
+			firingLength = list.length;
+			firing = true;
+			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+					memory = false; // To prevent further calls using add
+					break;
+				}
+			}
+			firing = false;
+			if ( list ) {
+				if ( stack ) {
+					if ( stack.length ) {
+						fire( stack.shift() );
+					}
+				} else if ( memory ) {
+					list = [];
+				} else {
+					self.disable();
+				}
+			}
+		},
+		// Actual Callbacks object
+		self = {
+			// Add a callback or a collection of callbacks to the list
+			add: function() {
+				if ( list ) {
+					// First, we save the current length
+					var start = list.length;
+					(function add( args ) {
+						jQuery.each( args, function( _, arg ) {
+							var type = jQuery.type( arg );
+							if ( type === "function" ) {
+								if ( !options.unique || !self.has( arg ) ) {
+									list.push( arg );
+								}
+							} else if ( arg && arg.length && type !== "string" ) {
+								// Inspect recursively
+								add( arg );
+							}
+						});
+					})( arguments );
+					// Do we need to add the callbacks to the
+					// current firing batch?
+					if ( firing ) {
+						firingLength = list.length;
+					// With memory, if we're not firing then
+					// we should call right away
+					} else if ( memory ) {
+						firingStart = start;
+						fire( memory );
+					}
+				}
+				return this;
+			},
+			// Remove a callback from the list
+			remove: function() {
+				if ( list ) {
+					jQuery.each( arguments, function( _, arg ) {
+						var index;
+						while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+							list.splice( index, 1 );
+							// Handle firing indexes
+							if ( firing ) {
+								if ( index <= firingLength ) {
+									firingLength--;
+								}
+								if ( index <= firingIndex ) {
+									firingIndex--;
+								}
+							}
+						}
+					});
+				}
+				return this;
+			},
+			// Check if a given callback is in the list.
+			// If no argument is given, return whether or not list has callbacks attached.
+			has: function( fn ) {
+				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+			},
+			// Remove all callbacks from the list
+			empty: function() {
+				list = [];
+				firingLength = 0;
+				return this;
+			},
+			// Have the list do nothing anymore
+			disable: function() {
+				list = stack = memory = undefined;
+				return this;
+			},
+			// Is it disabled?
+			disabled: function() {
+				return !list;
+			},
+			// Lock the list in its current state
+			lock: function() {
+				stack = undefined;
+				if ( !memory ) {
+					self.disable();
+				}
+				return this;
+			},
+			// Is it locked?
+			locked: function() {
+				return !stack;
+			},
+			// Call all callbacks with the given context and arguments
+			fireWith: function( context, args ) {
+				if ( list && ( !fired || stack ) ) {
+					args = args || [];
+					args = [ context, args.slice ? args.slice() : args ];
+					if ( firing ) {
+						stack.push( args );
+					} else {
+						fire( args );
+					}
+				}
+				return this;
+			},
+			// Call all the callbacks with the given arguments
+			fire: function() {
+				self.fireWith( this, arguments );
+				return this;
+			},
+			// To know if the callbacks have already been called at least once
+			fired: function() {
+				return !!fired;
+			}
+		};
+
+	return self;
+};
+jQuery.extend({
+
+	Deferred: function( func ) {
+		var tuples = [
+				// action, add listener, listener list, final state
+				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+				[ "notify", "progress", jQuery.Callbacks("memory") ]
+			],
+			state = "pending",
+			promise = {
+				state: function() {
+					return state;
+				},
+				always: function() {
+					deferred.done( arguments ).fail( arguments );
+					return this;
+				},
+				then: function( /* fnDone, fnFail, fnProgress */ ) {
+					var fns = arguments;
+					return jQuery.Deferred(function( newDefer ) {
+						jQuery.each( tuples, function( i, tuple ) {
+							var action = tuple[ 0 ],
+								fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+							// deferred[ done | fail | progress ] for forwarding actions to newDefer
+							deferred[ tuple[1] ](function() {
+								var returned = fn && fn.apply( this, arguments );
+								if ( returned && jQuery.isFunction( returned.promise ) ) {
+									returned.promise()
+										.done( newDefer.resolve )
+										.fail( newDefer.reject )
+										.progress( newDefer.notify );
+								} else {
+									newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+								}
+							});
+						});
+						fns = null;
+					}).promise();
+				},
+				// Get a promise for this deferred
+				// If obj is provided, the promise aspect is added to the object
+				promise: function( obj ) {
+					return obj != null ? jQuery.extend( obj, promise ) : promise;
+				}
+			},
+			deferred = {};
+
+		// Keep pipe for back-compat
+		promise.pipe = promise.then;
+
+		// Add list-specific methods
+		jQuery.each( tuples, function( i, tuple ) {
+			var list = tuple[ 2 ],
+				stateString = tuple[ 3 ];
+
+			// promise[ done | fail | progress ] = list.add
+			promise[ tuple[1] ] = list.add;
+
+			// Handle state
+			if ( stateString ) {
+				list.add(function() {
+					// state = [ resolved | rejected ]
+					state = stateString;
+
+				// [ reject_list | resolve_list ].disable; progress_list.lock
+				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+			}
+
+			// deferred[ resolve | reject | notify ]
+			deferred[ tuple[0] ] = function() {
+				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+				return this;
+			};
+			deferred[ tuple[0] + "With" ] = list.fireWith;
+		});
+
+		// Make the deferred a promise
+		promise.promise( deferred );
+
+		// Call given func if any
+		if ( func ) {
+			func.call( deferred, deferred );
+		}
+
+		// All done!
+		return deferred;
+	},
+
+	// Deferred helper
+	when: function( subordinate /* , ..., subordinateN */ ) {
+		var i = 0,
+			resolveValues = core_slice.call( arguments ),
+			length = resolveValues.length,
+
+			// the count of uncompleted subordinates
+			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+			// Update function for both resolve and progress values
+			updateFunc = function( i, contexts, values ) {
+				return function( value ) {
+					contexts[ i ] = this;
+					values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+					if( values === progressValues ) {
+						deferred.notifyWith( contexts, values );
+					} else if ( !( --remaining ) ) {
+						deferred.resolveWith( contexts, values );
+					}
+				};
+			},
+
+			progressValues, progressContexts, resolveContexts;
+
+		// add listeners to Deferred subordinates; treat others as resolved
+		if ( length > 1 ) {
+			progressValues = new Array( length );
+			progressContexts = new Array( length );
+			resolveContexts = new Array( length );
+			for ( ; i < length; i++ ) {
+				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+					resolveValues[ i ].promise()
+						.done( updateFunc( i, resolveContexts, resolveValues ) )
+						.fail( deferred.reject )
+						.progress( updateFunc( i, progressContexts, progressValues ) );
+				} else {
+					--remaining;
+				}
+			}
+		}
+
+		// if we're not waiting on anything, resolve the master
+		if ( !remaining ) {
+			deferred.resolveWith( resolveContexts, resolveValues );
+		}
+
+		return deferred.promise();
+	}
+});
+jQuery.support = (function( support ) {
+	var input = document.createElement("input"),
+		fragment = document.createDocumentFragment(),
+		div = document.createElement("div"),
+		select = document.createElement("select"),
+		opt = select.appendChild( document.createElement("option") );
+
+	// Finish early in limited environments
+	if ( !input.type ) {
+		return support;
+	}
+
+	input.type = "checkbox";
+
+	// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+	// Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
+	support.checkOn = input.value !== "";
+
+	// Must access the parent to make an option select properly
+	// Support: IE9, IE10
+	support.optSelected = opt.selected;
+
+	// Will be defined later
+	support.reliableMarginRight = true;
+	support.boxSizingReliable = true;
+	support.pixelPosition = false;
+
+	// Make sure checked status is properly cloned
+	// Support: IE9, IE10
+	input.checked = true;
+	support.noCloneChecked = input.cloneNode( true ).checked;
+
+	// Make sure that the options inside disabled selects aren't marked as disabled
+	// (WebKit marks them as disabled)
+	select.disabled = true;
+	support.optDisabled = !opt.disabled;
+
+	// Check if an input maintains its value after becoming a radio
+	// Support: IE9, IE10
+	input = document.createElement("input");
+	input.value = "t";
+	input.type = "radio";
+	support.radioValue = input.value === "t";
+
+	// #11217 - WebKit loses check when the name is after the checked attribute
+	input.setAttribute( "checked", "t" );
+	input.setAttribute( "name", "t" );
+
+	fragment.appendChild( input );
+
+	// Support: Safari 5.1, Android 4.x, Android 2.3
+	// old WebKit doesn't clone checked state correctly in fragments
+	support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+	// Support: Firefox, Chrome, Safari
+	// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+	support.focusinBubbles = "onfocusin" in window;
+
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	// Run tests that need a body at doc ready
+	jQuery(function() {
+		var container, marginDiv,
+			// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
+			divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",
+			body = document.getElementsByTagName("body")[ 0 ];
+
+		if ( !body ) {
+			// Return for frameset docs that don't have a body
+			return;
+		}
+
+		container = document.createElement("div");
+		container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+
+		// Check box-sizing and margin behavior.
+		body.appendChild( container ).appendChild( div );
+		div.innerHTML = "";
+		// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
+		div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%";
+
+		// Workaround failing boxSizing test due to offsetWidth returning wrong value
+		// with some non-1 values of body zoom, ticket #13543
+		jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
+			support.boxSizing = div.offsetWidth === 4;
+		});
+
+		// Use window.getComputedStyle because jsdom on node.js will break without it.
+		if ( window.getComputedStyle ) {
+			support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+			support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+			// Support: Android 2.3
+			// Check if div with explicit width and no margin-right incorrectly
+			// gets computed margin-right based on width of container. (#3333)
+			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+			marginDiv = div.appendChild( document.createElement("div") );
+			marginDiv.style.cssText = div.style.cssText = divReset;
+			marginDiv.style.marginRight = marginDiv.style.width = "0";
+			div.style.width = "1px";
+
+			support.reliableMarginRight =
+				!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+		}
+
+		body.removeChild( container );
+	});
+
+	return support;
+})( {} );
+
+/*
+	Implementation Summary
+
+	1. Enforce API surface and semantic compatibility with 1.9.x branch
+	2. Improve the module's maintainability by reducing the storage
+		paths to a single mechanism.
+	3. Use the same single mechanism to support "private" and "user" data.
+	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
+	5. Avoid exposing implementation details on user objects (eg. expando properties)
+	6. Provide a clear path for implementation upgrade to WeakMap in 2014
+*/
+var data_user, data_priv,
+	rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+	rmultiDash = /([A-Z])/g;
+
+function Data() {
+	// Support: Android < 4,
+	// Old WebKit does not have Object.preventExtensions/freeze method,
+	// return new empty object instead with no [[set]] accessor
+	Object.defineProperty( this.cache = {}, 0, {
+		get: function() {
+			return {};
+		}
+	});
+
+	this.expando = jQuery.expando + Math.random();
+}
+
+Data.uid = 1;
+
+Data.accepts = function( owner ) {
+	// Accepts only:
+	//  - Node
+	//    - Node.ELEMENT_NODE
+	//    - Node.DOCUMENT_NODE
+	//  - Object
+	//    - Any
+	return owner.nodeType ?
+		owner.nodeType === 1 || owner.nodeType === 9 : true;
+};
+
+Data.prototype = {
+	key: function( owner ) {
+		// We can accept data for non-element nodes in modern browsers,
+		// but we should not, see #8335.
+		// Always return the key for a frozen object.
+		if ( !Data.accepts( owner ) ) {
+			return 0;
+		}
+
+		var descriptor = {},
+			// Check if the owner object already has a cache key
+			unlock = owner[ this.expando ];
+
+		// If not, create one
+		if ( !unlock ) {
+			unlock = Data.uid++;
+
+			// Secure it in a non-enumerable, non-writable property
+			try {
+				descriptor[ this.expando ] = { value: unlock };
+				Object.defineProperties( owner, descriptor );
+
+			// Support: Android < 4
+			// Fallback to a less secure definition
+			} catch ( e ) {
+				descriptor[ this.expando ] = unlock;
+				jQuery.extend( owner, descriptor );
+			}
+		}
+
+		// Ensure the cache object
+		if ( !this.cache[ unlock ] ) {
+			this.cache[ unlock ] = {};
+		}
+
+		return unlock;
+	},
+	set: function( owner, data, value ) {
+		var prop,
+			// There may be an unlock assigned to this node,
+			// if there is no entry for this "owner", create one inline
+			// and set the unlock as though an owner entry had always existed
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		// Handle: [ owner, key, value ] args
+		if ( typeof data === "string" ) {
+			cache[ data ] = value;
+
+		// Handle: [ owner, { properties } ] args
+		} else {
+			// Fresh assignments by object are shallow copied
+			if ( jQuery.isEmptyObject( cache ) ) {
+				jQuery.extend( this.cache[ unlock ], data );
+			// Otherwise, copy the properties one-by-one to the cache object
+			} else {
+				for ( prop in data ) {
+					cache[ prop ] = data[ prop ];
+				}
+			}
+		}
+		return cache;
+	},
+	get: function( owner, key ) {
+		// Either a valid cache is found, or will be created.
+		// New caches will be created and the unlock returned,
+		// allowing direct access to the newly created
+		// empty data object. A valid owner object must be provided.
+		var cache = this.cache[ this.key( owner ) ];
+
+		return key === undefined ?
+			cache : cache[ key ];
+	},
+	access: function( owner, key, value ) {
+		var stored;
+		// In cases where either:
+		//
+		//   1. No key was specified
+		//   2. A string key was specified, but no value provided
+		//
+		// Take the "read" path and allow the get method to determine
+		// which value to return, respectively either:
+		//
+		//   1. The entire cache object
+		//   2. The data stored at the key
+		//
+		if ( key === undefined ||
+				((key && typeof key === "string") && value === undefined) ) {
+
+			stored = this.get( owner, key );
+
+			return stored !== undefined ?
+				stored : this.get( owner, jQuery.camelCase(key) );
+		}
+
+		// [*]When the key is not a string, or both a key and value
+		// are specified, set or extend (existing objects) with either:
+		//
+		//   1. An object of properties
+		//   2. A key and value
+		//
+		this.set( owner, key, value );
+
+		// Since the "set" path can have two possible entry points
+		// return the expected data based on which path was taken[*]
+		return value !== undefined ? value : key;
+	},
+	remove: function( owner, key ) {
+		var i, name, camel,
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		if ( key === undefined ) {
+			this.cache[ unlock ] = {};
+
+		} else {
+			// Support array or space separated string of keys
+			if ( jQuery.isArray( key ) ) {
+				// If "name" is an array of keys...
+				// When data is initially created, via ("key", "val") signature,
+				// keys will be converted to camelCase.
+				// Since there is no way to tell _how_ a key was added, remove
+				// both plain key and camelCase key. #12786
+				// This will only penalize the array argument path.
+				name = key.concat( key.map( jQuery.camelCase ) );
+			} else {
+				camel = jQuery.camelCase( key );
+				// Try the string as a key before any manipulation
+				if ( key in cache ) {
+					name = [ key, camel ];
+				} else {
+					// If a key with the spaces exists, use it.
+					// Otherwise, create an array by matching non-whitespace
+					name = camel;
+					name = name in cache ?
+						[ name ] : ( name.match( core_rnotwhite ) || [] );
+				}
+			}
+
+			i = name.length;
+			while ( i-- ) {
+				delete cache[ name[ i ] ];
+			}
+		}
+	},
+	hasData: function( owner ) {
+		return !jQuery.isEmptyObject(
+			this.cache[ owner[ this.expando ] ] || {}
+		);
+	},
+	discard: function( owner ) {
+		if ( owner[ this.expando ] ) {
+			delete this.cache[ owner[ this.expando ] ];
+		}
+	}
+};
+
+// These may be used throughout the jQuery core codebase
+data_user = new Data();
+data_priv = new Data();
+
+
+jQuery.extend({
+	acceptData: Data.accepts,
+
+	hasData: function( elem ) {
+		return data_user.hasData( elem ) || data_priv.hasData( elem );
+	},
+
+	data: function( elem, name, data ) {
+		return data_user.access( elem, name, data );
+	},
+
+	removeData: function( elem, name ) {
+		data_user.remove( elem, name );
+	},
+
+	// TODO: Now that all calls to _data and _removeData have been replaced
+	// with direct calls to data_priv methods, these can be deprecated.
+	_data: function( elem, name, data ) {
+		return data_priv.access( elem, name, data );
+	},
+
+	_removeData: function( elem, name ) {
+		data_priv.remove( elem, name );
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ) {
+		var attrs, name,
+			elem = this[ 0 ],
+			i = 0,
+			data = null;
+
+		// Gets all values
+		if ( key === undefined ) {
+			if ( this.length ) {
+				data = data_user.get( elem );
+
+				if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
+					attrs = elem.attributes;
+					for ( ; i < attrs.length; i++ ) {
+						name = attrs[ i ].name;
+
+						if ( name.indexOf( "data-" ) === 0 ) {
+							name = jQuery.camelCase( name.slice(5) );
+							dataAttr( elem, name, data[ name ] );
+						}
+					}
+					data_priv.set( elem, "hasDataAttrs", true );
+				}
+			}
+
+			return data;
+		}
+
+		// Sets multiple values
+		if ( typeof key === "object" ) {
+			return this.each(function() {
+				data_user.set( this, key );
+			});
+		}
+
+		return jQuery.access( this, function( value ) {
+			var data,
+				camelKey = jQuery.camelCase( key );
+
+			// The calling jQuery object (element matches) is not empty
+			// (and therefore has an element appears at this[ 0 ]) and the
+			// `value` parameter was not undefined. An empty jQuery object
+			// will result in `undefined` for elem = this[ 0 ] which will
+			// throw an exception if an attempt to read a data cache is made.
+			if ( elem && value === undefined ) {
+				// Attempt to get data from the cache
+				// with the key as-is
+				data = data_user.get( elem, key );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to get data from the cache
+				// with the key camelized
+				data = data_user.get( elem, camelKey );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to "discover" the data in
+				// HTML5 custom data-* attrs
+				data = dataAttr( elem, camelKey, undefined );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// We tried really hard, but the data doesn't exist.
+				return;
+			}
+
+			// Set the data...
+			this.each(function() {
+				// First, attempt to store a copy or reference of any
+				// data that might've been store with a camelCased key.
+				var data = data_user.get( this, camelKey );
+
+				// For HTML5 data-* attribute interop, we have to
+				// store property names with dashes in a camelCase form.
+				// This might not apply to all properties...*
+				data_user.set( this, camelKey, value );
+
+				// *... In the case of properties that might _actually_
+				// have dashes, we need to also store a copy of that
+				// unchanged property.
+				if ( key.indexOf("-") !== -1 && data !== undefined ) {
+					data_user.set( this, key, value );
+				}
+			});
+		}, null, value, arguments.length > 1, null, true );
+	},
+
+	removeData: function( key ) {
+		return this.each(function() {
+			data_user.remove( this, key );
+		});
+	}
+});
+
+function dataAttr( elem, key, data ) {
+	var name;
+
+	// If nothing was found internally, try to fetch any
+	// data from the HTML5 data-* attribute
+	if ( data === undefined && elem.nodeType === 1 ) {
+		name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+		data = elem.getAttribute( name );
+
+		if ( typeof data === "string" ) {
+			try {
+				data = data === "true" ? true :
+					data === "false" ? false :
+					data === "null" ? null :
+					// Only convert to a number if it doesn't change the string
+					+data + "" === data ? +data :
+					rbrace.test( data ) ? JSON.parse( data ) :
+					data;
+			} catch( e ) {}
+
+			// Make sure we set the data so it isn't changed later
+			data_user.set( elem, key, data );
+		} else {
+			data = undefined;
+		}
+	}
+	return data;
+}
+jQuery.extend({
+	queue: function( elem, type, data ) {
+		var queue;
+
+		if ( elem ) {
+			type = ( type || "fx" ) + "queue";
+			queue = data_priv.get( elem, type );
+
+			// Speed up dequeue by getting out quickly if this is just a lookup
+			if ( data ) {
+				if ( !queue || jQuery.isArray( data ) ) {
+					queue = data_priv.access( elem, type, jQuery.makeArray(data) );
+				} else {
+					queue.push( data );
+				}
+			}
+			return queue || [];
+		}
+	},
+
+	dequeue: function( elem, type ) {
+		type = type || "fx";
+
+		var queue = jQuery.queue( elem, type ),
+			startLength = queue.length,
+			fn = queue.shift(),
+			hooks = jQuery._queueHooks( elem, type ),
+			next = function() {
+				jQuery.dequeue( elem, type );
+			};
+
+		// If the fx queue is dequeued, always remove the progress sentinel
+		if ( fn === "inprogress" ) {
+			fn = queue.shift();
+			startLength--;
+		}
+
+		if ( fn ) {
+
+			// Add a progress sentinel to prevent the fx queue from being
+			// automatically dequeued
+			if ( type === "fx" ) {
+				queue.unshift( "inprogress" );
+			}
+
+			// clear up the last queue stop function
+			delete hooks.stop;
+			fn.call( elem, next, hooks );
+		}
+
+		if ( !startLength && hooks ) {
+			hooks.empty.fire();
+		}
+	},
+
+	// not intended for public consumption - generates a queueHooks object, or returns the current one
+	_queueHooks: function( elem, type ) {
+		var key = type + "queueHooks";
+		return data_priv.get( elem, key ) || data_priv.access( elem, key, {
+			empty: jQuery.Callbacks("once memory").add(function() {
+				data_priv.remove( elem, [ type + "queue", key ] );
+			})
+		});
+	}
+});
+
+jQuery.fn.extend({
+	queue: function( type, data ) {
+		var setter = 2;
+
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+			setter--;
+		}
+
+		if ( arguments.length < setter ) {
+			return jQuery.queue( this[0], type );
+		}
+
+		return data === undefined ?
+			this :
+			this.each(function() {
+				var queue = jQuery.queue( this, type, data );
+
+				// ensure a hooks for this queue
+				jQuery._queueHooks( this, type );
+
+				if ( type === "fx" && queue[0] !== "inprogress" ) {
+					jQuery.dequeue( this, type );
+				}
+			});
+	},
+	dequeue: function( type ) {
+		return this.each(function() {
+			jQuery.dequeue( this, type );
+		});
+	},
+	// Based off of the plugin by Clint Helfers, with permission.
+	// http://blindsignals.com/index.php/2009/07/jquery-delay/
+	delay: function( time, type ) {
+		time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+		type = type || "fx";
+
+		return this.queue( type, function( next, hooks ) {
+			var timeout = setTimeout( next, time );
+			hooks.stop = function() {
+				clearTimeout( timeout );
+			};
+		});
+	},
+	clearQueue: function( type ) {
+		return this.queue( type || "fx", [] );
+	},
+	// Get a promise resolved when queues of a certain type
+	// are emptied (fx is the type by default)
+	promise: function( type, obj ) {
+		var tmp,
+			count = 1,
+			defer = jQuery.Deferred(),
+			elements = this,
+			i = this.length,
+			resolve = function() {
+				if ( !( --count ) ) {
+					defer.resolveWith( elements, [ elements ] );
+				}
+			};
+
+		if ( typeof type !== "string" ) {
+			obj = type;
+			type = undefined;
+		}
+		type = type || "fx";
+
+		while( i-- ) {
+			tmp = data_priv.get( elements[ i ], type + "queueHooks" );
+			if ( tmp && tmp.empty ) {
+				count++;
+				tmp.empty.add( resolve );
+			}
+		}
+		resolve();
+		return defer.promise( obj );
+	}
+});
+var nodeHook, boolHook,
+	rclass = /[\t\r\n\f]/g,
+	rreturn = /\r/g,
+	rfocusable = /^(?:input|select|textarea|button)$/i;
+
+jQuery.fn.extend({
+	attr: function( name, value ) {
+		return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+	},
+
+	removeAttr: function( name ) {
+		return this.each(function() {
+			jQuery.removeAttr( this, name );
+		});
+	},
+
+	prop: function( name, value ) {
+		return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+	},
+
+	removeProp: function( name ) {
+		return this.each(function() {
+			delete this[ jQuery.propFix[ name ] || name ];
+		});
+	},
+
+	addClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = typeof value === "string" && value;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).addClass( value.call( this, j, this.className ) );
+			});
+		}
+
+		if ( proceed ) {
+			// The disjunction here is for better compressibility (see removeClass)
+			classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					" "
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+							cur += clazz + " ";
+						}
+					}
+					elem.className = jQuery.trim( cur );
+
+				}
+			}
+		}
+
+		return this;
+	},
+
+	removeClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = arguments.length === 0 || typeof value === "string" && value;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+			});
+		}
+		if ( proceed ) {
+			classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				// This expression is here for better compressibility (see addClass)
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					""
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						// Remove *all* instances
+						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+							cur = cur.replace( " " + clazz + " ", " " );
+						}
+					}
+					elem.className = value ? jQuery.trim( cur ) : "";
+				}
+			}
+		}
+
+		return this;
+	},
+
+	toggleClass: function( value, stateVal ) {
+		var type = typeof value;
+
+		if ( typeof stateVal === "boolean" && type === "string" ) {
+			return stateVal ? this.addClass( value ) : this.removeClass( value );
+		}
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+			});
+		}
+
+		return this.each(function() {
+			if ( type === "string" ) {
+				// toggle individual class names
+				var className,
+					i = 0,
+					self = jQuery( this ),
+					classNames = value.match( core_rnotwhite ) || [];
+
+				while ( (className = classNames[ i++ ]) ) {
+					// check each className given, space separated list
+					if ( self.hasClass( className ) ) {
+						self.removeClass( className );
+					} else {
+						self.addClass( className );
+					}
+				}
+
+			// Toggle whole class name
+			} else if ( type === core_strundefined || type === "boolean" ) {
+				if ( this.className ) {
+					// store className if set
+					data_priv.set( this, "__className__", this.className );
+				}
+
+				// If the element has a class name or if we're passed "false",
+				// then remove the whole classname (if there was one, the above saved it).
+				// Otherwise bring back whatever was previously saved (if anything),
+				// falling back to the empty string if nothing was stored.
+				this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
+			}
+		});
+	},
+
+	hasClass: function( selector ) {
+		var className = " " + selector + " ",
+			i = 0,
+			l = this.length;
+		for ( ; i < l; i++ ) {
+			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+				return true;
+			}
+		}
+
+		return false;
+	},
+
+	val: function( value ) {
+		var hooks, ret, isFunction,
+			elem = this[0];
+
+		if ( !arguments.length ) {
+			if ( elem ) {
+				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+					return ret;
+				}
+
+				ret = elem.value;
+
+				return typeof ret === "string" ?
+					// handle most common string cases
+					ret.replace(rreturn, "") :
+					// handle cases where value is null/undef or number
+					ret == null ? "" : ret;
+			}
+
+			return;
+		}
+
+		isFunction = jQuery.isFunction( value );
+
+		return this.each(function( i ) {
+			var val;
+
+			if ( this.nodeType !== 1 ) {
+				return;
+			}
+
+			if ( isFunction ) {
+				val = value.call( this, i, jQuery( this ).val() );
+			} else {
+				val = value;
+			}
+
+			// Treat null/undefined as ""; convert numbers to string
+			if ( val == null ) {
+				val = "";
+			} else if ( typeof val === "number" ) {
+				val += "";
+			} else if ( jQuery.isArray( val ) ) {
+				val = jQuery.map(val, function ( value ) {
+					return value == null ? "" : value + "";
+				});
+			}
+
+			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+			// If set returns undefined, fall back to normal setting
+			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+				this.value = val;
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	valHooks: {
+		option: {
+			get: function( elem ) {
+				// attributes.value is undefined in Blackberry 4.7 but
+				// uses .value. See #6932
+				var val = elem.attributes.value;
+				return !val || val.specified ? elem.value : elem.text;
+			}
+		},
+		select: {
+			get: function( elem ) {
+				var value, option,
+					options = elem.options,
+					index = elem.selectedIndex,
+					one = elem.type === "select-one" || index < 0,
+					values = one ? null : [],
+					max = one ? index + 1 : options.length,
+					i = index < 0 ?
+						max :
+						one ? index : 0;
+
+				// Loop through all the selected options
+				for ( ; i < max; i++ ) {
+					option = options[ i ];
+
+					// IE6-9 doesn't update selected after form reset (#2551)
+					if ( ( option.selected || i === index ) &&
+							// Don't return options that are disabled or in a disabled optgroup
+							( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+						// Get the specific value for the option
+						value = jQuery( option ).val();
+
+						// We don't need an array for one selects
+						if ( one ) {
+							return value;
+						}
+
+						// Multi-Selects return an array
+						values.push( value );
+					}
+				}
+
+				return values;
+			},
+
+			set: function( elem, value ) {
+				var optionSet, option,
+					options = elem.options,
+					values = jQuery.makeArray( value ),
+					i = options.length;
+
+				while ( i-- ) {
+					option = options[ i ];
+					if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
+						optionSet = true;
+					}
+				}
+
+				// force browsers to behave consistently when non-matching value is set
+				if ( !optionSet ) {
+					elem.selectedIndex = -1;
+				}
+				return values;
+			}
+		}
+	},
+
+	attr: function( elem, name, value ) {
+		var hooks, ret,
+			nType = elem.nodeType;
+
+		// don't get/set attributes on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		// Fallback to prop when attributes are not supported
+		if ( typeof elem.getAttribute === core_strundefined ) {
+			return jQuery.prop( elem, name, value );
+		}
+
+		// All attributes are lowercase
+		// Grab necessary hook if one is defined
+		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+			name = name.toLowerCase();
+			hooks = jQuery.attrHooks[ name ] ||
+				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+		}
+
+		if ( value !== undefined ) {
+
+			if ( value === null ) {
+				jQuery.removeAttr( elem, name );
+
+			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				elem.setAttribute( name, value + "" );
+				return value;
+			}
+
+		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+			return ret;
+
+		} else {
+			ret = jQuery.find.attr( elem, name );
+
+			// Non-existent attributes return null, we normalize to undefined
+			return ret == null ?
+				undefined :
+				ret;
+		}
+	},
+
+	removeAttr: function( elem, value ) {
+		var name, propName,
+			i = 0,
+			attrNames = value && value.match( core_rnotwhite );
+
+		if ( attrNames && elem.nodeType === 1 ) {
+			while ( (name = attrNames[i++]) ) {
+				propName = jQuery.propFix[ name ] || name;
+
+				// Boolean attributes get special treatment (#10870)
+				if ( jQuery.expr.match.bool.test( name ) ) {
+					// Set corresponding property to false
+					elem[ propName ] = false;
+				}
+
+				elem.removeAttribute( name );
+			}
+		}
+	},
+
+	attrHooks: {
+		type: {
+			set: function( elem, value ) {
+				if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+					// Setting the type on a radio button after the value resets the value in IE6-9
+					// Reset value to default in case type is set after value during creation
+					var val = elem.value;
+					elem.setAttribute( "type", value );
+					if ( val ) {
+						elem.value = val;
+					}
+					return value;
+				}
+			}
+		}
+	},
+
+	propFix: {
+		"for": "htmlFor",
+		"class": "className"
+	},
+
+	prop: function( elem, name, value ) {
+		var ret, hooks, notxml,
+			nType = elem.nodeType;
+
+		// don't get/set properties on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+		if ( notxml ) {
+			// Fix name and attach hooks
+			name = jQuery.propFix[ name ] || name;
+			hooks = jQuery.propHooks[ name ];
+		}
+
+		if ( value !== undefined ) {
+			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+				ret :
+				( elem[ name ] = value );
+
+		} else {
+			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+				ret :
+				elem[ name ];
+		}
+	},
+
+	propHooks: {
+		tabIndex: {
+			get: function( elem ) {
+				return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
+					elem.tabIndex :
+					-1;
+			}
+		}
+	}
+});
+
+// Hooks for boolean attributes
+boolHook = {
+	set: function( elem, value, name ) {
+		if ( value === false ) {
+			// Remove boolean attributes when set to false
+			jQuery.removeAttr( elem, name );
+		} else {
+			elem.setAttribute( name, name );
+		}
+		return name;
+	}
+};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+	var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+
+	jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) {
+		var fn = jQuery.expr.attrHandle[ name ],
+			ret = isXML ?
+				undefined :
+				/* jshint eqeqeq: false */
+				// Temporarily disable this handler to check existence
+				(jQuery.expr.attrHandle[ name ] = undefined) !=
+					getter( elem, name, isXML ) ?
+
+					name.toLowerCase() :
+					null;
+
+		// Restore handler
+		jQuery.expr.attrHandle[ name ] = fn;
+
+		return ret;
+	};
+});
+
+// Support: IE9+
+// Selectedness for an option in an optgroup can be inaccurate
+if ( !jQuery.support.optSelected ) {
+	jQuery.propHooks.selected = {
+		get: function( elem ) {
+			var parent = elem.parentNode;
+			if ( parent && parent.parentNode ) {
+				parent.parentNode.selectedIndex;
+			}
+			return null;
+		}
+	};
+}
+
+jQuery.each([
+	"tabIndex",
+	"readOnly",
+	"maxLength",
+	"cellSpacing",
+	"cellPadding",
+	"rowSpan",
+	"colSpan",
+	"useMap",
+	"frameBorder",
+	"contentEditable"
+], function() {
+	jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+	jQuery.valHooks[ this ] = {
+		set: function( elem, value ) {
+			if ( jQuery.isArray( value ) ) {
+				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+			}
+		}
+	};
+	if ( !jQuery.support.checkOn ) {
+		jQuery.valHooks[ this ].get = function( elem ) {
+			// Support: Webkit
+			// "" is returned instead of "on" if a value isn't specified
+			return elem.getAttribute("value") === null ? "on" : elem.value;
+		};
+	}
+});
+var rkeyEvent = /^key/,
+	rmouseEvent = /^(?:mouse|contextmenu)|click/,
+	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+	return true;
+}
+
+function returnFalse() {
+	return false;
+}
+
+function safeActiveElement() {
+	try {
+		return document.activeElement;
+	} catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+	global: {},
+
+	add: function( elem, types, handler, data, selector ) {
+
+		var handleObjIn, eventHandle, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.get( elem );
+
+		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+		if ( !elemData ) {
+			return;
+		}
+
+		// Caller can pass in an object of custom data in lieu of the handler
+		if ( handler.handler ) {
+			handleObjIn = handler;
+			handler = handleObjIn.handler;
+			selector = handleObjIn.selector;
+		}
+
+		// Make sure that the handler has a unique ID, used to find/remove it later
+		if ( !handler.guid ) {
+			handler.guid = jQuery.guid++;
+		}
+
+		// Init the element's event structure and main handler, if this is the first
+		if ( !(events = elemData.events) ) {
+			events = elemData.events = {};
+		}
+		if ( !(eventHandle = elemData.handle) ) {
+			eventHandle = elemData.handle = function( e ) {
+				// Discard the second event of a jQuery.event.trigger() and
+				// when an event is called after a page has unloaded
+				return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+					undefined;
+			};
+			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+			eventHandle.elem = elem;
+		}
+
+		// Handle multiple events separated by a space
+		types = ( types || "" ).match( core_rnotwhite ) || [""];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// There *must* be a type, no attaching namespace-only handlers
+			if ( !type ) {
+				continue;
+			}
+
+			// If event changes its type, use the special event handlers for the changed type
+			special = jQuery.event.special[ type ] || {};
+
+			// If selector defined, determine special event api type, otherwise given type
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+
+			// Update special based on newly reset type
+			special = jQuery.event.special[ type ] || {};
+
+			// handleObj is passed to all event handlers
+			handleObj = jQuery.extend({
+				type: type,
+				origType: origType,
+				data: data,
+				handler: handler,
+				guid: handler.guid,
+				selector: selector,
+				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+				namespace: namespaces.join(".")
+			}, handleObjIn );
+
+			// Init the event handler queue if we're the first
+			if ( !(handlers = events[ type ]) ) {
+				handlers = events[ type ] = [];
+				handlers.delegateCount = 0;
+
+				// Only use addEventListener if the special events handler returns false
+				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+					if ( elem.addEventListener ) {
+						elem.addEventListener( type, eventHandle, false );
+					}
+				}
+			}
+
+			if ( special.add ) {
+				special.add.call( elem, handleObj );
+
+				if ( !handleObj.handler.guid ) {
+					handleObj.handler.guid = handler.guid;
+				}
+			}
+
+			// Add to the element's handler list, delegates in front
+			if ( selector ) {
+				handlers.splice( handlers.delegateCount++, 0, handleObj );
+			} else {
+				handlers.push( handleObj );
+			}
+
+			// Keep track of which events have ever been used, for event optimization
+			jQuery.event.global[ type ] = true;
+		}
+
+		// Nullify elem to prevent memory leaks in IE
+		elem = null;
+	},
+
+	// Detach an event or set of events from an element
+	remove: function( elem, types, handler, selector, mappedTypes ) {
+
+		var j, origCount, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.hasData( elem ) && data_priv.get( elem );
+
+		if ( !elemData || !(events = elemData.events) ) {
+			return;
+		}
+
+		// Once for each type.namespace in types; type may be omitted
+		types = ( types || "" ).match( core_rnotwhite ) || [""];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// Unbind all events (on this namespace, if provided) for the element
+			if ( !type ) {
+				for ( type in events ) {
+					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+				}
+				continue;
+			}
+
+			special = jQuery.event.special[ type ] || {};
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+			handlers = events[ type ] || [];
+			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+			// Remove matching events
+			origCount = j = handlers.length;
+			while ( j-- ) {
+				handleObj = handlers[ j ];
+
+				if ( ( mappedTypes || origType === handleObj.origType ) &&
+					( !handler || handler.guid === handleObj.guid ) &&
+					( !tmp || tmp.test( handleObj.namespace ) ) &&
+					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+					handlers.splice( j, 1 );
+
+					if ( handleObj.selector ) {
+						handlers.delegateCount--;
+					}
+					if ( special.remove ) {
+						special.remove.call( elem, handleObj );
+					}
+				}
+			}
+
+			// Remove generic event handler if we removed something and no more handlers exist
+			// (avoids potential for endless recursion during removal of special event handlers)
+			if ( origCount && !handlers.length ) {
+				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+					jQuery.removeEvent( elem, type, elemData.handle );
+				}
+
+				delete events[ type ];
+			}
+		}
+
+		// Remove the expando if it's no longer used
+		if ( jQuery.isEmptyObject( events ) ) {
+			delete elemData.handle;
+			data_priv.remove( elem, "events" );
+		}
+	},
+
+	trigger: function( event, data, elem, onlyHandlers ) {
+
+		var i, cur, tmp, bubbleType, ontype, handle, special,
+			eventPath = [ elem || document ],
+			type = core_hasOwn.call( event, "type" ) ? event.type : event,
+			namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+		cur = tmp = elem = elem || document;
+
+		// Don't do events on text and comment nodes
+		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+			return;
+		}
+
+		// focus/blur morphs to focusin/out; ensure we're not firing them right now
+		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+			return;
+		}
+
+		if ( type.indexOf(".") >= 0 ) {
+			// Namespaced trigger; create a regexp to match event type in handle()
+			namespaces = type.split(".");
+			type = namespaces.shift();
+			namespaces.sort();
+		}
+		ontype = type.indexOf(":") < 0 && "on" + type;
+
+		// Caller can pass in a jQuery.Event object, Object, or just an event type string
+		event = event[ jQuery.expando ] ?
+			event :
+			new jQuery.Event( type, typeof event === "object" && event );
+
+		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+		event.isTrigger = onlyHandlers ? 2 : 3;
+		event.namespace = namespaces.join(".");
+		event.namespace_re = event.namespace ?
+			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+			null;
+
+		// Clean up the event in case it is being reused
+		event.result = undefined;
+		if ( !event.target ) {
+			event.target = elem;
+		}
+
+		// Clone any incoming data and prepend the event, creating the handler arg list
+		data = data == null ?
+			[ event ] :
+			jQuery.makeArray( data, [ event ] );
+
+		// Allow special events to draw outside the lines
+		special = jQuery.event.special[ type ] || {};
+		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+			return;
+		}
+
+		// Determine event propagation path in advance, per W3C events spec (#9951)
+		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+			bubbleType = special.delegateType || type;
+			if ( !rfocusMorph.test( bubbleType + type ) ) {
+				cur = cur.parentNode;
+			}
+			for ( ; cur; cur = cur.parentNode ) {
+				eventPath.push( cur );
+				tmp = cur;
+			}
+
+			// Only add window if we got to document (e.g., not plain obj or detached DOM)
+			if ( tmp === (elem.ownerDocument || document) ) {
+				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+			}
+		}
+
+		// Fire handlers on the event path
+		i = 0;
+		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+			event.type = i > 1 ?
+				bubbleType :
+				special.bindType || type;
+
+			// jQuery handler
+			handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
+			if ( handle ) {
+				handle.apply( cur, data );
+			}
+
+			// Native handler
+			handle = ontype && cur[ ontype ];
+			if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+				event.preventDefault();
+			}
+		}
+		event.type = type;
+
+		// If nobody prevented the default action, do it now
+		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+				jQuery.acceptData( elem ) ) {
+
+				// Call a native DOM method on the target with the same name name as the event.
+				// Don't do default actions on window, that's where global variables be (#6170)
+				if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
+
+					// Don't re-trigger an onFOO event when we call its FOO() method
+					tmp = elem[ ontype ];
+
+					if ( tmp ) {
+						elem[ ontype ] = null;
+					}
+
+					// Prevent re-triggering of the same event, since we already bubbled it above
+					jQuery.event.triggered = type;
+					elem[ type ]();
+					jQuery.event.triggered = undefined;
+
+					if ( tmp ) {
+						elem[ ontype ] = tmp;
+					}
+				}
+			}
+		}
+
+		return event.result;
+	},
+
+	dispatch: function( event ) {
+
+		// Make a writable jQuery.Event from the native event object
+		event = jQuery.event.fix( event );
+
+		var i, j, ret, matched, handleObj,
+			handlerQueue = [],
+			args = core_slice.call( arguments ),
+			handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
+			special = jQuery.event.special[ event.type ] || {};
+
+		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+		args[0] = event;
+		event.delegateTarget = this;
+
+		// Call the preDispatch hook for the mapped type, and let it bail if desired
+		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+			return;
+		}
+
+		// Determine handlers
+		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+		// Run delegates first; they may want to stop propagation beneath us
+		i = 0;
+		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+			event.currentTarget = matched.elem;
+
+			j = 0;
+			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+				// Triggered event must either 1) have no namespace, or
+				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+					event.handleObj = handleObj;
+					event.data = handleObj.data;
+
+					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+							.apply( matched.elem, args );
+
+					if ( ret !== undefined ) {
+						if ( (event.result = ret) === false ) {
+							event.preventDefault();
+							event.stopPropagation();
+						}
+					}
+				}
+			}
+		}
+
+		// Call the postDispatch hook for the mapped type
+		if ( special.postDispatch ) {
+			special.postDispatch.call( this, event );
+		}
+
+		return event.result;
+	},
+
+	handlers: function( event, handlers ) {
+		var i, matches, sel, handleObj,
+			handlerQueue = [],
+			delegateCount = handlers.delegateCount,
+			cur = event.target;
+
+		// Find delegate handlers
+		// Black-hole SVG <use> instance trees (#13180)
+		// Avoid non-left-click bubbling in Firefox (#3861)
+		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+			for ( ; cur !== this; cur = cur.parentNode || this ) {
+
+				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+				if ( cur.disabled !== true || event.type !== "click" ) {
+					matches = [];
+					for ( i = 0; i < delegateCount; i++ ) {
+						handleObj = handlers[ i ];
+
+						// Don't conflict with Object.prototype properties (#13203)
+						sel = handleObj.selector + " ";
+
+						if ( matches[ sel ] === undefined ) {
+							matches[ sel ] = handleObj.needsContext ?
+								jQuery( sel, this ).index( cur ) >= 0 :
+								jQuery.find( sel, this, null, [ cur ] ).length;
+						}
+						if ( matches[ sel ] ) {
+							matches.push( handleObj );
+						}
+					}
+					if ( matches.length ) {
+						handlerQueue.push({ elem: cur, handlers: matches });
+					}
+				}
+			}
+		}
+
+		// Add the remaining (directly-bound) handlers
+		if ( delegateCount < handlers.length ) {
+			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+		}
+
+		return handlerQueue;
+	},
+
+	// Includes some event props shared by KeyEvent and MouseEvent
+	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+	fixHooks: {},
+
+	keyHooks: {
+		props: "char charCode key keyCode".split(" "),
+		filter: function( event, original ) {
+
+			// Add which for key events
+			if ( event.which == null ) {
+				event.which = original.charCode != null ? original.charCode : original.keyCode;
+			}
+
+			return event;
+		}
+	},
+
+	mouseHooks: {
+		props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+		filter: function( event, original ) {
+			var eventDoc, doc, body,
+				button = original.button;
+
+			// Calculate pageX/Y if missing and clientX/Y available
+			if ( event.pageX == null && original.clientX != null ) {
+				eventDoc = event.target.ownerDocument || document;
+				doc = eventDoc.documentElement;
+				body = eventDoc.body;
+
+				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+			}
+
+			// Add which for click: 1 === left; 2 === middle; 3 === right
+			// Note: button is not normalized, so don't use it
+			if ( !event.which && button !== undefined ) {
+				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+			}
+
+			return event;
+		}
+	},
+
+	fix: function( event ) {
+		if ( event[ jQuery.expando ] ) {
+			return event;
+		}
+
+		// Create a writable copy of the event object and normalize some properties
+		var i, prop, copy,
+			type = event.type,
+			originalEvent = event,
+			fixHook = this.fixHooks[ type ];
+
+		if ( !fixHook ) {
+			this.fixHooks[ type ] = fixHook =
+				rmouseEvent.test( type ) ? this.mouseHooks :
+				rkeyEvent.test( type ) ? this.keyHooks :
+				{};
+		}
+		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+		event = new jQuery.Event( originalEvent );
+
+		i = copy.length;
+		while ( i-- ) {
+			prop = copy[ i ];
+			event[ prop ] = originalEvent[ prop ];
+		}
+
+		// Support: Cordova 2.5 (WebKit) (#13255)
+		// All events should have a target; Cordova deviceready doesn't
+		if ( !event.target ) {
+			event.target = document;
+		}
+
+		// Support: Safari 6.0+, Chrome < 28
+		// Target should not be a text node (#504, #13143)
+		if ( event.target.nodeType === 3 ) {
+			event.target = event.target.parentNode;
+		}
+
+		return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+	},
+
+	special: {
+		load: {
+			// Prevent triggered image.load events from bubbling to window.load
+			noBubble: true
+		},
+		focus: {
+			// Fire native event if possible so blur/focus sequence is correct
+			trigger: function() {
+				if ( this !== safeActiveElement() && this.focus ) {
+					this.focus();
+					return false;
+				}
+			},
+			delegateType: "focusin"
+		},
+		blur: {
+			trigger: function() {
+				if ( this === safeActiveElement() && this.blur ) {
+					this.blur();
+					return false;
+				}
+			},
+			delegateType: "focusout"
+		},
+		click: {
+			// For checkbox, fire native event so checked state will be right
+			trigger: function() {
+				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
+					this.click();
+					return false;
+				}
+			},
+
+			// For cross-browser consistency, don't fire native .click() on links
+			_default: function( event ) {
+				return jQuery.nodeName( event.target, "a" );
+			}
+		},
+
+		beforeunload: {
+			postDispatch: function( event ) {
+
+				// Support: Firefox 20+
+				// Firefox doesn't alert if the returnValue field is not set.
+				if ( event.result !== undefined ) {
+					event.originalEvent.returnValue = event.result;
+				}
+			}
+		}
+	},
+
+	simulate: function( type, elem, event, bubble ) {
+		// Piggyback on a donor event to simulate a different one.
+		// Fake originalEvent to avoid donor's stopPropagation, but if the
+		// simulated event prevents default then we do the same on the donor.
+		var e = jQuery.extend(
+			new jQuery.Event(),
+			event,
+			{
+				type: type,
+				isSimulated: true,
+				originalEvent: {}
+			}
+		);
+		if ( bubble ) {
+			jQuery.event.trigger( e, null, elem );
+		} else {
+			jQuery.event.dispatch.call( elem, e );
+		}
+		if ( e.isDefaultPrevented() ) {
+			event.preventDefault();
+		}
+	}
+};
+
+jQuery.removeEvent = function( elem, type, handle ) {
+	if ( elem.removeEventListener ) {
+		elem.removeEventListener( type, handle, false );
+	}
+};
+
+jQuery.Event = function( src, props ) {
+	// Allow instantiation without the 'new' keyword
+	if ( !(this instanceof jQuery.Event) ) {
+		return new jQuery.Event( src, props );
+	}
+
+	// Event object
+	if ( src && src.type ) {
+		this.originalEvent = src;
+		this.type = src.type;
+
+		// Events bubbling up the document may have been marked as prevented
+		// by a handler lower down the tree; reflect the correct value.
+		this.isDefaultPrevented = ( src.defaultPrevented ||
+			src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+	// Event type
+	} else {
+		this.type = src;
+	}
+
+	// Put explicitly provided properties onto the event object
+	if ( props ) {
+		jQuery.extend( this, props );
+	}
+
+	// Create a timestamp if incoming event doesn't have one
+	this.timeStamp = src && src.timeStamp || jQuery.now();
+
+	// Mark it as fixed
+	this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+	isDefaultPrevented: returnFalse,
+	isPropagationStopped: returnFalse,
+	isImmediatePropagationStopped: returnFalse,
+
+	preventDefault: function() {
+		var e = this.originalEvent;
+
+		this.isDefaultPrevented = returnTrue;
+
+		if ( e && e.preventDefault ) {
+			e.preventDefault();
+		}
+	},
+	stopPropagation: function() {
+		var e = this.originalEvent;
+
+		this.isPropagationStopped = returnTrue;
+
+		if ( e && e.stopPropagation ) {
+			e.stopPropagation();
+		}
+	},
+	stopImmediatePropagation: function() {
+		this.isImmediatePropagationStopped = returnTrue;
+		this.stopPropagation();
+	}
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+// Support: Chrome 15+
+jQuery.each({
+	mouseenter: "mouseover",
+	mouseleave: "mouseout"
+}, function( orig, fix ) {
+	jQuery.event.special[ orig ] = {
+		delegateType: fix,
+		bindType: fix,
+
+		handle: function( event ) {
+			var ret,
+				target = this,
+				related = event.relatedTarget,
+				handleObj = event.handleObj;
+
+			// For mousenter/leave call the handler if related is outside the target.
+			// NB: No relatedTarget if the mouse left/entered the browser window
+			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+				event.type = handleObj.origType;
+				ret = handleObj.handler.apply( this, arguments );
+				event.type = fix;
+			}
+			return ret;
+		}
+	};
+});
+
+// Create "bubbling" focus and blur events
+// Support: Firefox, Chrome, Safari
+if ( !jQuery.support.focusinBubbles ) {
+	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+		// Attach a single capturing handler while someone wants focusin/focusout
+		var attaches = 0,
+			handler = function( event ) {
+				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+			};
+
+		jQuery.event.special[ fix ] = {
+			setup: function() {
+				if ( attaches++ === 0 ) {
+					document.addEventListener( orig, handler, true );
+				}
+			},
+			teardown: function() {
+				if ( --attaches === 0 ) {
+					document.removeEventListener( orig, handler, true );
+				}
+			}
+		};
+	});
+}
+
+jQuery.fn.extend({
+
+	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+		var origFn, type;
+
+		// Types can be a map of types/handlers
+		if ( typeof types === "object" ) {
+			// ( types-Object, selector, data )
+			if ( typeof selector !== "string" ) {
+				// ( types-Object, data )
+				data = data || selector;
+				selector = undefined;
+			}
+			for ( type in types ) {
+				this.on( type, selector, data, types[ type ], one );
+			}
+			return this;
+		}
+
+		if ( data == null && fn == null ) {
+			// ( types, fn )
+			fn = selector;
+			data = selector = undefined;
+		} else if ( fn == null ) {
+			if ( typeof selector === "string" ) {
+				// ( types, selector, fn )
+				fn = data;
+				data = undefined;
+			} else {
+				// ( types, data, fn )
+				fn = data;
+				data = selector;
+				selector = undefined;
+			}
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		} else if ( !fn ) {
+			return this;
+		}
+
+		if ( one === 1 ) {
+			origFn = fn;
+			fn = function( event ) {
+				// Can use an empty set, since event contains the info
+				jQuery().off( event );
+				return origFn.apply( this, arguments );
+			};
+			// Use same guid so caller can remove using origFn
+			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+		}
+		return this.each( function() {
+			jQuery.event.add( this, types, fn, data, selector );
+		});
+	},
+	one: function( types, selector, data, fn ) {
+		return this.on( types, selector, data, fn, 1 );
+	},
+	off: function( types, selector, fn ) {
+		var handleObj, type;
+		if ( types && types.preventDefault && types.handleObj ) {
+			// ( event )  dispatched jQuery.Event
+			handleObj = types.handleObj;
+			jQuery( types.delegateTarget ).off(
+				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+				handleObj.selector,
+				handleObj.handler
+			);
+			return this;
+		}
+		if ( typeof types === "object" ) {
+			// ( types-object [, selector] )
+			for ( type in types ) {
+				this.off( type, selector, types[ type ] );
+			}
+			return this;
+		}
+		if ( selector === false || typeof selector === "function" ) {
+			// ( types [, fn] )
+			fn = selector;
+			selector = undefined;
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		}
+		return this.each(function() {
+			jQuery.event.remove( this, types, fn, selector );
+		});
+	},
+
+	trigger: function( type, data ) {
+		return this.each(function() {
+			jQuery.event.trigger( type, data, this );
+		});
+	},
+	triggerHandler: function( type, data ) {
+		var elem = this[0];
+		if ( elem ) {
+			return jQuery.event.trigger( type, data, elem, true );
+		}
+	}
+});
+var isSimple = /^.[^:#\[\.,]*$/,
+	rparentsprev = /^(?:parents|prev(?:Until|All))/,
+	rneedsContext = jQuery.expr.match.needsContext,
+	// methods guaranteed to produce a unique set when starting from a unique set
+	guaranteedUnique = {
+		children: true,
+		contents: true,
+		next: true,
+		prev: true
+	};
+
+jQuery.fn.extend({
+	find: function( selector ) {
+		var i,
+			ret = [],
+			self = this,
+			len = self.length;
+
+		if ( typeof selector !== "string" ) {
+			return this.pushStack( jQuery( selector ).filter(function() {
+				for ( i = 0; i < len; i++ ) {
+					if ( jQuery.contains( self[ i ], this ) ) {
+						return true;
+					}
+				}
+			}) );
+		}
+
+		for ( i = 0; i < len; i++ ) {
+			jQuery.find( selector, self[ i ], ret );
+		}
+
+		// Needed because $( selector, context ) becomes $( context ).find( selector )
+		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+		ret.selector = this.selector ? this.selector + " " + selector : selector;
+		return ret;
+	},
+
+	has: function( target ) {
+		var targets = jQuery( target, this ),
+			l = targets.length;
+
+		return this.filter(function() {
+			var i = 0;
+			for ( ; i < l; i++ ) {
+				if ( jQuery.contains( this, targets[i] ) ) {
+					return true;
+				}
+			}
+		});
+	},
+
+	not: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], true) );
+	},
+
+	filter: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], false) );
+	},
+
+	is: function( selector ) {
+		return !!winnow(
+			this,
+
+			// If this is a positional/relative selector, check membership in the returned set
+			// so $("p:first").is("p:last") won't return true for a doc with two "p".
+			typeof selector === "string" && rneedsContext.test( selector ) ?
+				jQuery( selector ) :
+				selector || [],
+			false
+		).length;
+	},
+
+	closest: function( selectors, context ) {
+		var cur,
+			i = 0,
+			l = this.length,
+			matched = [],
+			pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ?
+				jQuery( selectors, context || this.context ) :
+				0;
+
+		for ( ; i < l; i++ ) {
+			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+				// Always skip document fragments
+				if ( cur.nodeType < 11 && (pos ?
+					pos.index(cur) > -1 :
+
+					// Don't pass non-elements to Sizzle
+					cur.nodeType === 1 &&
+						jQuery.find.matchesSelector(cur, selectors)) ) {
+
+					cur = matched.push( cur );
+					break;
+				}
+			}
+		}
+
+		return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	index: function( elem ) {
+
+		// No argument, return index in parent
+		if ( !elem ) {
+			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
+		}
+
+		// index in selector
+		if ( typeof elem === "string" ) {
+			return core_indexOf.call( jQuery( elem ), this[ 0 ] );
+		}
+
+		// Locate the position of the desired element
+		return core_indexOf.call( this,
+
+			// If it receives a jQuery object, the first element is used
+			elem.jquery ? elem[ 0 ] : elem
+		);
+	},
+
+	add: function( selector, context ) {
+		var set = typeof selector === "string" ?
+				jQuery( selector, context ) :
+				jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+			all = jQuery.merge( this.get(), set );
+
+		return this.pushStack( jQuery.unique(all) );
+	},
+
+	addBack: function( selector ) {
+		return this.add( selector == null ?
+			this.prevObject : this.prevObject.filter(selector)
+		);
+	}
+});
+
+function sibling( cur, dir ) {
+	while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
+
+	return cur;
+}
+
+jQuery.each({
+	parent: function( elem ) {
+		var parent = elem.parentNode;
+		return parent && parent.nodeType !== 11 ? parent : null;
+	},
+	parents: function( elem ) {
+		return jQuery.dir( elem, "parentNode" );
+	},
+	parentsUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "parentNode", until );
+	},
+	next: function( elem ) {
+		return sibling( elem, "nextSibling" );
+	},
+	prev: function( elem ) {
+		return sibling( elem, "previousSibling" );
+	},
+	nextAll: function( elem ) {
+		return jQuery.dir( elem, "nextSibling" );
+	},
+	prevAll: function( elem ) {
+		return jQuery.dir( elem, "previousSibling" );
+	},
+	nextUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "nextSibling", until );
+	},
+	prevUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "previousSibling", until );
+	},
+	siblings: function( elem ) {
+		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+	},
+	children: function( elem ) {
+		return jQuery.sibling( elem.firstChild );
+	},
+	contents: function( elem ) {
+		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
+	}
+}, function( name, fn ) {
+	jQuery.fn[ name ] = function( until, selector ) {
+		var matched = jQuery.map( this, fn, until );
+
+		if ( name.slice( -5 ) !== "Until" ) {
+			selector = until;
+		}
+
+		if ( selector && typeof selector === "string" ) {
+			matched = jQuery.filter( selector, matched );
+		}
+
+		if ( this.length > 1 ) {
+			// Remove duplicates
+			if ( !guaranteedUnique[ name ] ) {
+				jQuery.unique( matched );
+			}
+
+			// Reverse order for parents* and prev-derivatives
+			if ( rparentsprev.test( name ) ) {
+				matched.reverse();
+			}
+		}
+
+		return this.pushStack( matched );
+	};
+});
+
+jQuery.extend({
+	filter: function( expr, elems, not ) {
+		var elem = elems[ 0 ];
+
+		if ( not ) {
+			expr = ":not(" + expr + ")";
+		}
+
+		return elems.length === 1 && elem.nodeType === 1 ?
+			jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+			jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+				return elem.nodeType === 1;
+			}));
+	},
+
+	dir: function( elem, dir, until ) {
+		var matched = [],
+			truncate = until !== undefined;
+
+		while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
+			if ( elem.nodeType === 1 ) {
+				if ( truncate && jQuery( elem ).is( until ) ) {
+					break;
+				}
+				matched.push( elem );
+			}
+		}
+		return matched;
+	},
+
+	sibling: function( n, elem ) {
+		var matched = [];
+
+		for ( ; n; n = n.nextSibling ) {
+			if ( n.nodeType === 1 && n !== elem ) {
+				matched.push( n );
+			}
+		}
+
+		return matched;
+	}
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+	if ( jQuery.isFunction( qualifier ) ) {
+		return jQuery.grep( elements, function( elem, i ) {
+			/* jshint -W018 */
+			return !!qualifier.call( elem, i, elem ) !== not;
+		});
+
+	}
+
+	if ( qualifier.nodeType ) {
+		return jQuery.grep( elements, function( elem ) {
+			return ( elem === qualifier ) !== not;
+		});
+
+	}
+
+	if ( typeof qualifier === "string" ) {
+		if ( isSimple.test( qualifier ) ) {
+			return jQuery.filter( qualifier, elements, not );
+		}
+
+		qualifier = jQuery.filter( qualifier, elements );
+	}
+
+	return jQuery.grep( elements, function( elem ) {
+		return ( core_indexOf.call( qualifier, elem ) >= 0 ) !== not;
+	});
+}
+var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+	rtagName = /<([\w:]+)/,
+	rhtml = /<|&#?\w+;/,
+	rnoInnerhtml = /<(?:script|style|link)/i,
+	manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
+	// checked="checked" or checked
+	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+	rscriptType = /^$|\/(?:java|ecma)script/i,
+	rscriptTypeMasked = /^true\/(.*)/,
+	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+	// We have to close these tags to support XHTML (#13200)
+	wrapMap = {
+
+		// Support: IE 9
+		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+
+		thead: [ 1, "<table>", "</table>" ],
+		col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
+		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+		_default: [ 0, "", "" ]
+	};
+
+// Support: IE 9
+wrapMap.optgroup = wrapMap.option;
+
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+jQuery.fn.extend({
+	text: function( value ) {
+		return jQuery.access( this, function( value ) {
+			return value === undefined ?
+				jQuery.text( this ) :
+				this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) );
+		}, null, value, arguments.length );
+	},
+
+	append: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.appendChild( elem );
+			}
+		});
+	},
+
+	prepend: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.insertBefore( elem, target.firstChild );
+			}
+		});
+	},
+
+	before: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this );
+			}
+		});
+	},
+
+	after: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this.nextSibling );
+			}
+		});
+	},
+
+	// keepData is for internal use only--do not document
+	remove: function( selector, keepData ) {
+		var elem,
+			elems = selector ? jQuery.filter( selector, this ) : this,
+			i = 0;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+			if ( !keepData && elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem ) );
+			}
+
+			if ( elem.parentNode ) {
+				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+					setGlobalEval( getAll( elem, "script" ) );
+				}
+				elem.parentNode.removeChild( elem );
+			}
+		}
+
+		return this;
+	},
+
+	empty: function() {
+		var elem,
+			i = 0;
+
+		for ( ; (elem = this[i]) != null; i++ ) {
+			if ( elem.nodeType === 1 ) {
+
+				// Prevent memory leaks
+				jQuery.cleanData( getAll( elem, false ) );
+
+				// Remove any remaining nodes
+				elem.textContent = "";
+			}
+		}
+
+		return this;
+	},
+
+	clone: function( dataAndEvents, deepDataAndEvents ) {
+		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+		return this.map( function () {
+			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+		});
+	},
+
+	html: function( value ) {
+		return jQuery.access( this, function( value ) {
+			var elem = this[ 0 ] || {},
+				i = 0,
+				l = this.length;
+
+			if ( value === undefined && elem.nodeType === 1 ) {
+				return elem.innerHTML;
+			}
+
+			// See if we can take a shortcut and just use innerHTML
+			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
+
+				value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+				try {
+					for ( ; i < l; i++ ) {
+						elem = this[ i ] || {};
+
+						// Remove element nodes and prevent memory leaks
+						if ( elem.nodeType === 1 ) {
+							jQuery.cleanData( getAll( elem, false ) );
+							elem.innerHTML = value;
+						}
+					}
+
+					elem = 0;
+
+				// If using innerHTML throws an exception, use the fallback method
+				} catch( e ) {}
+			}
+
+			if ( elem ) {
+				this.empty().append( value );
+			}
+		}, null, value, arguments.length );
+	},
+
+	replaceWith: function() {
+		var
+			// Snapshot the DOM in case .domManip sweeps something relevant into its fragment
+			args = jQuery.map( this, function( elem ) {
+				return [ elem.nextSibling, elem.parentNode ];
+			}),
+			i = 0;
+
+		// Make the changes, replacing each context element with the new content
+		this.domManip( arguments, function( elem ) {
+			var next = args[ i++ ],
+				parent = args[ i++ ];
+
+			if ( parent ) {
+				// Don't use the snapshot next if it has moved (#13810)
+				if ( next && next.parentNode !== parent ) {
+					next = this.nextSibling;
+				}
+				jQuery( this ).remove();
+				parent.insertBefore( elem, next );
+			}
+		// Allow new content to include elements from the context set
+		}, true );
+
+		// Force removal if there was no new content (e.g., from empty arguments)
+		return i ? this : this.remove();
+	},
+
+	detach: function( selector ) {
+		return this.remove( selector, true );
+	},
+
+	domManip: function( args, callback, allowIntersection ) {
+
+		// Flatten any nested arrays
+		args = core_concat.apply( [], args );
+
+		var fragment, first, scripts, hasScripts, node, doc,
+			i = 0,
+			l = this.length,
+			set = this,
+			iNoClone = l - 1,
+			value = args[ 0 ],
+			isFunction = jQuery.isFunction( value );
+
+		// We can't cloneNode fragments that contain checked, in WebKit
+		if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
+			return this.each(function( index ) {
+				var self = set.eq( index );
+				if ( isFunction ) {
+					args[ 0 ] = value.call( this, index, self.html() );
+				}
+				self.domManip( args, callback, allowIntersection );
+			});
+		}
+
+		if ( l ) {
+			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this );
+			first = fragment.firstChild;
+
+			if ( fragment.childNodes.length === 1 ) {
+				fragment = first;
+			}
+
+			if ( first ) {
+				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+				hasScripts = scripts.length;
+
+				// Use the original fragment for the last item instead of the first because it can end up
+				// being emptied incorrectly in certain situations (#8070).
+				for ( ; i < l; i++ ) {
+					node = fragment;
+
+					if ( i !== iNoClone ) {
+						node = jQuery.clone( node, true, true );
+
+						// Keep references to cloned scripts for later restoration
+						if ( hasScripts ) {
+							// Support: QtWebKit
+							// jQuery.merge because core_push.apply(_, arraylike) throws
+							jQuery.merge( scripts, getAll( node, "script" ) );
+						}
+					}
+
+					callback.call( this[ i ], node, i );
+				}
+
+				if ( hasScripts ) {
+					doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+					// Reenable scripts
+					jQuery.map( scripts, restoreScript );
+
+					// Evaluate executable scripts on first document insertion
+					for ( i = 0; i < hasScripts; i++ ) {
+						node = scripts[ i ];
+						if ( rscriptType.test( node.type || "" ) &&
+							!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+							if ( node.src ) {
+								// Hope ajax is available...
+								jQuery._evalUrl( node.src );
+							} else {
+								jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
+							}
+						}
+					}
+				}
+			}
+		}
+
+		return this;
+	}
+});
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function( name, original ) {
+	jQuery.fn[ name ] = function( selector ) {
+		var elems,
+			ret = [],
+			insert = jQuery( selector ),
+			last = insert.length - 1,
+			i = 0;
+
+		for ( ; i <= last; i++ ) {
+			elems = i === last ? this : this.clone( true );
+			jQuery( insert[ i ] )[ original ]( elems );
+
+			// Support: QtWebKit
+			// .get() because core_push.apply(_, arraylike) throws
+			core_push.apply( ret, elems.get() );
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+jQuery.extend({
+	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+		var i, l, srcElements, destElements,
+			clone = elem.cloneNode( true ),
+			inPage = jQuery.contains( elem.ownerDocument, elem );
+
+		// Support: IE >= 9
+		// Fix Cloning issues
+		if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) {
+
+			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+			destElements = getAll( clone );
+			srcElements = getAll( elem );
+
+			for ( i = 0, l = srcElements.length; i < l; i++ ) {
+				fixInput( srcElements[ i ], destElements[ i ] );
+			}
+		}
+
+		// Copy the events from the original to the clone
+		if ( dataAndEvents ) {
+			if ( deepDataAndEvents ) {
+				srcElements = srcElements || getAll( elem );
+				destElements = destElements || getAll( clone );
+
+				for ( i = 0, l = srcElements.length; i < l; i++ ) {
+					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
+				}
+			} else {
+				cloneCopyEvent( elem, clone );
+			}
+		}
+
+		// Preserve script evaluation history
+		destElements = getAll( clone, "script" );
+		if ( destElements.length > 0 ) {
+			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+		}
+
+		// Return the cloned set
+		return clone;
+	},
+
+	buildFragment: function( elems, context, scripts, selection ) {
+		var elem, tmp, tag, wrap, contains, j,
+			i = 0,
+			l = elems.length,
+			fragment = context.createDocumentFragment(),
+			nodes = [];
+
+		for ( ; i < l; i++ ) {
+			elem = elems[ i ];
+
+			if ( elem || elem === 0 ) {
+
+				// Add nodes directly
+				if ( jQuery.type( elem ) === "object" ) {
+					// Support: QtWebKit
+					// jQuery.merge because core_push.apply(_, arraylike) throws
+					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+				// Convert non-html into a text node
+				} else if ( !rhtml.test( elem ) ) {
+					nodes.push( context.createTextNode( elem ) );
+
+				// Convert html into DOM nodes
+				} else {
+					tmp = tmp || fragment.appendChild( context.createElement("div") );
+
+					// Deserialize a standard representation
+					tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase();
+					wrap = wrapMap[ tag ] || wrapMap._default;
+					tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
+
+					// Descend through wrappers to the right content
+					j = wrap[ 0 ];
+					while ( j-- ) {
+						tmp = tmp.lastChild;
+					}
+
+					// Support: QtWebKit
+					// jQuery.merge because core_push.apply(_, arraylike) throws
+					jQuery.merge( nodes, tmp.childNodes );
+
+					// Remember the top-level container
+					tmp = fragment.firstChild;
+
+					// Fixes #12346
+					// Support: Webkit, IE
+					tmp.textContent = "";
+				}
+			}
+		}
+
+		// Remove wrapper from fragment
+		fragment.textContent = "";
+
+		i = 0;
+		while ( (elem = nodes[ i++ ]) ) {
+
+			// #4087 - If origin and destination elements are the same, and this is
+			// that element, do not do anything
+			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+				continue;
+			}
+
+			contains = jQuery.contains( elem.ownerDocument, elem );
+
+			// Append to fragment
+			tmp = getAll( fragment.appendChild( elem ), "script" );
+
+			// Preserve script evaluation history
+			if ( contains ) {
+				setGlobalEval( tmp );
+			}
+
+			// Capture executables
+			if ( scripts ) {
+				j = 0;
+				while ( (elem = tmp[ j++ ]) ) {
+					if ( rscriptType.test( elem.type || "" ) ) {
+						scripts.push( elem );
+					}
+				}
+			}
+		}
+
+		return fragment;
+	},
+
+	cleanData: function( elems ) {
+		var data, elem, events, type, key, j,
+			special = jQuery.event.special,
+			i = 0;
+
+		for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
+			if ( Data.accepts( elem ) ) {
+				key = elem[ data_priv.expando ];
+
+				if ( key && (data = data_priv.cache[ key ]) ) {
+					events = Object.keys( data.events || {} );
+					if ( events.length ) {
+						for ( j = 0; (type = events[j]) !== undefined; j++ ) {
+							if ( special[ type ] ) {
+								jQuery.event.remove( elem, type );
+
+							// This is a shortcut to avoid jQuery.event.remove's overhead
+							} else {
+								jQuery.removeEvent( elem, type, data.handle );
+							}
+						}
+					}
+					if ( data_priv.cache[ key ] ) {
+						// Discard any remaining `private` data
+						delete data_priv.cache[ key ];
+					}
+				}
+			}
+			// Discard any remaining `user` data
+			delete data_user.cache[ elem[ data_user.expando ] ];
+		}
+	},
+
+	_evalUrl: function( url ) {
+		return jQuery.ajax({
+			url: url,
+			type: "GET",
+			dataType: "script",
+			async: false,
+			global: false,
+			"throws": true
+		});
+	}
+});
+
+// Support: 1.x compatibility
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+	return jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ?
+
+		elem.getElementsByTagName("tbody")[0] ||
+			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+		elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+	elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
+	return elem;
+}
+function restoreScript( elem ) {
+	var match = rscriptTypeMasked.exec( elem.type );
+
+	if ( match ) {
+		elem.type = match[ 1 ];
+	} else {
+		elem.removeAttribute("type");
+	}
+
+	return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+	var l = elems.length,
+		i = 0;
+
+	for ( ; i < l; i++ ) {
+		data_priv.set(
+			elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
+		);
+	}
+}
+
+function cloneCopyEvent( src, dest ) {
+	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
+
+	if ( dest.nodeType !== 1 ) {
+		return;
+	}
+
+	// 1. Copy private data: events, handlers, etc.
+	if ( data_priv.hasData( src ) ) {
+		pdataOld = data_priv.access( src );
+		pdataCur = data_priv.set( dest, pdataOld );
+		events = pdataOld.events;
+
+		if ( events ) {
+			delete pdataCur.handle;
+			pdataCur.events = {};
+
+			for ( type in events ) {
+				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+					jQuery.event.add( dest, type, events[ type ][ i ] );
+				}
+			}
+		}
+	}
+
+	// 2. Copy user data
+	if ( data_user.hasData( src ) ) {
+		udataOld = data_user.access( src );
+		udataCur = jQuery.extend( {}, udataOld );
+
+		data_user.set( dest, udataCur );
+	}
+}
+
+
+function getAll( context, tag ) {
+	var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
+			context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
+			[];
+
+	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+		jQuery.merge( [ context ], ret ) :
+		ret;
+}
+
+// Support: IE >= 9
+function fixInput( src, dest ) {
+	var nodeName = dest.nodeName.toLowerCase();
+
+	// Fails to persist the checked state of a cloned checkbox or radio button.
+	if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
+		dest.checked = src.checked;
+
+	// Fails to return the selected option to the default selected state when cloning options
+	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+		dest.defaultValue = src.defaultValue;
+	}
+}
+jQuery.fn.extend({
+	wrapAll: function( html ) {
+		var wrap;
+
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapAll( html.call(this, i) );
+			});
+		}
+
+		if ( this[ 0 ] ) {
+
+			// The elements to wrap the target around
+			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
+
+			if ( this[ 0 ].parentNode ) {
+				wrap.insertBefore( this[ 0 ] );
+			}
+
+			wrap.map(function() {
+				var elem = this;
+
+				while ( elem.firstElementChild ) {
+					elem = elem.firstElementChild;
+				}
+
+				return elem;
+			}).append( this );
+		}
+
+		return this;
+	},
+
+	wrapInner: function( html ) {
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapInner( html.call(this, i) );
+			});
+		}
+
+		return this.each(function() {
+			var self = jQuery( this ),
+				contents = self.contents();
+
+			if ( contents.length ) {
+				contents.wrapAll( html );
+
+			} else {
+				self.append( html );
+			}
+		});
+	},
+
+	wrap: function( html ) {
+		var isFunction = jQuery.isFunction( html );
+
+		return this.each(function( i ) {
+			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+		});
+	},
+
+	unwrap: function() {
+		return this.parent().each(function() {
+			if ( !jQuery.nodeName( this, "body" ) ) {
+				jQuery( this ).replaceWith( this.childNodes );
+			}
+		}).end();
+	}
+});
+var curCSS, iframe,
+	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+	rmargin = /^margin/,
+	rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+	rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
+	elemdisplay = { BODY: "block" },
+
+	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+	cssNormalTransform = {
+		letterSpacing: 0,
+		fontWeight: 400
+	},
+
+	cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+	// shortcut for names that are not vendor prefixed
+	if ( name in style ) {
+		return name;
+	}
+
+	// check for vendor prefixed names
+	var capName = name.charAt(0).toUpperCase() + name.slice(1),
+		origName = name,
+		i = cssPrefixes.length;
+
+	while ( i-- ) {
+		name = cssPrefixes[ i ] + capName;
+		if ( name in style ) {
+			return name;
+		}
+	}
+
+	return origName;
+}
+
+function isHidden( elem, el ) {
+	// isHidden might be called from jQuery#filter function;
+	// in that case, element will be second argument
+	elem = el || elem;
+	return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+// NOTE: we've included the "window" in window.getComputedStyle
+// because jsdom on node.js will break without it.
+function getStyles( elem ) {
+	return window.getComputedStyle( elem, null );
+}
+
+function showHide( elements, show ) {
+	var display, elem, hidden,
+		values = [],
+		index = 0,
+		length = elements.length;
+
+	for ( ; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+
+		values[ index ] = data_priv.get( elem, "olddisplay" );
+		display = elem.style.display;
+		if ( show ) {
+			// Reset the inline display of this element to learn if it is
+			// being hidden by cascaded rules or not
+			if ( !values[ index ] && display === "none" ) {
+				elem.style.display = "";
+			}
+
+			// Set elements which have been overridden with display: none
+			// in a stylesheet to whatever the default browser style is
+			// for such an element
+			if ( elem.style.display === "" && isHidden( elem ) ) {
+				values[ index ] = data_priv.access( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+			}
+		} else {
+
+			if ( !values[ index ] ) {
+				hidden = isHidden( elem );
+
+				if ( display && display !== "none" || !hidden ) {
+					data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") );
+				}
+			}
+		}
+	}
+
+	// Set the display of most of the elements in a second loop
+	// to avoid the constant reflow
+	for ( index = 0; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+			elem.style.display = show ? values[ index ] || "" : "none";
+		}
+	}
+
+	return elements;
+}
+
+jQuery.fn.extend({
+	css: function( name, value ) {
+		return jQuery.access( this, function( elem, name, value ) {
+			var styles, len,
+				map = {},
+				i = 0;
+
+			if ( jQuery.isArray( name ) ) {
+				styles = getStyles( elem );
+				len = name.length;
+
+				for ( ; i < len; i++ ) {
+					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+				}
+
+				return map;
+			}
+
+			return value !== undefined ?
+				jQuery.style( elem, name, value ) :
+				jQuery.css( elem, name );
+		}, name, value, arguments.length > 1 );
+	},
+	show: function() {
+		return showHide( this, true );
+	},
+	hide: function() {
+		return showHide( this );
+	},
+	toggle: function( state ) {
+		if ( typeof state === "boolean" ) {
+			return state ? this.show() : this.hide();
+		}
+
+		return this.each(function() {
+			if ( isHidden( this ) ) {
+				jQuery( this ).show();
+			} else {
+				jQuery( this ).hide();
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	// Add in style property hooks for overriding the default
+	// behavior of getting and setting a style property
+	cssHooks: {
+		opacity: {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// We should always get a number back from opacity
+					var ret = curCSS( elem, "opacity" );
+					return ret === "" ? "1" : ret;
+				}
+			}
+		}
+	},
+
+	// Don't automatically add "px" to these possibly-unitless properties
+	cssNumber: {
+		"columnCount": true,
+		"fillOpacity": true,
+		"fontWeight": true,
+		"lineHeight": true,
+		"opacity": true,
+		"order": true,
+		"orphans": true,
+		"widows": true,
+		"zIndex": true,
+		"zoom": true
+	},
+
+	// Add in properties whose names you wish to fix before
+	// setting or getting the value
+	cssProps: {
+		// normalize float css property
+		"float": "cssFloat"
+	},
+
+	// Get and set the style property on a DOM Node
+	style: function( elem, name, value, extra ) {
+		// Don't set styles on text and comment nodes
+		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+			return;
+		}
+
+		// Make sure that we're working with the right name
+		var ret, type, hooks,
+			origName = jQuery.camelCase( name ),
+			style = elem.style;
+
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// Check if we're setting a value
+		if ( value !== undefined ) {
+			type = typeof value;
+
+			// convert relative number strings (+= or -=) to relative numbers. #7345
+			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+				// Fixes bug #9237
+				type = "number";
+			}
+
+			// Make sure that NaN and null values aren't set. See: #7116
+			if ( value == null || type === "number" && isNaN( value ) ) {
+				return;
+			}
+
+			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+				value += "px";
+			}
+
+			// Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
+			// but it would mean to define eight (for every problematic property) identical functions
+			if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
+				style[ name ] = "inherit";
+			}
+
+			// If a hook was provided, use that value, otherwise just set the specified value
+			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+				style[ name ] = value;
+			}
+
+		} else {
+			// If a hook was provided get the non-computed value from there
+			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+				return ret;
+			}
+
+			// Otherwise just get the value from the style object
+			return style[ name ];
+		}
+	},
+
+	css: function( elem, name, extra, styles ) {
+		var val, num, hooks,
+			origName = jQuery.camelCase( name );
+
+		// Make sure that we're working with the right name
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// If a hook was provided get the computed value from there
+		if ( hooks && "get" in hooks ) {
+			val = hooks.get( elem, true, extra );
+		}
+
+		// Otherwise, if a way to get the computed value exists, use that
+		if ( val === undefined ) {
+			val = curCSS( elem, name, styles );
+		}
+
+		//convert "normal" to computed value
+		if ( val === "normal" && name in cssNormalTransform ) {
+			val = cssNormalTransform[ name ];
+		}
+
+		// Return, converting to number if forced or a qualifier was provided and val looks numeric
+		if ( extra === "" || extra ) {
+			num = parseFloat( val );
+			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+		}
+		return val;
+	}
+});
+
+curCSS = function( elem, name, _computed ) {
+	var width, minWidth, maxWidth,
+		computed = _computed || getStyles( elem ),
+
+		// Support: IE9
+		// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+		ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
+		style = elem.style;
+
+	if ( computed ) {
+
+		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+			ret = jQuery.style( elem, name );
+		}
+
+		// Support: Safari 5.1
+		// A tribute to the "awesome hack by Dean Edwards"
+		// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+		// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+		if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+			// Remember the original values
+			width = style.width;
+			minWidth = style.minWidth;
+			maxWidth = style.maxWidth;
+
+			// Put in the new values to get a computed value out
+			style.minWidth = style.maxWidth = style.width = ret;
+			ret = computed.width;
+
+			// Revert the changed values
+			style.width = width;
+			style.minWidth = minWidth;
+			style.maxWidth = maxWidth;
+		}
+	}
+
+	return ret;
+};
+
+
+function setPositiveNumber( elem, value, subtract ) {
+	var matches = rnumsplit.exec( value );
+	return matches ?
+		// Guard against undefined "subtract", e.g., when used as in cssHooks
+		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+		value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+	var i = extra === ( isBorderBox ? "border" : "content" ) ?
+		// If we already have the right measurement, avoid augmentation
+		4 :
+		// Otherwise initialize for horizontal or vertical properties
+		name === "width" ? 1 : 0,
+
+		val = 0;
+
+	for ( ; i < 4; i += 2 ) {
+		// both box models exclude margin, so add it if we want it
+		if ( extra === "margin" ) {
+			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+		}
+
+		if ( isBorderBox ) {
+			// border-box includes padding, so remove it if we want content
+			if ( extra === "content" ) {
+				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+			}
+
+			// at this point, extra isn't border nor margin, so remove border
+			if ( extra !== "margin" ) {
+				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		} else {
+			// at this point, extra isn't content, so add padding
+			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+			// at this point, extra isn't content nor padding, so add border
+			if ( extra !== "padding" ) {
+				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		}
+	}
+
+	return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+	// Start with offset property, which is equivalent to the border-box value
+	var valueIsBorderBox = true,
+		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+		styles = getStyles( elem ),
+		isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+	// some non-html elements return undefined for offsetWidth, so check for null/undefined
+	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+	if ( val <= 0 || val == null ) {
+		// Fall back to computed then uncomputed css if necessary
+		val = curCSS( elem, name, styles );
+		if ( val < 0 || val == null ) {
+			val = elem.style[ name ];
+		}
+
+		// Computed unit is not pixels. Stop here and return.
+		if ( rnumnonpx.test(val) ) {
+			return val;
+		}
+
+		// we need the check for style in case a browser which returns unreliable values
+		// for getComputedStyle silently falls back to the reliable elem.style
+		valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+		// Normalize "", auto, and prepare for extra
+		val = parseFloat( val ) || 0;
+	}
+
+	// use the active box-sizing model to add/subtract irrelevant styles
+	return ( val +
+		augmentWidthOrHeight(
+			elem,
+			name,
+			extra || ( isBorderBox ? "border" : "content" ),
+			valueIsBorderBox,
+			styles
+		)
+	) + "px";
+}
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+	var doc = document,
+		display = elemdisplay[ nodeName ];
+
+	if ( !display ) {
+		display = actualDisplay( nodeName, doc );
+
+		// If the simple way fails, read from inside an iframe
+		if ( display === "none" || !display ) {
+			// Use the already-created iframe if possible
+			iframe = ( iframe ||
+				jQuery("<iframe frameborder='0' width='0' height='0'/>")
+				.css( "cssText", "display:block !important" )
+			).appendTo( doc.documentElement );
+
+			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+			doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
+			doc.write("<!doctype html><html><body>");
+			doc.close();
+
+			display = actualDisplay( nodeName, doc );
+			iframe.detach();
+		}
+
+		// Store the correct default display
+		elemdisplay[ nodeName ] = display;
+	}
+
+	return display;
+}
+
+// Called ONLY from within css_defaultDisplay
+function actualDisplay( name, doc ) {
+	var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+		display = jQuery.css( elem[0], "display" );
+	elem.remove();
+	return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+	jQuery.cssHooks[ name ] = {
+		get: function( elem, computed, extra ) {
+			if ( computed ) {
+				// certain elements can have dimension info if we invisibly show them
+				// however, it must have a current display style that would benefit from this
+				return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
+					jQuery.swap( elem, cssShow, function() {
+						return getWidthOrHeight( elem, name, extra );
+					}) :
+					getWidthOrHeight( elem, name, extra );
+			}
+		},
+
+		set: function( elem, value, extra ) {
+			var styles = extra && getStyles( elem );
+			return setPositiveNumber( elem, value, extra ?
+				augmentWidthOrHeight(
+					elem,
+					name,
+					extra,
+					jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+					styles
+				) : 0
+			);
+		}
+	};
+});
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+	// Support: Android 2.3
+	if ( !jQuery.support.reliableMarginRight ) {
+		jQuery.cssHooks.marginRight = {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// Support: Android 2.3
+					// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+					// Work around by temporarily setting element display to inline-block
+					return jQuery.swap( elem, { "display": "inline-block" },
+						curCSS, [ elem, "marginRight" ] );
+				}
+			}
+		};
+	}
+
+	// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+	// getComputedStyle returns percent when specified for top/left/bottom/right
+	// rather than make the css module depend on the offset module, we just check for it here
+	if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+		jQuery.each( [ "top", "left" ], function( i, prop ) {
+			jQuery.cssHooks[ prop ] = {
+				get: function( elem, computed ) {
+					if ( computed ) {
+						computed = curCSS( elem, prop );
+						// if curCSS returns percentage, fallback to offset
+						return rnumnonpx.test( computed ) ?
+							jQuery( elem ).position()[ prop ] + "px" :
+							computed;
+					}
+				}
+			};
+		});
+	}
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	jQuery.expr.filters.hidden = function( elem ) {
+		// Support: Opera <= 12.12
+		// Opera reports offsetWidths and offsetHeights less than zero on some elements
+		return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
+	};
+
+	jQuery.expr.filters.visible = function( elem ) {
+		return !jQuery.expr.filters.hidden( elem );
+	};
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+	margin: "",
+	padding: "",
+	border: "Width"
+}, function( prefix, suffix ) {
+	jQuery.cssHooks[ prefix + suffix ] = {
+		expand: function( value ) {
+			var i = 0,
+				expanded = {},
+
+				// assumes a single number if not a string
+				parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+			for ( ; i < 4; i++ ) {
+				expanded[ prefix + cssExpand[ i ] + suffix ] =
+					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+			}
+
+			return expanded;
+		}
+	};
+
+	if ( !rmargin.test( prefix ) ) {
+		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+	}
+});
+var r20 = /%20/g,
+	rbracket = /\[\]$/,
+	rCRLF = /\r?\n/g,
+	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+jQuery.fn.extend({
+	serialize: function() {
+		return jQuery.param( this.serializeArray() );
+	},
+	serializeArray: function() {
+		return this.map(function(){
+			// Can add propHook for "elements" to filter or add form elements
+			var elements = jQuery.prop( this, "elements" );
+			return elements ? jQuery.makeArray( elements ) : this;
+		})
+		.filter(function(){
+			var type = this.type;
+			// Use .is(":disabled") so that fieldset[disabled] works
+			return this.name && !jQuery( this ).is( ":disabled" ) &&
+				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+				( this.checked || !manipulation_rcheckableType.test( type ) );
+		})
+		.map(function( i, elem ){
+			var val = jQuery( this ).val();
+
+			return val == null ?
+				null :
+				jQuery.isArray( val ) ?
+					jQuery.map( val, function( val ){
+						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+					}) :
+					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+		}).get();
+	}
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+	var prefix,
+		s = [],
+		add = function( key, value ) {
+			// If value is a function, invoke it and return its value
+			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+		};
+
+	// Set traditional to true for jQuery <= 1.3.2 behavior.
+	if ( traditional === undefined ) {
+		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+	}
+
+	// If an array was passed in, assume that it is an array of form elements.
+	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+		// Serialize the form elements
+		jQuery.each( a, function() {
+			add( this.name, this.value );
+		});
+
+	} else {
+		// If traditional, encode the "old" way (the way 1.3.2 or older
+		// did it), otherwise encode params recursively.
+		for ( prefix in a ) {
+			buildParams( prefix, a[ prefix ], traditional, add );
+		}
+	}
+
+	// Return the resulting serialization
+	return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+	var name;
+
+	if ( jQuery.isArray( obj ) ) {
+		// Serialize array item.
+		jQuery.each( obj, function( i, v ) {
+			if ( traditional || rbracket.test( prefix ) ) {
+				// Treat each array item as a scalar.
+				add( prefix, v );
+
+			} else {
+				// Item is non-scalar (array or object), encode its numeric index.
+				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+			}
+		});
+
+	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
+		// Serialize object item.
+		for ( name in obj ) {
+			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+		}
+
+	} else {
+		// Serialize scalar item.
+		add( prefix, obj );
+	}
+}
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+	// Handle event binding
+	jQuery.fn[ name ] = function( data, fn ) {
+		return arguments.length > 0 ?
+			this.on( name, null, data, fn ) :
+			this.trigger( name );
+	};
+});
+
+jQuery.fn.extend({
+	hover: function( fnOver, fnOut ) {
+		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+	},
+
+	bind: function( types, data, fn ) {
+		return this.on( types, null, data, fn );
+	},
+	unbind: function( types, fn ) {
+		return this.off( types, null, fn );
+	},
+
+	delegate: function( selector, types, data, fn ) {
+		return this.on( types, selector, data, fn );
+	},
+	undelegate: function( selector, types, fn ) {
+		// ( namespace ) or ( selector, types [, fn] )
+		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+	}
+});
+var
+	// Document location
+	ajaxLocParts,
+	ajaxLocation,
+
+	ajax_nonce = jQuery.now(),
+
+	ajax_rquery = /\?/,
+	rhash = /#.*$/,
+	rts = /([?&])_=[^&]*/,
+	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
+	// #7653, #8125, #8152: local protocol detection
+	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+	rnoContent = /^(?:GET|HEAD)$/,
+	rprotocol = /^\/\//,
+	rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+	// Keep a copy of the old load method
+	_load = jQuery.fn.load,
+
+	/* Prefilters
+	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+	 * 2) These are called:
+	 *    - BEFORE asking for a transport
+	 *    - AFTER param serialization (s.data is a string if s.processData is true)
+	 * 3) key is the dataType
+	 * 4) the catchall symbol "*" can be used
+	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+	 */
+	prefilters = {},
+
+	/* Transports bindings
+	 * 1) key is the dataType
+	 * 2) the catchall symbol "*" can be used
+	 * 3) selection will start with transport dataType and THEN go to "*" if needed
+	 */
+	transports = {},
+
+	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+	allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+	ajaxLocation = location.href;
+} catch( e ) {
+	// Use the href attribute of an A element
+	// since IE will modify it given document.location
+	ajaxLocation = document.createElement( "a" );
+	ajaxLocation.href = "";
+	ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+	// dataTypeExpression is optional and defaults to "*"
+	return function( dataTypeExpression, func ) {
+
+		if ( typeof dataTypeExpression !== "string" ) {
+			func = dataTypeExpression;
+			dataTypeExpression = "*";
+		}
+
+		var dataType,
+			i = 0,
+			dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
+
+		if ( jQuery.isFunction( func ) ) {
+			// For each dataType in the dataTypeExpression
+			while ( (dataType = dataTypes[i++]) ) {
+				// Prepend if requested
+				if ( dataType[0] === "+" ) {
+					dataType = dataType.slice( 1 ) || "*";
+					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+				// Otherwise append
+				} else {
+					(structure[ dataType ] = structure[ dataType ] || []).push( func );
+				}
+			}
+		}
+	};
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+	var inspected = {},
+		seekingTransport = ( structure === transports );
+
+	function inspect( dataType ) {
+		var selected;
+		inspected[ dataType ] = true;
+		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+			if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+				options.dataTypes.unshift( dataTypeOrTransport );
+				inspect( dataTypeOrTransport );
+				return false;
+			} else if ( seekingTransport ) {
+				return !( selected = dataTypeOrTransport );
+			}
+		});
+		return selected;
+	}
+
+	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+	var key, deep,
+		flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+	for ( key in src ) {
+		if ( src[ key ] !== undefined ) {
+			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+		}
+	}
+	if ( deep ) {
+		jQuery.extend( true, target, deep );
+	}
+
+	return target;
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+	if ( typeof url !== "string" && _load ) {
+		return _load.apply( this, arguments );
+	}
+
+	var selector, type, response,
+		self = this,
+		off = url.indexOf(" ");
+
+	if ( off >= 0 ) {
+		selector = url.slice( off );
+		url = url.slice( 0, off );
+	}
+
+	// If it's a function
+	if ( jQuery.isFunction( params ) ) {
+
+		// We assume that it's the callback
+		callback = params;
+		params = undefined;
+
+	// Otherwise, build a param string
+	} else if ( params && typeof params === "object" ) {
+		type = "POST";
+	}
+
+	// If we have elements to modify, make the request
+	if ( self.length > 0 ) {
+		jQuery.ajax({
+			url: url,
+
+			// if "type" variable is undefined, then "GET" method will be used
+			type: type,
+			dataType: "html",
+			data: params
+		}).done(function( responseText ) {
+
+			// Save response for use in complete callback
+			response = arguments;
+
+			self.html( selector ?
+
+				// If a selector was specified, locate the right elements in a dummy div
+				// Exclude scripts to avoid IE 'Permission Denied' errors
+				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+				// Otherwise use the full result
+				responseText );
+
+		}).complete( callback && function( jqXHR, status ) {
+			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+		});
+	}
+
+	return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
+	jQuery.fn[ type ] = function( fn ){
+		return this.on( type, fn );
+	};
+});
+
+jQuery.extend({
+
+	// Counter for holding the number of active queries
+	active: 0,
+
+	// Last-Modified header cache for next request
+	lastModified: {},
+	etag: {},
+
+	ajaxSettings: {
+		url: ajaxLocation,
+		type: "GET",
+		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+		global: true,
+		processData: true,
+		async: true,
+		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+		/*
+		timeout: 0,
+		data: null,
+		dataType: null,
+		username: null,
+		password: null,
+		cache: null,
+		throws: false,
+		traditional: false,
+		headers: {},
+		*/
+
+		accepts: {
+			"*": allTypes,
+			text: "text/plain",
+			html: "text/html",
+			xml: "application/xml, text/xml",
+			json: "application/json, text/javascript"
+		},
+
+		contents: {
+			xml: /xml/,
+			html: /html/,
+			json: /json/
+		},
+
+		responseFields: {
+			xml: "responseXML",
+			text: "responseText",
+			json: "responseJSON"
+		},
+
+		// Data converters
+		// Keys separate source (or catchall "*") and destination types with a single space
+		converters: {
+
+			// Convert anything to text
+			"* text": String,
+
+			// Text to html (true = no transformation)
+			"text html": true,
+
+			// Evaluate text as a json expression
+			"text json": jQuery.parseJSON,
+
+			// Parse text as xml
+			"text xml": jQuery.parseXML
+		},
+
+		// For options that shouldn't be deep extended:
+		// you can add your own custom options here if
+		// and when you create one that shouldn't be
+		// deep extended (see ajaxExtend)
+		flatOptions: {
+			url: true,
+			context: true
+		}
+	},
+
+	// Creates a full fledged settings object into target
+	// with both ajaxSettings and settings fields.
+	// If target is omitted, writes into ajaxSettings.
+	ajaxSetup: function( target, settings ) {
+		return settings ?
+
+			// Building a settings object
+			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+			// Extending ajaxSettings
+			ajaxExtend( jQuery.ajaxSettings, target );
+	},
+
+	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+	ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+	// Main method
+	ajax: function( url, options ) {
+
+		// If url is an object, simulate pre-1.5 signature
+		if ( typeof url === "object" ) {
+			options = url;
+			url = undefined;
+		}
+
+		// Force options to be an object
+		options = options || {};
+
+		var transport,
+			// URL without anti-cache param
+			cacheURL,
+			// Response headers
+			responseHeadersString,
+			responseHeaders,
+			// timeout handle
+			timeoutTimer,
+			// Cross-domain detection vars
+			parts,
+			// To know if global events are to be dispatched
+			fireGlobals,
+			// Loop variable
+			i,
+			// Create the final options object
+			s = jQuery.ajaxSetup( {}, options ),
+			// Callbacks context
+			callbackContext = s.context || s,
+			// Context for global events is callbackContext if it is a DOM node or jQuery collection
+			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+				jQuery( callbackContext ) :
+				jQuery.event,
+			// Deferreds
+			deferred = jQuery.Deferred(),
+			completeDeferred = jQuery.Callbacks("once memory"),
+			// Status-dependent callbacks
+			statusCode = s.statusCode || {},
+			// Headers (they are sent all at once)
+			requestHeaders = {},
+			requestHeadersNames = {},
+			// The jqXHR state
+			state = 0,
+			// Default abort message
+			strAbort = "canceled",
+			// Fake xhr
+			jqXHR = {
+				readyState: 0,
+
+				// Builds headers hashtable if needed
+				getResponseHeader: function( key ) {
+					var match;
+					if ( state === 2 ) {
+						if ( !responseHeaders ) {
+							responseHeaders = {};
+							while ( (match = rheaders.exec( responseHeadersString )) ) {
+								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+							}
+						}
+						match = responseHeaders[ key.toLowerCase() ];
+					}
+					return match == null ? null : match;
+				},
+
+				// Raw string
+				getAllResponseHeaders: function() {
+					return state === 2 ? responseHeadersString : null;
+				},
+
+				// Caches the header
+				setRequestHeader: function( name, value ) {
+					var lname = name.toLowerCase();
+					if ( !state ) {
+						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+						requestHeaders[ name ] = value;
+					}
+					return this;
+				},
+
+				// Overrides response content-type header
+				overrideMimeType: function( type ) {
+					if ( !state ) {
+						s.mimeType = type;
+					}
+					return this;
+				},
+
+				// Status-dependent callbacks
+				statusCode: function( map ) {
+					var code;
+					if ( map ) {
+						if ( state < 2 ) {
+							for ( code in map ) {
+								// Lazy-add the new callback in a way that preserves old ones
+								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+							}
+						} else {
+							// Execute the appropriate callbacks
+							jqXHR.always( map[ jqXHR.status ] );
+						}
+					}
+					return this;
+				},
+
+				// Cancel the request
+				abort: function( statusText ) {
+					var finalText = statusText || strAbort;
+					if ( transport ) {
+						transport.abort( finalText );
+					}
+					done( 0, finalText );
+					return this;
+				}
+			};
+
+		// Attach deferreds
+		deferred.promise( jqXHR ).complete = completeDeferred.add;
+		jqXHR.success = jqXHR.done;
+		jqXHR.error = jqXHR.fail;
+
+		// Remove hash character (#7531: and string promotion)
+		// Add protocol if not provided (prefilters might expect it)
+		// Handle falsy url in the settings object (#10093: consistency with old signature)
+		// We also use the url parameter if available
+		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
+			.replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+		// Alias method option to type as per ticket #12004
+		s.type = options.method || options.type || s.method || s.type;
+
+		// Extract dataTypes list
+		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
+
+		// A cross-domain request is in order when we have a protocol:host:port mismatch
+		if ( s.crossDomain == null ) {
+			parts = rurl.exec( s.url.toLowerCase() );
+			s.crossDomain = !!( parts &&
+				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+			);
+		}
+
+		// Convert data if not already a string
+		if ( s.data && s.processData && typeof s.data !== "string" ) {
+			s.data = jQuery.param( s.data, s.traditional );
+		}
+
+		// Apply prefilters
+		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+		// If request was aborted inside a prefilter, stop there
+		if ( state === 2 ) {
+			return jqXHR;
+		}
+
+		// We can fire global events as of now if asked to
+		fireGlobals = s.global;
+
+		// Watch for a new set of requests
+		if ( fireGlobals && jQuery.active++ === 0 ) {
+			jQuery.event.trigger("ajaxStart");
+		}
+
+		// Uppercase the type
+		s.type = s.type.toUpperCase();
+
+		// Determine if request has content
+		s.hasContent = !rnoContent.test( s.type );
+
+		// Save the URL in case we're toying with the If-Modified-Since
+		// and/or If-None-Match header later on
+		cacheURL = s.url;
+
+		// More options handling for requests with no content
+		if ( !s.hasContent ) {
+
+			// If data is available, append data to url
+			if ( s.data ) {
+				cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+				// #9682: remove data so that it's not used in an eventual retry
+				delete s.data;
+			}
+
+			// Add anti-cache in url if needed
+			if ( s.cache === false ) {
+				s.url = rts.test( cacheURL ) ?
+
+					// If there is already a '_' parameter, set its value
+					cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
+
+					// Otherwise add one to the end
+					cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
+			}
+		}
+
+		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+		if ( s.ifModified ) {
+			if ( jQuery.lastModified[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+			}
+			if ( jQuery.etag[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+			}
+		}
+
+		// Set the correct header, if data is being sent
+		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+			jqXHR.setRequestHeader( "Content-Type", s.contentType );
+		}
+
+		// Set the Accepts header for the server, depending on the dataType
+		jqXHR.setRequestHeader(
+			"Accept",
+			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+				s.accepts[ "*" ]
+		);
+
+		// Check for headers option
+		for ( i in s.headers ) {
+			jqXHR.setRequestHeader( i, s.headers[ i ] );
+		}
+
+		// Allow custom headers/mimetypes and early abort
+		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+			// Abort if not done already and return
+			return jqXHR.abort();
+		}
+
+		// aborting is no longer a cancellation
+		strAbort = "abort";
+
+		// Install callbacks on deferreds
+		for ( i in { success: 1, error: 1, complete: 1 } ) {
+			jqXHR[ i ]( s[ i ] );
+		}
+
+		// Get transport
+		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+		// If no transport, we auto-abort
+		if ( !transport ) {
+			done( -1, "No Transport" );
+		} else {
+			jqXHR.readyState = 1;
+
+			// Send global event
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+			}
+			// Timeout
+			if ( s.async && s.timeout > 0 ) {
+				timeoutTimer = setTimeout(function() {
+					jqXHR.abort("timeout");
+				}, s.timeout );
+			}
+
+			try {
+				state = 1;
+				transport.send( requestHeaders, done );
+			} catch ( e ) {
+				// Propagate exception as error if not done
+				if ( state < 2 ) {
+					done( -1, e );
+				// Simply rethrow otherwise
+				} else {
+					throw e;
+				}
+			}
+		}
+
+		// Callback for when everything is done
+		function done( status, nativeStatusText, responses, headers ) {
+			var isSuccess, success, error, response, modified,
+				statusText = nativeStatusText;
+
+			// Called once
+			if ( state === 2 ) {
+				return;
+			}
+
+			// State is "done" now
+			state = 2;
+
+			// Clear timeout if it exists
+			if ( timeoutTimer ) {
+				clearTimeout( timeoutTimer );
+			}
+
+			// Dereference transport for early garbage collection
+			// (no matter how long the jqXHR object will be used)
+			transport = undefined;
+
+			// Cache response headers
+			responseHeadersString = headers || "";
+
+			// Set readyState
+			jqXHR.readyState = status > 0 ? 4 : 0;
+
+			// Determine if successful
+			isSuccess = status >= 200 && status < 300 || status === 304;
+
+			// Get response data
+			if ( responses ) {
+				response = ajaxHandleResponses( s, jqXHR, responses );
+			}
+
+			// Convert no matter what (that way responseXXX fields are always set)
+			response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+			// If successful, handle type chaining
+			if ( isSuccess ) {
+
+				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+				if ( s.ifModified ) {
+					modified = jqXHR.getResponseHeader("Last-Modified");
+					if ( modified ) {
+						jQuery.lastModified[ cacheURL ] = modified;
+					}
+					modified = jqXHR.getResponseHeader("etag");
+					if ( modified ) {
+						jQuery.etag[ cacheURL ] = modified;
+					}
+				}
+
+				// if no content
+				if ( status === 204 || s.type === "HEAD" ) {
+					statusText = "nocontent";
+
+				// if not modified
+				} else if ( status === 304 ) {
+					statusText = "notmodified";
+
+				// If we have data, let's convert it
+				} else {
+					statusText = response.state;
+					success = response.data;
+					error = response.error;
+					isSuccess = !error;
+				}
+			} else {
+				// We extract error from statusText
+				// then normalize statusText and status for non-aborts
+				error = statusText;
+				if ( status || !statusText ) {
+					statusText = "error";
+					if ( status < 0 ) {
+						status = 0;
+					}
+				}
+			}
+
+			// Set data for the fake xhr object
+			jqXHR.status = status;
+			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+			// Success/Error
+			if ( isSuccess ) {
+				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+			} else {
+				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+			}
+
+			// Status-dependent callbacks
+			jqXHR.statusCode( statusCode );
+			statusCode = undefined;
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+					[ jqXHR, s, isSuccess ? success : error ] );
+			}
+
+			// Complete
+			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+				// Handle the global AJAX counter
+				if ( !( --jQuery.active ) ) {
+					jQuery.event.trigger("ajaxStop");
+				}
+			}
+		}
+
+		return jqXHR;
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get( url, data, callback, "json" );
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get( url, undefined, callback, "script" );
+	}
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+	jQuery[ method ] = function( url, data, callback, type ) {
+		// shift arguments if data argument was omitted
+		if ( jQuery.isFunction( data ) ) {
+			type = type || callback;
+			callback = data;
+			data = undefined;
+		}
+
+		return jQuery.ajax({
+			url: url,
+			type: method,
+			dataType: type,
+			data: data,
+			success: callback
+		});
+	};
+});
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+	var ct, type, finalDataType, firstDataType,
+		contents = s.contents,
+		dataTypes = s.dataTypes;
+
+	// Remove auto dataType and get content-type in the process
+	while( dataTypes[ 0 ] === "*" ) {
+		dataTypes.shift();
+		if ( ct === undefined ) {
+			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+		}
+	}
+
+	// Check if we're dealing with a known content-type
+	if ( ct ) {
+		for ( type in contents ) {
+			if ( contents[ type ] && contents[ type ].test( ct ) ) {
+				dataTypes.unshift( type );
+				break;
+			}
+		}
+	}
+
+	// Check to see if we have a response for the expected dataType
+	if ( dataTypes[ 0 ] in responses ) {
+		finalDataType = dataTypes[ 0 ];
+	} else {
+		// Try convertible dataTypes
+		for ( type in responses ) {
+			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+				finalDataType = type;
+				break;
+			}
+			if ( !firstDataType ) {
+				firstDataType = type;
+			}
+		}
+		// Or just use first one
+		finalDataType = finalDataType || firstDataType;
+	}
+
+	// If we found a dataType
+	// We add the dataType to the list if needed
+	// and return the corresponding response
+	if ( finalDataType ) {
+		if ( finalDataType !== dataTypes[ 0 ] ) {
+			dataTypes.unshift( finalDataType );
+		}
+		return responses[ finalDataType ];
+	}
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+	var conv2, current, conv, tmp, prev,
+		converters = {},
+		// Work with a copy of dataTypes in case we need to modify it for conversion
+		dataTypes = s.dataTypes.slice();
+
+	// Create converters map with lowercased keys
+	if ( dataTypes[ 1 ] ) {
+		for ( conv in s.converters ) {
+			converters[ conv.toLowerCase() ] = s.converters[ conv ];
+		}
+	}
+
+	current = dataTypes.shift();
+
+	// Convert to each sequential dataType
+	while ( current ) {
+
+		if ( s.responseFields[ current ] ) {
+			jqXHR[ s.responseFields[ current ] ] = response;
+		}
+
+		// Apply the dataFilter if provided
+		if ( !prev && isSuccess && s.dataFilter ) {
+			response = s.dataFilter( response, s.dataType );
+		}
+
+		prev = current;
+		current = dataTypes.shift();
+
+		if ( current ) {
+
+		// There's only work to do if current dataType is non-auto
+			if ( current === "*" ) {
+
+				current = prev;
+
+			// Convert response if prev dataType is non-auto and differs from current
+			} else if ( prev !== "*" && prev !== current ) {
+
+				// Seek a direct converter
+				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+				// If none found, seek a pair
+				if ( !conv ) {
+					for ( conv2 in converters ) {
+
+						// If conv2 outputs current
+						tmp = conv2.split( " " );
+						if ( tmp[ 1 ] === current ) {
+
+							// If prev can be converted to accepted input
+							conv = converters[ prev + " " + tmp[ 0 ] ] ||
+								converters[ "* " + tmp[ 0 ] ];
+							if ( conv ) {
+								// Condense equivalence converters
+								if ( conv === true ) {
+									conv = converters[ conv2 ];
+
+								// Otherwise, insert the intermediate dataType
+								} else if ( converters[ conv2 ] !== true ) {
+									current = tmp[ 0 ];
+									dataTypes.unshift( tmp[ 1 ] );
+								}
+								break;
+							}
+						}
+					}
+				}
+
+				// Apply converter (if not an equivalence)
+				if ( conv !== true ) {
+
+					// Unless errors are allowed to bubble, catch and return them
+					if ( conv && s[ "throws" ] ) {
+						response = conv( response );
+					} else {
+						try {
+							response = conv( response );
+						} catch ( e ) {
+							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return { state: "success", data: response };
+}
+// Install script dataType
+jQuery.ajaxSetup({
+	accepts: {
+		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+	},
+	contents: {
+		script: /(?:java|ecma)script/
+	},
+	converters: {
+		"text script": function( text ) {
+			jQuery.globalEval( text );
+			return text;
+		}
+	}
+});
+
+// Handle cache's special case and crossDomain
+jQuery.ajaxPrefilter( "script", function( s ) {
+	if ( s.cache === undefined ) {
+		s.cache = false;
+	}
+	if ( s.crossDomain ) {
+		s.type = "GET";
+	}
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function( s ) {
+	// This transport only deals with cross domain requests
+	if ( s.crossDomain ) {
+		var script, callback;
+		return {
+			send: function( _, complete ) {
+				script = jQuery("<script>").prop({
+					async: true,
+					charset: s.scriptCharset,
+					src: s.url
+				}).on(
+					"load error",
+					callback = function( evt ) {
+						script.remove();
+						callback = null;
+						if ( evt ) {
+							complete( evt.type === "error" ? 404 : 200, evt.type );
+						}
+					}
+				);
+				document.head.appendChild( script[ 0 ] );
+			},
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+var oldCallbacks = [],
+	rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+	jsonp: "callback",
+	jsonpCallback: function() {
+		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
+		this[ callback ] = true;
+		return callback;
+	}
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+	var callbackName, overwritten, responseContainer,
+		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+			"url" :
+			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+		);
+
+	// Handle iff the expected data type is "jsonp" or we have a parameter to set
+	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+		// Get callback name, remembering preexisting value associated with it
+		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+			s.jsonpCallback() :
+			s.jsonpCallback;
+
+		// Insert callback into url or form data
+		if ( jsonProp ) {
+			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+		} else if ( s.jsonp !== false ) {
+			s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+		}
+
+		// Use data converter to retrieve json after script execution
+		s.converters["script json"] = function() {
+			if ( !responseContainer ) {
+				jQuery.error( callbackName + " was not called" );
+			}
+			return responseContainer[ 0 ];
+		};
+
+		// force json dataType
+		s.dataTypes[ 0 ] = "json";
+
+		// Install callback
+		overwritten = window[ callbackName ];
+		window[ callbackName ] = function() {
+			responseContainer = arguments;
+		};
+
+		// Clean-up function (fires after converters)
+		jqXHR.always(function() {
+			// Restore preexisting value
+			window[ callbackName ] = overwritten;
+
+			// Save back as free
+			if ( s[ callbackName ] ) {
+				// make sure that re-using the options doesn't screw things around
+				s.jsonpCallback = originalSettings.jsonpCallback;
+
+				// save the callback name for future use
+				oldCallbacks.push( callbackName );
+			}
+
+			// Call if it was a function and we have a response
+			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+				overwritten( responseContainer[ 0 ] );
+			}
+
+			responseContainer = overwritten = undefined;
+		});
+
+		// Delegate to script
+		return "script";
+	}
+});
+jQuery.ajaxSettings.xhr = function() {
+	try {
+		return new XMLHttpRequest();
+	} catch( e ) {}
+};
+
+var xhrSupported = jQuery.ajaxSettings.xhr(),
+	xhrSuccessStatus = {
+		// file protocol always yields status code 0, assume 200
+		0: 200,
+		// Support: IE9
+		// #1450: sometimes IE returns 1223 when it should be 204
+		1223: 204
+	},
+	// Support: IE9
+	// We need to keep track of outbound xhr and abort them manually
+	// because IE is not smart enough to do it all by itself
+	xhrId = 0,
+	xhrCallbacks = {};
+
+if ( window.ActiveXObject ) {
+	jQuery( window ).on( "unload", function() {
+		for( var key in xhrCallbacks ) {
+			xhrCallbacks[ key ]();
+		}
+		xhrCallbacks = undefined;
+	});
+}
+
+jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+jQuery.support.ajax = xhrSupported = !!xhrSupported;
+
+jQuery.ajaxTransport(function( options ) {
+	var callback;
+	// Cross domain only allowed if supported through XMLHttpRequest
+	if ( jQuery.support.cors || xhrSupported && !options.crossDomain ) {
+		return {
+			send: function( headers, complete ) {
+				var i, id,
+					xhr = options.xhr();
+				xhr.open( options.type, options.url, options.async, options.username, options.password );
+				// Apply custom fields if provided
+				if ( options.xhrFields ) {
+					for ( i in options.xhrFields ) {
+						xhr[ i ] = options.xhrFields[ i ];
+					}
+				}
+				// Override mime type if needed
+				if ( options.mimeType && xhr.overrideMimeType ) {
+					xhr.overrideMimeType( options.mimeType );
+				}
+				// X-Requested-With header
+				// For cross-domain requests, seeing as conditions for a preflight are
+				// akin to a jigsaw puzzle, we simply never set it to be sure.
+				// (it can always be set on a per-request basis or even using ajaxSetup)
+				// For same-domain requests, won't change header if already provided.
+				if ( !options.crossDomain && !headers["X-Requested-With"] ) {
+					headers["X-Requested-With"] = "XMLHttpRequest";
+				}
+				// Set headers
+				for ( i in headers ) {
+					xhr.setRequestHeader( i, headers[ i ] );
+				}
+				// Callback
+				callback = function( type ) {
+					return function() {
+						if ( callback ) {
+							delete xhrCallbacks[ id ];
+							callback = xhr.onload = xhr.onerror = null;
+							if ( type === "abort" ) {
+								xhr.abort();
+							} else if ( type === "error" ) {
+								complete(
+									// file protocol always yields status 0, assume 404
+									xhr.status || 404,
+									xhr.statusText
+								);
+							} else {
+								complete(
+									xhrSuccessStatus[ xhr.status ] || xhr.status,
+									xhr.statusText,
+									// Support: IE9
+									// #11426: When requesting binary data, IE9 will throw an exception
+									// on any attempt to access responseText
+									typeof xhr.responseText === "string" ? {
+										text: xhr.responseText
+									} : undefined,
+									xhr.getAllResponseHeaders()
+								);
+							}
+						}
+					};
+				};
+				// Listen to events
+				xhr.onload = callback();
+				xhr.onerror = callback("error");
+				// Create the abort callback
+				callback = xhrCallbacks[( id = xhrId++ )] = callback("abort");
+				// Do send the request
+				// This may raise an exception which is actually
+				// handled in jQuery.ajax (so no try/catch here)
+				xhr.send( options.hasContent && options.data || null );
+			},
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+var fxNow, timerId,
+	rfxtypes = /^(?:toggle|show|hide)$/,
+	rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+	rrun = /queueHooks$/,
+	animationPrefilters = [ defaultPrefilter ],
+	tweeners = {
+		"*": [function( prop, value ) {
+			var tween = this.createTween( prop, value ),
+				target = tween.cur(),
+				parts = rfxnum.exec( value ),
+				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+				// Starting value computation is required for potential unit mismatches
+				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+				scale = 1,
+				maxIterations = 20;
+
+			if ( start && start[ 3 ] !== unit ) {
+				// Trust units reported by jQuery.css
+				unit = unit || start[ 3 ];
+
+				// Make sure we update the tween properties later on
+				parts = parts || [];
+
+				// Iteratively approximate from a nonzero starting point
+				start = +target || 1;
+
+				do {
+					// If previous iteration zeroed out, double until we get *something*
+					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
+					scale = scale || ".5";
+
+					// Adjust and apply
+					start = start / scale;
+					jQuery.style( tween.elem, prop, start + unit );
+
+				// Update scale, tolerating zero or NaN from tween.cur()
+				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+			}
+
+			// Update tween properties
+			if ( parts ) {
+				start = tween.start = +start || +target || 0;
+				tween.unit = unit;
+				// If a +=/-= token was provided, we're doing a relative animation
+				tween.end = parts[ 1 ] ?
+					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+					+parts[ 2 ];
+			}
+
+			return tween;
+		}]
+	};
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+	setTimeout(function() {
+		fxNow = undefined;
+	});
+	return ( fxNow = jQuery.now() );
+}
+
+function createTween( value, prop, animation ) {
+	var tween,
+		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+		index = 0,
+		length = collection.length;
+	for ( ; index < length; index++ ) {
+		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+			// we're done with this property
+			return tween;
+		}
+	}
+}
+
+function Animation( elem, properties, options ) {
+	var result,
+		stopped,
+		index = 0,
+		length = animationPrefilters.length,
+		deferred = jQuery.Deferred().always( function() {
+			// don't match elem in the :animated selector
+			delete tick.elem;
+		}),
+		tick = function() {
+			if ( stopped ) {
+				return false;
+			}
+			var currentTime = fxNow || createFxNow(),
+				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+				temp = remaining / animation.duration || 0,
+				percent = 1 - temp,
+				index = 0,
+				length = animation.tweens.length;
+
+			for ( ; index < length ; index++ ) {
+				animation.tweens[ index ].run( percent );
+			}
+
+			deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+			if ( percent < 1 && length ) {
+				return remaining;
+			} else {
+				deferred.resolveWith( elem, [ animation ] );
+				return false;
+			}
+		},
+		animation = deferred.promise({
+			elem: elem,
+			props: jQuery.extend( {}, properties ),
+			opts: jQuery.extend( true, { specialEasing: {} }, options ),
+			originalProperties: properties,
+			originalOptions: options,
+			startTime: fxNow || createFxNow(),
+			duration: options.duration,
+			tweens: [],
+			createTween: function( prop, end ) {
+				var tween = jQuery.Tween( elem, animation.opts, prop, end,
+						animation.opts.specialEasing[ prop ] || animation.opts.easing );
+				animation.tweens.push( tween );
+				return tween;
+			},
+			stop: function( gotoEnd ) {
+				var index = 0,
+					// if we are going to the end, we want to run all the tweens
+					// otherwise we skip this part
+					length = gotoEnd ? animation.tweens.length : 0;
+				if ( stopped ) {
+					return this;
+				}
+				stopped = true;
+				for ( ; index < length ; index++ ) {
+					animation.tweens[ index ].run( 1 );
+				}
+
+				// resolve when we played the last frame
+				// otherwise, reject
+				if ( gotoEnd ) {
+					deferred.resolveWith( elem, [ animation, gotoEnd ] );
+				} else {
+					deferred.rejectWith( elem, [ animation, gotoEnd ] );
+				}
+				return this;
+			}
+		}),
+		props = animation.props;
+
+	propFilter( props, animation.opts.specialEasing );
+
+	for ( ; index < length ; index++ ) {
+		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+		if ( result ) {
+			return result;
+		}
+	}
+
+	jQuery.map( props, createTween, animation );
+
+	if ( jQuery.isFunction( animation.opts.start ) ) {
+		animation.opts.start.call( elem, animation );
+	}
+
+	jQuery.fx.timer(
+		jQuery.extend( tick, {
+			elem: elem,
+			anim: animation,
+			queue: animation.opts.queue
+		})
+	);
+
+	// attach callbacks from options
+	return animation.progress( animation.opts.progress )
+		.done( animation.opts.done, animation.opts.complete )
+		.fail( animation.opts.fail )
+		.always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+	var index, name, easing, value, hooks;
+
+	// camelCase, specialEasing and expand cssHook pass
+	for ( index in props ) {
+		name = jQuery.camelCase( index );
+		easing = specialEasing[ name ];
+		value = props[ index ];
+		if ( jQuery.isArray( value ) ) {
+			easing = value[ 1 ];
+			value = props[ index ] = value[ 0 ];
+		}
+
+		if ( index !== name ) {
+			props[ name ] = value;
+			delete props[ index ];
+		}
+
+		hooks = jQuery.cssHooks[ name ];
+		if ( hooks && "expand" in hooks ) {
+			value = hooks.expand( value );
+			delete props[ name ];
+
+			// not quite $.extend, this wont overwrite keys already present.
+			// also - reusing 'index' from above because we have the correct "name"
+			for ( index in value ) {
+				if ( !( index in props ) ) {
+					props[ index ] = value[ index ];
+					specialEasing[ index ] = easing;
+				}
+			}
+		} else {
+			specialEasing[ name ] = easing;
+		}
+	}
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+	tweener: function( props, callback ) {
+		if ( jQuery.isFunction( props ) ) {
+			callback = props;
+			props = [ "*" ];
+		} else {
+			props = props.split(" ");
+		}
+
+		var prop,
+			index = 0,
+			length = props.length;
+
+		for ( ; index < length ; index++ ) {
+			prop = props[ index ];
+			tweeners[ prop ] = tweeners[ prop ] || [];
+			tweeners[ prop ].unshift( callback );
+		}
+	},
+
+	prefilter: function( callback, prepend ) {
+		if ( prepend ) {
+			animationPrefilters.unshift( callback );
+		} else {
+			animationPrefilters.push( callback );
+		}
+	}
+});
+
+function defaultPrefilter( elem, props, opts ) {
+	/* jshint validthis: true */
+	var prop, value, toggle, tween, hooks, oldfire,
+		anim = this,
+		orig = {},
+		style = elem.style,
+		hidden = elem.nodeType && isHidden( elem ),
+		dataShow = data_priv.get( elem, "fxshow" );
+
+	// handle queue: false promises
+	if ( !opts.queue ) {
+		hooks = jQuery._queueHooks( elem, "fx" );
+		if ( hooks.unqueued == null ) {
+			hooks.unqueued = 0;
+			oldfire = hooks.empty.fire;
+			hooks.empty.fire = function() {
+				if ( !hooks.unqueued ) {
+					oldfire();
+				}
+			};
+		}
+		hooks.unqueued++;
+
+		anim.always(function() {
+			// doing this makes sure that the complete handler will be called
+			// before this completes
+			anim.always(function() {
+				hooks.unqueued--;
+				if ( !jQuery.queue( elem, "fx" ).length ) {
+					hooks.empty.fire();
+				}
+			});
+		});
+	}
+
+	// height/width overflow pass
+	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+		// Make sure that nothing sneaks out
+		// Record all 3 overflow attributes because IE9-10 do not
+		// change the overflow attribute when overflowX and
+		// overflowY are set to the same value
+		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+		// Set display property to inline-block for height/width
+		// animations on inline elements that are having width/height animated
+		if ( jQuery.css( elem, "display" ) === "inline" &&
+				jQuery.css( elem, "float" ) === "none" ) {
+
+			style.display = "inline-block";
+		}
+	}
+
+	if ( opts.overflow ) {
+		style.overflow = "hidden";
+		anim.always(function() {
+			style.overflow = opts.overflow[ 0 ];
+			style.overflowX = opts.overflow[ 1 ];
+			style.overflowY = opts.overflow[ 2 ];
+		});
+	}
+
+
+	// show/hide pass
+	for ( prop in props ) {
+		value = props[ prop ];
+		if ( rfxtypes.exec( value ) ) {
+			delete props[ prop ];
+			toggle = toggle || value === "toggle";
+			if ( value === ( hidden ? "hide" : "show" ) ) {
+
+				// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
+				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
+					hidden = true;
+				} else {
+					continue;
+				}
+			}
+			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+		}
+	}
+
+	if ( !jQuery.isEmptyObject( orig ) ) {
+		if ( dataShow ) {
+			if ( "hidden" in dataShow ) {
+				hidden = dataShow.hidden;
+			}
+		} else {
+			dataShow = data_priv.access( elem, "fxshow", {} );
+		}
+
+		// store state if its toggle - enables .stop().toggle() to "reverse"
+		if ( toggle ) {
+			dataShow.hidden = !hidden;
+		}
+		if ( hidden ) {
+			jQuery( elem ).show();
+		} else {
+			anim.done(function() {
+				jQuery( elem ).hide();
+			});
+		}
+		anim.done(function() {
+			var prop;
+
+			data_priv.remove( elem, "fxshow" );
+			for ( prop in orig ) {
+				jQuery.style( elem, prop, orig[ prop ] );
+			}
+		});
+		for ( prop in orig ) {
+			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+			if ( !( prop in dataShow ) ) {
+				dataShow[ prop ] = tween.start;
+				if ( hidden ) {
+					tween.end = tween.start;
+					tween.start = prop === "width" || prop === "height" ? 1 : 0;
+				}
+			}
+		}
+	}
+}
+
+function Tween( elem, options, prop, end, easing ) {
+	return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+	constructor: Tween,
+	init: function( elem, options, prop, end, easing, unit ) {
+		this.elem = elem;
+		this.prop = prop;
+		this.easing = easing || "swing";
+		this.options = options;
+		this.start = this.now = this.cur();
+		this.end = end;
+		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+	},
+	cur: function() {
+		var hooks = Tween.propHooks[ this.prop ];
+
+		return hooks && hooks.get ?
+			hooks.get( this ) :
+			Tween.propHooks._default.get( this );
+	},
+	run: function( percent ) {
+		var eased,
+			hooks = Tween.propHooks[ this.prop ];
+
+		if ( this.options.duration ) {
+			this.pos = eased = jQuery.easing[ this.easing ](
+				percent, this.options.duration * percent, 0, 1, this.options.duration
+			);
+		} else {
+			this.pos = eased = percent;
+		}
+		this.now = ( this.end - this.start ) * eased + this.start;
+
+		if ( this.options.step ) {
+			this.options.step.call( this.elem, this.now, this );
+		}
+
+		if ( hooks && hooks.set ) {
+			hooks.set( this );
+		} else {
+			Tween.propHooks._default.set( this );
+		}
+		return this;
+	}
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+	_default: {
+		get: function( tween ) {
+			var result;
+
+			if ( tween.elem[ tween.prop ] != null &&
+				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+				return tween.elem[ tween.prop ];
+			}
+
+			// passing an empty string as a 3rd parameter to .css will automatically
+			// attempt a parseFloat and fallback to a string if the parse fails
+			// so, simple values such as "10px" are parsed to Float.
+			// complex values such as "rotate(1rad)" are returned as is.
+			result = jQuery.css( tween.elem, tween.prop, "" );
+			// Empty strings, null, undefined and "auto" are converted to 0.
+			return !result || result === "auto" ? 0 : result;
+		},
+		set: function( tween ) {
+			// use step hook for back compat - use cssHook if its there - use .style if its
+			// available and use plain properties where available
+			if ( jQuery.fx.step[ tween.prop ] ) {
+				jQuery.fx.step[ tween.prop ]( tween );
+			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+			} else {
+				tween.elem[ tween.prop ] = tween.now;
+			}
+		}
+	}
+};
+
+// Support: IE9
+// Panic based approach to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+	set: function( tween ) {
+		if ( tween.elem.nodeType && tween.elem.parentNode ) {
+			tween.elem[ tween.prop ] = tween.now;
+		}
+	}
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+	var cssFn = jQuery.fn[ name ];
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return speed == null || typeof speed === "boolean" ?
+			cssFn.apply( this, arguments ) :
+			this.animate( genFx( name, true ), speed, easing, callback );
+	};
+});
+
+jQuery.fn.extend({
+	fadeTo: function( speed, to, easing, callback ) {
+
+		// show any hidden elements after setting opacity to 0
+		return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+			// animate to the value specified
+			.end().animate({ opacity: to }, speed, easing, callback );
+	},
+	animate: function( prop, speed, easing, callback ) {
+		var empty = jQuery.isEmptyObject( prop ),
+			optall = jQuery.speed( speed, easing, callback ),
+			doAnimation = function() {
+				// Operate on a copy of prop so per-property easing won't be lost
+				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+				// Empty animations, or finishing resolves immediately
+				if ( empty || data_priv.get( this, "finish" ) ) {
+					anim.stop( true );
+				}
+			};
+			doAnimation.finish = doAnimation;
+
+		return empty || optall.queue === false ?
+			this.each( doAnimation ) :
+			this.queue( optall.queue, doAnimation );
+	},
+	stop: function( type, clearQueue, gotoEnd ) {
+		var stopQueue = function( hooks ) {
+			var stop = hooks.stop;
+			delete hooks.stop;
+			stop( gotoEnd );
+		};
+
+		if ( typeof type !== "string" ) {
+			gotoEnd = clearQueue;
+			clearQueue = type;
+			type = undefined;
+		}
+		if ( clearQueue && type !== false ) {
+			this.queue( type || "fx", [] );
+		}
+
+		return this.each(function() {
+			var dequeue = true,
+				index = type != null && type + "queueHooks",
+				timers = jQuery.timers,
+				data = data_priv.get( this );
+
+			if ( index ) {
+				if ( data[ index ] && data[ index ].stop ) {
+					stopQueue( data[ index ] );
+				}
+			} else {
+				for ( index in data ) {
+					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+						stopQueue( data[ index ] );
+					}
+				}
+			}
+
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+					timers[ index ].anim.stop( gotoEnd );
+					dequeue = false;
+					timers.splice( index, 1 );
+				}
+			}
+
+			// start the next in the queue if the last step wasn't forced
+			// timers currently will call their complete callbacks, which will dequeue
+			// but only if they were gotoEnd
+			if ( dequeue || !gotoEnd ) {
+				jQuery.dequeue( this, type );
+			}
+		});
+	},
+	finish: function( type ) {
+		if ( type !== false ) {
+			type = type || "fx";
+		}
+		return this.each(function() {
+			var index,
+				data = data_priv.get( this ),
+				queue = data[ type + "queue" ],
+				hooks = data[ type + "queueHooks" ],
+				timers = jQuery.timers,
+				length = queue ? queue.length : 0;
+
+			// enable finishing flag on private data
+			data.finish = true;
+
+			// empty the queue first
+			jQuery.queue( this, type, [] );
+
+			if ( hooks && hooks.stop ) {
+				hooks.stop.call( this, true );
+			}
+
+			// look for any active animations, and finish them
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+					timers[ index ].anim.stop( true );
+					timers.splice( index, 1 );
+				}
+			}
+
+			// look for any animations in the old queue and finish them
+			for ( index = 0; index < length; index++ ) {
+				if ( queue[ index ] && queue[ index ].finish ) {
+					queue[ index ].finish.call( this );
+				}
+			}
+
+			// turn off finishing flag
+			delete data.finish;
+		});
+	}
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+	var which,
+		attrs = { height: type },
+		i = 0;
+
+	// if we include width, step value is 1 to do all cssExpand values,
+	// if we don't include width, step value is 2 to skip over Left and Right
+	includeWidth = includeWidth? 1 : 0;
+	for( ; i < 4 ; i += 2 - includeWidth ) {
+		which = cssExpand[ i ];
+		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+	}
+
+	if ( includeWidth ) {
+		attrs.opacity = attrs.width = type;
+	}
+
+	return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+	slideDown: genFx("show"),
+	slideUp: genFx("hide"),
+	slideToggle: genFx("toggle"),
+	fadeIn: { opacity: "show" },
+	fadeOut: { opacity: "hide" },
+	fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return this.animate( props, speed, easing, callback );
+	};
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+		complete: fn || !fn && easing ||
+			jQuery.isFunction( speed ) && speed,
+		duration: speed,
+		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+	};
+
+	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+	// normalize opt.queue - true/undefined/null -> "fx"
+	if ( opt.queue == null || opt.queue === true ) {
+		opt.queue = "fx";
+	}
+
+	// Queueing
+	opt.old = opt.complete;
+
+	opt.complete = function() {
+		if ( jQuery.isFunction( opt.old ) ) {
+			opt.old.call( this );
+		}
+
+		if ( opt.queue ) {
+			jQuery.dequeue( this, opt.queue );
+		}
+	};
+
+	return opt;
+};
+
+jQuery.easing = {
+	linear: function( p ) {
+		return p;
+	},
+	swing: function( p ) {
+		return 0.5 - Math.cos( p*Math.PI ) / 2;
+	}
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+	var timer,
+		timers = jQuery.timers,
+		i = 0;
+
+	fxNow = jQuery.now();
+
+	for ( ; i < timers.length; i++ ) {
+		timer = timers[ i ];
+		// Checks the timer has not already been removed
+		if ( !timer() && timers[ i ] === timer ) {
+			timers.splice( i--, 1 );
+		}
+	}
+
+	if ( !timers.length ) {
+		jQuery.fx.stop();
+	}
+	fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+	if ( timer() && jQuery.timers.push( timer ) ) {
+		jQuery.fx.start();
+	}
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+	if ( !timerId ) {
+		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+	}
+};
+
+jQuery.fx.stop = function() {
+	clearInterval( timerId );
+	timerId = null;
+};
+
+jQuery.fx.speeds = {
+	slow: 600,
+	fast: 200,
+	// Default speed
+	_default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	jQuery.expr.filters.animated = function( elem ) {
+		return jQuery.grep(jQuery.timers, function( fn ) {
+			return elem === fn.elem;
+		}).length;
+	};
+}
+jQuery.fn.offset = function( options ) {
+	if ( arguments.length ) {
+		return options === undefined ?
+			this :
+			this.each(function( i ) {
+				jQuery.offset.setOffset( this, options, i );
+			});
+	}
+
+	var docElem, win,
+		elem = this[ 0 ],
+		box = { top: 0, left: 0 },
+		doc = elem && elem.ownerDocument;
+
+	if ( !doc ) {
+		return;
+	}
+
+	docElem = doc.documentElement;
+
+	// Make sure it's not a disconnected DOM node
+	if ( !jQuery.contains( docElem, elem ) ) {
+		return box;
+	}
+
+	// If we don't have gBCR, just use 0,0 rather than error
+	// BlackBerry 5, iOS 3 (original iPhone)
+	if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+		box = elem.getBoundingClientRect();
+	}
+	win = getWindow( doc );
+	return {
+		top: box.top + win.pageYOffset - docElem.clientTop,
+		left: box.left + win.pageXOffset - docElem.clientLeft
+	};
+};
+
+jQuery.offset = {
+
+	setOffset: function( elem, options, i ) {
+		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+			position = jQuery.css( elem, "position" ),
+			curElem = jQuery( elem ),
+			props = {};
+
+		// Set position first, in-case top/left are set even on static elem
+		if ( position === "static" ) {
+			elem.style.position = "relative";
+		}
+
+		curOffset = curElem.offset();
+		curCSSTop = jQuery.css( elem, "top" );
+		curCSSLeft = jQuery.css( elem, "left" );
+		calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
+
+		// Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+		if ( calculatePosition ) {
+			curPosition = curElem.position();
+			curTop = curPosition.top;
+			curLeft = curPosition.left;
+
+		} else {
+			curTop = parseFloat( curCSSTop ) || 0;
+			curLeft = parseFloat( curCSSLeft ) || 0;
+		}
+
+		if ( jQuery.isFunction( options ) ) {
+			options = options.call( elem, i, curOffset );
+		}
+
+		if ( options.top != null ) {
+			props.top = ( options.top - curOffset.top ) + curTop;
+		}
+		if ( options.left != null ) {
+			props.left = ( options.left - curOffset.left ) + curLeft;
+		}
+
+		if ( "using" in options ) {
+			options.using.call( elem, props );
+
+		} else {
+			curElem.css( props );
+		}
+	}
+};
+
+
+jQuery.fn.extend({
+
+	position: function() {
+		if ( !this[ 0 ] ) {
+			return;
+		}
+
+		var offsetParent, offset,
+			elem = this[ 0 ],
+			parentOffset = { top: 0, left: 0 };
+
+		// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
+		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+			// We assume that getBoundingClientRect is available when computed position is fixed
+			offset = elem.getBoundingClientRect();
+
+		} else {
+			// Get *real* offsetParent
+			offsetParent = this.offsetParent();
+
+			// Get correct offsets
+			offset = this.offset();
+			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+				parentOffset = offsetParent.offset();
+			}
+
+			// Add offsetParent borders
+			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+		}
+
+		// Subtract parent offsets and element margins
+		return {
+			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
+		};
+	},
+
+	offsetParent: function() {
+		return this.map(function() {
+			var offsetParent = this.offsetParent || docElem;
+
+			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
+				offsetParent = offsetParent.offsetParent;
+			}
+
+			return offsetParent || docElem;
+		});
+	}
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+	var top = "pageYOffset" === prop;
+
+	jQuery.fn[ method ] = function( val ) {
+		return jQuery.access( this, function( elem, method, val ) {
+			var win = getWindow( elem );
+
+			if ( val === undefined ) {
+				return win ? win[ prop ] : elem[ method ];
+			}
+
+			if ( win ) {
+				win.scrollTo(
+					!top ? val : window.pageXOffset,
+					top ? val : window.pageYOffset
+				);
+
+			} else {
+				elem[ method ] = val;
+			}
+		}, method, val, arguments.length, null );
+	};
+});
+
+function getWindow( elem ) {
+	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+		// margin is only for outerHeight, outerWidth
+		jQuery.fn[ funcName ] = function( margin, value ) {
+			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+			return jQuery.access( this, function( elem, type, value ) {
+				var doc;
+
+				if ( jQuery.isWindow( elem ) ) {
+					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+					// isn't a whole lot we can do. See pull request at this URL for discussion:
+					// https://github.com/jquery/jquery/pull/764
+					return elem.document.documentElement[ "client" + name ];
+				}
+
+				// Get document width or height
+				if ( elem.nodeType === 9 ) {
+					doc = elem.documentElement;
+
+					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
+					// whichever is greatest
+					return Math.max(
+						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+						elem.body[ "offset" + name ], doc[ "offset" + name ],
+						doc[ "client" + name ]
+					);
+				}
+
+				return value === undefined ?
+					// Get width or height on the element, requesting but not forcing parseFloat
+					jQuery.css( elem, type, extra ) :
+
+					// Set width or height on the element
+					jQuery.style( elem, type, value, extra );
+			}, type, chainable ? margin : undefined, chainable, null );
+		};
+	});
+});
+// Limit scope pollution from any deprecated API
+// (function() {
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+	return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+// })();
+if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+	// Expose jQuery as module.exports in loaders that implement the Node
+	// module pattern (including browserify). Do not create the global, since
+	// the user will be storing it themselves locally, and globals are frowned
+	// upon in the Node module world.
+	module.exports = jQuery;
+} else {
+	// Register as a named AMD module, since jQuery can be concatenated with other
+	// files that may use define, but not via a proper concatenation script that
+	// understands anonymous AMD modules. A named AMD is safest and most robust
+	// way to register. Lowercase jquery is used because AMD module names are
+	// derived from file names, and jQuery is normally delivered in a lowercase
+	// file name. Do this after creating the global so that if an AMD module wants
+	// to call noConflict to hide this version of jQuery, it will work.
+	if ( typeof define === "function" && define.amd ) {
+		define( "jquery", [], function () { return jQuery; } );
+	}
+}
+
+// If there is a window object, that at least has a document property,
+// define jQuery and $ identifiers
+if ( typeof window === "object" && typeof window.document === "object" ) {
+	window.jQuery = window.$ = jQuery;
+}
+
+})( window );
diff --git a/chrome/test/data/dromaeo/lib/mootools.js b/chrome/test/data/dromaeo/lib/mootools.js
index 7e1e482b..8b77ad95 100644
--- a/chrome/test/data/dromaeo/lib/mootools.js
+++ b/chrome/test/data/dromaeo/lib/mootools.js
@@ -1,126 +1,415 @@
 /*
-Script: Core.js
-	MooTools - My Object Oriented JavaScript Tools.
+---
+MooTools: the javascript framework
 
-License:
-	MIT-style license.
+web build:
+ - http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0
 
-Copyright:
-	Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
 
-Code & Documentation:
-	[The MooTools production team](http://mootools.net/developers/).
+/*
+---
 
-Inspiration:
-	- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
-	- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+  - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+  - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
 */
 
-var MooTools = {
-	'version': '1.2.1',
-	'build': '0d4845aab3d9a4fdee2f0d4a6dd59210e4b697cf'
+(function(){
+
+this.MooTools = {
+	version: '1.4.1',
+	build: 'd1fb25710e3c5482a219ab9dc675a4e0ad2176b6'
 };
 
-var Native = function(options){
-	options = options || {};
-	var name = options.name;
-	var legacy = options.legacy;
-	var protect = options.protect;
-	var methods = options.implement;
-	var generics = options.generics;
-	var initialize = options.initialize;
-	var afterImplement = options.afterImplement || function(){};
-	var object = initialize || legacy;
-	generics = generics !== false;
+// typeOf, instanceOf
 
-	object.constructor = Native;
-	object.$family = {name: 'native'};
-	if (legacy && initialize) object.prototype = legacy.prototype;
-	object.prototype.constructor = object;
+var typeOf = this.typeOf = function(item){
+	if (item == null) return 'null';
+	if (item.$family) return item.$family();
 
-	if (name){
-		var family = name.toLowerCase();
-		object.prototype.$family = {name: family};
-		Native.typize(object, family);
+	if (item.nodeName){
+		if (item.nodeType == 1) return 'element';
+		if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+	} else if (typeof item.length == 'number'){
+		if (item.callee) return 'arguments';
+		if ('item' in item) return 'collection';
 	}
 
-	var add = function(obj, name, method, force){
-		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
-		if (generics) Native.genericize(obj, name, protect);
-		afterImplement.call(obj, name, method);
-		return obj;
-	};
+	return typeof item;
+};
 
-	object.alias = function(a1, a2, a3){
-		if (typeof a1 == 'string'){
-			if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
+var instanceOf = this.instanceOf = function(item, object){
+	if (item == null) return false;
+	var constructor = item.$constructor || item.constructor;
+	while (constructor){
+		if (constructor === object) return true;
+		constructor = constructor.parent;
+	}
+	return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+	var self = this;
+	return function(a, b){
+		if (a == null) return this;
+		if (usePlural || typeof a != 'string'){
+			for (var k in a) self.call(this, k, a[k]);
+			if (enumerables) for (var i = enumerables.length; i--;){
+				k = enumerables[i];
+				if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+			}
+		} else {
+			self.call(this, a, b);
 		}
-		for (var a in a1) this.alias(a, a1[a], a2);
 		return this;
 	};
+};
 
-	object.implement = function(a1, a2, a3){
-		if (typeof a1 == 'string') return add(this, a1, a2, a3);
-		for (var p in a1) add(this, p, a1[p], a2);
+Function.prototype.overloadGetter = function(usePlural){
+	var self = this;
+	return function(a){
+		var args, result;
+		if (usePlural || typeof a != 'string') args = a;
+		else if (arguments.length > 1) args = arguments;
+		if (args){
+			result = {};
+			for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+		} else {
+			result = self.call(this, a);
+		}
+		return result;
+	};
+};
+
+Function.prototype.extend = function(key, value){
+	this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+	this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+	return (typeOf(item) == 'function') ? item : function(){
+		return item;
+	};
+};
+
+Array.from = function(item){
+	if (item == null) return [];
+	return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+	var number = parseFloat(item);
+	return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+	return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+	hide: function(){
+		this.$hidden = true;
 		return this;
-	};
+	},
 
-	if (methods) object.implement(methods);
-
-	return object;
-};
-
-Native.genericize = function(object, property, check){
-	if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
-		var args = Array.prototype.slice.call(arguments);
-		return object.prototype[property].apply(args.shift(), args);
-	};
-};
-
-Native.implement = function(objects, properties){
-	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
-};
-
-Native.typize = function(object, family){
-	if (!object.type) object.type = function(item){
-		return ($type(item) === family);
-	};
-};
-
-(function(){
-	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
-	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
-
-	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
-	for (var t in types) Native.typize(types[t], t);
-
-	var generics = {
-		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
-		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
-	};
-	for (var g in generics){
-		for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
-	};
-})();
-
-var Hash = new Native({
-
-	name: 'Hash',
-
-	initialize: function(object){
-		if ($type(object) == 'hash') object = $unlink(object.getClean());
-		for (var key in object) this[key] = object[key];
+	protect: function(){
+		this.$protected = true;
 		return this;
 	}
 
 });
 
+// Type
+
+var Type = this.Type = function(name, object){
+	if (name){
+		var lower = name.toLowerCase();
+		var typeCheck = function(item){
+			return (typeOf(item) == lower);
+		};
+
+		Type['is' + name] = typeCheck;
+		if (object != null){
+			object.prototype.$family = (function(){
+				return lower;
+			}).hide();
+			//<1.2compat>
+			object.type = typeCheck;
+			//</1.2compat>
+		}
+	}
+
+	if (object == null) return null;
+
+	object.extend(this);
+	object.$constructor = Type;
+	object.prototype.$constructor = object;
+
+	return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+	return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+	var type = typeOf(object.prototype);
+	return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+	if (method && method.$hidden) return;
+
+	var hooks = hooksOf(this);
+
+	for (var i = 0; i < hooks.length; i++){
+		var hook = hooks[i];
+		if (typeOf(hook) == 'type') implement.call(hook, name, method);
+		else hook.call(this, name, method);
+	}
+
+	var previous = this.prototype[name];
+	if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+	if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+		return method.apply(item, slice.call(arguments, 1));
+	});
+};
+
+var extend = function(name, method){
+	if (method && method.$hidden) return;
+	var previous = this[name];
+	if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+	implement: implement.overloadSetter(),
+
+	extend: extend.overloadSetter(),
+
+	alias: function(name, existing){
+		implement.call(this, name, this.prototype[existing]);
+	}.overloadSetter(),
+
+	mirror: function(hook){
+		hooksOf(this).push(hook);
+		return this;
+	}
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+	var isType = (object != Object),
+		prototype = object.prototype;
+
+	if (isType) object = new Type(name, object);
+
+	for (var i = 0, l = methods.length; i < l; i++){
+		var key = methods[i],
+			generic = object[key],
+			proto = prototype[key];
+
+		if (generic) generic.protect();
+
+		if (isType && proto){
+			delete prototype[key];
+			prototype[key] = proto.protect();
+		}
+	}
+
+	if (isType) object.implement(prototype);
+
+	return force;
+};
+
+force('String', String, [
+	'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+	'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+	'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+	'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+	'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+	'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+	'exec', 'test'
+])('Object', Object, [
+	'create', 'defineProperty', 'defineProperties', 'keys',
+	'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+	'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+	return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+	return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+	return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+	for (var key in object){
+		if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+	}
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+	forEach: function(fn, bind){
+		for (var i = 0, l = this.length; i < l; i++){
+			if (i in this) fn.call(bind, this[i], i, this);
+		}
+	},
+
+	each: function(fn, bind){
+		Array.forEach(this, fn, bind);
+		return this;
+	}
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+	switch (typeOf(item)){
+		case 'array': return item.clone();
+		case 'object': return Object.clone(item);
+		default: return item;
+	}
+};
+
+Array.implement('clone', function(){
+	var i = this.length, clone = new Array(i);
+	while (i--) clone[i] = cloneOf(this[i]);
+	return clone;
+});
+
+var mergeOne = function(source, key, current){
+	switch (typeOf(current)){
+		case 'object':
+			if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+			else source[key] = Object.clone(current);
+		break;
+		case 'array': source[key] = current.clone(); break;
+		default: source[key] = current;
+	}
+	return source;
+};
+
+Object.extend({
+
+	merge: function(source, k, v){
+		if (typeOf(k) == 'string') return mergeOne(source, k, v);
+		for (var i = 1, l = arguments.length; i < l; i++){
+			var object = arguments[i];
+			for (var key in object) mergeOne(source, key, object[key]);
+		}
+		return source;
+	},
+
+	clone: function(object){
+		var clone = {};
+		for (var key in object) clone[key] = cloneOf(object[key]);
+		return clone;
+	},
+
+	append: function(original){
+		for (var i = 1, l = arguments.length; i < l; i++){
+			var extended = arguments[i] || {};
+			for (var key in extended) original[key] = extended[key];
+		}
+		return original;
+	}
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+	new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+	return (UID++).toString(36);
+});
+
+//<1.2compat>
+
+var Hash = this.Hash = new Type('Hash', function(object){
+	if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
+	for (var key in object) this[key] = object[key];
+	return this;
+});
+
 Hash.implement({
 
 	forEach: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
-		}
+		Object.forEach(this, fn, bind);
 	},
 
 	getClean: function(){
@@ -141,353 +430,167 @@
 
 });
 
-Hash.alias('forEach', 'each');
+Hash.alias('each', 'forEach');
 
-Array.implement({
+Object.type = Type.isObject;
 
-	forEach: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
-	}
-
-});
-
-Array.alias('forEach', 'each');
-
-function $A(iterable){
-	if (iterable.item){
-		var array = [];
-		for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
-		return array;
-	}
-	return Array.prototype.slice.call(iterable);
+var Native = this.Native = function(properties){
+	return new Type(properties.name, properties.initialize);
 };
 
-function $arguments(i){
+Native.type = Type.type;
+
+Native.implement = function(objects, methods){
+	for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
+	return Native;
+};
+
+var arrayType = Array.type;
+Array.type = function(item){
+	return instanceOf(item, Array) || arrayType(item);
+};
+
+this.$A = function(item){
+	return Array.from(item).slice();
+};
+
+this.$arguments = function(i){
 	return function(){
 		return arguments[i];
 	};
 };
 
-function $chk(obj){
+this.$chk = function(obj){
 	return !!(obj || obj === 0);
 };
 
-function $clear(timer){
+this.$clear = function(timer){
 	clearTimeout(timer);
 	clearInterval(timer);
 	return null;
 };
 
-function $defined(obj){
-	return (obj != undefined);
+this.$defined = function(obj){
+	return (obj != null);
 };
 
-function $each(iterable, fn, bind){
-	var type = $type(iterable);
-	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
+this.$each = function(iterable, fn, bind){
+	var type = typeOf(iterable);
+	((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
 };
 
-function $empty(){};
+this.$empty = function(){};
 
-function $extend(original, extended){
-	for (var key in (extended || {})) original[key] = extended[key];
-	return original;
+this.$extend = function(original, extended){
+	return Object.append(original, extended);
 };
 
-function $H(object){
+this.$H = function(object){
 	return new Hash(object);
 };
 
-function $lambda(value){
-	return (typeof value == 'function') ? value : function(){
-		return value;
-	};
+this.$merge = function(){
+	var args = Array.slice(arguments);
+	args.unshift({});
+	return Object.merge.apply(null, args);
 };
 
-function $merge(){
-	var mix = {};
-	for (var i = 0, l = arguments.length; i < l; i++){
-		var object = arguments[i];
-		if ($type(object) != 'object') continue;
-		for (var key in object){
-			var op = object[key], mp = mix[key];
-			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
-		}
-	}
-	return mix;
+this.$lambda = Function.from;
+this.$mixin = Object.merge;
+this.$random = Number.random;
+this.$splat = Array.from;
+this.$time = Date.now;
+
+this.$type = function(object){
+	var type = typeOf(object);
+	if (type == 'elements') return 'array';
+	return (type == 'null') ? false : type;
 };
 
-function $pick(){
-	for (var i = 0, l = arguments.length; i < l; i++){
-		if (arguments[i] != undefined) return arguments[i];
-	}
-	return null;
-};
-
-function $random(min, max){
-	return Math.floor(Math.random() * (max - min + 1) + min);
-};
-
-function $splat(obj){
-	var type = $type(obj);
-	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
-};
-
-var $time = Date.now || function(){
-	return +new Date;
-};
-
-function $try(){
-	for (var i = 0, l = arguments.length; i < l; i++){
-		try {
-			return arguments[i]();
-		} catch(e){}
-	}
-	return null;
-};
-
-function $type(obj){
-	if (obj == undefined) return false;
-	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
-	if (obj.nodeName){
-		switch (obj.nodeType){
-			case 1: return 'element';
-			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
-		}
-	} else if (typeof obj.length == 'number'){
-		if (obj.callee) return 'arguments';
-		else if (obj.item) return 'collection';
-	}
-	return typeof obj;
-};
-
-function $unlink(object){
-	var unlinked;
-	switch ($type(object)){
-		case 'object':
-			unlinked = {};
-			for (var p in object) unlinked[p] = $unlink(object[p]);
-		break;
-		case 'hash':
-			unlinked = new Hash(object);
-		break;
-		case 'array':
-			unlinked = [];
-			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
-		break;
+this.$unlink = function(object){
+	switch (typeOf(object)){
+		case 'object': return Object.clone(object);
+		case 'array': return Array.clone(object);
+		case 'hash': return new Hash(object);
 		default: return object;
 	}
-	return unlinked;
 };
 
+//</1.2compat>
 
-/*
-Script: Browser.js
-	The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
-
-License:
-	MIT-style license.
-*/
-
-var Browser = $merge({
-
-	Engine: {name: 'unknown', version: 0},
-
-	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
-
-	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
-
-	Plugins: {},
-
-	Engines: {
-
-		presto: function(){
-			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
-		},
-
-		trident: function(){
-			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
-		},
-
-		webkit: function(){
-			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
-		},
-
-		gecko: function(){
-			return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
-		}
-
-	}
-
-}, Browser || {});
-
-Browser.Platform[Browser.Platform.name] = true;
-
-Browser.detect = function(){
-
-	for (var engine in this.Engines){
-		var version = this.Engines[engine]();
-		if (version){
-			this.Engine = {name: engine, version: version};
-			this.Engine[engine] = this.Engine[engine + version] = true;
-			break;
-		}
-	}
-
-	return {name: engine, version: version};
-
-};
-
-Browser.detect();
-
-Browser.Request = function(){
-	return $try(function(){
-		return new XMLHttpRequest();
-	}, function(){
-		return new ActiveXObject('MSXML2.XMLHTTP');
-	});
-};
-
-Browser.Features.xhr = !!(Browser.Request());
-
-Browser.Plugins.Flash = (function(){
-	var version = ($try(function(){
-		return navigator.plugins['Shockwave Flash'].description;
-	}, function(){
-		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
-	}) || '0 r0').match(/\d+/g);
-	return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)};
 })();
 
-function $exec(text){
-	if (!text) return text;
-	if (window.execScript){
-		window.execScript(text);
-	} else {
-		var script = document.createElement('script');
-		script.setAttribute('type', 'text/javascript');
-		script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
-		document.head.appendChild(script);
-		document.head.removeChild(script);
-	}
-	return text;
-};
-
-Native.UID = 1;
-
-var $uid = (Browser.Engine.trident) ? function(item){
-	return (item.uid || (item.uid = [Native.UID++]))[0];
-} : function(item){
-	return item.uid || (item.uid = Native.UID++);
-};
-
-var Window = new Native({
-
-	name: 'Window',
-
-	legacy: (Browser.Engine.trident) ? null: window.Window,
-
-	initialize: function(win){
-		$uid(win);
-		if (!win.Element){
-			win.Element = $empty;
-			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
-			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
-		}
-		win.document.window = win;
-		return $extend(win, Window.Prototype);
-	},
-
-	afterImplement: function(property, value){
-		window[property] = Window.Prototype[property] = value;
-	}
-
-});
-
-Window.Prototype = {$family: {name: 'window'}};
-
-new Window(window);
-
-var Document = new Native({
-
-	name: 'Document',
-
-	legacy: (Browser.Engine.trident) ? null: window.Document,
-
-	initialize: function(doc){
-		$uid(doc);
-		doc.head = doc.getElementsByTagName('head')[0];
-		doc.html = doc.getElementsByTagName('html')[0];
-		if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
-			doc.execCommand("BackgroundImageCache", false, true);
-		});
-		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
-			doc.window.detachEvent('onunload', arguments.callee);
-			doc.head = doc.html = doc.window = null;
-		});
-		return $extend(doc, Document.Prototype);
-	},
-
-	afterImplement: function(property, value){
-		document[property] = Document.Prototype[property] = value;
-	}
-
-});
-
-Document.Prototype = {$family: {name: 'document'}};
-
-new Document(document);
-
 
 /*
-Script: Array.js
-	Contains Array Prototypes like each, contains, and erase.
+---
 
-License:
-	MIT-style license.
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Array
+
+...
 */
 
 Array.implement({
 
+	/*<!ES5>*/
 	every: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++){
-			if (!fn.call(bind, this[i], i, this)) return false;
+		for (var i = 0, l = this.length >>> 0; i < l; i++){
+			if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
 		}
 		return true;
 	},
 
 	filter: function(fn, bind){
 		var results = [];
-		for (var i = 0, l = this.length; i < l; i++){
-			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
+		for (var i = 0, l = this.length >>> 0; i < l; i++){
+			if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
 		}
 		return results;
 	},
 
-	clean: function() {
-		return this.filter($defined);
-	},
-
 	indexOf: function(item, from){
-		var len = this.length;
-		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
+		var length = this.length >>> 0;
+		for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
 			if (this[i] === item) return i;
 		}
 		return -1;
 	},
 
 	map: function(fn, bind){
-		var results = [];
-		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
+		var length = this.length >>> 0, results = Array(length);
+		for (var i = 0; i < length; i++){
+			if (i in this) results[i] = fn.call(bind, this[i], i, this);
+		}
 		return results;
 	},
 
 	some: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++){
-			if (fn.call(bind, this[i], i, this)) return true;
+		for (var i = 0, l = this.length >>> 0; i < l; i++){
+			if ((i in this) && fn.call(bind, this[i], i, this)) return true;
 		}
 		return false;
 	},
+	/*</!ES5>*/
+
+	clean: function(){
+		return this.filter(function(item){
+			return item != null;
+		});
+	},
+
+	invoke: function(methodName){
+		var args = Array.slice(arguments, 1);
+		return this.map(function(item){
+			return item[methodName].apply(item, args);
+		});
+	},
 
 	associate: function(keys){
 		var obj = {}, length = Math.min(this.length, keys.length);
@@ -513,8 +616,8 @@
 		return this.indexOf(item, from) != -1;
 	},
 
-	extend: function(array){
-		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
+	append: function(array){
+		this.push.apply(this, array);
 		return this;
 	},
 
@@ -523,7 +626,7 @@
 	},
 
 	getRandom: function(){
-		return (this.length) ? this[$random(0, this.length - 1)] : null;
+		return (this.length) ? this[Number.random(0, this.length - 1)] : null;
 	},
 
 	include: function(item){
@@ -537,7 +640,7 @@
 	},
 
 	erase: function(item){
-		for (var i = this.length; i--; i){
+		for (var i = this.length; i--;){
 			if (this[i] === item) this.splice(i, 1);
 		}
 		return this;
@@ -551,13 +654,20 @@
 	flatten: function(){
 		var array = [];
 		for (var i = 0, l = this.length; i < l; i++){
-			var type = $type(this[i]);
-			if (!type) continue;
-			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
+			var type = typeOf(this[i]);
+			if (type == 'null') continue;
+			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
 		}
 		return array;
 	},
 
+	pick: function(){
+		for (var i = 0, l = this.length; i < l; i++){
+			if (this[i] != null) return this[i];
+		}
+		return null;
+	},
+
 	hexToRgb: function(array){
 		if (this.length != 3) return null;
 		var rgb = this.map(function(value){
@@ -580,76 +690,115 @@
 
 });
 
+//<1.2compat>
+
+Array.alias('extend', 'append');
+
+var $pick = function(){
+	return Array.from(arguments).pick();
+};
+
+//</1.2compat>
+
 
 /*
-Script: Function.js
-	Contains Function Prototypes like create, bind, pass, and delay.
+---
 
-License:
-	MIT-style license.
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: String
+
+...
 */
 
-Function.implement({
+String.implement({
 
-	extend: function(properties){
-		for (var property in properties) this[property] = properties[property];
-		return this;
+	test: function(regex, params){
+		return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
 	},
 
-	create: function(options){
-		var self = this;
-		options = options || {};
-		return function(event){
-			var args = options.arguments;
-			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
-			if (options.event) args = [event || window.event].extend(args);
-			var returns = function(){
-				return self.apply(options.bind || null, args);
-			};
-			if (options.delay) return setTimeout(returns, options.delay);
-			if (options.periodical) return setInterval(returns, options.periodical);
-			if (options.attempt) return $try(returns);
-			return returns();
-		};
+	contains: function(string, separator){
+		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
 	},
 
-	run: function(args, bind){
-		return this.apply(bind, $splat(args));
+	trim: function(){
+		return String(this).replace(/^\s+|\s+$/g, '');
 	},
 
-	pass: function(args, bind){
-		return this.create({bind: bind, arguments: args});
+	clean: function(){
+		return String(this).replace(/\s+/g, ' ').trim();
 	},
 
-	bind: function(bind, args){
-		return this.create({bind: bind, arguments: args});
+	camelCase: function(){
+		return String(this).replace(/-\D/g, function(match){
+			return match.charAt(1).toUpperCase();
+		});
 	},
 
-	bindWithEvent: function(bind, args){
-		return this.create({bind: bind, arguments: args, event: true});
+	hyphenate: function(){
+		return String(this).replace(/[A-Z]/g, function(match){
+			return ('-' + match.charAt(0).toLowerCase());
+		});
 	},
 
-	attempt: function(args, bind){
-		return this.create({bind: bind, arguments: args, attempt: true})();
+	capitalize: function(){
+		return String(this).replace(/\b[a-z]/g, function(match){
+			return match.toUpperCase();
+		});
 	},
 
-	delay: function(delay, bind, args){
-		return this.create({bind: bind, arguments: args, delay: delay})();
+	escapeRegExp: function(){
+		return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
 	},
 
-	periodical: function(periodical, bind, args){
-		return this.create({bind: bind, arguments: args, periodical: periodical})();
+	toInt: function(base){
+		return parseInt(this, base || 10);
+	},
+
+	toFloat: function(){
+		return parseFloat(this);
+	},
+
+	hexToRgb: function(array){
+		var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+		return (hex) ? hex.slice(1).hexToRgb(array) : null;
+	},
+
+	rgbToHex: function(array){
+		var rgb = String(this).match(/\d{1,3}/g);
+		return (rgb) ? rgb.rgbToHex(array) : null;
+	},
+
+	substitute: function(object, regexp){
+		return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+			if (match.charAt(0) == '\\') return match.slice(1);
+			return (object[name] != null) ? object[name] : '';
+		});
 	}
 
 });
 
 
 /*
-Script: Number.js
-	Contains Number Prototypes like limit, round, times, and ceil.
+---
 
-License:
-	MIT-style license.
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
 */
 
 Number.implement({
@@ -659,7 +808,7 @@
 	},
 
 	round: function(precision){
-		precision = Math.pow(10, precision || 0);
+		precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
 		return Math.round(this * precision) / precision;
 	},
 
@@ -677,13 +826,13 @@
 
 });
 
-Number.alias('times', 'each');
+Number.alias('each', 'times');
 
 (function(math){
 	var methods = {};
 	math.each(function(name){
 		if (!Number[name]) methods[name] = function(){
-			return Math[name].apply(null, [this].concat($A(arguments)));
+			return Math[name].apply(null, [this].concat(Array.from(arguments)));
 		};
 	});
 	Number.implement(methods);
@@ -691,124 +840,278 @@
 
 
 /*
-Script: String.js
-	Contains String Prototypes like camelCase, capitalize, test, and toInt.
+---
 
-License:
-	MIT-style license.
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
 */
 
-String.implement({
+Function.extend({
 
-	test: function(regex, params){
-		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
-	},
-
-	contains: function(string, separator){
-		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
-	},
-
-	trim: function(){
-		return this.replace(/^\s+|\s+$/g, '');
-	},
-
-	clean: function(){
-		return this.replace(/\s+/g, ' ').trim();
-	},
-
-	camelCase: function(){
-		return this.replace(/-\D/g, function(match){
-			return match.charAt(1).toUpperCase();
-		});
-	},
-
-	hyphenate: function(){
-		return this.replace(/[A-Z]/g, function(match){
-			return ('-' + match.charAt(0).toLowerCase());
-		});
-	},
-
-	capitalize: function(){
-		return this.replace(/\b[a-z]/g, function(match){
-			return match.toUpperCase();
-		});
-	},
-
-	escapeRegExp: function(){
-		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
-	},
-
-	toInt: function(base){
-		return parseInt(this, base || 10);
-	},
-
-	toFloat: function(){
-		return parseFloat(this);
-	},
-
-	hexToRgb: function(array){
-		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
-		return (hex) ? hex.slice(1).hexToRgb(array) : null;
-	},
-
-	rgbToHex: function(array){
-		var rgb = this.match(/\d{1,3}/g);
-		return (rgb) ? rgb.rgbToHex(array) : null;
-	},
-
-	stripScripts: function(option){
-		var scripts = '';
-		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
-			scripts += arguments[1] + '\n';
-			return '';
-		});
-		if (option === true) $exec(scripts);
-		else if ($type(option) == 'function') option(scripts, text);
-		return text;
-	},
-
-	substitute: function(object, regexp){
-		return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
-			if (match.charAt(0) == '\\') return match.slice(1);
-			return (object[name] != undefined) ? object[name] : '';
-		});
+	attempt: function(){
+		for (var i = 0, l = arguments.length; i < l; i++){
+			try {
+				return arguments[i]();
+			} catch (e){}
+		}
+		return null;
 	}
 
 });
 
+Function.implement({
+
+	attempt: function(args, bind){
+		try {
+			return this.apply(bind, Array.from(args));
+		} catch (e){}
+
+		return null;
+	},
+
+	/*<!ES5-bind>*/
+	bind: function(that){
+		var self = this,
+			args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+			F = function(){};
+
+		var bound = function(){
+			var context = that, length = arguments.length;
+			if (this instanceof bound){
+				F.prototype = self.prototype;
+				context = new F;
+			}
+			var result = (!args && !length)
+				? self.call(context)
+				: self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+			return context == that ? result : context;
+		};
+		return bound;
+	},
+	/*</!ES5-bind>*/
+
+	pass: function(args, bind){
+		var self = this;
+		if (args != null) args = Array.from(args);
+		return function(){
+			return self.apply(bind, args || arguments);
+		};
+	},
+
+	delay: function(delay, bind, args){
+		return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+	},
+
+	periodical: function(periodical, bind, args){
+		return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+	}
+
+});
+
+//<1.2compat>
+
+delete Function.prototype.bind;
+
+Function.implement({
+
+	create: function(options){
+		var self = this;
+		options = options || {};
+		return function(event){
+			var args = options.arguments;
+			args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0);
+			if (options.event) args = [event || window.event].extend(args);
+			var returns = function(){
+				return self.apply(options.bind || null, args);
+			};
+			if (options.delay) return setTimeout(returns, options.delay);
+			if (options.periodical) return setInterval(returns, options.periodical);
+			if (options.attempt) return Function.attempt(returns);
+			return returns();
+		};
+	},
+
+	bind: function(bind, args){
+		var self = this;
+		if (args != null) args = Array.from(args);
+		return function(){
+			return self.apply(bind, args || arguments);
+		};
+	},
+
+	bindWithEvent: function(bind, args){
+		var self = this;
+		if (args != null) args = Array.from(args);
+		return function(event){
+			return self.apply(bind, (args == null) ? arguments : [event].concat(args));
+		};
+	},
+
+	run: function(args, bind){
+		return this.apply(bind, Array.from(args));
+	}
+
+});
+
+if (Object.create == Function.prototype.create) Object.create = null;
+
+var $try = Function.attempt;
+
+//</1.2compat>
+
 
 /*
-Script: Hash.js
-	Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
+---
 
-License:
-	MIT-style license.
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
 */
 
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+	subset: function(object, keys){
+		var results = {};
+		for (var i = 0, l = keys.length; i < l; i++){
+			var k = keys[i];
+			if (k in object) results[k] = object[k];
+		}
+		return results;
+	},
+
+	map: function(object, fn, bind){
+		var results = {};
+		for (var key in object){
+			if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+		}
+		return results;
+	},
+
+	filter: function(object, fn, bind){
+		var results = {};
+		for (var key in object){
+			var value = object[key];
+			if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+		}
+		return results;
+	},
+
+	every: function(object, fn, bind){
+		for (var key in object){
+			if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+		}
+		return true;
+	},
+
+	some: function(object, fn, bind){
+		for (var key in object){
+			if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+		}
+		return false;
+	},
+
+	keys: function(object){
+		var keys = [];
+		for (var key in object){
+			if (hasOwnProperty.call(object, key)) keys.push(key);
+		}
+		return keys;
+	},
+
+	values: function(object){
+		var values = [];
+		for (var key in object){
+			if (hasOwnProperty.call(object, key)) values.push(object[key]);
+		}
+		return values;
+	},
+
+	getLength: function(object){
+		return Object.keys(object).length;
+	},
+
+	keyOf: function(object, value){
+		for (var key in object){
+			if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+		}
+		return null;
+	},
+
+	contains: function(object, value){
+		return Object.keyOf(object, value) != null;
+	},
+
+	toQueryString: function(object, base){
+		var queryString = [];
+
+		Object.each(object, function(value, key){
+			if (base) key = base + '[' + key + ']';
+			var result;
+			switch (typeOf(value)){
+				case 'object': result = Object.toQueryString(value, key); break;
+				case 'array':
+					var qs = {};
+					value.each(function(val, i){
+						qs[i] = val;
+					});
+					result = Object.toQueryString(qs, key);
+				break;
+				default: result = key + '=' + encodeURIComponent(value);
+			}
+			if (value != null) queryString.push(result);
+		});
+
+		return queryString.join('&');
+	}
+
+});
+
+})();
+
+//<1.2compat>
+
 Hash.implement({
 
 	has: Object.prototype.hasOwnProperty,
 
 	keyOf: function(value){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && this[key] === value) return key;
-		}
-		return null;
+		return Object.keyOf(this, value);
 	},
 
 	hasValue: function(value){
-		return (Hash.keyOf(this, value) !== null);
+		return Object.contains(this, value);
 	},
 
 	extend: function(properties){
-		Hash.each(properties, function(value, key){
+		Hash.each(properties || {}, function(value, key){
 			Hash.set(this, key, value);
 		}, this);
 		return this;
 	},
 
 	combine: function(properties){
-		Hash.each(properties, function(value, key){
+		Hash.each(properties || {}, function(value, key){
 			Hash.include(this, key, value);
 		}, this);
 		return this;
@@ -836,183 +1139,397 @@
 	},
 
 	include: function(key, value){
-		var k = this[key];
-		if (k == undefined) this[key] = value;
+		if (this[key] == null) this[key] = value;
 		return this;
 	},
 
 	map: function(fn, bind){
-		var results = new Hash;
-		Hash.each(this, function(value, key){
-			results.set(key, fn.call(bind, value, key, this));
-		}, this);
-		return results;
+		return new Hash(Object.map(this, fn, bind));
 	},
 
 	filter: function(fn, bind){
-		var results = new Hash;
-		Hash.each(this, function(value, key){
-			if (fn.call(bind, value, key, this)) results.set(key, value);
-		}, this);
-		return results;
+		return new Hash(Object.filter(this, fn, bind));
 	},
 
 	every: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
-		}
-		return true;
+		return Object.every(this, fn, bind);
 	},
 
 	some: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
-		}
-		return false;
+		return Object.some(this, fn, bind);
 	},
 
 	getKeys: function(){
-		var keys = [];
-		Hash.each(this, function(value, key){
-			keys.push(key);
-		});
-		return keys;
+		return Object.keys(this);
 	},
 
 	getValues: function(){
-		var values = [];
-		Hash.each(this, function(value){
-			values.push(value);
-		});
-		return values;
+		return Object.values(this);
 	},
 
 	toQueryString: function(base){
-		var queryString = [];
-		Hash.each(this, function(value, key){
-			if (base) key = base + '[' + key + ']';
-			var result;
-			switch ($type(value)){
-				case 'object': result = Hash.toQueryString(value, key); break;
-				case 'array':
-					var qs = {};
-					value.each(function(val, i){
-						qs[i] = val;
-					});
-					result = Hash.toQueryString(qs, key);
-				break;
-				default: result = key + '=' + encodeURIComponent(value);
-			}
-			if (value != undefined) queryString.push(result);
-		});
-
-		return queryString.join('&');
+		return Object.toQueryString(this, base);
 	}
 
 });
 
-Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
+Hash.extend = Object.append;
+
+Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
+
+//</1.2compat>
 
 
 /*
-Script: Event.js
-	Contains the Event Native, to make the event object completely crossbrowser.
+---
 
-License:
-	MIT-style license.
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
 */
 
-var Event = new Native({
+(function(){
 
-	name: 'Event',
+var document = this.document;
+var window = document.window = this;
 
-	initialize: function(event, win){
-		win = win || window;
-		var doc = win.document;
-		event = event || win.event;
-		if (event.$extended) return event;
-		this.$extended = true;
-		var type = event.type;
-		var target = event.target || event.srcElement;
-		while (target && target.nodeType == 3) target = target.parentNode;
+var UID = 1;
 
-		if (type.test(/key/)){
-			var code = event.which || event.keyCode;
-			var key = Event.Keys.keyOf(code);
-			if (type == 'keydown'){
-				var fKey = code - 111;
-				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
-			}
-			key = key || String.fromCharCode(code).toLowerCase();
-		} else if (type.match(/(click|mouse|menu)/i)){
-			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
-			var page = {
-				x: event.pageX || event.clientX + doc.scrollLeft,
-				y: event.pageY || event.clientY + doc.scrollTop
-			};
-			var client = {
-				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
-				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
-			};
-			if (type.match(/DOMMouseScroll|mousewheel/)){
-				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
-			}
-			var rightClick = (event.which == 3) || (event.button == 2);
-			var related = null;
-			if (type.match(/over|out/)){
-				switch (type){
-					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
-					case 'mouseout': related = event.relatedTarget || event.toElement;
-				}
-				if (!(function(){
-					while (related && related.nodeType == 3) related = related.parentNode;
-					return true;
-				}).create({attempt: Browser.Engine.gecko})()) related = false;
-			}
+this.$uid = (window.ActiveXObject) ? function(item){
+	return (item.uid || (item.uid = [UID++]))[0];
+} : function(item){
+	return item.uid || (item.uid = UID++);
+};
+
+$uid(window);
+$uid(document);
+
+var ua = navigator.userAgent.toLowerCase(),
+	platform = navigator.platform.toLowerCase(),
+	UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
+	mode = UA[1] == 'ie' && document.documentMode;
+
+var Browser = this.Browser = {
+
+	extend: Function.prototype.extend,
+
+	name: (UA[1] == 'version') ? UA[3] : UA[1],
+
+	version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+
+	Platform: {
+		name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
+	},
+
+	Features: {
+		xpath: !!(document.evaluate),
+		air: !!(window.runtime),
+		query: !!(document.querySelector),
+		json: !!(window.JSON)
+	},
+
+	Plugins: {}
+
+};
+
+Browser[Browser.name] = true;
+Browser[Browser.name + parseInt(Browser.version, 10)] = true;
+Browser.Platform[Browser.Platform.name] = true;
+
+// Request
+
+Browser.Request = (function(){
+
+	var XMLHTTP = function(){
+		return new XMLHttpRequest();
+	};
+
+	var MSXML2 = function(){
+		return new ActiveXObject('MSXML2.XMLHTTP');
+	};
+
+	var MSXML = function(){
+		return new ActiveXObject('Microsoft.XMLHTTP');
+	};
+
+	return Function.attempt(function(){
+		XMLHTTP();
+		return XMLHTTP;
+	}, function(){
+		MSXML2();
+		return MSXML2;
+	}, function(){
+		MSXML();
+		return MSXML;
+	});
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+// Flash detection
+
+var version = (Function.attempt(function(){
+	return navigator.plugins['Shockwave Flash'].description;
+}, function(){
+	return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+}) || '0 r0').match(/\d+/g);
+
+Browser.Plugins.Flash = {
+	version: Number(version[0] || '0.' + version[1]) || 0,
+	build: Number(version[2]) || 0
+};
+
+// String scripts
+
+Browser.exec = function(text){
+	if (!text) return text;
+	if (window.execScript){
+		window.execScript(text);
+	} else {
+		var script = document.createElement('script');
+		script.setAttribute('type', 'text/javascript');
+		script.text = text;
+		document.head.appendChild(script);
+		document.head.removeChild(script);
+	}
+	return text;
+};
+
+String.implement('stripScripts', function(exec){
+	var scripts = '';
+	var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+		scripts += code + '\n';
+		return '';
+	});
+	if (exec === true) Browser.exec(scripts);
+	else if (typeOf(exec) == 'function') exec(scripts, text);
+	return text;
+});
+
+// Window, Document
+
+Browser.extend({
+	Document: this.Document,
+	Window: this.Window,
+	Element: this.Element,
+	Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.from('window').hide();
+
+Window.mirror(function(name, method){
+	window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.from('document').hide();
+
+Document.mirror(function(name, method){
+	document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+	document.execCommand("BackgroundImageCache", false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+	var unloadEvent = function(){
+		this.detachEvent('onunload', unloadEvent);
+		document.head = document.html = document.window = null;
+	};
+	this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.from;
+try {
+	arrayFrom(document.html.childNodes);
+} catch(e){
+	Array.from = function(item){
+		if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+			var i = item.length, array = new Array(i);
+			while (i--) array[i] = item[i];
+			return array;
 		}
+		return arrayFrom(item);
+	};
 
-		return $extend(this, {
-			event: event,
-			type: type,
+	var prototype = Array.prototype,
+		slice = prototype.slice;
+	['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+		var method = prototype[name];
+		Array[name] = function(item){
+			return method.apply(Array.from(item), slice.call(arguments, 1));
+		};
+	});
+}
+/*</ltIE9>*/
 
-			page: page,
-			client: client,
-			rightClick: rightClick,
+//<1.2compat>
 
-			wheel: wheel,
+if (Browser.Platform.ios) Browser.Platform.ipod = true;
 
-			relatedTarget: related,
-			target: target,
+Browser.Engine = {};
 
-			code: code,
-			key: key,
+var setEngine = function(name, version){
+	Browser.Engine.name = name;
+	Browser.Engine[name + version] = true;
+	Browser.Engine.version = version;
+};
 
-			shift: event.shiftKey,
-			control: event.ctrlKey,
-			alt: event.altKey,
-			meta: event.metaKey
-		});
+if (Browser.ie){
+	Browser.Engine.trident = true;
+
+	switch (Browser.version){
+		case 6: setEngine('trident', 4); break;
+		case 7: setEngine('trident', 5); break;
+		case 8: setEngine('trident', 6);
+	}
+}
+
+if (Browser.firefox){
+	Browser.Engine.gecko = true;
+
+	if (Browser.version >= 3) setEngine('gecko', 19);
+	else setEngine('gecko', 18);
+}
+
+if (Browser.safari || Browser.chrome){
+	Browser.Engine.webkit = true;
+
+	switch (Browser.version){
+		case 2: setEngine('webkit', 419); break;
+		case 3: setEngine('webkit', 420); break;
+		case 4: setEngine('webkit', 525);
+	}
+}
+
+if (Browser.opera){
+	Browser.Engine.presto = true;
+
+	if (Browser.version >= 9.6) setEngine('presto', 960);
+	else if (Browser.version >= 9.5) setEngine('presto', 950);
+	else setEngine('presto', 925);
+}
+
+if (Browser.name == 'unknown'){
+	switch ((ua.match(/(?:webkit|khtml|gecko)/) || [])[0]){
+		case 'webkit':
+		case 'khtml':
+			Browser.Engine.webkit = true;
+		break;
+		case 'gecko':
+			Browser.Engine.gecko = true;
+	}
+}
+
+this.$exec = Browser.exec;
+
+//</1.2compat>
+
+})();
+
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function() {
+
+var _keys = {};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+	if (!win) win = window;
+	event = event || win.event;
+	if (event.$extended) return event;
+	this.event = event;
+	this.$extended = true;
+	this.shift = event.shiftKey;
+	this.control = event.ctrlKey;
+	this.alt = event.altKey;
+	this.meta = event.metaKey;
+	var type = this.type = event.type;
+	var target = event.target || event.srcElement;
+	while (target && target.nodeType == 3) target = target.parentNode;
+	this.target = document.id(target);
+
+	if (type.indexOf('key') == 0){
+		var code = this.code = (event.which || event.keyCode);
+		this.key = _keys[code]/*<1.3compat>*/ || Object.keyOf(Event.Keys, code)/*</1.3compat>*/;
+		if (type == 'keydown'){
+			if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+			else if (code > 95 && code < 106) this.key = code - 96;
+		}
+		if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+	} else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+		var doc = win.document;
+		doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+		this.page = {
+			x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+			y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+		};
+		this.client = {
+			x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+			y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+		};
+		if (type == 'DOMMouseScroll' || type == 'mousewheel')
+			this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+
+		this.rightClick = (event.which == 3 || event.button == 2);
+		if (type == 'mouseover' || type == 'mouseout'){
+			var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
+			while (related && related.nodeType == 3) related = related.parentNode;
+			this.relatedTarget = document.id(related);
+		}
+	} else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+		this.rotation = event.rotation;
+		this.scale = event.scale;
+		this.targetTouches = event.targetTouches;
+		this.changedTouches = event.changedTouches;
+		var touches = this.touches = event.touches;
+		if (touches && touches[0]){
+			var touch = touches[0];
+			this.page = {x: touch.pageX, y: touch.pageY};
+			this.client = {x: touch.clientX, y: touch.clientY};
+		}
 	}
 
+	if (!this.client) this.client = {};
+	if (!this.page) this.page = {};
 });
 
-Event.Keys = new Hash({
-	'enter': 13,
-	'up': 38,
-	'down': 40,
-	'left': 37,
-	'right': 39,
-	'esc': 27,
-	'space': 32,
-	'backspace': 8,
-	'tab': 9,
-	'delete': 46
-});
-
-Event.implement({
+DOMEvent.implement({
 
 	stop: function(){
-		return this.stopPropagation().preventDefault();
+		return this.preventDefault().stopPropagation();
 	},
 
 	stopPropagation: function(){
@@ -1029,141 +1546,174 @@
 
 });
 
+DOMEvent.defineKey = function(code, key){
+	_keys[code] = key;
+	return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+	'38': 'up', '40': 'down', '37': 'left', '39': 'right',
+	'27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+	'46': 'delete', '13': 'enter'
+});
+
+})();
+
+/*<1.3compat>*/
+var Event = DOMEvent;
+Event.Keys = {};
+/*</1.3compat>*/
+
+/*<1.2compat>*/
+
+Event.Keys = new Hash(Event.Keys);
+
+/*</1.2compat>*/
+
 
 /*
-Script: Class.js
-	Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+---
 
-License:
-	MIT-style license.
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
 */
 
-var Class = new Native({
+(function(){
 
-	name: 'Class',
+var Class = this.Class = new Type('Class', function(params){
+	if (instanceOf(params, Function)) params = {initialize: params};
 
-	initialize: function(properties){
-		properties = properties || {};
-		var klass = function(){
-			for (var key in this){
-				if ($type(this[key]) != 'function') this[key] = $unlink(this[key]);
-			}
-			this.constructor = klass;
-			if (Class.prototyping) return this;
-			var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this;
-			if (this.options && this.options.initialize) this.options.initialize.call(this);
-			return instance;
-		};
+	var newClass = function(){
+		reset(this);
+		if (newClass.$prototyping) return this;
+		this.$caller = null;
+		var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+		this.$caller = this.caller = null;
+		return value;
+	}.extend(this).implement(params);
 
-		for (var mutator in Class.Mutators){
-			if (!properties[mutator]) continue;
-			properties = Class.Mutators[mutator](properties, properties[mutator]);
-			delete properties[mutator];
+	newClass.$constructor = Class;
+	newClass.prototype.$constructor = newClass;
+	newClass.prototype.parent = parent;
+
+	return newClass;
+});
+
+var parent = function(){
+	if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+	var name = this.$caller.$name,
+		parent = this.$caller.$owner.parent,
+		previous = (parent) ? parent.prototype[name] : null;
+	if (!previous) throw new Error('The method "' + name + '" has no parent.');
+	return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+	for (var key in object){
+		var value = object[key];
+		switch (typeOf(value)){
+			case 'object':
+				var F = function(){};
+				F.prototype = value;
+				object[key] = reset(new F);
+			break;
+			case 'array': object[key] = value.clone(); break;
 		}
+	}
+	return object;
+};
 
-		$extend(klass, this);
-		klass.constructor = Class;
-		klass.prototype = properties;
-		return klass;
+var wrap = function(self, key, method){
+	if (method.$origin) method = method.$origin;
+	var wrapper = function(){
+		if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+		var caller = this.caller, current = this.$caller;
+		this.caller = current; this.$caller = wrapper;
+		var result = method.apply(this, arguments);
+		this.$caller = current; this.caller = caller;
+		return result;
+	}.extend({$owner: self, $origin: method, $name: key});
+	return wrapper;
+};
+
+var implement = function(key, value, retain){
+	if (Class.Mutators.hasOwnProperty(key)){
+		value = Class.Mutators[key].call(this, value);
+		if (value == null) return this;
 	}
 
-});
+	if (typeOf(value) == 'function'){
+		if (value.$hidden) return this;
+		this.prototype[key] = (retain) ? value : wrap(this, key, value);
+	} else {
+		Object.merge(this.prototype, key, value);
+	}
+
+	return this;
+};
+
+var getInstance = function(klass){
+	klass.$prototyping = true;
+	var proto = new klass;
+	delete klass.$prototyping;
+	return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
 
 Class.Mutators = {
 
-	Extends: function(self, klass){
-		Class.prototyping = klass.prototype;
-		var subclass = new klass;
-		delete subclass.parent;
-		subclass = Class.inherit(subclass, self);
-		delete Class.prototyping;
-		return subclass;
+	Extends: function(parent){
+		this.parent = parent;
+		this.prototype = getInstance(parent);
 	},
 
-	Implements: function(self, klasses){
-		$splat(klasses).each(function(klass){
-			Class.prototying = klass;
-			$extend(self, ($type(klass) == 'class') ? new klass : klass);
-			delete Class.prototyping;
-		});
-		return self;
+	Implements: function(items){
+		Array.from(items).each(function(item){
+			var instance = new item;
+			for (var key in instance) implement.call(this, key, instance[key], true);
+		}, this);
 	}
-
 };
 
-Class.extend({
-
-	inherit: function(object, properties){
-		var caller = arguments.callee.caller;
-		for (var key in properties){
-			var override = properties[key];
-			var previous = object[key];
-			var type = $type(override);
-			if (previous && type == 'function'){
-				if (override != previous){
-					if (caller){
-						override.__parent = previous;
-						object[key] = override;
-					} else {
-						Class.override(object, key, override);
-					}
-				}
-			} else if(type == 'object'){
-				object[key] = $merge(previous, override);
-			} else {
-				object[key] = override;
-			}
-		}
-
-		if (caller) object.parent = function(){
-			return arguments.callee.caller.__parent.apply(this, arguments);
-		};
-
-		return object;
-	},
-
-	override: function(object, name, method){
-		var parent = Class.prototyping;
-		if (parent && object[name] != parent[name]) parent = null;
-		var override = function(){
-			var previous = this.parent;
-			this.parent = parent ? parent[name] : object[name];
-			var value = method.apply(this, arguments);
-			this.parent = previous;
-			return value;
-		};
-		object[name] = override;
-	}
-
-});
-
-Class.implement({
-
-	implement: function(){
-		var proto = this.prototype;
-		$each(arguments, function(properties){
-			Class.inherit(proto, properties);
-		});
-		return this;
-	}
-
-});
+})();
 
 
 /*
-Script: Class.Extras.js
-	Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+---
 
-License:
-	MIT-style license.
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
 */
 
-var Chain = new Class({
+(function(){
+
+this.Chain = new Class({
 
 	$chain: [],
 
 	chain: function(){
-		this.$chain.extend(Array.flatten(arguments));
+		this.$chain.append(Array.flatten(arguments));
 		return this;
 	},
 
@@ -1178,17 +1728,25 @@
 
 });
 
-var Events = new Class({
+var removeOn = function(string){
+	return string.replace(/^on([A-Z])/, function(full, first){
+		return first.toLowerCase();
+	});
+};
+
+this.Events = new Class({
 
 	$events: {},
 
 	addEvent: function(type, fn, internal){
-		type = Events.removeOn(type);
-		if (fn != $empty){
-			this.$events[type] = this.$events[type] || [];
-			this.$events[type].include(fn);
-			if (internal) fn.internal = true;
-		}
+		type = removeOn(type);
+
+		/*<1.2compat>*/
+		if (fn == $empty) return this;
+		/*</1.2compat>*/
+
+		this.$events[type] = (this.$events[type] || []).include(fn);
+		if (internal) fn.internal = true;
 		return this;
 	},
 
@@ -1198,178 +1756,1505 @@
 	},
 
 	fireEvent: function(type, args, delay){
-		type = Events.removeOn(type);
-		if (!this.$events || !this.$events[type]) return this;
-		this.$events[type].each(function(fn){
-			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
+		type = removeOn(type);
+		var events = this.$events[type];
+		if (!events) return this;
+		args = Array.from(args);
+		events.each(function(fn){
+			if (delay) fn.delay(delay, this, args);
+			else fn.apply(this, args);
 		}, this);
 		return this;
 	},
 
 	removeEvent: function(type, fn){
-		type = Events.removeOn(type);
-		if (!this.$events[type]) return this;
-		if (!fn.internal) this.$events[type].erase(fn);
+		type = removeOn(type);
+		var events = this.$events[type];
+		if (events && !fn.internal){
+			var index =  events.indexOf(fn);
+			if (index != -1) delete events[index];
+		}
 		return this;
 	},
 
 	removeEvents: function(events){
-		if ($type(events) == 'object'){
-			for (var type in events) this.removeEvent(type, events[type]);
+		var type;
+		if (typeOf(events) == 'object'){
+			for (type in events) this.removeEvent(type, events[type]);
 			return this;
 		}
-		if (events) events = Events.removeOn(events);
-		for (var type in this.$events){
+		if (events) events = removeOn(events);
+		for (type in this.$events){
 			if (events && events != type) continue;
 			var fns = this.$events[type];
-			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
+			for (var i = fns.length; i--;) if (i in fns){
+				this.removeEvent(type, fns[i]);
+			}
 		}
 		return this;
 	}
 
 });
 
-Events.removeOn = function(string){
-	return string.replace(/^on([A-Z])/, function(full, first) {
-		return first.toLowerCase();
-	});
-};
-
-var Options = new Class({
+this.Options = new Class({
 
 	setOptions: function(){
-		this.options = $merge.run([this.options].extend(arguments));
-		if (!this.addEvent) return this;
-		for (var option in this.options){
-			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
-			this.addEvent(option, this.options[option]);
-			delete this.options[option];
+		var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+		if (this.addEvent) for (var option in options){
+			if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+			this.addEvent(option, options[option]);
+			delete options[option];
 		}
 		return this;
 	}
 
 });
 
+})();
+
 
 /*
-Script: Element.js
-	One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
-	time-saver methods to let you easily work with HTML Elements.
-
-License:
-	MIT-style license.
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
 */
 
-var Element = new Native({
+;(function(){
 
-	name: 'Element',
+var parsed,
+	separatorIndex,
+	combinatorIndex,
+	reversed,
+	cache = {},
+	reverseCache = {},
+	reUnescape = /\\/g;
 
-	legacy: window.Element,
+var parse = function(expression, isReversed){
+	if (expression == null) return null;
+	if (expression.Slick === true) return expression;
+	expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+	reversed = !!isReversed;
+	var currentCache = (reversed) ? reverseCache : cache;
+	if (currentCache[expression]) return currentCache[expression];
+	parsed = {
+		Slick: true,
+		expressions: [],
+		raw: expression,
+		reverse: function(){
+			return parse(this.raw, true);
+		}
+	};
+	separatorIndex = -1;
+	while (expression != (expression = expression.replace(regexp, parser)));
+	parsed.length = parsed.expressions.length;
+	return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
 
-	initialize: function(tag, props){
-		var konstructor = Element.Constructors.get(tag);
-		if (konstructor) return konstructor(props);
-		if (typeof tag == 'string') return document.newElement(tag, props);
-		return $(tag).set(props);
-	},
+var reverseCombinator = function(combinator){
+	if (combinator === '!') return ' ';
+	else if (combinator === ' ') return '!';
+	else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+	else return '!' + combinator;
+};
 
-	afterImplement: function(key, value){
-		Element.Prototype[key] = value;
-		if (Array[key]) return;
-		Elements.implement(key, function(){
-			var items = [], elements = true;
-			for (var i = 0, j = this.length; i < j; i++){
-				var returns = this[i][key].apply(this[i], arguments);
-				items.push(returns);
-				if (elements) elements = ($type(returns) == 'element');
-			}
-			return (elements) ? new Elements(items) : items;
-		});
+var reverse = function(expression){
+	var expressions = expression.expressions;
+	for (var i = 0; i < expressions.length; i++){
+		var exp = expressions[i];
+		var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+		for (var j = 0; j < exp.length; j++){
+			var cexp = exp[j];
+			if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+			cexp.combinator = cexp.reverseCombinator;
+			delete cexp.reverseCombinator;
+		}
+
+		exp.reverse().push(last);
+	}
+	return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+	return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+		return '\\' + match;
+	});
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+	"(?x)^(?:\
+	  \\s* ( , ) \\s*               # Separator          \n\
+	| \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
+	|      ( \\s+ )                 # CombinatorChildren \n\
+	|      ( <unicode>+ | \\* )     # Tag                \n\
+	| \\#  ( <unicode>+       )     # ID                 \n\
+	| \\.  ( <unicode>+       )     # ClassName          \n\
+	|                               # Attribute          \n\
+	\\[  \
+		\\s* (<unicode1>+)  (?:  \
+			\\s* ([*^$!~|]?=)  (?:  \
+				\\s* (?:\
+					([\"']?)(.*?)\\9 \
+				)\
+			)  \
+		)?  \\s*  \
+	\\](?!\\]) \n\
+	|   :+ ( <unicode>+ )(?:\
+	\\( (?:\
+		(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+	) \\)\
+	)?\
+	)"
+*/
+	"^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+	.replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
+	.replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+	.replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+	rawMatch,
+
+	separator,
+	combinator,
+	combinatorChildren,
+
+	tagName,
+	id,
+	className,
+
+	attributeKey,
+	attributeOperator,
+	attributeQuote,
+	attributeValue,
+
+	pseudoMarker,
+	pseudoClass,
+	pseudoQuote,
+	pseudoClassQuotedValue,
+	pseudoClassValue
+){
+	if (separator || separatorIndex === -1){
+		parsed.expressions[++separatorIndex] = [];
+		combinatorIndex = -1;
+		if (separator) return '';
 	}
 
+	if (combinator || combinatorChildren || combinatorIndex === -1){
+		combinator = combinator || ' ';
+		var currentSeparator = parsed.expressions[separatorIndex];
+		if (reversed && currentSeparator[combinatorIndex])
+			currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+		currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+	}
+
+	var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+	if (tagName){
+		currentParsed.tag = tagName.replace(reUnescape, '');
+
+	} else if (id){
+		currentParsed.id = id.replace(reUnescape, '');
+
+	} else if (className){
+		className = className.replace(reUnescape, '');
+
+		if (!currentParsed.classList) currentParsed.classList = [];
+		if (!currentParsed.classes) currentParsed.classes = [];
+		currentParsed.classList.push(className);
+		currentParsed.classes.push({
+			value: className,
+			regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+		});
+
+	} else if (pseudoClass){
+		pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+		pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+		if (!currentParsed.pseudos) currentParsed.pseudos = [];
+		currentParsed.pseudos.push({
+			key: pseudoClass.replace(reUnescape, ''),
+			value: pseudoClassValue,
+			type: pseudoMarker.length == 1 ? 'class' : 'element'
+		});
+
+	} else if (attributeKey){
+		attributeKey = attributeKey.replace(reUnescape, '');
+		attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+		var test, regexp;
+
+		switch (attributeOperator){
+			case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
+			case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
+			case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+			case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
+			case  '=' : test = function(value){
+				return attributeValue == value;
+			}; break;
+			case '*=' : test = function(value){
+				return value && value.indexOf(attributeValue) > -1;
+			}; break;
+			case '!=' : test = function(value){
+				return attributeValue != value;
+			}; break;
+			default   : test = function(value){
+				return !!value;
+			};
+		}
+
+		if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+			return false;
+		};
+
+		if (!test) test = function(value){
+			return value && regexp.test(value);
+		};
+
+		if (!currentParsed.attributes) currentParsed.attributes = [];
+		currentParsed.attributes.push({
+			key: attributeKey,
+			operator: attributeOperator,
+			value: attributeValue,
+			test: test
+		});
+
+	}
+
+	return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+	return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+	featuresCache = {},
+	toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+	return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+	return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+	(document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+	// convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+	var nodeType = document.nodeType;
+	if (nodeType == 9); // document
+	else if (nodeType) document = document.ownerDocument; // node
+	else if (document.navigator) document = document.document; // window
+	else return;
+
+	// check if it's the old document
+
+	if (this.document === document) return;
+	this.document = document;
+
+	// check if we have done feature detection on this document before
+
+	var root = document.documentElement,
+		rootUid = this.getUIDXML(root),
+		features = featuresCache[rootUid],
+		feature;
+
+	if (features){
+		for (feature in features){
+			this[feature] = features[feature];
+		}
+		return;
+	}
+
+	features = featuresCache[rootUid] = {};
+
+	features.root = root;
+	features.isXMLDocument = this.isXML(document);
+
+	features.brokenStarGEBTN
+	= features.starSelectsClosedQSA
+	= features.idGetsName
+	= features.brokenMixedCaseQSA
+	= features.brokenGEBCN
+	= features.brokenCheckedQSA
+	= features.brokenEmptyAttributeQSA
+	= features.isHTMLDocument
+	= features.nativeMatchesSelector
+	= false;
+
+	var starSelectsClosed, starSelectsComments,
+		brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+		brokenFormAttributeGetter;
+
+	var selected, id = 'slick_uniqueid';
+	var testNode = document.createElement('div');
+
+	var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+	testRoot.appendChild(testNode);
+
+	// on non-HTML documents innerHTML and getElementsById doesnt work properly
+	try {
+		testNode.innerHTML = '<a id="'+id+'"></a>';
+		features.isHTMLDocument = !!document.getElementById(id);
+	} catch(e){};
+
+	if (features.isHTMLDocument){
+
+		testNode.style.display = 'none';
+
+		// IE returns comment nodes for getElementsByTagName('*') for some documents
+		testNode.appendChild(document.createComment(''));
+		starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+		// IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+		try {
+			testNode.innerHTML = 'foo</foo>';
+			selected = testNode.getElementsByTagName('*');
+			starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+		} catch(e){};
+
+		features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+		// IE returns elements with the name instead of just id for getElementsById for some documents
+		try {
+			testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+			features.idGetsName = document.getElementById(id) === testNode.firstChild;
+		} catch(e){};
+
+		if (testNode.getElementsByClassName){
+
+			// Safari 3.2 getElementsByClassName caches results
+			try {
+				testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+				testNode.getElementsByClassName('b').length;
+				testNode.firstChild.className = 'b';
+				cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+			} catch(e){};
+
+			// Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+			try {
+				testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+				brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+			} catch(e){};
+
+			features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+		}
+
+		if (testNode.querySelectorAll){
+			// IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+			try {
+				testNode.innerHTML = 'foo</foo>';
+				selected = testNode.querySelectorAll('*');
+				features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+			} catch(e){};
+
+			// Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+			try {
+				testNode.innerHTML = '<a class="MiX"></a>';
+				features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+			} catch(e){};
+
+			// Webkit and Opera dont return selected options on querySelectorAll
+			try {
+				testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+				features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+			} catch(e){};
+
+			// IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+			try {
+				testNode.innerHTML = '<a class=""></a>';
+				features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+			} catch(e){};
+
+		}
+
+		// IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+		try {
+			testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+			brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+		} catch(e){};
+
+		// native matchesSelector function
+
+		features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+		if (features.nativeMatchesSelector) try {
+			// if matchesSelector trows errors on incorrect sintaxes we can use it
+			features.nativeMatchesSelector.call(root, ':slick');
+			features.nativeMatchesSelector = null;
+		} catch(e){};
+
+	}
+
+	try {
+		root.slick_expando = 1;
+		delete root.slick_expando;
+		features.getUID = this.getUIDHTML;
+	} catch(e) {
+		features.getUID = this.getUIDXML;
+	}
+
+	testRoot.removeChild(testNode);
+	testNode = selected = testRoot = null;
+
+	// getAttribute
+
+	features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+		var method = this.attributeGetters[name];
+		if (method) return method.call(node);
+		var attributeNode = node.getAttributeNode(name);
+		return (attributeNode) ? attributeNode.nodeValue : null;
+	} : function(node, name){
+		var method = this.attributeGetters[name];
+		return (method) ? method.call(node) : node.getAttribute(name);
+	};
+
+	// hasAttribute
+
+	features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
+		return node.hasAttribute(attribute);
+	} : function(node, attribute) {
+		node = node.getAttributeNode(attribute);
+		return !!(node && (node.specified || node.nodeValue));
+	};
+
+	// contains
+	// FIXME: Add specs: local.contains should be different for xml and html documents?
+	features.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){
+		return context.contains(node);
+	} : (root && root.compareDocumentPosition) ? function(context, node){
+		return context === node || !!(context.compareDocumentPosition(node) & 16);
+	} : function(context, node){
+		if (node) do {
+			if (node === context) return true;
+		} while ((node = node.parentNode));
+		return false;
+	};
+
+	// document order sorting
+	// credits to Sizzle (http://sizzlejs.com/)
+
+	features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+		if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+		return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+	} : ('sourceIndex' in root) ? function(a, b){
+		if (!a.sourceIndex || !b.sourceIndex) return 0;
+		return a.sourceIndex - b.sourceIndex;
+	} : (document.createRange) ? function(a, b){
+		if (!a.ownerDocument || !b.ownerDocument) return 0;
+		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+		aRange.setStart(a, 0);
+		aRange.setEnd(a, 0);
+		bRange.setStart(b, 0);
+		bRange.setEnd(b, 0);
+		return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+	} : null ;
+
+	root = null;
+
+	for (feature in features){
+		this[feature] = features[feature];
+	}
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+	reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+	qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+	var found = this.found = (first) ? null : (append || []);
+
+	if (!context) return found;
+	else if (context.navigator) context = context.document; // Convert the node from a window to a document
+	else if (!context.nodeType) return found;
+
+	// setup
+
+	var parsed, i,
+		uniques = this.uniques = {},
+		hasOthers = !!(append && append.length),
+		contextIsDocument = (context.nodeType == 9);
+
+	if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+	// avoid duplicating items already in the append array
+	if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+	// expression checks
+
+	if (typeof expression == 'string'){ // expression is a string
+
+		/*<simple-selectors-override>*/
+		var simpleSelector = expression.match(reSimpleSelector);
+		simpleSelectors: if (simpleSelector) {
+
+			var symbol = simpleSelector[1],
+				name = simpleSelector[2],
+				node, nodes;
+
+			if (!symbol){
+
+				if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+				nodes = context.getElementsByTagName(name);
+				if (first) return nodes[0] || null;
+				for (i = 0; node = nodes[i++];){
+					if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+				}
+
+			} else if (symbol == '#'){
+
+				if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+				node = context.getElementById(name);
+				if (!node) return found;
+				if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+				if (first) return node || null;
+				if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+			} else if (symbol == '.'){
+
+				if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+				if (context.getElementsByClassName && !this.brokenGEBCN){
+					nodes = context.getElementsByClassName(name);
+					if (first) return nodes[0] || null;
+					for (i = 0; node = nodes[i++];){
+						if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+					}
+				} else {
+					var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+					nodes = context.getElementsByTagName('*');
+					for (i = 0; node = nodes[i++];){
+						className = node.className;
+						if (!(className && matchClass.test(className))) continue;
+						if (first) return node;
+						if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+					}
+				}
+
+			}
+
+			if (hasOthers) this.sort(found);
+			return (first) ? null : found;
+
+		}
+		/*</simple-selectors-override>*/
+
+		/*<query-selector-override>*/
+		querySelector: if (context.querySelectorAll) {
+
+			if (!this.isHTMLDocument
+				|| qsaFailExpCache[expression]
+				//TODO: only skip when expression is actually mixed case
+				|| this.brokenMixedCaseQSA
+				|| (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+				|| (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+				|| (!contextIsDocument //Abort when !contextIsDocument and...
+					//  there are multiple expressions in the selector
+					//  since we currently only fix non-document rooted QSA for single expression selectors
+					&& expression.indexOf(',') > -1
+				)
+				|| Slick.disableQSA
+			) break querySelector;
+
+			var _expression = expression, _context = context;
+			if (!contextIsDocument){
+				// non-document rooted QSA
+				// credits to Andrew Dupont
+				var currentId = _context.getAttribute('id'), slickid = 'slickid__';
+				_context.setAttribute('id', slickid);
+				_expression = '#' + slickid + ' ' + _expression;
+				context = _context.parentNode;
+			}
+
+			try {
+				if (first) return context.querySelector(_expression) || null;
+				else nodes = context.querySelectorAll(_expression);
+			} catch(e) {
+				qsaFailExpCache[expression] = 1;
+				break querySelector;
+			} finally {
+				if (!contextIsDocument){
+					if (currentId) _context.setAttribute('id', currentId);
+					else _context.removeAttribute('id');
+					context = _context;
+				}
+			}
+
+			if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+				if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+			} else for (i = 0; node = nodes[i++];){
+				if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+			}
+
+			if (hasOthers) this.sort(found);
+			return found;
+
+		}
+		/*</query-selector-override>*/
+
+		parsed = this.Slick.parse(expression);
+		if (!parsed.length) return found;
+	} else if (expression == null){ // there is no expression
+		return found;
+	} else if (expression.Slick){ // expression is a parsed Slick object
+		parsed = expression;
+	} else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+		(found) ? found.push(expression) : found = expression;
+		return found;
+	} else { // other junk
+		return found;
+	}
+
+	/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+	// cache elements for the nth selectors
+
+	this.posNTH = {};
+	this.posNTHLast = {};
+	this.posNTHType = {};
+	this.posNTHTypeLast = {};
+
+	/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+	// if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+	this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+	if (found == null) found = [];
+
+	// default engine
+
+	var j, m, n;
+	var combinator, tag, id, classList, classes, attributes, pseudos;
+	var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+	search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+		combinator = 'combinator:' + currentBit.combinator;
+		if (!this[combinator]) continue search;
+
+		tag        = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+		id         = currentBit.id;
+		classList  = currentBit.classList;
+		classes    = currentBit.classes;
+		attributes = currentBit.attributes;
+		pseudos    = currentBit.pseudos;
+		lastBit    = (j === (currentExpression.length - 1));
+
+		this.bitUniques = {};
+
+		if (lastBit){
+			this.uniques = uniques;
+			this.found = found;
+		} else {
+			this.uniques = {};
+			this.found = [];
+		}
+
+		if (j === 0){
+			this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+			if (first && lastBit && found.length) break search;
+		} else {
+			if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+				this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+				if (found.length) break search;
+			} else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+		}
+
+		currentItems = this.found;
+	}
+
+	// should sort if there are nodes in append and if you pass multiple expressions.
+	if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+	return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+	var uid = node.getAttribute(this.uidk);
+	if (!uid){
+		uid = this.uidx++;
+		node.setAttribute(this.uidk, uid);
+	}
+	return uid;
+};
+
+local.getUIDHTML = function(node){
+	return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+	if (!this.documentSorter) return results;
+	results.sort(this.documentSorter);
+	return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+	var parsed = argument.match(this.matchNTH);
+	if (!parsed) return false;
+	var special = parsed[2] || false;
+	var a = parsed[1] || 1;
+	if (a == '-') a = -1;
+	var b = +parsed[3] || 0;
+	parsed =
+		(special == 'n')	? {a: a, b: b} :
+		(special == 'odd')	? {a: 2, b: 1} :
+		(special == 'even')	? {a: 2, b: 0} : {a: 0, b: a};
+
+	return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+	return function(node, argument){
+		var uid = this.getUID(node);
+		if (!this[positions][uid]){
+			var parent = node.parentNode;
+			if (!parent) return false;
+			var el = parent[child], count = 1;
+			if (ofType){
+				var nodeName = node.nodeName;
+				do {
+					if (el.nodeName != nodeName) continue;
+					this[positions][this.getUID(el)] = count++;
+				} while ((el = el[sibling]));
+			} else {
+				do {
+					if (el.nodeType != 1) continue;
+					this[positions][this.getUID(el)] = count++;
+				} while ((el = el[sibling]));
+			}
+		}
+		argument = argument || 'n';
+		var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+		if (!parsed) return false;
+		var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+		if (a == 0) return b == pos;
+		if (a > 0){
+			if (pos < b) return false;
+		} else {
+			if (b < pos) return false;
+		}
+		return ((pos - b) % a) == 0;
+	};
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+	if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+	var uid = this.getUID(node);
+	if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+		this.uniques[uid] = true;
+		this.found.push(node);
+	}
+};
+
+local.matchNode = function(node, selector){
+	if (this.isHTMLDocument && this.nativeMatchesSelector){
+		try {
+			return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+		} catch(matchError) {}
+	}
+
+	var parsed = this.Slick.parse(selector);
+	if (!parsed) return true;
+
+	// simple (single) selectors
+	var expressions = parsed.expressions, simpleExpCounter = 0, i;
+	for (i = 0; (currentExpression = expressions[i]); i++){
+		if (currentExpression.length == 1){
+			var exp = currentExpression[0];
+			if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+			simpleExpCounter++;
+		}
+	}
+
+	if (simpleExpCounter == parsed.length) return false;
+
+	var nodes = this.search(this.document, parsed), item;
+	for (i = 0; item = nodes[i++];){
+		if (item === node) return true;
+	}
+	return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+	var pseudoName = 'pseudo:' + name;
+	if (this[pseudoName]) return this[pseudoName](node, argument);
+	var attribute = this.getAttribute(node, name);
+	return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+	if (tag){
+		var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+		if (tag == '*'){
+			if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+		} else {
+			if (nodeName != tag) return false;
+		}
+	}
+
+	if (id && node.getAttribute('id') != id) return false;
+
+	var i, part, cls;
+	if (classes) for (i = classes.length; i--;){
+		cls = node.getAttribute('class') || node.className;
+		if (!(cls && classes[i].regexp.test(cls))) return false;
+	}
+	if (attributes) for (i = attributes.length; i--;){
+		part = attributes[i];
+		if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+	}
+	if (pseudos) for (i = pseudos.length; i--;){
+		part = pseudos[i];
+		if (!this.matchPseudo(node, part.key, part.value)) return false;
+	}
+	return true;
+};
+
+var combinators = {
+
+	' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+		var i, item, children;
+
+		if (this.isHTMLDocument){
+			getById: if (id){
+				item = this.document.getElementById(id);
+				if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+					// all[id] returns all the elements with that name or id inside node
+					// if theres just one it will return the element, else it will be a collection
+					children = node.all[id];
+					if (!children) return;
+					if (!children[0]) children = [children];
+					for (i = 0; item = children[i++];){
+						var idNode = item.getAttributeNode('id');
+						if (idNode && idNode.nodeValue == id){
+							this.push(item, tag, null, classes, attributes, pseudos);
+							break;
+						}
+					}
+					return;
+				}
+				if (!item){
+					// if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+					if (this.contains(this.root, node)) return;
+					else break getById;
+				} else if (this.document !== node && !this.contains(node, item)) return;
+				this.push(item, tag, null, classes, attributes, pseudos);
+				return;
+			}
+			getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+				children = node.getElementsByClassName(classList.join(' '));
+				if (!(children && children.length)) break getByClass;
+				for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+				return;
+			}
+		}
+		getByTag: {
+			children = node.getElementsByTagName(tag);
+			if (!(children && children.length)) break getByTag;
+			if (!this.brokenStarGEBTN) tag = null;
+			for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+		}
+	},
+
+	'>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+		if ((node = node.firstChild)) do {
+			if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+		} while ((node = node.nextSibling));
+	},
+
+	'+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+		while ((node = node.nextSibling)) if (node.nodeType == 1){
+			this.push(node, tag, id, classes, attributes, pseudos);
+			break;
+		}
+	},
+
+	'^': function(node, tag, id, classes, attributes, pseudos){ // first child
+		node = node.firstChild;
+		if (node){
+			if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+			else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+		}
+	},
+
+	'~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+		while ((node = node.nextSibling)){
+			if (node.nodeType != 1) continue;
+			var uid = this.getUID(node);
+			if (this.bitUniques[uid]) break;
+			this.bitUniques[uid] = true;
+			this.push(node, tag, id, classes, attributes, pseudos);
+		}
+	},
+
+	'++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+		this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+		this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+	},
+
+	'~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+		this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+		this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+	},
+
+	'!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+		while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+	},
+
+	'!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+		node = node.parentNode;
+		if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+	},
+
+	'!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+		while ((node = node.previousSibling)) if (node.nodeType == 1){
+			this.push(node, tag, id, classes, attributes, pseudos);
+			break;
+		}
+	},
+
+	'!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+		node = node.lastChild;
+		if (node){
+			if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+			else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+		}
+	},
+
+	'!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+		while ((node = node.previousSibling)){
+			if (node.nodeType != 1) continue;
+			var uid = this.getUID(node);
+			if (this.bitUniques[uid]) break;
+			this.bitUniques[uid] = true;
+			this.push(node, tag, id, classes, attributes, pseudos);
+		}
+	}
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+	/*<pseudo-selectors>*/
+
+	'empty': function(node){
+		var child = node.firstChild;
+		return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+	},
+
+	'not': function(node, expression){
+		return !this.matchNode(node, expression);
+	},
+
+	'contains': function(node, text){
+		return (node.innerText || node.textContent || '').indexOf(text) > -1;
+	},
+
+	'first-child': function(node){
+		while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+		return true;
+	},
+
+	'last-child': function(node){
+		while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+		return true;
+	},
+
+	'only-child': function(node){
+		var prev = node;
+		while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+		var next = node;
+		while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+		return true;
+	},
+
+	/*<nth-pseudo-selectors>*/
+
+	'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+	'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+	'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+	'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+	'index': function(node, index){
+		return this['pseudo:nth-child'](node, '' + index + 1);
+	},
+
+	'even': function(node){
+		return this['pseudo:nth-child'](node, '2n');
+	},
+
+	'odd': function(node){
+		return this['pseudo:nth-child'](node, '2n+1');
+	},
+
+	/*</nth-pseudo-selectors>*/
+
+	/*<of-type-pseudo-selectors>*/
+
+	'first-of-type': function(node){
+		var nodeName = node.nodeName;
+		while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+		return true;
+	},
+
+	'last-of-type': function(node){
+		var nodeName = node.nodeName;
+		while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+		return true;
+	},
+
+	'only-of-type': function(node){
+		var prev = node, nodeName = node.nodeName;
+		while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+		var next = node;
+		while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+		return true;
+	},
+
+	/*</of-type-pseudo-selectors>*/
+
+	// custom pseudos
+
+	'enabled': function(node){
+		return !node.disabled;
+	},
+
+	'disabled': function(node){
+		return node.disabled;
+	},
+
+	'checked': function(node){
+		return node.checked || node.selected;
+	},
+
+	'focus': function(node){
+		return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+	},
+
+	'root': function(node){
+		return (node === this.root);
+	},
+
+	'selected': function(node){
+		return node.selected;
+	}
+
+	/*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+	'class': function(){
+		return this.getAttribute('class') || this.className;
+	},
+
+	'for': function(){
+		return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+	},
+
+	'href': function(){
+		return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+	},
+
+	'style': function(){
+		return (this.style) ? this.style.cssText : this.getAttribute('style');
+	},
+
+	'tabindex': function(){
+		var attributeNode = this.getAttributeNode('tabindex');
+		return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+	},
+
+	'type': function(){
+		return this.getAttribute('type');
+	},
+
+	'maxlength': function(){
+		var attributeNode = this.getAttributeNode('maxLength');
+		return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+	}
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.6';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+	return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+	return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+	local.setDocument(container);
+	return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+	local.setDocument(node);
+	return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+	local.setDocument(node);
+	return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+	if (!(node && selector)) return false;
+	if (!selector || selector === node) return true;
+	local.setDocument(node);
+	return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+	local.attributeGetters[name] = fn;
+	return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+	return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+	local['pseudo:' + name] = function(node, argument){
+		return fn.call(node, argument);
+	};
+	return this;
+};
+
+Slick.lookupPseudo = function(name){
+	var pseudo = local['pseudo:' + name];
+	if (pseudo) return function(argument){
+		return pseudo.call(this, argument);
+	};
+	return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+	local.override(regexp, fn);
+	return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+	return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+
+name: Element
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, Iframe, Selectors]
+
+...
+*/
+
+var Element = function(tag, props){
+	var konstructor = Element.Constructors[tag];
+	if (konstructor) return konstructor(props);
+	if (typeof tag != 'string') return document.id(tag).set(props);
+
+	if (!props) props = {};
+
+	if (!(/^[\w-]+$/).test(tag)){
+		var parsed = Slick.parse(tag).expressions[0][0];
+		tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+		if (parsed.id && props.id == null) props.id = parsed.id;
+
+		var attributes = parsed.attributes;
+		if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+			attr = attributes[i];
+			if (props[attr.key] != null) continue;
+
+			if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+			else if (!attr.value && !attr.operator) props[attr.key] = true;
+		}
+
+		if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+	}
+
+	return document.newElement(tag, props);
+};
+
+if (Browser.Element) Element.prototype = Browser.Element.prototype;
+
+new Type('Element', Element).mirror(function(name){
+	if (Array.prototype[name]) return;
+
+	var obj = {};
+	obj[name] = function(){
+		var results = [], args = arguments, elements = true;
+		for (var i = 0, l = this.length; i < l; i++){
+			var element = this[i], result = results[i] = element[name].apply(element, args);
+			elements = (elements && typeOf(result) == 'element');
+		}
+		return (elements) ? new Elements(results) : results;
+	};
+
+	Elements.implement(obj);
 });
 
-Element.Prototype = {$family: {name: 'element'}};
+if (!Browser.Element){
+	Element.parent = Object;
+
+	Element.Prototype = {'$family': Function.from('element').hide()};
+
+	Element.mirror(function(name, method){
+		Element.Prototype[name] = method;
+	});
+}
+
+Element.Constructors = {};
+
+//<1.2compat>
 
 Element.Constructors = new Hash;
 
-var IFrame = new Native({
+//</1.2compat>
 
-	name: 'IFrame',
-
-	generics: false,
-
-	initialize: function(){
-		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
-		var props = params.properties || {};
-		var iframe = $(params.iframe) || false;
-		var onload = props.onload || $empty;
-		delete props.onload;
-		props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
-		iframe = new Element(iframe || 'iframe', props);
-		var onFrameLoad = function(){
-			var host = $try(function(){
-				return iframe.contentWindow.location.host;
-			});
-			if (host && host == window.location.host){
-				var win = new Window(iframe.contentWindow);
-				new Document(iframe.contentWindow.document);
-				$extend(win.Element.prototype, Element.Prototype);
-			}
-			onload.call(iframe.contentWindow, iframe.contentWindow.document);
-		};
-		(window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
-		return iframe;
-	}
-
-});
-
-var Elements = new Native({
-
-	initialize: function(elements, options){
-		options = $extend({ddup: true, cash: true}, options);
-		elements = elements || [];
-		if (options.ddup || options.cash){
-			var uniques = {}, returned = [];
-			for (var i = 0, l = elements.length; i < l; i++){
-				var el = $.element(elements[i], !options.cash);
-				if (options.ddup){
-					if (uniques[el.uid]) continue;
-					uniques[el.uid] = true;
-				}
-				returned.push(el);
-			}
-			elements = returned;
+var IFrame = new Type('IFrame', function(){
+	var params = Array.link(arguments, {
+		properties: Type.isObject,
+		iframe: function(obj){
+			return (obj != null);
 		}
-		return (options.cash) ? $extend(elements, this) : elements;
-	}
+	});
 
+	var props = params.properties || {}, iframe;
+	if (params.iframe) iframe = document.id(params.iframe);
+	var onload = props.onload || function(){};
+	delete props.onload;
+	props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+	iframe = new Element(iframe || 'iframe', props);
+
+	var onLoad = function(){
+		onload.call(iframe.contentWindow);
+	};
+
+	if (window.frames[props.id]) onLoad();
+	else iframe.addListener('load', onLoad);
+	return iframe;
 });
 
-Elements.implement({
+var Elements = this.Elements = function(nodes){
+	if (nodes && nodes.length){
+		var uniques = {}, node;
+		for (var i = 0; node = nodes[i++];){
+			var uid = Slick.uidOf(node);
+			if (!uniques[uid]){
+				uniques[uid] = true;
+				this.push(node);
+			}
+		}
+	}
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('Elements', Elements).implement({
 
 	filter: function(filter, bind){
 		if (!filter) return this;
-		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
+		return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
 			return item.match(filter);
 		} : filter, bind));
-	}
+	}.protect(),
+
+	push: function(){
+		var length = this.length;
+		for (var i = 0, l = arguments.length; i < l; i++){
+			var item = document.id(arguments[i]);
+			if (item) this[length++] = item;
+		}
+		return (this.length = length);
+	}.protect(),
+
+	unshift: function(){
+		var items = [];
+		for (var i = 0, l = arguments.length; i < l; i++){
+			var item = document.id(arguments[i]);
+			if (item) items.push(item);
+		}
+		return Array.prototype.unshift.apply(this, items);
+	}.protect(),
+
+	concat: function(){
+		var newElements = new Elements(this);
+		for (var i = 0, l = arguments.length; i < l; i++){
+			var item = arguments[i];
+			if (Type.isEnumerable(item)) newElements.append(item);
+			else newElements.push(item);
+		}
+		return newElements;
+	}.protect(),
+
+	append: function(collection){
+		for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+		return this;
+	}.protect(),
+
+	empty: function(){
+		while (this.length) delete this[--this.length];
+		return this;
+	}.protect()
 
 });
 
+//<1.2compat>
+
+Elements.alias('extend', 'append');
+
+//</1.2compat>
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+	var length = this.length;
+	var result = splice.apply(this, arguments);
+	while (length >= this.length) delete this[length--];
+	return result;
+}.protect());
+
+Elements.implement(Array.prototype);
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+	var x = document.createElement('<input name=x>');
+	createElementAcceptsHTML = (x.name == 'x');
+} catch(e){}
+
+var escapeQuotes = function(html){
+	return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
 Document.implement({
 
 	newElement: function(tag, props){
-		if (Browser.Engine.trident && props){
-			['name', 'type', 'checked'].each(function(attribute){
-				if (!props[attribute]) return;
-				tag += ' ' + attribute + '="' + props[attribute] + '"';
-				if (attribute != 'checked') delete props[attribute];
-			});
-			tag = '<' + tag + '>';
+		if (props && props.checked != null) props.defaultChecked = props.checked;
+		/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+		if (createElementAcceptsHTML && props){
+			tag = '<' + tag;
+			if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+			if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+			tag += '>';
+			delete props.name;
+			delete props.type;
 		}
-		return $.element(this.createElement(tag)).set(props);
-	},
+		/*</ltIE8>*/
+		return this.id(this.createElement(tag)).set(props);
+	}
+
+});
+
+})();
+
+Document.implement({
 
 	newTextNode: function(text){
 		return this.createTextNode(text);
@@ -1381,32 +3266,52 @@
 
 	getWindow: function(){
 		return this.window;
-	}
+	},
+
+	id: (function(){
+
+		var types = {
+
+			string: function(id, nocash, doc){
+				id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+				return (id) ? types.element(id, nocash) : null;
+			},
+
+			element: function(el, nocash){
+				$uid(el);
+				if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+					Object.append(el, Element.Prototype);
+				}
+				return el;
+			},
+
+			object: function(obj, nocash, doc){
+				if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+				return null;
+			}
+
+		};
+
+		types.textnode = types.whitespace = types.window = types.document = function(zero){
+			return zero;
+		};
+
+		return function(el, nocash, doc){
+			if (el && el.$family && el.uid) return el;
+			var type = typeOf(el);
+			return (types[type]) ? types[type](el, nocash, doc || document) : null;
+		};
+
+	})()
 
 });
 
+if (window.$ == null) Window.implement('$', function(el, nc){
+	return document.id(el, nc, this.document);
+});
+
 Window.implement({
 
-	$: function(el, nocash){
-		if (el && el.$family && el.uid) return el;
-		var type = $type(el);
-		return ($[type]) ? $[type](el, nocash, this.document) : null;
-	},
-
-	$$: function(selector){
-		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
-		var elements = [];
-		var args = Array.flatten(arguments);
-		for (var i = 0, l = args.length; i < l; i++){
-			var item = args[i];
-			switch ($type(item)){
-				case 'element': elements.push(item); break;
-				case 'string': elements.extend(this.document.getElements(item, true));
-			}
-		}
-		return new Elements(elements);
-	},
-
 	getDocument: function(){
 		return this.document;
 	},
@@ -1417,119 +3322,166 @@
 
 });
 
-$.string = function(id, nocash, doc){
-	id = doc.getElementById(id);
-	return (id) ? $.element(id, nocash) : null;
-};
+[Document, Element].invoke('implement', {
 
-$.element = function(el, nocash){
-	$uid(el);
-	if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
-		var proto = Element.Prototype;
-		for (var p in proto) el[p] = proto[p];
-	};
-	return el;
-};
-
-$.object = function(obj, nocash, doc){
-	if (obj.toElement) return $.element(obj.toElement(doc), nocash);
-	return null;
-};
-
-$.textnode = $.whitespace = $.window = $.document = $arguments(0);
-
-Native.implement([Element, Document], {
-
-	getElement: function(selector, nocash){
-		return $(this.getElements(selector, true)[0] || null, nocash);
+	getElements: function(expression){
+		return Slick.search(this, expression, new Elements);
 	},
 
-	getElements: function(tags, nocash){
-		tags = tags.split(',');
-		var elements = [];
-		var ddup = (tags.length > 1);
-		tags.each(function(tag){
-			var partial = this.getElementsByTagName(tag.trim());
-			(ddup) ? elements.extend(partial) : elements = partial;
-		}, this);
-		return new Elements(elements, {ddup: ddup, cash: !nocash});
+	getElement: function(expression){
+		return document.id(Slick.find(this, expression));
 	}
 
 });
 
+var contains = {contains: function(element){
+	return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+//<1.2compat>
+
+Element.implement('hasChild', function(element){
+	return this !== element && this.contains(element);
+});
+
+(function(search, find, match){
+
+	this.Selectors = {};
+	var pseudos = this.Selectors.Pseudo = new Hash();
+
+	var addSlickPseudos = function(){
+		for (var name in pseudos) if (pseudos.hasOwnProperty(name)){
+			Slick.definePseudo(name, pseudos[name]);
+			delete pseudos[name];
+		}
+	};
+
+	Slick.search = function(context, expression, append){
+		addSlickPseudos();
+		return search.call(this, context, expression, append);
+	};
+
+	Slick.find = function(context, expression){
+		addSlickPseudos();
+		return find.call(this, context, expression);
+	};
+
+	Slick.match = function(node, selector){
+		addSlickPseudos();
+		return match.call(this, node, selector);
+	};
+
+})(Slick.search, Slick.find, Slick.match);
+
+//</1.2compat>
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+	if (!expression) return combinator;
+
+	expression = Object.clone(Slick.parse(expression));
+
+	var expressions = expression.expressions;
+	for (var i = expressions.length; i--;)
+		expressions[i][0].combinator = combinator;
+
+	return expression;
+};
+
+Object.forEach({
+	getNext: '~',
+	getPrevious: '!~',
+	getParent: '!'
+}, function(combinator, method){
+	Element.implement(method, function(expression){
+		return this.getElement(injectCombinator(expression, combinator));
+	});
+});
+
+Object.forEach({
+	getAllNext: '~',
+	getAllPrevious: '!~',
+	getSiblings: '~~',
+	getChildren: '>',
+	getParents: '!'
+}, function(combinator, method){
+	Element.implement(method, function(expression){
+		return this.getElements(injectCombinator(expression, combinator));
+	});
+});
+
+Element.implement({
+
+	getFirst: function(expression){
+		return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+	},
+
+	getLast: function(expression){
+		return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+	},
+
+	getWindow: function(){
+		return this.ownerDocument.window;
+	},
+
+	getDocument: function(){
+		return this.ownerDocument;
+	},
+
+	getElementById: function(id){
+		return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+	},
+
+	match: function(expression){
+		return !expression || Slick.match(this, expression);
+	}
+
+});
+
+//<1.2compat>
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+	var elements = new Elements;
+	if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements);
+	var args = Array.flatten(arguments);
+	for (var i = 0, l = args.length; i < l; i++){
+		var item = args[i];
+		switch (typeOf(item)){
+			case 'element': elements.push(item); break;
+			case 'string': Slick.search(this.document, item, elements);
+		}
+	}
+	return elements;
+});
+
+//</1.2compat>
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+	if (arguments.length == 1){
+		if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+		else if (Type.isEnumerable(selector)) return new Elements(selector);
+	}
+	return new Elements(arguments);
+});
+
 (function(){
 
-var collected = {}, storage = {};
-var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
-
-var get = function(uid){
-	return (storage[uid] || (storage[uid] = {}));
-};
-
-var clean = function(item, retain){
-	if (!item) return;
-	var uid = item.uid;
-	if (Browser.Engine.trident){
-		if (item.clearAttributes){
-			var clone = retain && item.cloneNode(false);
-			item.clearAttributes();
-			if (clone) item.mergeAttributes(clone);
-		} else if (item.removeEvents){
-			item.removeEvents();
-		}
-		if ((/object/i).test(item.tagName)){
-			for (var p in item){
-				if (typeof item[p] == 'function') item[p] = $empty;
-			}
-			Element.dispose(item);
-		}
-	}	
-	if (!uid) return;
-	collected[uid] = storage[uid] = null;
-};
-
-var purge = function(){
-	Hash.each(collected, clean);
-	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
-	if (window.CollectGarbage) CollectGarbage();
-	collected = storage = null;
-};
-
-var walk = function(element, walk, start, match, all, nocash){
-	var el = element[start || walk];
-	var elements = [];
-	while (el){
-		if (el.nodeType == 1 && (!match || Element.match(el, match))){
-			if (!all) return $(el, nocash);
-			elements.push(el);
-		}
-		el = el[walk];
-	}
-	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
-};
-
-var attributes = {
-	'html': 'innerHTML',
-	'class': 'className',
-	'for': 'htmlFor',
-	'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
-};
-var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
-var camels = ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
-
-Hash.extend(attributes, bools.associate(bools));
-Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
+// Inserters
 
 var inserters = {
 
 	before: function(context, element){
-		if (element.parentNode) element.parentNode.insertBefore(context, element);
+		var parent = element.parentNode;
+		if (parent) parent.insertBefore(context, element);
 	},
 
 	after: function(context, element){
-		if (!element.parentNode) return;
-		var next = element.nextSibling;
-		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
+		var parent = element.parentNode;
+		if (parent) parent.insertBefore(context, element.nextSibling);
 	},
 
 	bottom: function(context, element){
@@ -1537,60 +3489,123 @@
 	},
 
 	top: function(context, element){
-		var first = element.firstChild;
-		(first) ? element.insertBefore(context, first) : element.appendChild(context);
+		element.insertBefore(context, element.firstChild);
 	}
 
 };
 
 inserters.inside = inserters.bottom;
 
-Hash.each(inserters, function(inserter, where){
+//<1.2compat>
+
+Object.each(inserters, function(inserter, where){
 
 	where = where.capitalize();
 
-	Element.implement('inject' + where, function(el){
-		inserter(this, $(el, true));
-		return this;
-	});
+	var methods = {};
 
-	Element.implement('grab' + where, function(el){
-		inserter($(el, true), this);
+	methods['inject' + where] = function(el){
+		inserter(this, document.id(el, true));
 		return this;
-	});
+	};
+
+	methods['grab' + where] = function(el){
+		inserter(document.id(el, true), this);
+		return this;
+	};
+
+	Element.implement(methods);
 
 });
 
+//</1.2compat>
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+	'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+	'frameBorder', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+	properties[property.toLowerCase()] = property;
+});
+
+Object.append(properties, {
+	'html': 'innerHTML',
+	'text': (function(){
+		var temp = document.createElement('div');
+		return (temp.textContent == null) ? 'innerText': 'textContent';
+	})()
+});
+
+Object.forEach(properties, function(real, key){
+	propertySetters[key] = function(node, value){
+		node[real] = value;
+	};
+	propertyGetters[key] = function(node){
+		return node[real];
+	};
+});
+
+// Booleans
+
+var bools = [
+	'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+	'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+	'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+	'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+	var lower = bool.toLowerCase();
+	booleans[lower] = bool;
+	propertySetters[lower] = function(node, value){
+		node[bool] = !!value;
+	};
+	propertyGetters[lower] = function(node){
+		return !!node[bool];
+	};
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+	'class': function(node, value){
+		('className' in node) ? node.className = value : node.setAttribute('class', value);
+	},
+
+	'for': function(node, value){
+		('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+	},
+
+	'style': function(node, value){
+		(node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+	}
+
+});
+
+/* getProperty, setProperty */
+
 Element.implement({
 
-	set: function(prop, value){
-		switch ($type(prop)){
-			case 'object':
-				for (var p in prop) this.set(p, prop[p]);
-				break;
-			case 'string':
-				var property = Element.Properties.get(prop);
-				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
+	setProperty: function(name, value){
+		var lower = name.toLowerCase();
+		if (value == null){
+			if (!booleans[lower]){
+				this.removeAttribute(name);
+				return this;
+			}
+			value = false;
 		}
-		return this;
-	},
-
-	get: function(prop){
-		var property = Element.Properties.get(prop);
-		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
-	},
-
-	erase: function(prop){
-		var property = Element.Properties.get(prop);
-		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
-		return this;
-	},
-
-	setProperty: function(attribute, value){
-		var key = attributes[attribute];
-		if (value == undefined) return this.removeProperty(attribute);
-		if (key && bools[attribute]) value = !!value;
-		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
+		var setter = propertySetters[lower];
+		if (setter) setter(this, value);
+		else this.setAttribute(name, value);
 		return this;
 	},
 
@@ -1599,21 +3614,20 @@
 		return this;
 	},
 
-	getProperty: function(attribute){
-		var key = attributes[attribute];
-		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
-		return (bools[attribute]) ? !!value : (key) ? value : value || null;
+	getProperty: function(name){
+		var getter = propertyGetters[name.toLowerCase()];
+		if (getter) return getter(this);
+		var result = Slick.getAttribute(this, name);
+		return (!result && !Slick.hasAttribute(this, name)) ? null : result;
 	},
 
 	getProperties: function(){
-		var args = $A(arguments);
+		var args = Array.from(arguments);
 		return args.map(this.getProperty, this).associate(args);
 	},
 
-	removeProperty: function(attribute){
-		var key = attributes[attribute];
-		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
-		return this;
+	removeProperty: function(name){
+		return this.setProperty(name, null);
 	},
 
 	removeProperties: function(){
@@ -1621,8 +3635,24 @@
 		return this;
 	},
 
+	set: function(prop, value){
+		var property = Element.Properties[prop];
+		(property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+	}.overloadSetter(),
+
+	get: function(prop){
+		var property = Element.Properties[prop];
+		return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+	}.overloadGetter(),
+
+	erase: function(prop){
+		var property = Element.Properties[prop];
+		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+		return this;
+	},
+
 	hasClass: function(className){
-		return this.className.contains(className, ' ');
+		return this.className.clean().contains(className, ' ');
 	},
 
 	addClass: function(className){
@@ -1635,15 +3665,22 @@
 		return this;
 	},
 
-	toggleClass: function(className){
-		return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
+	toggleClass: function(className, force){
+		if (force == null) force = !this.hasClass(className);
+		return (force) ? this.addClass(className) : this.removeClass(className);
 	},
 
 	adopt: function(){
-		Array.flatten(arguments).each(function(element){
-			element = $(element, true);
-			if (element) this.appendChild(element);
-		}, this);
+		var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+		if (length > 1) parent = fragment = document.createDocumentFragment();
+
+		for (var i = 0; i < length; i++){
+			var element = document.id(elements[i], true);
+			if (element) parent.appendChild(element);
+		}
+
+		if (fragment) this.appendChild(fragment);
+
 		return this;
 	},
 
@@ -1652,111 +3689,104 @@
 	},
 
 	grab: function(el, where){
-		inserters[where || 'bottom']($(el, true), this);
+		inserters[where || 'bottom'](document.id(el, true), this);
 		return this;
 	},
 
 	inject: function(el, where){
-		inserters[where || 'bottom'](this, $(el, true));
+		inserters[where || 'bottom'](this, document.id(el, true));
 		return this;
 	},
 
 	replaces: function(el){
-		el = $(el, true);
+		el = document.id(el, true);
 		el.parentNode.replaceChild(this, el);
 		return this;
 	},
 
 	wraps: function(el, where){
-		el = $(el, true);
+		el = document.id(el, true);
 		return this.replaces(el).grab(el, where);
 	},
 
-	getPrevious: function(match, nocash){
-		return walk(this, 'previousSibling', null, match, false, nocash);
-	},
-
-	getAllPrevious: function(match, nocash){
-		return walk(this, 'previousSibling', null, match, true, nocash);
-	},
-
-	getNext: function(match, nocash){
-		return walk(this, 'nextSibling', null, match, false, nocash);
-	},
-
-	getAllNext: function(match, nocash){
-		return walk(this, 'nextSibling', null, match, true, nocash);
-	},
-
-	getFirst: function(match, nocash){
-		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
-	},
-
-	getLast: function(match, nocash){
-		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
-	},
-
-	getParent: function(match, nocash){
-		return walk(this, 'parentNode', null, match, false, nocash);
-	},
-
-	getParents: function(match, nocash){
-		return walk(this, 'parentNode', null, match, true, nocash);
-	},
-
-	getChildren: function(match, nocash){
-		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
-	},
-
-	getWindow: function(){
-		return this.ownerDocument.window;
-	},
-
-	getDocument: function(){
-		return this.ownerDocument;
-	},
-
-	getElementById: function(id, nocash){
-		var el = this.ownerDocument.getElementById(id);
-		if (!el) return null;
-		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
-			if (!parent) return null;
-		}
-		return $.element(el, nocash);
-	},
-
 	getSelected: function(){
-		return new Elements($A(this.options).filter(function(option){
+		this.selectedIndex; // Safari 3.2.1
+		return new Elements(Array.from(this.options).filter(function(option){
 			return option.selected;
 		}));
 	},
 
-	getComputedStyle: function(property){
-		if (this.currentStyle) return this.currentStyle[property.camelCase()];
-		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
-		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
-	},
-
 	toQueryString: function(){
 		var queryString = [];
-		this.getElements('input, select, textarea', true).each(function(el){
-			if (!el.name || el.disabled) return;
-			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
-				return opt.value;
-			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
-			$splat(value).each(function(val){
-				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
+		this.getElements('input, select, textarea').each(function(el){
+			var type = el.type;
+			if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+			var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+				// IE
+				return document.id(opt).get('value');
+			}) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+			Array.from(value).each(function(val){
+				if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
 			});
 		});
 		return queryString.join('&');
+	}
+
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+	return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+	var uid = item.uid;
+	if (item.removeEvents) item.removeEvents();
+	if (item.clearAttributes) item.clearAttributes();
+	if (uid != null){
+		delete collected[uid];
+		delete storage[uid];
+	}
+	return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+	destroy: function(){
+		var children = clean(this).getElementsByTagName('*');
+		Array.each(children, clean);
+		Element.dispose(this);
+		return null;
+	},
+
+	empty: function(){
+		Array.from(this.childNodes).each(Element.dispose);
+		return this;
+	},
+
+	dispose: function(){
+		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
 	},
 
 	clone: function(contents, keepid){
 		contents = contents !== false;
-		var clone = this.cloneNode(contents);
-		var clean = function(node, element){
+		var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+		if (contents){
+			ce.append(Array.from(clone.getElementsByTagName('*')));
+			te.append(Array.from(this.getElementsByTagName('*')));
+		}
+
+		for (i = ce.length; i--;){
+			var node = ce[i], element = te[i];
 			if (!keepid) node.removeAttribute('id');
-			if (Browser.Engine.trident){
+			/*<ltIE9>*/
+			if (node.clearAttributes){
 				node.clearAttributes();
 				node.mergeAttributes(element);
 				node.removeAttribute('uid');
@@ -1765,51 +3795,23 @@
 					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
 				}
 			}
-			var prop = props[element.tagName.toLowerCase()];
+			/*</ltIE9>*/
+			var prop = formProps[element.tagName.toLowerCase()];
 			if (prop && element[prop]) node[prop] = element[prop];
-		};
-
-		if (contents){
-			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
-			for (var i = ce.length; i--;) clean(ce[i], te[i]);
 		}
 
-		clean(clone, this);
-		return $(clone);
-	},
-
-	destroy: function(){
-		Element.empty(this);
-		Element.dispose(this);
-		clean(this, true);
-		return null;
-	},
-
-	empty: function(){
-		$A(this.childNodes).each(function(node){
-			Element.destroy(node);
-		});
-		return this;
-	},
-
-	dispose: function(){
-		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
-	},
-
-	hasChild: function(el){
-		el = $(el, true);
-		if (!el) return false;
-		if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
-		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
-	},
-
-	match: function(tag){
-		return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
+		/*<ltIE9>*/
+		if (Browser.ie){
+			var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+			for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+		}
+		/*</ltIE9>*/
+		return document.id(clone);
 	}
 
 });
 
-Native.implement([Element, Window, Document], {
+[Element, Window, Document].invoke('implement', {
 
 	addListener: function(type, fn){
 		if (type == 'unload'){
@@ -1819,45 +3821,54 @@
 				old();
 			};
 		} else {
-			collected[this.uid] = this;
+			collected[$uid(this)] = this;
 		}
-		if (this.addEventListener) this.addEventListener(type, fn, false);
+		if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
 		else this.attachEvent('on' + type, fn);
 		return this;
 	},
 
 	removeListener: function(type, fn){
-		if (this.removeEventListener) this.removeEventListener(type, fn, false);
+		if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
 		else this.detachEvent('on' + type, fn);
 		return this;
 	},
 
 	retrieve: function(property, dflt){
-		var storage = get(this.uid), prop = storage[property];
-		if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
-		return $pick(prop);
+		var storage = get($uid(this)), prop = storage[property];
+		if (dflt != null && prop == null) prop = storage[property] = dflt;
+		return prop != null ? prop : null;
 	},
 
 	store: function(property, value){
-		var storage = get(this.uid);
+		var storage = get($uid(this));
 		storage[property] = value;
 		return this;
 	},
 
 	eliminate: function(property){
-		var storage = get(this.uid);
+		var storage = get($uid(this));
 		delete storage[property];
 		return this;
 	}
 
 });
 
-window.addListener('unload', purge);
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){
+	Object.each(collected, clean);
+	if (window.CollectGarbage) CollectGarbage();
+});
+/*</ltIE9>*/
 
-})();
+Element.Properties = {};
+
+//<1.2compat>
 
 Element.Properties = new Hash;
 
+//</1.2compat>
+
 Element.Properties.style = {
 
 	set: function(style){
@@ -1882,7 +3893,14 @@
 
 };
 
+/*<!webkit>*/
 Element.Properties.html = (function(){
+
+	var tableTest = Function.attempt(function(){
+		var table = document.createElement('table');
+		table.innerHTML = '<tr><td></td></tr>';
+	});
+
 	var wrapper = document.createElement('div');
 
 	var translations = {
@@ -1893,10 +3911,26 @@
 	};
 	translations.thead = translations.tfoot = translations.tbody;
 
+	/*<ltIE9>*/
+	// technique by jdbarlett - http://jdbartlett.com/innershiv/
+	wrapper.innerHTML = '<nav></nav>';
+	var HTML5Test = wrapper.childNodes.length == 1;
+	if (!HTML5Test){
+		var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
+			fragment = document.createDocumentFragment(), l = tags.length;
+		while (l--) fragment.createElement(tags[l]);
+		fragment.appendChild(wrapper);
+	}
+	/*</ltIE9>*/
+
 	var html = {
-		set: function(){
-			var html = Array.flatten(arguments).join('');
-			var wrap = Browser.Engine.trident && translations[this.get('tag')];
+		set: function(html){
+			if (typeOf(html) == 'array') html = html.join('');
+
+			var wrap = (!tableTest && translations[this.get('tag')]);
+			/*<ltIE9>*/
+			if (!wrap && !HTML5Test) wrap = [0, '', ''];
+			/*</ltIE9>*/
 			if (wrap){
 				var first = wrapper;
 				first.innerHTML = wrap[1] + html + wrap[2];
@@ -1912,222 +3946,122 @@
 
 	return html;
 })();
+/*</!webkit>*/
 
-if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+	set: function(value){
+		var tag = this.get('tag');
+		if (tag != 'select') return this.setProperty('value', value);
+		var options = this.getElements('option');
+		for (var i = 0; i < options.length; i++){
+			var option = options[i],
+				attr = option.getAttributeNode('value'),
+				optionValue = (attr && attr.specified) ? option.value : option.get('text');
+			if (optionValue == value) return option.selected = true;
+		}
+	},
+
 	get: function(){
-		if (this.innerText) return this.innerText;
-		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
-		var text = temp.innerText;
-		temp.destroy();
-		return text;
-	}
-};
+		var option = this, tag = option.get('tag');
 
+		if (tag != 'select' && tag != 'option') return this.getProperty('value');
 
-/*
-Script: Element.Event.js
-	Contains Element methods for dealing with events, and custom Events.
+		if (tag == 'select' && !(option = option.getSelected()[0])) return '';
 
-License:
-	MIT-style license.
-*/
-
-Element.Properties.events = {set: function(events){
-	this.addEvents(events);
-}};
-
-Native.implement([Element, Window, Document], {
-
-	addEvent: function(type, fn){
-		var events = this.retrieve('events', {});
-		events[type] = events[type] || {'keys': [], 'values': []};
-		if (events[type].keys.contains(fn)) return this;
-		events[type].keys.push(fn);
-		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
-		if (custom){
-			if (custom.onAdd) custom.onAdd.call(this, fn);
-			if (custom.condition){
-				condition = function(event){
-					if (custom.condition.call(this, event)) return fn.call(this, event);
-					return true;
-				};
-			}
-			realType = custom.base || realType;
-		}
-		var defn = function(){
-			return fn.call(self);
-		};
-		var nativeEvent = Element.NativeEvents[realType];
-		if (nativeEvent){
-			if (nativeEvent == 2){
-				defn = function(event){
-					event = new Event(event, self.getWindow());
-					if (condition.call(self, event) === false) event.stop();
-				};
-			}
-			this.addListener(realType, defn);
-		}
-		events[type].values.push(defn);
-		return this;
-	},
-
-	removeEvent: function(type, fn){
-		var events = this.retrieve('events');
-		if (!events || !events[type]) return this;
-		var pos = events[type].keys.indexOf(fn);
-		if (pos == -1) return this;
-		events[type].keys.splice(pos, 1);
-		var value = events[type].values.splice(pos, 1)[0];
-		var custom = Element.Events.get(type);
-		if (custom){
-			if (custom.onRemove) custom.onRemove.call(this, fn);
-			type = custom.base || type;
-		}
-		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
-	},
-
-	addEvents: function(events){
-		for (var event in events) this.addEvent(event, events[event]);
-		return this;
-	},
-
-	removeEvents: function(events){
-		if ($type(events) == 'object'){
-			for (var type in events) this.removeEvent(type, events[type]);
-			return this;
-		}
-		var attached = this.retrieve('events');
-		if (!attached) return this;
-		if (!events){
-			for (var type in attached) this.removeEvents(type);
-			this.eliminate('events');
-		} else if (attached[events]){
-			while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
-			attached[events] = null;
-		}
-		return this;
-	},
-
-	fireEvent: function(type, args, delay){
-		var events = this.retrieve('events');
-		if (!events || !events[type]) return this;
-		events[type].keys.each(function(fn){
-			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
-		}, this);
-		return this;
-	},
-
-	cloneEvents: function(from, type){
-		from = $(from);
-		var fevents = from.retrieve('events');
-		if (!fevents) return this;
-		if (!type){
-			for (var evType in fevents) this.cloneEvents(from, evType);
-		} else if (fevents[type]){
-			fevents[type].keys.each(function(fn){
-				this.addEvent(type, fn);
-			}, this);
-		}
-		return this;
+		var attr = option.getAttributeNode('value');
+		return (attr && attr.specified) ? option.value : option.get('text');
 	}
 
-});
-
-Element.NativeEvents = {
-	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
-	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
-	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
-	keydown: 2, keypress: 2, keyup: 2, //keyboard
-	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
-	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
-	error: 1, abort: 1, scroll: 1 //misc
 };
-
-(function(){
-
-var $check = function(event){
-	var related = event.relatedTarget;
-	if (related == undefined) return true;
-	if (related === false) return false;
-	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
-};
-
-Element.Events = new Hash({
-
-	mouseenter: {
-		base: 'mouseover',
-		condition: $check
-	},
-
-	mouseleave: {
-		base: 'mouseout',
-		condition: $check
-	},
-
-	mousewheel: {
-		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
-	}
-
-});
+/*</ltIE9>*/
 
 })();
 
 
 /*
-Script: Element.Style.js
-	Contains methods for interacting with the styles of Elements in a fashionable way.
+---
 
-License:
-	MIT-style license.
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
 */
 
+(function(){
+
+var html = document.html;
+
 Element.Properties.styles = {set: function(styles){
 	this.setStyles(styles);
 }};
 
-Element.Properties.opacity = {
+var hasOpacity = (html.style.opacity != null),
+	hasFilter = (html.style.filter != null),
+	reAlpha = /alpha\(opacity=([\d.]+)\)/i;
 
-	set: function(opacity, novisibility){
-		if (!novisibility){
-			if (opacity == 0){
-				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
-			} else {
-				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
-			}
-		}
-		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
-		if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
-		this.style.opacity = opacity;
-		this.store('opacity', opacity);
-	},
-
-	get: function(){
-		return this.retrieve('opacity', 1);
-	}
-
+var setVisibility = function(element, opacity){
+	element.store('$opacity', opacity);
+	element.style.visibility = opacity > 0 ? 'visible' : 'hidden';
 };
 
+var setOpacity = (hasOpacity ? function(element, opacity){
+	element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+	if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
+	opacity = (opacity * 100).limit(0, 100).round();
+	opacity = (opacity == 100) ? '' : 'alpha(opacity=' + opacity + ')';
+	var filter = element.style.filter || element.getComputedStyle('filter') || '';
+	element.style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity;
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+	var opacity = element.style.opacity || element.getComputedStyle('opacity');
+	return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+	var filter = (element.style.filter || element.getComputedStyle('filter')),
+		opacity;
+	if (filter) opacity = filter.match(reAlpha);
+	return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+	var opacity = element.retrieve('$opacity');
+	if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+	return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
+
 Element.implement({
 
-	setOpacity: function(value){
-		return this.set('opacity', value, true);
-	},
-
-	getOpacity: function(){
-		return this.get('opacity');
+	getComputedStyle: function(property){
+		if (this.currentStyle) return this.currentStyle[property.camelCase()];
+		var defaultView = Element.getDocument(this).defaultView,
+			computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+		return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
 	},
 
 	setStyle: function(property, value){
-		switch (property){
-			case 'opacity': return this.set('opacity', parseFloat(value));
-			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
+		if (property == 'opacity'){
+			setOpacity(this, parseFloat(value));
+			return this;
 		}
-		property = property.camelCase();
-		if ($type(value) != 'string'){
-			var map = (Element.Styles.get(property) || '@').split(' ');
-			value = $splat(value).map(function(val, i){
+		property = (property == 'float' ? floatName : property).camelCase();
+		if (typeOf(value) != 'string'){
+			var map = (Element.Styles[property] || '@').split(' ');
+			value = Array.from(value).map(function(val, i){
 				if (!map[i]) return '';
-				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+				return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
 			}).join(' ');
 		} else if (value == String(Number(value))){
 			value = Math.round(value);
@@ -2137,13 +4071,10 @@
 	},
 
 	getStyle: function(property){
-		switch (property){
-			case 'opacity': return this.get('opacity');
-			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
-		}
-		property = property.camelCase();
+		if (property == 'opacity') return getOpacity(this);
+		property = (property == 'float' ? floatName : property).camelCase();
 		var result = this.style[property];
-		if (!$chk(result)){
+		if (!result || property == 'zIndex'){
 			result = [];
 			for (var style in Element.ShortStyles){
 				if (property != style) continue;
@@ -2157,16 +4088,16 @@
 			var color = result.match(/rgba?\([\d\s,]+\)/);
 			if (color) result = result.replace(color[0], color[0].rgbToHex());
 		}
-		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result)))){
-			if (property.test(/^(height|width)$/)){
+		if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){
+			if ((/^(height|width)$/).test(property)){
 				var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
 				values.each(function(value){
 					size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
 				}, this);
 				return this['offset' + property.capitalize()] - size + 'px';
 			}
-			if ((Browser.Engine.presto) && String(result).test('px')) return result;
-			if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
+			if (Browser.opera && String(result).indexOf('px') != -1) return result;
+			if ((/^border(.+)Width|margin|padding/).test(property)) return '0px';
 		}
 		return result;
 	},
@@ -2178,7 +4109,7 @@
 
 	getStyles: function(){
 		var result = {};
-		Array.each(arguments, function(key){
+		Array.flatten(arguments).each(function(key){
 			result[key] = this.getStyle(key);
 		}, this);
 		return result;
@@ -2186,7 +4117,7 @@
 
 });
 
-Element.Styles = new Hash({
+Element.Styles = {
 	left: '@px', top: '@px', bottom: '@px', right: '@px',
 	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
 	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
@@ -2194,8 +4125,44 @@
 	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
 	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
 	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
+};
+
+//<1.3compat>
+
+Element.implement({
+
+	setOpacity: function(value){
+		setOpacity(this, value);
+		return this;
+	},
+
+	getOpacity: function(){
+		return getOpacity(this);
+	}
+
 });
 
+Element.Properties.opacity = {
+
+	set: function(opacity){
+		setOpacity(this, opacity);
+		setVisibility(this, opacity);
+	},
+
+	get: function(){
+		return getOpacity(this);
+	}
+
+};
+
+//</1.3compat>
+
+//<1.2compat>
+
+Element.Styles = new Hash(Element.Styles);
+
+//</1.2compat>
+
 Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
 
 ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
@@ -2214,21 +4181,439 @@
 	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
 });
 
+})();
+
 
 /*
-Script: Element.Dimensions.js
-	Contains methods to work with size, scroll, or positioning of Elements and the window object.
+---
 
-License:
-	MIT-style license.
+name: Element.Event
 
-Credits:
-	- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
-	- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
 */
 
 (function(){
 
+Element.Properties.events = {set: function(events){
+	this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+	addEvent: function(type, fn){
+		var events = this.retrieve('events', {});
+		if (!events[type]) events[type] = {keys: [], values: []};
+		if (events[type].keys.contains(fn)) return this;
+		events[type].keys.push(fn);
+		var realType = type,
+			custom = Element.Events[type],
+			condition = fn,
+			self = this;
+		if (custom){
+			if (custom.onAdd) custom.onAdd.call(this, fn, type);
+			if (custom.condition){
+				condition = function(event){
+					if (custom.condition.call(this, event, type)) return fn.call(this, event);
+					return true;
+				};
+			}
+			if (custom.base) realType = Function.from(custom.base).call(this, type);
+		}
+		var defn = function(){
+			return fn.call(self);
+		};
+		var nativeEvent = Element.NativeEvents[realType];
+		if (nativeEvent){
+			if (nativeEvent == 2){
+				defn = function(event){
+					event = new DOMEvent(event, self.getWindow());
+					if (condition.call(self, event) === false) event.stop();
+				};
+			}
+			this.addListener(realType, defn, arguments[2]);
+		}
+		events[type].values.push(defn);
+		return this;
+	},
+
+	removeEvent: function(type, fn){
+		var events = this.retrieve('events');
+		if (!events || !events[type]) return this;
+		var list = events[type];
+		var index = list.keys.indexOf(fn);
+		if (index == -1) return this;
+		var value = list.values[index];
+		delete list.keys[index];
+		delete list.values[index];
+		var custom = Element.Events[type];
+		if (custom){
+			if (custom.onRemove) custom.onRemove.call(this, fn, type);
+			if (custom.base) type = Function.from(custom.base).call(this, type);
+		}
+		return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+	},
+
+	addEvents: function(events){
+		for (var event in events) this.addEvent(event, events[event]);
+		return this;
+	},
+
+	removeEvents: function(events){
+		var type;
+		if (typeOf(events) == 'object'){
+			for (type in events) this.removeEvent(type, events[type]);
+			return this;
+		}
+		var attached = this.retrieve('events');
+		if (!attached) return this;
+		if (!events){
+			for (type in attached) this.removeEvents(type);
+			this.eliminate('events');
+		} else if (attached[events]){
+			attached[events].keys.each(function(fn){
+				this.removeEvent(events, fn);
+			}, this);
+			delete attached[events];
+		}
+		return this;
+	},
+
+	fireEvent: function(type, args, delay){
+		var events = this.retrieve('events');
+		if (!events || !events[type]) return this;
+		args = Array.from(args);
+
+		events[type].keys.each(function(fn){
+			if (delay) fn.delay(delay, this, args);
+			else fn.apply(this, args);
+		}, this);
+		return this;
+	},
+
+	cloneEvents: function(from, type){
+		from = document.id(from);
+		var events = from.retrieve('events');
+		if (!events) return this;
+		if (!type){
+			for (var eventType in events) this.cloneEvents(from, eventType);
+		} else if (events[type]){
+			events[type].keys.each(function(fn){
+				this.addEvent(type, fn);
+			}, this);
+		}
+		return this;
+	}
+
+});
+
+Element.NativeEvents = {
+	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+	keydown: 2, keypress: 2, keyup: 2, //keyboard
+	orientationchange: 2, // mobile
+	touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+	gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+	load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+	error: 1, abort: 1, scroll: 1 //misc
+};
+
+var check = function(event){
+	var related = event.relatedTarget;
+	if (related == null) return true;
+	if (!related) return false;
+	return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+};
+
+Element.Events = {
+
+	mouseenter: {
+		base: 'mouseover',
+		condition: check
+	},
+
+	mouseleave: {
+		base: 'mouseout',
+		condition: check
+	},
+
+	mousewheel: {
+		base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel'
+	}
+
+};
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+	Element.NativeEvents.propertychange = 2;
+	Element.Events.change = {
+		base: function(){
+			var type = this.type;
+			return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change'
+		},
+		condition: function(event){
+			return !!(this.type != 'radio' || this.checked);
+		}
+	}
+}
+/*</ltIE9>*/
+
+//<1.2compat>
+
+Element.Events = new Hash(Element.Events);
+
+//</1.2compat>
+
+})();
+
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+	while (target && target != self){
+		if (match(target, event)) return fn.call(target, event, target);
+		target = document.id(target.parentNode);
+	}
+};
+
+var map = {
+	mouseenter: {
+		base: 'mouseover'
+	},
+	mouseleave: {
+		base: 'mouseout'
+	},
+	focus: {
+		base: 'focus' + (eventListenerSupport ? '' : 'in'),
+		capture: true
+	},
+	blur: {
+		base: eventListenerSupport ? 'blur' : 'focusout',
+		capture: true
+	}
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+	return {
+
+		base: 'focusin',
+
+		remove: function(self, uid){
+			var list = self.retrieve(_key + type + 'listeners', {})[uid];
+			if (list && list.forms) for (var i = list.forms.length; i--;){
+				list.forms[i].removeEvent(type, list.fns[i]);
+			}
+		},
+
+		listen: function(self, match, fn, event, target, uid){
+			var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+			if (!form) return;
+
+			var listeners = self.retrieve(_key + type + 'listeners', {}),
+				listener = listeners[uid] || {forms: [], fns: []},
+				forms = listener.forms, fns = listener.fns;
+
+			if (forms.indexOf(form) != -1) return;
+			forms.push(form);
+
+			var _fn = function(event){
+				bubbleUp(self, match, fn, event, target);
+			};
+			form.addEvent(type, _fn);
+			fns.push(_fn);
+
+			listeners[uid] = listener;
+			self.store(_key + type + 'listeners', listeners);
+		}
+	};
+};
+
+var inputObserver = function(type){
+	return {
+		base: 'focusin',
+		listen: function(self, match, fn, event, target){
+			var events = {blur: function(){
+				this.removeEvents(events);
+			}};
+			events[type] = function(event){
+				bubbleUp(self, match, fn, event, target);
+			};
+			event.target.addEvents(events);
+		}
+	};
+};
+
+if (!eventListenerSupport) Object.append(map, {
+	submit: formObserver('submit'),
+	reset: formObserver('reset'),
+	change: inputObserver('change'),
+	select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+	addEvent = proto.addEvent,
+	removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+	return function(type, fn, useCapture){
+		if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+		var parsed = Slick.parse(type).expressions[0][0];
+		if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+		var newType = parsed.tag;
+		parsed.pseudos.slice(1).each(function(pseudo){
+			newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+		});
+		old.call(this, type, fn);
+		return method.call(this, newType, parsed.pseudos[0].value, fn);
+	};
+};
+
+var delegation = {
+
+	addEvent: function(type, match, fn){
+		var storage = this.retrieve('$delegates', {}), stored = storage[type];
+		if (stored) for (var _uid in stored){
+			if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+		}
+
+		var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+		type = _map.base || _type;
+
+		match = function(target){
+			return Slick.match(target, _match);
+		};
+
+		var elementEvent = Element.Events[_type];
+		if (elementEvent && elementEvent.condition){
+			var __match = match, condition = elementEvent.condition;
+			match = function(target, event){
+				return __match(target, event) && condition.call(target, event, type);
+			};
+		}
+
+		var self = this, uid = String.uniqueID();
+		var delegator = _map.listen ? function(event, target){
+			if (!target && event && event.target) target = event.target;
+			if (target) _map.listen(self, match, fn, event, target, uid);
+		} : function(event, target){
+			if (!target && event && event.target) target = event.target;
+			if (target) bubbleUp(self, match, fn, event, target);
+		};
+
+		if (!stored) stored = {};
+		stored[uid] = {
+			match: _match,
+			fn: _fn,
+			delegator: delegator
+		};
+		storage[_type] = stored;
+		return addEvent.call(this, type, delegator, _map.capture);
+	},
+
+	removeEvent: function(type, match, fn, _uid){
+		var storage = this.retrieve('$delegates', {}), stored = storage[type];
+		if (!stored) return this;
+
+		if (_uid){
+			var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+			type = _map.base || _type;
+			if (_map.remove) _map.remove(this, _uid);
+			delete stored[_uid];
+			storage[_type] = stored;
+			return removeEvent.call(this, type, delegator);
+		}
+
+		var __uid, s;
+		if (fn) for (__uid in stored){
+			s = stored[__uid];
+			if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+		} else for (__uid in stored){
+			s = stored[__uid];
+			if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+		}
+		return this;
+	}
+
+};
+
+[Element, Window, Document].invoke('implement', {
+	addEvent: relay(addEvent, delegation.addEvent),
+	removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Dimensions
+
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+  - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+  - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+	child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var isOffset = function(el){
+	return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+	return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
 Element.implement({
 
 	scrollTo: function(x, y){
@@ -2257,7 +4642,7 @@
 	},
 
 	getScrolls: function(){
-		var element = this, position = {x: 0, y: 0};
+		var element = this.parentNode, position = {x: 0, y: 0};
 		while (element && !isBody(element)){
 			position.x += element.scrollLeft;
 			position.y += element.scrollTop;
@@ -2266,22 +4651,36 @@
 		return position;
 	},
 
-	getOffsetParent: function(){
+	getOffsetParent: brokenOffsetParent ? function(){
 		var element = this;
-		if (isBody(element)) return null;
-		if (!Browser.Engine.trident) return element.offsetParent;
-		while ((element = element.parentNode) && !isBody(element)){
-			if (styleString(element, 'position') != 'static') return element;
+		if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+		var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+		while ((element = element.parentNode)){
+			if (isOffsetCheck(element)) return element;
 		}
 		return null;
+	} : function(){
+		var element = this;
+		if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+		try {
+			return element.offsetParent;
+		} catch(e) {}
+		return null;
 	},
 
 	getOffsets: function(){
-		if (Browser.Engine.trident){
-			var bound = this.getBoundingClientRect(), html = this.getDocument().documentElement;
+		if (this.getBoundingClientRect && !Browser.Platform.ios){
+			var bound = this.getBoundingClientRect(),
+				html = document.id(this.getDocument().documentElement),
+				htmlScroll = html.getScroll(),
+				elemScrolls = this.getScrolls(),
+				isFixed = (styleString(this, 'position') == 'fixed');
+
 			return {
-				x: bound.left + html.scrollLeft - html.clientLeft,
-				y: bound.top + html.scrollTop - html.clientTop
+				x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+				y: bound.top.toInt()  + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
 			};
 		}
 
@@ -2292,7 +4691,7 @@
 			position.x += element.offsetLeft;
 			position.y += element.offsetTop;
 
-			if (Browser.Engine.gecko){
+			if (Browser.firefox){
 				if (!borderBox(element)){
 					position.x += leftBorder(element);
 					position.y += topBorder(element);
@@ -2302,14 +4701,14 @@
 					position.x += leftBorder(parent);
 					position.y += topBorder(parent);
 				}
-			} else if (element != this && Browser.Engine.webkit){
+			} else if (element != this && Browser.safari){
 				position.x += leftBorder(element);
 				position.y += topBorder(element);
 			}
 
 			element = element.offsetParent;
 		}
-		if (Browser.Engine.gecko && !borderBox(this)){
+		if (Browser.firefox && !borderBox(this)){
 			position.x -= leftBorder(this);
 			position.y -= topBorder(this);
 		}
@@ -2317,51 +4716,67 @@
 	},
 
 	getPosition: function(relative){
-		if (isBody(this)) return {x: 0, y: 0};
-		var offset = this.getOffsets(), scroll = this.getScrolls();
-		var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
-		var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
-		return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
+		var offset = this.getOffsets(),
+			scroll = this.getScrolls();
+		var position = {
+			x: offset.x - scroll.x,
+			y: offset.y - scroll.y
+		};
+
+		if (relative && (relative = document.id(relative))){
+			var relativePosition = relative.getPosition();
+			return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+		}
+		return position;
 	},
 
 	getCoordinates: function(element){
 		if (isBody(this)) return this.getWindow().getCoordinates();
-		var position = this.getPosition(element), size = this.getSize();
-		var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
+		var position = this.getPosition(element),
+			size = this.getSize();
+		var obj = {
+			left: position.x,
+			top: position.y,
+			width: size.x,
+			height: size.y
+		};
 		obj.right = obj.left + obj.width;
 		obj.bottom = obj.top + obj.height;
 		return obj;
 	},
 
 	computePosition: function(obj){
-		return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
+		return {
+			left: obj.x - styleNumber(this, 'margin-left'),
+			top: obj.y - styleNumber(this, 'margin-top')
+		};
 	},
 
-	position: function(obj){
+	setPosition: function(obj){
 		return this.setStyles(this.computePosition(obj));
 	}
 
 });
 
-Native.implement([Document, Window], {
+
+[Document, Window].invoke('implement', {
 
 	getSize: function(){
-		var win = this.getWindow();
-		if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight};
 		var doc = getCompatElement(this);
 		return {x: doc.clientWidth, y: doc.clientHeight};
 	},
 
 	getScroll: function(){
-		var win = this.getWindow();
-		var doc = getCompatElement(this);
+		var win = this.getWindow(), doc = getCompatElement(this);
 		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
 	},
 
 	getScrollSize: function(){
-		var doc = getCompatElement(this);
-		var min = this.getSize();
-		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
+		var doc = getCompatElement(this),
+			min = this.getSize(),
+			body = this.getDocument().body;
+
+		return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
 	},
 
 	getPosition: function(){
@@ -2381,34 +4796,35 @@
 
 function styleNumber(element, style){
 	return styleString(element, style).toInt() || 0;
-};
+}
 
 function borderBox(element){
 	return styleString(element, '-moz-box-sizing') == 'border-box';
-};
+}
 
 function topBorder(element){
 	return styleNumber(element, 'border-top-width');
-};
+}
 
 function leftBorder(element){
 	return styleNumber(element, 'border-left-width');
-};
+}
 
 function isBody(element){
 	return (/^(?:body|html)$/i).test(element.tagName);
-};
+}
 
 function getCompatElement(element){
 	var doc = element.getDocument();
 	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
-};
+}
 
 })();
 
 //aliases
+Element.alias({position: 'setPosition'}); //compatability
 
-Native.implement([Window, Document, Element], {
+[Window, Document, Element].invoke('implement', {
 
 	getHeight: function(){
 		return this.getSize().y;
@@ -2446,665 +4862,44 @@
 
 
 /*
-Script: Selectors.js
-	Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.
+---
 
-License:
-	MIT-style license.
+name: Fx
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires: [Chain, Events, Options]
+
+provides: Fx
+
+...
 */
 
-Native.implement([Document, Element], {
-
-	getElements: function(expression, nocash){
-		expression = expression.split(',');
-		var items, local = {};
-		for (var i = 0, l = expression.length; i < l; i++){
-			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
-			if (i != 0 && elements.item) elements = $A(elements);
-			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
-		}
-		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
-	}
-
-});
-
-Element.implement({
-
-	match: function(selector){
-		if (!selector || (selector == this)) return true;
-		var tagid = Selectors.Utils.parseTagAndID(selector);
-		var tag = tagid[0], id = tagid[1];
-		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
-		var parsed = Selectors.Utils.parseSelector(selector);
-		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
-	}
-
-});
-
-var Selectors = {Cache: {nth: {}, parsed: {}}};
-
-Selectors.RegExps = {
-	id: (/#([\w-]+)/),
-	tag: (/^(\w+|\*)/),
-	quick: (/^(\w+|\*)$/),
-	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
-	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
-};
-
-Selectors.Utils = {
-
-	chk: function(item, uniques){
-		if (!uniques) return true;
-		var uid = $uid(item);
-		if (!uniques[uid]) return uniques[uid] = true;
-		return false;
-	},
-
-	parseNthArgument: function(argument){
-		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
-		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
-		if (!parsed) return false;
-		var inta = parseInt(parsed[1]);
-		var a = (inta || inta === 0) ? inta : 1;
-		var special = parsed[2] || false;
-		var b = parseInt(parsed[3]) || 0;
-		if (a != 0){
-			b--;
-			while (b < 1) b += a;
-			while (b >= a) b -= a;
-		} else {
-			a = b;
-			special = 'index';
-		}
-		switch (special){
-			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
-			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
-			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
-			case 'first': parsed = {a: 0, special: 'index'}; break;
-			case 'last': parsed = {special: 'last-child'}; break;
-			case 'only': parsed = {special: 'only-child'}; break;
-			default: parsed = {a: (a - 1), special: 'index'};
-		}
-
-		return Selectors.Cache.nth[argument] = parsed;
-	},
-
-	parseSelector: function(selector){
-		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
-		var m, parsed = {classes: [], pseudos: [], attributes: []};
-		while ((m = Selectors.RegExps.combined.exec(selector))){
-			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
-			if (cn){
-				parsed.classes.push(cn);
-			} else if (pn){
-				var parser = Selectors.Pseudo.get(pn);
-				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
-				else parsed.attributes.push({name: pn, operator: '=', value: pa});
-			} else if (an){
-				parsed.attributes.push({name: an, operator: ao, value: av});
-			}
-		}
-		if (!parsed.classes.length) delete parsed.classes;
-		if (!parsed.attributes.length) delete parsed.attributes;
-		if (!parsed.pseudos.length) delete parsed.pseudos;
-		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
-		return Selectors.Cache.parsed[selector] = parsed;
-	},
-
-	parseTagAndID: function(selector){
-		var tag = selector.match(Selectors.RegExps.tag);
-		var id = selector.match(Selectors.RegExps.id);
-		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
-	},
-
-	filter: function(item, parsed, local){
-		var i;
-		if (parsed.classes){
-			for (i = parsed.classes.length; i--; i){
-				var cn = parsed.classes[i];
-				if (!Selectors.Filters.byClass(item, cn)) return false;
-			}
-		}
-		if (parsed.attributes){
-			for (i = parsed.attributes.length; i--; i){
-				var att = parsed.attributes[i];
-				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
-			}
-		}
-		if (parsed.pseudos){
-			for (i = parsed.pseudos.length; i--; i){
-				var psd = parsed.pseudos[i];
-				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
-			}
-		}
-		return true;
-	},
-
-	getByTagAndID: function(ctx, tag, id){
-		if (id){
-			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
-			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
-		} else {
-			return ctx.getElementsByTagName(tag);
-		}
-	},
-
-	search: function(self, expression, local){
-		var splitters = [];
-
-		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
-			splitters.push(m1);
-			return ':)' + m2;
-		}).split(':)');
-
-		var items, filtered, item;
-
-		for (var i = 0, l = selectors.length; i < l; i++){
-
-			var selector = selectors[i];
-
-			if (i == 0 && Selectors.RegExps.quick.test(selector)){
-				items = self.getElementsByTagName(selector);
-				continue;
-			}
-
-			var splitter = splitters[i - 1];
-
-			var tagid = Selectors.Utils.parseTagAndID(selector);
-			var tag = tagid[0], id = tagid[1];
-
-			if (i == 0){
-				items = Selectors.Utils.getByTagAndID(self, tag, id);
-			} else {
-				var uniques = {}, found = [];
-				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
-				items = found;
-			}
-
-			var parsed = Selectors.Utils.parseSelector(selector);
-
-			if (parsed){
-				filtered = [];
-				for (var m = 0, n = items.length; m < n; m++){
-					item = items[m];
-					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
-				}
-				items = filtered;
-			}
-
-		}
-
-		return items;
-
-	}
-
-};
-
-Selectors.Getters = {
-
-	' ': function(found, self, tag, id, uniques){
-		var items = Selectors.Utils.getByTagAndID(self, tag, id);
-		for (var i = 0, l = items.length; i < l; i++){
-			var item = items[i];
-			if (Selectors.Utils.chk(item, uniques)) found.push(item);
-		}
-		return found;
-	},
-
-	'>': function(found, self, tag, id, uniques){
-		var children = Selectors.Utils.getByTagAndID(self, tag, id);
-		for (var i = 0, l = children.length; i < l; i++){
-			var child = children[i];
-			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
-		}
-		return found;
-	},
-
-	'+': function(found, self, tag, id, uniques){
-		while ((self = self.nextSibling)){
-			if (self.nodeType == 1){
-				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
-				break;
-			}
-		}
-		return found;
-	},
-
-	'~': function(found, self, tag, id, uniques){
-		while ((self = self.nextSibling)){
-			if (self.nodeType == 1){
-				if (!Selectors.Utils.chk(self, uniques)) break;
-				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
-			}
-		}
-		return found;
-	}
-
-};
-
-Selectors.Filters = {
-
-	byTag: function(self, tag){
-		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
-	},
-
-	byID: function(self, id){
-		return (!id || (self.id && self.id == id));
-	},
-
-	byClass: function(self, klass){
-		return (self.className && self.className.contains(klass, ' '));
-	},
-
-	byPseudo: function(self, parser, argument, local){
-		return parser.call(self, argument, local);
-	},
-
-	byAttribute: function(self, name, operator, value){
-		var result = Element.prototype.getProperty.call(self, name);
-		if (!result) return (operator == '!=');
-		if (!operator || value == undefined) return true;
-		switch (operator){
-			case '=': return (result == value);
-			case '*=': return (result.contains(value));
-			case '^=': return (result.substr(0, value.length) == value);
-			case '$=': return (result.substr(result.length - value.length) == value);
-			case '!=': return (result != value);
-			case '~=': return result.contains(value, ' ');
-			case '|=': return result.contains(value, '-');
-		}
-		return false;
-	}
-
-};
-
-Selectors.Pseudo = new Hash({
-
-	// w3c pseudo selectors
-
-	checked: function(){
-		return this.checked;
-	},
-
-	empty: function(){
-		return !(this.innerText || this.textContent || '').length;
-	},
-
-	not: function(selector){
-		return !Element.match(this, selector);
-	},
-
-	contains: function(text){
-		return (this.innerText || this.textContent || '').contains(text);
-	},
-
-	'first-child': function(){
-		return Selectors.Pseudo.index.call(this, 0);
-	},
-
-	'last-child': function(){
-		var element = this;
-		while ((element = element.nextSibling)){
-			if (element.nodeType == 1) return false;
-		}
-		return true;
-	},
-
-	'only-child': function(){
-		var prev = this;
-		while ((prev = prev.previousSibling)){
-			if (prev.nodeType == 1) return false;
-		}
-		var next = this;
-		while ((next = next.nextSibling)){
-			if (next.nodeType == 1) return false;
-		}
-		return true;
-	},
-
-	'nth-child': function(argument, local){
-		argument = (argument == undefined) ? 'n' : argument;
-		var parsed = Selectors.Utils.parseNthArgument(argument);
-		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
-		var count = 0;
-		local.positions = local.positions || {};
-		var uid = $uid(this);
-		if (!local.positions[uid]){
-			var self = this;
-			while ((self = self.previousSibling)){
-				if (self.nodeType != 1) continue;
-				count ++;
-				var position = local.positions[$uid(self)];
-				if (position != undefined){
-					count = position + count;
-					break;
-				}
-			}
-			local.positions[uid] = count;
-		}
-		return (local.positions[uid] % parsed.a == parsed.b);
-	},
-
-	// custom pseudo selectors
-
-	index: function(index){
-		var element = this, count = 0;
-		while ((element = element.previousSibling)){
-			if (element.nodeType == 1 && ++count > index) return false;
-		}
-		return (count == index);
-	},
-
-	even: function(argument, local){
-		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
-	},
-
-	odd: function(argument, local){
-		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
-	}
-
-});
-
-
-/*
-Script: Domready.js
-	Contains the domready custom event.
-
-License:
-	MIT-style license.
-*/
-
-Element.Events.domready = {
-
-	onAdd: function(fn){
-		if (Browser.loaded) fn.call(this);
-	}
-
-};
-
 (function(){
 
-	var domready = function(){
-		if (Browser.loaded) return;
-		Browser.loaded = true;
-		window.fireEvent('domready');
-		document.fireEvent('domready');
-	};
-
-	if (Browser.Engine.trident){
-		var temp = document.createElement('div');
-		(function(){
-			($try(function(){
-				temp.doScroll('left');
-				return $(temp).inject(document.body).set('html', 'temp').dispose();
-			})) ? domready() : arguments.callee.delay(50);
-		})();
-	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
-		(function(){
-			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
-		})();
-	} else {
-		window.addEvent('load', domready);
-		document.addEvent('DOMContentLoaded', domready);
-	}
-
-})();
-
-
-/*
-Script: JSON.js
-	JSON encoder and decoder.
-
-License:
-	MIT-style license.
-
-See Also:
-	<http://www.json.org/>
-*/
-
-var JSON = new Hash({
-
-	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
-
-	$replaceChars: function(chr){
-		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
-	},
-
-	encode: function(obj){
-		switch ($type(obj)){
-			case 'string':
-				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
-			case 'array':
-				return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
-			case 'object': case 'hash':
-				var string = [];
-				Hash.each(obj, function(value, key){
-					var json = JSON.encode(value);
-					if (json) string.push(JSON.encode(key) + ':' + json);
-				});
-				return '{' + string + '}';
-			case 'number': case 'boolean': return String(obj);
-			case false: return 'null';
-		}
-		return null;
-	},
-
-	decode: function(string, secure){
-		if ($type(string) != 'string' || !string.length) return null;
-		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
-		return eval('(' + string + ')');
-	}
-
-});
-
-Native.implement([Hash, Array, String, Number], {
-
-	toJSON: function(){
-		return JSON.encode(this);
-	}
-
-});
-
-
-/*
-Script: Cookie.js
-	Class for creating, loading, and saving browser Cookies.
-
-License:
-	MIT-style license.
-
-Credits:
-	Based on the functions by Peter-Paul Koch (http://quirksmode.org).
-*/
-
-var Cookie = new Class({
-
-	Implements: Options,
-
-	options: {
-		path: false,
-		domain: false,
-		duration: false,
-		secure: false,
-		document: document
-	},
-
-	initialize: function(key, options){
-		this.key = key;
-		this.setOptions(options);
-	},
-
-	write: function(value){
-		value = encodeURIComponent(value);
-		if (this.options.domain) value += '; domain=' + this.options.domain;
-		if (this.options.path) value += '; path=' + this.options.path;
-		if (this.options.duration){
-			var date = new Date();
-			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
-			value += '; expires=' + date.toGMTString();
-		}
-		if (this.options.secure) value += '; secure';
-		this.options.document.cookie = this.key + '=' + value;
-		return this;
-	},
-
-	read: function(){
-		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
-		return (value) ? decodeURIComponent(value[1]) : null;
-	},
-
-	dispose: function(){
-		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
-		return this;
-	}
-
-});
-
-Cookie.write = function(key, value, options){
-	return new Cookie(key, options).write(value);
-};
-
-Cookie.read = function(key){
-	return new Cookie(key).read();
-};
-
-Cookie.dispose = function(key, options){
-	return new Cookie(key, options).dispose();
-};
-
-
-/*
-Script: Swiff.js
-	Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.
-
-License:
-	MIT-style license.
-
-Credits:
-	Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
-*/
-
-var Swiff = new Class({
-
-	Implements: [Options],
-
-	options: {
-		id: null,
-		height: 1,
-		width: 1,
-		container: null,
-		properties: {},
-		params: {
-			quality: 'high',
-			allowScriptAccess: 'always',
-			wMode: 'transparent',
-			swLiveConnect: true
-		},
-		callBacks: {},
-		vars: {}
-	},
-
-	toElement: function(){
-		return this.object;
-	},
-
-	initialize: function(path, options){
-		this.instance = 'Swiff_' + $time();
-
-		this.setOptions(options);
-		options = this.options;
-		var id = this.id = options.id || this.instance;
-		var container = $(options.container);
-
-		Swiff.CallBacks[this.instance] = {};
-
-		var params = options.params, vars = options.vars, callBacks = options.callBacks;
-		var properties = $extend({height: options.height, width: options.width}, options.properties);
-
-		var self = this;
-
-		for (var callBack in callBacks){
-			Swiff.CallBacks[this.instance][callBack] = (function(option){
-				return function(){
-					return option.apply(self.object, arguments);
-				};
-			})(callBacks[callBack]);
-			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
-		}
-
-		params.flashVars = Hash.toQueryString(vars);
-		if (Browser.Engine.trident){
-			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
-			params.movie = path;
-		} else {
-			properties.type = 'application/x-shockwave-flash';
-			properties.data = path;
-		}
-		var build = '<object id="' + id + '"';
-		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
-		build += '>';
-		for (var param in params){
-			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
-		}
-		build += '</object>';
-		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
-	},
-
-	replaces: function(element){
-		element = $(element, true);
-		element.parentNode.replaceChild(this.toElement(), element);
-		return this;
-	},
-
-	inject: function(element){
-		$(element, true).appendChild(this.toElement());
-		return this;
-	},
-
-	remote: function(){
-		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
-	}
-
-});
-
-Swiff.CallBacks = {};
-
-Swiff.remote = function(obj, fn){
-	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
-	return eval(rs);
-};
-
-
-/*
-Script: Fx.js
-	Contains the basic animation logic to be extended by all other Fx Classes.
-
-License:
-	MIT-style license.
-*/
-
-var Fx = new Class({
+var Fx = this.Fx = new Class({
 
 	Implements: [Chain, Events, Options],
 
 	options: {
 		/*
-		onStart: $empty,
-		onCancel: $empty,
-		onComplete: $empty,
+		onStart: nil,
+		onCancel: nil,
+		onComplete: nil,
 		*/
-		fps: 50,
+		fps: 60,
 		unit: false,
 		duration: 500,
+		frames: null,
+		frameSkip: true,
 		link: 'ignore'
 	},
 
 	initialize: function(options){
 		this.subject = this.subject || this;
 		this.setOptions(options);
-		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
-		var wait = this.options.wait;
-		if (wait === false) this.options.link = 'cancel';
 	},
 
 	getTransition: function(){
@@ -3113,14 +4908,22 @@
 		};
 	},
 
-	step: function(){
-		var time = $time();
-		if (time < this.time + this.options.duration){
-			var delta = this.transition((time - this.time) / this.options.duration);
+	step: function(now){
+		if (this.options.frameSkip){
+			var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
+			this.time = now;
+			this.frame += frames;
+		} else {
+			this.frame++;
+		}
+
+		if (this.frame < this.frames){
+			var delta = this.transition(this.frame / this.frames);
 			this.set(this.compute(this.from, this.to, delta));
 		} else {
+			this.frame = this.frames;
 			this.set(this.compute(this.from, this.to, 1));
-			this.complete();
+			this.stop();
 		}
 	},
 
@@ -3132,71 +4935,71 @@
 		return Fx.compute(from, to, delta);
 	},
 
-	check: function(caller){
-		if (!this.timer) return true;
+	check: function(){
+		if (!this.isRunning()) return true;
 		switch (this.options.link){
 			case 'cancel': this.cancel(); return true;
-			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
+			case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
 		}
 		return false;
 	},
 
 	start: function(from, to){
-		if (!this.check(arguments.callee, from, to)) return this;
+		if (!this.check(from, to)) return this;
 		this.from = from;
 		this.to = to;
-		this.time = 0;
+		this.frame = (this.options.frameSkip) ? 0 : -1;
+		this.time = null;
 		this.transition = this.getTransition();
-		this.startTimer();
-		this.onStart();
+		var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
+		this.duration = Fx.Durations[duration] || duration.toInt();
+		this.frameInterval = 1000 / fps;
+		this.frames = frames || Math.round(this.duration / this.frameInterval);
+		this.fireEvent('start', this.subject);
+		pushInstance.call(this, fps);
 		return this;
 	},
 
-	complete: function(){
-		if (this.stopTimer()) this.onComplete();
+	stop: function(){
+		if (this.isRunning()){
+			this.time = null;
+			pullInstance.call(this, this.options.fps);
+			if (this.frames == this.frame){
+				this.fireEvent('complete', this.subject);
+				if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+			} else {
+				this.fireEvent('stop', this.subject);
+			}
+		}
 		return this;
 	},
 
 	cancel: function(){
-		if (this.stopTimer()) this.onCancel();
+		if (this.isRunning()){
+			this.time = null;
+			pullInstance.call(this, this.options.fps);
+			this.frame = this.frames;
+			this.fireEvent('cancel', this.subject).clearChain();
+		}
 		return this;
 	},
 
-	onStart: function(){
-		this.fireEvent('start', this.subject);
-	},
-
-	onComplete: function(){
-		this.fireEvent('complete', this.subject);
-		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
-	},
-
-	onCancel: function(){
-		this.fireEvent('cancel', this.subject).clearChain();
-	},
-
 	pause: function(){
-		this.stopTimer();
+		if (this.isRunning()){
+			this.time = null;
+			pullInstance.call(this, this.options.fps);
+		}
 		return this;
 	},
 
 	resume: function(){
-		this.startTimer();
+		if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps);
 		return this;
 	},
 
-	stopTimer: function(){
-		if (!this.timer) return false;
-		this.time = $time() - this.time;
-		this.timer = $clear(this.timer);
-		return true;
-	},
-
-	startTimer: function(){
-		if (this.timer) return false;
-		this.time = $time() - this.time;
-		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
-		return true;
+	isRunning: function(){
+		var list = instances[this.options.fps];
+		return list && list.contains(this);
 	}
 
 });
@@ -3207,13 +5010,52 @@
 
 Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
 
+// global timers
+
+var instances = {}, timers = {};
+
+var loop = function(){
+	var now = Date.now();
+	for (var i = this.length; i--;){
+		var instance = this[i];
+		if (instance) instance.step(now);
+	}
+};
+
+var pushInstance = function(fps){
+	var list = instances[fps] || (instances[fps] = []);
+	list.push(this);
+	if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
+};
+
+var pullInstance = function(fps){
+	var list = instances[fps];
+	if (list){
+		list.erase(this);
+		if (!list.length && timers[fps]){
+			delete instances[fps];
+			timers[fps] = clearInterval(timers[fps]);
+		}
+	}
+};
+
+})();
+
 
 /*
-Script: Fx.CSS.js
-	Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+---
 
-License:
-	MIT-style license.
+name: Fx.CSS
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires: [Fx, Element.Style]
+
+provides: Fx.CSS
+
+...
 */
 
 Fx.CSS = new Class({
@@ -3223,9 +5065,8 @@
 	//prepares the base from/to object
 
 	prepare: function(element, property, values){
-		values = $splat(values);
-		var values1 = values[1];
-		if (!$chk(values1)){
+		values = Array.from(values);
+		if (values[1] == null){
 			values[1] = values[0];
 			values[0] = element.getStyle(property);
 		}
@@ -3236,15 +5077,15 @@
 	//parses a value into an array
 
 	parse: function(value){
-		value = $lambda(value)();
-		value = (typeof value == 'string') ? value.split(' ') : $splat(value);
+		value = Function.from(value)();
+		value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
 		return value.map(function(val){
 			val = String(val);
 			var found = false;
-			Fx.CSS.Parsers.each(function(parser, key){
+			Object.each(Fx.CSS.Parsers, function(parser, key){
 				if (found) return;
 				var parsed = parser.parse(val);
-				if ($chk(parsed)) found = {value: parsed, parser: parser};
+				if (parsed || parsed === 0) found = {value: parsed, parser: parser};
 			});
 			found = found || {value: val, parser: Fx.CSS.Parsers.String};
 			return found;
@@ -3258,14 +5099,14 @@
 		(Math.min(from.length, to.length)).times(function(i){
 			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
 		});
-		computed.$family = {name: 'fx:css:value'};
+		computed.$family = Function.from('fx:css:value');
 		return computed;
 	},
 
 	//serves the value as settable
 
 	serve: function(value, unit){
-		if ($type(value) != 'fx:css:value') value = this.parse(value);
+		if (typeOf(value) != 'fx:css:value') value = this.parse(value);
 		var returned = [];
 		value.each(function(bit){
 			returned = returned.concat(bit.parser.serve(bit.value, unit));
@@ -3283,7 +5124,7 @@
 
 	search: function(selector){
 		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
-		var to = {};
+		var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
 		Array.each(document.styleSheets, function(sheet, j){
 			var href = sheet.href;
 			if (href && href.contains('://') && !href.contains(document.domain)) return;
@@ -3293,11 +5134,11 @@
 				var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
 					return m.toLowerCase();
 				}) : null;
-				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
-				Element.Styles.each(function(value, style){
+				if (!selectorText || !selectorTest.test(selectorText)) return;
+				Object.each(Element.Styles, function(value, style){
 					if (!rule.style[style] || Element.ShortStyles[style]) return;
 					value = String(rule.style[style]);
-					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
+					to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
 				});
 			});
 		});
@@ -3308,7 +5149,7 @@
 
 Fx.CSS.Cache = {};
 
-Fx.CSS.Parsers = new Hash({
+Fx.CSS.Parsers = {
 
 	Color: {
 		parse: function(value){
@@ -3334,20 +5175,38 @@
 	},
 
 	String: {
-		parse: $lambda(false),
-		compute: $arguments(1),
-		serve: $arguments(0)
+		parse: Function.from(false),
+		compute: function(zero, one){
+			return one;
+		},
+		serve: function(zero){
+			return zero;
+		}
 	}
 
-});
+};
+
+//<1.2compat>
+
+Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers);
+
+//</1.2compat>
 
 
 /*
-Script: Fx.Tween.js
-	Formerly Fx.Style, effect to transition any CSS property for an element.
+---
 
-License:
-	MIT-style license.
+name: Fx.Tween
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
 */
 
 Fx.Tween = new Class({
@@ -3355,7 +5214,7 @@
 	Extends: Fx.CSS,
 
 	initialize: function(element, options){
-		this.element = this.subject = $(element);
+		this.element = this.subject = document.id(element);
 		this.parent(options);
 	},
 
@@ -3369,7 +5228,7 @@
 	},
 
 	start: function(property, from, to){
-		if (!this.check(arguments.callee, property, from, to)) return this;
+		if (!this.check(property, from, to)) return this;
 		var args = Array.flatten(arguments);
 		this.property = this.options.property || args.shift();
 		var parsed = this.prepare(this.element, this.property, args);
@@ -3381,17 +5240,17 @@
 Element.Properties.tween = {
 
 	set: function(options){
-		var tween = this.retrieve('tween');
-		if (tween) tween.cancel();
-		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
+		this.get('tween').cancel().setOptions(options);
+		return this;
 	},
 
-	get: function(options){
-		if (options || !this.retrieve('tween')){
-			if (options || !this.retrieve('tween:options')) this.set('tween', options);
-			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
+	get: function(){
+		var tween = this.retrieve('tween');
+		if (!tween){
+			tween = new Fx.Tween(this, {link: 'cancel'});
+			this.store('tween', tween);
 		}
-		return this.retrieve('tween');
+		return tween;
 	}
 
 };
@@ -3399,27 +5258,33 @@
 Element.implement({
 
 	tween: function(property, from, to){
-		this.get('tween').start(arguments);
+		this.get('tween').start(property, from, to);
 		return this;
 	},
 
 	fade: function(how){
-		var fade = this.get('tween'), o = 'opacity', toggle;
-		how = $pick(how, 'toggle');
+		var fade = this.get('tween'), method, to, toggle;
+		if (how == null) how = 'toggle';
 		switch (how){
-			case 'in': fade.start(o, 1); break;
-			case 'out': fade.start(o, 0); break;
-			case 'show': fade.set(o, 1); break;
-			case 'hide': fade.set(o, 0); break;
+			case 'in': method = 'start'; to = 1; break;
+			case 'out': method = 'start'; to = 0; break;
+			case 'show': method = 'set'; to = 1; break;
+			case 'hide': method = 'set'; to = 0; break;
 			case 'toggle':
-				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
-				fade.start(o, (flag) ? 0 : 1);
+				var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
+				method = 'start';
+				to = flag ? 0 : 1;
 				this.store('fade:flag', !flag);
 				toggle = true;
 			break;
-			default: fade.start(o, arguments);
+			default: method = 'start'; to = how;
 		}
 		if (!toggle) this.eliminate('fade:flag');
+		fade[method]('opacity', to);
+		if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
+		else fade.chain(function(){
+			this.element.setStyle('visibility', 'hidden');
+		});
 		return this;
 	},
 
@@ -3440,11 +5305,19 @@
 
 
 /*
-Script: Fx.Morph.js
-	Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+---
 
-License:
-	MIT-style license.
+name: Fx.Morph
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: Fx.Morph
+
+...
 */
 
 Fx.Morph = new Class({
@@ -3452,7 +5325,7 @@
 	Extends: Fx.CSS,
 
 	initialize: function(element, options){
-		this.element = this.subject = $(element);
+		this.element = this.subject = document.id(element);
 		this.parent(options);
 	},
 
@@ -3469,7 +5342,7 @@
 	},
 
 	start: function(properties){
-		if (!this.check(arguments.callee, properties)) return this;
+		if (!this.check(properties)) return this;
 		if (typeof properties == 'string') properties = this.search(properties);
 		var from = {}, to = {};
 		for (var p in properties){
@@ -3485,17 +5358,17 @@
 Element.Properties.morph = {
 
 	set: function(options){
-		var morph = this.retrieve('morph');
-		if (morph) morph.cancel();
-		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
+		this.get('morph').cancel().setOptions(options);
+		return this;
 	},
 
-	get: function(options){
-		if (options || !this.retrieve('morph')){
-			if (options || !this.retrieve('morph:options')) this.set('morph', options);
-			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
+	get: function(){
+		var morph = this.retrieve('morph');
+		if (!morph){
+			morph = new Fx.Morph(this, {link: 'cancel'});
+			this.store('morph', morph);
 		}
-		return this.retrieve('morph');
+		return morph;
 	}
 
 };
@@ -3511,14 +5384,22 @@
 
 
 /*
-Script: Fx.Transitions.js
-	Contains a set of advanced transitions to be used with any of the Fx Classes.
+---
 
-License:
-	MIT-style license.
+name: Fx.Transitions
 
-Credits:
-	Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+  - Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires: Fx
+
+provides: Fx.Transitions
+
+...
 */
 
 Fx.implement({
@@ -3537,25 +5418,34 @@
 });
 
 Fx.Transition = function(transition, params){
-	params = $splat(params);
-	return $extend(transition, {
-		easeIn: function(pos){
-			return transition(pos, params);
-		},
+	params = Array.from(params);
+	var easeIn = function(pos){
+		return transition(pos, params);
+	};
+	return Object.append(easeIn, {
+		easeIn: easeIn,
 		easeOut: function(pos){
 			return 1 - transition(1 - pos, params);
 		},
 		easeInOut: function(pos){
-			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
+			return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
 		}
 	});
 };
 
-Fx.Transitions = new Hash({
+Fx.Transitions = {
 
-	linear: $arguments(0)
+	linear: function(zero){
+		return zero;
+	}
 
-});
+};
+
+//<1.2compat>
+
+Fx.Transitions = new Hash(Fx.Transitions);
+
+//</1.2compat>
 
 Fx.Transitions.extend = function(transitions){
 	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
@@ -3564,7 +5454,7 @@
 Fx.Transitions.extend({
 
 	Pow: function(p, x){
-		return Math.pow(p, x[0] || 6);
+		return Math.pow(p, x && x[0] || 6);
 	},
 
 	Expo: function(p){
@@ -3576,11 +5466,11 @@
 	},
 
 	Sine: function(p){
-		return 1 - Math.sin((1 - p) * Math.PI / 2);
+		return 1 - Math.cos(p * Math.PI / 2);
 	},
 
 	Back: function(p, x){
-		x = x[0] || 1.618;
+		x = x && x[0] || 1.618;
 		return Math.pow(p, 2) * ((x + 1) * p - x);
 	},
 
@@ -3596,37 +5486,55 @@
 	},
 
 	Elastic: function(p, x){
-		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
+		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
 	}
 
 });
 
 ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
 	Fx.Transitions[transition] = new Fx.Transition(function(p){
-		return Math.pow(p, [i + 2]);
+		return Math.pow(p, i + 2);
 	});
 });
 
 
 /*
-Script: Request.js
-	Powerful all purpose Request Class. Uses XMLHTTPRequest.
+---
 
-License:
-	MIT-style license.
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Browser]
+
+provides: Request
+
+...
 */
 
-var Request = new Class({
+(function(){
+
+var empty = function(){},
+	progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
 
 	Implements: [Chain, Events, Options],
 
 	options: {/*
-		onRequest: $empty,
-		onComplete: $empty,
-		onCancel: $empty,
-		onSuccess: $empty,
-		onFailure: $empty,
-		onException: $empty,*/
+		onRequest: function(){},
+		onLoadstart: function(event, xhr){},
+		onProgress: function(event, xhr){},
+		onComplete: function(){},
+		onCancel: function(){},
+		onSuccess: function(responseText, responseXML){},
+		onFailure: function(xhr){},
+		onException: function(headerName, value){},
+		onTimeout: function(){},
+		user: '',
+		password: '',*/
 		url: '',
 		data: '',
 		headers: {
@@ -3642,39 +5550,48 @@
 		urlEncoded: true,
 		encoding: 'utf-8',
 		evalScripts: false,
-		evalResponse: false
+		evalResponse: false,
+		timeout: 0,
+		noCache: false
 	},
 
 	initialize: function(options){
 		this.xhr = new Browser.Request();
 		this.setOptions(options);
-		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
-		this.headers = new Hash(this.options.headers);
+		this.headers = this.options.headers;
 	},
 
 	onStateChange: function(){
-		if (this.xhr.readyState != 4 || !this.running) return;
+		var xhr = this.xhr;
+		if (xhr.readyState != 4 || !this.running) return;
 		this.running = false;
 		this.status = 0;
-		$try(function(){
-			this.status = this.xhr.status;
+		Function.attempt(function(){
+			var status = xhr.status;
+			this.status = (status == 1223) ? 204 : status;
 		}.bind(this));
-		if (this.options.isSuccess.call(this, this.status)){
-			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
+		xhr.onreadystatechange = empty;
+		if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+		clearTimeout(this.timer);
+
+		this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+		if (this.options.isSuccess.call(this, this.status))
 			this.success(this.response.text, this.response.xml);
-		} else {
-			this.response = {text: null, xml: null};
+		else
 			this.failure();
-		}
-		this.xhr.onreadystatechange = $empty;
 	},
 
 	isSuccess: function(){
-		return ((this.status >= 200) && (this.status < 300));
+		var status = this.status;
+		return (status >= 200 && status < 300);
+	},
+
+	isRunning: function(){
+		return !!this.running;
 	},
 
 	processScripts: function(text){
-		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
+		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
 		return text.stripScripts(this.options.evalScripts);
 	},
 
@@ -3694,40 +5611,54 @@
 		this.fireEvent('complete').fireEvent('failure', this.xhr);
 	},
 
+	loadstart: function(event){
+		this.fireEvent('loadstart', [event, this.xhr]);
+	},
+
+	progress: function(event){
+		this.fireEvent('progress', [event, this.xhr]);
+	},
+
+	timeout: function(){
+		this.fireEvent('timeout', this.xhr);
+	},
+
 	setHeader: function(name, value){
-		this.headers.set(name, value);
+		this.headers[name] = value;
 		return this;
 	},
 
 	getHeader: function(name){
-		return $try(function(){
+		return Function.attempt(function(){
 			return this.xhr.getResponseHeader(name);
 		}.bind(this));
 	},
 
-	check: function(caller){
+	check: function(){
 		if (!this.running) return true;
 		switch (this.options.link){
 			case 'cancel': this.cancel(); return true;
-			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
+			case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
 		}
 		return false;
 	},
 
 	send: function(options){
-		if (!this.check(arguments.callee, options)) return this;
+		if (!this.check(options)) return this;
+
+		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
 		this.running = true;
 
-		var type = $type(options);
+		var type = typeOf(options);
 		if (type == 'string' || type == 'element') options = {data: options};
 
 		var old = this.options;
-		options = $extend({data: old.data, url: old.url, method: old.method}, options);
-		var data = options.data, url = options.url, method = options.method;
+		options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+		var data = options.data, url = String(options.url), method = options.method.toLowerCase();
 
-		switch ($type(data)){
-			case 'element': data = $(data).toQueryString(); break;
-			case 'object': case 'hash': data = Hash.toQueryString(data);
+		switch (typeOf(data)){
+			case 'element': data = document.id(data).toQueryString(); break;
+			case 'object': case 'hash': data = Object.toQueryString(data);
 		}
 
 		if (this.options.format){
@@ -3735,45 +5666,64 @@
 			data = (data) ? format + '&' + data : format;
 		}
 
-		if (this.options.emulation && ['put', 'delete'].contains(method)){
+		if (this.options.emulation && !['get', 'post'].contains(method)){
 			var _method = '_method=' + method;
 			data = (data) ? _method + '&' + data : _method;
 			method = 'post';
 		}
 
-		if (this.options.urlEncoded && method == 'post'){
+		if (this.options.urlEncoded && ['post', 'put'].contains(method)){
 			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
-			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
+			this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
 		}
 
+		if (!url) url = document.location.pathname;
+
+		var trimPosition = url.lastIndexOf('/');
+		if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+		if (this.options.noCache)
+			url += (url.contains('?') ? '&' : '?') + String.uniqueID();
+
 		if (data && method == 'get'){
-			url = url + (url.contains('?') ? '&' : '?') + data;
+			url += (url.contains('?') ? '&' : '?') + data;
 			data = null;
 		}
 
-		this.xhr.open(method.toUpperCase(), url, this.options.async);
+		var xhr = this.xhr;
+		if (progressSupport){
+			xhr.onloadstart = this.loadstart.bind(this);
+			xhr.onprogress = this.progress.bind(this);
+		}
 
-		this.xhr.onreadystatechange = this.onStateChange.bind(this);
+		xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+		if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
 
-		this.headers.each(function(value, key){
+		xhr.onreadystatechange = this.onStateChange.bind(this);
+
+		Object.each(this.headers, function(value, key){
 			try {
-				this.xhr.setRequestHeader(key, value);
+				xhr.setRequestHeader(key, value);
 			} catch (e){
 				this.fireEvent('exception', [key, value]);
 			}
 		}, this);
 
 		this.fireEvent('request');
-		this.xhr.send(data);
+		xhr.send(data);
 		if (!this.options.async) this.onStateChange();
+		if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
 		return this;
 	},
 
 	cancel: function(){
 		if (!this.running) return this;
 		this.running = false;
-		this.xhr.abort();
-		this.xhr.onreadystatechange = $empty;
+		var xhr = this.xhr;
+		xhr.abort();
+		clearTimeout(this.timer);
+		xhr.onreadystatechange = empty;
+		if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
 		this.xhr = new Browser.Request();
 		this.fireEvent('cancel');
 		return this;
@@ -3781,36 +5731,36 @@
 
 });
 
-(function(){
-
 var methods = {};
 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
-	methods[method] = function(){
-		var params = Array.link(arguments, {url: String.type, data: $defined});
-		return this.send($extend(params, {method: method.toLowerCase()}));
+	methods[method] = function(data){
+		var object = {
+			method: method
+		};
+		if (data != null) object.data = data;
+		return this.send(object);
 	};
 });
 
 Request.implement(methods);
 
-})();
-
 Element.Properties.send = {
 
 	set: function(options){
-		var send = this.retrieve('send');
-		if (send) send.cancel();
-		return this.eliminate('send').store('send:options', $extend({
-			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
-		}, options));
+		var send = this.get('send').cancel();
+		send.setOptions(options);
+		return this;
 	},
 
-	get: function(options){
-		if (options || !this.retrieve('send')){
-			if (options || !this.retrieve('send:options')) this.set('send', options);
-			this.store('send', new Request(this.retrieve('send:options')));
+	get: function(){
+		var send = this.retrieve('send');
+		if (!send){
+			send = new Request({
+				data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+			});
+			this.store('send', send);
 		}
-		return this.retrieve('send');
+		return send;
 	}
 
 };
@@ -3825,13 +5775,22 @@
 
 });
 
+})();
 
 /*
-Script: Request.HTML.js
-	Extends the basic Request Class with additional methods for interacting with HTML responses.
+---
 
-License:
-	MIT-style license.
+name: Request.HTML
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires: [Element, Request]
+
+provides: Request.HTML
+
+...
 */
 
 Request.HTML = new Class({
@@ -3840,32 +5799,12 @@
 
 	options: {
 		update: false,
+		append: false,
 		evalScripts: true,
-		filter: false
-	},
-
-	processHTML: function(text){
-		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
-		text = (match) ? match[1] : text;
-
-		var container = new Element('div');
-
-		return $try(function(){
-			var root = '<root>' + text + '</root>', doc;
-			if (Browser.Engine.trident){
-				doc = new ActiveXObject('Microsoft.XMLDOM');
-				doc.async = false;
-				doc.loadXML(root);
-			} else {
-				doc = new DOMParser().parseFromString(root, 'text/xml');
-			}
-			root = doc.getElementsByTagName('root')[0];
-			for (var i = 0, k = root.childNodes.length; i < k; i++){
-				var child = Element.clone(root.childNodes[i], true, true);
-				if (child) container.grab(child);
-			}
-			return container;
-		}) || container.set('html', text);
+		filter: false,
+		headers: {
+			Accept: 'text/html, application/xml, text/xml, */*'
+		}
 	},
 
 	success: function(text){
@@ -3875,14 +5814,24 @@
 			response.javascript = script;
 		});
 
-		var temp = this.processHTML(response.html);
+		var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+		if (match) response.html = match[1];
+		var temp = new Element('div').set('html', response.html);
 
 		response.tree = temp.childNodes;
-		response.elements = temp.getElements('*');
+		response.elements = temp.getElements(options.filter || '*');
 
-		if (options.filter) response.tree = response.elements.filter(options.filter);
-		if (options.update) $(options.update).empty().set('html', response.html);
-		if (options.evalScripts) $exec(response.javascript);
+		if (options.filter) response.tree = response.elements;
+		if (options.update){
+			var update = document.id(options.update).empty();
+			if (options.filter) update.adopt(response.elements);
+			else update.set('html', response.html);
+		} else if (options.append){
+			var append = document.id(options.append);
+			if (options.filter) response.elements.reverse().inject(append);
+			else append.adopt(temp.getChildren());
+		}
+		if (options.evalScripts) Browser.exec(response.javascript);
 
 		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
 	}
@@ -3892,17 +5841,18 @@
 Element.Properties.load = {
 
 	set: function(options){
-		var load = this.retrieve('load');
-		if (load) load.cancel();
-		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
+		var load = this.get('load').cancel();
+		load.setOptions(options);
+		return this;
 	},
 
-	get: function(options){
-		if (options || ! this.retrieve('load')){
-			if (options || !this.retrieve('load:options')) this.set('load', options);
-			this.store('load', new Request.HTML(this.retrieve('load:options')));
+	get: function(){
+		var load = this.retrieve('load');
+		if (!load){
+			load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
+			this.store('load', load);
 		}
-		return this.retrieve('load');
+		return load;
 	}
 
 };
@@ -3910,7 +5860,7 @@
 Element.implement({
 
 	load: function(){
-		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
+		this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
 		return this;
 	}
 
@@ -3918,11 +5868,102 @@
 
 
 /*
-Script: Request.JSON.js
-	Extends the basic Request Class with additional methods for sending and receiving JSON data.
+---
 
-License:
-	MIT-style license.
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+//<1.2compat>
+
+JSON = new Hash({
+	stringify: JSON.stringify,
+	parse: JSON.parse
+});
+
+//</1.2compat>
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+	return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+	string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+					replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+					replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+	return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+	return JSON.stringify(obj);
+} : function(obj){
+	if (obj && obj.toJSON) obj = obj.toJSON();
+
+	switch (typeOf(obj)){
+		case 'string':
+			return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+		case 'array':
+			return '[' + obj.map(JSON.encode).clean() + ']';
+		case 'object': case 'hash':
+			var string = [];
+			Object.each(obj, function(value, key){
+				var json = JSON.encode(value);
+				if (json) string.push(JSON.encode(key) + ':' + json);
+			});
+			return '{' + string + '}';
+		case 'number': case 'boolean': return '' + obj;
+		case 'null': return 'null';
+	}
+
+	return null;
+};
+
+JSON.decode = function(string, secure){
+	if (!string || typeOf(string) != 'string') return null;
+
+	if (secure || JSON.secure){
+		if (JSON.parse) return JSON.parse(string);
+		if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+	}
+
+	return eval('(' + string + ')');
+};
+
+})();
+
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
 */
 
 Request.JSON = new Class({
@@ -3930,17 +5971,327 @@
 	Extends: Request,
 
 	options: {
+		/*onError: function(text, error){},*/
 		secure: true
 	},
 
 	initialize: function(options){
 		this.parent(options);
-		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
+		Object.append(this.headers, {
+			'Accept': 'application/json',
+			'X-Request': 'JSON'
+		});
 	},
 
 	success: function(text){
-		this.response.json = JSON.decode(text, this.options.secure);
-		this.onSuccess(this.response.json, text);
+		var json;
+		try {
+			json = this.response.json = JSON.decode(text, this.options.secure);
+		} catch (error){
+			this.fireEvent('error', [text, error]);
+			return;
+		}
+		if (json == null) this.onFailure();
+		else this.onSuccess(json, text);
 	}
 
 });
+
+
+/*
+---
+
+name: Cookie
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+  - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires: [Options, Browser]
+
+provides: Cookie
+
+...
+*/
+
+var Cookie = new Class({
+
+	Implements: Options,
+
+	options: {
+		path: '/',
+		domain: false,
+		duration: false,
+		secure: false,
+		document: document,
+		encode: true
+	},
+
+	initialize: function(key, options){
+		this.key = key;
+		this.setOptions(options);
+	},
+
+	write: function(value){
+		if (this.options.encode) value = encodeURIComponent(value);
+		if (this.options.domain) value += '; domain=' + this.options.domain;
+		if (this.options.path) value += '; path=' + this.options.path;
+		if (this.options.duration){
+			var date = new Date();
+			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+			value += '; expires=' + date.toGMTString();
+		}
+		if (this.options.secure) value += '; secure';
+		this.options.document.cookie = this.key + '=' + value;
+		return this;
+	},
+
+	read: function(){
+		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
+		return (value) ? decodeURIComponent(value[1]) : null;
+	},
+
+	dispose: function(){
+		new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
+		return this;
+	}
+
+});
+
+Cookie.write = function(key, value, options){
+	return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+	return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+	return new Cookie(key, options).dispose();
+};
+
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+	loaded,
+	checks = [],
+	shouldPoll,
+	timer,
+	testElement = document.createElement('div');
+
+var domready = function(){
+	clearTimeout(timer);
+	if (ready) return;
+	Browser.loaded = ready = true;
+	document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+
+	document.fireEvent('domready');
+	window.fireEvent('domready');
+};
+
+var check = function(){
+	for (var i = checks.length; i--;) if (checks[i]()){
+		domready();
+		return true;
+	}
+	return false;
+};
+
+var poll = function(){
+	clearTimeout(timer);
+	if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+	try {
+		testElement.doScroll();
+		return true;
+	} catch (e){}
+	return false;
+};
+// If doScroll works already, it can't be used to determine domready
+//   e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+	checks.push(doScrollWorks);
+	shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+	var state = document.readyState;
+	return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+	onAdd: function(fn){
+		if (ready) fn.call(this);
+	}
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+	base: 'load',
+	onAdd: function(fn){
+		if (loaded && this == window) fn.call(this);
+	},
+	condition: function(){
+		if (this == window){
+			domready();
+			delete Element.Events.load;
+		}
+		return true;
+	}
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+	loaded = true;
+});
+
+})(window, document);
+
+
+/*
+---
+
+name: Swiff
+
+description: Wrapper for embedding SWF movies. Supports External Interface Communication.
+
+license: MIT-style license.
+
+credits:
+  - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
+
+requires: [Options, Object, Element]
+
+provides: Swiff
+
+...
+*/
+
+(function(){
+
+var Swiff = this.Swiff = new Class({
+
+	Implements: Options,
+
+	options: {
+		id: null,
+		height: 1,
+		width: 1,
+		container: null,
+		properties: {},
+		params: {
+			quality: 'high',
+			allowScriptAccess: 'always',
+			wMode: 'window',
+			swLiveConnect: true
+		},
+		callBacks: {},
+		vars: {}
+	},
+
+	toElement: function(){
+		return this.object;
+	},
+
+	initialize: function(path, options){
+		this.instance = 'Swiff_' + String.uniqueID();
+
+		this.setOptions(options);
+		options = this.options;
+		var id = this.id = options.id || this.instance;
+		var container = document.id(options.container);
+
+		Swiff.CallBacks[this.instance] = {};
+
+		var params = options.params, vars = options.vars, callBacks = options.callBacks;
+		var properties = Object.append({height: options.height, width: options.width}, options.properties);
+
+		var self = this;
+
+		for (var callBack in callBacks){
+			Swiff.CallBacks[this.instance][callBack] = (function(option){
+				return function(){
+					return option.apply(self.object, arguments);
+				};
+			})(callBacks[callBack]);
+			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
+		}
+
+		params.flashVars = Object.toQueryString(vars);
+		if (Browser.ie){
+			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
+			params.movie = path;
+		} else {
+			properties.type = 'application/x-shockwave-flash';
+		}
+		properties.data = path;
+
+		var build = '<object id="' + id + '"';
+		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
+		build += '>';
+		for (var param in params){
+			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
+		}
+		build += '</object>';
+		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
+	},
+
+	replaces: function(element){
+		element = document.id(element, true);
+		element.parentNode.replaceChild(this.toElement(), element);
+		return this;
+	},
+
+	inject: function(element){
+		document.id(element, true).appendChild(this.toElement());
+		return this;
+	},
+
+	remote: function(){
+		return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments));
+	}
+
+});
+
+Swiff.CallBacks = {};
+
+Swiff.remote = function(obj, fn){
+	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
+	return eval(rs);
+};
+
+})();
+
diff --git a/chrome/test/data/dromaeo/lib/prototype.js b/chrome/test/data/dromaeo/lib/prototype.js
index dfe8ab4..474b2231 100644
--- a/chrome/test/data/dromaeo/lib/prototype.js
+++ b/chrome/test/data/dromaeo/lib/prototype.js
@@ -1,5 +1,5 @@
-/*  Prototype JavaScript framework, version 1.6.0.3
- *  (c) 2005-2008 Sam Stephenson
+/*  Prototype JavaScript framework, version 1.7
+ *  (c) 2005-2010 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://www.prototypejs.org/
@@ -7,32 +7,53 @@
  *--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.6.0.3',
 
-  Browser: {
-    IE:     !!(window.attachEvent &&
-      navigator.userAgent.indexOf('Opera') === -1),
-    Opera:  navigator.userAgent.indexOf('Opera') > -1,
-    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
-    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
-      navigator.userAgent.indexOf('KHTML') === -1,
-    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
-  },
+  Version: '1.7',
+
+  Browser: (function(){
+    var ua = navigator.userAgent;
+    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
+    return {
+      IE:             !!window.attachEvent && !isOpera,
+      Opera:          isOpera,
+      WebKit:         ua.indexOf('AppleWebKit/') > -1,
+      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
+      MobileSafari:   /Apple.*Mobile/.test(ua)
+    }
+  })(),
 
   BrowserFeatures: {
     XPath: !!document.evaluate,
+
     SelectorsAPI: !!document.querySelector,
-    ElementExtensions: !!window.HTMLElement,
-    SpecificElementExtensions:
-      document.createElement('div')['__proto__'] &&
-      document.createElement('div')['__proto__'] !==
-        document.createElement('form')['__proto__']
+
+    ElementExtensions: (function() {
+      var constructor = window.Element || window.HTMLElement;
+      return !!(constructor && constructor.prototype);
+    })(),
+    SpecificElementExtensions: (function() {
+      if (typeof window.HTMLDivElement !== 'undefined')
+        return true;
+
+      var div = document.createElement('div'),
+          form = document.createElement('form'),
+          isSupported = false;
+
+      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
+        isSupported = true;
+      }
+
+      div = form = null;
+
+      return isSupported;
+    })()
   },
 
   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
 
   emptyFunction: function() { },
+
   K: function(x) { return x }
 };
 
@@ -40,232 +61,8 @@
   Prototype.BrowserFeatures.SpecificElementExtensions = false;
 
 
-/* Based on Alex Arnell's inheritance implementation. */
-var Class = {
-  create: function() {
-    var parent = null, properties = $A(arguments);
-    if (Object.isFunction(properties[0]))
-      parent = properties.shift();
-
-    function klass() {
-      this.initialize.apply(this, arguments);
-    }
-
-    Object.extend(klass, Class.Methods);
-    klass.superclass = parent;
-    klass.subclasses = [];
-
-    if (parent) {
-      var subclass = function() { };
-      subclass.prototype = parent.prototype;
-      klass.prototype = new subclass;
-      parent.subclasses.push(klass);
-    }
-
-    for (var i = 0; i < properties.length; i++)
-      klass.addMethods(properties[i]);
-
-    if (!klass.prototype.initialize)
-      klass.prototype.initialize = Prototype.emptyFunction;
-
-    klass.prototype.constructor = klass;
-
-    return klass;
-  }
-};
-
-Class.Methods = {
-  addMethods: function(source) {
-    var ancestor   = this.superclass && this.superclass.prototype;
-    var properties = Object.keys(source);
-
-    if (!Object.keys({ toString: true }).length)
-      properties.push("toString", "valueOf");
-
-    for (var i = 0, length = properties.length; i < length; i++) {
-      var property = properties[i], value = source[property];
-      if (ancestor && Object.isFunction(value) &&
-          value.argumentNames().first() == "$super") {
-        var method = value;
-        value = (function(m) {
-          return function() { return ancestor[m].apply(this, arguments) };
-        })(property).wrap(method);
-
-        value.valueOf = method.valueOf.bind(method);
-        value.toString = method.toString.bind(method);
-      }
-      this.prototype[property] = value;
-    }
-
-    return this;
-  }
-};
-
 var Abstract = { };
 
-Object.extend = function(destination, source) {
-  for (var property in source)
-    destination[property] = source[property];
-  return destination;
-};
-
-Object.extend(Object, {
-  inspect: function(object) {
-    try {
-      if (Object.isUndefined(object)) return 'undefined';
-      if (object === null) return 'null';
-      return object.inspect ? object.inspect() : String(object);
-    } catch (e) {
-      if (e instanceof RangeError) return '...';
-      throw e;
-    }
-  },
-
-  toJSON: function(object) {
-    var type = typeof object;
-    switch (type) {
-      case 'undefined':
-      case 'function':
-      case 'unknown': return;
-      case 'boolean': return object.toString();
-    }
-
-    if (object === null) return 'null';
-    if (object.toJSON) return object.toJSON();
-    if (Object.isElement(object)) return;
-
-    var results = [];
-    for (var property in object) {
-      var value = Object.toJSON(object[property]);
-      if (!Object.isUndefined(value))
-        results.push(property.toJSON() + ': ' + value);
-    }
-
-    return '{' + results.join(', ') + '}';
-  },
-
-  toQueryString: function(object) {
-    return $H(object).toQueryString();
-  },
-
-  toHTML: function(object) {
-    return object && object.toHTML ? object.toHTML() : String.interpret(object);
-  },
-
-  keys: function(object) {
-    var keys = [];
-    for (var property in object)
-      keys.push(property);
-    return keys;
-  },
-
-  values: function(object) {
-    var values = [];
-    for (var property in object)
-      values.push(object[property]);
-    return values;
-  },
-
-  clone: function(object) {
-    return Object.extend({ }, object);
-  },
-
-  isElement: function(object) {
-    return !!(object && object.nodeType == 1);
-  },
-
-  isArray: function(object) {
-    return object != null && typeof object == "object" &&
-      'splice' in object && 'join' in object;
-  },
-
-  isHash: function(object) {
-    return object instanceof Hash;
-  },
-
-  isFunction: function(object) {
-    return typeof object == "function";
-  },
-
-  isString: function(object) {
-    return typeof object == "string";
-  },
-
-  isNumber: function(object) {
-    return typeof object == "number";
-  },
-
-  isUndefined: function(object) {
-    return typeof object == "undefined";
-  }
-});
-
-Object.extend(Function.prototype, {
-  argumentNames: function() {
-    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
-      .replace(/\s+/g, '').split(',');
-    return names.length == 1 && !names[0] ? [] : names;
-  },
-
-  bind: function() {
-    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
-    var __method = this, args = $A(arguments), object = args.shift();
-    return function() {
-      return __method.apply(object, args.concat($A(arguments)));
-    }
-  },
-
-  bindAsEventListener: function() {
-    var __method = this, args = $A(arguments), object = args.shift();
-    return function(event) {
-      return __method.apply(object, [event || window.event].concat(args));
-    }
-  },
-
-  curry: function() {
-    if (!arguments.length) return this;
-    var __method = this, args = $A(arguments);
-    return function() {
-      return __method.apply(this, args.concat($A(arguments)));
-    }
-  },
-
-  delay: function() {
-    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
-    return window.setTimeout(function() {
-      return __method.apply(__method, args);
-    }, timeout);
-  },
-
-  defer: function() {
-    var args = [0.01].concat($A(arguments));
-    return this.delay.apply(this, args);
-  },
-
-  wrap: function(wrapper) {
-    var __method = this;
-    return function() {
-      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
-    }
-  },
-
-  methodize: function() {
-    if (this._methodized) return this._methodized;
-    var __method = this;
-    return this._methodized = function() {
-      return __method.apply(null, [this].concat($A(arguments)));
-    };
-  }
-});
-
-Date.prototype.toJSON = function() {
-  return '"' + this.getUTCFullYear() + '-' +
-    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
-    this.getUTCDate().toPaddedString(2) + 'T' +
-    this.getUTCHours().toPaddedString(2) + ':' +
-    this.getUTCMinutes().toPaddedString(2) + ':' +
-    this.getUTCSeconds().toPaddedString(2) + 'Z"';
-};
 
 var Try = {
   these: function() {
@@ -283,14 +80,407 @@
   }
 };
 
+/* Based on Alex Arnell's inheritance implementation. */
+
+var Class = (function() {
+
+  var IS_DONTENUM_BUGGY = (function(){
+    for (var p in { toString: 1 }) {
+      if (p === 'toString') return false;
+    }
+    return true;
+  })();
+
+  function subclass() {};
+  function create() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0, length = properties.length; i < length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+    return klass;
+  }
+
+  function addMethods(source) {
+    var ancestor   = this.superclass && this.superclass.prototype,
+        properties = Object.keys(source);
+
+    if (IS_DONTENUM_BUGGY) {
+      if (source.toString != Object.prototype.toString)
+        properties.push("toString");
+      if (source.valueOf != Object.prototype.valueOf)
+        properties.push("valueOf");
+    }
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames()[0] == "$super") {
+        var method = value;
+        value = (function(m) {
+          return function() { return ancestor[m].apply(this, arguments); };
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+
+  return {
+    create: create,
+    Methods: {
+      addMethods: addMethods
+    }
+  };
+})();
+(function() {
+
+  var _toString = Object.prototype.toString,
+      NULL_TYPE = 'Null',
+      UNDEFINED_TYPE = 'Undefined',
+      BOOLEAN_TYPE = 'Boolean',
+      NUMBER_TYPE = 'Number',
+      STRING_TYPE = 'String',
+      OBJECT_TYPE = 'Object',
+      FUNCTION_CLASS = '[object Function]',
+      BOOLEAN_CLASS = '[object Boolean]',
+      NUMBER_CLASS = '[object Number]',
+      STRING_CLASS = '[object String]',
+      ARRAY_CLASS = '[object Array]',
+      DATE_CLASS = '[object Date]',
+      NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
+        typeof JSON.stringify === 'function' &&
+        JSON.stringify(0) === '0' &&
+        typeof JSON.stringify(Prototype.K) === 'undefined';
+
+  function Type(o) {
+    switch(o) {
+      case null: return NULL_TYPE;
+      case (void 0): return UNDEFINED_TYPE;
+    }
+    var type = typeof o;
+    switch(type) {
+      case 'boolean': return BOOLEAN_TYPE;
+      case 'number':  return NUMBER_TYPE;
+      case 'string':  return STRING_TYPE;
+    }
+    return OBJECT_TYPE;
+  }
+
+  function extend(destination, source) {
+    for (var property in source)
+      destination[property] = source[property];
+    return destination;
+  }
+
+  function inspect(object) {
+    try {
+      if (isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : String(object);
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  }
+
+  function toJSON(value) {
+    return Str('', { '': value }, []);
+  }
+
+  function Str(key, holder, stack) {
+    var value = holder[key],
+        type = typeof value;
+
+    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
+      value = value.toJSON(key);
+    }
+
+    var _class = _toString.call(value);
+
+    switch (_class) {
+      case NUMBER_CLASS:
+      case BOOLEAN_CLASS:
+      case STRING_CLASS:
+        value = value.valueOf();
+    }
+
+    switch (value) {
+      case null: return 'null';
+      case true: return 'true';
+      case false: return 'false';
+    }
+
+    type = typeof value;
+    switch (type) {
+      case 'string':
+        return value.inspect(true);
+      case 'number':
+        return isFinite(value) ? String(value) : 'null';
+      case 'object':
+
+        for (var i = 0, length = stack.length; i < length; i++) {
+          if (stack[i] === value) { throw new TypeError(); }
+        }
+        stack.push(value);
+
+        var partial = [];
+        if (_class === ARRAY_CLASS) {
+          for (var i = 0, length = value.length; i < length; i++) {
+            var str = Str(i, value, stack);
+            partial.push(typeof str === 'undefined' ? 'null' : str);
+          }
+          partial = '[' + partial.join(',') + ']';
+        } else {
+          var keys = Object.keys(value);
+          for (var i = 0, length = keys.length; i < length; i++) {
+            var key = keys[i], str = Str(key, value, stack);
+            if (typeof str !== "undefined") {
+               partial.push(key.inspect(true)+ ':' + str);
+             }
+          }
+          partial = '{' + partial.join(',') + '}';
+        }
+        stack.pop();
+        return partial;
+    }
+  }
+
+  function stringify(object) {
+    return JSON.stringify(object);
+  }
+
+  function toQueryString(object) {
+    return $H(object).toQueryString();
+  }
+
+  function toHTML(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  }
+
+  function keys(object) {
+    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
+    var results = [];
+    for (var property in object) {
+      if (object.hasOwnProperty(property)) {
+        results.push(property);
+      }
+    }
+    return results;
+  }
+
+  function values(object) {
+    var results = [];
+    for (var property in object)
+      results.push(object[property]);
+    return results;
+  }
+
+  function clone(object) {
+    return extend({ }, object);
+  }
+
+  function isElement(object) {
+    return !!(object && object.nodeType == 1);
+  }
+
+  function isArray(object) {
+    return _toString.call(object) === ARRAY_CLASS;
+  }
+
+  var hasNativeIsArray = (typeof Array.isArray == 'function')
+    && Array.isArray([]) && !Array.isArray({});
+
+  if (hasNativeIsArray) {
+    isArray = Array.isArray;
+  }
+
+  function isHash(object) {
+    return object instanceof Hash;
+  }
+
+  function isFunction(object) {
+    return _toString.call(object) === FUNCTION_CLASS;
+  }
+
+  function isString(object) {
+    return _toString.call(object) === STRING_CLASS;
+  }
+
+  function isNumber(object) {
+    return _toString.call(object) === NUMBER_CLASS;
+  }
+
+  function isDate(object) {
+    return _toString.call(object) === DATE_CLASS;
+  }
+
+  function isUndefined(object) {
+    return typeof object === "undefined";
+  }
+
+  extend(Object, {
+    extend:        extend,
+    inspect:       inspect,
+    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
+    toQueryString: toQueryString,
+    toHTML:        toHTML,
+    keys:          Object.keys || keys,
+    values:        values,
+    clone:         clone,
+    isElement:     isElement,
+    isArray:       isArray,
+    isHash:        isHash,
+    isFunction:    isFunction,
+    isString:      isString,
+    isNumber:      isNumber,
+    isDate:        isDate,
+    isUndefined:   isUndefined
+  });
+})();
+Object.extend(Function.prototype, (function() {
+  var slice = Array.prototype.slice;
+
+  function update(array, args) {
+    var arrayLength = array.length, length = args.length;
+    while (length--) array[arrayLength + length] = args[length];
+    return array;
+  }
+
+  function merge(array, args) {
+    array = slice.call(array, 0);
+    return update(array, args);
+  }
+
+  function argumentNames() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
+      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
+      .replace(/\s+/g, '').split(',');
+    return names.length == 1 && !names[0] ? [] : names;
+  }
+
+  function bind(context) {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = slice.call(arguments, 1);
+    return function() {
+      var a = merge(args, arguments);
+      return __method.apply(context, a);
+    }
+  }
+
+  function bindAsEventListener(context) {
+    var __method = this, args = slice.call(arguments, 1);
+    return function(event) {
+      var a = update([event || window.event], args);
+      return __method.apply(context, a);
+    }
+  }
+
+  function curry() {
+    if (!arguments.length) return this;
+    var __method = this, args = slice.call(arguments, 0);
+    return function() {
+      var a = merge(args, arguments);
+      return __method.apply(this, a);
+    }
+  }
+
+  function delay(timeout) {
+    var __method = this, args = slice.call(arguments, 1);
+    timeout = timeout * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  }
+
+  function defer() {
+    var args = update([0.01], arguments);
+    return this.delay.apply(this, args);
+  }
+
+  function wrap(wrapper) {
+    var __method = this;
+    return function() {
+      var a = update([__method.bind(this)], arguments);
+      return wrapper.apply(this, a);
+    }
+  }
+
+  function methodize() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      var a = update([this], arguments);
+      return __method.apply(null, a);
+    };
+  }
+
+  return {
+    argumentNames:       argumentNames,
+    bind:                bind,
+    bindAsEventListener: bindAsEventListener,
+    curry:               curry,
+    delay:               delay,
+    defer:               defer,
+    wrap:                wrap,
+    methodize:           methodize
+  }
+})());
+
+
+
+(function(proto) {
+
+
+  function toISOString() {
+    return this.getUTCFullYear() + '-' +
+      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+      this.getUTCDate().toPaddedString(2) + 'T' +
+      this.getUTCHours().toPaddedString(2) + ':' +
+      this.getUTCMinutes().toPaddedString(2) + ':' +
+      this.getUTCSeconds().toPaddedString(2) + 'Z';
+  }
+
+
+  function toJSON() {
+    return this.toISOString();
+  }
+
+  if (!proto.toISOString) proto.toISOString = toISOString;
+  if (!proto.toJSON) proto.toJSON = toJSON;
+
+})(Date.prototype);
+
+
 RegExp.prototype.match = RegExp.prototype.test;
 
 RegExp.escape = function(str) {
   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
 };
-
-/*--------------------------------------------------------------------------*/
-
 var PeriodicalExecuter = Class.create({
   initialize: function(callback, frequency) {
     this.callback = callback;
@@ -319,8 +509,10 @@
       try {
         this.currentlyExecuting = true;
         this.execute();
-      } finally {
         this.currentlyExecuting = false;
+      } catch(e) {
+        this.currentlyExecuting = false;
+        throw e;
       }
     }
   }
@@ -339,10 +531,28 @@
   }
 });
 
-Object.extend(String.prototype, {
-  gsub: function(pattern, replacement) {
+Object.extend(String.prototype, (function() {
+  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
+    typeof JSON.parse === 'function' &&
+    JSON.parse('{"test": true}').test;
+
+  function prepareReplacement(replacement) {
+    if (Object.isFunction(replacement)) return replacement;
+    var template = new Template(replacement);
+    return function(match) { return template.evaluate(match) };
+  }
+
+  function gsub(pattern, replacement) {
     var result = '', source = this, match;
-    replacement = arguments.callee.prepareReplacement(replacement);
+    replacement = prepareReplacement(replacement);
+
+    if (Object.isString(pattern))
+      pattern = RegExp.escape(pattern);
+
+    if (!(pattern.length || pattern.source)) {
+      replacement = replacement('');
+      return replacement + source.split('').join(replacement) + replacement;
+    }
 
     while (source.length > 0) {
       if (match = source.match(pattern)) {
@@ -354,76 +564,72 @@
       }
     }
     return result;
-  },
+  }
 
-  sub: function(pattern, replacement, count) {
-    replacement = this.gsub.prepareReplacement(replacement);
+  function sub(pattern, replacement, count) {
+    replacement = prepareReplacement(replacement);
     count = Object.isUndefined(count) ? 1 : count;
 
     return this.gsub(pattern, function(match) {
       if (--count < 0) return match[0];
       return replacement(match);
     });
-  },
+  }
 
-  scan: function(pattern, iterator) {
+  function scan(pattern, iterator) {
     this.gsub(pattern, iterator);
     return String(this);
-  },
+  }
 
-  truncate: function(length, truncation) {
+  function truncate(length, truncation) {
     length = length || 30;
     truncation = Object.isUndefined(truncation) ? '...' : truncation;
     return this.length > length ?
       this.slice(0, length - truncation.length) + truncation : String(this);
-  },
+  }
 
-  strip: function() {
+  function strip() {
     return this.replace(/^\s+/, '').replace(/\s+$/, '');
-  },
+  }
 
-  stripTags: function() {
-    return this.replace(/<\/?[^>]+>/gi, '');
-  },
+  function stripTags() {
+    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
+  }
 
-  stripScripts: function() {
+  function stripScripts() {
     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
-  },
+  }
 
-  extractScripts: function() {
-    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
-    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+  function extractScripts() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
+        matchOne = new RegExp(Prototype.ScriptFragment, 'im');
     return (this.match(matchAll) || []).map(function(scriptTag) {
       return (scriptTag.match(matchOne) || ['', ''])[1];
     });
-  },
+  }
 
-  evalScripts: function() {
+  function evalScripts() {
     return this.extractScripts().map(function(script) { return eval(script) });
-  },
+  }
 
-  escapeHTML: function() {
-    var self = arguments.callee;
-    self.text.data = this;
-    return self.div.innerHTML;
-  },
+  function escapeHTML() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  }
 
-  unescapeHTML: function() {
-    var div = new Element('div');
-    div.innerHTML = this.stripTags();
-    return div.childNodes[0] ? (div.childNodes.length > 1 ?
-      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
-      div.childNodes[0].nodeValue) : '';
-  },
+  function unescapeHTML() {
+    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
+  }
 
-  toQueryParams: function(separator) {
+
+  function toQueryParams(separator) {
     var match = this.strip().match(/([^?#]*)(#.*)?$/);
     if (!match) return { };
 
     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
       if ((pair = pair.split('='))[0]) {
-        var key = decodeURIComponent(pair.shift());
-        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        var key = decodeURIComponent(pair.shift()),
+            value = pair.length > 1 ? pair.join('=') : pair[0];
+
         if (value != undefined) value = decodeURIComponent(value);
 
         if (key in hash) {
@@ -434,128 +640,144 @@
       }
       return hash;
     });
-  },
+  }
 
-  toArray: function() {
+  function toArray() {
     return this.split('');
-  },
+  }
 
-  succ: function() {
+  function succ() {
     return this.slice(0, this.length - 1) +
       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
-  },
+  }
 
-  times: function(count) {
+  function times(count) {
     return count < 1 ? '' : new Array(count + 1).join(this);
-  },
+  }
 
-  camelize: function() {
-    var parts = this.split('-'), len = parts.length;
-    if (len == 1) return parts[0];
+  function camelize() {
+    return this.replace(/-+(.)?/g, function(match, chr) {
+      return chr ? chr.toUpperCase() : '';
+    });
+  }
 
-    var camelized = this.charAt(0) == '-'
-      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
-      : parts[0];
-
-    for (var i = 1; i < len; i++)
-      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
-
-    return camelized;
-  },
-
-  capitalize: function() {
+  function capitalize() {
     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
-  },
+  }
 
-  underscore: function() {
-    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
-  },
+  function underscore() {
+    return this.replace(/::/g, '/')
+               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+               .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+               .replace(/-/g, '_')
+               .toLowerCase();
+  }
 
-  dasherize: function() {
-    return this.gsub(/_/,'-');
-  },
+  function dasherize() {
+    return this.replace(/_/g, '-');
+  }
 
-  inspect: function(useDoubleQuotes) {
-    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
-      var character = String.specialChar[match[0]];
-      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+  function inspect(useDoubleQuotes) {
+    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
+      if (character in String.specialChar) {
+        return String.specialChar[character];
+      }
+      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
     });
     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
-  },
+  }
 
-  toJSON: function() {
-    return this.inspect(true);
-  },
+  function unfilterJSON(filter) {
+    return this.replace(filter || Prototype.JSONFilter, '$1');
+  }
 
-  unfilterJSON: function(filter) {
-    return this.sub(filter || Prototype.JSONFilter, '#{1}');
-  },
-
-  isJSON: function() {
+  function isJSON() {
     var str = this;
     if (str.blank()) return false;
-    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
-    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
-  },
+    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
+    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+    return (/^[\],:{}\s]*$/).test(str);
+  }
 
-  evalJSON: function(sanitize) {
-    var json = this.unfilterJSON();
+  function evalJSON(sanitize) {
+    var json = this.unfilterJSON(),
+        cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+    if (cx.test(json)) {
+      json = json.replace(cx, function (a) {
+        return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+      });
+    }
     try {
       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
     } catch (e) { }
     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
-  },
+  }
 
-  include: function(pattern) {
+  function parseJSON() {
+    var json = this.unfilterJSON();
+    return JSON.parse(json);
+  }
+
+  function include(pattern) {
     return this.indexOf(pattern) > -1;
-  },
+  }
 
-  startsWith: function(pattern) {
-    return this.indexOf(pattern) === 0;
-  },
+  function startsWith(pattern) {
+    return this.lastIndexOf(pattern, 0) === 0;
+  }
 
-  endsWith: function(pattern) {
+  function endsWith(pattern) {
     var d = this.length - pattern.length;
-    return d >= 0 && this.lastIndexOf(pattern) === d;
-  },
+    return d >= 0 && this.indexOf(pattern, d) === d;
+  }
 
-  empty: function() {
+  function empty() {
     return this == '';
-  },
+  }
 
-  blank: function() {
+  function blank() {
     return /^\s*$/.test(this);
-  },
+  }
 
-  interpolate: function(object, pattern) {
+  function interpolate(object, pattern) {
     return new Template(this, pattern).evaluate(object);
   }
-});
 
-if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
-  escapeHTML: function() {
-    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
-  },
-  unescapeHTML: function() {
-    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
-  }
-});
-
-String.prototype.gsub.prepareReplacement = function(replacement) {
-  if (Object.isFunction(replacement)) return replacement;
-  var template = new Template(replacement);
-  return function(match) { return template.evaluate(match) };
-};
-
-String.prototype.parseQuery = String.prototype.toQueryParams;
-
-Object.extend(String.prototype.escapeHTML, {
-  div:  document.createElement('div'),
-  text: document.createTextNode('')
-});
-
-String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
+  return {
+    gsub:           gsub,
+    sub:            sub,
+    scan:           scan,
+    truncate:       truncate,
+    strip:          String.prototype.trim || strip,
+    stripTags:      stripTags,
+    stripScripts:   stripScripts,
+    extractScripts: extractScripts,
+    evalScripts:    evalScripts,
+    escapeHTML:     escapeHTML,
+    unescapeHTML:   unescapeHTML,
+    toQueryParams:  toQueryParams,
+    parseQuery:     toQueryParams,
+    toArray:        toArray,
+    succ:           succ,
+    times:          times,
+    camelize:       camelize,
+    capitalize:     capitalize,
+    underscore:     underscore,
+    dasherize:      dasherize,
+    inspect:        inspect,
+    unfilterJSON:   unfilterJSON,
+    isJSON:         isJSON,
+    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
+    include:        include,
+    startsWith:     startsWith,
+    endsWith:       endsWith,
+    empty:          empty,
+    blank:          blank,
+    interpolate:    interpolate
+  };
+})());
 
 var Template = Class.create({
   initialize: function(template, pattern) {
@@ -564,22 +786,23 @@
   },
 
   evaluate: function(object) {
-    if (Object.isFunction(object.toTemplateReplacements))
+    if (object && Object.isFunction(object.toTemplateReplacements))
       object = object.toTemplateReplacements();
 
     return this.template.gsub(this.pattern, function(match) {
-      if (object == null) return '';
+      if (object == null) return (match[1] + '');
 
       var before = match[1] || '';
       if (before == '\\') return match[2];
 
-      var ctx = object, expr = match[3];
-      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      var ctx = object, expr = match[3],
+          pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+
       match = pattern.exec(expr);
       if (match == null) return before;
 
       while (match != null) {
-        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
         ctx = ctx[comp];
         if (null == ctx || '' == match[3]) break;
         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
@@ -594,8 +817,8 @@
 
 var $break = { };
 
-var Enumerable = {
-  each: function(iterator, context) {
+var Enumerable = (function() {
+  function each(iterator, context) {
     var index = 0;
     try {
       this._each(function(value) {
@@ -605,17 +828,17 @@
       if (e != $break) throw e;
     }
     return this;
-  },
+  }
 
-  eachSlice: function(number, iterator, context) {
+  function eachSlice(number, iterator, context) {
     var index = -number, slices = [], array = this.toArray();
     if (number < 1) return array;
     while ((index += number) < array.length)
       slices.push(array.slice(index, index+number));
     return slices.collect(iterator, context);
-  },
+  }
 
-  all: function(iterator, context) {
+  function all(iterator, context) {
     iterator = iterator || Prototype.K;
     var result = true;
     this.each(function(value, index) {
@@ -623,9 +846,9 @@
       if (!result) throw $break;
     });
     return result;
-  },
+  }
 
-  any: function(iterator, context) {
+  function any(iterator, context) {
     iterator = iterator || Prototype.K;
     var result = false;
     this.each(function(value, index) {
@@ -633,18 +856,18 @@
         throw $break;
     });
     return result;
-  },
+  }
 
-  collect: function(iterator, context) {
+  function collect(iterator, context) {
     iterator = iterator || Prototype.K;
     var results = [];
     this.each(function(value, index) {
       results.push(iterator.call(context, value, index));
     });
     return results;
-  },
+  }
 
-  detect: function(iterator, context) {
+  function detect(iterator, context) {
     var result;
     this.each(function(value, index) {
       if (iterator.call(context, value, index)) {
@@ -653,32 +876,32 @@
       }
     });
     return result;
-  },
+  }
 
-  findAll: function(iterator, context) {
+  function findAll(iterator, context) {
     var results = [];
     this.each(function(value, index) {
       if (iterator.call(context, value, index))
         results.push(value);
     });
     return results;
-  },
+  }
 
-  grep: function(filter, iterator, context) {
+  function grep(filter, iterator, context) {
     iterator = iterator || Prototype.K;
     var results = [];
 
     if (Object.isString(filter))
-      filter = new RegExp(filter);
+      filter = new RegExp(RegExp.escape(filter));
 
     this.each(function(value, index) {
       if (filter.match(value))
         results.push(iterator.call(context, value, index));
     });
     return results;
-  },
+  }
 
-  include: function(object) {
+  function include(object) {
     if (Object.isFunction(this.indexOf))
       if (this.indexOf(object) != -1) return true;
 
@@ -690,31 +913,31 @@
       }
     });
     return found;
-  },
+  }
 
-  inGroupsOf: function(number, fillWith) {
+  function inGroupsOf(number, fillWith) {
     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
     return this.eachSlice(number, function(slice) {
       while(slice.length < number) slice.push(fillWith);
       return slice;
     });
-  },
+  }
 
-  inject: function(memo, iterator, context) {
+  function inject(memo, iterator, context) {
     this.each(function(value, index) {
       memo = iterator.call(context, memo, value, index);
     });
     return memo;
-  },
+  }
 
-  invoke: function(method) {
+  function invoke(method) {
     var args = $A(arguments).slice(1);
     return this.map(function(value) {
       return value[method].apply(value, args);
     });
-  },
+  }
 
-  max: function(iterator, context) {
+  function max(iterator, context) {
     iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
@@ -723,9 +946,9 @@
         result = value;
     });
     return result;
-  },
+  }
 
-  min: function(iterator, context) {
+  function min(iterator, context) {
     iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
@@ -734,9 +957,9 @@
         result = value;
     });
     return result;
-  },
+  }
 
-  partition: function(iterator, context) {
+  function partition(iterator, context) {
     iterator = iterator || Prototype.K;
     var trues = [], falses = [];
     this.each(function(value, index) {
@@ -744,26 +967,26 @@
         trues : falses).push(value);
     });
     return [trues, falses];
-  },
+  }
 
-  pluck: function(property) {
+  function pluck(property) {
     var results = [];
     this.each(function(value) {
       results.push(value[property]);
     });
     return results;
-  },
+  }
 
-  reject: function(iterator, context) {
+  function reject(iterator, context) {
     var results = [];
     this.each(function(value, index) {
       if (!iterator.call(context, value, index))
         results.push(value);
     });
     return results;
-  },
+  }
 
-  sortBy: function(iterator, context) {
+  function sortBy(iterator, context) {
     return this.map(function(value, index) {
       return {
         value: value,
@@ -773,13 +996,13 @@
       var a = left.criteria, b = right.criteria;
       return a < b ? -1 : a > b ? 1 : 0;
     }).pluck('value');
-  },
+  }
 
-  toArray: function() {
+  function toArray() {
     return this.map();
-  },
+  }
 
-  zip: function() {
+  function zip() {
     var iterator = Prototype.K, args = $A(arguments);
     if (Object.isFunction(args.last()))
       iterator = args.pop();
@@ -788,159 +1011,66 @@
     return this.map(function(value, index) {
       return iterator(collections.pluck(index));
     });
-  },
+  }
 
-  size: function() {
+  function size() {
     return this.toArray().length;
-  },
+  }
 
-  inspect: function() {
+  function inspect() {
     return '#<Enumerable:' + this.toArray().inspect() + '>';
   }
-};
 
-Object.extend(Enumerable, {
-  map:     Enumerable.collect,
-  find:    Enumerable.detect,
-  select:  Enumerable.findAll,
-  filter:  Enumerable.findAll,
-  member:  Enumerable.include,
-  entries: Enumerable.toArray,
-  every:   Enumerable.all,
-  some:    Enumerable.any
-});
+
+
+
+
+
+
+
+
+  return {
+    each:       each,
+    eachSlice:  eachSlice,
+    all:        all,
+    every:      all,
+    any:        any,
+    some:       any,
+    collect:    collect,
+    map:        collect,
+    detect:     detect,
+    findAll:    findAll,
+    select:     findAll,
+    filter:     findAll,
+    grep:       grep,
+    include:    include,
+    member:     include,
+    inGroupsOf: inGroupsOf,
+    inject:     inject,
+    invoke:     invoke,
+    max:        max,
+    min:        min,
+    partition:  partition,
+    pluck:      pluck,
+    reject:     reject,
+    sortBy:     sortBy,
+    toArray:    toArray,
+    entries:    toArray,
+    zip:        zip,
+    size:       size,
+    inspect:    inspect,
+    find:       detect
+  };
+})();
+
 function $A(iterable) {
   if (!iterable) return [];
-  if (iterable.toArray) return iterable.toArray();
+  if ('toArray' in Object(iterable)) return iterable.toArray();
   var length = iterable.length || 0, results = new Array(length);
   while (length--) results[length] = iterable[length];
   return results;
 }
 
-if (Prototype.Browser.WebKit) {
-  $A = function(iterable) {
-    if (!iterable) return [];
-    // In Safari, only use the `toArray` method if it's not a NodeList.
-    // A NodeList is a function, has an function `item` property, and a numeric
-    // `length` property. Adapted from Google Doctype.
-    if (!(typeof iterable === 'function' && typeof iterable.length ===
-        'number' && typeof iterable.item === 'function') && iterable.toArray)
-      return iterable.toArray();
-    var length = iterable.length || 0, results = new Array(length);
-    while (length--) results[length] = iterable[length];
-    return results;
-  };
-}
-
-Array.from = $A;
-
-Object.extend(Array.prototype, Enumerable);
-
-if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
-
-Object.extend(Array.prototype, {
-  _each: function(iterator) {
-    for (var i = 0, length = this.length; i < length; i++)
-      iterator(this[i]);
-  },
-
-  clear: function() {
-    this.length = 0;
-    return this;
-  },
-
-  first: function() {
-    return this[0];
-  },
-
-  last: function() {
-    return this[this.length - 1];
-  },
-
-  compact: function() {
-    return this.select(function(value) {
-      return value != null;
-    });
-  },
-
-  flatten: function() {
-    return this.inject([], function(array, value) {
-      return array.concat(Object.isArray(value) ?
-        value.flatten() : [value]);
-    });
-  },
-
-  without: function() {
-    var values = $A(arguments);
-    return this.select(function(value) {
-      return !values.include(value);
-    });
-  },
-
-  reverse: function(inline) {
-    return (inline !== false ? this : this.toArray())._reverse();
-  },
-
-  reduce: function() {
-    return this.length > 1 ? this : this[0];
-  },
-
-  uniq: function(sorted) {
-    return this.inject([], function(array, value, index) {
-      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
-        array.push(value);
-      return array;
-    });
-  },
-
-  intersect: function(array) {
-    return this.uniq().findAll(function(item) {
-      return array.detect(function(value) { return item === value });
-    });
-  },
-
-  clone: function() {
-    return [].concat(this);
-  },
-
-  size: function() {
-    return this.length;
-  },
-
-  inspect: function() {
-    return '[' + this.map(Object.inspect).join(', ') + ']';
-  },
-
-  toJSON: function() {
-    var results = [];
-    this.each(function(object) {
-      var value = Object.toJSON(object);
-      if (!Object.isUndefined(value)) results.push(value);
-    });
-    return '[' + results.join(', ') + ']';
-  }
-});
-
-// use native browser JS 1.6 implementation if available
-if (Object.isFunction(Array.prototype.forEach))
-  Array.prototype._each = Array.prototype.forEach;
-
-if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
-  i || (i = 0);
-  var length = this.length;
-  if (i < 0) i = length + i;
-  for (; i < length; i++)
-    if (this[i] === item) return i;
-  return -1;
-};
-
-if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
-  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
-  var n = this.slice(0, i).reverse().indexOf(item);
-  return (n < 0) ? n : i - n - 1;
-};
-
-Array.prototype.toArray = Array.prototype.clone;
 
 function $w(string) {
   if (!Object.isString(string)) return [];
@@ -948,176 +1078,349 @@
   return string ? string.split(/\s+/) : [];
 }
 
-if (Prototype.Browser.Opera){
-  Array.prototype.concat = function() {
-    var array = [];
-    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+Array.from = $A;
+
+
+(function() {
+  var arrayProto = Array.prototype,
+      slice = arrayProto.slice,
+      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
+
+  function each(iterator, context) {
+    for (var i = 0, length = this.length >>> 0; i < length; i++) {
+      if (i in this) iterator.call(context, this[i], i, this);
+    }
+  }
+  if (!_each) _each = each;
+
+  function clear() {
+    this.length = 0;
+    return this;
+  }
+
+  function first() {
+    return this[0];
+  }
+
+  function last() {
+    return this[this.length - 1];
+  }
+
+  function compact() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  }
+
+  function flatten() {
+    return this.inject([], function(array, value) {
+      if (Object.isArray(value))
+        return array.concat(value.flatten());
+      array.push(value);
+      return array;
+    });
+  }
+
+  function without() {
+    var values = slice.call(arguments, 0);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  }
+
+  function reverse(inline) {
+    return (inline === false ? this.toArray() : this)._reverse();
+  }
+
+  function uniq(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  }
+
+  function intersect(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  }
+
+
+  function clone() {
+    return slice.call(this, 0);
+  }
+
+  function size() {
+    return this.length;
+  }
+
+  function inspect() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+
+  function indexOf(item, i) {
+    i || (i = 0);
+    var length = this.length;
+    if (i < 0) i = length + i;
+    for (; i < length; i++)
+      if (this[i] === item) return i;
+    return -1;
+  }
+
+  function lastIndexOf(item, i) {
+    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+    var n = this.slice(0, i).reverse().indexOf(item);
+    return (n < 0) ? n : i - n - 1;
+  }
+
+  function concat() {
+    var array = slice.call(this, 0), item;
     for (var i = 0, length = arguments.length; i < length; i++) {
-      if (Object.isArray(arguments[i])) {
-        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
-          array.push(arguments[i][j]);
+      item = arguments[i];
+      if (Object.isArray(item) && !('callee' in item)) {
+        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
+          array.push(item[j]);
       } else {
-        array.push(arguments[i]);
+        array.push(item);
       }
     }
     return array;
-  };
-}
-Object.extend(Number.prototype, {
-  toColorPart: function() {
-    return this.toPaddedString(2, 16);
-  },
-
-  succ: function() {
-    return this + 1;
-  },
-
-  times: function(iterator, context) {
-    $R(0, this, true).each(iterator, context);
-    return this;
-  },
-
-  toPaddedString: function(length, radix) {
-    var string = this.toString(radix || 10);
-    return '0'.times(length - string.length) + string;
-  },
-
-  toJSON: function() {
-    return isFinite(this) ? this.toString() : 'null';
   }
-});
 
-$w('abs round ceil floor').each(function(method){
-  Number.prototype[method] = Math[method].methodize();
-});
+  Object.extend(arrayProto, Enumerable);
+
+  if (!arrayProto._reverse)
+    arrayProto._reverse = arrayProto.reverse;
+
+  Object.extend(arrayProto, {
+    _each:     _each,
+    clear:     clear,
+    first:     first,
+    last:      last,
+    compact:   compact,
+    flatten:   flatten,
+    without:   without,
+    reverse:   reverse,
+    uniq:      uniq,
+    intersect: intersect,
+    clone:     clone,
+    toArray:   clone,
+    size:      size,
+    inspect:   inspect
+  });
+
+  var CONCAT_ARGUMENTS_BUGGY = (function() {
+    return [].concat(arguments)[0][0] !== 1;
+  })(1,2)
+
+  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
+
+  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
+  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
+})();
 function $H(object) {
   return new Hash(object);
 };
 
 var Hash = Class.create(Enumerable, (function() {
+  function initialize(object) {
+    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+  }
+
+
+  function _each(iterator) {
+    for (var key in this._object) {
+      var value = this._object[key], pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  }
+
+  function set(key, value) {
+    return this._object[key] = value;
+  }
+
+  function get(key) {
+    if (this._object[key] !== Object.prototype[key])
+      return this._object[key];
+  }
+
+  function unset(key) {
+    var value = this._object[key];
+    delete this._object[key];
+    return value;
+  }
+
+  function toObject() {
+    return Object.clone(this._object);
+  }
+
+
+
+  function keys() {
+    return this.pluck('key');
+  }
+
+  function values() {
+    return this.pluck('value');
+  }
+
+  function index(value) {
+    var match = this.detect(function(pair) {
+      return pair.value === value;
+    });
+    return match && match.key;
+  }
+
+  function merge(object) {
+    return this.clone().update(object);
+  }
+
+  function update(object) {
+    return new Hash(object).inject(this, function(result, pair) {
+      result.set(pair.key, pair.value);
+      return result;
+    });
+  }
 
   function toQueryPair(key, value) {
     if (Object.isUndefined(value)) return key;
     return key + '=' + encodeURIComponent(String.interpret(value));
   }
 
-  return {
-    initialize: function(object) {
-      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
-    },
+  function toQueryString() {
+    return this.inject([], function(results, pair) {
+      var key = encodeURIComponent(pair.key), values = pair.value;
 
-    _each: function(iterator) {
-      for (var key in this._object) {
-        var value = this._object[key], pair = [key, value];
-        pair.key = key;
-        pair.value = value;
-        iterator(pair);
-      }
-    },
-
-    set: function(key, value) {
-      return this._object[key] = value;
-    },
-
-    get: function(key) {
-      // simulating poorly supported hasOwnProperty
-      if (this._object[key] !== Object.prototype[key])
-        return this._object[key];
-    },
-
-    unset: function(key) {
-      var value = this._object[key];
-      delete this._object[key];
-      return value;
-    },
-
-    toObject: function() {
-      return Object.clone(this._object);
-    },
-
-    keys: function() {
-      return this.pluck('key');
-    },
-
-    values: function() {
-      return this.pluck('value');
-    },
-
-    index: function(value) {
-      var match = this.detect(function(pair) {
-        return pair.value === value;
-      });
-      return match && match.key;
-    },
-
-    merge: function(object) {
-      return this.clone().update(object);
-    },
-
-    update: function(object) {
-      return new Hash(object).inject(this, function(result, pair) {
-        result.set(pair.key, pair.value);
-        return result;
-      });
-    },
-
-    toQueryString: function() {
-      return this.inject([], function(results, pair) {
-        var key = encodeURIComponent(pair.key), values = pair.value;
-
-        if (values && typeof values == 'object') {
-          if (Object.isArray(values))
-            return results.concat(values.map(toQueryPair.curry(key)));
-        } else results.push(toQueryPair(key, values));
-        return results;
-      }).join('&');
-    },
-
-    inspect: function() {
-      return '#<Hash:{' + this.map(function(pair) {
-        return pair.map(Object.inspect).join(': ');
-      }).join(', ') + '}>';
-    },
-
-    toJSON: function() {
-      return Object.toJSON(this.toObject());
-    },
-
-    clone: function() {
-      return new Hash(this);
-    }
+      if (values && typeof values == 'object') {
+        if (Object.isArray(values)) {
+          var queryValues = [];
+          for (var i = 0, len = values.length, value; i < len; i++) {
+            value = values[i];
+            queryValues.push(toQueryPair(key, value));
+          }
+          return results.concat(queryValues);
+        }
+      } else results.push(toQueryPair(key, values));
+      return results;
+    }).join('&');
   }
+
+  function inspect() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+
+  function clone() {
+    return new Hash(this);
+  }
+
+  return {
+    initialize:             initialize,
+    _each:                  _each,
+    set:                    set,
+    get:                    get,
+    unset:                  unset,
+    toObject:               toObject,
+    toTemplateReplacements: toObject,
+    keys:                   keys,
+    values:                 values,
+    index:                  index,
+    merge:                  merge,
+    update:                 update,
+    toQueryString:          toQueryString,
+    inspect:                inspect,
+    toJSON:                 toObject,
+    clone:                  clone
+  };
 })());
 
-Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
 Hash.from = $H;
-var ObjectRange = Class.create(Enumerable, {
-  initialize: function(start, end, exclusive) {
+Object.extend(Number.prototype, (function() {
+  function toColorPart() {
+    return this.toPaddedString(2, 16);
+  }
+
+  function succ() {
+    return this + 1;
+  }
+
+  function times(iterator, context) {
+    $R(0, this, true).each(iterator, context);
+    return this;
+  }
+
+  function toPaddedString(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  }
+
+  function abs() {
+    return Math.abs(this);
+  }
+
+  function round() {
+    return Math.round(this);
+  }
+
+  function ceil() {
+    return Math.ceil(this);
+  }
+
+  function floor() {
+    return Math.floor(this);
+  }
+
+  return {
+    toColorPart:    toColorPart,
+    succ:           succ,
+    times:          times,
+    toPaddedString: toPaddedString,
+    abs:            abs,
+    round:          round,
+    ceil:           ceil,
+    floor:          floor
+  };
+})());
+
+function $R(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var ObjectRange = Class.create(Enumerable, (function() {
+  function initialize(start, end, exclusive) {
     this.start = start;
     this.end = end;
     this.exclusive = exclusive;
-  },
+  }
 
-  _each: function(iterator) {
+  function _each(iterator) {
     var value = this.start;
     while (this.include(value)) {
       iterator(value);
       value = value.succ();
     }
-  },
+  }
 
-  include: function(value) {
+  function include(value) {
     if (value < this.start)
       return false;
     if (this.exclusive)
       return value < this.end;
     return value <= this.end;
   }
-});
 
-var $R = function(start, end, exclusive) {
-  return new ObjectRange(start, end, exclusive);
-};
+  return {
+    initialize: initialize,
+    _each:      _each,
+    include:    include
+  };
+})());
+
+
 
 var Ajax = {
   getTransport: function() {
@@ -1164,7 +1467,6 @@
   onCreate:   function() { Ajax.activeRequestCount++ },
   onComplete: function() { Ajax.activeRequestCount-- }
 });
-
 Ajax.Base = Class.create({
   initialize: function(options) {
     this.options = {
@@ -1180,13 +1482,10 @@
 
     this.options.method = this.options.method.toLowerCase();
 
-    if (Object.isString(this.options.parameters))
-      this.options.parameters = this.options.parameters.toQueryParams();
-    else if (Object.isHash(this.options.parameters))
+    if (Object.isHash(this.options.parameters))
       this.options.parameters = this.options.parameters.toObject();
   }
 });
-
 Ajax.Request = Class.create(Ajax.Base, {
   _complete: false,
 
@@ -1199,24 +1498,21 @@
   request: function(url) {
     this.url = url;
     this.method = this.options.method;
-    var params = Object.clone(this.options.parameters);
+    var params = Object.isString(this.options.parameters) ?
+          this.options.parameters :
+          Object.toQueryString(this.options.parameters);
 
     if (!['get', 'post'].include(this.method)) {
-      // simulate other verbs over post
-      params['_method'] = this.method;
+      params += (params ? '&' : '') + "_method=" + this.method;
       this.method = 'post';
     }
 
-    this.parameters = params;
-
-    if (params = Object.toQueryString(params)) {
-      // when GET, append parameters to URL
-      if (this.method == 'get')
-        this.url += (this.url.include('?') ? '&' : '?') + params;
-      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
-        params += '&_=';
+    if (params && this.method === 'get') {
+      this.url += (this.url.include('?') ? '&' : '?') + params;
     }
 
+    this.parameters = params.toQueryParams();
+
     try {
       var response = new Ajax.Response(this);
       if (this.options.onCreate) this.options.onCreate(response);
@@ -1269,7 +1565,6 @@
             headers['Connection'] = 'close';
     }
 
-    // user-defined headers
     if (typeof this.options.requestHeaders == 'object') {
       var extras = this.options.requestHeaders;
 
@@ -1286,11 +1581,12 @@
 
   success: function() {
     var status = this.getStatus();
-    return !status || (status >= 200 && status < 300);
+    return !status || (status >= 200 && status < 300) || status == 304;
   },
 
   getStatus: function() {
     try {
+      if (this.transport.status === 1223) return 204;
       return this.transport.status || 0;
     } catch (e) { return 0 }
   },
@@ -1323,7 +1619,6 @@
     }
 
     if (state == 'Complete') {
-      // avoid memory leak in MSIE: clean up
       this.transport.onreadystatechange = Prototype.emptyFunction;
     }
   },
@@ -1340,7 +1635,7 @@
   getHeader: function(name) {
     try {
       return this.transport.getResponseHeader(name) || null;
-    } catch (e) { return null }
+    } catch (e) { return null; }
   },
 
   evalResponse: function() {
@@ -1360,20 +1655,27 @@
 Ajax.Request.Events =
   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 
+
+
+
+
+
+
+
 Ajax.Response = Class.create({
   initialize: function(request){
     this.request = request;
     var transport  = this.transport  = request.transport,
         readyState = this.readyState = transport.readyState;
 
-    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
       this.status       = this.getStatus();
       this.statusText   = this.getStatusText();
       this.responseText = String.interpret(transport.responseText);
       this.headerJSON   = this._getHeaderJSON();
     }
 
-    if(readyState == 4) {
+    if (readyState == 4) {
       var xml = transport.responseXML;
       this.responseXML  = Object.isUndefined(xml) ? null : xml;
       this.responseJSON = this._getResponseJSON();
@@ -1381,6 +1683,7 @@
   },
 
   status:      0,
+
   statusText: '',
 
   getStatus: Ajax.Request.prototype.getStatus,
@@ -1510,6 +1813,8 @@
     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   }
 });
+
+
 function $(element) {
   if (arguments.length > 1) {
     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
@@ -1534,10 +1839,9 @@
 
 /*--------------------------------------------------------------------------*/
 
-if (!window.Node) var Node = { };
+if (!Node) var Node = { };
 
 if (!Node.ELEMENT_NODE) {
-  // DOM level 2 ECMAScript Language Binding
   Object.extend(Node, {
     ELEMENT_NODE: 1,
     ATTRIBUTE_NODE: 2,
@@ -1554,26 +1858,63 @@
   });
 }
 
-(function() {
-  var element = this.Element;
-  this.Element = function(tagName, attributes) {
+
+
+(function(global) {
+  function shouldUseCache(tagName, attributes) {
+    if (tagName === 'select') return false;
+    if ('type' in attributes) return false;
+    return true;
+  }
+
+  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
+    try {
+      var el = document.createElement('<input name="x">');
+      return el.tagName.toLowerCase() === 'input' && el.name === 'x';
+    }
+    catch(err) {
+      return false;
+    }
+  })();
+
+  var element = global.Element;
+
+  global.Element = function(tagName, attributes) {
     attributes = attributes || { };
     tagName = tagName.toLowerCase();
     var cache = Element.cache;
-    if (Prototype.Browser.IE && attributes.name) {
+
+    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
       tagName = '<' + tagName + ' name="' + attributes.name + '">';
       delete attributes.name;
       return Element.writeAttribute(document.createElement(tagName), attributes);
     }
-    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
-    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
-  };
-  Object.extend(this.Element, element || { });
-  if (element) this.Element.prototype = element.prototype;
-}).call(window);
 
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+
+    var node = shouldUseCache(tagName, attributes) ?
+     cache[tagName].cloneNode(false) : document.createElement(tagName);
+
+    return Element.writeAttribute(node, attributes);
+  };
+
+  Object.extend(global.Element, element || { });
+  if (element) global.Element.prototype = element.prototype;
+
+})(this);
+
+Element.idCounter = 1;
 Element.cache = { };
 
+Element._purgeElement = function(element) {
+  var uid = element._prototypeUID;
+  if (uid) {
+    Element.stopObserving(element);
+    element._prototypeUID = void 0;
+    delete Element.Storage[uid];
+  }
+}
+
 Element.Methods = {
   visible: function(element) {
     return $(element).style.display != 'none';
@@ -1603,15 +1944,116 @@
     return element;
   },
 
-  update: function(element, content) {
-    element = $(element);
-    if (content && content.toElement) content = content.toElement();
-    if (Object.isElement(content)) return element.update().insert(content);
-    content = Object.toHTML(content);
-    element.innerHTML = content.stripScripts();
-    content.evalScripts.bind(content).defer();
-    return element;
-  },
+  update: (function(){
+
+    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
+      var el = document.createElement("select"),
+          isBuggy = true;
+      el.innerHTML = "<option value=\"test\">test</option>";
+      if (el.options && el.options[0]) {
+        isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
+      }
+      el = null;
+      return isBuggy;
+    })();
+
+    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
+      try {
+        var el = document.createElement("table");
+        if (el && el.tBodies) {
+          el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
+          var isBuggy = typeof el.tBodies[0] == "undefined";
+          el = null;
+          return isBuggy;
+        }
+      } catch (e) {
+        return true;
+      }
+    })();
+
+    var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
+      try {
+        var el = document.createElement('div');
+        el.innerHTML = "<link>";
+        var isBuggy = (el.childNodes.length === 0);
+        el = null;
+        return isBuggy;
+      } catch(e) {
+        return true;
+      }
+    })();
+
+    var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
+     TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
+
+    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
+      var s = document.createElement("script"),
+          isBuggy = false;
+      try {
+        s.appendChild(document.createTextNode(""));
+        isBuggy = !s.firstChild ||
+          s.firstChild && s.firstChild.nodeType !== 3;
+      } catch (e) {
+        isBuggy = true;
+      }
+      s = null;
+      return isBuggy;
+    })();
+
+
+    function update(element, content) {
+      element = $(element);
+      var purgeElement = Element._purgeElement;
+
+      var descendants = element.getElementsByTagName('*'),
+       i = descendants.length;
+      while (i--) purgeElement(descendants[i]);
+
+      if (content && content.toElement)
+        content = content.toElement();
+
+      if (Object.isElement(content))
+        return element.update().insert(content);
+
+      content = Object.toHTML(content);
+
+      var tagName = element.tagName.toUpperCase();
+
+      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
+        element.text = content;
+        return element;
+      }
+
+      if (ANY_INNERHTML_BUGGY) {
+        if (tagName in Element._insertionTranslations.tags) {
+          while (element.firstChild) {
+            element.removeChild(element.firstChild);
+          }
+          Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+            .each(function(node) {
+              element.appendChild(node)
+            });
+        } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
+          while (element.firstChild) {
+            element.removeChild(element.firstChild);
+          }
+          var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
+          nodes.each(function(node) { element.appendChild(node) });
+        }
+        else {
+          element.innerHTML = content.stripScripts();
+        }
+      }
+      else {
+        element.innerHTML = content.stripScripts();
+      }
+
+      content.evalScripts.bind(content).defer();
+      return element;
+    }
+
+    return update;
+  })(),
 
   replace: function(element, content) {
     element = $(element);
@@ -1679,28 +2121,35 @@
     element = $(element);
     var result = '<' + element.tagName.toLowerCase();
     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
-      var property = pair.first(), attribute = pair.last();
-      var value = (element[property] || '').toString();
+      var property = pair.first(),
+          attribute = pair.last(),
+          value = (element[property] || '').toString();
       if (value) result += ' ' + attribute + '=' + value.inspect(true);
     });
     return result + '>';
   },
 
-  recursivelyCollect: function(element, property) {
+  recursivelyCollect: function(element, property, maximumLength) {
     element = $(element);
+    maximumLength = maximumLength || -1;
     var elements = [];
-    while (element = element[property])
+
+    while (element = element[property]) {
       if (element.nodeType == 1)
         elements.push(Element.extend(element));
+      if (elements.length == maximumLength)
+        break;
+    }
+
     return elements;
   },
 
   ancestors: function(element) {
-    return $(element).recursivelyCollect('parentNode');
+    return Element.recursivelyCollect(element, 'parentNode');
   },
 
   descendants: function(element) {
-    return $(element).select("*");
+    return Element.select(element, "*");
   },
 
   firstDescendant: function(element) {
@@ -1710,78 +2159,96 @@
   },
 
   immediateDescendants: function(element) {
-    if (!(element = $(element).firstChild)) return [];
-    while (element && element.nodeType != 1) element = element.nextSibling;
-    if (element) return [element].concat($(element).nextSiblings());
-    return [];
+    var results = [], child = $(element).firstChild;
+    while (child) {
+      if (child.nodeType === 1) {
+        results.push(Element.extend(child));
+      }
+      child = child.nextSibling;
+    }
+    return results;
   },
 
-  previousSiblings: function(element) {
-    return $(element).recursivelyCollect('previousSibling');
+  previousSiblings: function(element, maximumLength) {
+    return Element.recursivelyCollect(element, 'previousSibling');
   },
 
   nextSiblings: function(element) {
-    return $(element).recursivelyCollect('nextSibling');
+    return Element.recursivelyCollect(element, 'nextSibling');
   },
 
   siblings: function(element) {
     element = $(element);
-    return element.previousSiblings().reverse().concat(element.nextSiblings());
+    return Element.previousSiblings(element).reverse()
+      .concat(Element.nextSiblings(element));
   },
 
   match: function(element, selector) {
+    element = $(element);
     if (Object.isString(selector))
-      selector = new Selector(selector);
-    return selector.match($(element));
+      return Prototype.Selector.match(element, selector);
+    return selector.match(element);
   },
 
   up: function(element, expression, index) {
     element = $(element);
     if (arguments.length == 1) return $(element.parentNode);
-    var ancestors = element.ancestors();
+    var ancestors = Element.ancestors(element);
     return Object.isNumber(expression) ? ancestors[expression] :
-      Selector.findElement(ancestors, expression, index);
+      Prototype.Selector.find(ancestors, expression, index);
   },
 
   down: function(element, expression, index) {
     element = $(element);
-    if (arguments.length == 1) return element.firstDescendant();
-    return Object.isNumber(expression) ? element.descendants()[expression] :
+    if (arguments.length == 1) return Element.firstDescendant(element);
+    return Object.isNumber(expression) ? Element.descendants(element)[expression] :
       Element.select(element, expression)[index || 0];
   },
 
   previous: function(element, expression, index) {
     element = $(element);
-    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
-    var previousSiblings = element.previousSiblings();
-    return Object.isNumber(expression) ? previousSiblings[expression] :
-      Selector.findElement(previousSiblings, expression, index);
+    if (Object.isNumber(expression)) index = expression, expression = false;
+    if (!Object.isNumber(index)) index = 0;
+
+    if (expression) {
+      return Prototype.Selector.find(element.previousSiblings(), expression, index);
+    } else {
+      return element.recursivelyCollect("previousSibling", index + 1)[index];
+    }
   },
 
   next: function(element, expression, index) {
     element = $(element);
-    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
-    var nextSiblings = element.nextSiblings();
-    return Object.isNumber(expression) ? nextSiblings[expression] :
-      Selector.findElement(nextSiblings, expression, index);
+    if (Object.isNumber(expression)) index = expression, expression = false;
+    if (!Object.isNumber(index)) index = 0;
+
+    if (expression) {
+      return Prototype.Selector.find(element.nextSiblings(), expression, index);
+    } else {
+      var maximumLength = Object.isNumber(index) ? index + 1 : 1;
+      return element.recursivelyCollect("nextSibling", index + 1)[index];
+    }
   },
 
-  select: function() {
-    var args = $A(arguments), element = $(args.shift());
-    return Selector.findChildElements(element, args);
+
+  select: function(element) {
+    element = $(element);
+    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
+    return Prototype.Selector.select(expressions, element);
   },
 
-  adjacent: function() {
-    var args = $A(arguments), element = $(args.shift());
-    return Selector.findChildElements(element.parentNode, args).without(element);
+  adjacent: function(element) {
+    element = $(element);
+    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
+    return Prototype.Selector.select(expressions, element.parentNode).without(element);
   },
 
   identify: function(element) {
     element = $(element);
-    var id = element.readAttribute('id'), self = arguments.callee;
+    var id = Element.readAttribute(element, 'id');
     if (id) return id;
-    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
-    element.writeAttribute('id', id);
+    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
+    Element.writeAttribute(element, 'id', id);
     return id;
   },
 
@@ -1820,11 +2287,11 @@
   },
 
   getHeight: function(element) {
-    return $(element).getDimensions().height;
+    return Element.getDimensions(element).height;
   },
 
   getWidth: function(element) {
-    return $(element).getDimensions().width;
+    return Element.getDimensions(element).width;
   },
 
   classNames: function(element) {
@@ -1840,7 +2307,7 @@
 
   addClassName: function(element, className) {
     if (!(element = $(element))) return;
-    if (!element.hasClassName(className))
+    if (!Element.hasClassName(element, className))
       element.className += (element.className ? ' ' : '') + className;
     return element;
   },
@@ -1854,11 +2321,10 @@
 
   toggleClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return element[element.hasClassName(className) ?
-      'removeClassName' : 'addClassName'](className);
+    return Element[Element.hasClassName(element, className) ?
+      'removeClassName' : 'addClassName'](element, className);
   },
 
-  // removes whitespace-only text node children
   cleanWhitespace: function(element) {
     element = $(element);
     var node = element.firstChild;
@@ -1892,7 +2358,7 @@
 
   scrollTo: function(element) {
     element = $(element);
-    var pos = element.cumulativeOffset();
+    var pos = Element.cumulativeOffset(element);
     window.scrollTo(pos[0], pos[1]);
     return element;
   },
@@ -1938,37 +2404,12 @@
     return element;
   },
 
-  getDimensions: function(element) {
-    element = $(element);
-    var display = element.getStyle('display');
-    if (display != 'none' && display != null) // Safari bug
-      return {width: element.offsetWidth, height: element.offsetHeight};
-
-    // All *Width and *Height properties give 0 on elements with display none,
-    // so enable the element temporarily
-    var els = element.style;
-    var originalVisibility = els.visibility;
-    var originalPosition = els.position;
-    var originalDisplay = els.display;
-    els.visibility = 'hidden';
-    els.position = 'absolute';
-    els.display = 'block';
-    var originalWidth = element.clientWidth;
-    var originalHeight = element.clientHeight;
-    els.display = originalDisplay;
-    els.position = originalPosition;
-    els.visibility = originalVisibility;
-    return {width: originalWidth, height: originalHeight};
-  },
-
   makePositioned: function(element) {
     element = $(element);
     var pos = Element.getStyle(element, 'position');
     if (pos == 'static' || !pos) {
       element._madePositioned = true;
       element.style.position = 'relative';
-      // Opera returns the offset relative to the positioning context, when an
-      // element is position relative but top and left have not been defined
       if (Prototype.Browser.Opera) {
         element.style.top = 0;
         element.style.left = 0;
@@ -2007,117 +2448,6 @@
     return element;
   },
 
-  cumulativeOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-    } while (element);
-    return Element._returnOffset(valueL, valueT);
-  },
-
-  positionedOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-      if (element) {
-        if (element.tagName.toUpperCase() == 'BODY') break;
-        var p = Element.getStyle(element, 'position');
-        if (p !== 'static') break;
-      }
-    } while (element);
-    return Element._returnOffset(valueL, valueT);
-  },
-
-  absolutize: function(element) {
-    element = $(element);
-    if (element.getStyle('position') == 'absolute') return element;
-    // Position.prepare(); // To be done manually by Scripty when it needs it.
-
-    var offsets = element.positionedOffset();
-    var top     = offsets[1];
-    var left    = offsets[0];
-    var width   = element.clientWidth;
-    var height  = element.clientHeight;
-
-    element._originalLeft   = left - parseFloat(element.style.left  || 0);
-    element._originalTop    = top  - parseFloat(element.style.top || 0);
-    element._originalWidth  = element.style.width;
-    element._originalHeight = element.style.height;
-
-    element.style.position = 'absolute';
-    element.style.top    = top + 'px';
-    element.style.left   = left + 'px';
-    element.style.width  = width + 'px';
-    element.style.height = height + 'px';
-    return element;
-  },
-
-  relativize: function(element) {
-    element = $(element);
-    if (element.getStyle('position') == 'relative') return element;
-    // Position.prepare(); // To be done manually by Scripty when it needs it.
-
-    element.style.position = 'relative';
-    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
-    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
-
-    element.style.top    = top + 'px';
-    element.style.left   = left + 'px';
-    element.style.height = element._originalHeight;
-    element.style.width  = element._originalWidth;
-    return element;
-  },
-
-  cumulativeScrollOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.scrollTop  || 0;
-      valueL += element.scrollLeft || 0;
-      element = element.parentNode;
-    } while (element);
-    return Element._returnOffset(valueL, valueT);
-  },
-
-  getOffsetParent: function(element) {
-    if (element.offsetParent) return $(element.offsetParent);
-    if (element == document.body) return $(element);
-
-    while ((element = element.parentNode) && element != document.body)
-      if (Element.getStyle(element, 'position') != 'static')
-        return $(element);
-
-    return $(document.body);
-  },
-
-  viewportOffset: function(forElement) {
-    var valueT = 0, valueL = 0;
-
-    var element = forElement;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-
-      // Safari fix
-      if (element.offsetParent == document.body &&
-        Element.getStyle(element, 'position') == 'absolute') break;
-
-    } while (element = element.offsetParent);
-
-    element = forElement;
-    do {
-      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
-        valueT -= element.scrollTop  || 0;
-        valueL -= element.scrollLeft || 0;
-      }
-    } while (element = element.parentNode);
-
-    return Element._returnOffset(valueL, valueT);
-  },
-
   clonePosition: function(element, source) {
     var options = Object.extend({
       setLeft:    true,
@@ -2128,28 +2458,21 @@
       offsetLeft: 0
     }, arguments[2] || { });
 
-    // find page position of source
     source = $(source);
-    var p = source.viewportOffset();
+    var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
 
-    // find coordinate system to use
     element = $(element);
-    var delta = [0, 0];
-    var parent = null;
-    // delta [0,0] will do fine with position: fixed elements,
-    // position:absolute needs offsetParent deltas
+
     if (Element.getStyle(element, 'position') == 'absolute') {
-      parent = element.getOffsetParent();
-      delta = parent.viewportOffset();
+      parent = Element.getOffsetParent(element);
+      delta = Element.viewportOffset(parent);
     }
 
-    // correct by body offsets (fixes Safari)
     if (parent == document.body) {
       delta[0] -= document.body.offsetLeft;
       delta[1] -= document.body.offsetTop;
     }
 
-    // set position
     if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
     if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
     if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
@@ -2158,10 +2481,9 @@
   }
 };
 
-Element.Methods.identify.counter = 1;
-
 Object.extend(Element.Methods, {
   getElementsBySelector: Element.Methods.select,
+
   childElements: Element.Methods.immediateDescendants
 });
 
@@ -2179,14 +2501,9 @@
   Element.Methods.getStyle = Element.Methods.getStyle.wrap(
     function(proceed, element, style) {
       switch (style) {
-        case 'left': case 'top': case 'right': case 'bottom':
-          if (proceed(element, 'position') === 'static') return null;
         case 'height': case 'width':
-          // returns '0px' for hidden elements; we want it to return null
           if (!Element.visible(element)) return null;
 
-          // returns the border-box dimensions rather than the content-box
-          // dimensions, so we subtract padding and borders from the value
           var dim = parseInt(proceed(element, style), 10);
 
           if (dim !== element['offset' + style.capitalize()])
@@ -2219,52 +2536,6 @@
 }
 
 else if (Prototype.Browser.IE) {
-  // IE doesn't report offsets correctly for static elements, so we change them
-  // to "relative" to get the values, then change them back.
-  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
-    function(proceed, element) {
-      element = $(element);
-      // IE throws an error if element is not in document
-      try { element.offsetParent }
-      catch(e) { return $(document.body) }
-      var position = element.getStyle('position');
-      if (position !== 'static') return proceed(element);
-      element.setStyle({ position: 'relative' });
-      var value = proceed(element);
-      element.setStyle({ position: position });
-      return value;
-    }
-  );
-
-  $w('positionedOffset viewportOffset').each(function(method) {
-    Element.Methods[method] = Element.Methods[method].wrap(
-      function(proceed, element) {
-        element = $(element);
-        try { element.offsetParent }
-        catch(e) { return Element._returnOffset(0,0) }
-        var position = element.getStyle('position');
-        if (position !== 'static') return proceed(element);
-        // Trigger hasLayout on the offset parent so that IE6 reports
-        // accurate offsetTop and offsetLeft values for position: fixed.
-        var offsetParent = element.getOffsetParent();
-        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
-          offsetParent.setStyle({ zoom: 1 });
-        element.setStyle({ position: 'relative' });
-        var value = proceed(element);
-        element.setStyle({ position: position });
-        return value;
-      }
-    );
-  });
-
-  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
-    function(proceed, element) {
-      try { element.offsetParent }
-      catch(e) { return Element._returnOffset(0,0) }
-      return proceed(element);
-    }
-  );
-
   Element.Methods.getStyle = function(element, style) {
     element = $(element);
     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
@@ -2306,36 +2577,90 @@
     return element;
   };
 
-  Element._attributeTranslations = {
-    read: {
-      names: {
-        'class': 'className',
-        'for':   'htmlFor'
-      },
-      values: {
-        _getAttr: function(element, attribute) {
-          return element.getAttribute(attribute, 2);
+  Element._attributeTranslations = (function(){
+
+    var classProp = 'className',
+        forProp = 'for',
+        el = document.createElement('div');
+
+    el.setAttribute(classProp, 'x');
+
+    if (el.className !== 'x') {
+      el.setAttribute('class', 'x');
+      if (el.className === 'x') {
+        classProp = 'class';
+      }
+    }
+    el = null;
+
+    el = document.createElement('label');
+    el.setAttribute(forProp, 'x');
+    if (el.htmlFor !== 'x') {
+      el.setAttribute('htmlFor', 'x');
+      if (el.htmlFor === 'x') {
+        forProp = 'htmlFor';
+      }
+    }
+    el = null;
+
+    return {
+      read: {
+        names: {
+          'class':      classProp,
+          'className':  classProp,
+          'for':        forProp,
+          'htmlFor':    forProp
         },
-        _getAttrNode: function(element, attribute) {
-          var node = element.getAttributeNode(attribute);
-          return node ? node.value : "";
-        },
-        _getEv: function(element, attribute) {
-          attribute = element.getAttribute(attribute);
-          return attribute ? attribute.toString().slice(23, -2) : null;
-        },
-        _flag: function(element, attribute) {
-          return $(element).hasAttribute(attribute) ? attribute : null;
-        },
-        style: function(element) {
-          return element.style.cssText.toLowerCase();
-        },
-        title: function(element) {
-          return element.title;
+        values: {
+          _getAttr: function(element, attribute) {
+            return element.getAttribute(attribute);
+          },
+          _getAttr2: function(element, attribute) {
+            return element.getAttribute(attribute, 2);
+          },
+          _getAttrNode: function(element, attribute) {
+            var node = element.getAttributeNode(attribute);
+            return node ? node.value : "";
+          },
+          _getEv: (function(){
+
+            var el = document.createElement('div'), f;
+            el.onclick = Prototype.emptyFunction;
+            var value = el.getAttribute('onclick');
+
+            if (String(value).indexOf('{') > -1) {
+              f = function(element, attribute) {
+                attribute = element.getAttribute(attribute);
+                if (!attribute) return null;
+                attribute = attribute.toString();
+                attribute = attribute.split('{')[1];
+                attribute = attribute.split('}')[0];
+                return attribute.strip();
+              };
+            }
+            else if (value === '') {
+              f = function(element, attribute) {
+                attribute = element.getAttribute(attribute);
+                if (!attribute) return null;
+                return attribute.strip();
+              };
+            }
+            el = null;
+            return f;
+          })(),
+          _flag: function(element, attribute) {
+            return $(element).hasAttribute(attribute) ? attribute : null;
+          },
+          style: function(element) {
+            return element.style.cssText.toLowerCase();
+          },
+          title: function(element) {
+            return element.title;
+          }
         }
       }
     }
-  };
+  })();
 
   Element._attributeTranslations.write = {
     names: Object.extend({
@@ -2363,8 +2688,8 @@
 
   (function(v) {
     Object.extend(v, {
-      href:        v._getAttr,
-      src:         v._getAttr,
+      href:        v._getAttr2,
+      src:         v._getAttr2,
       type:        v._getAttr,
       action:      v._getAttrNode,
       disabled:    v._flag,
@@ -2391,6 +2716,26 @@
       onchange:    v._getEv
     });
   })(Element._attributeTranslations.read.values);
+
+  if (Prototype.BrowserFeatures.ElementExtensions) {
+    (function() {
+      function _descendants(element) {
+        var nodes = element.getElementsByTagName('*'), results = [];
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName !== "!") // Filter out comment nodes.
+            results.push(node);
+        return results;
+      }
+
+      Element.Methods.down = function(element, expression, index) {
+        element = $(element);
+        if (arguments.length == 1) return element.firstDescendant();
+        return Object.isNumber(expression) ? _descendants(element)[expression] :
+          Element.select(element, expression)[index || 0];
+      }
+    })();
+  }
+
 }
 
 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
@@ -2409,7 +2754,7 @@
       (value < 0.00001) ? 0 : value;
 
     if (value == 1)
-      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
+      if (element.tagName.toUpperCase() == 'IMG' && element.width) {
         element.width++; element.width--;
       } else try {
         var n = document.createTextNode(' ');
@@ -2419,49 +2764,9 @@
 
     return element;
   };
-
-  // Safari returns margins on body which is incorrect if the child is absolutely
-  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
-  // KHTML/WebKit only.
-  Element.Methods.cumulativeOffset = function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      if (element.offsetParent == document.body)
-        if (Element.getStyle(element, 'position') == 'absolute') break;
-
-      element = element.offsetParent;
-    } while (element);
-
-    return Element._returnOffset(valueL, valueT);
-  };
 }
 
-if (Prototype.Browser.IE || Prototype.Browser.Opera) {
-  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
-  Element.Methods.update = function(element, content) {
-    element = $(element);
-
-    if (content && content.toElement) content = content.toElement();
-    if (Object.isElement(content)) return element.update().insert(content);
-
-    content = Object.toHTML(content);
-    var tagName = element.tagName.toUpperCase();
-
-    if (tagName in Element._insertionTranslations.tags) {
-      $A(element.childNodes).each(function(node) { element.removeChild(node) });
-      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
-        .each(function(node) { element.appendChild(node) });
-    }
-    else element.innerHTML = content.stripScripts();
-
-    content.evalScripts.bind(content).defer();
-    return element;
-  };
-}
-
-if ('outerHTML' in document.createElement('div')) {
+if ('outerHTML' in document.documentElement) {
   Element.Methods.replace = function(element, content) {
     element = $(element);
 
@@ -2475,8 +2780,8 @@
     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
 
     if (Element._insertionTranslations.tags[tagName]) {
-      var nextSibling = element.next();
-      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      var nextSibling = element.next(),
+          fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
       parent.removeChild(element);
       if (nextSibling)
         fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
@@ -2497,12 +2802,27 @@
   return result;
 };
 
-Element._getContentFromAnonymousElement = function(tagName, html) {
-  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
-  if (t) {
-    div.innerHTML = t[0] + html + t[1];
-    t[2].times(function() { div = div.firstChild });
-  } else div.innerHTML = html;
+Element._getContentFromAnonymousElement = function(tagName, html, force) {
+  var div = new Element('div'),
+      t = Element._insertionTranslations.tags[tagName];
+
+  var workaround = false;
+  if (t) workaround = true;
+  else if (force) {
+    workaround = true;
+    t = ['', '', 0];
+  }
+
+  if (workaround) {
+    div.innerHTML = '&nbsp;' + t[0] + html + t[1];
+    div.removeChild(div.firstChild);
+    for (var i = t[2]; i--; ) {
+      div = div.firstChild;
+    }
+  }
+  else {
+    div.innerHTML = html;
+  }
   return $A(div.childNodes);
 };
 
@@ -2529,12 +2849,13 @@
 };
 
 (function() {
-  Object.extend(this.tags, {
-    THEAD: this.tags.TBODY,
-    TFOOT: this.tags.TBODY,
-    TH:    this.tags.TD
+  var tags = Element._insertionTranslations.tags;
+  Object.extend(tags, {
+    THEAD: tags.TBODY,
+    TFOOT: tags.TBODY,
+    TH:    tags.TD
   });
-}).call(Element._insertionTranslations);
+})();
 
 Element.Methods.Simulated = {
   hasAttribute: function(element, attribute) {
@@ -2548,41 +2869,81 @@
 
 Object.extend(Element, Element.Methods);
 
-if (!Prototype.BrowserFeatures.ElementExtensions &&
-    document.createElement('div')['__proto__']) {
-  window.HTMLElement = { };
-  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
-  Prototype.BrowserFeatures.ElementExtensions = true;
-}
+(function(div) {
+
+  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
+    window.HTMLElement = { };
+    window.HTMLElement.prototype = div['__proto__'];
+    Prototype.BrowserFeatures.ElementExtensions = true;
+  }
+
+  div = null;
+
+})(document.createElement('div'));
 
 Element.extend = (function() {
-  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+
+  function checkDeficiency(tagName) {
+    if (typeof window.Element != 'undefined') {
+      var proto = window.Element.prototype;
+      if (proto) {
+        var id = '_' + (Math.random()+'').slice(2),
+            el = document.createElement(tagName);
+        proto[id] = 'x';
+        var isBuggy = (el[id] !== 'x');
+        delete proto[id];
+        el = null;
+        return isBuggy;
+      }
+    }
+    return false;
+  }
+
+  function extendElementWith(element, methods) {
+    for (var property in methods) {
+      var value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+  }
+
+  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
+
+  if (Prototype.BrowserFeatures.SpecificElementExtensions) {
+    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
+      return function(element) {
+        if (element && typeof element._extendedByPrototype == 'undefined') {
+          var t = element.tagName;
+          if (t && (/^(?:object|applet|embed)$/i.test(t))) {
+            extendElementWith(element, Element.Methods);
+            extendElementWith(element, Element.Methods.Simulated);
+            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
+          }
+        }
+        return element;
+      }
+    }
     return Prototype.K;
+  }
 
   var Methods = { }, ByTag = Element.Methods.ByTag;
 
   var extend = Object.extend(function(element) {
-    if (!element || element._extendedByPrototype ||
+    if (!element || typeof element._extendedByPrototype != 'undefined' ||
         element.nodeType != 1 || element == window) return element;
 
     var methods = Object.clone(Methods),
-      tagName = element.tagName.toUpperCase(), property, value;
+        tagName = element.tagName.toUpperCase();
 
-    // extend methods for specific tags
     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
 
-    for (property in methods) {
-      value = methods[property];
-      if (Object.isFunction(value) && !(property in element))
-        element[property] = value.methodize();
-    }
+    extendElementWith(element, methods);
 
     element._extendedByPrototype = Prototype.emptyFunction;
     return element;
 
   }, {
     refresh: function() {
-      // extend methods for all tags (Safari doesn't need this)
       if (!Prototype.BrowserFeatures.ElementExtensions) {
         Object.extend(Methods, Element.Methods);
         Object.extend(Methods, Element.Methods.Simulated);
@@ -2594,10 +2955,14 @@
   return extend;
 })();
 
-Element.hasAttribute = function(element, attribute) {
-  if (element.hasAttribute) return element.hasAttribute(attribute);
-  return Element.Methods.Simulated.hasAttribute(element, attribute);
-};
+if (document.documentElement.hasAttribute) {
+  Element.hasAttribute = function(element, attribute) {
+    return element.hasAttribute(attribute);
+  };
+}
+else {
+  Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
+}
 
 Element.addMethods = function(methods) {
   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
@@ -2609,7 +2974,8 @@
       "FORM":     Object.clone(Form.Methods),
       "INPUT":    Object.clone(Form.Element.Methods),
       "SELECT":   Object.clone(Form.Element.Methods),
-      "TEXTAREA": Object.clone(Form.Element.Methods)
+      "TEXTAREA": Object.clone(Form.Element.Methods),
+      "BUTTON":   Object.clone(Form.Element.Methods)
     });
   }
 
@@ -2661,14 +3027,19 @@
     klass = 'HTML' + tagName.capitalize() + 'Element';
     if (window[klass]) return window[klass];
 
-    window[klass] = { };
-    window[klass].prototype = document.createElement(tagName)['__proto__'];
-    return window[klass];
+    var element = document.createElement(tagName),
+        proto = element['__proto__'] || element.constructor.prototype;
+
+    element = null;
+    return proto;
   }
 
+  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
+   Element.prototype;
+
   if (F.ElementExtensions) {
-    copy(Element.Methods, HTMLElement.prototype);
-    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+    copy(Element.Methods, elementPrototype);
+    copy(Element.Methods.Simulated, elementPrototype, true);
   }
 
   if (F.SpecificElementExtensions) {
@@ -2686,791 +3057,1947 @@
   Element.cache = { };
 };
 
+
 document.viewport = {
+
   getDimensions: function() {
-    var dimensions = { }, B = Prototype.Browser;
-    $w('width height').each(function(d) {
-      var D = d.capitalize();
-      if (B.WebKit && !document.evaluate) {
-        // Safari <3.0 needs self.innerWidth/Height
-        dimensions[d] = self['inner' + D];
-      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
-        // Opera <9.5 needs document.body.clientWidth/Height
-        dimensions[d] = document.body['client' + D]
-      } else {
-        dimensions[d] = document.documentElement['client' + D];
-      }
-    });
-    return dimensions;
-  },
-
-  getWidth: function() {
-    return this.getDimensions().width;
-  },
-
-  getHeight: function() {
-    return this.getDimensions().height;
+    return { width: this.getWidth(), height: this.getHeight() };
   },
 
   getScrollOffsets: function() {
     return Element._returnOffset(
       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
-      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
   }
 };
-/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
- * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
- * license.  Please see http://www.yui-ext.com/ for more information. */
 
-var Selector = Class.create({
-  initialize: function(expression) {
-    this.expression = expression.strip();
+(function(viewport) {
+  var B = Prototype.Browser, doc = document, element, property = {};
 
-    if (this.shouldUseSelectorsAPI()) {
-      this.mode = 'selectorsAPI';
-    } else if (this.shouldUseXPath()) {
-      this.mode = 'xpath';
-      this.compileXPathMatcher();
+  function getRootElement() {
+    if (B.WebKit && !doc.evaluate)
+      return document;
+
+    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
+      return document.body;
+
+    return document.documentElement;
+  }
+
+  function define(D) {
+    if (!element) element = getRootElement();
+
+    property[D] = 'client' + D;
+
+    viewport['get' + D] = function() { return element[property[D]] };
+    return viewport['get' + D]();
+  }
+
+  viewport.getWidth  = define.curry('Width');
+
+  viewport.getHeight = define.curry('Height');
+})(document.viewport);
+
+
+Element.Storage = {
+  UID: 1
+};
+
+Element.addMethods({
+  getStorage: function(element) {
+    if (!(element = $(element))) return;
+
+    var uid;
+    if (element === window) {
+      uid = 0;
     } else {
-      this.mode = "normal";
-      this.compileMatcher();
+      if (typeof element._prototypeUID === "undefined")
+        element._prototypeUID = Element.Storage.UID++;
+      uid = element._prototypeUID;
     }
 
+    if (!Element.Storage[uid])
+      Element.Storage[uid] = $H();
+
+    return Element.Storage[uid];
   },
 
-  shouldUseXPath: function() {
-    if (!Prototype.BrowserFeatures.XPath) return false;
+  store: function(element, key, value) {
+    if (!(element = $(element))) return;
 
-    var e = this.expression;
-
-    // Safari 3 chokes on :*-of-type and :empty
-    if (Prototype.Browser.WebKit &&
-     (e.include("-of-type") || e.include(":empty")))
-      return false;
-
-    // XPath can't do namespaced attributes, nor can it read
-    // the "checked" property from DOM nodes
-    if ((/(\[[\w-]*?:|:checked)/).test(e))
-      return false;
-
-    return true;
-  },
-
-  shouldUseSelectorsAPI: function() {
-    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
-
-    if (!Selector._div) Selector._div = new Element('div');
-
-    // Make sure the browser treats the selector as valid. Test on an
-    // isolated element to minimize cost of this check.
-    try {
-      Selector._div.querySelector(this.expression);
-    } catch(e) {
-      return false;
+    if (arguments.length === 2) {
+      Element.getStorage(element).update(key);
+    } else {
+      Element.getStorage(element).set(key, value);
     }
 
-    return true;
+    return element;
   },
 
-  compileMatcher: function() {
-    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
-        c = Selector.criteria, le, p, m;
+  retrieve: function(element, key, defaultValue) {
+    if (!(element = $(element))) return;
+    var hash = Element.getStorage(element), value = hash.get(key);
 
-    if (Selector._cache[e]) {
-      this.matcher = Selector._cache[e];
-      return;
+    if (Object.isUndefined(value)) {
+      hash.set(key, defaultValue);
+      value = defaultValue;
     }
 
-    this.matcher = ["this.matcher = function(root) {",
-                    "var r = root, h = Selector.handlers, c = false, n;"];
+    return value;
+  },
 
-    while (e && le != e && (/\S/).test(e)) {
-      le = e;
-      for (var i in ps) {
-        p = ps[i];
-        if (m = e.match(p)) {
-          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
-            new Template(c[i]).evaluate(m));
-          e = e.replace(m[0], '');
-          break;
-        }
+  clone: function(element, deep) {
+    if (!(element = $(element))) return;
+    var clone = element.cloneNode(deep);
+    clone._prototypeUID = void 0;
+    if (deep) {
+      var descendants = Element.select(clone, '*'),
+          i = descendants.length;
+      while (i--) {
+        descendants[i]._prototypeUID = void 0;
       }
     }
-
-    this.matcher.push("return h.unique(n);\n}");
-    eval(this.matcher.join('\n'));
-    Selector._cache[this.expression] = this.matcher;
+    return Element.extend(clone);
   },
 
-  compileXPathMatcher: function() {
-    var e = this.expression, ps = Selector.patterns,
-        x = Selector.xpath, le, m;
+  purge: function(element) {
+    if (!(element = $(element))) return;
+    var purgeElement = Element._purgeElement;
 
-    if (Selector._cache[e]) {
-      this.xpath = Selector._cache[e]; return;
-    }
+    purgeElement(element);
 
-    this.matcher = ['.//*'];
-    while (e && le != e && (/\S/).test(e)) {
-      le = e;
-      for (var i in ps) {
-        if (m = e.match(ps[i])) {
-          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
-            new Template(x[i]).evaluate(m));
-          e = e.replace(m[0], '');
-          break;
-        }
-      }
-    }
+    var descendants = element.getElementsByTagName('*'),
+     i = descendants.length;
 
-    this.xpath = this.matcher.join('');
-    Selector._cache[this.expression] = this.xpath;
-  },
+    while (i--) purgeElement(descendants[i]);
 
-  findElements: function(root) {
-    root = root || document;
-    var e = this.expression, results;
-
-    switch (this.mode) {
-      case 'selectorsAPI':
-        // querySelectorAll queries document-wide, then filters to descendants
-        // of the context element. That's not what we want.
-        // Add an explicit context to the selector if necessary.
-        if (root !== document) {
-          var oldId = root.id, id = $(root).identify();
-          e = "#" + id + " " + e;
-        }
-
-        results = $A(root.querySelectorAll(e)).map(Element.extend);
-        root.id = oldId;
-
-        return results;
-      case 'xpath':
-        return document._getElementsByXPath(this.xpath, root);
-      default:
-       return this.matcher(root);
-    }
-  },
-
-  match: function(element) {
-    this.tokens = [];
-
-    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
-    var le, p, m;
-
-    while (e && le !== e && (/\S/).test(e)) {
-      le = e;
-      for (var i in ps) {
-        p = ps[i];
-        if (m = e.match(p)) {
-          // use the Selector.assertions methods unless the selector
-          // is too complex.
-          if (as[i]) {
-            this.tokens.push([i, Object.clone(m)]);
-            e = e.replace(m[0], '');
-          } else {
-            // reluctantly do a document-wide search
-            // and look for a match in the array
-            return this.findElements(document).include(element);
-          }
-        }
-      }
-    }
-
-    var match = true, name, matches;
-    for (var i = 0, token; token = this.tokens[i]; i++) {
-      name = token[0], matches = token[1];
-      if (!Selector.assertions[name](element, matches)) {
-        match = false; break;
-      }
-    }
-
-    return match;
-  },
-
-  toString: function() {
-    return this.expression;
-  },
-
-  inspect: function() {
-    return "#<Selector:" + this.expression.inspect() + ">";
+    return null;
   }
 });
 
-Object.extend(Selector, {
-  _cache: { },
+(function() {
 
-  xpath: {
-    descendant:   "//*",
-    child:        "/*",
-    adjacent:     "/following-sibling::*[1]",
-    laterSibling: '/following-sibling::*',
-    tagName:      function(m) {
-      if (m[1] == '*') return '';
-      return "[local-name()='" + m[1].toLowerCase() +
-             "' or local-name()='" + m[1].toUpperCase() + "']";
-    },
-    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
-    id:           "[@id='#{1}']",
-    attrPresence: function(m) {
-      m[1] = m[1].toLowerCase();
-      return new Template("[@#{1}]").evaluate(m);
-    },
-    attr: function(m) {
-      m[1] = m[1].toLowerCase();
-      m[3] = m[5] || m[6];
-      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
-    },
-    pseudo: function(m) {
-      var h = Selector.xpath.pseudos[m[1]];
-      if (!h) return '';
-      if (Object.isFunction(h)) return h(m);
-      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
-    },
-    operators: {
-      '=':  "[@#{1}='#{3}']",
-      '!=': "[@#{1}!='#{3}']",
-      '^=': "[starts-with(@#{1}, '#{3}')]",
-      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
-      '*=': "[contains(@#{1}, '#{3}')]",
-      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
-      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
-    },
-    pseudos: {
-      'first-child': '[not(preceding-sibling::*)]',
-      'last-child':  '[not(following-sibling::*)]',
-      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
-      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
-      'checked':     "[@checked]",
-      'disabled':    "[(@disabled) and (@type!='hidden')]",
-      'enabled':     "[not(@disabled) and (@type!='hidden')]",
-      'not': function(m) {
-        var e = m[6], p = Selector.patterns,
-            x = Selector.xpath, le, v;
+  function toDecimal(pctString) {
+    var match = pctString.match(/^(\d+)%?$/i);
+    if (!match) return null;
+    return (Number(match[1]) / 100);
+  }
 
-        var exclusion = [];
-        while (e && le != e && (/\S/).test(e)) {
-          le = e;
-          for (var i in p) {
-            if (m = e.match(p[i])) {
-              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
-              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
-              e = e.replace(m[0], '');
-              break;
-            }
-          }
-        }
-        return "[not(" + exclusion.join(" and ") + ")]";
-      },
-      'nth-child':      function(m) {
-        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
-      },
-      'nth-last-child': function(m) {
-        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
-      },
-      'nth-of-type':    function(m) {
-        return Selector.xpath.pseudos.nth("position() ", m);
-      },
-      'nth-last-of-type': function(m) {
-        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
-      },
-      'first-of-type':  function(m) {
-        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
-      },
-      'last-of-type':   function(m) {
-        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
-      },
-      'only-of-type':   function(m) {
-        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
-      },
-      nth: function(fragment, m) {
-        var mm, formula = m[6], predicate;
-        if (formula == 'even') formula = '2n+0';
-        if (formula == 'odd')  formula = '2n+1';
-        if (mm = formula.match(/^(\d+)$/)) // digit only
-          return '[' + fragment + "= " + mm[1] + ']';
-        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
-          if (mm[1] == "-") mm[1] = -1;
-          var a = mm[1] ? Number(mm[1]) : 1;
-          var b = mm[2] ? Number(mm[2]) : 0;
-          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
-          "((#{fragment} - #{b}) div #{a} >= 0)]";
-          return new Template(predicate).evaluate({
-            fragment: fragment, a: a, b: b });
-        }
-      }
+  function getPixelValue(value, property, context) {
+    var element = null;
+    if (Object.isElement(value)) {
+      element = value;
+      value = element.getStyle(property);
     }
-  },
 
-  criteria: {
-    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
-    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
-    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
-    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
-    attr: function(m) {
-      m[3] = (m[5] || m[6]);
-      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
-    },
-    pseudo: function(m) {
-      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
-      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
-    },
-    descendant:   'c = "descendant";',
-    child:        'c = "child";',
-    adjacent:     'c = "adjacent";',
-    laterSibling: 'c = "laterSibling";'
-  },
-
-  patterns: {
-    // combinators must be listed first
-    // (and descendant needs to be last combinator)
-    laterSibling: /^\s*~\s*/,
-    child:        /^\s*>\s*/,
-    adjacent:     /^\s*\+\s*/,
-    descendant:   /^\s/,
-
-    // selectors follow
-    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
-    id:           /^#([\w\-\*]+)(\b|$)/,
-    className:    /^\.([\w\-\*]+)(\b|$)/,
-    pseudo:
-/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
-    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
-    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
-  },
-
-  // for Selector.match and Element#match
-  assertions: {
-    tagName: function(element, matches) {
-      return matches[1].toUpperCase() == element.tagName.toUpperCase();
-    },
-
-    className: function(element, matches) {
-      return Element.hasClassName(element, matches[1]);
-    },
-
-    id: function(element, matches) {
-      return element.id === matches[1];
-    },
-
-    attrPresence: function(element, matches) {
-      return Element.hasAttribute(element, matches[1]);
-    },
-
-    attr: function(element, matches) {
-      var nodeValue = Element.readAttribute(element, matches[1]);
-      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+    if (value === null) {
+      return null;
     }
-  },
 
-  handlers: {
-    // UTILITY FUNCTIONS
-    // joins two collections
-    concat: function(a, b) {
-      for (var i = 0, node; node = b[i]; i++)
-        a.push(node);
-      return a;
-    },
+    if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
+      return window.parseFloat(value);
+    }
 
-    // marks an array of nodes for counting
-    mark: function(nodes) {
-      var _true = Prototype.emptyFunction;
-      for (var i = 0, node; node = nodes[i]; i++)
-        node._countedByPrototype = _true;
-      return nodes;
-    },
+    var isPercentage = value.include('%'), isViewport = (context === document.viewport);
 
-    unmark: function(nodes) {
-      for (var i = 0, node; node = nodes[i]; i++)
-        node._countedByPrototype = undefined;
-      return nodes;
-    },
+    if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
+      var style = element.style.left, rStyle = element.runtimeStyle.left;
+      element.runtimeStyle.left = element.currentStyle.left;
+      element.style.left = value || 0;
+      value = element.style.pixelLeft;
+      element.style.left = style;
+      element.runtimeStyle.left = rStyle;
 
-    // mark each child node with its position (for nth calls)
-    // "ofType" flag indicates whether we're indexing for nth-of-type
-    // rather than nth-child
-    index: function(parentNode, reverse, ofType) {
-      parentNode._countedByPrototype = Prototype.emptyFunction;
-      if (reverse) {
-        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
-          var node = nodes[i];
-          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+      return value;
+    }
+
+    if (element && isPercentage) {
+      context = context || element.parentNode;
+      var decimal = toDecimal(value);
+      var whole = null;
+      var position = element.getStyle('position');
+
+      var isHorizontal = property.include('left') || property.include('right') ||
+       property.include('width');
+
+      var isVertical =  property.include('top') || property.include('bottom') ||
+        property.include('height');
+
+      if (context === document.viewport) {
+        if (isHorizontal) {
+          whole = document.viewport.getWidth();
+        } else if (isVertical) {
+          whole = document.viewport.getHeight();
         }
       } else {
-        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
-          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
-      }
-    },
-
-    // filters out duplicates and extends all nodes
-    unique: function(nodes) {
-      if (nodes.length == 0) return nodes;
-      var results = [], n;
-      for (var i = 0, l = nodes.length; i < l; i++)
-        if (!(n = nodes[i])._countedByPrototype) {
-          n._countedByPrototype = Prototype.emptyFunction;
-          results.push(Element.extend(n));
+        if (isHorizontal) {
+          whole = $(context).measure('width');
+        } else if (isVertical) {
+          whole = $(context).measure('height');
         }
-      return Selector.handlers.unmark(results);
-    },
-
-    // COMBINATOR FUNCTIONS
-    descendant: function(nodes) {
-      var h = Selector.handlers;
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        h.concat(results, node.getElementsByTagName('*'));
-      return results;
-    },
-
-    child: function(nodes) {
-      var h = Selector.handlers;
-      for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        for (var j = 0, child; child = node.childNodes[j]; j++)
-          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
       }
-      return results;
-    },
 
-    adjacent: function(nodes) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        var next = this.nextElementSibling(node);
-        if (next) results.push(next);
-      }
-      return results;
-    },
-
-    laterSibling: function(nodes) {
-      var h = Selector.handlers;
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        h.concat(results, Element.nextSiblings(node));
-      return results;
-    },
-
-    nextElementSibling: function(node) {
-      while (node = node.nextSibling)
-        if (node.nodeType == 1) return node;
-      return null;
-    },
-
-    previousElementSibling: function(node) {
-      while (node = node.previousSibling)
-        if (node.nodeType == 1) return node;
-      return null;
-    },
-
-    // TOKEN FUNCTIONS
-    tagName: function(nodes, root, tagName, combinator) {
-      var uTagName = tagName.toUpperCase();
-      var results = [], h = Selector.handlers;
-      if (nodes) {
-        if (combinator) {
-          // fastlane for ordinary descendant combinators
-          if (combinator == "descendant") {
-            for (var i = 0, node; node = nodes[i]; i++)
-              h.concat(results, node.getElementsByTagName(tagName));
-            return results;
-          } else nodes = this[combinator](nodes);
-          if (tagName == "*") return nodes;
-        }
-        for (var i = 0, node; node = nodes[i]; i++)
-          if (node.tagName.toUpperCase() === uTagName) results.push(node);
-        return results;
-      } else return root.getElementsByTagName(tagName);
-    },
-
-    id: function(nodes, root, id, combinator) {
-      var targetNode = $(id), h = Selector.handlers;
-      if (!targetNode) return [];
-      if (!nodes && root == document) return [targetNode];
-      if (nodes) {
-        if (combinator) {
-          if (combinator == 'child') {
-            for (var i = 0, node; node = nodes[i]; i++)
-              if (targetNode.parentNode == node) return [targetNode];
-          } else if (combinator == 'descendant') {
-            for (var i = 0, node; node = nodes[i]; i++)
-              if (Element.descendantOf(targetNode, node)) return [targetNode];
-          } else if (combinator == 'adjacent') {
-            for (var i = 0, node; node = nodes[i]; i++)
-              if (Selector.handlers.previousElementSibling(targetNode) == node)
-                return [targetNode];
-          } else nodes = h[combinator](nodes);
-        }
-        for (var i = 0, node; node = nodes[i]; i++)
-          if (node == targetNode) return [targetNode];
-        return [];
-      }
-      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
-    },
-
-    className: function(nodes, root, className, combinator) {
-      if (nodes && combinator) nodes = this[combinator](nodes);
-      return Selector.handlers.byClassName(nodes, root, className);
-    },
-
-    byClassName: function(nodes, root, className) {
-      if (!nodes) nodes = Selector.handlers.descendant([root]);
-      var needle = ' ' + className + ' ';
-      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
-        nodeClassName = node.className;
-        if (nodeClassName.length == 0) continue;
-        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
-          results.push(node);
-      }
-      return results;
-    },
-
-    attrPresence: function(nodes, root, attr, combinator) {
-      if (!nodes) nodes = root.getElementsByTagName("*");
-      if (nodes && combinator) nodes = this[combinator](nodes);
-      var results = [];
-      for (var i = 0, node; node = nodes[i]; i++)
-        if (Element.hasAttribute(node, attr)) results.push(node);
-      return results;
-    },
-
-    attr: function(nodes, root, attr, value, operator, combinator) {
-      if (!nodes) nodes = root.getElementsByTagName("*");
-      if (nodes && combinator) nodes = this[combinator](nodes);
-      var handler = Selector.operators[operator], results = [];
-      for (var i = 0, node; node = nodes[i]; i++) {
-        var nodeValue = Element.readAttribute(node, attr);
-        if (nodeValue === null) continue;
-        if (handler(nodeValue, value)) results.push(node);
-      }
-      return results;
-    },
-
-    pseudo: function(nodes, name, value, root, combinator) {
-      if (nodes && combinator) nodes = this[combinator](nodes);
-      if (!nodes) nodes = root.getElementsByTagName("*");
-      return Selector.pseudos[name](nodes, value, root);
+      return (whole === null) ? 0 : whole * decimal;
     }
-  },
 
-  pseudos: {
-    'first-child': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        if (Selector.handlers.previousElementSibling(node)) continue;
-          results.push(node);
-      }
-      return results;
-    },
-    'last-child': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        if (Selector.handlers.nextElementSibling(node)) continue;
-          results.push(node);
-      }
-      return results;
-    },
-    'only-child': function(nodes, value, root) {
-      var h = Selector.handlers;
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
-          results.push(node);
-      return results;
-    },
-    'nth-child':        function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, formula, root);
-    },
-    'nth-last-child':   function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, formula, root, true);
-    },
-    'nth-of-type':      function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, formula, root, false, true);
-    },
-    'nth-last-of-type': function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, formula, root, true, true);
-    },
-    'first-of-type':    function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, "1", root, false, true);
-    },
-    'last-of-type':     function(nodes, formula, root) {
-      return Selector.pseudos.nth(nodes, "1", root, true, true);
-    },
-    'only-of-type':     function(nodes, formula, root) {
-      var p = Selector.pseudos;
-      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
-    },
-
-    // handles the an+b logic
-    getIndices: function(a, b, total) {
-      if (a == 0) return b > 0 ? [b] : [];
-      return $R(1, total).inject([], function(memo, i) {
-        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
-        return memo;
-      });
-    },
-
-    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
-    nth: function(nodes, formula, root, reverse, ofType) {
-      if (nodes.length == 0) return [];
-      if (formula == 'even') formula = '2n+0';
-      if (formula == 'odd')  formula = '2n+1';
-      var h = Selector.handlers, results = [], indexed = [], m;
-      h.mark(nodes);
-      for (var i = 0, node; node = nodes[i]; i++) {
-        if (!node.parentNode._countedByPrototype) {
-          h.index(node.parentNode, reverse, ofType);
-          indexed.push(node.parentNode);
-        }
-      }
-      if (formula.match(/^\d+$/)) { // just a number
-        formula = Number(formula);
-        for (var i = 0, node; node = nodes[i]; i++)
-          if (node.nodeIndex == formula) results.push(node);
-      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
-        if (m[1] == "-") m[1] = -1;
-        var a = m[1] ? Number(m[1]) : 1;
-        var b = m[2] ? Number(m[2]) : 0;
-        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
-        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
-          for (var j = 0; j < l; j++)
-            if (node.nodeIndex == indices[j]) results.push(node);
-        }
-      }
-      h.unmark(nodes);
-      h.unmark(indexed);
-      return results;
-    },
-
-    'empty': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++) {
-        // IE treats comments as element nodes
-        if (node.tagName == '!' || node.firstChild) continue;
-        results.push(node);
-      }
-      return results;
-    },
-
-    'not': function(nodes, selector, root) {
-      var h = Selector.handlers, selectorType, m;
-      var exclusions = new Selector(selector).findElements(root);
-      h.mark(exclusions);
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node._countedByPrototype) results.push(node);
-      h.unmark(exclusions);
-      return results;
-    },
-
-    'enabled': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node.disabled && (!node.type || node.type !== 'hidden'))
-          results.push(node);
-      return results;
-    },
-
-    'disabled': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (node.disabled) results.push(node);
-      return results;
-    },
-
-    'checked': function(nodes, value, root) {
-      for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (node.checked) results.push(node);
-      return results;
-    }
-  },
-
-  operators: {
-    '=':  function(nv, v) { return nv == v; },
-    '!=': function(nv, v) { return nv != v; },
-    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
-    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
-    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
-    '$=': function(nv, v) { return nv.endsWith(v); },
-    '*=': function(nv, v) { return nv.include(v); },
-    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
-    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
-     '-').include('-' + (v || "").toUpperCase() + '-'); }
-  },
-
-  split: function(expression) {
-    var expressions = [];
-    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
-      expressions.push(m[1].strip());
-    });
-    return expressions;
-  },
-
-  matchElements: function(elements, expression) {
-    var matches = $$(expression), h = Selector.handlers;
-    h.mark(matches);
-    for (var i = 0, results = [], element; element = elements[i]; i++)
-      if (element._countedByPrototype) results.push(element);
-    h.unmark(matches);
-    return results;
-  },
-
-  findElement: function(elements, expression, index) {
-    if (Object.isNumber(expression)) {
-      index = expression; expression = false;
-    }
-    return Selector.matchElements(elements, expression || '*')[index || 0];
-  },
-
-  findChildElements: function(element, expressions) {
-    expressions = Selector.split(expressions.join(','));
-    var results = [], h = Selector.handlers;
-    for (var i = 0, l = expressions.length, selector; i < l; i++) {
-      selector = new Selector(expressions[i].strip());
-      h.concat(results, selector.findElements(element));
-    }
-    return (l > 1) ? h.unique(results) : results;
+    return 0;
   }
-});
 
-if (Prototype.Browser.IE) {
-  Object.extend(Selector.handlers, {
-    // IE returns comment nodes on getElementsByTagName("*").
-    // Filter them out.
-    concat: function(a, b) {
-      for (var i = 0, node; node = b[i]; i++)
-        if (node.tagName !== "!") a.push(node);
-      return a;
+  function toCSSPixels(number) {
+    if (Object.isString(number) && number.endsWith('px')) {
+      return number;
+    }
+    return number + 'px';
+  }
+
+  function isDisplayed(element) {
+    var originalElement = element;
+    while (element && element.parentNode) {
+      var display = element.getStyle('display');
+      if (display === 'none') {
+        return false;
+      }
+      element = $(element.parentNode);
+    }
+    return true;
+  }
+
+  var hasLayout = Prototype.K;
+  if ('currentStyle' in document.documentElement) {
+    hasLayout = function(element) {
+      if (!element.currentStyle.hasLayout) {
+        element.style.zoom = 1;
+      }
+      return element;
+    };
+  }
+
+  function cssNameFor(key) {
+    if (key.include('border')) key = key + '-width';
+    return key.camelize();
+  }
+
+  Element.Layout = Class.create(Hash, {
+    initialize: function($super, element, preCompute) {
+      $super();
+      this.element = $(element);
+
+      Element.Layout.PROPERTIES.each( function(property) {
+        this._set(property, null);
+      }, this);
+
+      if (preCompute) {
+        this._preComputing = true;
+        this._begin();
+        Element.Layout.PROPERTIES.each( this._compute, this );
+        this._end();
+        this._preComputing = false;
+      }
     },
 
-    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
-    unmark: function(nodes) {
-      for (var i = 0, node; node = nodes[i]; i++)
-        node.removeAttribute('_countedByPrototype');
-      return nodes;
+    _set: function(property, value) {
+      return Hash.prototype.set.call(this, property, value);
+    },
+
+    set: function(property, value) {
+      throw "Properties of Element.Layout are read-only.";
+    },
+
+    get: function($super, property) {
+      var value = $super(property);
+      return value === null ? this._compute(property) : value;
+    },
+
+    _begin: function() {
+      if (this._prepared) return;
+
+      var element = this.element;
+      if (isDisplayed(element)) {
+        this._prepared = true;
+        return;
+      }
+
+      var originalStyles = {
+        position:   element.style.position   || '',
+        width:      element.style.width      || '',
+        visibility: element.style.visibility || '',
+        display:    element.style.display    || ''
+      };
+
+      element.store('prototype_original_styles', originalStyles);
+
+      var position = element.getStyle('position'),
+       width = element.getStyle('width');
+
+      if (width === "0px" || width === null) {
+        element.style.display = 'block';
+        width = element.getStyle('width');
+      }
+
+      var context = (position === 'fixed') ? document.viewport :
+       element.parentNode;
+
+      element.setStyle({
+        position:   'absolute',
+        visibility: 'hidden',
+        display:    'block'
+      });
+
+      var positionedWidth = element.getStyle('width');
+
+      var newWidth;
+      if (width && (positionedWidth === width)) {
+        newWidth = getPixelValue(element, 'width', context);
+      } else if (position === 'absolute' || position === 'fixed') {
+        newWidth = getPixelValue(element, 'width', context);
+      } else {
+        var parent = element.parentNode, pLayout = $(parent).getLayout();
+
+        newWidth = pLayout.get('width') -
+         this.get('margin-left') -
+         this.get('border-left') -
+         this.get('padding-left') -
+         this.get('padding-right') -
+         this.get('border-right') -
+         this.get('margin-right');
+      }
+
+      element.setStyle({ width: newWidth + 'px' });
+
+      this._prepared = true;
+    },
+
+    _end: function() {
+      var element = this.element;
+      var originalStyles = element.retrieve('prototype_original_styles');
+      element.store('prototype_original_styles', null);
+      element.setStyle(originalStyles);
+      this._prepared = false;
+    },
+
+    _compute: function(property) {
+      var COMPUTATIONS = Element.Layout.COMPUTATIONS;
+      if (!(property in COMPUTATIONS)) {
+        throw "Property not found.";
+      }
+
+      return this._set(property, COMPUTATIONS[property].call(this, this.element));
+    },
+
+    toObject: function() {
+      var args = $A(arguments);
+      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+       args.join(' ').split(' ');
+      var obj = {};
+      keys.each( function(key) {
+        if (!Element.Layout.PROPERTIES.include(key)) return;
+        var value = this.get(key);
+        if (value != null) obj[key] = value;
+      }, this);
+      return obj;
+    },
+
+    toHash: function() {
+      var obj = this.toObject.apply(this, arguments);
+      return new Hash(obj);
+    },
+
+    toCSS: function() {
+      var args = $A(arguments);
+      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+       args.join(' ').split(' ');
+      var css = {};
+
+      keys.each( function(key) {
+        if (!Element.Layout.PROPERTIES.include(key)) return;
+        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
+
+        var value = this.get(key);
+        if (value != null) css[cssNameFor(key)] = value + 'px';
+      }, this);
+      return css;
+    },
+
+    inspect: function() {
+      return "#<Element.Layout>";
     }
   });
+
+  Object.extend(Element.Layout, {
+    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
+
+    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
+
+    COMPUTATIONS: {
+      'height': function(element) {
+        if (!this._preComputing) this._begin();
+
+        var bHeight = this.get('border-box-height');
+        if (bHeight <= 0) {
+          if (!this._preComputing) this._end();
+          return 0;
+        }
+
+        var bTop = this.get('border-top'),
+         bBottom = this.get('border-bottom');
+
+        var pTop = this.get('padding-top'),
+         pBottom = this.get('padding-bottom');
+
+        if (!this._preComputing) this._end();
+
+        return bHeight - bTop - bBottom - pTop - pBottom;
+      },
+
+      'width': function(element) {
+        if (!this._preComputing) this._begin();
+
+        var bWidth = this.get('border-box-width');
+        if (bWidth <= 0) {
+          if (!this._preComputing) this._end();
+          return 0;
+        }
+
+        var bLeft = this.get('border-left'),
+         bRight = this.get('border-right');
+
+        var pLeft = this.get('padding-left'),
+         pRight = this.get('padding-right');
+
+        if (!this._preComputing) this._end();
+
+        return bWidth - bLeft - bRight - pLeft - pRight;
+      },
+
+      'padding-box-height': function(element) {
+        var height = this.get('height'),
+         pTop = this.get('padding-top'),
+         pBottom = this.get('padding-bottom');
+
+        return height + pTop + pBottom;
+      },
+
+      'padding-box-width': function(element) {
+        var width = this.get('width'),
+         pLeft = this.get('padding-left'),
+         pRight = this.get('padding-right');
+
+        return width + pLeft + pRight;
+      },
+
+      'border-box-height': function(element) {
+        if (!this._preComputing) this._begin();
+        var height = element.offsetHeight;
+        if (!this._preComputing) this._end();
+        return height;
+      },
+
+      'border-box-width': function(element) {
+        if (!this._preComputing) this._begin();
+        var width = element.offsetWidth;
+        if (!this._preComputing) this._end();
+        return width;
+      },
+
+      'margin-box-height': function(element) {
+        var bHeight = this.get('border-box-height'),
+         mTop = this.get('margin-top'),
+         mBottom = this.get('margin-bottom');
+
+        if (bHeight <= 0) return 0;
+
+        return bHeight + mTop + mBottom;
+      },
+
+      'margin-box-width': function(element) {
+        var bWidth = this.get('border-box-width'),
+         mLeft = this.get('margin-left'),
+         mRight = this.get('margin-right');
+
+        if (bWidth <= 0) return 0;
+
+        return bWidth + mLeft + mRight;
+      },
+
+      'top': function(element) {
+        var offset = element.positionedOffset();
+        return offset.top;
+      },
+
+      'bottom': function(element) {
+        var offset = element.positionedOffset(),
+         parent = element.getOffsetParent(),
+         pHeight = parent.measure('height');
+
+        var mHeight = this.get('border-box-height');
+
+        return pHeight - mHeight - offset.top;
+      },
+
+      'left': function(element) {
+        var offset = element.positionedOffset();
+        return offset.left;
+      },
+
+      'right': function(element) {
+        var offset = element.positionedOffset(),
+         parent = element.getOffsetParent(),
+         pWidth = parent.measure('width');
+
+        var mWidth = this.get('border-box-width');
+
+        return pWidth - mWidth - offset.left;
+      },
+
+      'padding-top': function(element) {
+        return getPixelValue(element, 'paddingTop');
+      },
+
+      'padding-bottom': function(element) {
+        return getPixelValue(element, 'paddingBottom');
+      },
+
+      'padding-left': function(element) {
+        return getPixelValue(element, 'paddingLeft');
+      },
+
+      'padding-right': function(element) {
+        return getPixelValue(element, 'paddingRight');
+      },
+
+      'border-top': function(element) {
+        return getPixelValue(element, 'borderTopWidth');
+      },
+
+      'border-bottom': function(element) {
+        return getPixelValue(element, 'borderBottomWidth');
+      },
+
+      'border-left': function(element) {
+        return getPixelValue(element, 'borderLeftWidth');
+      },
+
+      'border-right': function(element) {
+        return getPixelValue(element, 'borderRightWidth');
+      },
+
+      'margin-top': function(element) {
+        return getPixelValue(element, 'marginTop');
+      },
+
+      'margin-bottom': function(element) {
+        return getPixelValue(element, 'marginBottom');
+      },
+
+      'margin-left': function(element) {
+        return getPixelValue(element, 'marginLeft');
+      },
+
+      'margin-right': function(element) {
+        return getPixelValue(element, 'marginRight');
+      }
+    }
+  });
+
+  if ('getBoundingClientRect' in document.documentElement) {
+    Object.extend(Element.Layout.COMPUTATIONS, {
+      'right': function(element) {
+        var parent = hasLayout(element.getOffsetParent());
+        var rect = element.getBoundingClientRect(),
+         pRect = parent.getBoundingClientRect();
+
+        return (pRect.right - rect.right).round();
+      },
+
+      'bottom': function(element) {
+        var parent = hasLayout(element.getOffsetParent());
+        var rect = element.getBoundingClientRect(),
+         pRect = parent.getBoundingClientRect();
+
+        return (pRect.bottom - rect.bottom).round();
+      }
+    });
+  }
+
+  Element.Offset = Class.create({
+    initialize: function(left, top) {
+      this.left = left.round();
+      this.top  = top.round();
+
+      this[0] = this.left;
+      this[1] = this.top;
+    },
+
+    relativeTo: function(offset) {
+      return new Element.Offset(
+        this.left - offset.left,
+        this.top  - offset.top
+      );
+    },
+
+    inspect: function() {
+      return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
+    },
+
+    toString: function() {
+      return "[#{left}, #{top}]".interpolate(this);
+    },
+
+    toArray: function() {
+      return [this.left, this.top];
+    }
+  });
+
+  function getLayout(element, preCompute) {
+    return new Element.Layout(element, preCompute);
+  }
+
+  function measure(element, property) {
+    return $(element).getLayout().get(property);
+  }
+
+  function getDimensions(element) {
+    element = $(element);
+    var display = Element.getStyle(element, 'display');
+
+    if (display && display !== 'none') {
+      return { width: element.offsetWidth, height: element.offsetHeight };
+    }
+
+    var style = element.style;
+    var originalStyles = {
+      visibility: style.visibility,
+      position:   style.position,
+      display:    style.display
+    };
+
+    var newStyles = {
+      visibility: 'hidden',
+      display:    'block'
+    };
+
+    if (originalStyles.position !== 'fixed')
+      newStyles.position = 'absolute';
+
+    Element.setStyle(element, newStyles);
+
+    var dimensions = {
+      width:  element.offsetWidth,
+      height: element.offsetHeight
+    };
+
+    Element.setStyle(element, originalStyles);
+
+    return dimensions;
+  }
+
+  function getOffsetParent(element) {
+    element = $(element);
+
+    if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
+      return $(document.body);
+
+    var isInline = (Element.getStyle(element, 'display') === 'inline');
+    if (!isInline && element.offsetParent) return $(element.offsetParent);
+
+    while ((element = element.parentNode) && element !== document.body) {
+      if (Element.getStyle(element, 'position') !== 'static') {
+        return isHtml(element) ? $(document.body) : $(element);
+      }
+    }
+
+    return $(document.body);
+  }
+
+
+  function cumulativeOffset(element) {
+    element = $(element);
+    var valueT = 0, valueL = 0;
+    if (element.parentNode) {
+      do {
+        valueT += element.offsetTop  || 0;
+        valueL += element.offsetLeft || 0;
+        element = element.offsetParent;
+      } while (element);
+    }
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function positionedOffset(element) {
+    element = $(element);
+
+    var layout = element.getLayout();
+
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (isBody(element)) break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+
+    valueL -= layout.get('margin-top');
+    valueT -= layout.get('margin-left');
+
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function cumulativeScrollOffset(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function viewportOffset(forElement) {
+    element = $(element);
+    var valueT = 0, valueL = 0, docBody = document.body;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == docBody &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (element != docBody) {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function absolutize(element) {
+    element = $(element);
+
+    if (Element.getStyle(element, 'position') === 'absolute') {
+      return element;
+    }
+
+    var offsetParent = getOffsetParent(element);
+    var eOffset = element.viewportOffset(),
+     pOffset = offsetParent.viewportOffset();
+
+    var offset = eOffset.relativeTo(pOffset);
+    var layout = element.getLayout();
+
+    element.store('prototype_absolutize_original_styles', {
+      left:   element.getStyle('left'),
+      top:    element.getStyle('top'),
+      width:  element.getStyle('width'),
+      height: element.getStyle('height')
+    });
+
+    element.setStyle({
+      position: 'absolute',
+      top:    offset.top + 'px',
+      left:   offset.left + 'px',
+      width:  layout.get('width') + 'px',
+      height: layout.get('height') + 'px'
+    });
+
+    return element;
+  }
+
+  function relativize(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'position') === 'relative') {
+      return element;
+    }
+
+    var originalStyles =
+     element.retrieve('prototype_absolutize_original_styles');
+
+    if (originalStyles) element.setStyle(originalStyles);
+    return element;
+  }
+
+  if (Prototype.Browser.IE) {
+    getOffsetParent = getOffsetParent.wrap(
+      function(proceed, element) {
+        element = $(element);
+
+        if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
+          return $(document.body);
+
+        var position = element.getStyle('position');
+        if (position !== 'static') return proceed(element);
+
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+
+    positionedOffset = positionedOffset.wrap(function(proceed, element) {
+      element = $(element);
+      if (!element.parentNode) return new Element.Offset(0, 0);
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+
+      var offsetParent = element.getOffsetParent();
+      if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+        hasLayout(offsetParent);
+
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    });
+  } else if (Prototype.Browser.Webkit) {
+    cumulativeOffset = function(element) {
+      element = $(element);
+      var valueT = 0, valueL = 0;
+      do {
+        valueT += element.offsetTop  || 0;
+        valueL += element.offsetLeft || 0;
+        if (element.offsetParent == document.body)
+          if (Element.getStyle(element, 'position') == 'absolute') break;
+
+        element = element.offsetParent;
+      } while (element);
+
+      return new Element.Offset(valueL, valueT);
+    };
+  }
+
+
+  Element.addMethods({
+    getLayout:              getLayout,
+    measure:                measure,
+    getDimensions:          getDimensions,
+    getOffsetParent:        getOffsetParent,
+    cumulativeOffset:       cumulativeOffset,
+    positionedOffset:       positionedOffset,
+    cumulativeScrollOffset: cumulativeScrollOffset,
+    viewportOffset:         viewportOffset,
+    absolutize:             absolutize,
+    relativize:             relativize
+  });
+
+  function isBody(element) {
+    return element.nodeName.toUpperCase() === 'BODY';
+  }
+
+  function isHtml(element) {
+    return element.nodeName.toUpperCase() === 'HTML';
+  }
+
+  function isDocument(element) {
+    return element.nodeType === Node.DOCUMENT_NODE;
+  }
+
+  function isDetached(element) {
+    return element !== document.body &&
+     !Element.descendantOf(element, document.body);
+  }
+
+  if ('getBoundingClientRect' in document.documentElement) {
+    Element.addMethods({
+      viewportOffset: function(element) {
+        element = $(element);
+        if (isDetached(element)) return new Element.Offset(0, 0);
+
+        var rect = element.getBoundingClientRect(),
+         docEl = document.documentElement;
+        return new Element.Offset(rect.left - docEl.clientLeft,
+         rect.top - docEl.clientTop);
+      }
+    });
+  }
+})();
+window.$$ = function() {
+  var expression = $A(arguments).join(', ');
+  return Prototype.Selector.select(expression, document);
+};
+
+Prototype.Selector = (function() {
+
+  function select() {
+    throw new Error('Method "Prototype.Selector.select" must be defined.');
+  }
+
+  function match() {
+    throw new Error('Method "Prototype.Selector.match" must be defined.');
+  }
+
+  function find(elements, expression, index) {
+    index = index || 0;
+    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
+
+    for (i = 0; i < length; i++) {
+      if (match(elements[i], expression) && index == matchIndex++) {
+        return Element.extend(elements[i]);
+      }
+    }
+  }
+
+  function extendElements(elements) {
+    for (var i = 0, length = elements.length; i < length; i++) {
+      Element.extend(elements[i]);
+    }
+    return elements;
+  }
+
+
+  var K = Prototype.K;
+
+  return {
+    select: select,
+    match: match,
+    find: find,
+    extendElements: (Element.extend === K) ? K : extendElements,
+    extendElement: Element.extend
+  };
+})();
+Prototype._original_property = window.Sizzle;
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+	done = 0,
+	toString = Object.prototype.toString,
+	hasDuplicate = false,
+	baseHasDuplicate = true;
+
+[0, 0].sort(function(){
+	baseHasDuplicate = false;
+	return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+	results = results || [];
+	var origContext = context = context || document;
+
+	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
+		soFar = selector;
+
+	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+		soFar = m[3];
+
+		parts.push( m[1] );
+
+		if ( m[2] ) {
+			extra = m[3];
+			break;
+		}
+	}
+
+	if ( parts.length > 1 && origPOS.exec( selector ) ) {
+		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+			set = posProcess( parts[0] + parts[1], context );
+		} else {
+			set = Expr.relative[ parts[0] ] ?
+				[ context ] :
+				Sizzle( parts.shift(), context );
+
+			while ( parts.length ) {
+				selector = parts.shift();
+
+				if ( Expr.relative[ selector ] )
+					selector += parts.shift();
+
+				set = posProcess( selector, set );
+			}
+		}
+	} else {
+		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+			var ret = Sizzle.find( parts.shift(), context, contextXML );
+			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+		}
+
+		if ( context ) {
+			var ret = seed ?
+				{ expr: parts.pop(), set: makeArray(seed) } :
+				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+			if ( parts.length > 0 ) {
+				checkSet = makeArray(set);
+			} else {
+				prune = false;
+			}
+
+			while ( parts.length ) {
+				var cur = parts.pop(), pop = cur;
+
+				if ( !Expr.relative[ cur ] ) {
+					cur = "";
+				} else {
+					pop = parts.pop();
+				}
+
+				if ( pop == null ) {
+					pop = context;
+				}
+
+				Expr.relative[ cur ]( checkSet, pop, contextXML );
+			}
+		} else {
+			checkSet = parts = [];
+		}
+	}
+
+	if ( !checkSet ) {
+		checkSet = set;
+	}
+
+	if ( !checkSet ) {
+		throw "Syntax error, unrecognized expression: " + (cur || selector);
+	}
+
+	if ( toString.call(checkSet) === "[object Array]" ) {
+		if ( !prune ) {
+			results.push.apply( results, checkSet );
+		} else if ( context && context.nodeType === 1 ) {
+			for ( var i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+					results.push( set[i] );
+				}
+			}
+		} else {
+			for ( var i = 0; checkSet[i] != null; i++ ) {
+				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+					results.push( set[i] );
+				}
+			}
+		}
+	} else {
+		makeArray( checkSet, results );
+	}
+
+	if ( extra ) {
+		Sizzle( extra, origContext, results, seed );
+		Sizzle.uniqueSort( results );
+	}
+
+	return results;
+};
+
+Sizzle.uniqueSort = function(results){
+	if ( sortOrder ) {
+		hasDuplicate = baseHasDuplicate;
+		results.sort(sortOrder);
+
+		if ( hasDuplicate ) {
+			for ( var i = 1; i < results.length; i++ ) {
+				if ( results[i] === results[i-1] ) {
+					results.splice(i--, 1);
+				}
+			}
+		}
+	}
+
+	return results;
+};
+
+Sizzle.matches = function(expr, set){
+	return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+	var set, match;
+
+	if ( !expr ) {
+		return [];
+	}
+
+	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+		var type = Expr.order[i], match;
+
+		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+			var left = match[1];
+			match.splice(1,1);
+
+			if ( left.substr( left.length - 1 ) !== "\\" ) {
+				match[1] = (match[1] || "").replace(/\\/g, "");
+				set = Expr.find[ type ]( match, context, isXML );
+				if ( set != null ) {
+					expr = expr.replace( Expr.match[ type ], "" );
+					break;
+				}
+			}
+		}
+	}
+
+	if ( !set ) {
+		set = context.getElementsByTagName("*");
+	}
+
+	return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+	var old = expr, result = [], curLoop = set, match, anyFound,
+		isXMLFilter = set && set[0] && isXML(set[0]);
+
+	while ( expr && set.length ) {
+		for ( var type in Expr.filter ) {
+			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+				var filter = Expr.filter[ type ], found, item;
+				anyFound = false;
+
+				if ( curLoop == result ) {
+					result = [];
+				}
+
+				if ( Expr.preFilter[ type ] ) {
+					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+					if ( !match ) {
+						anyFound = found = true;
+					} else if ( match === true ) {
+						continue;
+					}
+				}
+
+				if ( match ) {
+					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+						if ( item ) {
+							found = filter( item, match, i, curLoop );
+							var pass = not ^ !!found;
+
+							if ( inplace && found != null ) {
+								if ( pass ) {
+									anyFound = true;
+								} else {
+									curLoop[i] = false;
+								}
+							} else if ( pass ) {
+								result.push( item );
+								anyFound = true;
+							}
+						}
+					}
+				}
+
+				if ( found !== undefined ) {
+					if ( !inplace ) {
+						curLoop = result;
+					}
+
+					expr = expr.replace( Expr.match[ type ], "" );
+
+					if ( !anyFound ) {
+						return [];
+					}
+
+					break;
+				}
+			}
+		}
+
+		if ( expr == old ) {
+			if ( anyFound == null ) {
+				throw "Syntax error, unrecognized expression: " + expr;
+			} else {
+				break;
+			}
+		}
+
+		old = expr;
+	}
+
+	return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+	order: [ "ID", "NAME", "TAG" ],
+	match: {
+		ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+		CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+		TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+		PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+	},
+	leftMatch: {},
+	attrMap: {
+		"class": "className",
+		"for": "htmlFor"
+	},
+	attrHandle: {
+		href: function(elem){
+			return elem.getAttribute("href");
+		}
+	},
+	relative: {
+		"+": function(checkSet, part, isXML){
+			var isPartStr = typeof part === "string",
+				isTag = isPartStr && !/\W/.test(part),
+				isPartStrNotTag = isPartStr && !isTag;
+
+			if ( isTag && !isXML ) {
+				part = part.toUpperCase();
+			}
+
+			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+				if ( (elem = checkSet[i]) ) {
+					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+						elem || false :
+						elem === part;
+				}
+			}
+
+			if ( isPartStrNotTag ) {
+				Sizzle.filter( part, checkSet, true );
+			}
+		},
+		">": function(checkSet, part, isXML){
+			var isPartStr = typeof part === "string";
+
+			if ( isPartStr && !/\W/.test(part) ) {
+				part = isXML ? part : part.toUpperCase();
+
+				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+					var elem = checkSet[i];
+					if ( elem ) {
+						var parent = elem.parentNode;
+						checkSet[i] = parent.nodeName === part ? parent : false;
+					}
+				}
+			} else {
+				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+					var elem = checkSet[i];
+					if ( elem ) {
+						checkSet[i] = isPartStr ?
+							elem.parentNode :
+							elem.parentNode === part;
+					}
+				}
+
+				if ( isPartStr ) {
+					Sizzle.filter( part, checkSet, true );
+				}
+			}
+		},
+		"": function(checkSet, part, isXML){
+			var doneName = done++, checkFn = dirCheck;
+
+			if ( !/\W/.test(part) ) {
+				var nodeCheck = part = isXML ? part : part.toUpperCase();
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+		},
+		"~": function(checkSet, part, isXML){
+			var doneName = done++, checkFn = dirCheck;
+
+			if ( typeof part === "string" && !/\W/.test(part) ) {
+				var nodeCheck = part = isXML ? part : part.toUpperCase();
+				checkFn = dirNodeCheck;
+			}
+
+			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+		}
+	},
+	find: {
+		ID: function(match, context, isXML){
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+				return m ? [m] : [];
+			}
+		},
+		NAME: function(match, context, isXML){
+			if ( typeof context.getElementsByName !== "undefined" ) {
+				var ret = [], results = context.getElementsByName(match[1]);
+
+				for ( var i = 0, l = results.length; i < l; i++ ) {
+					if ( results[i].getAttribute("name") === match[1] ) {
+						ret.push( results[i] );
+					}
+				}
+
+				return ret.length === 0 ? null : ret;
+			}
+		},
+		TAG: function(match, context){
+			return context.getElementsByTagName(match[1]);
+		}
+	},
+	preFilter: {
+		CLASS: function(match, curLoop, inplace, result, not, isXML){
+			match = " " + match[1].replace(/\\/g, "") + " ";
+
+			if ( isXML ) {
+				return match;
+			}
+
+			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+				if ( elem ) {
+					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+						if ( !inplace )
+							result.push( elem );
+					} else if ( inplace ) {
+						curLoop[i] = false;
+					}
+				}
+			}
+
+			return false;
+		},
+		ID: function(match){
+			return match[1].replace(/\\/g, "");
+		},
+		TAG: function(match, curLoop){
+			for ( var i = 0; curLoop[i] === false; i++ ){}
+			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+		},
+		CHILD: function(match){
+			if ( match[1] == "nth" ) {
+				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+				match[2] = (test[1] + (test[2] || 1)) - 0;
+				match[3] = test[3] - 0;
+			}
+
+			match[0] = done++;
+
+			return match;
+		},
+		ATTR: function(match, curLoop, inplace, result, not, isXML){
+			var name = match[1].replace(/\\/g, "");
+
+			if ( !isXML && Expr.attrMap[name] ) {
+				match[1] = Expr.attrMap[name];
+			}
+
+			if ( match[2] === "~=" ) {
+				match[4] = " " + match[4] + " ";
+			}
+
+			return match;
+		},
+		PSEUDO: function(match, curLoop, inplace, result, not){
+			if ( match[1] === "not" ) {
+				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+					match[3] = Sizzle(match[3], null, null, curLoop);
+				} else {
+					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+					if ( !inplace ) {
+						result.push.apply( result, ret );
+					}
+					return false;
+				}
+			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+				return true;
+			}
+
+			return match;
+		},
+		POS: function(match){
+			match.unshift( true );
+			return match;
+		}
+	},
+	filters: {
+		enabled: function(elem){
+			return elem.disabled === false && elem.type !== "hidden";
+		},
+		disabled: function(elem){
+			return elem.disabled === true;
+		},
+		checked: function(elem){
+			return elem.checked === true;
+		},
+		selected: function(elem){
+			elem.parentNode.selectedIndex;
+			return elem.selected === true;
+		},
+		parent: function(elem){
+			return !!elem.firstChild;
+		},
+		empty: function(elem){
+			return !elem.firstChild;
+		},
+		has: function(elem, i, match){
+			return !!Sizzle( match[3], elem ).length;
+		},
+		header: function(elem){
+			return /h\d/i.test( elem.nodeName );
+		},
+		text: function(elem){
+			return "text" === elem.type;
+		},
+		radio: function(elem){
+			return "radio" === elem.type;
+		},
+		checkbox: function(elem){
+			return "checkbox" === elem.type;
+		},
+		file: function(elem){
+			return "file" === elem.type;
+		},
+		password: function(elem){
+			return "password" === elem.type;
+		},
+		submit: function(elem){
+			return "submit" === elem.type;
+		},
+		image: function(elem){
+			return "image" === elem.type;
+		},
+		reset: function(elem){
+			return "reset" === elem.type;
+		},
+		button: function(elem){
+			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+		},
+		input: function(elem){
+			return /input|select|textarea|button/i.test(elem.nodeName);
+		}
+	},
+	setFilters: {
+		first: function(elem, i){
+			return i === 0;
+		},
+		last: function(elem, i, match, array){
+			return i === array.length - 1;
+		},
+		even: function(elem, i){
+			return i % 2 === 0;
+		},
+		odd: function(elem, i){
+			return i % 2 === 1;
+		},
+		lt: function(elem, i, match){
+			return i < match[3] - 0;
+		},
+		gt: function(elem, i, match){
+			return i > match[3] - 0;
+		},
+		nth: function(elem, i, match){
+			return match[3] - 0 == i;
+		},
+		eq: function(elem, i, match){
+			return match[3] - 0 == i;
+		}
+	},
+	filter: {
+		PSEUDO: function(elem, match, i, array){
+			var name = match[1], filter = Expr.filters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+			} else if ( name === "contains" ) {
+				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+			} else if ( name === "not" ) {
+				var not = match[3];
+
+				for ( var i = 0, l = not.length; i < l; i++ ) {
+					if ( not[i] === elem ) {
+						return false;
+					}
+				}
+
+				return true;
+			}
+		},
+		CHILD: function(elem, match){
+			var type = match[1], node = elem;
+			switch (type) {
+				case 'only':
+				case 'first':
+					while ( (node = node.previousSibling) )  {
+						if ( node.nodeType === 1 ) return false;
+					}
+					if ( type == 'first') return true;
+					node = elem;
+				case 'last':
+					while ( (node = node.nextSibling) )  {
+						if ( node.nodeType === 1 ) return false;
+					}
+					return true;
+				case 'nth':
+					var first = match[2], last = match[3];
+
+					if ( first == 1 && last == 0 ) {
+						return true;
+					}
+
+					var doneName = match[0],
+						parent = elem.parentNode;
+
+					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+						var count = 0;
+						for ( node = parent.firstChild; node; node = node.nextSibling ) {
+							if ( node.nodeType === 1 ) {
+								node.nodeIndex = ++count;
+							}
+						}
+						parent.sizcache = doneName;
+					}
+
+					var diff = elem.nodeIndex - last;
+					if ( first == 0 ) {
+						return diff == 0;
+					} else {
+						return ( diff % first == 0 && diff / first >= 0 );
+					}
+			}
+		},
+		ID: function(elem, match){
+			return elem.nodeType === 1 && elem.getAttribute("id") === match;
+		},
+		TAG: function(elem, match){
+			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+		},
+		CLASS: function(elem, match){
+			return (" " + (elem.className || elem.getAttribute("class")) + " ")
+				.indexOf( match ) > -1;
+		},
+		ATTR: function(elem, match){
+			var name = match[1],
+				result = Expr.attrHandle[ name ] ?
+					Expr.attrHandle[ name ]( elem ) :
+					elem[ name ] != null ?
+						elem[ name ] :
+						elem.getAttribute( name ),
+				value = result + "",
+				type = match[2],
+				check = match[4];
+
+			return result == null ?
+				type === "!=" :
+				type === "=" ?
+				value === check :
+				type === "*=" ?
+				value.indexOf(check) >= 0 :
+				type === "~=" ?
+				(" " + value + " ").indexOf(check) >= 0 :
+				!check ?
+				value && result !== false :
+				type === "!=" ?
+				value != check :
+				type === "^=" ?
+				value.indexOf(check) === 0 :
+				type === "$=" ?
+				value.substr(value.length - check.length) === check :
+				type === "|=" ?
+				value === check || value.substr(0, check.length + 1) === check + "-" :
+				false;
+		},
+		POS: function(elem, match, i, array){
+			var name = match[2], filter = Expr.setFilters[ name ];
+
+			if ( filter ) {
+				return filter( elem, i, match, array );
+			}
+		}
+	}
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
 }
 
-function $$() {
-  return Selector.findChildElements(document, $A(arguments));
+var makeArray = function(array, results) {
+	array = Array.prototype.slice.call( array, 0 );
+
+	if ( results ) {
+		results.push.apply( results, array );
+		return results;
+	}
+
+	return array;
+};
+
+try {
+	Array.prototype.slice.call( document.documentElement.childNodes, 0 );
+
+} catch(e){
+	makeArray = function(array, results) {
+		var ret = results || [];
+
+		if ( toString.call(array) === "[object Array]" ) {
+			Array.prototype.push.apply( ret, array );
+		} else {
+			if ( typeof array.length === "number" ) {
+				for ( var i = 0, l = array.length; i < l; i++ ) {
+					ret.push( array[i] );
+				}
+			} else {
+				for ( var i = 0; array[i]; i++ ) {
+					ret.push( array[i] );
+				}
+			}
+		}
+
+		return ret;
+	};
 }
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+	sortOrder = function( a, b ) {
+		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+			if ( a == b ) {
+				hasDuplicate = true;
+			}
+			return 0;
+		}
+
+		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+} else if ( "sourceIndex" in document.documentElement ) {
+	sortOrder = function( a, b ) {
+		if ( !a.sourceIndex || !b.sourceIndex ) {
+			if ( a == b ) {
+				hasDuplicate = true;
+			}
+			return 0;
+		}
+
+		var ret = a.sourceIndex - b.sourceIndex;
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+} else if ( document.createRange ) {
+	sortOrder = function( a, b ) {
+		if ( !a.ownerDocument || !b.ownerDocument ) {
+			if ( a == b ) {
+				hasDuplicate = true;
+			}
+			return 0;
+		}
+
+		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+		aRange.setStart(a, 0);
+		aRange.setEnd(a, 0);
+		bRange.setStart(b, 0);
+		bRange.setEnd(b, 0);
+		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+		if ( ret === 0 ) {
+			hasDuplicate = true;
+		}
+		return ret;
+	};
+}
+
+(function(){
+	var form = document.createElement("div"),
+		id = "script" + (new Date).getTime();
+	form.innerHTML = "<a name='" + id + "'/>";
+
+	var root = document.documentElement;
+	root.insertBefore( form, root.firstChild );
+
+	if ( !!document.getElementById( id ) ) {
+		Expr.find.ID = function(match, context, isXML){
+			if ( typeof context.getElementById !== "undefined" && !isXML ) {
+				var m = context.getElementById(match[1]);
+				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+			}
+		};
+
+		Expr.filter.ID = function(elem, match){
+			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+			return elem.nodeType === 1 && node && node.nodeValue === match;
+		};
+	}
+
+	root.removeChild( form );
+	root = form = null; // release memory in IE
+})();
+
+(function(){
+
+	var div = document.createElement("div");
+	div.appendChild( document.createComment("") );
+
+	if ( div.getElementsByTagName("*").length > 0 ) {
+		Expr.find.TAG = function(match, context){
+			var results = context.getElementsByTagName(match[1]);
+
+			if ( match[1] === "*" ) {
+				var tmp = [];
+
+				for ( var i = 0; results[i]; i++ ) {
+					if ( results[i].nodeType === 1 ) {
+						tmp.push( results[i] );
+					}
+				}
+
+				results = tmp;
+			}
+
+			return results;
+		};
+	}
+
+	div.innerHTML = "<a href='#'></a>";
+	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+			div.firstChild.getAttribute("href") !== "#" ) {
+		Expr.attrHandle.href = function(elem){
+			return elem.getAttribute("href", 2);
+		};
+	}
+
+	div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) (function(){
+	var oldSizzle = Sizzle, div = document.createElement("div");
+	div.innerHTML = "<p class='TEST'></p>";
+
+	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+		return;
+	}
+
+	Sizzle = function(query, context, extra, seed){
+		context = context || document;
+
+		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+			try {
+				return makeArray( context.querySelectorAll(query), extra );
+			} catch(e){}
+		}
+
+		return oldSizzle(query, context, extra, seed);
+	};
+
+	for ( var prop in oldSizzle ) {
+		Sizzle[ prop ] = oldSizzle[ prop ];
+	}
+
+	div = null; // release memory in IE
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+	var div = document.createElement("div");
+	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+	if ( div.getElementsByClassName("e").length === 0 )
+		return;
+
+	div.lastChild.className = "e";
+
+	if ( div.getElementsByClassName("e").length === 1 )
+		return;
+
+	Expr.order.splice(1, 0, "CLASS");
+	Expr.find.CLASS = function(match, context, isXML) {
+		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+			return context.getElementsByClassName(match[1]);
+		}
+	};
+
+	div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	var sibDir = dir == "previousSibling" && !isXML;
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+		if ( elem ) {
+			if ( sibDir && elem.nodeType === 1 ){
+				elem.sizcache = doneName;
+				elem.sizset = i;
+			}
+			elem = elem[dir];
+			var match = false;
+
+			while ( elem ) {
+				if ( elem.sizcache === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 && !isXML ){
+					elem.sizcache = doneName;
+					elem.sizset = i;
+				}
+
+				if ( elem.nodeName === cur ) {
+					match = elem;
+					break;
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+	var sibDir = dir == "previousSibling" && !isXML;
+	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+		var elem = checkSet[i];
+		if ( elem ) {
+			if ( sibDir && elem.nodeType === 1 ) {
+				elem.sizcache = doneName;
+				elem.sizset = i;
+			}
+			elem = elem[dir];
+			var match = false;
+
+			while ( elem ) {
+				if ( elem.sizcache === doneName ) {
+					match = checkSet[elem.sizset];
+					break;
+				}
+
+				if ( elem.nodeType === 1 ) {
+					if ( !isXML ) {
+						elem.sizcache = doneName;
+						elem.sizset = i;
+					}
+					if ( typeof cur !== "string" ) {
+						if ( elem === cur ) {
+							match = true;
+							break;
+						}
+
+					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+						match = elem;
+						break;
+					}
+				}
+
+				elem = elem[dir];
+			}
+
+			checkSet[i] = match;
+		}
+	}
+}
+
+var contains = document.compareDocumentPosition ?  function(a, b){
+	return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+	return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+		!!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
+};
+
+var posProcess = function(selector, context){
+	var tmpSet = [], later = "", match,
+		root = context.nodeType ? [context] : context;
+
+	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+		later += match[0];
+		selector = selector.replace( Expr.match.PSEUDO, "" );
+	}
+
+	selector = Expr.relative[selector] ? selector + "*" : selector;
+
+	for ( var i = 0, l = root.length; i < l; i++ ) {
+		Sizzle( selector, root[i], tmpSet );
+	}
+
+	return Sizzle.filter( later, tmpSet );
+};
+
+
+window.Sizzle = Sizzle;
+
+})();
+
+;(function(engine) {
+  var extendElements = Prototype.Selector.extendElements;
+
+  function select(selector, scope) {
+    return extendElements(engine(selector, scope || document));
+  }
+
+  function match(element, selector) {
+    return engine.matches(selector, [element]).length == 1;
+  }
+
+  Prototype.Selector.engine = engine;
+  Prototype.Selector.select = select;
+  Prototype.Selector.match = match;
+})(Sizzle);
+
+window.Sizzle = Prototype._original_property;
+delete Prototype._original_property;
+
 var Form = {
   reset: function(form) {
-    $(form).reset();
+    form = $(form);
+    form.reset();
     return form;
   },
 
   serializeElements: function(elements, options) {
     if (typeof options != 'object') options = { hash: !!options };
     else if (Object.isUndefined(options.hash)) options.hash = true;
-    var key, value, submitted = false, submit = options.submit;
+    var key, value, submitted = false, submit = options.submit, accumulator, initial;
 
-    var data = elements.inject({ }, function(result, element) {
+    if (options.hash) {
+      initial = {};
+      accumulator = function(result, key, value) {
+        if (key in result) {
+          if (!Object.isArray(result[key])) result[key] = [result[key]];
+          result[key].push(value);
+        } else result[key] = value;
+        return result;
+      };
+    } else {
+      initial = '';
+      accumulator = function(result, key, value) {
+        return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
+      }
+    }
+
+    return elements.inject(initial, function(result, element) {
       if (!element.disabled && element.name) {
         key = element.name; value = $(element).getValue();
         if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
             submit !== false && (!submit || key == submit) && (submitted = true)))) {
-          if (key in result) {
-            // a key is already present; construct an array of values
-            if (!Object.isArray(result[key])) result[key] = [result[key]];
-            result[key].push(value);
-          }
-          else result[key] = value;
+          result = accumulator(result, key, value);
         }
       }
       return result;
     });
-
-    return options.hash ? data : Object.toQueryString(data);
   }
 };
 
@@ -3480,13 +5007,18 @@
   },
 
   getElements: function(form) {
-    return $A($(form).getElementsByTagName('*')).inject([],
-      function(elements, child) {
-        if (Form.Element.Serializers[child.tagName.toLowerCase()])
-          elements.push(Element.extend(child));
-        return elements;
-      }
-    );
+    var elements = $(form).getElementsByTagName('*'),
+        element,
+        arr = [ ],
+        serializers = Form.Element.Serializers;
+    for (var i = 0; element = elements[i]; i++) {
+      arr.push(element);
+    }
+    return arr.inject([], function(elements, child) {
+      if (serializers[child.tagName.toLowerCase()])
+        elements.push(Element.extend(child));
+      return elements;
+    })
   },
 
   getInputs: function(form, typeName, name) {
@@ -3526,13 +5058,14 @@
     }).sortBy(function(element) { return element.tabIndex }).first();
 
     return firstByIndex ? firstByIndex : elements.find(function(element) {
-      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+      return /^(?:input|select|textarea)$/i.test(element.tagName);
     });
   },
 
   focusFirstElement: function(form) {
     form = $(form);
-    form.findFirstElement().activate();
+    var element = form.findFirstElement();
+    if (element) element.activate();
     return form;
   },
 
@@ -3557,6 +5090,7 @@
 
 /*--------------------------------------------------------------------------*/
 
+
 Form.Element = {
   focus: function(element) {
     $(element).focus();
@@ -3570,6 +5104,7 @@
 };
 
 Form.Element.Methods = {
+
   serialize: function(element) {
     element = $(element);
     if (!element.disabled && element.name) {
@@ -3610,7 +5145,7 @@
     try {
       element.focus();
       if (element.select && (element.tagName.toLowerCase() != 'input' ||
-          !['button', 'reset', 'submit'].include(element.type)))
+          !(/^(?:button|reset|submit)$/i.test(element.type))))
         element.select();
     } catch (e) { }
     return element;
@@ -3632,75 +5167,86 @@
 /*--------------------------------------------------------------------------*/
 
 var Field = Form.Element;
+
 var $F = Form.Element.Methods.getValue;
 
 /*--------------------------------------------------------------------------*/
 
-Form.Element.Serializers = {
-  input: function(element, value) {
+Form.Element.Serializers = (function() {
+  function input(element, value) {
     switch (element.type.toLowerCase()) {
       case 'checkbox':
       case 'radio':
-        return Form.Element.Serializers.inputSelector(element, value);
+        return inputSelector(element, value);
       default:
-        return Form.Element.Serializers.textarea(element, value);
+        return valueSelector(element, value);
     }
-  },
+  }
 
-  inputSelector: function(element, value) {
-    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+  function inputSelector(element, value) {
+    if (Object.isUndefined(value))
+      return element.checked ? element.value : null;
     else element.checked = !!value;
-  },
+  }
 
-  textarea: function(element, value) {
+  function valueSelector(element, value) {
     if (Object.isUndefined(value)) return element.value;
     else element.value = value;
-  },
+  }
 
-  select: function(element, value) {
+  function select(element, value) {
     if (Object.isUndefined(value))
-      return this[element.type == 'select-one' ?
-        'selectOne' : 'selectMany'](element);
-    else {
-      var opt, currentValue, single = !Object.isArray(value);
-      for (var i = 0, length = element.length; i < length; i++) {
-        opt = element.options[i];
-        currentValue = this.optionValue(opt);
-        if (single) {
-          if (currentValue == value) {
-            opt.selected = true;
-            return;
-          }
+      return (element.type === 'select-one' ? selectOne : selectMany)(element);
+
+    var opt, currentValue, single = !Object.isArray(value);
+    for (var i = 0, length = element.length; i < length; i++) {
+      opt = element.options[i];
+      currentValue = this.optionValue(opt);
+      if (single) {
+        if (currentValue == value) {
+          opt.selected = true;
+          return;
         }
-        else opt.selected = value.include(currentValue);
       }
+      else opt.selected = value.include(currentValue);
     }
-  },
+  }
 
-  selectOne: function(element) {
+  function selectOne(element) {
     var index = element.selectedIndex;
-    return index >= 0 ? this.optionValue(element.options[index]) : null;
-  },
+    return index >= 0 ? optionValue(element.options[index]) : null;
+  }
 
-  selectMany: function(element) {
+  function selectMany(element) {
     var values, length = element.length;
     if (!length) return null;
 
     for (var i = 0, values = []; i < length; i++) {
       var opt = element.options[i];
-      if (opt.selected) values.push(this.optionValue(opt));
+      if (opt.selected) values.push(optionValue(opt));
     }
     return values;
-  },
-
-  optionValue: function(opt) {
-    // extend element because hasAttribute may not be native
-    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
   }
-};
+
+  function optionValue(opt) {
+    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
+  }
+
+  return {
+    input:         input,
+    inputSelector: inputSelector,
+    textarea:      valueSelector,
+    select:        select,
+    selectOne:     selectOne,
+    selectMany:    selectMany,
+    optionValue:   optionValue,
+    button:        valueSelector
+  };
+})();
 
 /*--------------------------------------------------------------------------*/
 
+
 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
   initialize: function($super, element, frequency, callback) {
     $super(callback, frequency);
@@ -3782,354 +5328,527 @@
     return Form.serialize(this.element);
   }
 });
-if (!window.Event) var Event = { };
+(function() {
 
-Object.extend(Event, {
-  KEY_BACKSPACE: 8,
-  KEY_TAB:       9,
-  KEY_RETURN:   13,
-  KEY_ESC:      27,
-  KEY_LEFT:     37,
-  KEY_UP:       38,
-  KEY_RIGHT:    39,
-  KEY_DOWN:     40,
-  KEY_DELETE:   46,
-  KEY_HOME:     36,
-  KEY_END:      35,
-  KEY_PAGEUP:   33,
-  KEY_PAGEDOWN: 34,
-  KEY_INSERT:   45,
+  var Event = {
+    KEY_BACKSPACE: 8,
+    KEY_TAB:       9,
+    KEY_RETURN:   13,
+    KEY_ESC:      27,
+    KEY_LEFT:     37,
+    KEY_UP:       38,
+    KEY_RIGHT:    39,
+    KEY_DOWN:     40,
+    KEY_DELETE:   46,
+    KEY_HOME:     36,
+    KEY_END:      35,
+    KEY_PAGEUP:   33,
+    KEY_PAGEDOWN: 34,
+    KEY_INSERT:   45,
 
-  cache: { },
-
-  relatedTarget: function(event) {
-    var element;
-    switch(event.type) {
-      case 'mouseover': element = event.fromElement; break;
-      case 'mouseout':  element = event.toElement;   break;
-      default: return null;
-    }
-    return Element.extend(element);
-  }
-});
-
-Event.Methods = (function() {
-  var isButton;
-
-  if (Prototype.Browser.IE) {
-    var buttonMap = { 0: 1, 1: 4, 2: 2 };
-    isButton = function(event, code) {
-      return event.button == buttonMap[code];
-    };
-
-  } else if (Prototype.Browser.WebKit) {
-    isButton = function(event, code) {
-      switch (code) {
-        case 0: return event.which == 1 && !event.metaKey;
-        case 1: return event.which == 1 && event.metaKey;
-        default: return false;
-      }
-    };
-
-  } else {
-    isButton = function(event, code) {
-      return event.which ? (event.which === code + 1) : (event.button === code);
-    };
-  }
-
-  return {
-    isLeftClick:   function(event) { return isButton(event, 0) },
-    isMiddleClick: function(event) { return isButton(event, 1) },
-    isRightClick:  function(event) { return isButton(event, 2) },
-
-    element: function(event) {
-      event = Event.extend(event);
-
-      var node          = event.target,
-          type          = event.type,
-          currentTarget = event.currentTarget;
-
-      if (currentTarget && currentTarget.tagName) {
-        // Firefox screws up the "click" event when moving between radio buttons
-        // via arrow keys. It also screws up the "load" and "error" events on images,
-        // reporting the document as the target instead of the original image.
-        if (type === 'load' || type === 'error' ||
-          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
-            && currentTarget.type === 'radio'))
-              node = currentTarget;
-      }
-      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
-      return Element.extend(node);
-    },
-
-    findElement: function(event, expression) {
-      var element = Event.element(event);
-      if (!expression) return element;
-      var elements = [element].concat(element.ancestors());
-      return Selector.findElement(elements, expression, 0);
-    },
-
-    pointer: function(event) {
-      var docElement = document.documentElement,
-      body = document.body || { scrollLeft: 0, scrollTop: 0 };
-      return {
-        x: event.pageX || (event.clientX +
-          (docElement.scrollLeft || body.scrollLeft) -
-          (docElement.clientLeft || 0)),
-        y: event.pageY || (event.clientY +
-          (docElement.scrollTop || body.scrollTop) -
-          (docElement.clientTop || 0))
-      };
-    },
-
-    pointerX: function(event) { return Event.pointer(event).x },
-    pointerY: function(event) { return Event.pointer(event).y },
-
-    stop: function(event) {
-      Event.extend(event);
-      event.preventDefault();
-      event.stopPropagation();
-      event.stopped = true;
-    }
+    cache: {}
   };
-})();
 
-Event.extend = (function() {
+  var docEl = document.documentElement;
+  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
+    && 'onmouseleave' in docEl;
+
+
+
+  var isIELegacyEvent = function(event) { return false; };
+
+  if (window.attachEvent) {
+    if (window.addEventListener) {
+      isIELegacyEvent = function(event) {
+        return !(event instanceof window.Event);
+      };
+    } else {
+      isIELegacyEvent = function(event) { return true; };
+    }
+  }
+
+  var _isButton;
+
+  function _isButtonForDOMEvents(event, code) {
+    return event.which ? (event.which === code + 1) : (event.button === code);
+  }
+
+  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
+  function _isButtonForLegacyEvents(event, code) {
+    return event.button === legacyButtonMap[code];
+  }
+
+  function _isButtonForWebKit(event, code) {
+    switch (code) {
+      case 0: return event.which == 1 && !event.metaKey;
+      case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
+      case 2: return event.which == 3;
+      default: return false;
+    }
+  }
+
+  if (window.attachEvent) {
+    if (!window.addEventListener) {
+      _isButton = _isButtonForLegacyEvents;
+    } else {
+      _isButton = function(event, code) {
+        return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
+         _isButtonForDOMEvents(event, code);
+      }
+    }
+  } else if (Prototype.Browser.WebKit) {
+    _isButton = _isButtonForWebKit;
+  } else {
+    _isButton = _isButtonForDOMEvents;
+  }
+
+  function isLeftClick(event)   { return _isButton(event, 0) }
+
+  function isMiddleClick(event) { return _isButton(event, 1) }
+
+  function isRightClick(event)  { return _isButton(event, 2) }
+
+  function element(event) {
+    event = Event.extend(event);
+
+    var node = event.target, type = event.type,
+     currentTarget = event.currentTarget;
+
+    if (currentTarget && currentTarget.tagName) {
+      if (type === 'load' || type === 'error' ||
+        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+          && currentTarget.type === 'radio'))
+            node = currentTarget;
+    }
+
+    if (node.nodeType == Node.TEXT_NODE)
+      node = node.parentNode;
+
+    return Element.extend(node);
+  }
+
+  function findElement(event, expression) {
+    var element = Event.element(event);
+
+    if (!expression) return element;
+    while (element) {
+      if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
+        return Element.extend(element);
+      }
+      element = element.parentNode;
+    }
+  }
+
+  function pointer(event) {
+    return { x: pointerX(event), y: pointerY(event) };
+  }
+
+  function pointerX(event) {
+    var docElement = document.documentElement,
+     body = document.body || { scrollLeft: 0 };
+
+    return event.pageX || (event.clientX +
+      (docElement.scrollLeft || body.scrollLeft) -
+      (docElement.clientLeft || 0));
+  }
+
+  function pointerY(event) {
+    var docElement = document.documentElement,
+     body = document.body || { scrollTop: 0 };
+
+    return  event.pageY || (event.clientY +
+       (docElement.scrollTop || body.scrollTop) -
+       (docElement.clientTop || 0));
+  }
+
+
+  function stop(event) {
+    Event.extend(event);
+    event.preventDefault();
+    event.stopPropagation();
+
+    event.stopped = true;
+  }
+
+
+  Event.Methods = {
+    isLeftClick:   isLeftClick,
+    isMiddleClick: isMiddleClick,
+    isRightClick:  isRightClick,
+
+    element:     element,
+    findElement: findElement,
+
+    pointer:  pointer,
+    pointerX: pointerX,
+    pointerY: pointerY,
+
+    stop: stop
+  };
+
   var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
     m[name] = Event.Methods[name].methodize();
     return m;
   });
 
-  if (Prototype.Browser.IE) {
-    Object.extend(methods, {
+  if (window.attachEvent) {
+    function _relatedTarget(event) {
+      var element;
+      switch (event.type) {
+        case 'mouseover':
+        case 'mouseenter':
+          element = event.fromElement;
+          break;
+        case 'mouseout':
+        case 'mouseleave':
+          element = event.toElement;
+          break;
+        default:
+          return null;
+      }
+      return Element.extend(element);
+    }
+
+    var additionalMethods = {
       stopPropagation: function() { this.cancelBubble = true },
       preventDefault:  function() { this.returnValue = false },
-      inspect: function() { return "[object Event]" }
-    });
+      inspect: function() { return '[object Event]' }
+    };
 
-    return function(event) {
+    Event.extend = function(event, element) {
       if (!event) return false;
-      if (event._extendedByPrototype) return event;
 
+      if (!isIELegacyEvent(event)) return event;
+
+      if (event._extendedByPrototype) return event;
       event._extendedByPrototype = Prototype.emptyFunction;
+
       var pointer = Event.pointer(event);
+
       Object.extend(event, {
-        target: event.srcElement,
-        relatedTarget: Event.relatedTarget(event),
+        target: event.srcElement || element,
+        relatedTarget: _relatedTarget(event),
         pageX:  pointer.x,
         pageY:  pointer.y
       });
-      return Object.extend(event, methods);
+
+      Object.extend(event, methods);
+      Object.extend(event, additionalMethods);
+
+      return event;
     };
-
   } else {
-    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
+    Event.extend = Prototype.K;
+  }
+
+  if (window.addEventListener) {
+    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
     Object.extend(Event.prototype, methods);
-    return Prototype.K;
-  }
-})();
-
-Object.extend(Event, (function() {
-  var cache = Event.cache;
-
-  function getEventID(element) {
-    if (element._prototypeEventID) return element._prototypeEventID[0];
-    arguments.callee.id = arguments.callee.id || 1;
-    return element._prototypeEventID = [++arguments.callee.id];
   }
 
-  function getDOMEventName(eventName) {
-    if (eventName && eventName.include(':')) return "dataavailable";
-    return eventName;
-  }
+  function _createResponder(element, eventName, handler) {
+    var registry = Element.retrieve(element, 'prototype_event_registry');
 
-  function getCacheForID(id) {
-    return cache[id] = cache[id] || { };
-  }
+    if (Object.isUndefined(registry)) {
+      CACHE.push(element);
+      registry = Element.retrieve(element, 'prototype_event_registry', $H());
+    }
 
-  function getWrappersForEventName(id, eventName) {
-    var c = getCacheForID(id);
-    return c[eventName] = c[eventName] || [];
-  }
+    var respondersForEvent = registry.get(eventName);
+    if (Object.isUndefined(respondersForEvent)) {
+      respondersForEvent = [];
+      registry.set(eventName, respondersForEvent);
+    }
 
-  function createWrapper(element, eventName, handler) {
-    var id = getEventID(element);
-    var c = getWrappersForEventName(id, eventName);
-    if (c.pluck("handler").include(handler)) return false;
+    if (respondersForEvent.pluck('handler').include(handler)) return false;
 
-    var wrapper = function(event) {
-      if (!Event || !Event.extend ||
-        (event.eventName && event.eventName != eventName))
+    var responder;
+    if (eventName.include(":")) {
+      responder = function(event) {
+        if (Object.isUndefined(event.eventName))
           return false;
 
-      Event.extend(event);
-      handler.call(element, event);
-    };
+        if (event.eventName !== eventName)
+          return false;
 
-    wrapper.handler = handler;
-    c.push(wrapper);
-    return wrapper;
-  }
+        Event.extend(event, element);
+        handler.call(element, event);
+      };
+    } else {
+      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
+       (eventName === "mouseenter" || eventName === "mouseleave")) {
+        if (eventName === "mouseenter" || eventName === "mouseleave") {
+          responder = function(event) {
+            Event.extend(event, element);
 
-  function findWrapper(id, eventName, handler) {
-    var c = getWrappersForEventName(id, eventName);
-    return c.find(function(wrapper) { return wrapper.handler == handler });
-  }
+            var parent = event.relatedTarget;
+            while (parent && parent !== element) {
+              try { parent = parent.parentNode; }
+              catch(e) { parent = element; }
+            }
 
-  function destroyWrapper(id, eventName, handler) {
-    var c = getCacheForID(id);
-    if (!c[eventName]) return false;
-    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
-  }
+            if (parent === element) return;
 
-  function destroyCache() {
-    for (var id in cache)
-      for (var eventName in cache[id])
-        cache[id][eventName] = null;
-  }
-
-
-  // Internet Explorer needs to remove event handlers on page unload
-  // in order to avoid memory leaks.
-  if (window.attachEvent) {
-    window.attachEvent("onunload", destroyCache);
-  }
-
-  // Safari has a dummy event handler on page unload so that it won't
-  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
-  // object when page is returned to via the back button using its bfcache.
-  if (Prototype.Browser.WebKit) {
-    window.addEventListener('unload', Prototype.emptyFunction, false);
-  }
-
-  return {
-    observe: function(element, eventName, handler) {
-      element = $(element);
-      var name = getDOMEventName(eventName);
-
-      var wrapper = createWrapper(element, eventName, handler);
-      if (!wrapper) return element;
-
-      if (element.addEventListener) {
-        element.addEventListener(name, wrapper, false);
+            handler.call(element, event);
+          };
+        }
       } else {
-        element.attachEvent("on" + name, wrapper);
+        responder = function(event) {
+          Event.extend(event, element);
+          handler.call(element, event);
+        };
       }
-
-      return element;
-    },
-
-    stopObserving: function(element, eventName, handler) {
-      element = $(element);
-      var id = getEventID(element), name = getDOMEventName(eventName);
-
-      if (!handler && eventName) {
-        getWrappersForEventName(id, eventName).each(function(wrapper) {
-          element.stopObserving(eventName, wrapper.handler);
-        });
-        return element;
-
-      } else if (!eventName) {
-        Object.keys(getCacheForID(id)).each(function(eventName) {
-          element.stopObserving(eventName);
-        });
-        return element;
-      }
-
-      var wrapper = findWrapper(id, eventName, handler);
-      if (!wrapper) return element;
-
-      if (element.removeEventListener) {
-        element.removeEventListener(name, wrapper, false);
-      } else {
-        element.detachEvent("on" + name, wrapper);
-      }
-
-      destroyWrapper(id, eventName, handler);
-
-      return element;
-    },
-
-    fire: function(element, eventName, memo) {
-      element = $(element);
-      if (element == document && document.createEvent && !element.dispatchEvent)
-        element = document.documentElement;
-
-      var event;
-      if (document.createEvent) {
-        event = document.createEvent("HTMLEvents");
-        event.initEvent("dataavailable", true, true);
-      } else {
-        event = document.createEventObject();
-        event.eventType = "ondataavailable";
-      }
-
-      event.eventName = eventName;
-      event.memo = memo || { };
-
-      if (document.createEvent) {
-        element.dispatchEvent(event);
-      } else {
-        element.fireEvent(event.eventType, event);
-      }
-
-      return Event.extend(event);
     }
-  };
-})());
 
-Object.extend(Event, Event.Methods);
+    responder.handler = handler;
+    respondersForEvent.push(responder);
+    return responder;
+  }
 
-Element.addMethods({
-  fire:          Event.fire,
-  observe:       Event.observe,
-  stopObserving: Event.stopObserving
-});
+  function _destroyCache() {
+    for (var i = 0, length = CACHE.length; i < length; i++) {
+      Event.stopObserving(CACHE[i]);
+      CACHE[i] = null;
+    }
+  }
 
-Object.extend(document, {
-  fire:          Element.Methods.fire.methodize(),
-  observe:       Element.Methods.observe.methodize(),
-  stopObserving: Element.Methods.stopObserving.methodize(),
-  loaded:        false
-});
+  var CACHE = [];
+
+  if (Prototype.Browser.IE)
+    window.attachEvent('onunload', _destroyCache);
+
+  if (Prototype.Browser.WebKit)
+    window.addEventListener('unload', Prototype.emptyFunction, false);
+
+
+  var _getDOMEventName = Prototype.K,
+      translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
+
+  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
+    _getDOMEventName = function(eventName) {
+      return (translations[eventName] || eventName);
+    };
+  }
+
+  function observe(element, eventName, handler) {
+    element = $(element);
+
+    var responder = _createResponder(element, eventName, handler);
+
+    if (!responder) return element;
+
+    if (eventName.include(':')) {
+      if (element.addEventListener)
+        element.addEventListener("dataavailable", responder, false);
+      else {
+        element.attachEvent("ondataavailable", responder);
+        element.attachEvent("onlosecapture", responder);
+      }
+    } else {
+      var actualEventName = _getDOMEventName(eventName);
+
+      if (element.addEventListener)
+        element.addEventListener(actualEventName, responder, false);
+      else
+        element.attachEvent("on" + actualEventName, responder);
+    }
+
+    return element;
+  }
+
+  function stopObserving(element, eventName, handler) {
+    element = $(element);
+
+    var registry = Element.retrieve(element, 'prototype_event_registry');
+    if (!registry) return element;
+
+    if (!eventName) {
+      registry.each( function(pair) {
+        var eventName = pair.key;
+        stopObserving(element, eventName);
+      });
+      return element;
+    }
+
+    var responders = registry.get(eventName);
+    if (!responders) return element;
+
+    if (!handler) {
+      responders.each(function(r) {
+        stopObserving(element, eventName, r.handler);
+      });
+      return element;
+    }
+
+    var i = responders.length, responder;
+    while (i--) {
+      if (responders[i].handler === handler) {
+        responder = responders[i];
+        break;
+      }
+    }
+    if (!responder) return element;
+
+    if (eventName.include(':')) {
+      if (element.removeEventListener)
+        element.removeEventListener("dataavailable", responder, false);
+      else {
+        element.detachEvent("ondataavailable", responder);
+        element.detachEvent("onlosecapture", responder);
+      }
+    } else {
+      var actualEventName = _getDOMEventName(eventName);
+      if (element.removeEventListener)
+        element.removeEventListener(actualEventName, responder, false);
+      else
+        element.detachEvent('on' + actualEventName, responder);
+    }
+
+    registry.set(eventName, responders.without(responder));
+
+    return element;
+  }
+
+  function fire(element, eventName, memo, bubble) {
+    element = $(element);
+
+    if (Object.isUndefined(bubble))
+      bubble = true;
+
+    if (element == document && document.createEvent && !element.dispatchEvent)
+      element = document.documentElement;
+
+    var event;
+    if (document.createEvent) {
+      event = document.createEvent('HTMLEvents');
+      event.initEvent('dataavailable', bubble, true);
+    } else {
+      event = document.createEventObject();
+      event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
+    }
+
+    event.eventName = eventName;
+    event.memo = memo || { };
+
+    if (document.createEvent)
+      element.dispatchEvent(event);
+    else
+      element.fireEvent(event.eventType, event);
+
+    return Event.extend(event);
+  }
+
+  Event.Handler = Class.create({
+    initialize: function(element, eventName, selector, callback) {
+      this.element   = $(element);
+      this.eventName = eventName;
+      this.selector  = selector;
+      this.callback  = callback;
+      this.handler   = this.handleEvent.bind(this);
+    },
+
+    start: function() {
+      Event.observe(this.element, this.eventName, this.handler);
+      return this;
+    },
+
+    stop: function() {
+      Event.stopObserving(this.element, this.eventName, this.handler);
+      return this;
+    },
+
+    handleEvent: function(event) {
+      var element = Event.findElement(event, this.selector);
+      if (element) this.callback.call(this.element, event, element);
+    }
+  });
+
+  function on(element, eventName, selector, callback) {
+    element = $(element);
+    if (Object.isFunction(selector) && Object.isUndefined(callback)) {
+      callback = selector, selector = null;
+    }
+
+    return new Event.Handler(element, eventName, selector, callback).start();
+  }
+
+  Object.extend(Event, Event.Methods);
+
+  Object.extend(Event, {
+    fire:          fire,
+    observe:       observe,
+    stopObserving: stopObserving,
+    on:            on
+  });
+
+  Element.addMethods({
+    fire:          fire,
+
+    observe:       observe,
+
+    stopObserving: stopObserving,
+
+    on:            on
+  });
+
+  Object.extend(document, {
+    fire:          fire.methodize(),
+
+    observe:       observe.methodize(),
+
+    stopObserving: stopObserving.methodize(),
+
+    on:            on.methodize(),
+
+    loaded:        false
+  });
+
+  if (window.Event) Object.extend(window.Event, Event);
+  else window.Event = Event;
+})();
 
 (function() {
   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
-     Matthias Miller, Dean Edwards and John Resig. */
+     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
 
   var timer;
 
   function fireContentLoadedEvent() {
     if (document.loaded) return;
-    if (timer) window.clearInterval(timer);
-    document.fire("dom:loaded");
+    if (timer) window.clearTimeout(timer);
     document.loaded = true;
+    document.fire('dom:loaded');
+  }
+
+  function checkReadyState() {
+    if (document.readyState === 'complete') {
+      document.stopObserving('readystatechange', checkReadyState);
+      fireContentLoadedEvent();
+    }
+  }
+
+  function pollDoScroll() {
+    try { document.documentElement.doScroll('left'); }
+    catch(e) {
+      timer = pollDoScroll.defer();
+      return;
+    }
+    fireContentLoadedEvent();
   }
 
   if (document.addEventListener) {
-    if (Prototype.Browser.WebKit) {
-      timer = window.setInterval(function() {
-        if (/loaded|complete/.test(document.readyState))
-          fireContentLoadedEvent();
-      }, 0);
-
-      Event.observe(window, "load", fireContentLoadedEvent);
-
-    } else {
-      document.addEventListener("DOMContentLoaded",
-        fireContentLoadedEvent, false);
-    }
-
+    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
   } else {
-    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
-    $("__onDOMContentLoaded").onreadystatechange = function() {
-      if (this.readyState == "complete") {
-        this.onreadystatechange = null;
-        fireContentLoadedEvent();
-      }
-    };
+    document.observe('readystatechange', checkReadyState);
+    if (window == top)
+      timer = pollDoScroll.defer();
   }
+
+  Event.observe(window, 'load', fireContentLoadedEvent);
 })();
+
+Element.addMethods();
+
 /*------------------------------- DEPRECATED -------------------------------*/
 
 Hash.toQueryString = Object.toQueryString;
@@ -4158,16 +5877,9 @@
 
 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
 
-// This should be moved to script.aculo.us; notice the deprecated methods
-// further below, that map to the newer Element methods.
 var Position = {
-  // set to true if needed, warning: firefox performance problems
-  // NOT neeeded for page scrolling, only if draggable contained in
-  // scrollable elements
   includeScrollOffsets: false,
 
-  // must be called before calling withinIncludingScrolloffset, every time the
-  // page is scrolled
   prepare: function() {
     this.deltaX =  window.pageXOffset
                 || document.documentElement.scrollLeft
@@ -4179,7 +5891,6 @@
                 || 0;
   },
 
-  // caches x/y coordinate pair to use with overlap
   within: function(element, x, y) {
     if (this.includeScrollOffsets)
       return this.withinIncludingScrolloffsets(element, x, y);
@@ -4206,7 +5917,6 @@
             this.xcomp <  this.offset[0] + element.offsetWidth);
   },
 
-  // within must be called directly before
   overlap: function(mode, element) {
     if (!mode) return 0;
     if (mode == 'vertical')
@@ -4217,7 +5927,6 @@
         element.offsetWidth;
   },
 
-  // Deprecation layer -- use newer Element methods now (1.5.2).
 
   cumulativeOffset: Element.Methods.cumulativeOffset,
 
@@ -4317,4 +6026,57 @@
 
 /*--------------------------------------------------------------------------*/
 
-Element.addMethods();
\ No newline at end of file
+(function() {
+  window.Selector = Class.create({
+    initialize: function(expression) {
+      this.expression = expression.strip();
+    },
+
+    findElements: function(rootElement) {
+      return Prototype.Selector.select(this.expression, rootElement);
+    },
+
+    match: function(element) {
+      return Prototype.Selector.match(element, this.expression);
+    },
+
+    toString: function() {
+      return this.expression;
+    },
+
+    inspect: function() {
+      return "#<Selector: " + this.expression + ">";
+    }
+  });
+
+  Object.extend(Selector, {
+    matchElements: function(elements, expression) {
+      var match = Prototype.Selector.match,
+          results = [];
+
+      for (var i = 0, length = elements.length; i < length; i++) {
+        var element = elements[i];
+        if (match(element, expression)) {
+          results.push(Element.extend(element));
+        }
+      }
+      return results;
+    },
+
+    findElement: function(elements, expression, index) {
+      index = index || 0;
+      var matchIndex = 0, element;
+      for (var i = 0, length = elements.length; i < length; i++) {
+        element = elements[i];
+        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
+          return Element.extend(element);
+        }
+      }
+    },
+
+    findChildElements: function(element, expressions) {
+      var selector = expressions.toArray().join(', ');
+      return Prototype.Selector.select(selector, element || document);
+    }
+  });
+})();
diff --git a/chrome/test/data/dromaeo/patches/librefs.patch b/chrome/test/data/dromaeo/patches/librefs.patch
new file mode 100644
index 0000000..734c2b1
--- /dev/null
+++ b/chrome/test/data/dromaeo/patches/librefs.patch
@@ -0,0 +1,318 @@
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-dojo.html b/chrome/test/data/dromaeo/tests/cssquery-dojo.html
+index b27c95b..fd8e4aa 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-dojo.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-dojo.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"></script>
++<script src="../lib/dojo.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-dojo", '5477d2ae');
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-ext.html b/chrome/test/data/dromaeo/tests/cssquery-ext.html
+index acb35c1..11a8413 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-ext.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-ext.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/ext-core/3.1.0/ext-core.js"></script>
++<script src="../lib/ext-core.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-ext", '5bc05a17');
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html b/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html
+index 6b95fee..b180f5a 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-jquery", '5dfe1a12');
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-jquery.html b/chrome/test/data/dromaeo/tests/cssquery-jquery.html
+index a83ef5c..6e552f1 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-jquery.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-jquery", 'de808e5f');
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-mootools.html b/chrome/test/data/dromaeo/tests/cssquery-mootools.html
+index 5009efc..1422b2a 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-mootools.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-mootools.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.4.1/mootools-yui-compressed.js"></script>
++<script src="../lib/mootools-yui-compressed.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-mootools", 'c5ac656b');
+diff --git a/chrome/test/data/dromaeo/tests/cssquery-prototype.html b/chrome/test/data/dromaeo/tests/cssquery-prototype.html
+index 324d4ed..28bada1 100644
+--- a/chrome/test/data/dromaeo/tests/cssquery-prototype.html
++++ b/chrome/test/data/dromaeo/tests/cssquery-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("cssquery-prototype", 'd151161f');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html
+index ca6d338..867938a 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-attr-jquery", 'aeb3b2c1');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html b/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
+index 70f8fdc..f089b52 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
++++ b/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-attr-jquery", '675a354f');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html b/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
+index c0e8006..998b990 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
++++ b/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-attr-prototype", '4fb93c6a');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html
+index 4e5abef..db65510 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-event-jquery", '161a447c');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-event-jquery.html b/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
+index 22ae395..bfaf354 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
++++ b/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-event-jquery", '690f881e');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-event-prototype.html b/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
+index c601a89..78d4626 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
++++ b/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-event-prototype", '03b86d96');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html
+index d03d56b..2c8323d 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-modify-jquery", 'ed2c7ef2');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html b/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
+index 12c6a35..34fa179 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
++++ b/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-modify-jquery", '950aaaa1');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html b/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
+index e5e2a4d..866fd35 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
++++ b/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-modify-prototype", '6693b3a5');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html
+index ce477b8..72c19c6 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-style-jquery", '08bc6988');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-style-jquery.html b/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
+index 35697967..3c99a70 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
++++ b/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-style-jquery", 'a803790a');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-style-prototype.html b/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
+index 2762401..9ba6e48 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
++++ b/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-style-prototype", 'c41174f0');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html
+index afc5b3d6..9e9792c 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html
++++ b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
++<script src="../lib/jquery.2.0.3.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-traverse-jquery", '66e690c0');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
+index 910e2e6..19b6741 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
++++ b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
++<script src="../lib/jquery.10.1.2.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-traverse-jquery", 'bc5b18f3');
+diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html b/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
+index 37c28d6..68fe3bc 100644
+--- a/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
++++ b/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
+@@ -1,7 +1,7 @@
+ <html>
+ <head>
+ <script src="../htmlrunner.js"></script>
+-<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
++<script src="../lib/prototype.js"></script>
+ <script>
+ window.onload = function(){
+ startTest("jslib-traverse-prototype", 'd46d3eab');
+-- 
+2.8.0.rc3.226.g39d4020
+
+
+From 677579e646af497d7e1625c85d8a7cffaf62aa13 Mon Sep 17 00:00:00 2001
+From: David Vallet <dvallet@google.com>
+Date: Thu, 17 Nov 2016 16:38:01 +1100
+Subject: [PATCH 2/4] change readme
+
+---
+ chrome/test/data/dromaeo/README.chromium | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/chrome/test/data/dromaeo/README.chromium b/chrome/test/data/dromaeo/README.chromium
+index 8f036bf..c57c79f 100644
+--- a/chrome/test/data/dromaeo/README.chromium
++++ b/chrome/test/data/dromaeo/README.chromium
+@@ -2,7 +2,10 @@ This directory contains Dromaeo, a JavaScript and DOM performance benchmark.
+ 
+ Dromaeo was developed and released by John Resig (jresig at mozilla.com)/
+ 
+-This benchmark was slightly modified to enable Chromium automation.
++This benchmark was slightly modified to: 
++ - Enable Chromium automation.
++ - Avoid grabbing javascript libraries from the Web.
++
+ 
+ How to update:
+ 
+@@ -11,3 +14,4 @@ How to update:
+ directory;
+ 2) run 'make web' to build standalone version;
+ 3) copy all the files from web dir into this directory.
++4) modify links to libraries and keep automation changes.
+-- 
+2.8.0.rc3.226.g39d4020
+
+
+From cb8e4fa9f308c17dada9e7aefb6122429cf61654 Mon Sep 17 00:00:00 2001
+From: David Vallet <dvallet@google.com>
+Date: Thu, 17 Nov 2016 16:41:15 +1100
+Subject: [PATCH 3/4] added fix for median calcualtion
+-- 
+2.8.0.rc3.226.g39d4020
+
diff --git a/chrome/test/data/dromaeo/patches/webrunner.patch b/chrome/test/data/dromaeo/patches/webrunner.patch
new file mode 100644
index 0000000..a273425
--- /dev/null
+++ b/chrome/test/data/dromaeo/patches/webrunner.patch
@@ -0,0 +1,156 @@
+diff --git a/chrome/test/data/dromaeo/webrunner.js b/chrome/test/data/dromaeo/webrunner.js
+index 63d777b..598998b 100644
+--- a/chrome/test/data/dromaeo/webrunner.js
++++ b/chrome/test/data/dromaeo/webrunner.js
+@@ -230,6 +230,8 @@
+ 	var time = 0;
+ 	var title, testName, testID, testSummary = {} , testSummaryNum = {}, maxTotal = 0, maxTotalNum = 0;
+ 	var nameDone = {};
++	var automated = false;
++	var post_json = false;
+ 	
+ 	// Query String Parsing
+ 	var search = window.limitSearch || (window.location.search || "?").substr(1);
+@@ -269,6 +271,11 @@
+ 		m = /^numTests=(\d+)$/.exec(parts[i]);
+ 		if (m)
+ 			numTests = Number(m[1]);
++
++		if (/^automated$/.exec(parts[i]))
++			automated = true;
++		if (/^post_json$/.exec(parts[i]))
++			post_json = true;
+ 	}
+ 
+ 	jQuery(function(){
+@@ -377,17 +384,28 @@
+ 				}
+ 	
+ 			} else if ( dataStore && dataStore.length ) {
+-				$("body").addClass("alldone");
+-				var div = jQuery("<div class='results'>Saving...</div>").insertBefore("#overview");
+-				jQuery.ajax({
+-					type: "POST",
+-					url: "store.php",
+-					data: "data=" + encodeURIComponent(JSON.stringify(dataStore)) + "&style=" + runStyle,
+-					success: function(id){
+-						var url = window.location.href.replace(/\?.*$/, "") + "?id=" + id;
+-						div.html("Results saved. You can access them at a later time at the following URL:<br/><strong><a href='" + url + "'>" + url + "</a></strong></div>");
+-					}
+-				});
++				if (!automated) {
++					$("body").addClass("alldone");
++					var div = jQuery("<div class='results'>Saving...</div>").insertBefore("#overview");
++					jQuery.ajax({
++						type: "POST",
++						url: "store.php",
++						data: "data=" + encodeURIComponent(JSON.stringify(dataStore)) + "&style=" + runStyle,
++						success: function(id){
++							var url = window.location.href.replace(/\?.*$/, "") + "?id=" + id;
++							div.html("Results saved. You can access them at a later time at the following URL:<br/><strong><a href='" + url + "'>" + url + "</a></strong></div>");
++						}
++					});
++				} else if (post_json) {
++					jQuery.ajax({
++						type: "POST",
++						url: "store.php",
++						data: "data=" + encodeURIComponent(JSON.stringify(window.automation.GetResults()))
++					});
++				}
++				else {
++					window.automation.SetDone();
++				}
+ 			}
+ 		}
+ 	}
+@@ -406,20 +424,28 @@
+ 		time += timePerTest;
+ 		updateTime();
+ 		
+-		$("#pause")
+-			.val("Run")
+-			.click(function(){
+-				if ( interval ) {
+-					interval = null;
+-					this.value = "Run";
+-				} else {
+-					if ( !interval ) {
+-						interval = true;
+-						dequeue();
++		if (!automated) {
++			$("#pause")
++				.val("Run")
++				.click(function(){
++					if ( interval ) {
++						interval = null;
++						this.value = "Run";
++					} else {
++						if ( !interval ) {
++							interval = true;
++							dequeue();
++						}
++						this.value = "Pause";
+ 					}
+-					this.value = "Pause";
+-				}
+-			});
++				});
++		} else {
++			$("#pause")
++				.val("Automated")
++				.click(function(){});
++			interval = true;
++			dequeue();
++		}
+ 
+ 		if ( window.limitSearch ) {
+ 			$("#pause").click();
+@@ -778,4 +804,45 @@
+ 
+ 		updateTestPos({curID: testID, collection: tests[testID] ? tests[testID].name : testID, version: testVersions[testID]}, true);
+ 	}
++
++	if (automated) {
++		// Add some more stuff if running in automated mode.
++		window.automation = {}
++		window.automation.SetDone = function() {
++			console.log("Total: " + this.GetScore());
++			window.document.cookie = "__done=1; path=/";
++		}
++		window.automation.GetScore = function() {
++			return (runStyle === "runs/s" ? Math.pow(Math.E, maxTotal / maxTotalNum) : maxTotal).toString();
++		}
++		window.automation.GetResults = function() {
++			var results = {};
++			var aggregated = {};
++			function normalizeName(name) {
++				// At least for ui_tests, dots are not allowed.
++				return name.replace(".", "_");
++			}
++			function appendToAggregated(name, value) {
++				name = normalizeName(name);
++				(aggregated[name] || (aggregated[name] = [])).push(Math.log(value));
++			}
++
++			for (var i = 0; i < dataStore.length; i++) {
++				var data = dataStore[i];
++				var topName = data.collection.split("-", 1)[0];
++				appendToAggregated(topName, data.mean);
++				appendToAggregated(data.collection, data.mean);
++				results[normalizeName(data.collection + "/" + data.name)] = data.mean.toString();
++			}
++
++			for (var name in aggregated) {
++				var means = aggregated[name];
++				var sum = 0;
++				for (var i = 0; i < means.length; i++) sum += means[i];
++				results[name] = Math.pow(Math.E, sum/means.length).toString();
++			}
++
++			return results;
++		}
++	}
+ })();
+-- 
+2.8.0.rc3.226.g39d4020
+
diff --git a/chrome/test/data/dromaeo/store.php b/chrome/test/data/dromaeo/store.php
index 4ebb5c3f..7d89525 100644
--- a/chrome/test/data/dromaeo/store.php
+++ b/chrome/test/data/dromaeo/store.php
@@ -25,59 +25,64 @@
 OTHER DEALINGS IN THE SOFTWARE.
 */
 
-	$server = 'mysql.dromaeo.com';
-	$user = 'dromaeo';
-	$pass = 'dromaeo';
+$server = 'mysql.dromaeo.com';
+$user = 'dromaeo';
+$pass = 'dromaeo';
 
-	require('JSON.php');
+require('JSON.php');
 
-	$json = new Services_JSON();
-        $sql = mysql_connect( $server, $user, $pass );
+$json = new Services_JSON();
+$sql = mysql_connect( $server, $user, $pass );
 
-        mysql_select_db( 'dromaeo' );
+mysql_select_db( 'dromaeo' );
 
-	$id = str_replace(';', "", $_REQUEST['id']);
+$id = preg_replace('/[^\d,]/', '', $_REQUEST['id']);
 
-	if ( $id ) {
-		$sets = array();
-		$ids = split(",", $id);
+if ( $id ) {
+	$sets = array();
+	$ids = split(",", $id);
 
-		foreach ($ids as $i) {
-			$query = mysql_query( "SELECT * FROM runs WHERE id=$i;" );
-			$data = mysql_fetch_assoc($query);
+	foreach ($ids as $i) {
+		$query = mysql_query( sprintf("SELECT * FROM runs WHERE id=%s;",
+			mysql_real_escape_string($i)));
+		$data = mysql_fetch_assoc($query);
+
+		$query = mysql_query( sprintf("SELECT * FROM results WHERE run_id=%s;",
+			mysql_real_escape_string($i)));
+		$results = array();
 	
-			$query = mysql_query( "SELECT * FROM results WHERE run_id=$i;" );
-			$results = array();
-		
-			while ( $row = mysql_fetch_assoc($query) ) {
-				array_push($results, $row);
-			}
-
-			$data['results'] = $results;
-			$data['ip'] = '';
-
-			array_push($sets, $data);
+		while ( $row = mysql_fetch_assoc($query) ) {
+			array_push($results, $row);
 		}
 
-		echo $json->encode($sets);
-	} else {
-		$data = $json->decode(str_replace('\\"', '"', $_REQUEST['data']));
+		$data['results'] = $results;
+		$data['ip'] = '';
 
-		if ( $data ) {
+		array_push($sets, $data);
+	}
+
+	echo $json->encode($sets);
+} else {
+	$data = $json->decode(str_replace('\\"', '"', $_REQUEST['data']));
+
+	if ( $data ) {
 		mysql_query( sprintf("INSERT into runs VALUES(NULL,'%s','%s',NOW(),'%s');",
-			$_SERVER['HTTP_USER_AGENT'], $_SERVER['REMOTE_ADDR'], str_replace(';', "", $_REQUEST['style'])) );
+			mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']),
+			mysql_real_escape_string($_SERVER['REMOTE_ADDR']),
+			mysql_real_escape_string(str_replace(';', "", $_REQUEST['style']))
+		));
 
 		$id = mysql_insert_id();
 
 		if ( $id ) {
 
-		foreach ($data as $row) {
-			mysql_query( sprintf("INSERT into results VALUES(NULL,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
-				$id, $row->collection, $row->version, $row->name, $row->scale, $row->median, $row->min, $row->max, $row->mean, $row->deviation, $row->runs) );
-		}
+			foreach ($data as $row) {
+				mysql_query( sprintf("INSERT into results VALUES(NULL,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');",
+					$id, $row->collection, $row->version, $row->name, $row->scale, $row->median, $row->min, $row->max, $row->mean, $row->deviation, $row->runs) );
+			}
 
-		echo $id;
-		}
+			echo $id;
 		}
 	}
+}
 ?>
diff --git a/chrome/test/data/dromaeo/tests/MANIFEST.json b/chrome/test/data/dromaeo/tests/MANIFEST.json
index 24d6376a..297cfc9 100644
--- a/chrome/test/data/dromaeo/tests/MANIFEST.json
+++ b/chrome/test/data/dromaeo/tests/MANIFEST.json
@@ -1,442 +1,442 @@
 {
 "dromaeo-3d-cube": {
-	file: "dromaeo-3d-cube.html",
-	name: "Rotating 3D Cube",
-	origin: ["Simon Speich", "http://www.speich.net/computer/moztesting/3d.htm"],
-	desc: "Rotating the individual pixels of a cube. No rendering done.",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["object", "array", "property", "math"]
+	"file": "dromaeo-3d-cube.html",
+	"name": "Rotating 3D Cube",
+	"origin": ["Simon Speich", "http://www.speich.net/computer/moztesting/3d.htm"],
+	"desc": "Rotating the individual pixels of a cube. No rendering done.",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["object", "array", "property", "math"]
 },
 "dromaeo-string-base64": {
-	file: "dromaeo-string-base64.html",
-	name: "Base 64 Encoding and Decoding",
-	origin: ["Mozilla", "http://mozilla.org/"],
-	desc: "Encode and decode a random string to base 64.",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["string","array","looping","bitops"]
+	"file": "dromaeo-string-base64.html",
+	"name": "Base 64 Encoding and Decoding",
+	"origin": ["Mozilla", "http://mozilla.org/"],
+	"desc": "Encode and decode a random string to base 64.",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["string","array","looping","bitops"]
 },
 "dromaeo-core-eval": {
-	file: "dromaeo-core-eval.html",
-	name: "Code Evaluation",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Microtests of code evaluation (eval, new Function).",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["eval","microtest"]
+	"file": "dromaeo-core-eval.html",
+	"name": "Code Evaluation",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Microtests of code evaluation (eval, new Function).",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["eval","microtest"]
 },
 "dromaeo-object-array": {
-	file: "dromaeo-object-array.html",
-	name: "Arrays",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Microtests of arrays (construction, methods, access).",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["array","microtest"]
+	"file": "dromaeo-object-array.html",
+	"name": "Arrays",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Microtests of arrays (construction, methods, access).",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["array","microtest"]
 },
 "dromaeo-object-regexp": {
-	file: "dromaeo-object-regexp.html",
-	name: "Regular Expressions",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Microtests of regular expressions (construction, matching, replace).",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["regexp","microtest"]
+	"file": "dromaeo-object-regexp.html",
+	"name": "Regular Expressions",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Microtests of regular expressions (construction, matching, replace).",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["regexp","microtest"]
 },
 "dromaeo-object-string": {
-	file: "dromaeo-object-string.html",
-	name: "Strings",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Microtests of strings (concatenation, methods).",
-	category: "Dromaeo JavaScript Tests",
-	tags: ["string","microtest"]
+	"file": "dromaeo-object-string.html",
+	"name": "Strings",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Microtests of strings (concatenation, methods).",
+	"category": "Dromaeo JavaScript Tests",
+	"tags": ["string","microtest"]
 },
 "v8-crypto": {
-	file: "v8-crypto.html",
-	name: "RSA Encryption/Decryption",
-	origin: ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
-	desc: "Encrypt a string and then decrypt it again using RSA.",
-	category: "V8 Benchmark Tests",
-	tags: ["looping","string","bitops"]
+	"file": "v8-crypto.html",
+	"name": "RSA Encryption/Decryption",
+	"origin": ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
+	"desc": "Encrypt a string and then decrypt it again using RSA.",
+	"category": "V8 Benchmark Tests",
+	"tags": ["looping","string","bitops"]
 },
 "v8-deltablue": {
-	file: "v8-deltablue.html",
-	name: "DeltaBlue Constraint Solving",
-	origin: ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
-	desc: "Computing a number of limitations on a set of values.",
-	category: "V8 Benchmark Tests",
-	tags: ["looping","functions"]
+	"file": "v8-deltablue.html",
+	"name": "DeltaBlue Constraint Solving",
+	"origin": ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
+	"desc": "Computing a number of limitations on a set of values.",
+	"category": "V8 Benchmark Tests",
+	"tags": ["looping","functions"]
 },
 "v8-earley-boyer": {
-	file: "v8-earley-boyer.html",
-	name: "String Parsing and Searching",
-	origin: ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
-	desc: "Tests for parsing languages and searching strings.",
-	category: "V8 Benchmark Tests",
-	tags: ["looping","functions","string"]
+	"file": "v8-earley-boyer.html",
+	"name": "String Parsing and Searching",
+	"origin": ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
+	"desc": "Tests for parsing languages and searching strings.",
+	"category": "V8 Benchmark Tests",
+	"tags": ["looping","functions","string"]
 },
 "v8-raytrace": {
-	file: "v8-raytrace.html",
-	name: "RayTracer",
-	origin: ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
-	desc: "Renders a scene using raytracing (no rendering done).",
-	category: "V8 Benchmark Tests",
-	tags: ["looping","functions","object"]
+	"file": "v8-raytrace.html",
+	"name": "RayTracer",
+	"origin": ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
+	"desc": "Renders a scene using raytracing (no rendering done).",
+	"category": "V8 Benchmark Tests",
+	"tags": ["looping","functions","object"]
 },
 "v8-richards": {
-	file: "v8-richards.html",
-	name: "Richards Benchmarks",
-	origin: ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
-	desc: "A series of benchmarks to test the quality of system implementation languages.",
-	category: "V8 Benchmark Tests",
-	tags: ["functions","object"]
+	"file": "v8-richards.html",
+	"name": "Richards Benchmarks",
+	"origin": ["V8 Benchmark", "http://code.google.com/apis/v8/benchmarks.html"],
+	"desc": "A series of benchmarks to test the quality of system implementation languages.",
+	"category": "V8 Benchmark Tests",
+	"tags": ["functions","object"]
 },
 "sunspider-3d-morph": {
-	file: "sunspider-3d-morph.html",
-	name: "3D Mesh Transformation",
-	origin: ["WebKit", "http://webkit.org/misc/morph.html"],
-	desc: "Transforming the points of a matrix. No visual output.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["array", "looping", "math"]
+	"file": "sunspider-3d-morph.html",
+	"name": "3D Mesh Transformation",
+	"origin": ["WebKit", "http://webkit.org/misc/morph.html"],
+	"desc": "Transforming the points of a matrix. No visual output.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["array", "looping", "math"]
 },
 "sunspider-3d-raytrace": {
-	file: "sunspider-3d-raytrace.html",
-	name: "3D Raytrace",
-	origin: ["Apple", "http://apple.com/"],
-	desc: "Rendering a scene using raytracing techniques. No visual output.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["array", "functions", "math"]
+	"file": "sunspider-3d-raytrace.html",
+	"name": "3D Raytrace",
+	"origin": ["Apple", "http://apple.com/"],
+	"desc": "Rendering a scene using raytracing techniques. No visual output.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["array", "functions", "math"]
 },
 "sunspider-access-binary-trees": {
-	file: "sunspider-access-binary-trees.html",
-	name: "Traversing Binary Trees",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Moving through an object representation of a binary tree.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["object", "recursion"]
+	"file": "sunspider-access-binary-trees.html",
+	"name": "Traversing Binary Trees",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Moving through an object representation of a binary tree.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["object", "recursion"]
 },
 "sunspider-access-fannkuch": {
-	file: "sunspider-access-fannkuch.html",
-	name: "Fannkuch",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Figure out the number of ways in which a set of numbers can be manipulated.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["array", "looping"]
+	"file": "sunspider-access-fannkuch.html",
+	"name": "Fannkuch",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Figure out the number of ways in which a set of numbers can be manipulated.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["array", "looping"]
 },
 "sunspider-access-nbody": {
-	file: "sunspider-access-nbody.html",
-	name: "N-Body Rotation and Gravity",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Compute the location of multiple planets based upon rotation and gravity.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["object", "property", "looping", "math"]
+	"file": "sunspider-access-nbody.html",
+	"name": "N-Body Rotation and Gravity",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Compute the location of multiple planets based upon rotation and gravity.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["object", "property", "looping", "math"]
 },
 "sunspider-access-nsieve": {
-	file: "sunspider-access-nsieve.html",
-	name: "Prime Number Computation",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Compute the number of prime numbers in a specific range of numbers.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping", "array"]
+	"file": "sunspider-access-nsieve.html",
+	"name": "Prime Number Computation",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Compute the number of prime numbers in a specific range of numbers.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping", "array"]
 },
 "sunspider-bitops-3bit-bits-in-byte": {
-	file: "sunspider-bitops-3bit-bits-in-byte.html",
-	name: "Compute Bits in Byte",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Compute the number of bits in a number using bitops.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping", "bitops"]
+	"file": "sunspider-bitops-3bit-bits-in-byte.html",
+	"name": "Compute Bits in Byte",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Compute the number of bits in a number using bitops.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping", "bitops"]
 },
 "sunspider-bitops-bits-in-byte": {
-	file: "sunspider-bitops-bits-in-byte.html",
-	name: "Compute Bits in Byte (2)",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Compute the number of bits in a number using bitops.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping", "bitops"]
+	"file": "sunspider-bitops-bits-in-byte.html",
+	"name": "Compute Bits in Byte (2)",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Compute the number of bits in a number using bitops.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping", "bitops"]
 },
 "sunspider-bitops-bitwise-and": {
-	file: "sunspider-bitops-bitwise-and.html",
-	name: "Bitwise And",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Compute a number by using a series of 'and' bit operations.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping", "bitops"]
+	"file": "sunspider-bitops-bitwise-and.html",
+	"name": "Bitwise And",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Compute a number by using a series of 'and' bit operations.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping", "bitops"]
 },
 "sunspider-bitops-nsieve-bits": {
-	file: "sunspider-bitops-nsieve-bits.html",
-	name: "Prime Number Computation (2)",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Compute the number of prime numbers in a specific range of numbers using bit operations.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping", "bitops", "array"]
+	"file": "sunspider-bitops-nsieve-bits.html",
+	"name": "Prime Number Computation (2)",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Compute the number of prime numbers in a specific range of numbers using bit operations.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping", "bitops", "array"]
 },
 "sunspider-controlflow-recursive": {
-	file: "sunspider-controlflow-recursive.html",
-	name: "Recursive Number Calculation",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Compute various numbers in a recursive manner.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["functions"]
+	"file": "sunspider-controlflow-recursive.html",
+	"name": "Recursive Number Calculation",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Compute various numbers in a recursive manner.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["functions"]
 },
 "sunspider-crypto-aes": {
-	file: "sunspider-crypto-aes.html",
-	name: "AES Encryption/Decryption",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Encrypt a string and then decrypt it again using AES.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping","string","bitops"]
+	"file": "sunspider-crypto-aes.html",
+	"name": "AES Encryption/Decryption",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Encrypt a string and then decrypt it again using AES.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping","string","bitops"]
 },
 "sunspider-crypto-md5": {
-	file: "sunspider-crypto-md5.html",
-	name: "MD5 Hashing",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Hash a long string using MD5.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping","string"]
+	"file": "sunspider-crypto-md5.html",
+	"name": "MD5 Hashing",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Hash a long string using MD5.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping","string"]
 },
 "sunspider-crypto-sha1": {
-	file: "sunspider-crypto-sha1.html",
-	name: "SHA1 Hashing",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Hash a long string using SHA1.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["looping","string"]
+	"file": "sunspider-crypto-sha1.html",
+	"name": "SHA1 Hashing",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Hash a long string using SHA1.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["looping","string"]
 },
 "sunspider-date-format-xparb": {
-	file: "sunspider-date-format-xparb.html",
-	name: "Date Formatting",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Converting a date into a string representation.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["date","string"]
+	"file": "sunspider-date-format-xparb.html",
+	"name": "Date Formatting",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Converting a date into a string representation.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["date","string"]
 },
 "sunspider-date-format-tofte": {
-	file: "sunspider-date-format-tofte.html",
-	name: "Date Formatting (2)",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Converting a date into a string representation.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["date","string"]
+	"file": "sunspider-date-format-tofte.html",
+	"name": "Date Formatting (2)",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Converting a date into a string representation.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["date","string"]
 },
 "sunspider-math-cordic": {
-	file: "sunspider-math-cordic.html",
-	name: "Trigonometric Calculation",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Calculate values from hyperbolic and trigonometric functions.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["math","looping"]
+	"file": "sunspider-math-cordic.html",
+	"name": "Trigonometric Calculation",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Calculate values from hyperbolic and trigonometric functions.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["math","looping"]
 },
 "sunspider-math-partial-sums": {
-	file: "sunspider-math-partial-sums.html",
-	name: "Partial Sum Calculation",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Calculate the partial sum of a few different number series.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["math","looping"]
+	"file": "sunspider-math-partial-sums.html",
+	"name": "Partial Sum Calculation",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Calculate the partial sum of a few different number series.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["math","looping"]
 },
 "sunspider-math-spectral-norm": {
-	file: "sunspider-math-spectral-norm.html",
-	name: "Spectral Norm of a Matrix",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Calculate the spectral norm of a matrix of numbers.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["array","looping","math"]
+	"file": "sunspider-math-spectral-norm.html",
+	"name": "Spectral Norm of a Matrix",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Calculate the spectral norm of a matrix of numbers.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["array","looping","math"]
 },
 "sunspider-regexp-dna": {
-	file: "sunspider-regexp-dna.html",
-	name: "DNA Sequence Counting",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Counts occurences in a DNA sequence.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["string","regexp"]
+	"file": "sunspider-regexp-dna.html",
+	"name": "DNA Sequence Counting",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Counts occurences in a DNA sequence.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["string","regexp"]
 },
 "sunspider-string-fasta": {
-	file: "sunspider-string-fasta.html",
-	name: "DNA Sequence Alignment",
-	origin: ["Language Shootout", "http://shootout.alioth.debian.org/"],
-	desc: "Find DNA matches within a larger sequence.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["string","object","looping"]
+	"file": "sunspider-string-fasta.html",
+	"name": "DNA Sequence Alignment",
+	"origin": ["Language Shootout", "http://shootout.alioth.debian.org/"],
+	"desc": "Find DNA matches within a larger sequence.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["string","object","looping"]
 },
 "sunspider-string-tagcloud": {
-	file: "sunspider-string-tagcloud.html",
-	name: "Tag Cloud Creation",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Convert a JSON structure into an HTML tag cloud.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["string","regexp"]
+	"file": "sunspider-string-tagcloud.html",
+	"name": "Tag Cloud Creation",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Convert a JSON structure into an HTML tag cloud.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["string","regexp"]
 },
 "sunspider-string-unpack-code": {
-	file: "sunspider-string-unpack-code.html",
-	name: "Script Unpacking",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Decompressing scripts run through Dean Edwards' Packer.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["regexp","string","looping"]
+	"file": "sunspider-string-unpack-code.html",
+	"name": "Script Unpacking",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Decompressing scripts run through Dean Edwards' Packer.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["regexp","string","looping"]
 },
 "sunspider-string-validate-input": {
-	file: "sunspider-string-validate-input.html",
-	name: "Validate User Input",
-	origin: ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
-	desc: "Test user input against a series of rules.",
-	category: "SunSpider JavaScript Tests",
-	tags: ["string","regexp"]
+	"file": "sunspider-string-validate-input.html",
+	"name": "Validate User Input",
+	"origin": ["SunSpider", "http://www2.webkit.org/perf/sunspider-0.9/sunspider.html"],
+	"desc": "Test user input against a series of rules.",
+	"category": "SunSpider JavaScript Tests",
+	"tags": ["string","regexp"]
 },
 "dom-attr": {
-	file: "dom-attr.html",
-	name: "DOM Attributes",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Setting and getting DOM node attributes.",
-	category: "Dromaeo DOM Tests",
-	tags: ["dom","attributes"]
+	"file": "dom-attr.html",
+	"name": "DOM Attributes",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Setting and getting DOM node attributes.",
+	"category": "Dromaeo DOM Tests",
+	"tags": ["dom","attributes"]
 },
 "dom-modify": {
-	file: "dom-modify.html",
-	name: "DOM Modification",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Creating and injecting DOM nodes into a document.",
-	category: "Dromaeo DOM Tests",
-	tags: ["dom","modify"]
+	"file": "dom-modify.html",
+	"name": "DOM Modification",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Creating and injecting DOM nodes into a document.",
+	"category": "Dromaeo DOM Tests",
+	"tags": ["dom","modify"]
 },
 "dom-query": {
-	file: "dom-query.html",
-	name: "DOM Query",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document.",
-	category: "Dromaeo DOM Tests",
-	tags: ["dom","query"]
+	"file": "dom-query.html",
+	"name": "DOM Query",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document.",
+	"category": "Dromaeo DOM Tests",
+	"tags": ["dom","query"]
 },
 "dom-traverse": {
-	file: "dom-traverse.html",
-	name: "DOM Traversal",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Traversing a DOM structure.",
-	category: "Dromaeo DOM Tests",
-	tags: ["dom","traverse"]
+	"file": "dom-traverse.html",
+	"name": "DOM Traversal",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Traversing a DOM structure.",
+	"category": "Dromaeo DOM Tests",
+	"tags": ["dom","traverse"]
 },
 "jslib-attr-jquery": {
-	file: "jslib-attr-jquery.html",
-	name: "DOM Attributes (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Setting and getting DOM node attributes using the jQuery JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","jquery","attributes"]
+	"file": "jslib-attr-jquery.html",
+	"name": "DOM Attributes (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Setting and getting DOM node attributes using the jQuery JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","jquery","attributes"]
 },
 "jslib-attr-prototype": {
-	file: "jslib-attr-prototype.html",
-	name: "DOM Attributes (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Setting and getting DOM node attributes using the Prototype JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","prototype","attributes"]
+	"file": "jslib-attr-prototype.html",
+	"name": "DOM Attributes (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Setting and getting DOM node attributes using the Prototype JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","prototype","attributes"]
 },
 "jslib-event-jquery": {
-	file: "jslib-event-jquery.html",
-	name: "DOM Events (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Binding, removing, and triggering DOM events using the jQuery JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","jquery","events"]
+	"file": "jslib-event-jquery.html",
+	"name": "DOM Events (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Binding, removing, and triggering DOM events using the jQuery JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","jquery","events"]
 },
 "jslib-event-prototype": {
-	file: "jslib-event-prototype.html",
-	name: "DOM Events (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Binding, removing, and triggering DOM events using the Prototype JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","prototype","events"]
+	"file": "jslib-event-prototype.html",
+	"name": "DOM Events (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Binding, removing, and triggering DOM events using the Prototype JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","prototype","events"]
 },
 "jslib-modify-jquery": {
-	file: "jslib-modify-jquery.html",
-	name: "DOM Modification (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Creating and injecting DOM nodes into a document using the jQuery JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","jquery","modify"]
+	"file": "jslib-modify-jquery.html",
+	"name": "DOM Modification (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Creating and injecting DOM nodes into a document using the jQuery JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","jquery","modify"]
 },
 "jslib-modify-prototype": {
-	file: "jslib-modify-prototype.html",
-	name: "DOM Modification (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Creating and injecting DOM nodes into a document using the Prototype JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","prototype","modify"]
+	"file": "jslib-modify-prototype.html",
+	"name": "DOM Modification (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Creating and injecting DOM nodes into a document using the Prototype JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","prototype","modify"]
 },
 "jslib-style-jquery": {
-	file: "jslib-style-jquery.html",
-	name: "DOM Style (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Getting and setting CSS information on DOM elements using the jQuery JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","jquery","style"]
+	"file": "jslib-style-jquery.html",
+	"name": "DOM Style (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Getting and setting CSS information on DOM elements using the jQuery JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","jquery","style"]
 },
 "jslib-style-prototype": {
-	file: "jslib-style-prototype.html",
-	name: "DOM Style (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Getting and setting CSS information on DOM elements using the Prototype JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","prototype","style"]
+	"file": "jslib-style-prototype.html",
+	"name": "DOM Style (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Getting and setting CSS information on DOM elements using the Prototype JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","prototype","style"]
 },
 "jslib-traverse-jquery": {
-	file: "jslib-traverse-jquery.html",
-	name: "DOM Traversal (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Traversing a DOM structure using the jQuery JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","jquery","traverse"]
+	"file": "jslib-traverse-jquery.html",
+	"name": "DOM Traversal (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Traversing a DOM structure using the jQuery JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","jquery","traverse"]
 },
 "jslib-traverse-prototype": {
-	file: "jslib-traverse-prototype.html",
-	name: "DOM Traversal (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Traversing a DOM structure using the Prototype JavaScript Library.",
-	category: "JavaScript Library Tests",
-	tags: ["dom","jslib","prototype","traverse"]
+	"file": "jslib-traverse-prototype.html",
+	"name": "DOM Traversal (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Traversing a DOM structure using the Prototype JavaScript Library.",
+	"category": "JavaScript Library Tests",
+	"tags": ["dom","jslib","prototype","traverse"]
 },
 "cssquery-jquery": {
-	file: "cssquery-jquery.html",
-	name: "DOM Query (jQuery)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the jQuery JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","jquery","query"]
+	"file": "cssquery-jquery.html",
+	"name": "DOM Query (jQuery)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the jQuery JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","jquery","query"]
 },
 "cssquery-prototype": {
-	file: "cssquery-prototype.html",
-	name: "DOM Query (Prototype)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the Prototype JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","prototype","query"]
+	"file": "cssquery-prototype.html",
+	"name": "DOM Query (Prototype)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the Prototype JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","prototype","query"]
 },
 "cssquery-dojo": {
-	file: "cssquery-dojo.html",
-	name: "DOM Query (Dojo)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the Dojo JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","dojo","query"]
+	"file": "cssquery-dojo.html",
+	"name": "DOM Query (Dojo)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the Dojo JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","dojo","query"]
 },
 "cssquery-ext": {
-	file: "cssquery-ext.html",
-	name: "DOM Query (ExtJS)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the ExtJS JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","extjs","query"]
+	"file": "cssquery-ext.html",
+	"name": "DOM Query (ExtJS)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the ExtJS JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","extjs","query"]
 },
 "cssquery-mootools": {
-	file: "cssquery-mootools.html",
-	name: "DOM Query (Mootools)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the Mootools JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","mootools","query"]
+	"file": "cssquery-mootools.html",
+	"name": "DOM Query (Mootools)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the Mootools JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","mootools","query"]
 },
 "cssquery-yui": {
-	file: "cssquery-yui.html",
-	name: "DOM Query (Yahoo UI)",
-	origin: ["John Resig", "http://ejohn.org/"],
-	desc: "Querying DOM elements in a document using the Yahoo UI JavaScript Library.",
-	category: "CSS Selector Tests",
-	tags: ["dom","jslib","yui","query"]
+	"file": "cssquery-yui.html",
+	"name": "DOM Query (Yahoo UI)",
+	"origin": ["John Resig", "http://ejohn.org/"],
+	"desc": "Querying DOM elements in a document using the Yahoo UI JavaScript Library.",
+	"category": "CSS Selector Tests",
+	"tags": ["dom","jslib","yui","query"]
 }
-}
+}
\ No newline at end of file
diff --git a/chrome/test/data/dromaeo/tests/cssquery-dojo.html b/chrome/test/data/dromaeo/tests/cssquery-dojo.html
index c2a49b6..fd8e4aa 100644
--- a/chrome/test/data/dromaeo/tests/cssquery-dojo.html
+++ b/chrome/test/data/dromaeo/tests/cssquery-dojo.html
@@ -4,7 +4,7 @@
 <script src="../lib/dojo.js"></script>
 <script>
 window.onload = function(){
-startTest("cssquery-dojo", 'ed0ed65e');
+startTest("cssquery-dojo", '5477d2ae');
 
 // Try to force real results
 var ret, tmp;
@@ -418,7 +418,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1601,7 +1601,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1618,7 +1618,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1810,7 +1810,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1824,7 +1824,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1837,7 +1837,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -2016,11 +2016,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2029,8 +2029,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -2039,11 +2039,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2056,8 +2056,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2072,7 +2072,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2210,7 +2210,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2321,11 +2321,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2832,14 +2832,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2863,13 +2863,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2877,10 +2877,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2898,11 +2898,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2938,9 +2938,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2959,7 +2959,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -3012,7 +3012,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -3035,7 +3035,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/cssquery-ext.html b/chrome/test/data/dromaeo/tests/cssquery-ext.html
index b6f2f15e..11a84134 100644
--- a/chrome/test/data/dromaeo/tests/cssquery-ext.html
+++ b/chrome/test/data/dromaeo/tests/cssquery-ext.html
@@ -1,11 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/ext-base.js"></script>
 <script src="../lib/ext-core.js"></script>
 <script>
 window.onload = function(){
-startTest("cssquery-ext", '559e157b');
+startTest("cssquery-ext", '5bc05a17');
 
 // Try to force real results
 var ret, tmp;
@@ -18,7 +17,7 @@
 		div.innerHTML = html;
 		document.body.appendChild( div );
 	});
-	
+
 	test("Ext - *", function(){
 		query("*");
 	});
@@ -419,7 +418,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1602,7 +1601,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1619,7 +1618,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1811,7 +1810,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1825,7 +1824,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1838,7 +1837,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -2017,11 +2016,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2030,8 +2029,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -2040,11 +2039,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2057,8 +2056,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2073,7 +2072,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2211,7 +2210,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2322,11 +2321,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2833,14 +2832,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2864,13 +2863,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2878,10 +2877,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2899,11 +2898,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2939,9 +2938,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2960,7 +2959,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -3013,7 +3012,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -3036,7 +3035,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html b/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html
new file mode 100644
index 0000000..b180f5a
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/cssquery-jquery-2.x.html
@@ -0,0 +1,3062 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("cssquery-jquery", '5dfe1a12');
+
+// Try to force real results
+var ret, tmp;
+
+var html = document.body.innerHTML;
+
+	prep(function(){
+		var div = document.createElement("div");
+		div.innerHTML = html;
+		document.body.appendChild( div );
+	});
+
+	test("jQuery - *", function(){
+		jQuery("*");
+	});
+
+	test("jQuery - div:only-child", function(){
+		jQuery("div:only-child");
+	});
+
+	test("jQuery - div:first-child", function(){
+		jQuery("div:first-child");
+	});
+
+	test("jQuery - div:nth-child(even)", function(){
+		jQuery("div:nth-child(even)");
+	});
+
+	test("jQuery - div:nth-child(2n)", function(){
+		jQuery("div:nth-child(2n)");
+	});
+
+	test("jQuery - div:nth-child(odd)", function(){
+		jQuery("div:nth-child(odd)");
+	});
+
+	test("jQuery - div:nth-child(2n+1)", function(){
+		jQuery("div:nth-child(2n+1)");
+	});
+
+	test("jQuery - div:nth-child(n)", function(){
+		jQuery("div:nth-child(n)");
+	});
+
+	test("jQuery - div:last-child", function(){
+		jQuery("div:last-child");
+	});
+
+	test("jQuery - div > div", function(){
+		jQuery("div > div");
+	});
+
+	test("jQuery - div + div", function(){
+		jQuery("div + div");
+	});
+
+	test("jQuery - div ~ div", function(){
+		jQuery("div ~ div");
+	});
+
+	test("jQuery - body", function(){
+		jQuery("body");
+	});
+
+	test("jQuery - body div", function(){
+		jQuery("body div");
+	});
+
+	test("jQuery - div", function(){
+		jQuery("div");
+	});
+
+	test("jQuery - div div", function(){
+		jQuery("div div");
+	});
+
+	test("jQuery - div div div", function(){
+		jQuery("div div div");
+	});
+
+	test("jQuery - div, div, div", function(){
+		jQuery("div, div, div");
+	});
+
+	test("jQuery - div, a, span", function(){
+		jQuery("div, a, span");
+	});
+
+	test("jQuery - .dialog", function(){
+		jQuery(".dialog");
+	});
+
+	test("jQuery - div.dialog", function(){
+		jQuery("div.dialog");
+	});
+
+	test("jQuery - div .dialog", function(){
+		jQuery("div .dialog");
+	});
+
+	test("jQuery - div.character, div.dialog", function(){
+		jQuery("div.character, div.dialog");
+	});
+
+	test("jQuery - #speech5", function(){
+		jQuery("#speech5");
+	});
+
+	test("jQuery - div#speech5", function(){
+		jQuery("div#speech5");
+	});
+
+	test("jQuery - div #speech5", function(){
+		jQuery("div #speech5");
+	});
+
+	test("jQuery - div.scene div.dialog", function(){
+		jQuery("div.scene div.dialog");
+	});
+
+	test("jQuery - div#scene1 div.dialog div", function(){
+		jQuery("div#scene1 div.dialog div");
+	});
+
+	test("jQuery - #scene1 #speech1", function(){
+		jQuery("#scene1 #speech1");
+	});
+
+	test("jQuery - div[class]", function(){
+		jQuery("div[class]");
+	});
+
+	test("jQuery - div[class=dialog]", function(){
+		jQuery("div[class=dialog]");
+	});
+
+	test("jQuery - div[class^=dia]", function(){
+		jQuery("div[class^=dia]");
+	});
+
+	test("jQuery - div[class$=log]", function(){
+		jQuery("div[class$=log]");
+	});
+
+	test("jQuery - div[class*=sce]", function(){
+		jQuery("div[class*=sce]");
+	});
+
+	test("jQuery - div[class|=dialog]", function(){
+		jQuery("div[class|=dialog]");
+	});
+
+	test("jQuery - div[class~=dialog]", function(){
+		jQuery("div[class~=dialog]");
+	});
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/cssquery-jquery.html b/chrome/test/data/dromaeo/tests/cssquery-jquery.html
index efd9866..6e552f1 100644
--- a/chrome/test/data/dromaeo/tests/cssquery-jquery.html
+++ b/chrome/test/data/dromaeo/tests/cssquery-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("cssquery-jquery", 'a1e81afd');
+startTest("cssquery-jquery", 'de808e5f');
 
 // Try to force real results
 var ret, tmp;
@@ -16,7 +16,7 @@
 		div.innerHTML = html;
 		document.body.appendChild( div );
 	});
-	
+
 	test("jQuery - *", function(){
 		jQuery("*");
 	});
@@ -417,7 +417,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1600,7 +1600,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1617,7 +1617,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1809,7 +1809,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1823,7 +1823,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1836,7 +1836,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -2015,11 +2015,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2028,8 +2028,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -2038,11 +2038,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2055,8 +2055,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2071,7 +2071,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2209,7 +2209,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2320,11 +2320,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2831,14 +2831,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2862,13 +2862,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2876,10 +2876,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2897,11 +2897,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2937,9 +2937,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2958,7 +2958,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -3011,7 +3011,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -3034,7 +3034,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/cssquery-mootools.html b/chrome/test/data/dromaeo/tests/cssquery-mootools.html
index 85026ac..1422b2a 100644
--- a/chrome/test/data/dromaeo/tests/cssquery-mootools.html
+++ b/chrome/test/data/dromaeo/tests/cssquery-mootools.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/mootools.js"></script>
+<script src="../lib/mootools-yui-compressed.js"></script>
 <script>
 window.onload = function(){
-startTest("cssquery-mootools", 'a31d1311');
+startTest("cssquery-mootools", 'c5ac656b');
 
 // Try to force real results
 var ret, tmp;
@@ -16,7 +16,7 @@
 		div.innerHTML = html;
 		document.body.appendChild( div );
 	});
-	
+
 	test("Mootools - *", function(){
 		$$("*");
 	});
@@ -417,7 +417,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1600,7 +1600,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1617,7 +1617,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1809,7 +1809,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1823,7 +1823,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1836,7 +1836,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -2015,11 +2015,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2028,8 +2028,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -2038,11 +2038,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2055,8 +2055,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2071,7 +2071,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2209,7 +2209,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2320,11 +2320,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2831,14 +2831,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2862,13 +2862,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2876,10 +2876,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2897,11 +2897,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2937,9 +2937,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2958,7 +2958,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -3011,7 +3011,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -3034,7 +3034,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/cssquery-prototype.html b/chrome/test/data/dromaeo/tests/cssquery-prototype.html
index d252c44..28bada1c 100644
--- a/chrome/test/data/dromaeo/tests/cssquery-prototype.html
+++ b/chrome/test/data/dromaeo/tests/cssquery-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("cssquery-prototype", '5f350493');
+startTest("cssquery-prototype", 'd151161f');
 
 // Try to force real results
 var ret, tmp;
@@ -417,7 +417,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1600,7 +1600,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1617,7 +1617,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1809,7 +1809,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1823,7 +1823,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1836,7 +1836,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -2015,11 +2015,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2028,8 +2028,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -2038,11 +2038,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2055,8 +2055,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2071,7 +2071,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2209,7 +2209,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2320,11 +2320,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2831,14 +2831,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2862,13 +2862,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2876,10 +2876,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2897,11 +2897,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2937,9 +2937,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2958,7 +2958,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -3011,7 +3011,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -3034,7 +3034,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html
new file mode 100644
index 0000000..867938a5
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/jslib-attr-jquery-2.x.html
@@ -0,0 +1,2949 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("jslib-attr-jquery", 'aeb3b2c1');
+
+// Try to force real results
+var ret, tmp, div;
+
+var html = document.body.innerHTML;
+
+	prep(function(){
+		div = jQuery("div");
+		var tmp = document.createElement("div");
+		tmp.innerHTML = html;
+		document.body.appendChild( tmp );
+	});
+
+	test("jQuery - addClass", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.addClass("foo");
+	});
+
+	test("jQuery - removeClass", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.removeClass("foo");
+	});
+
+	test("jQuery - hasClass x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.hasClass("test");
+	});
+
+	test("jQuery - attr(class) x100", function(){
+		for ( var i = 0; i < 1000; i++ )
+		ret = div.attr("class");
+	});
+
+	test("jQuery - attr(class,test)", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.attr("class","test");
+	});
+
+	test("jQuery - removeAttribute", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.removeAttr("id");
+	});
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html b/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
index eff26ddf..f089b52 100644
--- a/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
+++ b/chrome/test/data/dromaeo/tests/jslib-attr-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-attr-jquery", 'd2fbb123');
+startTest("jslib-attr-jquery", '675a354f');
 
 // Try to force real results
 var ret, tmp, div;
@@ -17,7 +17,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("jQuery - addClass", function(){
 		for ( var i = 0; i < 10; i++ )
 		div.addClass("foo");
@@ -304,7 +304,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1487,7 +1487,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1504,7 +1504,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1696,7 +1696,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1710,7 +1710,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1723,7 +1723,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1902,11 +1902,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1915,8 +1915,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1925,11 +1925,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1942,8 +1942,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1958,7 +1958,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2096,7 +2096,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2207,11 +2207,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2718,14 +2718,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2749,13 +2749,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2763,10 +2763,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2784,11 +2784,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2824,9 +2824,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2845,7 +2845,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2898,7 +2898,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2921,7 +2921,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html b/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
index 7ff519a..998b990 100644
--- a/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
+++ b/chrome/test/data/dromaeo/tests/jslib-attr-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-attr-prototype", 'a7983bbe');
+startTest("jslib-attr-prototype", '4fb93c6a');
 
 // Try to force real results
 var ret, tmp, div;
@@ -17,7 +17,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("Prototype - addClassName", function(){
 		for ( var i = 0; i < 10; i++ )
 		div.invoke("addClassName", "foo");
@@ -304,7 +304,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1487,7 +1487,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1504,7 +1504,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1696,7 +1696,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1710,7 +1710,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1723,7 +1723,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1902,11 +1902,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1915,8 +1915,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1925,11 +1925,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1942,8 +1942,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1958,7 +1958,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2096,7 +2096,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2207,11 +2207,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2718,14 +2718,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2749,13 +2749,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2763,10 +2763,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2784,11 +2784,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2824,9 +2824,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2845,7 +2845,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2898,7 +2898,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2921,7 +2921,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html
new file mode 100644
index 0000000..db655109
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/jslib-event-jquery-2.x.html
@@ -0,0 +1,2936 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("jslib-event-jquery", '161a447c');
+
+// Try to force real results
+var ret, tmp, div;
+
+var html = document.body.innerHTML;
+
+function testfn(){}
+
+	prep(function(){
+		div = jQuery("div");
+		var tmp = document.createElement("div");
+		tmp.innerHTML = html;
+		document.body.appendChild( tmp );
+	});
+
+	test("jQuery - bind", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.bind("click", testfn);
+	});
+
+	test("jQuery - trigger", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.trigger("click");
+	});
+
+	test("jQuery - unbind x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		div.unbind("click", testfn);
+	});
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/jslib-event-jquery.html b/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
index aa7dfa9f..bfaf354 100644
--- a/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
+++ b/chrome/test/data/dromaeo/tests/jslib-event-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-event-jquery", '471bfd04');
+startTest("jslib-event-jquery", '690f881e');
 
 // Try to force real results
 var ret, tmp, div;
@@ -19,7 +19,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("jQuery - bind", function(){
 		for ( var i = 0; i < 10; i++ )
 		div.bind("click", testfn);
@@ -291,7 +291,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1474,7 +1474,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1491,7 +1491,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1683,7 +1683,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1697,7 +1697,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1710,7 +1710,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1889,11 +1889,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1902,8 +1902,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1912,11 +1912,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1929,8 +1929,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1945,7 +1945,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2083,7 +2083,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2194,11 +2194,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2705,14 +2705,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2736,13 +2736,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2750,10 +2750,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2771,11 +2771,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2811,9 +2811,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2832,7 +2832,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2885,7 +2885,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2908,7 +2908,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-event-prototype.html b/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
index 65060a5..78d4626 100644
--- a/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
+++ b/chrome/test/data/dromaeo/tests/jslib-event-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-event-prototype", '05cfe7b8');
+startTest("jslib-event-prototype", '03b86d96');
 
 // Try to force real results
 var ret, tmp, div;
@@ -19,7 +19,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("Prototype - observe", function(){
 		for ( var i = 0; i < 10; i++ )
 		div.invoke("observe", "click", testfn);
@@ -291,7 +291,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1474,7 +1474,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1491,7 +1491,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1683,7 +1683,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1697,7 +1697,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1710,7 +1710,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1889,11 +1889,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1902,8 +1902,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1912,11 +1912,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1929,8 +1929,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1945,7 +1945,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2083,7 +2083,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2194,11 +2194,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2705,14 +2705,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2736,13 +2736,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2750,10 +2750,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2771,11 +2771,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2811,9 +2811,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2832,7 +2832,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2885,7 +2885,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2908,7 +2908,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html
new file mode 100644
index 0000000..2c8323d
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/jslib-modify-jquery-2.x.html
@@ -0,0 +1,2960 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("jslib-modify-jquery", 'ed2c7ef2');
+
+// Try to force real results
+var ret, tmp, div, a, dd;
+
+var html = document.body.innerHTML,
+	elem = document.createElement("div"),
+	elem2 = document.createElement("strong"),
+	elemStr = "<strong>some text</strong>";
+
+	prep(function(){
+		a = jQuery("a");
+		dd = jQuery("dd");
+		div = jQuery("div");
+		var tmp = document.createElement("div");
+		tmp.innerHTML = html;
+		document.body.appendChild( tmp );
+	});
+
+	/* // Need to find a good way to test this
+	test("jQuery - wrap()", function(){
+		div.wrap( elem );
+	});
+	*/
+
+	test("jQuery - html()", function(){
+		dd.html( elemStr );
+	});
+
+	test("jQuery - before()", function(){
+		div.before( elemStr );
+	});
+
+	test("jQuery - after()", function(){
+		div.before( elemStr );
+	});
+
+	test("jQuery - prepend()", function(){
+		div.prepend( elemStr );
+	});
+
+	test("jQuery - append()", function(){
+		div.append( elemStr );
+	});
+
+	/* // Need a good way to test
+	test("jQuery - replaceWith()", function(){
+		div.replaceWith( elem );
+	});
+
+	test("jQuery - remove()", function(){
+		div.remove();
+	});
+	*/
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html b/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
index a8e24c79..34fa179 100644
--- a/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
+++ b/chrome/test/data/dromaeo/tests/jslib-modify-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-modify-jquery", '0e3fac95');
+startTest("jslib-modify-jquery", '950aaaa1');
 
 // Try to force real results
 var ret, tmp, div, a, dd;
@@ -315,7 +315,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1498,7 +1498,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1515,7 +1515,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1707,7 +1707,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1721,7 +1721,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1734,7 +1734,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1913,11 +1913,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1926,8 +1926,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1936,11 +1936,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1953,8 +1953,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1969,7 +1969,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2107,7 +2107,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2218,11 +2218,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2729,14 +2729,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2760,13 +2760,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2774,10 +2774,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2795,11 +2795,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2835,9 +2835,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2856,7 +2856,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2909,7 +2909,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2932,7 +2932,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html b/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
index 3f0cd509..866fd35 100644
--- a/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
+++ b/chrome/test/data/dromaeo/tests/jslib-modify-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-modify-prototype", '8fe35dff');
+startTest("jslib-modify-prototype", '6693b3a5');
 
 // Try to force real results
 var ret, tmp, div, a, dd;
@@ -32,7 +32,7 @@
 	test("Prototype - update()", function(){
 		dd.invoke("update", elemStr);
 	});
-	
+
 	test("Prototype - before", function(){
 		div.invoke("insert", {before: elemStr});
 	});
@@ -315,7 +315,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1498,7 +1498,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1515,7 +1515,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1707,7 +1707,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1721,7 +1721,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1734,7 +1734,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1913,11 +1913,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1926,8 +1926,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1936,11 +1936,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1953,8 +1953,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1969,7 +1969,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2107,7 +2107,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2218,11 +2218,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2729,14 +2729,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2760,13 +2760,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2774,10 +2774,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2795,11 +2795,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2835,9 +2835,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2856,7 +2856,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2909,7 +2909,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2932,7 +2932,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html
new file mode 100644
index 0000000..72c19c6
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/jslib-style-jquery-2.x.html
@@ -0,0 +1,2958 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("jslib-style-jquery", '08bc6988');
+
+// Try to force real results
+var ret, tmp, div;
+
+var html = document.body.innerHTML;
+
+	prep(function(){
+		div = jQuery("div");
+		var tmp = document.createElement("div");
+		tmp.innerHTML = html;
+		document.body.appendChild( tmp );
+	});
+
+	test("jQuery - css(color) x100", function(){
+		for ( var i = 0; i < 1000; i++ )
+		ret = div.css("color");
+	});
+
+	test("jQuery - css(color,red)", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.css("color","red");
+	});
+
+	test("jQuery - height() x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.height();
+	});
+
+	test("jQuery - width() x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.width();
+	});
+
+	test("jQuery - .is(:visible)", function(){
+		for ( var i = 0; i < 10; i++ )
+		ret = div.is(":visible");
+	});
+
+	test("jQuery - .show()", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.show();
+	});
+
+	test("jQuery - .hide()", function(){
+		for ( var i = 0; i < 10; i++ )
+		div.hide();
+	});
+
+	test("jQuery - .toggle()", function(){
+		div.toggle();
+	});
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/jslib-style-jquery.html b/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
index 4d26858..3c99a70 100644
--- a/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
+++ b/chrome/test/data/dromaeo/tests/jslib-style-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-style-jquery", '47bfc8bd');
+startTest("jslib-style-jquery", 'a803790a');
 
 // Try to force real results
 var ret, tmp, div;
@@ -17,7 +17,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("jQuery - css(color) x100", function(){
 		for ( var i = 0; i < 1000; i++ )
 		ret = div.css("color");
@@ -313,7 +313,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1496,7 +1496,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1513,7 +1513,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1705,7 +1705,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1719,7 +1719,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1732,7 +1732,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1911,11 +1911,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1924,8 +1924,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1934,11 +1934,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1951,8 +1951,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1967,7 +1967,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2105,7 +2105,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2216,11 +2216,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2727,14 +2727,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2758,13 +2758,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2772,10 +2772,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2793,11 +2793,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2833,9 +2833,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2854,7 +2854,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2907,7 +2907,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2930,7 +2930,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-style-prototype.html b/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
index 927e476..9ba6e48 100644
--- a/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
+++ b/chrome/test/data/dromaeo/tests/jslib-style-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-style-prototype", 'f511098c');
+startTest("jslib-style-prototype", 'c41174f0');
 
 // Try to force real results
 var ret, tmp, div;
@@ -17,7 +17,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("Prototype - getStyle()", function(){
 		for ( var i = 0; i < 10; i++ )
 		ret = div.invoke("getStyle","color");
@@ -314,7 +314,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1497,7 +1497,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1514,7 +1514,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1706,7 +1706,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1720,7 +1720,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1733,7 +1733,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1912,11 +1912,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1925,8 +1925,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1935,11 +1935,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1952,8 +1952,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1968,7 +1968,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2106,7 +2106,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2217,11 +2217,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2728,14 +2728,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2759,13 +2759,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2773,10 +2773,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2794,11 +2794,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2834,9 +2834,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2855,7 +2855,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2908,7 +2908,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2931,7 +2931,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html
new file mode 100644
index 0000000..9e9792c3
--- /dev/null
+++ b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery-2.x.html
@@ -0,0 +1,2960 @@
+<html>
+<head>
+<script src="../htmlrunner.js"></script>
+<script src="../lib/jquery.2.0.3.js"></script>
+<script>
+window.onload = function(){
+startTest("jslib-traverse-jquery", '66e690c0');
+
+// Try to force real results
+var ret, tmp, div, dd;
+
+var html = document.body.innerHTML;
+
+	prep(function(){
+		div = jQuery("div");
+		dd = jQuery("dd");
+		var tmp = document.createElement("div");
+		tmp.innerHTML = html;
+		document.body.appendChild( tmp );
+	});
+
+	test("jQuery - parent x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.parent().length;
+	});
+
+	test("jQuery - parents x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.parents().length;
+	});
+
+	test("jQuery - prev x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.prev().length;
+	});
+
+	test("jQuery - prevAll", function(){
+		for ( var i = 0; i < 10; i++ )
+		ret = dd.prevAll().length;
+	});
+
+	test("jQuery - next x10", function(){
+		for ( var i = 0; i < 100; i++ )
+		ret = div.next().length;
+	});
+
+	test("jQuery - nextAll", function(){
+		for ( var i = 0; i < 10; i++ )
+		ret = dd.nextAll().length;
+	});
+
+	test("jQuery - siblings", function(){
+		for ( var i = 0; i < 10; i++ )
+		ret = dd.siblings().length;
+	});
+
+	test("jQuery - children", function(){
+		for ( var i = 0; i < 10; i++ )
+		ret = div.children().length;
+	});
+
+endTest();
+};
+</script>
+</head>
+<body>
+  <div class="head">
+   <p><a href="http://www.w3.org/"><img height=48 alt=W3C src="http://www.w3.org/Icons/w3c_home" width=72></a>
+
+   <h1 id="title">Selectors</h1>
+
+   <h2>W3C Working Draft 15 December 2005</h2>
+
+   <dl>
+
+    <dt>This version:
+
+    <dd><a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215">
+                 http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>
+
+    <dt>Latest version:
+
+    <dd><a href="http://www.w3.org/TR/css3-selectors">
+                 http://www.w3.org/TR/css3-selectors</a>
+
+    <dt>Previous version:
+
+    <dd><a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113">
+                 http://www.w3.org/TR/2001/CR-css3-selectors-20011113</a>
+
+    <dt><a name=editors-list></a>Editors:
+
+    <dd class="vcard"><span class="fn">Daniel Glazman</span> (Invited Expert)</dd>
+
+    <dd class="vcard"><a lang="tr" class="url fn" href="http://www.tantek.com/">Tantek &Ccedil;elik</a> (Invited Expert)
+
+    <dd class="vcard"><a href="mailto:ian@hixie.ch" class="url fn">Ian Hickson</a> (<span
+    class="company"><a href="http://www.google.com/">Google</a></span>)
+
+    <dd class="vcard"><span class="fn">Peter Linss</span> (former editor, <span class="company"><a
+    href="http://www.netscape.com/">Netscape/AOL</a></span>)
+
+    <dd class="vcard"><span class="fn">John Williams</span> (former editor, <span class="company"><a
+    href="http://www.quark.com/">Quark, Inc.</a></span>)
+
+   </dl>
+
+   <p class="copyright"><a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">
+   Copyright</a> &copy; 2005 <a href="http://www.w3.org/"><abbr
+   title="World Wide Web Consortium">W3C</abbr></a><sup>&reg;</sup>
+   (<a href="http://www.csail.mit.edu/"><abbr title="Massachusetts
+   Institute of Technology">MIT</abbr></a>, <a
+   href="http://www.ercim.org/"><acronym title="European Research
+   Consortium for Informatics and Mathematics">ERCIM</acronym></a>, <a
+   href="http://www.keio.ac.jp/">Keio</a>), All Rights Reserved.  W3C
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+   <a
+   href="http://www.w3.org/Consortium/Legal/copyright-documents">document
+   use</a> rules apply.
+
+   <hr title="Separator for header">
+
+  </div>
+
+  <h2><a name=abstract></a>Abstract</h2>
+
+  <p><em>Selectors</em> are patterns that match against elements in a
+  tree. Selectors have been optimized for use with HTML and XML, and
+  are designed to be usable in performance-critical code.</p>
+
+  <p><acronym title="Cascading Style Sheets">CSS</acronym> (Cascading
+  Style Sheets) is a language for describing the rendering of <acronym
+  title="Hypertext Markup Language">HTML</acronym> and <acronym
+  title="Extensible Markup Language">XML</acronym> documents on
+  screen, on paper, in speech, etc. CSS uses Selectors for binding
+  style properties to elements in the document. This document
+  describes extensions to the selectors defined in CSS level 2. These
+  extended selectors will be used by CSS level 3.
+
+  <p>Selectors define the following function:</p>
+
+  <pre>expression &#x2217; element &rarr; boolean</pre>
+
+  <p>That is, given an element and a selector, this specification
+  defines whether that element matches the selector.</p>
+
+  <p>These expressions can also be used, for instance, to select a set
+  of elements, or a single element from a set of elements, by
+  evaluating the expression across all the elements in a
+  subtree. <acronym title="Simple Tree Transformation
+  Sheets">STTS</acronym> (Simple Tree Transformation Sheets), a
+  language for transforming XML trees, uses this mechanism. <a href="#refsSTTS">[STTS]</a></p>
+
+  <h2><a name=status></a>Status of this document</h2>
+
+  <p><em>This section describes the status of this document at the
+  time of its publication. Other documents may supersede this
+  document. A list of current W3C publications and the latest revision
+  of this technical report can be found in the <a
+  href="http://www.w3.org/TR/">W3C technical reports index at
+  http://www.w3.org/TR/.</a></em></p>
+
+  <p>This document describes the selectors that already exist in <a
+  href="#refsCSS1"><abbr title="CSS level 1">CSS1</abbr></a> and <a
+  href="#refsCSS21"><abbr title="CSS level 2">CSS2</abbr></a>, and
+  also proposes new selectors for <abbr title="CSS level
+  3">CSS3</abbr> and other languages that may need them.</p>
+
+  <p>The CSS Working Group doesn't expect that all implementations of
+  CSS3 will have to implement all selectors. Instead, there will
+  probably be a small number of variants of CSS3, called profiles. For
+  example, it may be that only a profile for interactive user agents
+  will include all of the selectors.</p>
+
+  <p>This specification is a last call working draft for the the <a
+  href="http://www.w3.org/Style/CSS/members">CSS Working Group</a>
+  (<a href="/Style/">Style Activity</a>). This
+  document is a revision of the <a
+  href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">Candidate
+  Recommendation dated 2001 November 13</a>, and has incorporated
+  implementation feedback received in the past few years. It is
+  expected that this last call will proceed straight to Proposed
+  Recommendation stage since it is believed that interoperability will
+  be demonstrable.</p>
+
+  <p>All persons are encouraged to review and implement this
+  specification and return comments to the (<a
+  href="http://lists.w3.org/Archives/Public/www-style/">archived</a>)
+  public mailing list <a
+  href="http://www.w3.org/Mail/Lists.html#www-style">www-style</a>
+  (see <a href="http://www.w3.org/Mail/Request">instructions</a>). W3C
+  Members can also send comments directly to the CSS Working
+  Group.
+  The deadline for comments is 14 January 2006.</p>
+
+  <p>This is still a draft document and may be updated, replaced, or
+  obsoleted by other documents at any time. It is inappropriate to
+  cite a W3C Working Draft as other than &quot;work in progress&quot;.
+
+  <p>This document may be available in <a
+  href="http://www.w3.org/Style/css3-selectors-updates/translations">translation</a>.
+  The English version of this specification is the only normative
+  version.
+
+  <div class="subtoc">
+
+   <h2 id="test10"><a name=contents>Table of contents</a></h2>
+
+   <ul class="toc">
+    <li class="tocline2"><a href="#context">1. Introduction</a>
+     <ul>
+      <li><a href="#dependencies">1.1. Dependencies</a> </li>
+      <li><a href="#terminology">1.2. Terminology</a> </li>
+      <li><a href="#changesFromCSS2">1.3. Changes from CSS2</a> </li>
+     </ul>
+    <li class="tocline2"><a href="#selectors">2. Selectors</a>
+    <li class="tocline2"><a href="#casesens">3. Case sensitivity</a>
+    <li class="tocline2"><a href="#selector-syntax">4. Selector syntax</a>
+    <li class="tocline2"><a href="#grouping">5. Groups of selectors</a>
+    <li class="tocline2"><a href="#simple-selectors">6. Simple selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#type-selectors">6.1. Type selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#typenmsp">6.1.1. Type selectors and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#universal-selector">6.2. Universal selector</a>
+       <ul>
+        <li><a href="#univnmsp">6.2.1. Universal selector and namespaces</a></li>
+       </ul>
+      <li class="tocline3"><a href="#attribute-selectors">6.3. Attribute selectors</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#attribute-representation">6.3.1. Representation of attributes and attributes values</a>
+        <li><a href="#attribute-substrings">6.3.2. Substring matching attribute selectors</a>
+        <li class="tocline4"><a href="#attrnmsp">6.3.3. Attribute selectors and namespaces</a>
+        <li class="tocline4"><a href="#def-values">6.3.4. Default attribute values in DTDs</a></li>
+       </ul>
+      <li class="tocline3"><a href="#class-html">6.4. Class selectors</a>
+      <li class="tocline3"><a href="#id-selectors">6.5. ID selectors</a>
+      <li class="tocline3"><a href="#pseudo-classes">6.6. Pseudo-classes</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#dynamic-pseudos">6.6.1. Dynamic pseudo-classes</a>
+        <li class="tocline4"><a href="#target-pseudo">6.6.2. The :target pseudo-class</a>
+        <li class="tocline4"><a href="#lang-pseudo">6.6.3. The :lang() pseudo-class</a>
+        <li class="tocline4"><a href="#UIstates">6.6.4. UI element states pseudo-classes</a>
+        <li class="tocline4"><a href="#structural-pseudos">6.6.5. Structural pseudo-classes</a>
+         <ul>
+          <li><a href="#root-pseudo">:root pseudo-class</a>
+          <li><a href="#nth-child-pseudo">:nth-child() pseudo-class</a>
+          <li><a href="#nth-last-child-pseudo">:nth-last-child()</a>
+          <li><a href="#nth-of-type-pseudo">:nth-of-type() pseudo-class</a>
+          <li><a href="#nth-last-of-type-pseudo">:nth-last-of-type()</a>
+          <li><a href="#first-child-pseudo">:first-child pseudo-class</a>
+          <li><a href="#last-child-pseudo">:last-child pseudo-class</a>
+          <li><a href="#first-of-type-pseudo">:first-of-type pseudo-class</a>
+          <li><a href="#last-of-type-pseudo">:last-of-type pseudo-class</a>
+          <li><a href="#only-child-pseudo">:only-child pseudo-class</a>
+          <li><a href="#only-of-type-pseudo">:only-of-type pseudo-class</a>
+          <li><a href="#empty-pseudo">:empty pseudo-class</a></li>
+         </ul>
+        <li class="tocline4"><a href="#negation">6.6.7. The negation pseudo-class</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li><a href="#pseudo-elements">7. Pseudo-elements</a>
+     <ul>
+      <li><a href="#first-line">7.1. The ::first-line pseudo-element</a>
+      <li><a href="#first-letter">7.2. The ::first-letter pseudo-element</a>
+      <li><a href="#UIfragments">7.3. The ::selection pseudo-element</a>
+      <li><a href="#gen-content">7.4. The ::before and ::after pseudo-elements</a></li>
+     </ul>
+    <li class="tocline2"><a href="#combinators">8. Combinators</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#descendant-combinators">8.1. Descendant combinators</a>
+      <li class="tocline3"><a href="#child-combinators">8.2. Child combinators</a>
+      <li class="tocline3"><a href="#sibling-combinators">8.3. Sibling combinators</a>
+       <ul class="toc">
+        <li class="tocline4"><a href="#adjacent-sibling-combinators">8.3.1. Adjacent sibling combinator</a>
+        <li class="tocline4"><a href="#general-sibling-combinators">8.3.2. General sibling combinator</a></li>
+       </ul>
+      </li>
+     </ul>
+    <li class="tocline2"><a href="#specificity">9. Calculating a selector's specificity</a>
+    <li class="tocline2"><a href="#w3cselgrammar">10. The grammar of Selectors</a>
+     <ul class="toc">
+      <li class="tocline3"><a href="#grammar">10.1. Grammar</a>
+      <li class="tocline3"><a href="#lex">10.2. Lexical scanner</a></li>
+     </ul>
+    <li class="tocline2"><a href="#downlevel">11. Namespaces and down-level clients</a>
+    <li class="tocline2"><a href="#profiling">12. Profiles</a>
+    <li><a href="#Conformance">13. Conformance and requirements</a>
+    <li><a href="#Tests">14. Tests</a>
+    <li><a href="#ACKS">15. Acknowledgements</a>
+    <li class="tocline2"><a href="#references">16. References</a>
+   </ul>
+
+  </div>
+
+  <h2><a name=context>1. Introduction</a></h2>
+
+  <h3><a name=dependencies></a>1.1. Dependencies</h3>
+
+  <p>Some features of this specification are specific to CSS, or have
+  particular limitations or rules specific to CSS. In this
+  specification, these have been described in terms of CSS2.1. <a
+  href="#refsCSS21">[CSS21]</a></p>
+
+  <h3><a name=terminology></a>1.2. Terminology</h3>
+
+  <p>All of the text of this specification is normative except
+  examples, notes, and sections explicitly marked as
+  non-normative.</p>
+
+  <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
+
+  <p><em>This section is non-normative.</em></p>
+
+  <p>The main differences between the selectors in CSS2 and those in
+  Selectors are:
+
+  <ul>
+
+   <li>the list of basic definitions (selector, group of selectors,
+   simple selector, etc.) has been changed; in particular, what was
+   referred to in CSS2 as a simple selector is now called a sequence
+   of simple selectors, and the term "simple selector" is now used for
+   the components of this sequence</li>
+
+   <li>an optional namespace component is now allowed in type element
+   selectors, the universal selector and attribute selectors</li>
+
+   <li>a <a href="#general-sibling-combinators">new combinator</a> has been introduced</li>
+
+   <li>new simple selectors including substring matching attribute
+   selectors, and new pseudo-classes</li>
+
+   <li>new pseudo-elements, and introduction of the "::" convention
+   for pseudo-elements</li>
+
+   <li>the grammar has been rewritten</li>
+
+   <li>profiles to be added to specifications integrating Selectors
+   and defining the set of selectors which is actually supported by
+   each specification</li>
+
+   <li>Selectors are now a CSS3 Module and an independent
+   specification; other specifications can now refer to this document
+   independently of CSS</li>
+
+   <li>the specification now has its own test suite</li>
+
+  </ul>
+
+<h2><a name=selectors></a>2. Selectors</h2>
+
+<p><em>This section is non-normative, as it merely summarizes the
+following sections.</em></p>
+
+<p>A Selector represents a structure. This structure can be used as a
+condition (e.g. in a CSS rule) that determines which elements a
+selector matches in the document tree, or as a flat description of the
+HTML or XML fragment corresponding to that structure.</p>
+
+<p>Selectors may range from simple element names to rich contextual
+representations.</p>
+
+<p>The following table summarizes the Selector syntax:</p>
+
+<table class="selectorsReview">
+  <thead>
+  <tr>
+    <th class="pattern">Pattern</th>
+    <th class="meaning">Meaning</th>
+    <th class="described">Described in section</th>
+    <th class="origin">First defined in CSS level</th></tr>
+  <tbody>
+  <tr>
+    <td class="pattern">*</td>
+    <td class="meaning">any element</td>
+    <td class="described"><a
+      href="#universal-selector">Universal
+      selector</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E</td>
+    <td class="meaning">an element of type E</td>
+    <td class="described"><a
+      href="#type-selectors">Type selector</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E[foo]</td>
+    <td class="meaning">an E element with a "foo" attribute</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is exactly
+      equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo~="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value is a list of
+      space-separated values, one of which is exactly equal to "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E[foo^="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value begins exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo$="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value ends exactly
+      with the string "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[foo*="bar"]</td>
+    <td class="meaning">an E element whose "foo" attribute value contains the
+      substring "bar"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E[hreflang|="en"]</td>
+    <td class="meaning">an E element whose "hreflang" attribute has a hyphen-separated
+      list of values beginning (from the left) with "en"</td>
+    <td class="described"><a
+      href="#attribute-selectors">Attribute
+      selectors</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:root</td>
+    <td class="meaning">an E element, root of the document</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-child(n)</td>
+    <td class="meaning">an E element, the n-th child of its parent, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:nth-last-of-type(n)</td>
+    <td class="meaning">an E element, the n-th sibling of its type, counting
+      from the last one</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-child</td>
+    <td class="meaning">an E element, first child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:last-child</td>
+    <td class="meaning">an E element, last child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:first-of-type</td>
+    <td class="meaning">an E element, first sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:last-of-type</td>
+    <td class="meaning">an E element, last sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-child</td>
+    <td class="meaning">an E element, only child of its parent</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:only-of-type</td>
+    <td class="meaning">an E element, only sibling of its type</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:empty</td>
+    <td class="meaning">an E element that has no children (including text
+    nodes)</td>
+    <td class="described"><a
+      href="#structural-pseudos">Structural
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:link<br>E:visited</td>
+    <td class="meaning">an E element being the source anchor of a hyperlink of
+      which the target is not yet visited (:link) or already visited
+    (:visited)</td>
+    <td class="described"><a
+      href="#link">The link
+      pseudo-classes</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:active<br>E:hover<br>E:focus</td>
+    <td class="meaning">an E element during certain user actions</td>
+    <td class="described"><a
+      href="#useraction-pseudos">The user
+      action pseudo-classes</a></td>
+    <td class="origin">1 and 2</td></tr>
+  <tr>
+    <td class="pattern">E:target</td>
+    <td class="meaning">an E element being the target of the referring URI</td>
+    <td class="described"><a
+      href="#target-pseudo">The target
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:lang(fr)</td>
+    <td class="meaning">an element of type E in language "fr" (the document
+      language specifies how language is determined)</td>
+    <td class="described"><a
+      href="#lang-pseudo">The :lang()
+      pseudo-class</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E:enabled<br>E:disabled</td>
+    <td class="meaning">a user interface element E which is enabled or
+    disabled</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E:checked<!--<br>E:indeterminate--></td>
+    <td class="meaning">a user interface element E which is checked<!-- or in an
+      indeterminate state--> (for instance a radio-button or checkbox)</td>
+    <td class="described"><a
+      href="#UIstates">The UI element states
+      pseudo-classes</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::first-line</td>
+    <td class="meaning">the first formatted line of an E element</td>
+    <td class="described"><a
+      href="#first-line">The ::first-line
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::first-letter</td>
+    <td class="meaning">the first formatted letter of an E element</td>
+    <td class="described"><a
+      href="#first-letter">The ::first-letter
+      pseudo-element</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E::selection</td>
+    <td class="meaning">the portion of an E element that is currently
+      selected/highlighted by the user</td>
+    <td class="described"><a
+      href="#UIfragments">The UI element
+      fragments pseudo-elements</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E::before</td>
+    <td class="meaning">generated content before an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::before
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E::after</td>
+    <td class="meaning">generated content after an E element</td>
+    <td class="described"><a
+      href="#gen-content">The ::after
+      pseudo-element</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E.warning</td>
+    <td class="meaning">an E element whose class is
+"warning" (the document language specifies how class is determined).</td>
+    <td class="described"><a
+      href="#class-html">Class
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E#myid</td>
+    <td class="meaning">an E element with ID equal to "myid".</td>
+    <td class="described"><a
+      href="#id-selectors">ID
+    selectors</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E:not(s)</td>
+    <td class="meaning">an E element that does not match simple selector s</td>
+    <td class="described"><a
+      href="#negation">Negation
+      pseudo-class</a></td>
+    <td class="origin">3</td></tr>
+  <tr>
+    <td class="pattern">E F</td>
+    <td class="meaning">an F element descendant of an E element</td>
+    <td class="described"><a
+      href="#descendant-combinators">Descendant
+      combinator</a></td>
+    <td class="origin">1</td></tr>
+  <tr>
+    <td class="pattern">E &gt; F</td>
+    <td class="meaning">an F element child of an E element</td>
+    <td class="described"><a
+      href="#child-combinators">Child
+      combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E + F</td>
+    <td class="meaning">an F element immediately preceded by an E element</td>
+    <td class="described"><a
+      href="#adjacent-sibling-combinators">Adjacent sibling combinator</a></td>
+    <td class="origin">2</td></tr>
+  <tr>
+    <td class="pattern">E ~ F</td>
+    <td class="meaning">an F element preceded by an E element</td>
+    <td class="described"><a
+      href="#general-sibling-combinators">General sibling combinator</a></td>
+    <td class="origin">3</td></tr></tbody></table>
+
+<p>The meaning of each selector is derived from the table above by
+prepending "matches" to the contents of each cell in the "Meaning"
+column.</p>
+
+<h2><a name=casesens>3. Case sensitivity</a></h2>
+
+<p>The case sensitivity of document language element names, attribute
+names, and attribute values in selectors depends on the document
+language. For example, in HTML, element names are case-insensitive,
+but in XML, they are case-sensitive.</p>
+
+<h2><a name=selector-syntax>4. Selector syntax</a></h2>
+
+<p>A <dfn><a name=selector>selector</a></dfn> is a chain of one
+or more <a href="#sequence">sequences of simple selectors</a>
+separated by <a href="#combinators">combinators</a>.</p>
+
+<p>A <dfn><a name=sequence>sequence of simple selectors</a></dfn>
+is a chain of <a href="#simple-selectors-dfn">simple selectors</a>
+that are not separated by a <a href="#combinators">combinator</a>. It
+always begins with a <a href="#type-selectors">type selector</a> or a
+<a href="#universal-selector">universal selector</a>. No other type
+selector or universal selector is allowed in the sequence.</p>
+
+<p>A <dfn><a name=simple-selectors-dfn></a><a
+href="#simple-selectors">simple selector</a></dfn> is either a <a
+href="#type-selectors">type selector</a>, <a
+href="#universal-selector">universal selector</a>, <a
+href="#attribute-selectors">attribute selector</a>, <a
+href="#class-html">class selector</a>, <a
+href="#id-selectors">ID selector</a>, <a
+href="#content-selectors">content selector</a>, or <a
+href="#pseudo-classes">pseudo-class</a>. One <a
+href="#pseudo-elements">pseudo-element</a> may be appended to the last
+sequence of simple selectors.</p>
+
+<p><dfn>Combinators</dfn> are: white space, &quot;greater-than
+sign&quot; (U+003E, <code>&gt;</code>), &quot;plus sign&quot; (U+002B,
+<code>+</code>) and &quot;tilde&quot; (U+007E, <code>~</code>).  White
+space may appear between a combinator and the simple selectors around
+it. <a name=whitespace></a>Only the characters "space" (U+0020), "tab"
+(U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form
+feed" (U+000C) can occur in white space. Other space-like characters,
+such as "em-space" (U+2003) and "ideographic space" (U+3000), are
+never part of white space.</p>
+
+<p>The elements of a document tree that are represented by a selector
+are the <dfn><a name=subject></a>subjects of the selector</dfn>. A
+selector consisting of a single sequence of simple selectors
+represents any element satisfying its requirements. Prepending another
+sequence of simple selectors and a combinator to a sequence imposes
+additional matching constraints, so the subjects of a selector are
+always a subset of the elements represented by the last sequence of
+simple selectors.</p>
+
+<p>An empty selector, containing no sequence of simple selectors and
+no pseudo-element, is an <a href="#Conformance">invalid
+selector</a>.</p>
+
+<h2><a name=grouping>5. Groups of selectors</a></h2>
+
+<p>When several selectors share the same declarations, they may be
+grouped into a comma-separated list. (A comma is U+002C.)</p>
+
+<div class="example">
+<p>CSS examples:</p>
+<p>In this example, we condense three rules with identical
+declarations into one. Thus,</p>
+<pre>h1 { font-family: sans-serif }
+h2 { font-family: sans-serif }
+h3 { font-family: sans-serif }</pre>
+<p>is equivalent to:</p>
+<pre>h1, h2, h3 { font-family: sans-serif }</pre>
+</div>
+
+<p><strong>Warning</strong>: the equivalence is true in this example
+because all the selectors are valid selectors. If just one of these
+selectors were invalid, the entire group of selectors would be
+invalid. This would invalidate the rule for all three heading
+elements, whereas in the former case only one of the three individual
+heading rules would be invalidated.</p>
+
+
+<h2><a name=simple-selectors>6. Simple selectors</a></h2>
+
+<h3><a name=type-selectors>6.1. Type selector</a></h3>
+
+<p>A <dfn>type selector</dfn> is the name of a document language
+element type. A type selector represents an instance of the element
+type in the document tree.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents an <code>h1</code> element in the document tree:</p>
+ <pre>h1</pre>
+</div>
+
+
+<h4><a name=typenmsp>6.1.1. Type selectors and namespaces</a></h4>
+
+<p>Type selectors allow an optional namespace (<a
+href="#refsXMLNAMES">[XMLNAMES]</a>) component. A namespace prefix
+that has been previously declared may be prepended to the element name
+separated by the namespace separator &quot;vertical bar&quot;
+(U+007C, <code>|</code>).</p>
+
+<p>The namespace component may be left empty to indicate that the
+selector is only to represent elements with no declared namespace.</p>
+
+<p>An asterisk may be used for the namespace prefix, indicating that
+the selector represents elements in any namespace (including elements
+with no namespace).</p>
+
+<p>Element type selectors that have no namespace component (no
+namespace separator), represent elements without regard to the
+element's namespace (equivalent to "<code>*|</code>") unless a default
+namespace has been declared. If a default namespace has been declared,
+the selector will represent only elements in the default
+namespace.</p>
+
+<p>A type selector containing a namespace prefix that has not been
+previously declared is an <a href="#Conformance">invalid</a> selector.
+The mechanism for declaring a namespace prefix is left up to the
+language implementing Selectors. In CSS, such a mechanism is defined
+in the General Syntax module.</p>
+
+<p>In a namespace-aware client, element type selectors will only match
+against the <a
+href="http://www.w3.org/TR/REC-xml-names/#NT-LocalPart">local part</a>
+of the element's <a
+href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">qualified
+name</a>. See <a href="#downlevel">below</a> for notes about matching
+behaviors in down-level clients.</p>
+
+<p>In summary:</p>
+
+<dl>
+  <dt><code>ns|E</code></dt>
+  <dd>elements with name E in namespace ns</dd>
+  <dt><code>*|E</code></dt>
+  <dd>elements with name E in any namespace, including those without any
+  declared namespace</dd>
+  <dt><code>|E</code></dt>
+  <dd>elements with name E without any declared namespace</dd>
+  <dt><code>E</code></dt>
+  <dd>if no default namespace has been specified, this is equivalent to *|E.
+  Otherwise it is equivalent to ns|E where ns is the default namespace.</dd>
+</dl>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <pre>@namespace foo url(http://www.example.com);
+ foo|h1 { color: blue }
+ foo|* { color: yellow }
+ |h1 { color: red }
+ *|h1 { color: green }
+ h1 { color: green }</pre>
+
+ <p>The first rule will match only <code>h1</code> elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The second rule will match all elements in the
+ "http://www.example.com" namespace.</p>
+
+ <p>The third rule will match only <code>h1</code> elements without
+ any declared namespace.</p>
+
+ <p>The fourth rule will match <code>h1</code> elements in any
+ namespace (including those without any declared namespace).</p>
+
+ <p>The last rule is equivalent to the fourth rule because no default
+ namespace has been defined.</p>
+
+</div>
+
+<h3><a name=universal-selector>6.2. Universal selector</a> </h3>
+
+<p>The <dfn>universal selector</dfn>, written &quot;asterisk&quot;
+(<code>*</code>), represents the qualified name of any element
+type. It represents any single element in the document tree in any
+namespace (including those without any declared namespace) if no
+default namespace has been specified. If a default namespace has been
+specified, see <a href="#univnmsp">Universal selector and
+Namespaces</a> below.</p>
+
+<p>If the universal selector is not the only component of a sequence
+of simple selectors, the <code>*</code> may be omitted.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <ul>
+  <li><code>*[hreflang|=en]</code> and <code>[hreflang|=en]</code> are equivalent,</li>
+  <li><code>*.warning</code> and <code>.warning</code> are equivalent,</li>
+  <li><code>*#myid</code> and <code>#myid</code> are equivalent.</li>
+ </ul>
+</div>
+
+<p class="note"><strong>Note:</strong> it is recommended that the
+<code>*</code>, representing the universal selector, not be
+omitted.</p>
+
+<h4><a name=univnmsp>6.2.1. Universal selector and namespaces</a></h4>
+
+<p>The universal selector allows an optional namespace component. It
+is used as follows:</p>
+
+<dl>
+ <dt><code>ns|*</code></dt>
+ <dd>all elements in namespace ns</dd>
+ <dt><code>*|*</code></dt>
+ <dd>all elements</dd>
+ <dt><code>|*</code></dt>
+ <dd>all elements without any declared namespace</dd>
+ <dt><code>*</code></dt>
+ <dd>if no default namespace has been specified, this is equivalent to *|*.
+ Otherwise it is equivalent to ns|* where ns is the default namespace.</dd>
+</dl>
+
+<p>A universal selector containing a namespace prefix that has not
+been previously declared is an <a href="#Conformance">invalid</a>
+selector.  The mechanism for declaring a namespace prefix is left up
+to the language implementing Selectors.  In CSS, such a mechanism is
+defined in the General Syntax module.</p>
+
+
+<h3><a name=attribute-selectors>6.3. Attribute selectors</a></h3>
+
+<p>Selectors allow the representation of an element's attributes. When
+a selector is used as an expression to match against an element,
+attribute selectors must be considered to match an element if that
+element has an attribute that matches the attribute represented by the
+attribute selector.</p>
+
+<h4><a name=attribute-representation>6.3.1. Attribute presence and values
+selectors</a></h4>
+
+<p>CSS2 introduced four attribute selectors:</p>
+
+<dl>
+  <dt><code>[att]</code>
+  <dd>Represents an element with the <code>att</code> attribute, whatever the value of
+  the attribute.</dd>
+  <dt><code>[att=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is exactly
+  "val".</dd>
+  <dt><code>[att~=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value is a <a
+  href="#whitespace">whitespace</a>-separated list of words, one of
+  which is exactly "val". If "val" contains whitespace, it will never
+  represent anything (since the words are <em>separated</em> by
+  spaces).</dd>
+  <dt><code>[att|=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute, its value either
+  being exactly "val" or beginning with "val" immediately followed by
+  "-" (U+002D).  This is primarily intended to allow language subcode
+  matches (e.g., the <code>hreflang</code> attribute on the
+  <code>link</code> element in HTML) as described in RFC 3066 (<a
+  href="#refsRFC3066">[RFC3066]</a>).  For <code>lang</code> (or
+  <code>xml:lang</code>) language subcode matching, please see <a
+  href="#lang-pseudo">the <code>:lang</code> pseudo-class</a>.</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names and values in selectors depends on
+the document language.</p>
+
+<div class="example">
+
+  <p>Examples:</p>
+
+  <p>The following attribute selector represents an <code>h1</code>
+  element that carries the <code>title</code> attribute, whatever its
+  value:</p>
+
+  <pre>h1[title]</pre>
+
+  <p>In the following example, the selector represents a
+  <code>span</code> element whose <code>class</code> attribute has
+  exactly the value "example":</p>
+
+  <pre>span[class="example"]</pre>
+
+  <p>Multiple attribute selectors can be used to represent several
+  attributes of an element, or several conditions on the same
+  attribute. Here, the selector represents a <code>span</code> element
+  whose <code>hello</code> attribute has exactly the value "Cleveland"
+  and whose <code>goodbye</code> attribute has exactly the value
+  "Columbus":</p>
+
+  <pre>span[hello="Cleveland"][goodbye="Columbus"]</pre>
+
+  <p>The following selectors illustrate the differences between "="
+  and "~=".  The first selector will represent, for example, the value
+  "copyright copyleft copyeditor" on a <code>rel</code> attribute. The
+  second selector will only represent an <code>a</code> element with
+  an <code>href</code> attribute having the exact value
+  "http://www.w3.org/".</p>
+
+  <pre>a[rel~="copyright"]
+a[href="http://www.w3.org/"]</pre>
+
+  <p>The following selector represents a <code>link</code> element
+  whose <code>hreflang</code> attribute is exactly "fr".</p>
+
+  <pre>link[hreflang=fr]</pre>
+
+  <p>The following selector represents a <code>link</code> element for
+  which the values of the <code>hreflang</code> attribute begins with
+  "en", including "en", "en-US", and "en-cockney":</p>
+
+  <pre>link[hreflang|="en"]</pre>
+
+  <p>Similarly, the following selectors represents a
+  <code>DIALOGUE</code> element whenever it has one of two different
+  values for an attribute <code>character</code>:</p>
+
+  <pre>DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]</pre>
+
+</div>
+
+<h4><a name=attribute-substrings></a>6.3.2. Substring matching attribute
+selectors</h4>
+
+<p>Three additional attribute selectors are provided for matching
+substrings in the value of an attribute:</p>
+
+<dl>
+  <dt><code>[att^=val]</code></dt>
+  <dd>Represents an element with the <code>att</code> attribute whose value begins
+  with the prefix "val".</dd>
+  <dt><code>[att$=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value ends with
+  the suffix "val".</dd>
+  <dt><code>[att*=val]</code>
+  <dd>Represents an element with the <code>att</code> attribute whose value contains
+  at least one instance of the substring "val".</dd>
+</dl>
+
+<p>Attribute values must be identifiers or strings. The
+case-sensitivity of attribute names in selectors depends on the
+document language.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents an HTML <code>object</code>, referencing an
+ image:</p>
+ <pre>object[type^="image/"]</pre>
+ <p>The following selector represents an HTML anchor <code>a</code> with an
+ <code>href</code> attribute whose value ends with ".html".</p>
+ <pre>a[href$=".html"]</pre>
+ <p>The following selector represents an HTML paragraph with a <code>title</code>
+ attribute whose value contains the substring "hello"</p>
+ <pre>p[title*="hello"]</pre>
+</div>
+
+<h4><a name=attrnmsp>6.3.3. Attribute selectors and namespaces</a></h4>
+
+<p>Attribute selectors allow an optional namespace component to the
+attribute name. A namespace prefix that has been previously declared
+may be prepended to the attribute name separated by the namespace
+separator &quot;vertical bar&quot; (<code>|</code>). In keeping with
+the Namespaces in the XML recommendation, default namespaces do not
+apply to attributes, therefore attribute selectors without a namespace
+component apply only to attributes that have no declared namespace
+(equivalent to "<code>|attr</code>"). An asterisk may be used for the
+namespace prefix indicating that the selector is to match all
+attribute names without regard to the attribute's namespace.
+
+<p>An attribute selector with an attribute name containing a namespace
+prefix that has not been previously declared is an <a
+href="#Conformance">invalid</a> selector.  The mechanism for declaring
+a namespace prefix is left up to the language implementing Selectors.
+In CSS, such a mechanism is defined in the General Syntax module.
+
+<div class="example">
+  <p>CSS examples:</p>
+  <pre>@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }</pre>
+
+  <p>The first rule will match only elements with the attribute
+  <code>att</code> in the "http://www.example.com" namespace with the
+  value "val".</p>
+
+  <p>The second rule will match only elements with the attribute
+  <code>att</code> regardless of the namespace of the attribute
+  (including no declared namespace).</p>
+
+  <p>The last two rules are equivalent and will match only elements
+  with the attribute <code>att</code> where the attribute is not
+  declared to be in a namespace.</p>
+
+</div>
+
+<h4><a name=def-values>6.3.4. Default attribute values in DTDs</a></h4>
+
+<p>Attribute selectors represent explicitly set attribute values in
+the document tree. Default attribute values may be defined in a DTD or
+elsewhere, but cannot always be selected by attribute
+selectors. Selectors should be designed so that they work even if the
+default values are not included in the document tree.</p>
+
+<p>More precisely, a UA is <em>not</em> required to read an "external
+subset" of the DTD but <em>is</em> required to look for default
+attribute values in the document's "internal subset." (See <a
+href="#refsXML10">[XML10]</a> for definitions of these subsets.)</p>
+
+<p>A UA that recognizes an XML namespace <a
+href="#refsXMLNAMES">[XMLNAMES]</a> is not required to use its
+knowledge of that namespace to treat default attribute values as if
+they were present in the document. (For example, an XHTML UA is not
+required to use its built-in knowledge of the XHTML DTD.)</p>
+
+<p class="note"><strong>Note:</strong> Typically, implementations
+choose to ignore external subsets.</p>
+
+<div class="example">
+<p>Example:</p>
+
+<p>Consider an element EXAMPLE with an attribute "notation" that has a
+default value of "decimal". The DTD fragment might be</p>
+
+<pre class="dtd-example">&lt;!ATTLIST EXAMPLE notation (decimal,octal) "decimal"></pre>
+
+<p>If the style sheet contains the rules</p>
+
+<pre>EXAMPLE[notation=decimal] { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>the first rule will not match elements whose "notation" attribute
+is set by default, i.e. not set explicitly. To catch all cases, the
+attribute selector for the default value must be dropped:</p>
+
+<pre>EXAMPLE                   { /*... default property settings ...*/ }
+EXAMPLE[notation=octal]   { /*... other settings...*/ }</pre>
+
+<p>Here, because the selector <code>EXAMPLE[notation=octal]</code> is
+more specific than the tag
+selector alone, the style declarations in the second rule will override
+those in the first for elements that have a "notation" attribute value
+of "octal". Care has to be taken that all property declarations that
+are to apply only to the default case are overridden in the non-default
+cases' style rules.</p>
+
+</div>
+
+<h3><a name=class-html>6.4. Class selectors</a></h3>
+
+<p>Working with HTML, authors may use the period (U+002E,
+<code>.</code>) notation as an alternative to the <code>~=</code>
+notation when representing the <code>class</code> attribute. Thus, for
+HTML, <code>div.value</code> and <code>div[class~=value]</code> have
+the same meaning. The attribute value must immediately follow the
+&quot;period&quot; (<code>.</code>).</p>
+
+<p>UAs may apply selectors using the period (.) notation in XML
+documents if the UA has namespace-specific knowledge that allows it to
+determine which attribute is the &quot;class&quot; attribute for the
+respective namespace. One such example of namespace-specific knowledge
+is the prose in the specification for a particular namespace (e.g. SVG
+1.0 <a href="#refsSVG">[SVG]</a> describes the <a
+href="http://www.w3.org/TR/2001/PR-SVG-20010719/styling.html#ClassAttribute">SVG
+&quot;class&quot; attribute</a> and how a UA should interpret it, and
+similarly MathML 1.01 <a href="#refsMATH">[MATH]</a> describes the <a
+href="http://www.w3.org/1999/07/REC-MathML-19990707/chapter2.html#sec2.3.4">MathML
+&quot;class&quot; attribute</a>.)</p>
+
+<div class="example">
+ <p>CSS examples:</p>
+
+ <p>We can assign style information to all elements with
+ <code>class~="pastoral"</code> as follows:</p>
+
+  <pre>*.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>or just</p>
+
+  <pre>.pastoral { color: green }  /* all elements with class~=pastoral */</pre>
+
+  <p>The following assigns style only to H1 elements with
+  <code>class~="pastoral"</code>:</p>
+
+  <pre>H1.pastoral { color: green }  /* H1 elements with class~=pastoral */</pre>
+
+  <p>Given these rules, the first H1 instance below would not have
+  green text, while the second would:</p>
+
+  <pre>&lt;H1&gt;Not green&lt;/H1&gt;
+&lt;H1 class="pastoral"&gt;Very green&lt;/H1&gt;</pre>
+
+</div>
+
+<p>To represent a subset of "class" values, each value must be preceded
+by a ".", in any order.</P>
+
+<div class="example">
+
+  <p>CSS example:</p>
+
+  <p>The following rule matches any P element whose "class" attribute
+  has been assigned a list of <a
+  href="#whitespace">whitespace</a>-separated values that includes
+  "pastoral" and "marine":</p>
+
+  <pre>p.pastoral.marine { color: green }</pre>
+
+  <p>This rule matches when <code>class="pastoral blue aqua
+  marine"</code> but does not match for <code>class="pastoral
+  blue"</code>.</p>
+
+</div>
+
+<p class="note"><strong>Note:</strong> Because CSS gives considerable
+power to the "class" attribute, authors could conceivably design their
+own "document language" based on elements with almost no associated
+presentation (such as DIV and SPAN in HTML) and assigning style
+information through the "class" attribute.  Authors should avoid this
+practice since the structural elements of a document language often
+have recognized and accepted meanings and author-defined classes may
+not.</p>
+
+<p class="note"><strong>Note:</strong> If an element has multiple
+class attributes, their values must be concatenated with spaces
+between the values before searching for the class. As of this time the
+working group is not aware of any manner in which this situation can
+be reached, however, so this behavior is explicitly non-normative in
+this specification.</p>
+
+<h3><a name=id-selectors>6.5. ID selectors</a></h3>
+
+<p>Document languages may contain attributes that are declared to be
+of type ID. What makes attributes of type ID special is that no two
+such attributes can have the same value in a document, regardless of
+the type of the elements that carry them; whatever the document
+language, an ID typed attribute can be used to uniquely identify its
+element. In HTML all ID attributes are named "id"; XML applications
+may name ID attributes differently, but the same restriction
+applies.</p>
+
+<p>An ID-typed attribute of a document language allows authors to
+assign an identifier to one element instance in the document tree. W3C
+ID selectors represent an element instance based on its identifier. An
+ID selector contains a &quot;number sign&quot; (U+0023,
+<code>#</code>) immediately followed by the ID value, which must be an
+identifier.</p>
+
+<p>Selectors does not specify how a UA knows the ID-typed attribute of
+an element. The UA may, e.g., read a document's DTD, have the
+information hard-coded or ask the user.
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following ID selector represents an <code>h1</code> element
+  whose ID-typed attribute has the value "chapter1":</p>
+  <pre>h1#chapter1</pre>
+  <p>The following ID selector represents any element whose ID-typed
+  attribute has the value "chapter1":</p>
+  <pre>#chapter1</pre>
+  <p>The following selector represents any element whose ID-typed
+  attribute has the value "z98y".</p>
+  <pre>*#z98y</pre>
+</div>
+
+<p class="note"><strong>Note.</strong> In XML 1.0 <a
+href="#refsXML10">[XML10]</a>, the information about which attribute
+contains an element's IDs is contained in a DTD or a schema. When
+parsing XML, UAs do not always read the DTD, and thus may not know
+what the ID of an element is (though a UA may have namespace-specific
+knowledge that allows it to determine which attribute is the ID
+attribute for that namespace). If a style sheet designer knows or
+suspects that a UA may not know what the ID of an element is, he
+should use normal attribute selectors instead:
+<code>[name=p371]</code> instead of <code>#p371</code>.  Elements in
+XML 1.0 documents without a DTD do not have IDs at all.</p>
+
+<p>If an element has multiple ID attributes, all of them must be
+treated as IDs for that element for the purposes of the ID
+selector. Such a situation could be reached using mixtures of xml:id,
+DOM3 Core, XML DTDs, and namespace-specific knowledge.</p>
+
+<h3><a name=pseudo-classes>6.6. Pseudo-classes</a></h3>
+
+<p>The pseudo-class concept is introduced to permit selection based on
+information that lies outside of the document tree or that cannot be
+expressed using the other simple selectors.</p>
+
+<p>A pseudo-class always consists of a &quot;colon&quot;
+(<code>:</code>) followed by the name of the pseudo-class and
+optionally by a value between parentheses.</p>
+
+<p>Pseudo-classes are allowed in all sequences of simple selectors
+contained in a selector. Pseudo-classes are allowed anywhere in
+sequences of simple selectors, after the leading type selector or
+universal selector (possibly omitted). Pseudo-class names are
+case-insensitive. Some pseudo-classes are mutually exclusive, while
+others can be applied simultaneously to the same
+element. Pseudo-classes may be dynamic, in the sense that an element
+may acquire or lose a pseudo-class while a user interacts with the
+document.</p>
+
+
+<h4><a name=dynamic-pseudos>6.6.1. Dynamic pseudo-classes</a></h4>
+
+<p>Dynamic pseudo-classes classify elements on characteristics other
+than their name, attributes, or content, in principle characteristics
+that cannot be deduced from the document tree.</p>
+
+<p>Dynamic pseudo-classes do not appear in the document source or
+document tree.</p>
+
+
+<h5>The <a name=link>link pseudo-classes: :link and :visited</a></h5>
+
+<p>User agents commonly display unvisited links differently from
+previously visited ones. Selectors
+provides the pseudo-classes <code>:link</code> and
+<code>:visited</code> to distinguish them:</p>
+
+<ul>
+  <li>The <code>:link</code> pseudo-class applies to links that have
+  not yet been visited.</li>
+  <li>The <code>:visited</code> pseudo-class applies once the link has
+  been visited by the user. </li>
+</ul>
+
+<p>After some amount of time, user agents may choose to return a
+visited link to the (unvisited) ':link' state.</p>
+
+<p>The two states are mutually exclusive.</p>
+
+<div class="example">
+
+  <p>Example:</p>
+
+  <p>The following selector represents links carrying class
+  <code>external</code> and already visited:</p>
+
+  <pre>a.external:visited</pre>
+
+</div>
+
+<p class="note"><strong>Note:</strong> It is possible for style sheet
+authors to abuse the :link and :visited pseudo-classes to determine
+which sites a user has visited without the user's consent.
+
+<p>UAs may therefore treat all links as unvisited links, or implement
+other measures to preserve the user's privacy while rendering visited
+and unvisited links differently.</p>
+
+<h5>The <a name=useraction-pseudos>user action pseudo-classes
+:hover, :active, and :focus</a></h5>
+
+<p>Interactive user agents sometimes change the rendering in response
+to user actions. Selectors provides
+three pseudo-classes for the selection of an element the user is
+acting on.</p>
+
+<ul>
+
+  <li>The <code>:hover</code> pseudo-class applies while the user
+  designates an element with a pointing device, but does not activate
+  it. For example, a visual user agent could apply this pseudo-class
+  when the cursor (mouse pointer) hovers over a box generated by the
+  element. User agents not that do not support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> do not have to support this pseudo-class. Some conforming
+  user agents that support <a
+  href="http://www.w3.org/TR/REC-CSS2/media.html#interactive-media-group">interactive
+  media</a> may not be able to support this pseudo-class (e.g., a pen
+  device that does not detect hovering).</li>
+
+  <li>The <code>:active</code> pseudo-class applies while an element
+  is being activated by the user. For example, between the times the
+  user presses the mouse button and releases it.</li>
+
+  <li>The <code>:focus</code> pseudo-class applies while an element
+  has the focus (accepts keyboard or mouse events, or other forms of
+  input). </li>
+
+</ul>
+
+<p>There may be document language or implementation specific limits on
+which elements can become <code>:active</code> or acquire
+<code>:focus</code>.</p>
+
+<p>These pseudo-classes are not mutually exclusive. An element may
+match several pseudo-classes at the same time.</p>
+
+<p>Selectors doesn't define if the parent of an element that is
+':active' or ':hover' is also in that state.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <pre>a:link    /* unvisited links */
+a:visited /* visited links */
+a:hover   /* user hovers */
+a:active  /* active links */</pre>
+  <p>An example of combining dynamic pseudo-classes:</p>
+  <pre>a:focus
+a:focus:hover</pre>
+  <p>The last selector matches <code>a</code> elements that are in
+  the pseudo-class :focus and in the pseudo-class :hover.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> An element can be both ':visited'
+and ':active' (or ':link' and ':active').</p>
+
+<h4><a name=target-pseudo>6.6.2. The target pseudo-class :target</a></h4>
+
+<p>Some URIs refer to a location within a resource. This kind of URI
+ends with a &quot;number sign&quot; (#) followed by an anchor
+identifier (called the fragment identifier).</p>
+
+<p>URIs with fragment identifiers link to a certain element within the
+document, known as the target element. For instance, here is a URI
+pointing to an anchor named <code>section_2</code> in an HTML
+document:</p>
+
+<pre>http://example.com/html/top.html#section_2</pre>
+
+<p>A target element can be represented by the <code>:target</code>
+pseudo-class. If the document's URI has no fragment identifier, then
+the document has no target element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>p.note:target</pre>
+ <p>This selector represents a <code>p</code> element of class
+ <code>note</code> that is the target element of the referring
+ URI.</p>
+</div>
+
+<div class="example">
+ <p>CSS example:</p>
+ <p>Here, the <code>:target</code> pseudo-class is used to make the
+ target element red and place an image before it, if there is one:</p>
+ <pre>*:target { color : red }
+*:target::before { content : url(target.png) }</pre>
+</div>
+
+<h4><a name=lang-pseudo>6.6.3. The language pseudo-class :lang</a></h4>
+
+<p>If the document language specifies how the human language of an
+element is determined, it is possible to write selectors that
+represent an element based on its language. For example, in HTML <a
+href="#refsHTML4">[HTML4]</a>, the language is determined by a
+combination of the <code>lang</code> attribute, the <code>meta</code>
+element, and possibly by information from the protocol (such as HTTP
+headers). XML uses an attribute called <code>xml:lang</code>, and
+there may be other document language-specific methods for determining
+the language.</p>
+
+<p>The pseudo-class <code>:lang(C)</code> represents an element that
+is in language C. Whether an element is represented by a
+<code>:lang()</code> selector is based solely on the identifier C
+being either equal to, or a hyphen-separated substring of, the
+element's language value, in the same way as if performed by the <a
+href="#attribute-representation">'|='</a> operator in attribute
+selectors. The identifier C does not have to be a valid language
+name.</p>
+
+<p>C must not be empty. (If it is, the selector is invalid.)</p>
+
+<p class="note"><strong>Note:</strong> It is recommended that
+documents and protocols indicate language using codes from RFC 3066 <a
+href="#refsRFC3066">[RFC3066]</a> or its successor, and by means of
+"xml:lang" attributes in the case of XML-based documents <a
+href="#refsXML10">[XML10]</a>. See <a
+href="http://www.w3.org/International/questions/qa-lang-2or3.html">
+"FAQ: Two-letter or three-letter language codes."</a></p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The two following selectors represent an HTML document that is in
+  Belgian, French, or German. The two next selectors represent
+  <code>q</code> quotations in an arbitrary element in Belgian, French,
+  or German.</p>
+  <pre>html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) &gt; q
+:lang(de) &gt; q</pre>
+</div>
+
+<h4><a name=UIstates>6.6.4. The UI element states pseudo-classes</a></h4>
+
+<h5><a name=enableddisabled>The :enabled and :disabled pseudo-classes</a></h5>
+
+<p>The <code>:enabled</code> pseudo-class allows authors to customize
+the look of user interface elements that are enabled &mdash; which the
+user can select or activate in some fashion (e.g. clicking on a button
+with a mouse).  There is a need for such a pseudo-class because there
+is no way to programmatically specify the default appearance of say,
+an enabled <code>input</code> element without also specifying what it
+would look like when it was disabled.</p>
+
+<p>Similar to <code>:enabled</code>, <code>:disabled</code> allows the
+author to specify precisely how a disabled or inactive user interface
+element should look.</p>
+
+<p>Most elements will be neither enabled nor disabled.  An element is
+enabled if the user can either activate it or transfer the focus to
+it. An element is disabled if it could be enabled, but the user cannot
+presently activate it or transfer focus to it.</p>
+
+
+<h5><a name=checked>The :checked pseudo-class</a></h5>
+
+<p>Radio and checkbox elements can be toggled by the user. Some menu
+items are "checked" when the user selects them. When such elements are
+toggled "on" the <code>:checked</code> pseudo-class applies. The
+<code>:checked</code> pseudo-class initially applies to such elements
+that have the HTML4 <code>selected</code> and <code>checked</code>
+attributes as described in <a
+href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.2.1">Section
+17.2.1 of HTML4</a>, but of course the user can toggle "off" such
+elements in which case the <code>:checked</code> pseudo-class would no
+longer apply. While the <code>:checked</code> pseudo-class is dynamic
+in nature, and is altered by user action, since it can also be based
+on the presence of the semantic HTML4 <code>selected</code> and
+<code>checked</code> attributes, it applies to all media.
+
+
+<h5><a name=indeterminate>The :indeterminate pseudo-class</a></h5>
+
+<div class="note">
+
+<p>Radio and checkbox elements can be toggled by the user, but are
+sometimes in an indeterminate state, neither checked nor unchecked.
+This can be due to an element attribute, or DOM manipulation.</p>
+
+<p>A future version of this specification may introduce an
+<code>:indeterminate</code> pseudo-class that applies to such elements.
+<!--While the <code>:indeterminate</code> pseudo-class is dynamic in
+nature, and is altered by user action, since it can also be based on
+the presence of an element attribute, it applies to all media.</p>
+
+<p>Components of a radio-group initialized with no pre-selected choice
+are an example of :indeterminate state.--></p>
+
+</div>
+
+
+<h4><a name=structural-pseudos>6.6.5. Structural pseudo-classes</a></h4>
+
+<p>Selectors introduces the concept of <dfn>structural
+pseudo-classes</dfn> to permit selection based on extra information that lies in
+the document tree but cannot be represented by other simple selectors or
+combinators.
+
+<p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
+not counted when calculating the position of an element in the list of
+children of its parent. When calculating the position of an element in
+the list of children of its parent, the index numbering starts at 1.
+
+
+<h5><a name=root-pseudo>:root pseudo-class</a></h5>
+
+<p>The <code>:root</code> pseudo-class represents an element that is
+the root of the document. In HTML 4, this is always the
+<code>HTML</code> element.
+
+
+<h5><a name=nth-child-pseudo>:nth-child() pseudo-class</a></h5>
+
+<p>The
+<code>:nth-child(<var>a</var><code>n</code>+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>before</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. In
+other words, this matches the <var>b</var>th child of an element after
+all the children have been split into groups of <var>a</var> elements
+each. For example, this allows the selectors to address every other
+row in a table, and could be used to alternate the color
+of paragraph text in a cycle of four. The <var>a</var> and
+<var>b</var> values must be zero, negative integers or positive
+integers. The index of the first child of an element is 1.
+
+<p>In addition to this, <code>:nth-child()</code> can take
+'<code>odd</code>' and '<code>even</code>' as arguments instead.
+'<code>odd</code>' has the same signification as <code>2n+1</code>,
+and '<code>even</code>' has the same signification as <code>2n</code>.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n)   /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }</pre>
+</div>
+
+<p>When <var>a</var>=0, no repeating is used, so for example
+<code>:nth-child(0n+5)</code> matches only the fifth child. When
+<var>a</var>=0, the <var>a</var><code>n</code> part need not be
+included, so the syntax simplifies to
+<code>:nth-child(<var>b</var>)</code> and the last example simplifies
+to <code>:nth-child(5)</code>.
+
+<div class="example">
+<p>Examples:</p>
+<pre>foo:nth-child(0n+1)   /* represents an element foo, first child of its parent element */
+foo:nth-child(1)      /* same */</pre>
+</div>
+
+<p>When <var>a</var>=1, the number may be omitted from the rule.
+
+<div class="example">
+<p>Examples:</p>
+<p>The following selectors are therefore equivalent:</p>
+<pre>bar:nth-child(1n+0)   /* represents all bar elements, specificity (0,1,1) */
+bar:nth-child(n+0)    /* same */
+bar:nth-child(n)      /* same */
+bar                   /* same but lower specificity (0,0,1) */</pre>
+</div>
+
+<p>If <var>b</var>=0, then every <var>a</var>th element is picked. In
+such a case, the <var>b</var> part may be omitted.
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(2n) /* same */</pre>
+</div>
+
+<p>If both <var>a</var> and <var>b</var> are equal to zero, the
+pseudo-class represents no element in the document tree.</p>
+
+<p>The value <var>a</var> can be negative, but only the positive
+values of <var>a</var><code>n</code>+<var>b</var>, for
+<code>n</code>&ge;0, may represent an element in the document
+tree.</p>
+
+<div class="example">
+<p>Example:</p>
+<pre>html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */</pre>
+</div>
+
+<p>When the value <var>b</var> is negative, the "+" character in the
+expression must be removed (it is effectively replaced by the "-"
+character indicating the negative value of <var>b</var>).</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */</pre>
+</div>
+
+
+<h5><a name=nth-last-child-pseudo>:nth-last-child() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-child(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings
+<strong>after</strong> it in the document tree, for a given positive
+integer or zero value of <code>n</code>, and has a parent element. See
+<code>:nth-child()</code> pseudo-class for the syntax of its argument.
+It also accepts the '<code>even</code>' and '<code>odd</code>' values
+as arguments.
+
+
+<div class="example">
+<p>Examples:</p>
+<pre>tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
+
+foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
+                              counting from the last one */</pre>
+</div>
+
+
+<h5><a name=nth-of-type-pseudo>:nth-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>before</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. In other words, this matches the <var>b</var>th child
+of that type after all the children of that type have been split into
+groups of a elements each. See <code>:nth-child()</code> pseudo-class
+for the syntax of its argument. It also accepts the
+'<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+<p>CSS example:</p>
+<p>This allows an author to alternate the position of floated images:</p>
+<pre>img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }</pre>
+</div>
+
+
+<h5><a name=nth-last-of-type-pseudo>:nth-last-of-type() pseudo-class</a></h5>
+
+<p>The <code>:nth-last-of-type(<var>a</var>n+<var>b</var>)</code>
+pseudo-class notation represents an element that has
+<var>a</var><code>n</code>+<var>b</var>-1 siblings with the same
+element name <strong>after</strong> it in the document tree, for a
+given zero or positive integer value of <code>n</code>, and has a
+parent element. See <code>:nth-child()</code> pseudo-class for the
+syntax of its argument. It also accepts the '<code>even</code>' and '<code>odd</code>' values.
+
+
+<div class="example">
+ <p>Example:</p>
+ <p>To represent all <code>h2</code> children of an XHTML
+ <code>body</code> except the first and last, one could use the
+ following selector:</p>
+ <pre>body &gt; h2:nth-of-type(n+2):nth-last-of-type(n+2)</pre>
+ <p>In this case, one could also use <code>:not()</code>, although the
+ selector ends up being just as long:</p>
+ <pre>body &gt; h2:not(:first-of-type):not(:last-of-type)</pre>
+</div>
+
+
+<h5><a name=first-child-pseudo>:first-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-child(1)</code>. The <code>:first-child</code> pseudo-class
+represents an element that is the first child of some other element.
+
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following selector represents a <code>p</code> element that is
+  the first child of a <code>div</code> element:</p>
+  <pre>div &gt; p:first-child</pre>
+  <p>This selector can represent the <code>p</code> inside the
+  <code>div</code> of the following fragment:</p>
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
+fragment:
+  <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
+&lt;div class="note"&gt;
+   &lt;h2&gt; Note &lt;/h2&gt;
+   &lt;p&gt; The first P inside the note.&lt;/p&gt;
+&lt;/div&gt;</pre>
+  <p>The following two selectors are usually equivalent:</p>
+  <pre>* &gt; a:first-child /* a is first child of any element */
+a:first-child /* Same (assuming a is not the root element) */</pre>
+</div>
+
+<h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
+represents an element that is the last child of some other element.
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents a list item <code>li</code> that
+ is the last child of an ordered list <code>ol</code>.
+ <pre>ol &gt; li:last-child</pre>
+</div>
+
+<h5><a name=first-of-type-pseudo>:first-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
+represents an element that is the first sibling of its type in the list of
+children of its parent element.
+
+<div class="example">
+<p>Example:</p>
+<p>The following selector represents a definition title
+<code>dt</code> inside a definition list <code>dl</code>, this
+<code>dt</code> being the first of its type in the list of children of
+its parent element.</p>
+<pre>dl dt:first-of-type</pre>
+<p>It is a valid description for the first two <code>dt</code>
+elements in the following example but not for the third one:</p>
+<pre>&lt;dl&gt;
+ &lt;dt&gt;gigogne&lt;/dt&gt;
+ &lt;dd&gt;
+  &lt;dl&gt;
+   &lt;dt&gt;fus&eacute;e&lt;/dt&gt;
+   &lt;dd&gt;multistage rocket&lt;/dd&gt;
+   &lt;dt&gt;table&lt;/dt&gt;
+   &lt;dd&gt;nest of tables&lt;/dd&gt;
+  &lt;/dl&gt;
+ &lt;/dd&gt;
+&lt;/dl&gt;</pre>
+</div>
+
+<h5><a name=last-of-type-pseudo>:last-of-type pseudo-class</a></h5>
+
+<p>Same as <code>:nth-last-of-type(1)</code>. The
+<code>:last-of-type</code> pseudo-class represents an element that is
+the last sibling of its type in the list of children of its parent
+element.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <p>The following selector represents the last data cell
+ <code>td</code> of a table row.</p>
+ <pre>tr &gt; td:last-of-type</pre>
+</div>
+
+<h5><a name=only-child-pseudo>:only-child pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children. Same as
+<code>:first-child:last-child</code> or
+<code>:nth-child(1):nth-last-child(1)</code>, but with a lower
+specificity.</p>
+
+<h5><a name=only-of-type-pseudo>:only-of-type pseudo-class</a></h5>
+
+<p>Represents an element that has a parent element and whose parent
+element has no other element children with the same element name. Same
+as <code>:first-of-type:last-of-type</code> or
+<code>:nth-of-type(1):nth-last-of-type(1)</code>, but with a lower
+specificity.</p>
+
+
+<h5><a name=empty-pseudo></a>:empty pseudo-class</h5>
+
+<p>The <code>:empty</code> pseudo-class represents an element that has
+no children at all. In terms of the DOM, only element nodes and text
+nodes (including CDATA nodes and entity references) whose data has a
+non-zero length must be considered as affecting emptiness; comments,
+PIs, and other nodes must not affect whether an element is considered
+empty or not.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p><code>p:empty</code> is a valid representation of the following fragment:</p>
+ <pre>&lt;p&gt;&lt;/p&gt;</pre>
+ <p><code>foo:empty</code> is not a valid representation for the
+ following fragments:</p>
+ <pre>&lt;foo&gt;bar&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;&lt;bar&gt;bla&lt;/bar&gt;&lt;/foo&gt;</pre>
+ <pre>&lt;foo&gt;this is not &lt;bar&gt;:empty&lt;/bar&gt;&lt;/foo&gt;</pre>
+</div>
+
+<h4><a name=content-selectors>6.6.6. Blank</a></h4> <!-- It's the Return of Appendix H!!! Run away! -->
+
+<p>This section intentionally left blank.</p>
+<!-- (used to be :contains()) -->
+
+<h4><a name=negation></a>6.6.7. The negation pseudo-class</h4>
+
+<p>The negation pseudo-class, <code>:not(<var>X</var>)</code>, is a
+functional notation taking a <a href="#simple-selectors-dfn">simple
+selector</a> (excluding the negation pseudo-class itself and
+pseudo-elements) as an argument. It represents an element that is not
+represented by the argument.
+
+<!-- pseudo-elements are not simple selectors, so the above paragraph
+may be a bit confusing -->
+
+<div class="example">
+  <p>Examples:</p>
+  <p>The following CSS selector matches all <code>button</code>
+  elements in an HTML document that are not disabled.</p>
+  <pre>button:not([DISABLED])</pre>
+  <p>The following selector represents all but <code>FOO</code>
+  elements.</p>
+  <pre>*:not(FOO)</pre>
+  <p>The following group of selectors represents all HTML elements
+  except links.</p>
+  <pre>html|*:not(:link):not(:visited)</pre>
+</div>
+
+<p>Default namespace declarations do not affect the argument of the
+negation pseudo-class unless the argument is a universal selector or a
+type selector.</p>
+
+<div class="example">
+  <p>Examples:</p>
+  <p>Assuming that the default namespace is bound to
+  "http://example.com/", the following selector represents all
+  elements that are not in that namespace:</p>
+  <pre>*|*:not(*)</pre>
+  <p>The following CSS selector matches any element being hovered,
+  regardless of its namespace. In particular, it is not limited to
+  only matching elements in the default namespace that are not being
+  hovered, and elements not in the default namespace don't match the
+  rule when they <em>are</em> being hovered.</p>
+  <pre>*|*:not(:hover)</pre>
+</div>
+
+<p class="note"><strong>Note</strong>: the :not() pseudo allows
+useless selectors to be written.  For instance <code>:not(*|*)</code>,
+which represents no element at all, or <code>foo:not(bar)</code>,
+which is equivalent to <code>foo</code> but with a higher
+specificity.</p>
+
+<h3><a name=pseudo-elements>7. Pseudo-elements</a></h3>
+
+<p>Pseudo-elements create abstractions about the document tree beyond
+those specified by the document language. For instance, document
+languages do not offer mechanisms to access the first letter or first
+line of an element's content. Pseudo-elements allow designers to refer
+to this otherwise inaccessible information. Pseudo-elements may also
+provide designers a way to refer to content that does not exist in the
+source document (e.g., the <code>::before</code> and
+<code>::after</code> pseudo-elements give access to generated
+content).</p>
+
+<p>A pseudo-element is made of two colons (<code>::</code>) followed
+by the name of the pseudo-element.</p>
+
+<p>This <code>::</code> notation is introduced by the current document
+in order to establish a discrimination between pseudo-classes and
+pseudo-elements.  For compatibility with existing style sheets, user
+agents must also accept the previous one-colon notation for
+pseudo-elements introduced in CSS levels 1 and 2 (namely,
+<code>:first-line</code>, <code>:first-letter</code>,
+<code>:before</code> and <code>:after</code>). This compatibility is
+not allowed for the new pseudo-elements introduced in CSS level 3.</p>
+
+<p>Only one pseudo-element may appear per selector, and if present it
+must appear after the sequence of simple selectors that represents the
+<a href="#subject">subjects</a> of the selector. <span class="note">A
+future version of this specification may allow multiple
+pesudo-elements per selector.</span></p>
+
+<h4><a name=first-line>7.1. The ::first-line pseudo-element</a></h4>
+
+<p>The <code>::first-line</code> pseudo-element describes the contents
+of the first formatted line of an element.
+
+<div class="example">
+<p>CSS example:</p>
+<pre>p::first-line { text-transform: uppercase }</pre>
+<p>The above rule means "change the letters of the first line of every
+paragraph to uppercase".</p>
+</div>
+
+<p>The selector <code>p::first-line</code> does not match any real
+HTML element. It does match a pseudo-element that conforming user
+agents will insert at the beginning of every paragraph.</p>
+
+<p>Note that the length of the first line depends on a number of
+factors, including the width of the page, the font size, etc.  Thus,
+an ordinary HTML paragraph such as:</p>
+
+<pre>
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the lines of which happen to be broken as follows:
+
+<pre>
+THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
+will be broken into several lines. The first
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
+ordinary lines in the paragraph.
+</pre>
+
+<p>This paragraph might be "rewritten" by user agents to include the
+<em>fictional tag sequence</em> for <code>::first-line</code>. This
+fictional tag sequence helps to show how properties are inherited.</p>
+
+<pre>
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
+paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>If a pseudo-element breaks up a real element, the desired effect
+can often be described by a fictional tag sequence that closes and
+then re-opens the element. Thus, if we mark up the previous paragraph
+with a <code>span</code> element:</p>
+
+<pre>
+&lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
+paragraph that will be broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>the user agent could simulate start and end tags for
+<code>span</code> when inserting the fictional tag sequence for
+<code>::first-line</code>.
+
+<pre>
+&lt;P&gt;&lt;P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> This is a
+somewhat long HTML
+paragraph that will <b>&lt;/SPAN&gt;</b>&lt;/P::first-line&gt;<b>&lt;SPAN class="test"&gt;</b> be
+broken into several
+lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
+paragraph.&lt;/P&gt;
+</pre>
+
+<p>In CSS, the <code>::first-line</code> pseudo-element can only be
+attached to a block-level element, an inline-block, a table-caption,
+or a table-cell.</p>
+
+<p><a name="first-formatted-line"></a>The "first formatted line" of an
+element may occur inside a
+block-level descendant in the same flow (i.e., a block-level
+descendant that is not positioned and not a float). E.g., the first
+line of the <code>div</code> in <code>&lt;DIV>&lt;P>This
+line...&lt;/P>&lt/DIV></code> is the first line of the <code>p</code> (assuming
+that both <code>p</code> and <code>div</code> are block-level).
+
+<p>The first line of a table-cell or inline-block cannot be the first
+formatted line of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first formatted line of the
+<code>div</code> is not the line "Hello".
+
+<p class="note">Note that the first line of the <code>p</code> in this
+fragment: <code>&lt;p&gt&lt;br&gt;First...</code> doesn't contain any
+letters (assuming the default style for <code>br</code> in HTML
+4). The word "First" is not on the first formatted line.
+
+<p>A UA should act as if the fictional start tags of the
+<code>::first-line</code> pseudo-elements were nested just inside the
+innermost enclosing block-level element. (Since CSS1 and CSS2 were
+silent on this case, authors should not rely on this behavior.) Here
+is an example. The fictional tag sequence for</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>First paragraph&lt;/P>
+  &lt;P>Second paragraph&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>is</p>
+
+<pre>
+&lt;DIV>
+  &lt;P>&lt;DIV::first-line>&lt;P::first-line>First paragraph&lt;/P::first-line>&lt;/DIV::first-line>&lt;/P>
+  &lt;P>&lt;P::first-line>Second paragraph&lt;/P::first-line>&lt;/P>
+&lt;/DIV>
+</pre>
+
+<p>The <code>::first-line</code> pseudo-element is similar to an
+inline-level element, but with certain restrictions. In CSS, the
+following properties apply to a <code>::first-line</code>
+pseudo-element: font properties, color property, background
+properties, 'word-spacing', 'letter-spacing', 'text-decoration',
+'vertical-align', 'text-transform', 'line-height'. UAs may apply other
+properties as well.</p>
+
+
+<h4><a name=first-letter>7.2. The ::first-letter pseudo-element</a></h4>
+
+<p>The <code>::first-letter</code> pseudo-element represents the first
+letter of the first line of a block, if it is not preceded by any
+other content (such as images or inline tables) on its line. The
+::first-letter pseudo-element may be used for "initial caps" and "drop
+caps", which are common typographical effects. This type of initial
+letter is similar to an inline-level element if its 'float' property
+is 'none'; otherwise, it is similar to a floated element.</p>
+
+<p>In CSS, these are the properties that apply to <code>::first-letter</code>
+pseudo-elements: font properties, 'text-decoration', 'text-transform',
+'letter-spacing', 'word-spacing' (when appropriate), 'line-height',
+'float', 'vertical-align' (only if 'float' is 'none'), margin
+properties, padding properties, border properties, color property,
+background properties.  UAs may apply other properties as well.  To
+allow UAs to render a typographically correct drop cap or initial cap,
+the UA may choose a line-height, width and height based on the shape
+of the letter, unlike for normal elements.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>This example shows a possible rendering of an initial cap. Note
+that the 'line-height' that is inherited by the <code>::first-letter</code>
+pseudo-element is 1.1, but the UA in this example has computed the
+height of the first letter differently, so that it doesn't cause any
+unnecessary space between the first two lines. Also note that the
+fictional start tag of the first letter is inside the <span>span</span>, and thus
+the font weight of the first letter is normal, not bold as the <span>span</span>:
+<pre>
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+...
+&lt;p>&lt;span>Het hemelsche&lt;/span> gerecht heeft zich ten lange lesten&lt;br>
+Erbarremt over my en mijn benaeuwde vesten&lt;br>
+En arme burgery, en op mijn volcx gebed&lt;br>
+En dagelix geschrey de bange stad ontzet.
+</pre>
+<div class="figure">
+<p><img src="initial-cap.png" alt="Image illustrating the ::first-letter pseudo-element">
+</div>
+</div>
+
+<div class="example">
+<p>The following CSS will make a drop cap initial letter span about two lines:</p>
+
+<pre>
+&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"&gt;
+&lt;HTML&gt;
+ &lt;HEAD&gt;
+  &lt;TITLE&gt;Drop cap initial letter&lt;/TITLE&gt;
+  &lt;STYLE type="text/css"&gt;
+   P               { font-size: 12pt; line-height: 1.2 }
+   P::first-letter { font-size: 200%; font-weight: bold; float: left }
+   SPAN            { text-transform: uppercase }
+  &lt;/STYLE&gt;
+ &lt;/HEAD&gt;
+ &lt;BODY&gt;
+  &lt;P&gt;&lt;SPAN&gt;The first&lt;/SPAN&gt; few words of an article
+    in The Economist.&lt;/P&gt;
+ &lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre>
+
+<p>This example might be formatted as follows:</p>
+
+<div class="figure">
+<P><img src="first-letter.gif" alt="Image illustrating the combined effect of the ::first-letter and ::first-line pseudo-elements"></p>
+</div>
+
+<p>The <span class="index-inst" title="fictional tag
+sequence">fictional tag sequence</span> is:</p>
+
+<pre>
+&lt;P&gt;
+&lt;SPAN&gt;
+&lt;P::first-letter&gt;
+T
+&lt;/P::first-letter&gt;he first
+&lt;/SPAN&gt;
+few words of an article in the Economist.
+&lt;/P&gt;
+</pre>
+
+<p>Note that the <code>::first-letter</code> pseudo-element tags abut
+the content (i.e., the initial character), while the ::first-line
+pseudo-element start tag is inserted right after the start tag of the
+block element.</p> </div>
+
+<p>In order to achieve traditional drop caps formatting, user agents
+may approximate font sizes, for example to align baselines. Also, the
+glyph outline may be taken into account when formatting.</p>
+
+<p>Punctuation (i.e, characters defined in Unicode in the "open" (Ps),
+"close" (Pe), "initial" (Pi). "final" (Pf) and "other" (Po)
+punctuation classes), that precedes or follows the first letter should
+be included. <a href="#refsUNICODE">[UNICODE]</a></p>
+
+<div class="figure">
+<P><img src="first-letter2.gif" alt="Quotes that precede the
+first letter should be included."></p>
+</div>
+
+<p>The <code>::first-letter</code> also applies if the first letter is
+in fact a digit, e.g., the "6" in "67 million dollars is a lot of
+money."</p>
+
+<p>In CSS, the <code>::first-letter</code> pseudo-element applies to
+block, list-item, table-cell, table-caption, and inline-block
+elements. <span class="note">A future version of this specification
+may allow this pesudo-element to apply to more element
+types.</span></p>
+
+<p>The <code>::first-letter</code> pseudo-element can be used with all
+such elements that contain text, or that have a descendant in the same
+flow that contains text. A UA should act as if the fictional start tag
+of the ::first-letter pseudo-element is just before the first text of
+the element, even if that first text is in a descendant.</p>
+
+<div class="example">
+<p>Example:</p>
+<p>The fictional tag sequence for this HTMLfragment:
+<pre>&lt;div>
+&lt;p>The first text.</pre>
+<p>is:
+<pre>&lt;div>
+&lt;p>&lt;div::first-letter>&lt;p::first-letter>T&lt;/...>&lt;/...>he first text.</pre>
+</div>
+
+<p>The first letter of a table-cell or inline-block cannot be the
+first letter of an ancestor element. Thus, in <code>&lt;DIV&gt;&lt;P
+STYLE="display: inline-block">Hello&lt;BR&gt;Goodbye&lt;/P&gt;
+etcetera&lt;/DIV&gt;</code> the first letter of the <code>div</code> is not the
+letter "H". In fact, the <code>div</code> doesn't have a first letter.
+
+<p>The first letter must occur on the <a
+href="#first-formatted-line">first formatted line.</a> For example, in
+this fragment: <code>&lt;p&gt&lt;br&gt;First...</code> the first line
+doesn't contain any letters and <code>::first-letter</code> doesn't
+match anything (assuming the default style for <code>br</code> in HTML
+4). In particular, it does not match the "F" of "First."
+
+<p>In CSS, if an element is a list item ('display: list-item'), the
+<code>::first-letter</code> applies to the first letter in the
+principal box after the marker. UAs may ignore
+<code>::first-letter</code> on list items with 'list-style-position:
+inside'. If an element has <code>::before</code> or
+<code>::after</code> content, the <code>::first-letter</code> applies
+to the first letter of the element <em>including</em> that content.
+
+<div class="example">
+<p>Example:</p>
+<p>After the rule 'p::before {content: "Note: "}', the selector
+'p::first-letter' matches the "N" of "Note".</p>
+</div>
+
+<p>Some languages may have specific rules about how to treat certain
+letter combinations. In Dutch, for example, if the letter combination
+"ij" appears at the beginning of a word, both letters should be
+considered within the <code>::first-letter</code> pseudo-element.
+
+<p>If the letters that would form the ::first-letter are not in the
+same element, such as "'T" in <code>&lt;p>'&lt;em>T...</code>, the UA
+may create a ::first-letter pseudo-element from one of the elements,
+both elements, or simply not create a pseudo-element.</p>
+
+<p>Similarly, if the first letter(s) of the block are not at the start
+of the line (for example due to bidirectional reordering), then the UA
+need not create the pseudo-element(s).
+
+<div class="example">
+<p>Example:</p>
+<p><a name="overlapping-example">The following example</a> illustrates
+how overlapping pseudo-elements may interact.  The first letter of
+each P element will be green with a font size of '24pt'. The rest of
+the first formatted line will be 'blue' while the rest of the
+paragraph will be 'red'.</p>
+
+<pre>p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+&lt;P&gt;Some text that ends up on two lines&lt;/P&gt;</pre>
+
+<p>Assuming that a line break will occur before the word "ends", the
+<span class="index-inst" title="fictional tag sequence">fictional tag
+sequence</span> for this fragment might be:</p>
+
+<pre>&lt;P&gt;
+&lt;P::first-line&gt;
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
+&lt;/P&gt;</pre>
+
+<p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
+element.  Properties set on <code>::first-line</code> are inherited by
+<code>::first-letter</code>, but are overridden if the same property is set on
+<code>::first-letter</code>.</p>
+</div>
+
+
+<h4><a name=UIfragments>7.3.</a> <a name=selection>The ::selection pseudo-element</a></h4>
+
+<p>The <code>::selection</code> pseudo-element applies to the portion
+of a document that has been highlighted by the user. This also
+applies, for example, to selected text within an editable text
+field. This pseudo-element should not be confused with the <code><a
+href="#checked">:checked</a></code> pseudo-class (which used to be
+named <code>:selected</code>)
+
+<p>Although the <code>::selection</code> pseudo-element is dynamic in
+nature, and is altered by user action, it is reasonable to expect that
+when a UA re-renders to a static medium (such as a printed page, see
+<a href="#refsCSS21">[CSS21]</a>) which was originally rendered to a
+dynamic medium (like screen), the UA may wish to transfer the current
+<code>::selection</code> state to that other medium, and have all the
+appropriate formatting and rendering take effect as well. This is not
+required &mdash; UAs may omit the <code>::selection</code>
+pseudo-element for static media.
+
+<p>These are the CSS properties that apply to <code>::selection</code>
+pseudo-elements: color, background, cursor (optional), outline
+(optional). The computed value of the 'background-image' property on
+<code>::selection</code> may be ignored.
+
+
+<h4><a name=gen-content>7.4. The ::before and ::after pseudo-elements</a></h4>
+
+<p>The <code>::before</code> and <code>::after</code> pseudo-elements
+can be used to describe generated content before or after an element's
+content. They are explained in CSS 2.1 <a
+href="#refsCSS21">[CSS21]</a>.</p>
+
+<p>When the <code>::first-letter</code> and <code>::first-line</code>
+pseudo-elements are combined with <code>::before</code> and
+<code>::after</code>, they apply to the first letter or line of the
+element including the inserted text.</p>
+
+<h2><a name=combinators>8. Combinators</a></h2>
+
+<h3><a name=descendant-combinators>8.1. Descendant combinator</a></h3>
+
+<p>At times, authors may want selectors to describe an element that is
+the descendant of another element in the document tree (e.g., "an
+<code>EM</code> element that is contained within an <code>H1</code>
+element"). Descendant combinators express such a relationship. A
+descendant combinator is <a href="#whitespace">white space</a> that
+separates two sequences of simple selectors.  A selector of the form
+"<code>A B</code>" represents an element <code>B</code> that is an
+arbitrary descendant of some ancestor element <code>A</code>.
+
+<div class="example">
+ <p>Examples:</p>
+ <p>For example, consider the following selector:</p>
+ <pre>h1 em</pre>
+ <p>It represents an <code>em</code> element being the descendant of
+ an <code>h1</code> element. It is a correct and valid, but partial,
+ description of the following fragment:</p>
+ <pre>&lt;h1&gt;This &lt;span class="myclass"&gt;headline
+is &lt;em&gt;very&lt;/em&gt; important&lt;/span&gt;&lt;/h1&gt;</pre>
+ <p>The following selector:</p>
+ <pre>div * p</pre>
+ <p>represents a <code>p</code> element that is a grandchild or later
+ descendant of a <code>div</code> element. Note the whitespace on
+ either side of the "*" is not part of the universal selector; the
+ whitespace is a combinator indicating that the DIV must be the
+ ancestor of some element, and that that element must be an ancestor
+ of the P.</p>
+ <p>The following selector, which combines descendant combinators and
+ <a href="#attribute-selectors">attribute selectors</a>, represents an
+ element that (1) has the <code>href</code> attribute set and (2) is
+ inside a <code>p</code> that is itself inside a <code>div</code>:</p>
+ <pre>div p *[href]</pre>
+</div>
+
+<h3><a name=child-combinators>8.2. Child combinators</a></h3>
+
+<p>A <dfn>child combinator</dfn> describes a childhood relationship
+between two elements. A child combinator is made of the
+&quot;greater-than sign&quot; (<code>&gt;</code>) character and
+separates two sequences of simple selectors.
+
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element that is
+ child of <code>body</code>:</p>
+ <pre>body &gt; p</pre>
+ <p>The following example combines descendant combinators and child
+ combinators.</p>
+ <pre>div ol&gt;li p</pre><!-- LEAVE THOSE SPACES OUT! see below -->
+ <p>It represents a <code>p</code> element that is a descendant of an
+ <code>li</code> element; the <code>li</code> element must be the
+ child of an <code>ol</code> element; the <code>ol</code> element must
+ be a descendant of a <code>div</code>. Notice that the optional white
+ space around the "&gt;" combinator has been left out.</p>
+</div>
+
+<p>For information on selecting the first child of an element, please
+see the section on the <code><a
+href="#structural-pseudos">:first-child</a></code> pseudo-class
+above.</p>
+
+<h3><a name=sibling-combinators>8.3. Sibling combinators</a></h3>
+
+<p>There are two different sibling combinators: the adjacent sibling
+combinator and the general sibling combinator. In both cases,
+non-element nodes (e.g. text between elements) are ignored when
+considering adjacency of elements.</p>
+
+<h4><a name=adjacent-sibling-combinators>8.3.1. Adjacent sibling combinator</a></h4>
+
+<p>The adjacent sibling combinator is made of the &quot;plus
+sign&quot; (U+002B, <code>+</code>) character that separates two
+sequences of simple selectors. The elements represented by the two
+sequences share the same parent in the document tree and the element
+represented by the first sequence immediately precedes the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Examples:</p>
+ <p>The following selector represents a <code>p</code> element
+ immediately following a <code>math</code> element:</p>
+ <pre>math + p</pre>
+ <p>The following selector is conceptually similar to the one in the
+ previous example, except that it adds an attribute selector &mdash; it
+ adds a constraint to the <code>h1</code> element, that it must have
+ <code>class="opener"</code>:</p>
+ <pre>h1.opener + h2</pre>
+</div>
+
+
+<h4><a name=general-sibling-combinators>8.3.2. General sibling combinator</a></h4>
+
+<p>The general sibling combinator is made of the &quot;tilde&quot;
+(U+007E, <code>~</code>) character that separates two sequences of
+simple selectors. The elements represented by the two sequences share
+the same parent in the document tree and the element represented by
+the first sequence precedes (not necessarily immediately) the element
+represented by the second one.</p>
+
+<div class="example">
+ <p>Example:</p>
+ <pre>h1 ~ pre</pre>
+ <p>represents a <code>pre</code> element following an <code>h1</code>. It
+ is a correct and valid, but partial, description of:</p>
+ <pre>&lt;h1&gt;Definition of the function a&lt;/h1&gt;
+&lt;p&gt;Function a(x) has to be applied to all figures in the table.&lt;/p&gt;
+&lt;pre&gt;function a(x) = 12x/13.5&lt;/pre&gt;</pre>
+</div>
+
+<h2><a name=specificity>9. Calculating a selector's specificity</a></h2>
+
+<p>A selector's specificity is calculated as follows:</p>
+
+<ul>
+  <li>count the number of ID selectors in the selector (= a)</li>
+  <li>count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)</li>
+  <li>count the number of element names in the selector (= c)</li>
+  <li>ignore pseudo-elements</li>
+</ul>
+
+<p>Selectors inside <a href="#negation">the negation pseudo-class</a>
+are counted like any other, but the negation itself does not count as
+a pseudo-class.</p>
+
+<p>Concatenating the three numbers a-b-c (in a number system with a
+large base) gives the specificity.</p>
+
+<div class="example">
+<p>Examples:</p>
+<pre>*               /* a=0 b=0 c=0 -&gt; specificity =   0 */
+LI              /* a=0 b=0 c=1 -&gt; specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -&gt; specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -&gt; specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -&gt; specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -&gt; specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -&gt; specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -&gt; specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -&gt; specificity = 101 */
+</pre>
+</div>
+
+<p class="note"><strong>Note:</strong> the specificity of the styles
+specified in an HTML <code>style</code> attribute is described in CSS
+2.1. <a href="#refsCSS21">[CSS21]</a>.</p>
+
+<h2><a name=w3cselgrammar>10. The grammar of Selectors</a></h2>
+
+<h3><a name=grammar>10.1. Grammar</a></h3>
+
+<p>The grammar below defines the syntax of Selectors.  It is globally
+LL(1) and can be locally LL(2) (but note that most UA's should not use
+it directly, since it doesn't express the parsing conventions). The
+format of the productions is optimized for human consumption and some
+shorthand notations beyond Yacc (see <a href="#refsYACC">[YACC]</a>)
+are used:</p>
+
+<ul>
+  <li><b>*</b>: 0 or more
+  <li><b>+</b>: 1 or more
+  <li><b>?</b>: 0 or 1
+  <li><b>|</b>: separates alternatives
+  <li><b>[ ]</b>: grouping </li>
+</ul>
+
+<p>The productions are:</p>
+
+<pre>selectors_group
+  : selector [ COMMA S* selector ]*
+  ;
+
+selector
+  : simple_selector_sequence [ combinator simple_selector_sequence ]*
+  ;
+
+combinator
+  /* combinators can be surrounded by white space */
+  : PLUS S* | GREATER S* | TILDE S* | S+
+  ;
+
+simple_selector_sequence
+  : [ type_selector | universal ]
+    [ HASH | class | attrib | pseudo | negation ]*
+  | [ HASH | class | attrib | pseudo | negation ]+
+  ;
+
+type_selector
+  : [ namespace_prefix ]? element_name
+  ;
+
+namespace_prefix
+  : [ IDENT | '*' ]? '|'
+  ;
+
+element_name
+  : IDENT
+  ;
+
+universal
+  : [ namespace_prefix ]? '*'
+  ;
+
+class
+  : '.' IDENT
+  ;
+
+attrib
+  : '[' S* [ namespace_prefix ]? IDENT S*
+        [ [ PREFIXMATCH |
+            SUFFIXMATCH |
+            SUBSTRINGMATCH |
+            '=' |
+            INCLUDES |
+            DASHMATCH ] S* [ IDENT | STRING ] S*
+        ]? ']'
+  ;
+
+pseudo
+  /* '::' starts a pseudo-element, ':' a pseudo-class */
+  /* Exceptions: :first-line, :first-letter, :before and :after. */
+  /* Note that pseudo-elements are restricted to one per selector and */
+  /* occur only in the last simple_selector_sequence. */
+  : ':' ':'? [ IDENT | functional_pseudo ]
+  ;
+
+functional_pseudo
+  : FUNCTION S* expression ')'
+  ;
+
+expression
+  /* In CSS3, the expressions are identifiers, strings, */
+  /* or of the form "an+b" */
+  : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+  ;
+
+negation
+  : NOT S* negation_arg S* ')'
+  ;
+
+negation_arg
+  : type_selector | universal | HASH | class | attrib | pseudo
+  ;</pre>
+
+
+<h3><a name=lex>10.2. Lexical scanner</a></h3>
+
+<p>The following is the <a name=x3>tokenizer</a>, written in Flex (see
+<a href="#refsFLEX">[FLEX]</a>) notation. The tokenizer is
+case-insensitive.</p>
+
+<p>The two occurrences of "\377" represent the highest character
+number that current versions of Flex can deal with (decimal 255). They
+should be read as "\4177777" (decimal 1114111), which is the highest
+possible code point in Unicode/ISO-10646. <a
+href="#refsUNICODE">[UNICODE]</a></p>
+
+<pre>%option case-insensitive
+
+ident     [-]?{nmstart}{nmchar}*
+name      {nmchar}+
+nmstart   [_a-z]|{nonascii}|{escape}
+nonascii  [^\0-\177]
+unicode   \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+escape    {unicode}|\\[^\n\r\f0-9a-f]
+nmchar    [_a-z0-9-]|{nonascii}|{escape}
+num       [0-9]+|[0-9]*\.[0-9]+
+string    {string1}|{string2}
+string1   \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*\"
+string2   \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*\'
+invalid   {invalid1}|{invalid2}
+invalid1  \"([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*
+invalid2  \'([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
+nl        \n|\r\n|\r|\f
+w         [ \t\r\n\f]*
+
+%%
+
+[ \t\r\n\f]+     return S;
+
+"~="             return INCLUDES;
+"|="             return DASHMATCH;
+"^="             return PREFIXMATCH;
+"$="             return SUFFIXMATCH;
+"*="             return SUBSTRINGMATCH;
+{ident}          return IDENT;
+{string}         return STRING;
+{ident}"("       return FUNCTION;
+{num}            return NUMBER;
+"#"{name}        return HASH;
+{w}"+"           return PLUS;
+{w}"&gt;"           return GREATER;
+{w}","           return COMMA;
+{w}"~"           return TILDE;
+":not("          return NOT;
+@{ident}         return ATKEYWORD;
+{invalid}        return INVALID;
+{num}%           return PERCENTAGE;
+{num}{ident}     return DIMENSION;
+"&lt;!--"           return CDO;
+"--&gt;"            return CDC;
+
+"url("{w}{string}{w}")"                           return URI;
+"url("{w}([!#$%&*-~]|{nonascii}|{escape})*{w}")"  return URI;
+U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?                return UNICODE_RANGE;
+
+\/\*[^*]*\*+([^/*][^*]*\*+)*\/                    /* ignore comments */
+
+.                return *yytext;</pre>
+
+
+
+<h2><a name=downlevel>11. Namespaces and down-level clients</a></h2>
+
+<p>An important issue is the interaction of CSS selectors with XML
+documents in web clients that were produced prior to this
+document. Unfortunately, due to the fact that namespaces must be
+matched based on the URI which identifies the namespace, not the
+namespace prefix, some mechanism is required to identify namespaces in
+CSS by their URI as well. Without such a mechanism, it is impossible
+to construct a CSS style sheet which will properly match selectors in
+all cases against a random set of XML documents. However, given
+complete knowledge of the XML document to which a style sheet is to be
+applied, and a limited use of namespaces within the XML document, it
+is possible to construct a style sheet in which selectors would match
+elements and attributes correctly.</p>
+
+<p>It should be noted that a down-level CSS client will (if it
+properly conforms to CSS forward compatible parsing rules) ignore all
+<code>@namespace</code> at-rules, as well as all style rules that make
+use of namespace qualified element type or attribute selectors. The
+syntax of delimiting namespace prefixes in CSS was deliberately chosen
+so that down-level CSS clients would ignore the style rules rather
+than possibly match them incorrectly.</p>
+
+<p>The use of default namespaces in CSS makes it possible to write
+element type selectors that will function in both namespace aware CSS
+clients as well as down-level clients. It should be noted that
+down-level clients may incorrectly match selectors against XML
+elements in other namespaces.</p>
+
+<p>The following are scenarios and examples in which it is possible to
+construct style sheets which would function properly in web clients
+that do not implement this proposal.</p>
+
+<ol>
+  <li>
+
+   <p>The XML document does not use namespaces.</p>
+
+   <ul>
+
+    <li>In this case, it is obviously not necessary to declare or use
+    namespaces in the style sheet. Standard CSS element type and
+    attribute selectors will function adequately in a down-level
+    client.</li>
+
+    <li>In a CSS namespace aware client, the default behavior of
+    element selectors matching without regard to namespace will
+    function properly against all elements, since no namespaces are
+    present. However, the use of specific element type selectors that
+    match only elements that have no namespace ("<code>|name</code>")
+    will guarantee that selectors will match only XML elements that do
+    not have a declared namespace. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document defines a single, default namespace used
+   throughout the document. No namespace prefixes are used in element
+   names.</p>
+
+   <ul>
+
+    <li>In this case, a down-level client will function as if
+    namespaces were not used in the XML document at all. Standard CSS
+    element type and attribute selectors will match against all
+    elements. </li>
+
+   </ul>
+
+  </li>
+
+  <li>
+
+   <p>The XML document does <b>not</b> use a default namespace, all
+   namespace prefixes used are known to the style sheet author, and
+   there is a direct mapping between namespace prefixes and namespace
+   URIs. (A given prefix may only be mapped to one namespace URI
+   throughout the XML document; there may be multiple prefixes mapped
+   to the same URI).</p>
+
+   <ul>
+
+    <li>In this case, the down-level client will view and match
+    element type and attribute selectors based on their fully
+    qualified name, not the local part as outlined in the <a
+    href="#typenmsp">Type selectors and Namespaces</a> section. CSS
+    selectors may be declared using an escaped colon "<code>\:</code>"
+    to describe the fully qualified names, e.g.
+    "<code>html\:h1</code>" will match
+    <code>&lt;html:h1&gt;</code>. Selectors using the qualified name
+    will only match XML elements that use the same prefix. Other
+    namespace prefixes used in the XML that are mapped to the same URI
+    will not match as expected unless additional CSS style rules are
+    declared for them.</li>
+
+    <li>Note that selectors declared in this fashion will
+    <em>only</em> match in down-level clients. A CSS namespace aware
+    client will match element type and attribute selectors based on
+    the name's local part. Selectors declared with the fully
+    qualified name will not match (unless there is no namespace prefix
+    in the fully qualified name).</li>
+
+   </ul>
+
+  </li>
+
+ </ol>
+
+<p>In other scenarios: when the namespace prefixes used in the XML are
+not known in advance by the style sheet author; or a combination of
+elements with no namespace are used in conjunction with elements using
+a default namespace; or the same namespace prefix is mapped to
+<em>different</em> namespace URIs within the same document, or in
+different documents; it is impossible to construct a CSS style sheet
+that will function properly against all elements in those documents,
+unless, the style sheet is written using a namespace URI syntax (as
+outlined in this document or similar) and the document is processed by
+a CSS and XML namespace aware client.</p>
+
+<h2><a name=profiling>12. Profiles</a></h2>
+
+<p>Each specification using Selectors must define the subset of W3C
+Selectors it allows and excludes, and describe the local meaning of
+all the components of that subset.</p>
+
+<p>Non normative examples:
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 1</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>class selectors<br>ID selectors<br>:link,
+      :visited and :active pseudo-classes<br>descendant combinator
+     <br>::first-line and ::first-letter pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>universal selector<br>attribute selectors<br>:hover and :focus
+      pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
+      element states pseudo-classes<br>all structural
+      pseudo-classes<br>negation pseudo-class<br>all
+      UI element fragments pseudo-elements<br>::before and ::after
+      pseudo-elements<br>child combinators<br>sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>only one class selector allowed per sequence of simple
+  selectors</td></tr></tbody></table><br><br>
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+    <td>CSS level 2</td></tr>
+  <tr>
+    <th>Accepts</th>
+    <td>type selectors<br>universal selector<br>attribute presence and
+      values selectors<br>class selectors<br>ID selectors<br>:link, :visited,
+      :active, :hover, :focus, :lang() and :first-child pseudo-classes
+     <br>descendant combinator<br>child combinator<br>adjacent sibling
+      combinator<br>::first-line and ::first-letter pseudo-elements<br>::before
+      and ::after pseudo-elements</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>
+
+<p>content selectors<br>substring matching attribute
+      selectors<br>:target pseudo-classes<br>all UI element
+      states pseudo-classes<br>all structural pseudo-classes other
+      than :first-child<br>negation pseudo-class<br>all UI element
+      fragments pseudo-elements<br>general sibling combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>more than one class selector per sequence of simple selectors (CSS1
+      constraint) allowed</td></tr></tbody></table>
+
+<p>In CSS, selectors express pattern matching rules that determine which style
+rules apply to elements in the document tree.
+
+<p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
+<pre>h1 a[name]</pre>
+
+<p>All CSS declarations attached to such a selector are applied to elements
+matching it. </div>
+
+<div class="profile">
+<table class="tprofile">
+  <tbody>
+  <tr>
+    <th class="title" colspan=2>Selectors profile</th></tr>
+  <tr>
+    <th>Specification</th>
+      <td>STTS 3</td>
+    </tr>
+  <tr>
+    <th>Accepts</th>
+    <td>
+
+<p>type selectors<br>universal selectors<br>attribute selectors<br>class
+      selectors<br>ID selectors<br>all structural pseudo-classes<br>
+          all combinators
+
+<p>namespaces</td></tr>
+  <tr>
+    <th>Excludes</th>
+    <td>non-accepted pseudo-classes<br>pseudo-elements<br></td></tr>
+  <tr>
+    <th>Extra constraints</th>
+    <td>some selectors and combinators are not allowed in fragment
+      descriptions on the right side of STTS declarations.</td></tr></tbody></table>
+<form>
+<input type="text" name="test10"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+<input type="text" name="foo"/>
+</form>
+
+<p>Selectors can be used in STTS 3 in two different
+    manners:
+<ol>
+  <li>a selection mechanism equivalent to CSS selection mechanism: declarations
+  attached to a given selector are applied to elements matching that selector,
+  <li>fragment descriptions that appear on the right side of declarations.
+</li></ol></div>
+
+<h2><a name=Conformance></a>13. Conformance and requirements</h2>
+
+<p>This section defines conformance with the present specification only.
+
+<p>The inability of a user agent to implement part of this specification due to
+the limitations of a particular device (e.g., non interactive user agents will
+probably not implement dynamic pseudo-classes because they make no sense without
+interactivity) does not imply non-conformance.
+
+<p>All specifications reusing Selectors must contain a <a
+href="#profiling">Profile</a> listing the
+subset of Selectors it accepts or excludes, and describing the constraints
+it adds to the current specification.
+
+<p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
+which is not allowed at the current parsing point.
+
+<p>User agents must observe the rules for handling parsing errors:
+<ul>
+  <li>a simple selector containing an undeclared namespace prefix is invalid</li>
+  <li>a selector containing an invalid simple selector, an invalid combinator
+    or an invalid token is invalid. </li>
+  <li>a group of selectors containing an invalid selector is invalid.</li>
+</ul>
+
+<p class="foo test10 bar">Specifications reusing Selectors must define how to handle parsing
+errors. (In the case of CSS, the entire rule in which the selector is
+used is dropped.)</p>
+
+<!-- Apparently all these references are out of date:
+<p>Implementations of this specification must behave as
+"recipients of text data" as defined by <a href="#refsCWWW">[CWWW]</a>
+when parsing selectors and attempting matches. (In particular,
+implementations must assume the data is normalized and must not
+normalize it.) Normative rules for matching strings are defined in
+<a href="#refsCWWW">[CWWW]</a> and <a
+href="#refsUNICODE">[UNICODE]</a> and apply to implementations of this
+specification.</p>-->
+
+<h2><a name=Tests></a>14. Tests</h2>
+
+<p>This specification has <a
+href="http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/">a test
+suite</a> allowing user agents to verify their basic conformance to
+the specification. This test suite does not pretend to be exhaustive
+and does not cover all possible combined cases of Selectors.</p>
+
+<h2><a name=ACKS></a>15. Acknowledgements</h2>
+
+<p>The CSS working group would like to thank everyone who has sent
+comments on this specification over the years.</p>
+
+<p>The working group would like to extend special thanks to Donna
+McManus, Justin Baker, Joel Sklar, and Molly Ives Brower who perfermed
+the final editorial review.</p>
+
+<h2><a name=references>16. References</a></h2>
+
+<dl class="refs">
+
+  <dt>[CSS1]
+  <dd><a name=refsCSS1></a> Bert Bos, H&aring;kon Wium Lie; "<cite>Cascading Style Sheets, level 1</cite>", W3C Recommendation, 17 Dec 1996, revised 11 Jan 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
+
+  <dt>[CSS21]
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
+  <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
+
+  <dt>[CWWW]
+  <dd><a name=refsCWWW></a> Martin J. D&uuml;rst, Fran&ccedil;ois Yergeau, Misha Wolf, Asmus Freytag, Tex Texin, editors; "<cite>Character Model for the World Wide Web</cite>", W3C Recommendation, 15 February 2005
+  <dd>(<code><a href="http://www.w3.org/TR/charmod/">http://www.w3.org/TR/charmod/</a></code>)
+
+  <dt>[FLEX]
+  <dd><a name="refsFLEX"></a> "<cite>Flex: The Lexical Scanner Generator</cite>", Version 2.3.7, ISBN 1882114213
+
+  <dt>[HTML4]
+  <dd><a name="refsHTML4"></a> Dave Ragget, Arnaud Le Hors, Ian Jacobs, editors; "<cite>HTML 4.01 Specification</cite>", W3C Recommendation, 24 December 1999
+  <dd>(<a href="http://www.w3.org/TR/html4/"><code>http://www.w3.org/TR/html4/</code></a>)
+
+  <dt>[MATH]
+  <dd><a name="refsMATH"></a> Patrick Ion, Robert Miner, editors; "<cite>Mathematical Markup Language (MathML) 1.01</cite>", W3C Recommendation, revision of 7 July 1999
+  <dd>(<code><a href="http://www.w3.org/TR/REC-MathML/">http://www.w3.org/TR/REC-MathML/</a></code>)
+
+  <dt>[RFC3066]
+  <dd><a name="refsRFC3066"></a> H. Alvestrand; "<cite>Tags for the Identification of Languages</cite>", Request for Comments 3066, January 2001
+  <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
+
+  <dt>[STTS]
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
+  <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
+
+  <dt>[SVG]
+  <dd><a name="refsSVG"></a> Jon Ferraiolo, &#34276;&#27810; &#28147;, Dean Jackson, editors; "<cite>Scalable Vector Graphics (SVG) 1.1 Specification</cite>", W3C Recommendation, 14 January 2003
+  <dd>(<code><a href="http://www.w3.org/TR/SVG/">http://www.w3.org/TR/SVG/</a></code>)
+
+  <dt>[UNICODE]</dt>
+  <dd><a name="refsUNICODE"></a> <cite><a
+   href="http://www.unicode.org/versions/Unicode4.1.0/">The Unicode Standard, Version 4.1</a></cite>, The Unicode Consortium. Boston, MA, Addison-Wesley, March 2005. ISBN 0-321-18578-1, as amended by <a href="http://www.unicode.org/versions/Unicode4.0.1/">Unicode 4.0.1</a> and <a href="http://www.unicode.org/versions/Unicode4.1.0/">Unicode  4.1.0</a>.
+  <dd>(<code><a href="http://www.unicode.org/versions/">http://www.unicode.org/versions/</a></code>)</dd>
+
+  <dt>[XML10]
+  <dd><a name="refsXML10"></a> Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler, Fran&ccedil;ois Yergeau, editors; "<cite>Extensible Markup Language (XML) 1.0 (Third Edition)</cite>", W3C Recommendation, 4 February 2004
+  <dd>(<a href="http://www.w3.org/TR/REC-xml/"><code>http://www.w3.org/TR/REC-xml/</code></a>)
+
+  <dt>[XMLNAMES]
+  <dd><a name="refsXMLNAMES"></a> Tim Bray, Dave Hollander, Andrew Layman, editors; "<cite>Namespaces in XML</cite>", W3C Recommendation, 14 January 1999
+  <dd>(<a href="http://www.w3.org/TR/REC-xml-names/"><code>http://www.w3.org/TR/REC-xml-names/</code></a>)
+
+  <dt>[YACC]
+  <dd><a name="refsYACC"></a> S. C. Johnson; "<cite>YACC &mdash; Yet another compiler compiler</cite>", Technical Report, Murray Hill, 1975
+
+</dl>
+</body>
+</html>
diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
index edd37d32..19b6741 100644
--- a/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
+++ b/chrome/test/data/dromaeo/tests/jslib-traverse-jquery.html
@@ -1,10 +1,10 @@
 <html>
 <head>
 <script src="../htmlrunner.js"></script>
-<script src="../lib/jquery.js"></script>
+<script src="../lib/jquery.10.1.2.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-traverse-jquery", 'c67d0b8e');
+startTest("jslib-traverse-jquery", 'bc5b18f3');
 
 // Try to force real results
 var ret, tmp, div, dd;
@@ -18,7 +18,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("jQuery - parent x10", function(){
 		for ( var i = 0; i < 100; i++ )
 		ret = div.parent().length;
@@ -315,7 +315,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1498,7 +1498,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1515,7 +1515,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1707,7 +1707,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1721,7 +1721,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1734,7 +1734,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1913,11 +1913,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1926,8 +1926,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1936,11 +1936,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1953,8 +1953,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1969,7 +1969,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2107,7 +2107,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2218,11 +2218,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2729,14 +2729,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2760,13 +2760,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2774,10 +2774,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2795,11 +2795,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2835,9 +2835,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2856,7 +2856,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2909,7 +2909,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2932,7 +2932,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html b/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
index a39d148..68fe3bc2 100644
--- a/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
+++ b/chrome/test/data/dromaeo/tests/jslib-traverse-prototype.html
@@ -4,7 +4,7 @@
 <script src="../lib/prototype.js"></script>
 <script>
 window.onload = function(){
-startTest("jslib-traverse-prototype", '3358ef63');
+startTest("jslib-traverse-prototype", 'd46d3eab');
 
 // Try to force real results
 var ret, tmp, div, dd;
@@ -18,7 +18,7 @@
 		tmp.innerHTML = html;
 		document.body.appendChild( tmp );
 	});
-	
+
 	test("Prototype - up", function(){
 		for ( var i = 0; i < 10; i++ )
 		ret = div.invoke("up").length;
@@ -315,7 +315,7 @@
   non-normative.</p>
 
   <h3><a name=changesFromCSS2></a>1.3. Changes from CSS2</h3>
- 
+
   <p><em>This section is non-normative.</em></p>
 
   <p>The main differences between the selectors in CSS2 and those in
@@ -1498,7 +1498,7 @@
 sometimes in an indeterminate state, neither checked nor unchecked.
 This can be due to an element attribute, or DOM manipulation.</p>
 
-<p>A future version of this specification may introduce an 
+<p>A future version of this specification may introduce an
 <code>:indeterminate</code> pseudo-class that applies to such elements.
 <!--While the <code>:indeterminate</code> pseudo-class is dynamic in
 nature, and is altered by user action, since it can also be based on
@@ -1515,7 +1515,7 @@
 <p>Selectors introduces the concept of <dfn>structural
 pseudo-classes</dfn> to permit selection based on extra information that lies in
 the document tree but cannot be represented by other simple selectors or
-combinators. 
+combinators.
 
 <p>Note that standalone pieces of PCDATA (text nodes in the DOM) are
 not counted when calculating the position of an element in the list of
@@ -1707,7 +1707,7 @@
 &lt;div class="note"&gt;
    &lt;p&gt; The first P inside the note.&lt;/p&gt;
 &lt;/div&gt;</pre>but cannot represent the second <code>p</code> in the following
-fragment: 
+fragment:
   <pre>&lt;p&gt; The last P before the note.&lt;/p&gt;
 &lt;div class="note"&gt;
    &lt;h2&gt; Note &lt;/h2&gt;
@@ -1721,7 +1721,7 @@
 <h5><a name=last-child-pseudo>:last-child pseudo-class</a></h5>
 
 <p>Same as <code>:nth-last-child(1)</code>. The <code>:last-child</code> pseudo-class
-represents an element that is the last child of some other element. 
+represents an element that is the last child of some other element.
 
 <div class="example">
  <p>Example:</p>
@@ -1734,7 +1734,7 @@
 
 <p>Same as <code>:nth-of-type(1)</code>. The <code>:first-of-type</code> pseudo-class
 represents an element that is the first sibling of its type in the list of
-children of its parent element. 
+children of its parent element.
 
 <div class="example">
 <p>Example:</p>
@@ -1913,11 +1913,11 @@
 an ordinary HTML paragraph such as:</p>
 
 <pre>
-&lt;P&gt;This is a somewhat long HTML 
-paragraph that will be broken into several 
+&lt;P&gt;This is a somewhat long HTML
+paragraph that will be broken into several
 lines. The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1926,8 +1926,8 @@
 <pre>
 THIS IS A SOMEWHAT LONG HTML PARAGRAPH THAT
 will be broken into several lines. The first
-line will be identified by a fictional tag 
-sequence. The other lines will be treated as 
+line will be identified by a fictional tag
+sequence. The other lines will be treated as
 ordinary lines in the paragraph.
 </pre>
 
@@ -1936,11 +1936,11 @@
 fictional tag sequence helps to show how properties are inherited.</p>
 
 <pre>
-&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML 
+&lt;P&gt;<b>&lt;P::first-line&gt;</b> This is a somewhat long HTML
 paragraph that <b>&lt;/P::first-line&gt;</b> will be broken into several
-lines. The first line will be identified 
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+lines. The first line will be identified
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1953,8 +1953,8 @@
 &lt;P&gt;<b>&lt;SPAN class="test"&gt;</b> This is a somewhat long HTML
 paragraph that will be broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
-by a fictional tag sequence. The other lines 
-will be treated as ordinary lines in the 
+by a fictional tag sequence. The other lines
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -1969,7 +1969,7 @@
 broken into several
 lines.<b>&lt;/SPAN&gt;</b> The first line will be identified
 by a fictional tag sequence. The other lines
-will be treated as ordinary lines in the 
+will be treated as ordinary lines in the
 paragraph.&lt;/P&gt;
 </pre>
 
@@ -2107,7 +2107,7 @@
 &lt;P::first-letter&gt;
 T
 &lt;/P::first-letter&gt;he first
-&lt;/SPAN&gt; 
+&lt;/SPAN&gt;
 few words of an article in the Economist.
 &lt;/P&gt;
 </pre>
@@ -2218,11 +2218,11 @@
 
 <pre>&lt;P&gt;
 &lt;P::first-line&gt;
-&lt;P::first-letter&gt; 
-S 
-&lt;/P::first-letter&gt;ome text that 
-&lt;/P::first-line&gt; 
-ends up on two lines 
+&lt;P::first-letter&gt;
+S
+&lt;/P::first-letter&gt;ome text that
+&lt;/P::first-line&gt;
+ends up on two lines
 &lt;/P&gt;</pre>
 
 <p>Note that the <code>::first-letter</code> element is inside the <code>::first-line</code>
@@ -2729,14 +2729,14 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>universal selector<br>attribute selectors<br>:hover and :focus
       pseudo-classes<br>:target pseudo-class<br>:lang() pseudo-class<br>all UI
       element states pseudo-classes<br>all structural
       pseudo-classes<br>negation pseudo-class<br>all
       UI element fragments pseudo-elements<br>::before and ::after
       pseudo-elements<br>child combinators<br>sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2760,13 +2760,13 @@
   <tr>
     <th>Excludes</th>
     <td>
-      
+
 <p>content selectors<br>substring matching attribute
       selectors<br>:target pseudo-classes<br>all UI element
       states pseudo-classes<br>all structural pseudo-classes other
       than :first-child<br>negation pseudo-class<br>all UI element
       fragments pseudo-elements<br>general sibling combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Extra constraints</th>
@@ -2774,10 +2774,10 @@
       constraint) allowed</td></tr></tbody></table>
 
 <p>In CSS, selectors express pattern matching rules that determine which style
-rules apply to elements in the document tree. 
+rules apply to elements in the document tree.
 
 <p>The following selector (CSS level 2) will <b>match</b> all anchors <code>a</code>
-with attribute <code>name</code> set inside a section 1 header <code>h1</code>: 
+with attribute <code>name</code> set inside a section 1 header <code>h1</code>:
 <pre>h1 a[name]</pre>
 
 <p>All CSS declarations attached to such a selector are applied to elements
@@ -2795,11 +2795,11 @@
   <tr>
     <th>Accepts</th>
     <td>
-      
+
 <p>type selectors<br>universal selectors<br>attribute selectors<br>class
       selectors<br>ID selectors<br>all structural pseudo-classes<br>
           all combinators
-      
+
 <p>namespaces</td></tr>
   <tr>
     <th>Excludes</th>
@@ -2835,9 +2835,9 @@
 <input type="text" name="foo"/>
 <input type="text" name="foo"/>
 </form>
-  
+
 <p>Selectors can be used in STTS 3 in two different
-    manners: 
+    manners:
 <ol>
   <li>a selection mechanism equivalent to CSS selection mechanism: declarations
   attached to a given selector are applied to elements matching that selector,
@@ -2856,7 +2856,7 @@
 <p>All specifications reusing Selectors must contain a <a
 href="#profiling">Profile</a> listing the
 subset of Selectors it accepts or excludes, and describing the constraints
-it adds to the current specification. 
+it adds to the current specification.
 
 <p>Invalidity is caused by a parsing error, e.g. an unrecognized token or a token
 which is not allowed at the current parsing point.
@@ -2909,7 +2909,7 @@
   <dd>(<code><a href="http://www.w3.org/TR/REC-CSS1">http://www.w3.org/TR/REC-CSS1</a></code>)
 
   <dt>[CSS21]
-  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005 
+  <dd><a name=refsCSS21></a> Bert Bos, Tantek &Ccedil;elik, Ian Hickson, H&aring;kon Wium Lie, editors; "<cite>Cascading Style Sheets, level 2 revision 1</cite>", W3C Working Draft, 13 June 2005
   <dd>(<code><a href="http://www.w3.org/TR/CSS21">http://www.w3.org/TR/CSS21</a></code>)
 
   <dt>[CWWW]
@@ -2932,7 +2932,7 @@
   <dd>(<a href="http://www.ietf.org/rfc/rfc3066.txt"><code>http://www.ietf.org/rfc/rfc3066.txt</code></a>)
 
   <dt>[STTS]
-  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998 
+  <dd><a name=refsSTTS></a> Daniel Glazman; "<cite>Simple Tree Transformation Sheets 3</cite>", Electricit&eacute; de France, submission to the W3C, 11 November 1998
   <dd>(<code><a href="http://www.w3.org/TR/NOTE-STTS3">http://www.w3.org/TR/NOTE-STTS3</a></code>)
 
   <dt>[SVG]
diff --git a/chrome/test/data/dromaeo/webrunner.js b/chrome/test/data/dromaeo/webrunner.js
index 8b0e346..598998bb 100644
--- a/chrome/test/data/dromaeo/webrunner.js
+++ b/chrome/test/data/dromaeo/webrunner.js
@@ -148,7 +148,7 @@
 		});
 
 		function compute(times, runs){
-			var results = {runs: runs}, num = times.length;
+			var results = {runs: runs}, num = times.length, middle = num/2;
 
 			times = times.sort(function(a,b){
 				return a - b;
@@ -171,8 +171,8 @@
 			
 			// Make Median
 			results.median = num % 2 == 0 ?
-				(times[Math.floor(num/2)] + times[Math.ceil(num/2)]) / 2 :
-				times[Math.round(num/2)];
+				(times[middle-1] + times[middle]) / 2 :
+				times[Math.floor(middle)];
 			
 			// Make Variance
 			results.variance = 0;
diff --git a/chrome/test/data/extensions/api_test/messaging/background_only/test.js b/chrome/test/data/extensions/api_test/messaging/background_only/test.js
index de7cd9e..b6ed602 100644
--- a/chrome/test/data/extensions/api_test/messaging/background_only/test.js
+++ b/chrome/test/data/extensions/api_test/messaging/background_only/test.js
@@ -42,4 +42,58 @@
     chrome.runtime.connect({ name: 'The Last Port'}).onDisconnect.addListener(
         chrome.test.callbackFail(kPortErrorMessage));
   },
+
+  // Regression test for crbug.com/597698
+  function sendMessageNoCallback() {
+    var f = document.createElement('iframe');
+    var onMessageInFrame = chrome.test.callbackPass(function(msg) {
+      f.remove();
+      chrome.test.assertEq('sendMessage without callback', msg);
+    });
+    f.onload = function() {
+      f.contentWindow.chrome.runtime.onMessage.addListener(onMessageInFrame);
+      chrome.runtime.sendMessage('sendMessage without callback');
+    };
+
+    // The exact file is not important, as long as it is an extension page, so
+    // that the extension APIs become available (about:blank would not work).
+    f.src = 'manifest.json';
+    document.body.appendChild(f);
+  },
+
+  // Regression test for crbug.com/597698
+  function connectAndDisconnect() {
+    var gotMessage = chrome.test.callbackAdded();
+    var gotDisconnect = chrome.test.callbackAdded();
+
+    var senderPort;
+    var f = document.createElement('iframe');
+    f.onload = function() {
+      f.contentWindow.chrome.runtime.onConnect.addListener(function(port) {
+        chrome.test.assertEq('port with active frame', port.name);
+        chrome.test.assertEq(null, senderPort, 'onConnect should be async');
+        var didCallOnMessage = false;
+        port.onMessage.addListener(function(msg) {
+          chrome.test.assertEq(false, didCallOnMessage);
+          didCallOnMessage = true;
+          chrome.test.assertEq('fire and forget', msg);
+          gotMessage();
+        });
+        port.onDisconnect.addListener(function() {
+          f.remove();
+          gotDisconnect();
+        });
+      });
+
+      senderPort = chrome.runtime.connect({ name: 'port with active frame' });
+      senderPort.postMessage('fire and forget');
+      senderPort.disconnect();
+      senderPort = null;
+    };
+
+    // The exact file is not important, as long as it is an extension page, so
+    // that the extension APIs become available (about:blank would not work).
+    f.src = 'manifest.json';
+    document.body.appendChild(f);
+  },
 ]);
diff --git a/chrome/test/data/extensions/platform_apps/active_test/test.js b/chrome/test/data/extensions/platform_apps/active_test/test.js
index b2671c5..984c49748 100644
--- a/chrome/test/data/extensions/platform_apps/active_test/test.js
+++ b/chrome/test/data/extensions/platform_apps/active_test/test.js
@@ -33,16 +33,9 @@
 
     chrome.app.window.create('empty.html', createOptions,
         function(createdWindow) {
-      handleWindowReady = function() {
-        createdWindow.onClosed.addListener(windowClosed);
-        windows.push(createdWindow);
-        processNextCommand();
-      }
-
-      if (createOptions.hidden)
-        handleWindowReady();
-      else
-        createdWindow.handleWindowFirstShownForTests(handleWindowReady);
+      createdWindow.onClosed.addListener(windowClosed);
+      windows.push(createdWindow);
+      processNextCommand();
     });
   });
 }
diff --git a/chrome/test/data/extensions/platform_apps/background_page_navigation/nav-target.js b/chrome/test/data/extensions/platform_apps/background_page_navigation/nav-target.js
index 77938ce8..e6e186b 100644
--- a/chrome/test/data/extensions/platform_apps/background_page_navigation/nav-target.js
+++ b/chrome/test/data/extensions/platform_apps/background_page_navigation/nav-target.js
@@ -4,6 +4,4 @@
 
 // We should never reach this page; if we have then it's a signal that we've
 // navigated away from the app page, and we should have the test fail.
-// TODO(lazyboy): Because of http://crbug.com/585570, we *do* reach this page,
-// enable the following line once the bug is fixed.
-//chrome.test.notifyFail('Navigated to ' + window.location.href);
+chrome.test.notifyFail('Navigated to ' + window.location.href);
diff --git a/chrome/test/data/extensions/platform_apps/background_page_navigation/test.js b/chrome/test/data/extensions/platform_apps/background_page_navigation/test.js
index 1da617e..f2060db 100644
--- a/chrome/test/data/extensions/platform_apps/background_page_navigation/test.js
+++ b/chrome/test/data/extensions/platform_apps/background_page_navigation/test.js
@@ -25,8 +25,7 @@
     // Trying to open a local resource in a new window will fail.
     function windowOpenInAppRelativeURL() {
       var w = window.open(IN_APP_RELATIVE_URL);
-      // TODO(lazyboy): Enable the assert once http://crbug.com/585570 is fixed.
-      //chrome.test.assertTrue(!w);
+      chrome.test.assertTrue(!w);
       chrome.test.succeed();
     },
     // Trying to open a local resource in a new window will fail.
@@ -39,8 +38,7 @@
     // Similar to windowOpenInAppRelativeURL().
     function windowOpenInAppAbsoluteURL() {
       var w = window.open(IN_APP_ABSOLUTE_URL);
-      // TODO(lazyboy): Enable the assert once http://crbug.com/585570 is fixed.
-      //chrome.test.assertTrue(!w);
+      chrome.test.assertTrue(!w);
       chrome.test.succeed();
     },
     // Similar to openLinkToInAppRelativeURL().
diff --git a/chrome/test/data/page_load_metrics/page_with_css.html b/chrome/test/data/page_load_metrics/page_with_css.html
index c9d154f..e7ee7a8 100644
--- a/chrome/test/data/page_load_metrics/page_with_css.html
+++ b/chrome/test/data/page_load_metrics/page_with_css.html
@@ -7,6 +7,6 @@
 <script>
   for (var i = 0; i < 50; i++) {
     document.write('<link rel="stylesheet" href="/page_load_metrics/simple.css?' + i + '">');
+    document.write('<div><p><a>Mark me up! We need a few of these tags to cause a long enough aggregate UpdateStyle');
   }
 </script>
-<p>Mark me up!</p>
diff --git a/chrome/test/data/pdf/zoom_manager_test.js b/chrome/test/data/pdf/zoom_manager_test.js
index f02010c6..23bc8c3 100644
--- a/chrome/test/data/pdf/zoom_manager_test.js
+++ b/chrome/test/data/pdf/zoom_manager_test.js
@@ -9,12 +9,17 @@
     constructor() {
       this.zooms = [];
       this.zoom = 1;
+      this.browserOnlyZoomChange = false;
     }
 
     setZoom(zoom) {
       this.zooms.push(zoom);
       this.zoom = zoom;
     }
+
+    updateZoomFromBrowserChange(oldBrowserZoom) {
+      this.browserOnlyZoomChange = true;
+    }
   }
 
   /**
@@ -53,9 +58,9 @@
     function testZoomChange() {
       let viewport = new MockViewport();
       let browserZoomSetter = new MockBrowserZoomSetter();
-      let zoomManager = new ZoomManager(
-          viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter),
-          1);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport,
+          browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), 1);
       viewport.zoom = 2;
       zoomManager.onPdfZoomChange();
       chrome.test.assertEq(2, browserZoomSetter.zoom);
@@ -65,7 +70,8 @@
 
     function testBrowserZoomChange() {
       let viewport = new MockViewport();
-      let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport, chrome.test.fail, 1);
       zoomManager.onBrowserZoomChange(3);
       chrome.test.assertEq(1, viewport.zooms.length);
       chrome.test.assertEq(3, viewport.zooms[0]);
@@ -73,12 +79,28 @@
       chrome.test.succeed();
     },
 
+    function testBrowserZoomChangeEmbedded() {
+      let viewport = new MockViewport();
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.PROPAGATE_PARENT, viewport,
+          function() { return Promise.reject(); }, 1);
+
+      // Zooming in the browser should not overwrite the viewport's zoom,
+      // but be applied seperately.
+      viewport.zoom = 2;
+      zoomManager.onBrowserZoomChange(3);
+      chrome.test.assertEq(2, viewport.zoom);
+      chrome.test.assertTrue(viewport.browserOnlyZoomChange);
+
+      chrome.test.succeed();
+    },
+
     function testSmallZoomChange() {
       let viewport = new MockViewport();
       let browserZoomSetter = new MockBrowserZoomSetter();
-      let zoomManager = new ZoomManager(
-          viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter),
-          2);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport,
+          browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), 2);
       viewport.zoom = 2.0001;
       zoomManager.onPdfZoomChange();
       chrome.test.assertEq(1, browserZoomSetter.zoom);
@@ -88,7 +110,8 @@
 
     function testSmallBrowserZoomChange() {
       let viewport = new MockViewport();
-      let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport, chrome.test.fail, 1);
       zoomManager.onBrowserZoomChange(0.999);
       chrome.test.assertEq(0, viewport.zooms.length);
       chrome.test.assertEq(1, viewport.zoom);
@@ -98,9 +121,9 @@
     function testMultiplePdfZoomChanges() {
       let viewport = new MockViewport();
       let browserZoomSetter = new MockBrowserZoomSetter();
-      let zoomManager = new ZoomManager(
-          viewport, browserZoomSetter.setBrowserZoom.bind(browserZoomSetter),
-          1);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport,
+          browserZoomSetter.setBrowserZoom.bind(browserZoomSetter), 1);
       viewport.zoom = 2;
       zoomManager.onPdfZoomChange();
       viewport.zoom = 3;
@@ -117,7 +140,8 @@
 
     function testMultipleBrowserZoomChanges() {
       let viewport = new MockViewport();
-      let zoomManager = new ZoomManager(viewport, chrome.test.fail, 1);
+      let zoomManager = ZoomManager.create(
+          BrowserApi.ZoomBehavior.MANAGE, viewport, chrome.test.fail, 1);
       zoomManager.onBrowserZoomChange(2);
       zoomManager.onBrowserZoomChange(3);
       chrome.test.assertEq(2, viewport.zooms.length);
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index 6ffdaf9..08c8baa 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -18,8 +18,7 @@
 
     if (cr.isChromeOS) {
       methodNames.push(
-        'getCurrentChannel',
-        'getTargetChannel',
+        'getChannelInfo',
         'getVersionInfo',
         'getRegulatoryInfo',
         'setChannel');
@@ -31,18 +30,19 @@
     this.updateStatus_ = UpdateStatus.UPDATED;
 
     if (cr.isChromeOS) {
-      /** @type {!VersionInfo} */
+      /** @private {!VersionInfo} */
       this.versionInfo_ = {
         arcVersion: '',
         osFirmware: '',
         osVersion: '',
       };
 
-      /** @private {!BrowserChannel} */
-      this.currentChannel_ = BrowserChannel.BETA;
-
-      /** @private {!BrowserChannel} */
-      this.targetChannel_ = BrowserChannel.BETA;
+      /** @private {!ChannelInfo} */
+      this.channelInfo_ = {
+        currentChannel: BrowserChannel.BETA,
+        targetChannel: BrowserChannel.BETA,
+        canChangeChannel: true,
+      };
 
       /** @private {?RegulatoryInfo} */
       this.regulatoryInfo_ = null;
@@ -88,17 +88,22 @@
       this.versionInfo_ = versionInfo;
     };
 
+    /** @param {boolean} canChangeChannel */
+    TestAboutPageBrowserProxy.prototype.setCanChangeChannel = function(
+        canChangeChannel) {
+      this.channelInfo_.canChangeChannel = canChangeChannel;
+    };
+
     /**
      * @param {!BrowserChannel} current
      * @param {!BrowserChannel} target
      */
     TestAboutPageBrowserProxy.prototype.setChannels = function(
         current, target) {
-      this.currentChannel_ = current;
-      this.targetChannel_ = target;
+      this.channelInfo_.currentChannel = current;
+      this.channelInfo_.targetChannel = target;
     };
 
-
     /** @param {?RegulatoryInfo} regulatoryInfo */
     TestAboutPageBrowserProxy.prototype.setRegulatoryInfo = function(
         regulatoryInfo) {
@@ -106,15 +111,9 @@
     };
 
     /** @override */
-    TestAboutPageBrowserProxy.prototype.getCurrentChannel = function() {
-      this.methodCalled('getCurrentChannel');
-      return Promise.resolve(this.currentChannel_);
-    };
-
-    /** @override */
-    TestAboutPageBrowserProxy.prototype.getTargetChannel = function() {
-      this.methodCalled('getTargetChannel');
-      return Promise.resolve(this.targetChannel_);
+    TestAboutPageBrowserProxy.prototype.getChannelInfo = function() {
+      this.methodCalled('getChannelInfo');
+      return Promise.resolve(this.channelInfo_);
     };
 
     /** @override */
@@ -186,7 +185,14 @@
         page = document.createElement('settings-about-page');
         settings.navigateTo(settings.Route.ABOUT);
         document.body.appendChild(page);
-        return aboutBrowserProxy.whenCalled('refreshUpdateStatus');
+        if (!cr.isChromeOS) {
+          return aboutBrowserProxy.whenCalled('refreshUpdateStatus');
+        } else {
+          return Promise.all([
+            aboutBrowserProxy.whenCalled('getChannelInfo'),
+            aboutBrowserProxy.whenCalled('refreshUpdateStatus'),
+          ]);
+        }
       }
 
       /**
@@ -257,7 +263,9 @@
           assertTrue(!!page.$.deprecationWarning);
 
           assertFalse(page.$.deprecationWarning.hidden);
-          assertFalse(page.$.updateStatusMessage.hidden);
+          // Update status message should be hidden before user has checked for
+          // updates, on ChromeOS.
+          assertEquals(cr.isChromeOS, page.$.updateStatusMessage.hidden);
 
           fireStatusChanged(UpdateStatus.CHECKING);
           assertEquals(SPINNER_ICON, icon.src);
@@ -318,12 +326,12 @@
       });
 
       test('Relaunch', function() {
-        var relaunchContainer = page.$.relaunchContainer;
-        assertTrue(!!relaunchContainer);
-        assertTrue(relaunchContainer.hidden);
+        var relaunch = page.$.relaunch;
+        assertTrue(!!relaunch);
+        assertTrue(relaunch.hidden);
 
         fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
-        assertFalse(relaunchContainer.hidden);
+        assertFalse(relaunch.hidden);
 
         var relaunch = page.$.relaunch;
         assertTrue(!!relaunch);
@@ -338,25 +346,28 @@
          * channel are the same.
          */
         test('ButtonsUpdate_SameChannel', function() {
-          var relaunchContainer = page.$.relaunchContainer;
+          var relaunch = page.$.relaunch;
           var checkForUpdates = page.$.checkForUpdates;
           var relaunchAndPowerwash = page.$.relaunchAndPowerwash;
 
-          assertTrue(!!relaunchContainer);
+          assertTrue(!!relaunch);
           assertTrue(!!relaunchAndPowerwash);
           assertTrue(!!checkForUpdates);
 
           function assertAllHidden() {
             assertTrue(checkForUpdates.hidden);
-            assertTrue(relaunchContainer.hidden);
+            assertTrue(relaunch.hidden);
             assertTrue(relaunchAndPowerwash.hidden);
+            // Ensure that when all buttons are hidden, the container is also
+            // hidden.
+            assertTrue(page.$.buttonContainer.hidden);
           }
 
           // Check that |UPDATED| status is ignored if the user has not
           // explicitly checked for updates yet.
           fireStatusChanged(UpdateStatus.UPDATED);
           assertFalse(checkForUpdates.hidden);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
           assertTrue(relaunchAndPowerwash.hidden);
 
           fireStatusChanged(UpdateStatus.CHECKING);
@@ -367,7 +378,7 @@
 
           fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
           assertTrue(checkForUpdates.hidden);
-          assertFalse(relaunchContainer.hidden);
+          assertFalse(relaunch.hidden);
           assertTrue(relaunchAndPowerwash.hidden);
 
           fireStatusChanged(UpdateStatus.UPDATED);
@@ -375,7 +386,7 @@
 
           fireStatusChanged(UpdateStatus.FAILED);
           assertFalse(checkForUpdates.hidden);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
           assertTrue(relaunchAndPowerwash.hidden);
 
           fireStatusChanged(UpdateStatus.DISABLED);
@@ -396,10 +407,10 @@
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
           return initNewPage().then(function() {
-            assertTrue(!!page.$.relaunchContainer);
+            assertTrue(!!page.$.relaunch);
             assertTrue(!!page.$.relaunchAndPowerwash);
 
-            assertTrue(page.$.relaunchContainer.hidden);
+            assertTrue(page.$.relaunch.hidden);
             assertFalse(page.$.relaunchAndPowerwash.hidden);
 
             MockInteractions.tap(page.$.relaunchAndPowerwash);
@@ -418,10 +429,10 @@
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
           return initNewPage().then(function() {
-            assertTrue(!!page.$.relaunchContainer);
+            assertTrue(!!page.$.relaunch);
             assertTrue(!!page.$.relaunchAndPowerwash);
 
-            assertFalse(page.$.relaunchContainer.hidden);
+            assertFalse(page.$.relaunch.hidden);
             assertTrue(page.$.relaunchAndPowerwash.hidden);
 
             MockInteractions.tap(page.$.relaunch);
@@ -440,15 +451,15 @@
           aboutBrowserProxy.setUpdateStatus(UpdateStatus.NEARLY_UPDATED);
 
           return initNewPage().then(function() {
-            assertFalse(page.$.relaunchContainer.hidden);
+            assertFalse(page.$.relaunch.hidden);
             assertTrue(page.$.relaunchAndPowerwash.hidden);
 
             page.fire('target-channel-changed', BrowserChannel.DEV);
-            assertFalse(page.$.relaunchContainer.hidden);
+            assertFalse(page.$.relaunch.hidden);
             assertTrue(page.$.relaunchAndPowerwash.hidden);
 
             page.fire('target-channel-changed', BrowserChannel.STABLE);
-            assertTrue(page.$.relaunchContainer.hidden);
+            assertTrue(page.$.relaunch.hidden);
             assertFalse(page.$.relaunchAndPowerwash.hidden);
           });
         });
@@ -494,29 +505,29 @@
          * 'update-status-changed' events.
          */
         test('ButtonsUpdate', function() {
-          var relaunchContainer = page.$.relaunchContainer;
-          assertTrue(!!relaunchContainer);
+          var relaunch = page.$.relaunch;
+          assertTrue(!!relaunch);
 
           fireStatusChanged(UpdateStatus.CHECKING);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.UPDATING);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
-          assertFalse(relaunchContainer.hidden);
+          assertFalse(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.UPDATED);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.FAILED);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.DISABLED);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
 
           fireStatusChanged(UpdateStatus.DISABLED_BY_ADMIN);
-          assertTrue(relaunchContainer.hidden);
+          assertTrue(relaunch.hidden);
         });
       }
 
@@ -580,7 +591,7 @@
           return Promise.all([
             browserProxy.whenCalled('pageReady'),
             browserProxy.whenCalled('getVersionInfo'),
-            browserProxy.whenCalled('getCurrentChannel'),
+            browserProxy.whenCalled('getChannelInfo'),
           ]).then(function() {
             assertEquals(versionInfo.arcVersion, page.$.arcVersion.textContent);
             assertEquals(versionInfo.osVersion, page.$.osVersion.textContent);
@@ -594,25 +605,25 @@
          * dictated by the browser via loadTimeData boolean).
          * @param {boolean} canChangeChannel Whether to simulate the case where
          *     changing channels is allowed.
+         * @return {!Promise}
          */
         function checkChangeChannelButton(canChangeChannel) {
-          loadTimeData.overrideValues({
-            aboutCanChangeChannel: canChangeChannel
-          });
+          browserProxy.setCanChangeChannel(canChangeChannel);
           page = document.createElement('settings-detailed-build-info');
           document.body.appendChild(page);
-
-          var changeChannelButton = page.$$('paper-button');
-          assertTrue(!!changeChannelButton);
-          assertEquals(canChangeChannel, !changeChannelButton.disabled)
+          return browserProxy.whenCalled('getChannelInfo').then(function() {
+            var changeChannelButton = page.$$('paper-button');
+            assertTrue(!!changeChannelButton);
+            assertEquals(canChangeChannel, !changeChannelButton.disabled);
+          });
         }
 
         test('ChangeChannel_Enabled', function() {
-          checkChangeChannelButton(true);
+          return checkChangeChannelButton(true);
         });
 
         test('ChangeChannel_Disabled', function() {
-          checkChangeChannelButton(false);
+          return checkChangeChannelButton(false);
         });
       });
     }
@@ -635,7 +646,7 @@
           radioButtons = dialog.shadowRoot.querySelectorAll(
               'paper-radio-button');
           assertEquals(3, radioButtons.length);
-          return browserProxy.whenCalled('getCurrentChannel');
+          return browserProxy.whenCalled('getChannelInfo');
         });
 
         teardown(function() { dialog.remove(); });
@@ -664,21 +675,23 @@
           MockInteractions.tap(radioButtons.item(2));
           Polymer.dom.flush();
 
-          assertFalse(dialog.$.warning.hidden);
-          // Check that only the "Change channel" button becomes visible.
-          assertTrue(dialog.$.changeChannelAndPowerwash.hidden);
-          assertFalse(dialog.$.changeChannel.hidden);
+          return browserProxy.whenCalled('getChannelInfo').then(function() {
+            assertFalse(dialog.$.warning.hidden);
+            // Check that only the "Change channel" button becomes visible.
+            assertTrue(dialog.$.changeChannelAndPowerwash.hidden);
+            assertFalse(dialog.$.changeChannel.hidden);
 
-          var whenTargetChannelChangedFired = test_util.eventToPromise(
-              'target-channel-changed', dialog);
+            var whenTargetChannelChangedFired = test_util.eventToPromise(
+                'target-channel-changed', dialog);
 
-          MockInteractions.tap(dialog.$.changeChannel);
-          return browserProxy.whenCalled('setChannel').then(function(args) {
-            assertEquals(BrowserChannel.DEV, args[0]);
-            assertFalse(args[1]);
-            return whenTargetChannelChangedFired;
-          }).then(function(event) {
-            assertEquals(BrowserChannel.DEV, event.detail);
+            MockInteractions.tap(dialog.$.changeChannel);
+            return browserProxy.whenCalled('setChannel').then(function(args) {
+              assertEquals(BrowserChannel.DEV, args[0]);
+              assertFalse(args[1]);
+              return whenTargetChannelChangedFired;
+            }).then(function(event) {
+              assertEquals(BrowserChannel.DEV, event.detail);
+            });
           });
         });
 
@@ -688,22 +701,24 @@
           MockInteractions.tap(radioButtons.item(0));
           Polymer.dom.flush();
 
-          assertFalse(dialog.$.warning.hidden);
-          // Check that only the "Change channel and Powerwash" button becomes
-          // visible.
-          assertFalse(dialog.$.changeChannelAndPowerwash.hidden);
-          assertTrue(dialog.$.changeChannel.hidden);
+          return browserProxy.whenCalled('getChannelInfo').then(function() {
+            assertFalse(dialog.$.warning.hidden);
+            // Check that only the "Change channel and Powerwash" button becomes
+            // visible.
+            assertFalse(dialog.$.changeChannelAndPowerwash.hidden);
+            assertTrue(dialog.$.changeChannel.hidden);
 
-          var whenTargetChannelChangedFired = test_util.eventToPromise(
-              'target-channel-changed', dialog);
+            var whenTargetChannelChangedFired = test_util.eventToPromise(
+                'target-channel-changed', dialog);
 
-          MockInteractions.tap(dialog.$.changeChannelAndPowerwash);
-          return browserProxy.whenCalled('setChannel').then(function(args) {
-            assertEquals(BrowserChannel.STABLE, args[0]);
-            assertTrue(args[1]);
-            return whenTargetChannelChangedFired;
-          }).then(function(event) {
-            assertEquals(BrowserChannel.STABLE, event.detail);
+            MockInteractions.tap(dialog.$.changeChannelAndPowerwash);
+            return browserProxy.whenCalled('setChannel').then(function(args) {
+              assertEquals(BrowserChannel.STABLE, args[0]);
+              assertTrue(args[1]);
+              return whenTargetChannelChangedFired;
+            }).then(function(event) {
+              assertEquals(BrowserChannel.STABLE, event.detail);
+            });
           });
         });
       });
diff --git a/chrome/test/data/webui/settings/basic_page_browsertest.js b/chrome/test/data/webui/settings/basic_page_browsertest.js
index 9fde2be..8e1b2d90 100644
--- a/chrome/test/data/webui/settings/basic_page_browsertest.js
+++ b/chrome/test/data/webui/settings/basic_page_browsertest.js
@@ -13,7 +13,12 @@
 function SettingsBasicPageBrowserTest() {}
 
 SettingsBasicPageBrowserTest.prototype = {
-  __proto__: SettingsPageBrowserTest.prototype
+  __proto__: SettingsPageBrowserTest.prototype,
+
+  /** @override */
+  extraLibraries: SettingsPageBrowserTest.prototype.extraLibraries.concat([
+    'test_browser_proxy.js',
+  ]),
 };
 
 // Times out on debug builders and may time out on memory bots because
@@ -30,6 +35,49 @@
   // and test() will be a Mocha 'Suite' or 'Test' instance.
   var self = this;
 
+  /**
+   * This fake SearchManager just hides and re-displays the sections on search.
+   *
+   * @implements {SearchManager}
+   * @extends {settings.TestBrowserProxy}
+   */
+  var TestSearchManager = function() {
+    settings.TestBrowserProxy.call(this, [
+      'search',
+    ]);
+
+    /** @private {?settings.SearchRequest} */
+    this.searchRequest_ = null;
+  }
+
+  TestSearchManager.prototype = {
+    __proto__: settings.TestBrowserProxy.prototype,
+
+    /** @override */
+    search: function(text, page) {
+      if (this.searchRequest_ == null || !this.searchRequest_.isSame(text)) {
+        this.searchRequest_ = new settings.SearchRequest(text);
+        this.searchRequest_.finished = true;
+        this.searchRequest_.updateMatches(false);
+
+        // The search much be resolved asynchronously just like the real
+        // SearchManager. Otherwise, the race condition doesn't manifest.
+        setTimeout(function() {
+          // All the sections must be hidden by the TestSearchManager, just like
+          // the real SearchManager. Otherwise, the bug doesn't manifest.
+          var sections =
+              Polymer.dom().querySelectorAll('* /deep/ settings-section');
+          for (var i = 0; i < sections.length; i++)
+            sections[i].hiddenBySearch = !!text;
+
+          this.searchRequest_.resolver.resolve(this.searchRequest_);
+          this.methodCalled('search', text);
+        }.bind(this), 0);
+      }
+      return this.searchRequest_.resolver.promise;
+    },
+  };
+
   // Register mocha tests.
   suite('SettingsPage', function() {
     test('load page', function() {
@@ -81,6 +129,33 @@
         assertEquals(0, page.scroller.scrollTop);
       });
     });
+
+    test('scroll to section after exiting search', function() {
+      var searchManager = new TestSearchManager();
+      settings.setSearchManagerForTesting(searchManager);
+
+      settings.navigateTo(settings.Route.BASIC,
+                          new URLSearchParams(`search=foobar`),
+                          /* removeSearch */ false);
+      return searchManager.whenCalled('search').then(function() {
+        return new Promise(function(resolve) {
+          settings.navigateTo(settings.Route.ON_STARTUP,
+                              /* dynamicParams */ null,
+                              /* removeSearch */ true);
+
+          var page = self.getPage('basic');
+          assertTrue(!!page);
+
+          // Should (after some time) be scrolled to the On Startup section.
+          var intervalId = window.setInterval(function() {
+            if (page.scroller.scrollTop != 0) {
+              window.clearInterval(intervalId);
+              resolve();
+            }
+          }, 55);
+        });
+      });
+    });
   });
 
   // Run all registered tests.
diff --git a/chrome/test/data/webui/settings/languages_page_browsertest.js b/chrome/test/data/webui/settings/languages_page_browsertest.js
index 6d8a539..8a64216e 100644
--- a/chrome/test/data/webui/settings/languages_page_browsertest.js
+++ b/chrome/test/data/webui/settings/languages_page_browsertest.js
@@ -295,6 +295,27 @@
         assertFalse(spellCheckSettingsExist);
       } else {
         assertTrue(spellCheckSettingsExist);
+
+        // Ensure no language has spell check enabled.
+        for (var i = 0; i < languagesPage.languages.enabled.length; i++) {
+          languagesPage.set(
+              'languages.enabled.' + i + '.spellCheckEnabled', false);
+        }
+
+        // The row button should have the extra row only if some language has
+        // spell check enabled.
+        var triggerRow = languagesPage.$.spellCheckSubpageTrigger;
+        assertFalse(triggerRow.classList.contains('two-line'));
+        assertEquals(
+            0, triggerRow.querySelector('.secondary').textContent.length);
+
+        languagesPage.set(
+            'languages.enabled.0.language.supportsSpellcheck', true);
+        languagesPage.set('languages.enabled.0.spellCheckEnabled', true);
+        assertTrue(triggerRow.classList.contains('two-line'));
+        assertLT(
+            0, triggerRow.querySelector('.secondary').textContent.length);
+
         MockInteractions.tap(
             spellCheckCollapse.querySelector('.list-button:last-of-type'));
         assertTrue(!!languagesPage.$$('settings-edit-dictionary-page'));
diff --git a/chrome/test/data/webui/settings/search_engines_page_test.js b/chrome/test/data/webui/settings/search_engines_page_test.js
index d4f684b..6f26ef0 100644
--- a/chrome/test/data/webui/settings/search_engines_page_test.js
+++ b/chrome/test/data/webui/settings/search_engines_page_test.js
@@ -256,6 +256,16 @@
       test('Edit_Disabled', function() {
         testButtonDisabled(createSampleSearchEngine(true, false, true), 'edit');
       });
+
+      test('All_Disabled', function() {
+        entry.engine = createSampleSearchEngine(true, false, false);
+        Polymer.dom.flush();
+        assertTrue(entry.hasAttribute('show-dots_'));
+
+        entry.engine = createSampleSearchEngine(false, false, false);
+        Polymer.dom.flush();
+        assertFalse(entry.hasAttribute('show-dots_'));
+      });
     });
   }
 
diff --git a/chrome/test/data/webui/settings/site_settings_category_tests.js b/chrome/test/data/webui/settings/site_settings_category_tests.js
index 58eb160..fc192598 100644
--- a/chrome/test/data/webui/settings/site_settings_category_tests.js
+++ b/chrome/test/data/webui/settings/site_settings_category_tests.js
@@ -24,7 +24,9 @@
        */
       var prefsLocationDisabled = {
         defaults: {
-          geolocation: 'block',
+          geolocation: {
+            setting: 'block',
+          },
         },
         exceptions: {
           geolocation: [],
@@ -37,7 +39,9 @@
        */
       var prefsLocationEnabled = {
         defaults: {
-          geolocation: 'allow',
+          geolocation: {
+            setting: 'allow',
+          },
         },
         exceptions: {
           geolocation: [],
@@ -49,7 +53,9 @@
        */
       var prefsFlashDetect = {
         defaults: {
-          plugins: 'detect_important_content',
+          plugins: {
+            setting: 'detect_important_content',
+          },
         },
         exceptions: {
           plugins: [],
@@ -62,7 +68,9 @@
        */
       var prefsCookesSessionOnly = {
         defaults: {
-          cookies: 'session_only',
+          cookies: {
+            setting: 'session_only',
+          },
         },
         exceptions: {
           cookies: [],
@@ -94,6 +102,7 @@
       });
 
       function testCategoryEnabled(testElement, enabled) {
+        browserProxy.reset();
         browserProxy.setPrefs(
             enabled ? prefsLocationEnabled : prefsLocationDisabled);
 
@@ -105,13 +114,13 @@
             assertEquals(enabled, testElement.categoryEnabled);
             MockInteractions.tap(testElement.$.toggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             assertEquals(
-                settings.ContentSettingsTypes.GEOLOCATION, arguments[0]);
+                settings.ContentSettingsTypes.GEOLOCATION, args[0]);
             assertEquals(
                 enabled ? settings.PermissionValues.BLOCK :
                     settings.PermissionValues.ASK,
-                arguments[1]);
+                args[1]);
             assertNotEquals(enabled, testElement.categoryEnabled);
           });
       }
@@ -167,114 +176,115 @@
         }
       });
 
-      function testTristateCategory(prefs, category, thirdState, checkbox) {
+      function testTristateCategory(prefs, category, thirdState,
+                                    secondaryToggleId) {
         browserProxy.setPrefs(prefs);
 
         testElement.category = category;
-        var askCheckbox = null;
+        var secondaryToggle = null;
 
         return browserProxy.whenCalled('getDefaultValueForContentType').then(
           function(contentType) {
             Polymer.dom.flush();
 
-            askCheckbox = testElement.$$(checkbox);
-            assertTrue(!!askCheckbox);
+            secondaryToggle = testElement.$$(secondaryToggleId);
+            assertTrue(!!secondaryToggle);
 
             assertEquals(category, contentType);
             assertTrue(testElement.categoryEnabled);
-            assertFalse(askCheckbox.disabled);
-            assertTrue(askCheckbox.checked);
+            assertFalse(secondaryToggle.disabled);
+            assertTrue(secondaryToggle.checked);
 
             MockInteractions.tap(testElement.$.toggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check THIRD_STATE => BLOCK transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
-            assertEquals(settings.PermissionValues.BLOCK, arguments[1]);
+            assertEquals(category, args[0]);
+            assertEquals(settings.PermissionValues.BLOCK, args[1]);
             assertFalse(testElement.categoryEnabled);
-            assertTrue(askCheckbox.disabled);
-            assertTrue(askCheckbox.checked);
+            assertTrue(secondaryToggle.disabled);
+            assertTrue(secondaryToggle.checked);
 
             browserProxy.resetResolver('setDefaultValueForContentType');
             MockInteractions.tap(testElement.$.toggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check BLOCK => THIRD_STATE transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
-            assertEquals(thirdState, arguments[1]);
+            assertEquals(category, args[0]);
+            assertEquals(thirdState, args[1]);
             assertTrue(testElement.categoryEnabled);
-            assertFalse(askCheckbox.disabled);
-            assertTrue(askCheckbox.checked);
+            assertFalse(secondaryToggle.disabled);
+            assertTrue(secondaryToggle.checked);
 
             browserProxy.resetResolver('setDefaultValueForContentType');
-            MockInteractions.tap(askCheckbox);
+            MockInteractions.tap(secondaryToggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check THIRD_STATE => ALLOW transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
+            assertEquals(category, args[0]);
             assertEquals(
-                settings.PermissionValues.ALLOW, arguments[1]);
+                settings.PermissionValues.ALLOW, args[1]);
             assertTrue(testElement.categoryEnabled);
-            assertFalse(askCheckbox.disabled);
-            assertFalse(askCheckbox.checked);
+            assertFalse(secondaryToggle.disabled);
+            assertFalse(secondaryToggle.checked);
 
             browserProxy.resetResolver('setDefaultValueForContentType');
             MockInteractions.tap(testElement.$.toggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check ALLOW => BLOCK transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
-            assertEquals(settings.PermissionValues.BLOCK, arguments[1]);
+            assertEquals(category, args[0]);
+            assertEquals(settings.PermissionValues.BLOCK, args[1]);
             assertFalse(testElement.categoryEnabled);
-            assertTrue(askCheckbox.disabled);
-            assertFalse(askCheckbox.checked);
+            assertTrue(secondaryToggle.disabled);
+            assertFalse(secondaryToggle.checked);
 
             browserProxy.resetResolver('setDefaultValueForContentType');
             MockInteractions.tap(testElement.$.toggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check BLOCK => ALLOW transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
-            assertEquals(settings.PermissionValues.ALLOW, arguments[1]);
+            assertEquals(category, args[0]);
+            assertEquals(settings.PermissionValues.ALLOW, args[1]);
             assertTrue(testElement.categoryEnabled);
-            assertFalse(askCheckbox.disabled);
-            assertFalse(askCheckbox.checked);
+            assertFalse(secondaryToggle.disabled);
+            assertFalse(secondaryToggle.checked);
 
             browserProxy.resetResolver('setDefaultValueForContentType');
-            MockInteractions.tap(askCheckbox);
+            MockInteractions.tap(secondaryToggle);
             return browserProxy.whenCalled('setDefaultValueForContentType');
-          }).then(function(arguments) {
+          }).then(function(args) {
             // Check ALLOW => THIRD_STATE transition succeeded.
             Polymer.dom.flush();
 
-            assertEquals(category, arguments[0]);
-            assertEquals(thirdState, arguments[1]);
+            assertEquals(category, args[0]);
+            assertEquals(thirdState, args[1]);
             assertTrue(testElement.categoryEnabled);
-            assertFalse(askCheckbox.disabled);
-            assertTrue(askCheckbox.checked);
+            assertFalse(secondaryToggle.disabled);
+            assertTrue(secondaryToggle.checked);
           });
       }
 
       test('test special tri-state Flash category', function() {
         return testTristateCategory(
             prefsFlashDetect, settings.ContentSettingsTypes.PLUGINS,
-            settings.PermissionValues.IMPORTANT_CONTENT, '#flashAskCheckbox');
+            settings.PermissionValues.IMPORTANT_CONTENT, '#flashAskToggle');
       });
 
       test('test special tri-state Cookies category', function() {
         return testTristateCategory(
             prefsCookesSessionOnly, settings.ContentSettingsTypes.COOKIES,
-            settings.PermissionValues.SESSION_ONLY, '#sessionOnlyCheckbox');
+            settings.PermissionValues.SESSION_ONLY, '#sessionOnlyToggle');
       });
     });
   }
diff --git a/chrome/test/data/webui/settings/zoom_levels_tests.js b/chrome/test/data/webui/settings/zoom_levels_tests.js
index 35d819eba..a688b66 100644
--- a/chrome/test/data/webui/settings/zoom_levels_tests.js
+++ b/chrome/test/data/webui/settings/zoom_levels_tests.js
@@ -96,8 +96,8 @@
           assert(!!removeButton);
           MockInteractions.tap(removeButton);
           return browserProxy.whenCalled('removeZoomLevel');
-        }).then(function(arguments) {
-          assertEquals("http://www.google.com", arguments[0]);
+        }).then(function(args) {
+          assertEquals("http://www.google.com", args[0]);
         });
       });
     });
diff --git a/chrome/test/media_router/resources/common.js b/chrome/test/media_router/resources/common.js
index 35029eaf..6740342 100644
--- a/chrome/test/media_router/resources/common.js
+++ b/chrome/test/media_router/resources/common.js
@@ -14,7 +14,7 @@
 if (window.location.href.indexOf('__is_android__=true') >= 0) {
   // For android, "google.com/cast" is required in presentation URL.
   // TODO(zqzhang): this requirement may be removed in the future.
-  presentationUrl = "http://google.com/cast#__castAppId__=CCCCCCCC/";
+  presentationUrl = "https://google.com/cast#__castAppId__=CCCCCCCC/";
 } else {
   presentationUrl = "http://www.google.com/#__testprovider__=true";
 }
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 3703eb9..961ae91 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -210,7 +210,7 @@
       # URLRequestTestHTTP.GetTest_ManyCookies takes roughly 55s to run. Increase
       # timeout to 90s from 45s to allow it to pass (b/19821476)
       # ProxyScriptFetcherImplTest.HttpMimeType is flaking (b/19848784)
-      # Running a batch of net_unittests has high overhead. Run tests in batches of 25 to reduce number of batches (b/23156294).
+      # Running a batch of net_unittests has high overhead. Run tests in batches of 50 to reduce number of batches (b/23156294).
       gtest_excludes = [
         "KeygenHandlerTest.SmokeTest",
         "KeygenHandlerTest.ConcurrencyTest",
@@ -218,7 +218,7 @@
       ]
       args = [
         "--test-launcher-timeout=90000",
-        "--test-launcher-batch-limit=25",
+        "--test-launcher-batch-limit=50",
       ]
     } else if (is_cast_desktop_build || target_os == "android") {
       # URLRequestTestHTTP.GetTest_ManyCookies takes roughly 55s to run. Increase
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 5b801d3..9de1c59 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -48,7 +48,7 @@
     "//net/android:net_java",
 
     # TODO(slan): We may need to pass this in as a parameter.
-    "//third_party/android_tools:android_support_v4_java",
+    "//third_party/android_tools:android_support_core_utils_java",
     "//ui/android:ui_java",
   ]
 }
diff --git a/chromecast/common/cast_content_client.h b/chromecast/common/cast_content_client.h
index e672036..2e0dd269 100644
--- a/chromecast/common/cast_content_client.h
+++ b/chromecast/common/cast_content_client.h
@@ -29,7 +29,7 @@
       int resource_id) const override;
   gfx::Image& GetNativeImageNamed(int resource_id) const override;
 #if defined(OS_ANDROID)
-  media::MediaClientAndroid* GetMediaClientAndroid() override;
+  ::media::MediaClientAndroid* GetMediaClientAndroid() override;
 #endif  // OS_ANDROID
 };
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index fd70b5e..2bbc8a6 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9052.0.0
\ No newline at end of file
+9056.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index a022c32..37cec4e 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -217,6 +217,9 @@
 // Enables ARC OptIn flow in OOBE.
 const char kEnableArcOOBEOptIn[] = "enable-arc-oobe-optin";
 
+// Enables consume kiosk mode.
+const char kEnableConsumerKiosk[] = "enable-consumer-kiosk";
+
 // Enables Data Saver prompt on cellular networks.
 const char kEnableDataSaverPrompt[] = "enable-datasaver-prompt";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index cd64265..66c8278 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -77,6 +77,7 @@
 CHROMEOS_EXPORT extern const char kEnableAd[];
 CHROMEOS_EXPORT extern const char kEnableArc[];
 CHROMEOS_EXPORT extern const char kEnableArcOOBEOptIn[];
+CHROMEOS_EXPORT extern const char kEnableConsumerKiosk[];
 CHROMEOS_EXPORT extern const char kEnableDataSaverPrompt[];
 CHROMEOS_EXPORT extern const char kEnableExperimentalAccessibilityFeatures[];
 CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
diff --git a/chromeos/dbus/auth_policy_client.cc b/chromeos/dbus/auth_policy_client.cc
index 076dd58f..98bf6a6 100644
--- a/chromeos/dbus/auth_policy_client.cc
+++ b/chromeos/dbus/auth_policy_client.cc
@@ -81,16 +81,16 @@
     if (!response) {
       LOG(ERROR) << "Join: Couldn't call to authpolicy";
       // TODO(rsorokin): make proper call, after defining possible errors codes.
-      callback.Run(authpolicy::AD_JOIN_ERROR_UNKNOWN);
+      callback.Run(authpolicy::types::AD_JOIN_ERROR_UNKNOWN);
       return;
     }
 
     dbus::MessageReader reader(response);
-    int res = authpolicy::AD_JOIN_ERROR_UNKNOWN;
+    int res = authpolicy::types::AD_JOIN_ERROR_UNKNOWN;
     if (!reader.PopInt32(&res)) {
       LOG(ERROR) << "Join: Couldn't get an error from the response";
       // TODO(rsorokin): make proper call, after defining possible errors codes.
-      callback.Run(authpolicy::AD_JOIN_ERROR_DBUS_FAIL);
+      callback.Run(authpolicy::types::AD_JOIN_ERROR_DBUS_FAIL);
       return;
     }
 
diff --git a/chromeos/dbus/auth_policy_client.h b/chromeos/dbus/auth_policy_client.h
index 740d6af..1947b4a5 100644
--- a/chromeos/dbus/auth_policy_client.h
+++ b/chromeos/dbus/auth_policy_client.h
@@ -14,12 +14,14 @@
 // TODO(rsorokin): Switch to service constants when it's landed.
 // (see crbug.com/659732)
 namespace authpolicy {
+namespace types {
 enum ADJoinErrorType {
   AD_JOIN_ERROR_NONE = 0,
   AD_JOIN_ERROR_UNKNOWN = 1,
   AD_JOIN_ERROR_DBUS_FAIL = 2,
 };
-}
+}  // namespace types
+}  // namespace authpolicy
 
 namespace chromeos {
 
diff --git a/chromeos/dbus/fake_auth_policy_client.cc b/chromeos/dbus/fake_auth_policy_client.cc
index 19647ea..aaaa9b72 100644
--- a/chromeos/dbus/fake_auth_policy_client.cc
+++ b/chromeos/dbus/fake_auth_policy_client.cc
@@ -64,7 +64,7 @@
                                         const std::string& user,
                                         int password_fd,
                                         const JoinCallback& callback) {
-  callback.Run(authpolicy::AD_JOIN_ERROR_NONE);
+  callback.Run(authpolicy::types::AD_JOIN_ERROR_NONE);
 }
 
 void FakeAuthPolicyClient::RefreshDevicePolicy(
diff --git a/chromeos/dbus/fake_shill_service_client.cc b/chromeos/dbus/fake_shill_service_client.cc
index 33a8169..cc995650f 100644
--- a/chromeos/dbus/fake_shill_service_client.cc
+++ b/chromeos/dbus/fake_shill_service_client.cc
@@ -420,7 +420,7 @@
     provider->SetWithoutPathExpansion(key, value.DeepCopy());
     new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider);
     changed_property = shill::kProviderProperty;
-  } else if (value.GetType() == base::Value::TYPE_DICTIONARY) {
+  } else if (value.GetType() == base::Value::Type::DICTIONARY) {
     const base::DictionaryValue* new_dict = NULL;
     value.GetAsDictionary(&new_dict);
     CHECK(new_dict);
diff --git a/chromeos/dbus/shill_client_helper.cc b/chromeos/dbus/shill_client_helper.cc
index 647c473..46ba9b5 100644
--- a/chromeos/dbus/shill_client_helper.cc
+++ b/chromeos/dbus/shill_client_helper.cc
@@ -442,7 +442,7 @@
                                       DictionaryType dictionary_type) {
   // Support basic types and string-to-string dictionary.
   switch (value.GetType()) {
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dictionary = NULL;
       value.GetAsDictionary(&dictionary);
       if (dictionary_type == DICTIONARY_TYPE_STRING) {
@@ -456,7 +456,7 @@
       }
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list = NULL;
       value.GetAsList(&list);
       dbus::MessageWriter variant_writer(NULL);
@@ -475,10 +475,10 @@
       writer->CloseContainer(&variant_writer);
       break;
     }
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_INTEGER:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::INTEGER:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::STRING:
       dbus::AppendBasicTypeValueDataAsVariant(writer, value);
       break;
     default:
diff --git a/chromeos/dbus/shill_ipconfig_client.cc b/chromeos/dbus/shill_ipconfig_client.cc
index 778e306..84c1e1a 100644
--- a/chromeos/dbus/shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill_ipconfig_client.cc
@@ -116,7 +116,7 @@
   writer.AppendString(name);
   // IPConfig supports writing basic type and string array properties.
   switch (value.GetType()) {
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list_value = NULL;
       value.GetAsList(&list_value);
       dbus::MessageWriter variant_writer(NULL);
@@ -126,7 +126,7 @@
       for (base::ListValue::const_iterator it = list_value->begin();
            it != list_value->end();
            ++it) {
-        DLOG_IF(ERROR, (*it)->GetType() != base::Value::TYPE_STRING)
+        DLOG_IF(ERROR, (*it)->GetType() != base::Value::Type::STRING)
             << "Unexpected type " << (*it)->GetType();
         std::string str;
         (*it)->GetAsString(&str);
@@ -135,10 +135,10 @@
       variant_writer.CloseContainer(&array_writer);
       writer.CloseContainer(&variant_writer);
     }
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_INTEGER:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::INTEGER:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::STRING:
       dbus::AppendBasicTypeValueDataAsVariant(&writer, value);
       break;
     default:
diff --git a/chromeos/geolocation/simple_geolocation_request.cc b/chromeos/geolocation/simple_geolocation_request.cc
index cb10cd6..1369458 100644
--- a/chromeos/geolocation/simple_geolocation_request.cc
+++ b/chromeos/geolocation/simple_geolocation_request.cc
@@ -199,7 +199,8 @@
     PrintGeolocationError(
         server_url,
         "Unexpected response type : " +
-            base::StringPrintf("%u", response_value->GetType()),
+            base::StringPrintf(
+                "%u", static_cast<unsigned int>(response_value->GetType())),
         position);
     RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
     return false;
diff --git a/chromeos/network/network_sms_handler.cc b/chromeos/network/network_sms_handler.cc
index a12a75f..f3eb86e 100644
--- a/chromeos/network/network_sms_handler.cc
+++ b/chromeos/network/network_sms_handler.cc
@@ -410,7 +410,7 @@
   }
   const base::Value* value;
   if (!properties.GetWithoutPathExpansion(shill::kDevicesProperty, &value) ||
-      value->GetType() != base::Value::TYPE_LIST) {
+      value->GetType() != base::Value::Type::LIST) {
     LOG(ERROR) << "NetworkSmsHandler: No list value for: "
                << shill::kDevicesProperty;
     return;
diff --git a/chromeos/network/network_state_unittest.cc b/chromeos/network/network_state_unittest.cc
index bea0c85..6cd28ae 100644
--- a/chromeos/network/network_state_unittest.cc
+++ b/chromeos/network/network_state_unittest.cc
@@ -25,9 +25,7 @@
 class TestStringValue : public base::Value {
  public:
   explicit TestStringValue(const std::string& in_value)
-      : base::Value(TYPE_STRING),
-        value_(in_value) {
-  }
+      : base::Value(Type::STRING), value_(in_value) {}
 
   ~TestStringValue() override {}
 
diff --git a/chromeos/network/onc/onc_mapper.cc b/chromeos/network/onc/onc_mapper.cc
index 427e223..22f5766 100644
--- a/chromeos/network/onc/onc_mapper.cc
+++ b/chromeos/network/onc/onc_mapper.cc
@@ -26,13 +26,13 @@
     bool* error) {
   std::unique_ptr<base::Value> result_value;
   switch (onc_value.GetType()) {
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dict = NULL;
       onc_value.GetAsDictionary(&dict);
       result_value = MapObject(signature, *dict, error);
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list = NULL;
       onc_value.GetAsList(&list);
       result_value = MapArray(signature, *list, error);
diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/network/onc/onc_merger.cc
index f71ded3..df8fd5bd 100644
--- a/chromeos/network/onc/onc_merger.cc
+++ b/chromeos/network/onc/onc_merger.cc
@@ -114,7 +114,7 @@
           continue;
 
         std::unique_ptr<base::Value> merged_value;
-        if (field.value().IsType(base::Value::TYPE_DICTIONARY)) {
+        if (field.value().IsType(base::Value::Type::DICTIONARY)) {
           DictPtrs nested_dicts;
           for (DictPtrs::const_iterator it_inner = dicts.begin();
                it_inner != dicts.end(); ++it_inner) {
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc
index 0587c11..b4a0cfc 100644
--- a/chromeos/network/onc/onc_signature.cc
+++ b/chromeos/network/onc/onc_signature.cc
@@ -13,25 +13,19 @@
 namespace onc {
 namespace {
 
-const OncValueSignature kBoolSignature = {base::Value::TYPE_BOOLEAN, NULL};
-const OncValueSignature kStringSignature = {base::Value::TYPE_STRING, NULL};
-const OncValueSignature kIntegerSignature = {base::Value::TYPE_INTEGER, NULL};
-const OncValueSignature kStringListSignature = {base::Value::TYPE_LIST,
-                                                NULL,
+const OncValueSignature kBoolSignature = {base::Value::Type::BOOLEAN, NULL};
+const OncValueSignature kStringSignature = {base::Value::Type::STRING, NULL};
+const OncValueSignature kIntegerSignature = {base::Value::Type::INTEGER, NULL};
+const OncValueSignature kStringListSignature = {base::Value::Type::LIST, NULL,
                                                 &kStringSignature};
-const OncValueSignature kIntegerListSignature = {base::Value::TYPE_LIST,
-                                                 NULL,
+const OncValueSignature kIntegerListSignature = {base::Value::Type::LIST, NULL,
                                                  &kIntegerSignature};
-const OncValueSignature kIPConfigListSignature = {base::Value::TYPE_LIST,
-                                                  NULL,
+const OncValueSignature kIPConfigListSignature = {base::Value::Type::LIST, NULL,
                                                   &kIPConfigSignature};
-const OncValueSignature kCellularApnListSignature = {base::Value::TYPE_LIST,
-                                                     NULL,
-                                                     &kCellularApnSignature};
+const OncValueSignature kCellularApnListSignature = {
+    base::Value::Type::LIST, NULL, &kCellularApnSignature};
 const OncValueSignature kCellularFoundNetworkListSignature = {
-    base::Value::TYPE_LIST,
-    NULL,
-    &kCellularFoundNetworkSignature};
+    base::Value::Type::LIST, NULL, &kCellularFoundNetworkSignature};
 
 const OncFieldSignature issuer_subject_pattern_fields[] = {
     {::onc::client_cert::kCommonName, &kStringSignature},
@@ -374,133 +368,84 @@
 
 }  // namespace
 
-const OncValueSignature kRecommendedSignature = {base::Value::TYPE_LIST,
-                                                 NULL,
+const OncValueSignature kRecommendedSignature = {base::Value::Type::LIST, NULL,
                                                  &kStringSignature};
-const OncValueSignature kEAPSignature = {base::Value::TYPE_DICTIONARY,
-                                         eap_fields,
-                                         NULL};
+const OncValueSignature kEAPSignature = {base::Value::Type::DICTIONARY,
+                                         eap_fields, NULL};
 const OncValueSignature kIssuerSubjectPatternSignature = {
-    base::Value::TYPE_DICTIONARY,
-    issuer_subject_pattern_fields,
-    NULL};
+    base::Value::Type::DICTIONARY, issuer_subject_pattern_fields, NULL};
 const OncValueSignature kCertificatePatternSignature = {
-    base::Value::TYPE_DICTIONARY,
-    certificate_pattern_fields,
-    NULL};
-const OncValueSignature kIPsecSignature = {base::Value::TYPE_DICTIONARY,
-                                           ipsec_fields,
-                                           NULL};
-const OncValueSignature kXAUTHSignature = {base::Value::TYPE_DICTIONARY,
-                                           xauth_fields,
-                                           NULL};
-const OncValueSignature kL2TPSignature = {base::Value::TYPE_DICTIONARY,
-                                          l2tp_fields,
-                                          NULL};
-const OncValueSignature kOpenVPNSignature = {base::Value::TYPE_DICTIONARY,
-                                             openvpn_fields,
-                                             NULL};
-const OncValueSignature kThirdPartyVPNSignature = {base::Value::TYPE_DICTIONARY,
-                                                   third_party_vpn_fields,
-                                                   NULL};
-const OncValueSignature kVerifyX509Signature = {base::Value::TYPE_DICTIONARY,
-                                                verify_x509_fields,
-                                                NULL};
-const OncValueSignature kVPNSignature = {base::Value::TYPE_DICTIONARY,
-                                         vpn_fields,
-                                         NULL};
-const OncValueSignature kEthernetSignature = {base::Value::TYPE_DICTIONARY,
-                                              ethernet_fields,
-                                              NULL};
-const OncValueSignature kIPConfigSignature = {base::Value::TYPE_DICTIONARY,
-                                              ipconfig_fields,
-                                              NULL};
-const OncValueSignature kSavedIPConfigSignature = {base::Value::TYPE_DICTIONARY,
-                                                   ipconfig_fields,
-                                                   NULL};
+    base::Value::Type::DICTIONARY, certificate_pattern_fields, NULL};
+const OncValueSignature kIPsecSignature = {base::Value::Type::DICTIONARY,
+                                           ipsec_fields, NULL};
+const OncValueSignature kXAUTHSignature = {base::Value::Type::DICTIONARY,
+                                           xauth_fields, NULL};
+const OncValueSignature kL2TPSignature = {base::Value::Type::DICTIONARY,
+                                          l2tp_fields, NULL};
+const OncValueSignature kOpenVPNSignature = {base::Value::Type::DICTIONARY,
+                                             openvpn_fields, NULL};
+const OncValueSignature kThirdPartyVPNSignature = {
+    base::Value::Type::DICTIONARY, third_party_vpn_fields, NULL};
+const OncValueSignature kVerifyX509Signature = {base::Value::Type::DICTIONARY,
+                                                verify_x509_fields, NULL};
+const OncValueSignature kVPNSignature = {base::Value::Type::DICTIONARY,
+                                         vpn_fields, NULL};
+const OncValueSignature kEthernetSignature = {base::Value::Type::DICTIONARY,
+                                              ethernet_fields, NULL};
+const OncValueSignature kIPConfigSignature = {base::Value::Type::DICTIONARY,
+                                              ipconfig_fields, NULL};
+const OncValueSignature kSavedIPConfigSignature = {
+    base::Value::Type::DICTIONARY, ipconfig_fields, NULL};
 const OncValueSignature kStaticIPConfigSignature = {
-    base::Value::TYPE_DICTIONARY,
-    ipconfig_fields,
-    NULL};
-const OncValueSignature kProxyLocationSignature = {base::Value::TYPE_DICTIONARY,
-                                                   proxy_location_fields,
-                                                   NULL};
-const OncValueSignature kProxyManualSignature = {base::Value::TYPE_DICTIONARY,
-                                                 proxy_manual_fields,
-                                                 NULL};
-const OncValueSignature kProxySettingsSignature = {base::Value::TYPE_DICTIONARY,
-                                                   proxy_settings_fields,
-                                                   NULL};
-const OncValueSignature kWiFiSignature = {base::Value::TYPE_DICTIONARY,
-                                          wifi_fields,
-                                          NULL};
-const OncValueSignature kWiMAXSignature = {base::Value::TYPE_DICTIONARY,
-                                           wimax_fields,
-                                           NULL};
-const OncValueSignature kCertificateSignature = {base::Value::TYPE_DICTIONARY,
-                                                 certificate_fields,
-                                                 NULL};
+    base::Value::Type::DICTIONARY, ipconfig_fields, NULL};
+const OncValueSignature kProxyLocationSignature = {
+    base::Value::Type::DICTIONARY, proxy_location_fields, NULL};
+const OncValueSignature kProxyManualSignature = {base::Value::Type::DICTIONARY,
+                                                 proxy_manual_fields, NULL};
+const OncValueSignature kProxySettingsSignature = {
+    base::Value::Type::DICTIONARY, proxy_settings_fields, NULL};
+const OncValueSignature kWiFiSignature = {base::Value::Type::DICTIONARY,
+                                          wifi_fields, NULL};
+const OncValueSignature kWiMAXSignature = {base::Value::Type::DICTIONARY,
+                                           wimax_fields, NULL};
+const OncValueSignature kCertificateSignature = {base::Value::Type::DICTIONARY,
+                                                 certificate_fields, NULL};
 const OncValueSignature kNetworkConfigurationSignature = {
-    base::Value::TYPE_DICTIONARY,
-    network_configuration_fields,
-    NULL};
+    base::Value::Type::DICTIONARY, network_configuration_fields, NULL};
 const OncValueSignature kGlobalNetworkConfigurationSignature = {
-    base::Value::TYPE_DICTIONARY,
-    global_network_configuration_fields,
-    NULL};
-const OncValueSignature kCertificateListSignature = {base::Value::TYPE_LIST,
-                                                     NULL,
-                                                     &kCertificateSignature};
+    base::Value::Type::DICTIONARY, global_network_configuration_fields, NULL};
+const OncValueSignature kCertificateListSignature = {
+    base::Value::Type::LIST, NULL, &kCertificateSignature};
 const OncValueSignature kNetworkConfigurationListSignature = {
-    base::Value::TYPE_LIST,
-    NULL,
-    &kNetworkConfigurationSignature};
+    base::Value::Type::LIST, NULL, &kNetworkConfigurationSignature};
 const OncValueSignature kToplevelConfigurationSignature = {
-    base::Value::TYPE_DICTIONARY,
-    toplevel_configuration_fields,
-    NULL};
+    base::Value::Type::DICTIONARY, toplevel_configuration_fields, NULL};
 
 // Derived "ONC with State" signatures.
 const OncValueSignature kNetworkWithStateSignature = {
-    base::Value::TYPE_DICTIONARY,
-    network_with_state_fields,
-    NULL,
+    base::Value::Type::DICTIONARY, network_with_state_fields, NULL,
     &kNetworkConfigurationSignature};
-const OncValueSignature kWiFiWithStateSignature = {base::Value::TYPE_DICTIONARY,
-                                                   wifi_with_state_fields,
-                                                   NULL,
-                                                   &kWiFiSignature};
+const OncValueSignature kWiFiWithStateSignature = {
+    base::Value::Type::DICTIONARY, wifi_with_state_fields, NULL,
+    &kWiFiSignature};
 const OncValueSignature kWiMAXWithStateSignature = {
-    base::Value::TYPE_DICTIONARY,
-    wimax_with_state_fields,
-    NULL,
+    base::Value::Type::DICTIONARY, wimax_with_state_fields, NULL,
     &kWiMAXSignature};
-const OncValueSignature kCellularSignature = {base::Value::TYPE_DICTIONARY,
-                                              cellular_fields,
-                                              NULL};
+const OncValueSignature kCellularSignature = {base::Value::Type::DICTIONARY,
+                                              cellular_fields, NULL};
 const OncValueSignature kCellularWithStateSignature = {
-    base::Value::TYPE_DICTIONARY,
-    cellular_with_state_fields,
-    NULL,
+    base::Value::Type::DICTIONARY, cellular_with_state_fields, NULL,
     &kCellularSignature};
 const OncValueSignature kCellularPaymentPortalSignature = {
-    base::Value::TYPE_DICTIONARY,
-    cellular_payment_portal_fields,
-    NULL};
+    base::Value::Type::DICTIONARY, cellular_payment_portal_fields, NULL};
 const OncValueSignature kCellularProviderSignature = {
-    base::Value::TYPE_DICTIONARY,
-    cellular_provider_fields,
-    NULL};
-const OncValueSignature kCellularApnSignature = {base::Value::TYPE_DICTIONARY,
-                                                 cellular_apn_fields,
-                                                 NULL};
+    base::Value::Type::DICTIONARY, cellular_provider_fields, NULL};
+const OncValueSignature kCellularApnSignature = {base::Value::Type::DICTIONARY,
+                                                 cellular_apn_fields, NULL};
 const OncValueSignature kCellularFoundNetworkSignature = {
-    base::Value::TYPE_DICTIONARY,
-    cellular_found_network_fields,
-    NULL};
-const OncValueSignature kSIMLockStatusSignature = {base::Value::TYPE_DICTIONARY,
-                                                   sim_lock_status_fields,
-                                                   NULL};
+    base::Value::Type::DICTIONARY, cellular_found_network_fields, NULL};
+const OncValueSignature kSIMLockStatusSignature = {
+    base::Value::Type::DICTIONARY, sim_lock_status_fields, NULL};
 
 const OncFieldSignature* GetFieldSignature(const OncValueSignature& signature,
                                            const std::string& onc_field_name) {
diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc
index 2e62462a..72ee0d8b9 100644
--- a/chromeos/network/onc/onc_translator_shill_to_onc.cc
+++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc
@@ -32,7 +32,7 @@
 std::unique_ptr<base::Value> ConvertStringToValue(const std::string& str,
                                                   base::Value::Type type) {
   std::unique_ptr<base::Value> value;
-  if (type == base::Value::TYPE_STRING) {
+  if (type == base::Value::Type::STRING) {
     value.reset(new base::StringValue(str));
   } else {
     value = base::JSONReader::Read(str);
@@ -672,10 +672,10 @@
     const base::ListValue& list) {
   const OncFieldSignature* field_signature =
       GetFieldSignature(*onc_signature_, onc_field_name);
-  if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
+  if (field_signature->value_signature->onc_type != base::Value::Type::LIST) {
     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
                << field_signature->value_signature->onc_type
-               << "', expected: base::Value::TYPE_LIST: " << GetName();
+               << "', expected: base::Value::Type::LIST: " << GetName();
     return;
   }
   DCHECK(field_signature->value_signature->onc_array_entry_signature);
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index dec7cfb..db6efd0 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -258,7 +258,7 @@
       found_error = true;
       error_cause = "unknown";
     } else if (field_signature->value_signature->onc_type ==
-               base::Value::TYPE_DICTIONARY) {
+               base::Value::Type::DICTIONARY) {
       found_error = true;
       error_cause = "dictionary-typed";
     }
diff --git a/chromeos/printing/ppd_cache.cc b/chromeos/printing/ppd_cache.cc
index 2726b8ac..2674d50f 100644
--- a/chromeos/printing/ppd_cache.cc
+++ b/chromeos/printing/ppd_cache.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -49,6 +50,7 @@
   // Public API functions.
   base::Optional<base::FilePath> Find(
       const Printer::PpdReference& reference) const override {
+    base::AutoLock l(lock_);
     base::ThreadRestrictions::AssertIOAllowed();
     base::Optional<base::FilePath> ret;
 
@@ -69,6 +71,7 @@
       const Printer::PpdReference& reference,
       const std::string& ppd_contents) override {
     base::ThreadRestrictions::AssertIOAllowed();
+    base::AutoLock l(lock_);
     if (!EnsureCacheDirectoryExists()) {
       return base::nullopt;
     }
@@ -96,23 +99,25 @@
     return ret;
   }
 
-  const PpdProvider::AvailablePrintersMap* FindAvailablePrinters() override {
+  base::Optional<PpdProvider::AvailablePrintersMap> FindAvailablePrinters()
+      override {
     base::ThreadRestrictions::AssertIOAllowed();
+    base::AutoLock l(lock_);
     if (available_printers_ != nullptr &&
         base::Time::Now() - available_printers_timestamp_ <
             options_.max_available_list_staleness) {
       // Satisfy from memory cache.
-      return available_printers_.get();
+      return *available_printers_;
     }
     std::string buf;
     if (!MaybeReadAvailablePrintersCache(&buf)) {
       // Disk cache miss.
-      return nullptr;
+      return base::nullopt;
     }
     auto dict = base::DictionaryValue::From(base::JSONReader::Read(buf));
     if (dict == nullptr) {
       LOG(ERROR) << "Failed to deserialize available printers cache";
-      return nullptr;
+      return base::nullopt;
     }
     // Note if we got here, we've already set available_printers_timestamp_ to
     // the mtime of the file we read from.
@@ -136,7 +141,7 @@
         }
       }
     }
-    return available_printers_.get();
+    return *available_printers_;
   }
 
   // Note we throw up our hands and fail (gracefully) to store if we encounter
@@ -146,6 +151,7 @@
   void StoreAvailablePrinters(std::unique_ptr<PpdProvider::AvailablePrintersMap>
                                   available_printers) override {
     base::ThreadRestrictions::AssertIOAllowed();
+    base::AutoLock l(lock_);
     if (!EnsureCacheDirectoryExists()) {
       return;
     }
@@ -286,6 +292,8 @@
   const base::FilePath available_printers_file_;
   const PpdCache::Options options_;
 
+  mutable base::Lock lock_;
+
   DISALLOW_COPY_AND_ASSIGN(PpdCacheImpl);
 };
 
diff --git a/chromeos/printing/ppd_cache.h b/chromeos/printing/ppd_cache.h
index 809d3c9..c131c17 100644
--- a/chromeos/printing/ppd_cache.h
+++ b/chromeos/printing/ppd_cache.h
@@ -25,6 +25,8 @@
 // the cache).  However, changing *any* field in PpdReference will make the
 // previous cache entry invalid.  This is the intentional behavior -- we want to
 // re-run the resolution logic if we have new meta-information about a printer.
+//
+// All cache functions must be called on a thread which is permitted to do I/O.
 class CHROMEOS_EXPORT PpdCache {
  public:
   // Options that can be tweaked.  These should all have sane defaults.  This
@@ -75,12 +77,11 @@
       const std::string& ppd_contents) = 0;
 
   // Return a map of available printers, if we have one available and it's
-  // not too stale.  Returns null if no map is available.
-  //
-  // If a map is returned, ownership is retained by the cache.  The map is
-  // guaranteed to remain valid and unchanged until the next
-  // {Find|Store}AvailablePrinters call.
-  virtual const PpdProvider::AvailablePrintersMap* FindAvailablePrinters() = 0;
+  // not too stale.  Returns nullopt if no map is available.  Note that, if the
+  // number of printers gets extremely large this map copy could get expensive
+  // and need to be reworked.
+  virtual base::Optional<PpdProvider::AvailablePrintersMap>
+  FindAvailablePrinters() = 0;
 
   // Store |available_printers|, replacing any existing entry.
   virtual void StoreAvailablePrinters(
diff --git a/chromeos/printing/ppd_cache_unittest.cc b/chromeos/printing/ppd_cache_unittest.cc
index 51f2eec..2587cd1 100644
--- a/chromeos/printing/ppd_cache_unittest.cc
+++ b/chromeos/printing/ppd_cache_unittest.cc
@@ -157,9 +157,8 @@
   auto cache = CreateTestCache();
 
   // Nothing stored, so should miss in the cache.
-  const PpdProvider::AvailablePrintersMap* result =
-      cache->FindAvailablePrinters();
-  EXPECT_EQ(nullptr, result);
+  auto result = cache->FindAvailablePrinters();
+  EXPECT_FALSE(result);
 
   // Create something to store.
   auto a = base::MakeUnique<PpdProvider::AvailablePrintersMap>();
@@ -171,9 +170,9 @@
   // Store it, get it back.
   cache->StoreAvailablePrinters(std::move(a));
   result = cache->FindAvailablePrinters();
-  ASSERT_NE(nullptr, result);
+  ASSERT_TRUE(result);
 
-  EXPECT_EQ(original, *result);
+  EXPECT_EQ(original, result.value());
 }
 
 // When an entry is too old, we shouldn't return it.
@@ -188,7 +187,7 @@
       base::MakeUnique<PpdProvider::AvailablePrintersMap>());
 
   // Should *miss* in the cache because the entry is already expired.
-  EXPECT_EQ(nullptr, cache->FindAvailablePrinters());
+  EXPECT_FALSE(cache->FindAvailablePrinters());
 }
 
 }  // namespace
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index 80e7b00f..1d85f0a 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -4,15 +4,17 @@
 
 #include "chromeos/printing/ppd_provider.h"
 
+#include <unordered_map>
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/files/file_util.h"
 #include "base/json/json_parser.h"
 #include "base/memory/ptr_util.h"
-#include "base/sequence_checker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
@@ -38,23 +40,49 @@
 
 class PpdProviderImpl;
 
-// URLFetcherDelegate that just invokes a callback when the fetch is complete.
-class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate {
+// PpdProvider handles two types of URL fetches, and so uses these delegates
+// to route OnURLFetchComplete to the appropriate handler.
+class ResolveURLFetcherDelegate : public net::URLFetcherDelegate {
  public:
-  explicit ForwardingURLFetcherDelegate(
-      const base::Callback<void()>& done_callback)
-      : done_callback_(done_callback) {}
-  ~ForwardingURLFetcherDelegate() override {}
+  explicit ResolveURLFetcherDelegate(PpdProviderImpl* parent)
+      : parent_(parent) {}
 
-  // URLFetcherDelegate API method.  Defined below since we need the
-  // PpdProviderImpl definition first.
-  void OnURLFetchComplete(const net::URLFetcher* source) override {
-    done_callback_.Run();
-  }
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
 
- private:
-  // Callback to be run on fetch complete.
-  base::Callback<void()> done_callback_;
+  // Link back to parent.  Not owned.
+  PpdProviderImpl* const parent_;
+};
+
+class QueryAvailableURLFetcherDelegate : public net::URLFetcherDelegate {
+ public:
+  explicit QueryAvailableURLFetcherDelegate(PpdProviderImpl* parent)
+      : parent_(parent) {}
+
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  // Link back to parent.  Not owned.
+  PpdProviderImpl* const parent_;
+};
+
+// Data involved in an active Resolve() URL fetch.
+struct ResolveFetchData {
+  // The fetcher doing the fetch.
+  std::unique_ptr<net::URLFetcher> fetcher;
+
+  // The reference being resolved.
+  Printer::PpdReference ppd_reference;
+
+  // Callback to invoke on completion.
+  PpdProvider::ResolveCallback done_callback;
+};
+
+// Data involved in an active QueryAvailable() URL fetch.
+struct QueryAvailableFetchData {
+  // The fetcher doing the fetch.
+  std::unique_ptr<net::URLFetcher> fetcher;
+
+  // Callback to invoke on completion.
+  PpdProvider::QueryAvailableCallback done_callback;
 };
 
 class PpdProviderImpl : public PpdProvider {
@@ -70,6 +98,8 @@
         io_task_runner_(io_task_runner),
         cache_(std::move(cache)),
         options_(options),
+        resolve_delegate_(this),
+        query_available_delegate_(this),
         weak_factory_(this) {
     CHECK_GT(options_.max_ppd_contents_size_, 0U);
   }
@@ -78,12 +108,8 @@
   void Resolve(const Printer::PpdReference& ppd_reference,
                const PpdProvider::ResolveCallback& cb) override {
     CHECK(!cb.is_null());
-    CHECK(resolve_sequence_checker_.CalledOnValidSequence());
     CHECK(base::SequencedTaskRunnerHandle::IsSet())
         << "Resolve must be called from a SequencedTaskRunner context";
-    CHECK(!resolve_inflight_)
-        << "Can't have concurrent PpdProvider Resolve calls";
-    resolve_inflight_ = true;
     auto cache_result = base::MakeUnique<base::Optional<base::FilePath>>();
     auto* raw_cache_result_ptr = cache_result.get();
     bool post_result = io_task_runner_->PostTaskAndReply(
@@ -92,7 +118,7 @@
                               raw_cache_result_ptr),
         base::Bind(&PpdProviderImpl::ResolveCacheLookupDone,
                    weak_factory_.GetWeakPtr(), ppd_reference, cb,
-                   std::move(cache_result)));
+                   base::Passed(&cache_result)));
     DCHECK(post_result);
   }
 
@@ -100,17 +126,16 @@
     CHECK(!cb.is_null());
     CHECK(base::SequencedTaskRunnerHandle::IsSet())
         << "QueryAvailable() must be called from a SequencedTaskRunner context";
-    auto cache_result = base::MakeUnique<
-        base::Optional<const PpdProvider::AvailablePrintersMap*>>();
-    CHECK(!query_inflight_)
-        << "Can't have concurrent PpdProvider QueryAvailable calls";
-    query_inflight_ = true;
+    auto cache_result =
+        base::MakeUnique<base::Optional<PpdProvider::AvailablePrintersMap>>();
     auto* raw_cache_result_ptr = cache_result.get();
-    CHECK(io_task_runner_->PostTaskAndReply(
+    bool post_result = io_task_runner_->PostTaskAndReply(
         FROM_HERE, base::Bind(&PpdProviderImpl::QueryAvailableDoCacheLookup,
                               weak_factory_.GetWeakPtr(), raw_cache_result_ptr),
         base::Bind(&PpdProviderImpl::QueryAvailableCacheLookupDone,
-                   weak_factory_.GetWeakPtr(), cb, std::move(cache_result))));
+                   weak_factory_.GetWeakPtr(), cb,
+                   base::Passed(&cache_result)));
+    DCHECK(post_result);
   }
 
   bool CachePpd(const Printer::PpdReference& ppd_reference,
@@ -123,129 +148,27 @@
     return static_cast<bool>(cache_->Store(ppd_reference, buf));
   }
 
- private:
-  // Trivial wrappers around PpdCache::Find() and
-  // PpdCache::FindAvailablePrinters().  We need these wrappers both because we
-  // use weak_ptrs to manage lifetime, and so we need to bind callbacks to
-  // *this*, and because weak_ptr's preclude return values in posted tasks, so
-  // we have to use a parameter to return state.
-  void ResolveDoCacheLookup(
-      const Printer::PpdReference& reference,
-      base::Optional<base::FilePath>* cache_result) const {
-    *cache_result = cache_->Find(reference);
-  }
-
-  void QueryAvailableDoCacheLookup(
-      base::Optional<const PpdProvider::AvailablePrintersMap*>* cache_result)
-      const {
-    DCHECK(cache_result);
-    auto tmp = cache_->FindAvailablePrinters();
-    if (tmp != nullptr) {
-      *cache_result = tmp;
-    } else {
-      *cache_result = base::nullopt;
-    }
-  }
-
-  // Callback that happens when the Resolve() cache lookup completes.  If the
-  // cache satisfied the request, finish the Resolve, otherwise start a URL
-  // request to satisfy the request.  This runs on the same thread as Resolve()
-  // was invoked on.
-  void ResolveCacheLookupDone(
-      const Printer::PpdReference& ppd_reference,
-      const PpdProvider::ResolveCallback& done_callback,
-      const std::unique_ptr<base::Optional<base::FilePath>>& cache_result) {
-    CHECK(resolve_sequence_checker_.CalledOnValidSequence());
-    if (*cache_result) {
-      // Cache hit.  Schedule the callback now and return.
-      resolve_inflight_ = false;
-      done_callback.Run(PpdProvider::SUCCESS, cache_result->value());
-      return;
-    }
-
-    // We don't have a way to automatically resolve user-supplied PPDs yet.  So
-    // if we have one specified, and it's not cached, we fail out rather than
-    // fall back to quirks-server based resolution.  The reasoning here is that
-    // if the user has specified a PPD when a quirks-server one exists, it
-    // probably means the quirks server one doesn't work for some reason, so we
-    // shouldn't silently use it.
-    if (!ppd_reference.user_supplied_ppd_url.empty()) {
-      resolve_inflight_ = false;
-      done_callback.Run(PpdProvider::NOT_FOUND, base::FilePath());
-      return;
-    }
-
-    // Missed in the cache, so start a URLRequest to resolve the request.
-    resolve_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>(
-        base::Bind(&PpdProviderImpl::OnResolveFetchComplete,
-                   weak_factory_.GetWeakPtr(), ppd_reference, done_callback));
-
-    resolve_fetcher_ = net::URLFetcher::Create(
-        GetQuirksServerPpdLookupURL(ppd_reference), net::URLFetcher::GET,
-        resolve_fetcher_delegate_.get());
-
-    resolve_fetcher_->SetRequestContext(url_context_getter_.get());
-    resolve_fetcher_->SetLoadFlags(
-        net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
-        net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
-        net::LOAD_DO_NOT_SEND_AUTH_DATA);
-    resolve_fetcher_->Start();
-  }
-
   // Called on the same thread as Resolve() when the fetcher completes its
   // fetch.
-  void OnResolveFetchComplete(
-      const Printer::PpdReference& ppd_reference,
-      const PpdProvider::ResolveCallback& done_callback) {
-    CHECK(resolve_sequence_checker_.CalledOnValidSequence());
-    // Scope the allocated |resolve_fetcher_| and |resolve_fetcher_delegate_|
-    // into this function so we clean it up when we're done here instead of
-    // leaving it around until the next Resolve() call.
-    auto fetcher_delegate(std::move(resolve_fetcher_delegate_));
-    auto fetcher(std::move(resolve_fetcher_));
-    resolve_inflight_ = false;
-    std::string contents;
-    if (!ValidateAndGetResponseAsString(*fetcher, &contents)) {
-      // Something went wrong with the fetch.
-      done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath());
-      return;
-    }
-
-    auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents));
-    if (dict == nullptr) {
-      done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath());
-      return;
-    }
+  void OnResolveFetchComplete(const net::URLFetcher* source) {
+    std::unique_ptr<ResolveFetchData> fetch_data = GetResolveFetchData(source);
     std::string ppd_contents;
-    std::string last_updated_time_string;
     uint64_t last_updated_time;
-    if (!(dict->GetString(kJSONPPDKey, &ppd_contents) &&
-          dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) &&
-          base::StringToUint64(last_updated_time_string, &last_updated_time))) {
-      // Malformed response.  TODO(justincarlson) - LOG something here?
-      done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath());
+    if (!ExtractResolveResponseData(source, fetch_data.get(), &ppd_contents,
+                                    &last_updated_time)) {
+      fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR,
+                                    base::FilePath());
       return;
     }
 
-    if (ppd_contents.size() > options_.max_ppd_contents_size_) {
-      // PPD is too big.
-      //
-      // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
-      // a user that's not from an explicitly trusted source, we should also
-      // check *uncompressed* size here to head off zip-bombs (e.g. let's
-      // compress 1GBs of zeros into a 900kb file and see what cups does when it
-      // tries to expand that...)
-      done_callback.Run(PpdProvider::SERVER_ERROR, base::FilePath());
-      return;
-    }
-
-    auto ppd_file = cache_->Store(ppd_reference, ppd_contents);
+    auto ppd_file = cache_->Store(fetch_data->ppd_reference, ppd_contents);
     if (!ppd_file) {
       // Failed to store.
-      done_callback.Run(PpdProvider::INTERNAL_ERROR, base::FilePath());
+      fetch_data->done_callback.Run(PpdProvider::INTERNAL_ERROR,
+                                    base::FilePath());
       return;
     }
-    done_callback.Run(PpdProvider::SUCCESS, ppd_file.value());
+    fetch_data->done_callback.Run(PpdProvider::SUCCESS, ppd_file.value());
   }
 
   // Called on the same thread as QueryAvailable() when the cache lookup is
@@ -254,44 +177,38 @@
   // QueryAvailable() was invoked on.
   void QueryAvailableCacheLookupDone(
       const PpdProvider::QueryAvailableCallback& done_callback,
-      const std::unique_ptr<
-          base::Optional<const PpdProvider::AvailablePrintersMap*>>&
+      std::unique_ptr<base::Optional<PpdProvider::AvailablePrintersMap>>
           cache_result) {
-    CHECK(query_sequence_checker_.CalledOnValidSequence());
     if (*cache_result) {
-      query_inflight_ = false;
-      done_callback.Run(PpdProvider::SUCCESS, *cache_result->value());
+      done_callback.Run(PpdProvider::SUCCESS, cache_result->value());
       return;
     }
-    // Missed in the cache, start a query.
-    query_fetcher_delegate_ = base::MakeUnique<ForwardingURLFetcherDelegate>(
-        base::Bind(&PpdProviderImpl::OnQueryAvailableFetchComplete,
-                   weak_factory_.GetWeakPtr(), done_callback));
 
-    query_fetcher_ = net::URLFetcher::Create(GetQuirksServerPpdListURL(),
-                                             net::URLFetcher::GET,
-                                             query_fetcher_delegate_.get());
-    query_fetcher_->SetRequestContext(url_context_getter_.get());
-    query_fetcher_->SetLoadFlags(
+    auto fetch_data = base::MakeUnique<QueryAvailableFetchData>();
+    fetch_data->done_callback = done_callback;
+
+    fetch_data->fetcher = net::URLFetcher::Create(GetQuirksServerPpdListURL(),
+                                                  net::URLFetcher::GET,
+                                                  &query_available_delegate_);
+    fetch_data->fetcher->SetRequestContext(url_context_getter_.get());
+    fetch_data->fetcher->SetLoadFlags(
         net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
         net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
         net::LOAD_DO_NOT_SEND_AUTH_DATA);
-    query_fetcher_->Start();
+    auto* fetcher = fetch_data->fetcher.get();
+    StoreQueryAvailableFetchData(std::move(fetch_data));
+    fetcher->Start();
   }
 
-  void OnQueryAvailableFetchComplete(
-      const PpdProvider::QueryAvailableCallback& done_callback) {
-    CHECK(query_sequence_checker_.CalledOnValidSequence());
-    // Scope the object fetcher and task_runner into this function so we clean
-    // it up when we're done here instead of leaving it around until the next
-    // QueryAvailable() call.
-    auto fetcher_delegate(std::move(query_fetcher_delegate_));
-    auto fetcher(std::move(query_fetcher_));
-    query_inflight_ = false;
+  void OnQueryAvailableFetchComplete(const net::URLFetcher* source) {
+    std::unique_ptr<QueryAvailableFetchData> fetch_data =
+        GetQueryAvailableFetchData(source);
+
     std::string contents;
-    if (!ValidateAndGetResponseAsString(*fetcher, &contents)) {
+    if (!ValidateAndGetResponseAsString(*source, &contents)) {
       // Something went wrong with the fetch.
-      done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap());
+      fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR,
+                                    AvailablePrintersMap());
       return;
     }
 
@@ -302,7 +219,8 @@
     const base::ListValue* top_list;
     if (top_dict == nullptr || !top_dict->GetList(kJSONTopListKey, &top_list)) {
       LOG(ERROR) << "Malformed response from quirks server";
-      done_callback.Run(PpdProvider::SERVER_ERROR, AvailablePrintersMap());
+      fetch_data->done_callback.Run(PpdProvider::SERVER_ERROR,
+                                    AvailablePrintersMap());
       return;
     }
 
@@ -330,7 +248,7 @@
         }
       }
     }
-    done_callback.Run(PpdProvider::SUCCESS, *result);
+    fetch_data->done_callback.Run(PpdProvider::SUCCESS, *result);
     if (!result->empty()) {
       cache_->StoreAvailablePrinters(std::move(result));
     } else {
@@ -342,6 +260,137 @@
     }
   }
 
+ private:
+  void StoreResolveFetchData(std::unique_ptr<ResolveFetchData> fetch_data) {
+    auto raw_ptr = fetch_data->fetcher.get();
+    base::AutoLock lock(resolve_fetches_lock_);
+    resolve_fetches_[raw_ptr] = std::move(fetch_data);
+  }
+
+  void StoreQueryAvailableFetchData(
+      std::unique_ptr<QueryAvailableFetchData> fetch_data) {
+    auto raw_ptr = fetch_data->fetcher.get();
+    base::AutoLock lock(query_available_fetches_lock_);
+    query_available_fetches_[raw_ptr] = std::move(fetch_data);
+  }
+
+  // Extract the result of a resolve url fetch into |ppd_contents| and
+  // |last_updated time|.  Returns true on success.
+  bool ExtractResolveResponseData(const net::URLFetcher* source,
+                                  const ResolveFetchData* fetch,
+                                  std::string* ppd_contents,
+                                  uint64_t* last_updated_time) {
+    std::string contents;
+    if (!ValidateAndGetResponseAsString(*source, &contents)) {
+      return false;
+    }
+
+    auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents));
+    if (dict == nullptr) {
+      return false;
+    }
+    std::string last_updated_time_string;
+    if (!dict->GetString(kJSONPPDKey, ppd_contents) ||
+        !dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) ||
+        !base::StringToUint64(last_updated_time_string, last_updated_time)) {
+      // Malformed response.  TODO(justincarlson) - LOG something here?
+      return false;
+    }
+
+    if (ppd_contents->size() > options_.max_ppd_contents_size_) {
+      // PPD is too big.
+      //
+      // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to
+      // a user that's not from an explicitly trusted source, we should also
+      // check *uncompressed* size here to head off zip-bombs (e.g. let's
+      // compress 1GBs of zeros into a 900kb file and see what cups does when it
+      // tries to expand that...)
+      ppd_contents->clear();
+      return false;
+    }
+    return true;
+  }
+
+  // Return the ResolveFetchData associated with |source|.
+  std::unique_ptr<ResolveFetchData> GetResolveFetchData(
+      const net::URLFetcher* source) {
+    base::AutoLock l(resolve_fetches_lock_);
+    auto it = resolve_fetches_.find(source);
+    CHECK(it != resolve_fetches_.end());
+    auto ret = std::move(it->second);
+    resolve_fetches_.erase(it);
+    return ret;
+  }
+
+  // Return the QueryAvailableFetchData associated with |source|.
+  std::unique_ptr<QueryAvailableFetchData> GetQueryAvailableFetchData(
+      const net::URLFetcher* source) {
+    base::AutoLock lock(query_available_fetches_lock_);
+    auto it = query_available_fetches_.find(source);
+    CHECK(it != query_available_fetches_.end()) << "Fetch data not found!";
+    auto fetch_data = std::move(it->second);
+    query_available_fetches_.erase(it);
+    return fetch_data;
+  }
+
+  // Trivial wrappers around PpdCache::Find() and
+  // PpdCache::FindAvailablePrinters().  We need these wrappers both because we
+  // use weak_ptrs to manage lifetime, and so we need to bind callbacks to
+  // *this*, and because weak_ptr's preclude return values in posted tasks, so
+  // we have to use a parameter to return state.
+  void ResolveDoCacheLookup(
+      const Printer::PpdReference& reference,
+      base::Optional<base::FilePath>* cache_result) const {
+    *cache_result = cache_->Find(reference);
+  }
+
+  void QueryAvailableDoCacheLookup(
+      base::Optional<PpdProvider::AvailablePrintersMap>* cache_result) const {
+    *cache_result = cache_->FindAvailablePrinters();
+  }
+
+  // Callback that happens when the Resolve() cache lookup completes.  If the
+  // cache satisfied the request, finish the Resolve, otherwise start a URL
+  // request to satisfy the request.  This runs on the same thread as Resolve()
+  // was invoked on.
+  void ResolveCacheLookupDone(
+      const Printer::PpdReference& ppd_reference,
+      const PpdProvider::ResolveCallback& done_callback,
+      std::unique_ptr<base::Optional<base::FilePath>> cache_result) {
+    if (*cache_result) {
+      // Cache hit.  Schedule the callback now and return.
+      done_callback.Run(PpdProvider::SUCCESS, cache_result->value());
+      return;
+    }
+
+    // We don't have a way to automatically resolve user-supplied PPDs yet.  So
+    // if we have one specified, and it's not cached, we fail out rather than
+    // fall back to quirks-server based resolution.  The reasoning here is that
+    // if the user has specified a PPD when a quirks-server one exists, it
+    // probably means the quirks server one doesn't work for some reason, so we
+    // shouldn't silently use it.
+    if (!ppd_reference.user_supplied_ppd_url.empty()) {
+      done_callback.Run(PpdProvider::NOT_FOUND, base::FilePath());
+      return;
+    }
+
+    auto fetch_data = base::MakeUnique<ResolveFetchData>();
+    fetch_data->ppd_reference = ppd_reference;
+    fetch_data->done_callback = done_callback;
+    fetch_data->fetcher =
+        net::URLFetcher::Create(GetQuirksServerPpdLookupURL(ppd_reference),
+                                net::URLFetcher::GET, &resolve_delegate_);
+
+    fetch_data->fetcher->SetRequestContext(url_context_getter_.get());
+    fetch_data->fetcher->SetLoadFlags(
+        net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+        net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
+        net::LOAD_DO_NOT_SEND_AUTH_DATA);
+    auto* fetcher = fetch_data->fetcher.get();
+    StoreResolveFetchData(std::move(fetch_data));
+    fetcher->Start();
+  }
+
   // Generate a url to look up a manufacturer/model from the quirks server
   GURL GetQuirksServerPpdLookupURL(
       const Printer::PpdReference& ppd_reference) const {
@@ -369,35 +418,6 @@
             fetcher.GetResponseAsString(contents));
   }
 
-  // State held across a Resolve() call.
-
-  // Check that Resolve() and its callback are sequenced appropriately.
-  base::SequenceChecker resolve_sequence_checker_;
-
-  // Fetcher and associated delegate for the current Resolve() call, if a fetch
-  // is in progress.  These are both null if no Resolve() is in flight.
-  std::unique_ptr<net::URLFetcher> resolve_fetcher_;
-  std::unique_ptr<ForwardingURLFetcherDelegate> resolve_fetcher_delegate_;
-  // Is there a current Resolve() inflight?  Used to help fail-fast in the case
-  // of inappropriate concurrent usage.
-  bool resolve_inflight_ = false;
-
-  // State held across a QueryAvailable() call.
-
-  // Check that QueryAvailable() and its callback are sequenced appropriately.
-  base::SequenceChecker query_sequence_checker_;
-
-  // Fetcher and associated delegate for the current QueryAvailable() call, if a
-  // fetch is in progress.  These are both null if no QueryAvailable() is in
-  // flight.
-  std::unique_ptr<net::URLFetcher> query_fetcher_;
-  std::unique_ptr<ForwardingURLFetcherDelegate> query_fetcher_delegate_;
-  // Is there a current QueryAvailable() inflight?  Used to help fail-fast in
-  // the case of inappropriate concurrent usage.
-  bool query_inflight_ = false;
-
-  // Common state.
-
   // API key for accessing quirks server.
   const std::string api_key_;
 
@@ -408,9 +428,33 @@
   // Construction-time options, immutable.
   const PpdProvider::Options options_;
 
+  ResolveURLFetcherDelegate resolve_delegate_;
+  QueryAvailableURLFetcherDelegate query_available_delegate_;
+
+  // Active resolve fetches and associated lock.
+  std::unordered_map<const net::URLFetcher*, std::unique_ptr<ResolveFetchData>>
+      resolve_fetches_;
+  base::Lock resolve_fetches_lock_;
+
+  // Active QueryAvailable() fetches and associated lock.
+  std::unordered_map<const net::URLFetcher*,
+                     std::unique_ptr<QueryAvailableFetchData>>
+      query_available_fetches_;
+  base::Lock query_available_fetches_lock_;
+
   base::WeakPtrFactory<PpdProviderImpl> weak_factory_;
 };
 
+void ResolveURLFetcherDelegate::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  parent_->OnResolveFetchComplete(source);
+}
+
+void QueryAvailableURLFetcherDelegate::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  parent_->OnQueryAvailableFetchComplete(source);
+}
+
 }  // namespace
 
 // static
diff --git a/chromeos/printing/ppd_provider.h b/chromeos/printing/ppd_provider.h
index 917e5a1..0e8f354f 100644
--- a/chromeos/printing/ppd_provider.h
+++ b/chromeos/printing/ppd_provider.h
@@ -29,6 +29,8 @@
 // CUPS-PostScript Printer Description (PPD) files.  It provides PPDs that a
 // user previously identified for use, and falls back to querying quirksserver
 // based on manufacturer/model of the printer.
+//
+// This class can be accessed from any thread.
 class CHROMEOS_EXPORT PpdProvider {
  public:
   // Possible result codes of a Resolve() or QueryAvailable() call.
@@ -94,8 +96,6 @@
   // base::SequencedTaskRunnerHandle::IsSet() must be true).
   //
   // |cb| will only be called after the task invoking Resolve() is finished.
-  //
-  // Only one Resolve() call may be outstanding at a time.
   virtual void Resolve(const Printer::PpdReference& ppd_reference,
                        const ResolveCallback& cb) = 0;
 
@@ -106,8 +106,6 @@
   //
   // |cb| will only be called after the task invoking QueryAvailable() is
   // finished.
-  //
-  // Only one QueryAvailable() call may be outstanding at a time.
   virtual void QueryAvailable(const QueryAvailableCallback& cb) = 0;
 
   // Most of the time, the cache is just an invisible backend to the Provider,
diff --git a/chromeos/printing/printer_configuration.cc b/chromeos/printing/printer_configuration.cc
index 3101d15..b5c8c3f 100644
--- a/chromeos/printing/printer_configuration.cc
+++ b/chromeos/printing/printer_configuration.cc
@@ -25,4 +25,9 @@
 
 Printer::~Printer() {}
 
+bool Printer::IsIppEverywhere() const {
+  // TODO(skau): Add check for IPP Everywhere value.
+  return false;
+}
+
 }  // namespace chromeos
diff --git a/chromeos/printing/printer_configuration.h b/chromeos/printing/printer_configuration.h
index 1d5bfcc..233a09f0 100644
--- a/chromeos/printing/printer_configuration.h
+++ b/chromeos/printing/printer_configuration.h
@@ -85,6 +85,11 @@
   const std::string& uuid() const { return uuid_; }
   void set_uuid(const std::string& uuid) { uuid_ = uuid; }
 
+  // Returns true if the printer should be automatically configured using
+  // IPP Everywhere.  Computed using information from |ppd_reference_| and
+  // |uri_|.
+  bool IsIppEverywhere() const;
+
  private:
   // Globally unique identifier. Empty indicates a new printer.
   std::string id_;
diff --git a/chromeos/settings/cros_settings_names.cc b/chromeos/settings/cros_settings_names.cc
index 14f566ad..094a6c7 100644
--- a/chromeos/settings/cros_settings_names.cc
+++ b/chromeos/settings/cros_settings_names.cc
@@ -56,6 +56,9 @@
 // True if auto-update was disabled by the system administrator.
 const char kUpdateDisabled[] = "cros.system.updateDisabled";
 
+// True if a target version prefix is set by the system administrator.
+const char kTargetVersionPrefix[] = "cros.system.targetVersionPrefix";
+
 // A list of strings which specifies allowed connection types for
 // update.
 const char kAllowedConnectionTypesForUpdate[] =
diff --git a/chromeos/settings/cros_settings_names.h b/chromeos/settings/cros_settings_names.h
index d3369f67..6ffc24db 100644
--- a/chromeos/settings/cros_settings_names.h
+++ b/chromeos/settings/cros_settings_names.h
@@ -43,6 +43,7 @@
 CHROMEOS_EXPORT extern const char kSignedDataRoamingEnabled[];
 
 CHROMEOS_EXPORT extern const char kUpdateDisabled[];
+CHROMEOS_EXPORT extern const char kTargetVersionPrefix[];
 CHROMEOS_EXPORT extern const char kAllowedConnectionTypesForUpdate[];
 
 CHROMEOS_EXPORT extern const char kSystemTimezonePolicy[];
diff --git a/chromeos/timezone/timezone_request.cc b/chromeos/timezone/timezone_request.cc
index 940632e..df1ef21e 100644
--- a/chromeos/timezone/timezone_request.cc
+++ b/chromeos/timezone/timezone_request.cc
@@ -188,10 +188,12 @@
 
   const base::DictionaryValue* response_object = NULL;
   if (!response_value->GetAsDictionary(&response_object)) {
-    PrintTimeZoneError(server_url,
-                       "Unexpected response type : " +
-                           base::StringPrintf("%u", response_value->GetType()),
-                       timezone);
+    PrintTimeZoneError(
+        server_url,
+        "Unexpected response type : " +
+            base::StringPrintf(
+                "%u", static_cast<unsigned int>(response_value->GetType())),
+        timezone);
     RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED);
     return false;
   }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 82a929a..e5cff859 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -99,11 +99,10 @@
     "//components/network_time:unit_tests",
     "//components/ntp_snippets:unit_tests",
     "//components/ntp_tiles:unit_tests",
-    "//components/offline_pages:unit_tests",
-    "//components/offline_pages/background:unit_tests",
     "//components/offline_pages/core:unit_tests",
-    "//components/offline_pages/downloads:unit_tests",
-    "//components/offline_pages/request_header:unit_tests",
+    "//components/offline_pages/core/background:unit_tests",
+    "//components/offline_pages/core/downloads:unit_tests",
+    "//components/offline_pages/core/request_header:unit_tests",
     "//components/omnibox/browser:unit_tests",
     "//components/open_from_clipboard:unit_tests",
     "//components/os_crypt:unit_tests",
@@ -134,6 +133,8 @@
     "//components/sync_bookmarks:unit_tests",
     "//components/sync_preferences:unit_tests",
     "//components/sync_sessions:unit_tests",
+    "//components/task_scheduler_util/initialization:unit_tests",
+    "//components/task_scheduler_util/variations:unit_tests",
     "//components/test:test_support",
     "//components/translate/core/browser:unit_tests",
     "//components/translate/core/common:unit_tests",
diff --git a/components/arc/arc_bridge_host_impl.cc b/components/arc/arc_bridge_host_impl.cc
index 1fc93b5..b22068e5 100644
--- a/components/arc/arc_bridge_host_impl.cc
+++ b/components/arc/arc_bridge_host_impl.cc
@@ -67,8 +67,12 @@
 
 }  // namespace
 
-ArcBridgeHostImpl::ArcBridgeHostImpl(mojom::ArcBridgeInstancePtr instance)
-    : binding_(this), instance_(std::move(instance)) {
+ArcBridgeHostImpl::ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
+                                     mojom::ArcBridgeInstancePtr instance)
+    : arc_bridge_service_(arc_bridge_service),
+      binding_(this),
+      instance_(std::move(instance)) {
+  DCHECK(arc_bridge_service_);
   DCHECK(instance_.is_bound());
   instance_.set_connection_error_handler(
       base::Bind(&ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
@@ -80,129 +84,126 @@
 }
 
 void ArcBridgeHostImpl::OnAppInstanceReady(mojom::AppInstancePtr app_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->app(), std::move(app_ptr));
+  OnInstanceReady(arc_bridge_service_->app(), std::move(app_ptr));
 }
 
 void ArcBridgeHostImpl::OnAudioInstanceReady(
     mojom::AudioInstancePtr audio_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->audio(), std::move(audio_ptr));
+  OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_ptr));
 }
 
 void ArcBridgeHostImpl::OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->auth(), std::move(auth_ptr));
+  OnInstanceReady(arc_bridge_service_->auth(), std::move(auth_ptr));
 }
 
 void ArcBridgeHostImpl::OnBluetoothInstanceReady(
     mojom::BluetoothInstancePtr bluetooth_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->bluetooth(),
-                  std::move(bluetooth_ptr));
+  OnInstanceReady(arc_bridge_service_->bluetooth(), std::move(bluetooth_ptr));
 }
 
 void ArcBridgeHostImpl::OnBootPhaseMonitorInstanceReady(
     mojom::BootPhaseMonitorInstancePtr boot_phase_monitor_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->boot_phase_monitor(),
+  OnInstanceReady(arc_bridge_service_->boot_phase_monitor(),
                   std::move(boot_phase_monitor_ptr));
 }
 
 void ArcBridgeHostImpl::OnClipboardInstanceReady(
     mojom::ClipboardInstancePtr clipboard_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->clipboard(),
-                  std::move(clipboard_ptr));
+  OnInstanceReady(arc_bridge_service_->clipboard(), std::move(clipboard_ptr));
 }
 
 void ArcBridgeHostImpl::OnCrashCollectorInstanceReady(
     mojom::CrashCollectorInstancePtr crash_collector_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->crash_collector(),
+  OnInstanceReady(arc_bridge_service_->crash_collector(),
                   std::move(crash_collector_ptr));
 }
 
 void ArcBridgeHostImpl::OnEnterpriseReportingInstanceReady(
     mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->enterprise_reporting(),
+  OnInstanceReady(arc_bridge_service_->enterprise_reporting(),
                   std::move(enterprise_reporting_ptr));
 }
 
 void ArcBridgeHostImpl::OnFileSystemInstanceReady(
     mojom::FileSystemInstancePtr file_system_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->file_system(),
+  OnInstanceReady(arc_bridge_service_->file_system(),
                   std::move(file_system_ptr));
 }
 
 void ArcBridgeHostImpl::OnImeInstanceReady(mojom::ImeInstancePtr ime_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->ime(), std::move(ime_ptr));
+  OnInstanceReady(arc_bridge_service_->ime(), std::move(ime_ptr));
 }
 
 void ArcBridgeHostImpl::OnIntentHelperInstanceReady(
     mojom::IntentHelperInstancePtr intent_helper_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->intent_helper(),
+  OnInstanceReady(arc_bridge_service_->intent_helper(),
                   std::move(intent_helper_ptr));
 }
 
 void ArcBridgeHostImpl::OnKioskInstanceReady(
     mojom::KioskInstancePtr kiosk_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->kiosk(), std::move(kiosk_ptr));
+  OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr));
 }
 
 void ArcBridgeHostImpl::OnMetricsInstanceReady(
     mojom::MetricsInstancePtr metrics_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->metrics(), std::move(metrics_ptr));
+  OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_ptr));
 }
 
 void ArcBridgeHostImpl::OnNetInstanceReady(mojom::NetInstancePtr net_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->net(), std::move(net_ptr));
+  OnInstanceReady(arc_bridge_service_->net(), std::move(net_ptr));
 }
 
 void ArcBridgeHostImpl::OnNotificationsInstanceReady(
     mojom::NotificationsInstancePtr notifications_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->notifications(),
+  OnInstanceReady(arc_bridge_service_->notifications(),
                   std::move(notifications_ptr));
 }
 
 void ArcBridgeHostImpl::OnObbMounterInstanceReady(
     mojom::ObbMounterInstancePtr obb_mounter_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->obb_mounter(),
+  OnInstanceReady(arc_bridge_service_->obb_mounter(),
                   std::move(obb_mounter_ptr));
 }
 
 void ArcBridgeHostImpl::OnPolicyInstanceReady(
     mojom::PolicyInstancePtr policy_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->policy(), std::move(policy_ptr));
+  OnInstanceReady(arc_bridge_service_->policy(), std::move(policy_ptr));
 }
 
 void ArcBridgeHostImpl::OnPowerInstanceReady(
     mojom::PowerInstancePtr power_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->power(), std::move(power_ptr));
+  OnInstanceReady(arc_bridge_service_->power(), std::move(power_ptr));
 }
 
 void ArcBridgeHostImpl::OnPrintInstanceReady(
     mojom::PrintInstancePtr print_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->print(), std::move(print_ptr));
+  OnInstanceReady(arc_bridge_service_->print(), std::move(print_ptr));
 }
 
 void ArcBridgeHostImpl::OnProcessInstanceReady(
     mojom::ProcessInstancePtr process_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->process(), std::move(process_ptr));
+  OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr));
 }
 
 void ArcBridgeHostImpl::OnStorageManagerInstanceReady(
     mojom::StorageManagerInstancePtr storage_manager_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->storage_manager(),
+  OnInstanceReady(arc_bridge_service_->storage_manager(),
                   std::move(storage_manager_ptr));
 }
 
 void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->tts(), std::move(tts_ptr));
+  OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr));
 }
 
 void ArcBridgeHostImpl::OnVideoInstanceReady(
     mojom::VideoInstancePtr video_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->video(), std::move(video_ptr));
+  OnInstanceReady(arc_bridge_service_->video(), std::move(video_ptr));
 }
 
 void ArcBridgeHostImpl::OnWallpaperInstanceReady(
     mojom::WallpaperInstancePtr wallpaper_ptr) {
-  OnInstanceReady(ArcBridgeService::Get()->wallpaper(),
-                  std::move(wallpaper_ptr));
+  OnInstanceReady(arc_bridge_service_->wallpaper(), std::move(wallpaper_ptr));
 }
 
 void ArcBridgeHostImpl::OnClosed() {
diff --git a/components/arc/arc_bridge_host_impl.h b/components/arc/arc_bridge_host_impl.h
index b99a4953..b420725 100644
--- a/components/arc/arc_bridge_host_impl.h
+++ b/components/arc/arc_bridge_host_impl.h
@@ -17,6 +17,8 @@
 
 namespace arc {
 
+class ArcBridgeService;
+
 // Implementation of the ArcBridgeHost.
 // The lifetime of ArcBridgeHost and ArcBridgeInstance mojo channels are tied
 // to this instance. Also, any ARC related Mojo channel will be closed if
@@ -31,7 +33,8 @@
   // Interface to keep the Mojo channel InterfacePtr.
   class MojoChannel;
 
-  explicit ArcBridgeHostImpl(mojom::ArcBridgeInstancePtr instance);
+  ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
+                    mojom::ArcBridgeInstancePtr instance);
   ~ArcBridgeHostImpl() override;
 
   // ArcBridgeHost overrides.
@@ -86,6 +89,9 @@
 
   base::ThreadChecker thread_checker_;
 
+  // Owned by ArcServiceManager.
+  ArcBridgeService* const arc_bridge_service_;
+
   mojo::Binding<mojom::ArcBridgeHost> binding_;
   mojom::ArcBridgeInstancePtr instance_;
 
diff --git a/components/arc/arc_bridge_service_impl.cc b/components/arc/arc_bridge_service_impl.cc
index d426b63..51a7cd7 100644
--- a/components/arc/arc_bridge_service_impl.cc
+++ b/components/arc/arc_bridge_service_impl.cc
@@ -31,7 +31,7 @@
 ArcBridgeServiceImpl::ArcBridgeServiceImpl(
     const scoped_refptr<base::TaskRunner>& blocking_task_runner)
     : session_started_(false),
-      factory_(base::Bind(ArcSession::Create, blocking_task_runner)),
+      factory_(base::Bind(ArcSession::Create, this, blocking_task_runner)),
       weak_factory_(this) {
   DCHECK(!g_arc_bridge_service);
   g_arc_bridge_service = this;
diff --git a/components/arc/arc_session.cc b/components/arc/arc_session.cc
index 6c85b992..2de8f50f2 100644
--- a/components/arc/arc_session.cc
+++ b/components/arc/arc_session.cc
@@ -199,8 +199,8 @@
     STOPPED,
   };
 
-  explicit ArcSessionImpl(
-      const scoped_refptr<base::TaskRunner>& blocking_task_runner);
+  ArcSessionImpl(ArcBridgeService* arc_bridge_service,
+                 const scoped_refptr<base::TaskRunner>& blocking_task_runner);
   ~ArcSessionImpl() override;
 
   // ArcSession overrides:
@@ -238,6 +238,9 @@
   // created.
   base::ThreadChecker thread_checker_;
 
+  // Owned by ArcServiceManager.
+  ArcBridgeService* const arc_bridge_service_;
+
   // Task runner to run a blocking tasks.
   scoped_refptr<base::TaskRunner> blocking_task_runner_;
 
@@ -262,8 +265,11 @@
 };
 
 ArcSessionImpl::ArcSessionImpl(
+    ArcBridgeService* arc_bridge_service,
     const scoped_refptr<base::TaskRunner>& blocking_task_runner)
-    : blocking_task_runner_(blocking_task_runner), weak_factory_(this) {
+    : arc_bridge_service_(arc_bridge_service),
+      blocking_task_runner_(blocking_task_runner),
+      weak_factory_(this) {
   chromeos::SessionManagerClient* client = GetSessionManagerClient();
   if (client == nullptr)
     return;
@@ -487,7 +493,8 @@
   mojom::ArcBridgeInstancePtr instance;
   instance.Bind(mojo::InterfacePtrInfo<mojom::ArcBridgeInstance>(
       std::move(server_pipe), 0u));
-  arc_bridge_host_.reset(new ArcBridgeHostImpl(std::move(instance)));
+  arc_bridge_host_ = base::MakeUnique<ArcBridgeHostImpl>(arc_bridge_service_,
+                                                         std::move(instance));
 
   VLOG(2) << "Mojo is connected. ARC is running.";
   state_ = State::RUNNING;
@@ -633,8 +640,10 @@
 
 // static
 std::unique_ptr<ArcSession> ArcSession::Create(
+    ArcBridgeService* arc_bridge_service,
     const scoped_refptr<base::TaskRunner>& blocking_task_runner) {
-  return base::MakeUnique<ArcSessionImpl>(blocking_task_runner);
+  return base::MakeUnique<ArcSessionImpl>(arc_bridge_service,
+                                          blocking_task_runner);
 }
 
 }  // namespace arc
diff --git a/components/arc/arc_session.h b/components/arc/arc_session.h
index 9d82e776..e39b43d 100644
--- a/components/arc/arc_session.h
+++ b/components/arc/arc_session.h
@@ -44,6 +44,7 @@
 
   // Creates a default instance of ArcSession.
   static std::unique_ptr<ArcSession> Create(
+      ArcBridgeService* arc_bridge_service,
       const scoped_refptr<base::TaskRunner>& blocking_task_runner);
   virtual ~ArcSession();
 
diff --git a/components/arc/bluetooth/bluetooth_type_converters_unittest.cc b/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
index 5b61f83..34fd9459 100644
--- a/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
+++ b/components/arc/bluetooth/bluetooth_type_converters_unittest.cc
@@ -122,7 +122,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
             nulltypeAttributeBlueZ.type());
   EXPECT_EQ(0u, nulltypeAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_NULL, nulltypeAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::NONE, nulltypeAttributeBlueZ.value().GetType());
 
   // Construct Mojo attribute with TYPE_BOOLEAN value.
   bool valueBool = true;
@@ -136,7 +136,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::BOOL,
             boolAttributeBlueZ.type());
   EXPECT_EQ(sizeof(valueBool), boolAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, boolAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, boolAttributeBlueZ.value().GetType());
   EXPECT_TRUE(boolAttributeBlueZ.value().GetAsBoolean(&valueBool));
   EXPECT_TRUE(valueBool);
 
@@ -154,7 +154,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::UINT,
             uintAttributeBlueZ.type());
   EXPECT_EQ(sizeof(valueUint16), uintAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, uintAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, uintAttributeBlueZ.value().GetType());
   EXPECT_TRUE(uintAttributeBlueZ.value().GetAsInteger(&valueUint16AsInt));
   EXPECT_EQ(valueUint16, static_cast<uint16_t>(valueUint16AsInt));
 
@@ -172,7 +172,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::INT,
             intAttributeBlueZ.type());
   EXPECT_EQ(sizeof(valueInt16), intAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, intAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, intAttributeBlueZ.value().GetType());
   EXPECT_TRUE(intAttributeBlueZ.value().GetAsInteger(&valueInt16AsInt));
   EXPECT_EQ(valueInt16, static_cast<int16_t>(valueInt16AsInt));
 
@@ -192,7 +192,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::UUID,
             uuidAttributeBlueZ.type());
   EXPECT_EQ(sizeof(uint16_t), uuidAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_STRING, uuidAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::STRING, uuidAttributeBlueZ.value().GetType());
   EXPECT_TRUE(uuidAttributeBlueZ.value().GetAsString(&actualUUID));
   EXPECT_EQ(expectedUUID, actualUUID);
 
@@ -213,7 +213,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::STRING,
             stringAttributeBlueZ.type());
   EXPECT_EQ(expectedString.length(), stringAttributeBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_STRING, stringAttributeBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::STRING, stringAttributeBlueZ.value().GetType());
   EXPECT_TRUE(stringAttributeBlueZ.value().GetAsString(&actualString));
   EXPECT_EQ(expectedString, actualString);
 }
@@ -278,7 +278,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
             valueNoDataBlueZ.type());
   EXPECT_EQ(0u, valueNoDataBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_NULL, valueNoDataBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::NONE, valueNoDataBlueZ.value().GetType());
 }
 
 TEST(BluetoothTypeConvertorTest,
@@ -295,7 +295,7 @@
   EXPECT_EQ(bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE,
             sequenceNoDataBlueZ.type());
   EXPECT_EQ(0u, sequenceNoDataBlueZ.size());
-  EXPECT_EQ(base::Value::TYPE_NULL, sequenceNoDataBlueZ.value().GetType());
+  EXPECT_EQ(base::Value::Type::NONE, sequenceNoDataBlueZ.value().GetType());
 
   // Create a Mojo attribute with the depth = arc::kBluetoothSDPMaxDepth + 3.
   auto sequenceTooDeepMojo =
@@ -322,15 +322,14 @@
             nulltypeAttributeMojo->type);
   EXPECT_EQ(0u, nulltypeAttributeMojo->type_size);
   EXPECT_TRUE(nulltypeAttributeMojo->value.Get(0, &actualValue));
-  EXPECT_EQ(base::Value::TYPE_NULL, actualValue->GetType());
+  EXPECT_EQ(base::Value::Type::NONE, actualValue->GetType());
 
   // Check integer types (INT, UINT).
   uint16_t valueUint16 = 10;
   int valueUint16AsInt;
   auto uintAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
       bluez::BluetoothServiceAttributeValueBlueZ::UINT, sizeof(valueUint16),
-      base::MakeUnique<base::FundamentalValue>(
-          base::FundamentalValue(static_cast<int>(valueUint16))));
+      base::MakeUnique<base::FundamentalValue>(static_cast<int>(valueUint16)));
 
   auto uintAttributeMojo =
       ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(uintAttributeBlueZ);
@@ -346,8 +345,7 @@
   bool actualBool = true;
   auto boolAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
       bluez::BluetoothServiceAttributeValueBlueZ::BOOL, sizeof(bool),
-      base::MakeUnique<base::FundamentalValue>(
-          base::FundamentalValue(valueBool)));
+      base::MakeUnique<base::FundamentalValue>(valueBool));
 
   auto boolAttributeMojo =
       ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(boolAttributeBlueZ);
@@ -364,7 +362,7 @@
   std::string actualUUID;
   auto uuidAttributeBlueZ = bluez::BluetoothServiceAttributeValueBlueZ(
       bluez::BluetoothServiceAttributeValueBlueZ::UUID, sizeof(uint16_t),
-      base::MakeUnique<base::StringValue>(base::StringValue(valueUUID)));
+      base::MakeUnique<base::StringValue>(valueUUID));
 
   auto uuidAttributeMojo =
       ConvertTo<arc::mojom::BluetoothSdpAttributePtr>(uuidAttributeBlueZ);
@@ -402,11 +400,10 @@
       sequence(new bluez::BluetoothServiceAttributeValueBlueZ::Sequence());
   sequence->push_back(bluez::BluetoothServiceAttributeValueBlueZ(
       bluez::BluetoothServiceAttributeValueBlueZ::UUID, sizeof(uint16_t),
-      base::MakeUnique<base::StringValue>(base::StringValue(l2capUUID))));
+      base::MakeUnique<base::StringValue>(l2capUUID)));
   sequence->push_back(bluez::BluetoothServiceAttributeValueBlueZ(
       bluez::BluetoothServiceAttributeValueBlueZ::UINT, sizeof(uint16_t),
-      base::MakeUnique<base::FundamentalValue>(
-          base::FundamentalValue(l2capChannel))));
+      base::MakeUnique<base::FundamentalValue>(l2capChannel)));
 
   auto sequenceBlueZ =
       bluez::BluetoothServiceAttributeValueBlueZ(std::move(sequence));
diff --git a/components/arc/common/bluetooth.mojom b/components/arc/common/bluetooth.mojom
index a1f5a0d..370c352 100644
--- a/components/arc/common/bluetooth.mojom
+++ b/components/arc/common/bluetooth.mojom
@@ -6,7 +6,7 @@
 
 module arc.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/values.mojom";
 
 [Extensible]
 enum BluetoothAdapterState {
diff --git a/components/arc/ime/arc_ime_service.h b/components/arc/ime/arc_ime_service.h
index a88e2cb..12610a8b 100644
--- a/components/arc/ime/arc_ime_service.h
+++ b/components/arc/ime/arc_ime_service.h
@@ -109,7 +109,7 @@
   bool ChangeTextDirectionAndLayoutAlignment(
       base::i18n::TextDirection direction) override;
   void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretInRect(const gfx::Rect& rect) override {}
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override {}
   bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
   void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override {
   }
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 0851be6..391c28e 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -6,10 +6,10 @@
 
 #include <utility>
 
+#include "ash/common/new_window_controller.h"
 #include "ash/common/shell_delegate.h"
 #include "ash/common/wallpaper/wallpaper_controller.h"
 #include "ash/common/wm_shell.h"
-#include "ash/public/interfaces/new_window.mojom.h"
 #include "ash/shell.h"
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
@@ -68,7 +68,7 @@
   // downloads by default, which is what we want.  However if it is open it will
   // simply be brought to the forgeground without forcibly being navigated to
   // downloads, which is probably not ideal.
-  ash::WmShell::Get()->new_window_client()->OpenFileManager();
+  ash::WmShell::Get()->new_window_controller()->OpenFileManager();
 }
 
 void ArcIntentHelperBridge::OnOpenUrl(const std::string& url) {
diff --git a/components/autofill/content/common/autofill_agent.mojom b/components/autofill/content/common/autofill_agent.mojom
index a2f40a9d..7870cafc 100644
--- a/components/autofill/content/common/autofill_agent.mojom
+++ b/components/autofill/content/common/autofill_agent.mojom
@@ -5,7 +5,7 @@
 module autofill.mojom;
 
 import "components/autofill/content/common/autofill_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
 
 // There is one instance of this interface per render frame in the render
 // process.
diff --git a/components/autofill/content/common/autofill_driver.mojom b/components/autofill/content/common/autofill_driver.mojom
index 9df4791..8fb379d 100644
--- a/components/autofill/content/common/autofill_driver.mojom
+++ b/components/autofill/content/common/autofill_driver.mojom
@@ -5,7 +5,8 @@
 module autofill.mojom;
 
 import "components/autofill/content/common/autofill_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/time.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 // There is one instance of this interface per render frame host in the browser
diff --git a/components/autofill/content/common/autofill_types.mojom b/components/autofill/content/common/autofill_types.mojom
index 91905f2..55a34df 100644
--- a/components/autofill/content/common/autofill_types.mojom
+++ b/components/autofill/content/common/autofill_types.mojom
@@ -4,7 +4,7 @@
 
 module autofill.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 import "url/mojo/origin.mojom";
 import "url/mojo/url.mojom";
 
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 474a745..041d62b3a 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -30,7 +30,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace syncer {
@@ -103,8 +103,8 @@
   // Gets the IdentityProvider associated with the client (for OAuth2).
   virtual IdentityProvider* GetIdentityProvider() = 0;
 
-  // Gets the RapporService associated with the client (for metrics).
-  virtual rappor::RapporService* GetRapporService() = 0;
+  // Gets the RapporServiceImpl associated with the client (for metrics).
+  virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0;
 
   // Causes the Autofill settings UI to be shown.
   virtual void ShowAutofillSettings() = 0;
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 1546ad5..fe2557c 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -60,7 +60,8 @@
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "google_apis/gaia/identity_provider.h"
 #include "grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -967,7 +968,7 @@
 
   // Parse and store the server predictions.
   FormStructure::ParseQueryResponse(std::move(response), queried_forms,
-                                    client_->GetRapporService());
+                                    client_->GetRapporServiceImpl());
 
   // Will log quality metrics for each FormStructure based on the presence of
   // autocomplete attributes, if available.
@@ -1287,8 +1288,8 @@
 
 void AutofillManager::CollectRapportSample(const GURL& source_url,
                                            const char* metric_name) const {
-  if (source_url.is_valid() && client_->GetRapporService()) {
-    rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporService(),
+  if (source_url.is_valid() && client_->GetRapporServiceImpl()) {
+    rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporServiceImpl(),
                                             metric_name, source_url);
   }
 }
@@ -1303,9 +1304,10 @@
     const TimeTicks& interaction_time,
     const TimeTicks& submission_time,
     bool observed_submission) {
-  submitted_form->LogQualityMetrics(
-      load_time, interaction_time, submission_time, client_->GetRapporService(),
-      did_show_suggestions_, observed_submission);
+  submitted_form->LogQualityMetrics(load_time, interaction_time,
+                                    submission_time,
+                                    client_->GetRapporServiceImpl(),
+                                    did_show_suggestions_, observed_submission);
 
   if (submitted_form->ShouldBeCrowdsourced())
     UploadFormData(*submitted_form, observed_submission);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index eacfe7b1..684de522 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -4590,7 +4590,7 @@
       "Autofill.CardUploadDecisionExpanded",
       AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1);
 
-  rappor::TestRapporService* rappor_service =
+  rappor::TestRapporServiceImpl* rappor_service =
       autofill_client_.test_rappor_service();
   EXPECT_EQ(1, rappor_service->GetReportsCount());
   std::string sample;
@@ -4702,7 +4702,7 @@
       "Autofill.CardUploadDecisionExpanded",
       AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS, 1);
 
-  rappor::TestRapporService* rappor_service =
+  rappor::TestRapporServiceImpl* rappor_service =
       autofill_client_.test_rappor_service();
   EXPECT_EQ(1, rappor_service->GetReportsCount());
   std::string sample;
@@ -4755,7 +4755,7 @@
       "Autofill.CardUploadDecisionExpanded",
       AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME, 1);
 
-  rappor::TestRapporService* rappor_service =
+  rappor::TestRapporServiceImpl* rappor_service =
       autofill_client_.test_rappor_service();
   EXPECT_EQ(1, rappor_service->GetReportsCount());
   std::string sample;
@@ -5022,7 +5022,7 @@
       "Autofill.CardUploadDecisionExpanded",
       AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, 1);
 
-  rappor::TestRapporService* rappor_service =
+  rappor::TestRapporServiceImpl* rappor_service =
       autofill_client_.test_rappor_service();
   EXPECT_EQ(1, rappor_service->GetReportsCount());
   std::string sample;
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 681b131..459e871 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -42,7 +42,7 @@
 using base::ASCIIToUTF16;
 using base::Bucket;
 using base::TimeTicks;
-using rappor::TestRapporService;
+using rappor::TestRapporServiceImpl;
 using ::testing::ElementsAre;
 
 namespace autofill {
@@ -4035,7 +4035,7 @@
   }
 
  protected:
-  TestRapporService rappor_service_;
+  TestRapporServiceImpl rappor_service_;
   std::vector<std::unique_ptr<FormStructure>> owned_forms_;
   std::vector<FormStructure*> forms_;
 };
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index cf4ae5c..193a9b0a 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -33,8 +33,8 @@
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/form_field_data_predictions.h"
 #include "components/autofill/core/common/signatures_util.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 
 namespace autofill {
 namespace {
@@ -442,9 +442,10 @@
 }
 
 // static
-void FormStructure::ParseQueryResponse(std::string payload,
-                                       const std::vector<FormStructure*>& forms,
-                                       rappor::RapporService* rappor_service) {
+void FormStructure::ParseQueryResponse(
+    std::string payload,
+    const std::vector<FormStructure*>& forms,
+    rappor::RapporServiceImpl* rappor_service) {
   AutofillMetrics::LogServerQueryMetric(
       AutofillMetrics::QUERY_RESPONSE_RECEIVED);
 
@@ -669,7 +670,7 @@
 void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time,
                                       const base::TimeTicks& interaction_time,
                                       const base::TimeTicks& submission_time,
-                                      rappor::RapporService* rappor_service,
+                                      rappor::RapporServiceImpl* rappor_service,
                                       bool did_show_suggestions,
                                       bool observed_submission) const {
   size_t num_detected_field_types = 0;
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 8e9cba3..54288a7 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -34,7 +34,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace autofill {
@@ -75,7 +75,7 @@
   // |rappor_service| may be null.
   static void ParseQueryResponse(std::string response,
                                  const std::vector<FormStructure*>& forms,
-                                 rappor::RapporService* rappor_service);
+                                 rappor::RapporServiceImpl* rappor_service);
 
   // Returns predictions using the details from the given |form_structures| and
   // their fields' predicted types.
@@ -129,7 +129,7 @@
   void LogQualityMetrics(const base::TimeTicks& load_time,
                          const base::TimeTicks& interaction_time,
                          const base::TimeTicks& submission_time,
-                         rappor::RapporService* rappor_service,
+                         rappor::RapporServiceImpl* rappor_service,
                          bool did_show_suggestions,
                          bool observed_submission) const;
 
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 32b2cca..5fbeca3 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -458,7 +458,7 @@
       std::string error_code;
       std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
       if (message_value.get() &&
-          message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+          message_value->IsType(base::Value::Type::DICTIONARY)) {
         response_dict.reset(
             static_cast<base::DictionaryValue*>(message_value.release()));
         response_dict->GetString("error.code", &error_code);
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index 36e71f2f..9989e0a 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -11,7 +11,7 @@
 TestAutofillClient::TestAutofillClient()
     : token_service_(new FakeOAuth2TokenService()),
       identity_provider_(new FakeIdentityProvider(token_service_.get())),
-      rappor_service_(new rappor::TestRapporService()) {}
+      rappor_service_(new rappor::TestRapporServiceImpl()) {}
 
 TestAutofillClient::~TestAutofillClient() {
 }
@@ -36,7 +36,7 @@
   return identity_provider_.get();
 }
 
-rappor::RapporService* TestAutofillClient::GetRapporService() {
+rappor::RapporServiceImpl* TestAutofillClient::GetRapporServiceImpl() {
   return rappor_service_.get();
 }
 
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 31f641c1..95311d7 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -33,7 +33,7 @@
   PrefService* GetPrefs() override;
   syncer::SyncService* GetSyncService() override;
   IdentityProvider* GetIdentityProvider() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   void ShowAutofillSettings() override;
   void ShowUnmaskPrompt(const CreditCard& card,
                         UnmaskCardReason reason,
@@ -76,7 +76,7 @@
     prefs_ = std::move(prefs);
   }
 
-  rappor::TestRapporService* test_rappor_service() {
+  rappor::TestRapporServiceImpl* test_rappor_service() {
     return rappor_service_.get();
   }
 
@@ -85,7 +85,7 @@
   std::unique_ptr<PrefService> prefs_;
   std::unique_ptr<FakeOAuth2TokenService> token_service_;
   std::unique_ptr<FakeIdentityProvider> identity_provider_;
-  std::unique_ptr<rappor::TestRapporService> rappor_service_;
+  std::unique_ptr<rappor::TestRapporServiceImpl> rappor_service_;
 
   DISALLOW_COPY_AND_ASSIGN(TestAutofillClient);
 };
diff --git a/components/autofill/ios/browser/form_suggestion.h b/components/autofill/ios/browser/form_suggestion.h
index 7fd9df1d..8fb6689 100644
--- a/components/autofill/ios/browser/form_suggestion.h
+++ b/components/autofill/ios/browser/form_suggestion.h
@@ -7,8 +7,6 @@
 
 #import <Foundation/Foundation.h>
 
-#include "base/mac/objc_property_releaser.h"
-
 // Represents a user-selectable suggestion for a single field within a form
 // on a web page.
 @interface FormSuggestion : NSObject
diff --git a/components/autofill/ios/browser/form_suggestion.mm b/components/autofill/ios/browser/form_suggestion.mm
index 709bb63..f4bb40c 100644
--- a/components/autofill/ios/browser/form_suggestion.mm
+++ b/components/autofill/ios/browser/form_suggestion.mm
@@ -4,6 +4,8 @@
 
 #import "components/autofill/ios/browser/form_suggestion.h"
 
+#include "base/mac/objc_property_releaser.h"
+
 @interface FormSuggestion ()
 // Local initializer for a FormSuggestion.
 - (id)initWithValue:(NSString*)value
diff --git a/components/bookmarks/browser/bookmark_client.cc b/components/bookmarks/browser/bookmark_client.cc
index 9325015..a031ee7 100644
--- a/components/bookmarks/browser/bookmark_client.cc
+++ b/components/bookmarks/browser/bookmark_client.cc
@@ -22,13 +22,12 @@
   return base::CancelableTaskTracker::kBadTaskId;
 }
 
-bool BookmarkClient::SupportsTypedCountForNodes() {
+bool BookmarkClient::SupportsTypedCountForUrls() {
   return false;
 }
 
-void BookmarkClient::GetTypedCountForNodes(
-    const NodeSet& nodes,
-    NodeTypedCountPairs* node_typed_count_pairs) {
+void BookmarkClient::GetTypedCountForUrls(
+    UrlTypedCountMap* url_typed_count_map) {
   NOTREACHED();
 }
 
diff --git a/components/bookmarks/browser/bookmark_client.h b/components/bookmarks/browser/bookmark_client.h
index 5eb625fa..7648cb9 100644
--- a/components/bookmarks/browser/bookmark_client.h
+++ b/components/bookmarks/browser/bookmark_client.h
@@ -5,9 +5,8 @@
 #ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_CLIENT_H_
 #define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_CLIENT_H_
 
-#include <set>
+#include <map>
 #include <utility>
-#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/task/cancelable_task_tracker.h"
@@ -32,12 +31,9 @@
 // e.g. Chrome.
 class BookmarkClient {
  public:
-  // Types representing a set of BookmarkNode and a mapping from BookmarkNode
-  // to the number of time the corresponding URL has been typed by the user in
-  // the Omnibox.
-  typedef std::set<const BookmarkNode*> NodeSet;
-  typedef std::pair<const BookmarkNode*, int> NodeTypedCountPair;
-  typedef std::vector<NodeTypedCountPair> NodeTypedCountPairs;
+  // Type representing a mapping from URLs to the number of times the URL has
+  // been typed by the user in the Omnibox.
+  using UrlTypedCountMap = std::unordered_map<const GURL*, int>;
 
   virtual ~BookmarkClient() {}
 
@@ -62,13 +58,13 @@
       base::CancelableTaskTracker* tracker);
 
   // Returns true if the embedder supports typed count for URL.
-  virtual bool SupportsTypedCountForNodes();
+  virtual bool SupportsTypedCountForUrls();
 
-  // Retrieves the number of time each BookmarkNode URL has been typed in
-  // the Omnibox by the user.
-  virtual void GetTypedCountForNodes(
-      const NodeSet& nodes,
-      NodeTypedCountPairs* node_typed_count_pairs);
+  // Retrieves the number of times each bookmark URL has been typed in
+  // the Omnibox by the user. For each key (URL) in |url_typed_count_map|,
+  // the corresponding value will be updated with the typed count of that URL.
+  // |url_typed_count_map| must not be null.
+  virtual void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map);
 
   // Returns whether the embedder wants permanent node |node|
   // to always be visible or to only show them when not empty.
diff --git a/components/bookmarks/browser/bookmark_codec.cc b/components/bookmarks/browser/bookmark_codec.cc
index 1d6ff89..eadb666 100644
--- a/components/bookmarks/browser/bookmark_codec.cc
+++ b/components/bookmarks/browser/bookmark_codec.cc
@@ -177,7 +177,7 @@
 
   const base::Value* checksum_value;
   if (d_value->Get(kChecksumKey, &checksum_value)) {
-    if (checksum_value->GetType() != base::Value::TYPE_STRING)
+    if (checksum_value->GetType() != base::Value::Type::STRING)
       return false;
     if (!checksum_value->GetAsString(&stored_checksum_))
       return false;
@@ -333,7 +333,7 @@
     if (!value.Get(kChildrenKey, &child_values))
       return false;
 
-    if (child_values->GetType() != base::Value::TYPE_LIST)
+    if (child_values->GetType() != base::Value::Type::LIST)
       return false;
 
     if (!node) {
@@ -395,7 +395,7 @@
 
   // Meta info used to be stored as a serialized dictionary, so attempt to
   // parse the value as one.
-  if (meta_info->IsType(base::Value::TYPE_STRING)) {
+  if (meta_info->IsType(base::Value::Type::STRING)) {
     std::string meta_info_str;
     meta_info->GetAsString(&meta_info_str);
     JSONStringValueDeserializer deserializer(meta_info_str);
@@ -433,11 +433,11 @@
     const std::string& prefix,
     BookmarkNode::MetaInfoMap* meta_info_map) {
   for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
-    if (it.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    if (it.value().IsType(base::Value::Type::DICTIONARY)) {
       const base::DictionaryValue* subdict;
       it.value().GetAsDictionary(&subdict);
       DecodeMetaInfoHelper(*subdict, prefix + it.key() + ".", meta_info_map);
-    } else if (it.value().IsType(base::Value::TYPE_STRING)) {
+    } else if (it.value().IsType(base::Value::Type::STRING)) {
       it.value().GetAsString(&(*meta_info_map)[prefix + it.key()]);
     }
   }
diff --git a/components/bookmarks/browser/bookmark_codec_unittest.cc b/components/bookmarks/browser/bookmark_codec_unittest.cc
index edac5fe3..a9d8523 100644
--- a/components/bookmarks/browser/bookmark_codec_unittest.cc
+++ b/components/bookmarks/browser/bookmark_codec_unittest.cc
@@ -114,33 +114,33 @@
   void GetBookmarksBarChildValue(base::Value* value,
                                  size_t index,
                                  base::DictionaryValue** result_value) {
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
 
     base::DictionaryValue* d_value = nullptr;
     value->GetAsDictionary(&d_value);
     base::Value* roots;
     ASSERT_TRUE(d_value->Get(BookmarkCodec::kRootsKey, &roots));
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, roots->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, roots->GetType());
 
     base::DictionaryValue* roots_d_value = nullptr;
     roots->GetAsDictionary(&roots_d_value);
     base::Value* bb_value;
     ASSERT_TRUE(
         roots_d_value->Get(BookmarkCodec::kRootFolderNameKey, &bb_value));
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, bb_value->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, bb_value->GetType());
 
     base::DictionaryValue* bb_d_value = nullptr;
     bb_value->GetAsDictionary(&bb_d_value);
     base::Value* bb_children_value;
     ASSERT_TRUE(
         bb_d_value->Get(BookmarkCodec::kChildrenKey, &bb_children_value));
-    ASSERT_EQ(base::Value::TYPE_LIST, bb_children_value->GetType());
+    ASSERT_EQ(base::Value::Type::LIST, bb_children_value->GetType());
 
     base::ListValue* bb_children_l_value = nullptr;
     bb_children_value->GetAsList(&bb_children_l_value);
     base::Value* child_value;
     ASSERT_TRUE(bb_children_l_value->Get(index, &child_value));
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, child_value->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, child_value->GetType());
 
     child_value->GetAsDictionary(result_value);
   }
diff --git a/components/bookmarks/browser/bookmark_index.cc b/components/bookmarks/browser/bookmark_index.cc
index 9c34e5a2..14d1dedc 100644
--- a/components/bookmarks/browser/bookmark_index.cc
+++ b/components/bookmarks/browser/bookmark_index.cc
@@ -26,11 +26,14 @@
 
 namespace bookmarks {
 
-typedef BookmarkClient::NodeTypedCountPair NodeTypedCountPair;
-typedef BookmarkClient::NodeTypedCountPairs NodeTypedCountPairs;
+using UrlTypedCountMap = BookmarkClient::UrlTypedCountMap;
 
 namespace {
 
+using UrlNodeMap = std::unordered_map<const GURL*, const BookmarkNode*>;
+using UrlTypedCountPair = std::pair<const GURL*, int>;
+using UrlTypedCountPairs = std::vector<UrlTypedCountPair>;
+
 // Returns a normalized version of the UTF16 string |text|.  If it fails to
 // normalize the string, returns |text| itself as a best-effort.
 base::string16 Normalize(const base::string16& text) {
@@ -54,20 +57,30 @@
                         unicode_normalized_text.length());
 }
 
-// Sort functor for NodeTypedCountPairs. We sort in decreasing order of typed
-// count so that the best matches will always be added to the results.
-struct NodeTypedCountPairSortFunctor {
-  bool operator()(const NodeTypedCountPair& a,
-                  const NodeTypedCountPair& b) const {
+// Sort functor for sorting bookmark URLs by typed count. We sort in decreasing
+// order of typed count so that the best matches will always be added to the
+// results.
+struct UrlTypedCountPairSortFunctor {
+  bool operator()(const UrlTypedCountPair& a,
+                  const UrlTypedCountPair& b) const {
     return a.second > b.second;
   }
 };
 
-// Extract the const Node* stored in a BookmarkClient::NodeTypedCountPair.
-struct NodeTypedCountPairExtractNodeFunctor {
-  const BookmarkNode* operator()(const NodeTypedCountPair& pair) const {
-    return pair.first;
+// Extract the GURL stored in a BookmarkClient::UrlTypedCountPair and use it to
+// look up the corresponding BookmarkNode.
+class UrlTypedCountPairNodeLookupFunctor {
+ public:
+  explicit UrlTypedCountPairNodeLookupFunctor(UrlNodeMap& url_node_map)
+      : url_node_map_(url_node_map) {
   }
+
+  const BookmarkNode* operator()(const UrlTypedCountPair& pair) const {
+    return url_node_map_[pair.first];
+  }
+
+ private:
+  UrlNodeMap& url_node_map_;
 };
 
 }  // namespace
@@ -149,16 +162,27 @@
 void BookmarkIndex::SortMatches(const NodeSet& matches,
                                 Nodes* sorted_nodes) const {
   sorted_nodes->reserve(matches.size());
-  if (client_->SupportsTypedCountForNodes()) {
-    NodeTypedCountPairs node_typed_counts;
-    client_->GetTypedCountForNodes(matches, &node_typed_counts);
-    std::sort(node_typed_counts.begin(),
-              node_typed_counts.end(),
-              NodeTypedCountPairSortFunctor());
-    std::transform(node_typed_counts.begin(),
-                   node_typed_counts.end(),
+  if (client_->SupportsTypedCountForUrls()) {
+    UrlNodeMap url_node_map;
+    UrlTypedCountMap url_typed_count_map;
+    for (auto node : matches) {
+      url_node_map.insert(std::make_pair(&node->url(), node));
+      url_typed_count_map.insert(std::make_pair(&node->url(), 0));
+    }
+
+    client_->GetTypedCountForUrls(&url_typed_count_map);
+
+    UrlTypedCountPairs url_typed_counts;
+    std::copy(url_typed_count_map.begin(),
+              url_typed_count_map.end(),
+              std::back_inserter(url_typed_counts));
+    std::sort(url_typed_counts.begin(),
+              url_typed_counts.end(),
+              UrlTypedCountPairSortFunctor());
+    std::transform(url_typed_counts.begin(),
+                   url_typed_counts.end(),
                    std::back_inserter(*sorted_nodes),
-                   NodeTypedCountPairExtractNodeFunctor());
+                   UrlTypedCountPairNodeLookupFunctor(url_node_map));
   } else {
     sorted_nodes->insert(sorted_nodes->end(), matches.begin(), matches.end());
   }
@@ -169,6 +193,9 @@
     query_parser::QueryParser* parser,
     const query_parser::QueryNodeVector& query_nodes,
     std::vector<BookmarkMatch>* results) {
+  if (!node) {
+    return;
+  }
   // Check that the result matches the query.  The previous search
   // was a simple per-word search, while the more complex matching
   // of QueryParser may filter it out.  For example, the query
diff --git a/components/bookmarks/browser/bookmark_index_unittest.cc b/components/bookmarks/browser/bookmark_index_unittest.cc
index 3e23cc38..b476d86b 100644
--- a/components/bookmarks/browser/bookmark_index_unittest.cc
+++ b/components/bookmarks/browser/bookmark_index_unittest.cc
@@ -34,19 +34,19 @@
   BookmarkClientMock(const std::map<GURL, int>& typed_count_map)
       : typed_count_map_(typed_count_map) {}
 
-  bool SupportsTypedCountForNodes() override { return true; }
+  bool SupportsTypedCountForUrls() override { return true; }
 
-  void GetTypedCountForNodes(
-      const NodeSet& nodes,
-      NodeTypedCountPairs* node_typed_count_pairs) override {
-    for (NodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
-      const BookmarkNode* node = *it;
-      std::map<GURL, int>::const_iterator found =
-          typed_count_map_.find(node->url());
+  void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map) override {
+    for (auto& url_typed_count_pair : *url_typed_count_map) {
+      const GURL* url = url_typed_count_pair.first;
+      if (!url)
+        continue;
+
+      auto found = typed_count_map_.find(*url);
       if (found == typed_count_map_.end())
         continue;
 
-      node_typed_count_pairs->push_back(std::make_pair(node, found->second));
+      url_typed_count_pair.second = found->second;
     }
   }
 
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index aa5c627..00f400d 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -52,15 +52,15 @@
 using sync_bookmarks::BookmarkChangeProcessor;
 using sync_bookmarks::BookmarkDataTypeController;
 using sync_bookmarks::BookmarkModelAssociator;
+using sync_sessions::SessionDataTypeController;
+using syncer::AsyncDirectoryTypeController;
 using syncer::DataTypeController;
 using syncer::DataTypeManager;
 using syncer::DataTypeManagerImpl;
 using syncer::DataTypeManagerObserver;
 using syncer::DeviceInfoDataTypeController;
-using syncer::ProxyDataTypeController;
 using syncer::ModelTypeController;
-using syncer::AsyncDirectoryTypeController;
-using sync_sessions::SessionDataTypeController;
+using syncer::ProxyDataTypeController;
 
 namespace browser_sync {
 
@@ -297,14 +297,16 @@
 }
 
 DataTypeManager* ProfileSyncComponentsFactoryImpl::CreateDataTypeManager(
+    syncer::ModelTypeSet initial_types,
     const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
         debug_info_listener,
     const DataTypeController::TypeMap* controllers,
     const syncer::DataTypeEncryptionHandler* encryption_handler,
-    syncer::SyncEngine* engine,
+    syncer::ModelTypeConfigurer* configurer,
     DataTypeManagerObserver* observer) {
-  return new DataTypeManagerImpl(debug_info_listener, controllers,
-                                 encryption_handler, engine, observer);
+  return new DataTypeManagerImpl(initial_types, debug_info_listener,
+                                 controllers, encryption_handler, configurer,
+                                 observer);
 }
 
 syncer::SyncEngine* ProfileSyncComponentsFactoryImpl::CreateSyncEngine(
diff --git a/components/browser_sync/profile_sync_components_factory_impl.h b/components/browser_sync/profile_sync_components_factory_impl.h
index ab96295..5b70ca2 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/components/browser_sync/profile_sync_components_factory_impl.h
@@ -65,11 +65,12 @@
       syncer::SyncService* sync_service,
       const RegisterDataTypesMethod& register_platform_types_method) override;
   syncer::DataTypeManager* CreateDataTypeManager(
+      syncer::ModelTypeSet initial_types,
       const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
           debug_info_listener,
       const syncer::DataTypeController::TypeMap* controllers,
       const syncer::DataTypeEncryptionHandler* encryption_handler,
-      syncer::SyncEngine* engine,
+      syncer::ModelTypeConfigurer* configurer,
       syncer::DataTypeManagerObserver* observer) override;
   syncer::SyncEngine* CreateSyncEngine(
       const std::string& name,
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 67e30c88..b4a269f 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -964,49 +964,8 @@
   }
 }
 
-void ProfileSyncService::PostEngineInitialization() {
-  if (protocol_event_observers_.might_have_observers()) {
-    engine_->RequestBufferedProtocolEventsAndEnableForwarding();
-  }
-
-  if (type_debug_info_observers_.might_have_observers()) {
-    engine_->EnableDirectoryTypeDebugInfoForwarding();
-  }
-
-  // If we have a cached passphrase use it to decrypt/encrypt data now that the
-  // engine is initialized. We want to call this before notifying observers in
-  // case this operation affects the "passphrase required" status.
-  ConsumeCachedPassphraseIfPossible();
-
-  // The very first time the engine initializes is effectively the first time
-  // we can say we successfully "synced".  LastSyncedTime will only be null in
-  // this case, because the pref wasn't restored on StartUp.
-  if (sync_prefs_.GetLastSyncedTime().is_null()) {
-    UpdateLastSyncedTime();
-  }
-
-  // Auto-start means IsFirstSetupComplete gets set automatically.
-  if (start_behavior_ == AUTO_START && !IsFirstSetupComplete()) {
-    // This will trigger a configure if it completes setup.
-    SetFirstSetupComplete();
-  } else if (CanConfigureDataTypes()) {
-    ConfigureDataTypeManager();
-  }
-
-  // Check for a cookie jar mismatch.
-  std::vector<gaia::ListedAccount> accounts;
-  std::vector<gaia::ListedAccount> signed_out_accounts;
-  GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
-  if (gaia_cookie_manager_service_ &&
-      gaia_cookie_manager_service_->ListAccounts(
-          &accounts, &signed_out_accounts, "ChromiumProfileSyncService")) {
-    OnGaiaAccountsInCookieUpdated(accounts, signed_out_accounts, error);
-  }
-
-  NotifyObservers();
-}
-
 void ProfileSyncService::OnEngineInitialized(
+    ModelTypeSet initial_types,
     const syncer::WeakHandle<syncer::JsBackend>& js_backend,
     const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
         debug_info_listener,
@@ -1047,7 +1006,50 @@
   local_device_->Initialize(cache_guid, signin_scoped_device_id,
                             blocking_pool_);
 
-  PostEngineInitialization();
+  if (protocol_event_observers_.might_have_observers()) {
+    engine_->RequestBufferedProtocolEventsAndEnableForwarding();
+  }
+
+  if (type_debug_info_observers_.might_have_observers()) {
+    engine_->EnableDirectoryTypeDebugInfoForwarding();
+  }
+
+  // If we have a cached passphrase use it to decrypt/encrypt data now that the
+  // backend is initialized. We want to call this before notifying observers in
+  // case this operation affects the "passphrase required" status.
+  ConsumeCachedPassphraseIfPossible();
+
+  // The very first time the backend initializes is effectively the first time
+  // we can say we successfully "synced".  LastSyncedTime will only be null in
+  // this case, because the pref wasn't restored on StartUp.
+  if (sync_prefs_.GetLastSyncedTime().is_null()) {
+    UpdateLastSyncedTime();
+  }
+
+  data_type_manager_.reset(
+      sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
+          initial_types, debug_info_listener_, &data_type_controllers_, this,
+          engine_.get(), this));
+
+  // Auto-start means IsFirstSetupComplete gets set automatically.
+  if (start_behavior_ == AUTO_START && !IsFirstSetupComplete()) {
+    // This will trigger a configure if it completes setup.
+    SetFirstSetupComplete();
+  } else if (CanConfigureDataTypes()) {
+    ConfigureDataTypeManager();
+  }
+
+  // Check for a cookie jar mismatch.
+  std::vector<gaia::ListedAccount> accounts;
+  std::vector<gaia::ListedAccount> signed_out_accounts;
+  GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
+  if (gaia_cookie_manager_service_ &&
+      gaia_cookie_manager_service_->ListAccounts(
+          &accounts, &signed_out_accounts, "ChromiumProfileSyncService")) {
+    OnGaiaAccountsInCookieUpdated(accounts, signed_out_accounts, error);
+  }
+
+  NotifyObservers();
 }
 
 void ProfileSyncService::OnSyncCycleCompleted() {
@@ -1831,12 +1833,8 @@
   }
 
   bool restart = false;
-  if (!data_type_manager_) {
+  if (!migrator_) {
     restart = true;
-    data_type_manager_.reset(
-        sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
-            debug_info_listener_, &data_type_controllers_, this, engine_.get(),
-            this));
 
     // We create the migrator at the same time.
     migrator_ = base::MakeUnique<BackendMigrator>(
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index ca82b84..3fb2c5b 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -359,6 +359,7 @@
 
   // SyncEngineHost implementation.
   void OnEngineInitialized(
+      syncer::ModelTypeSet initial_types,
       const syncer::WeakHandle<syncer::JsBackend>& js_backend,
       const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
           debug_info_listener,
@@ -716,9 +717,6 @@
   // Update UMA for syncing engine.
   void UpdateEngineInitUMA(bool success);
 
-  // Various setup following engine initialization, mostly for syncing engine.
-  void PostEngineInitialization();
-
   // Whether sync has been authenticated with an account ID.
   bool IsSignedIn() const;
 
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc
index 7304cda..77c09e3 100644
--- a/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -370,8 +370,8 @@
 };
 
 ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) {
-  return new syncer::DataTypeManagerImpl(debug_listener, arg1, arg2, arg3,
-                                         arg4);
+  return new syncer::DataTypeManagerImpl(arg0, debug_listener, arg2, arg3, arg4,
+                                         arg5);
 }
 
 class MockPersonalDataManager : public PersonalDataManager {
@@ -476,7 +476,7 @@
     CreateSyncService(std::move(sync_client_owned_), callback);
 
     EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
-                CreateDataTypeManager(_, _, _, _, _))
+                CreateDataTypeManager(_, _, _, _, _, _))
         .WillOnce(ReturnNewDataTypeManagerWithDebugListener(
             syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr())));
 
diff --git a/components/browser_sync/profile_sync_service_mock.h b/components/browser_sync/profile_sync_service_mock.h
index 5e8c5ca..02ea586 100644
--- a/components/browser_sync/profile_sync_service_mock.h
+++ b/components/browser_sync/profile_sync_service_mock.h
@@ -33,9 +33,10 @@
 
   virtual ~ProfileSyncServiceMock();
 
-  MOCK_METHOD4(
+  MOCK_METHOD5(
       OnEngineInitialized,
-      void(const syncer::WeakHandle<syncer::JsBackend>&,
+      void(syncer::ModelTypeSet initial_types,
+           const syncer::WeakHandle<syncer::JsBackend>&,
            const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&,
            const std::string&,
            bool));
diff --git a/components/browser_sync/profile_sync_service_startup_unittest.cc b/components/browser_sync/profile_sync_service_startup_unittest.cc
index 1ff7cdfa..dae10f1 100644
--- a/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -131,7 +131,7 @@
 
   DataTypeManagerMock* SetUpDataTypeManager() {
     DataTypeManagerMock* data_type_manager = new DataTypeManagerMock();
-    EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+    EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
         .WillOnce(Return(data_type_manager));
     return data_type_manager;
   }
@@ -213,7 +213,7 @@
 
   // Should not actually start, rather just clean things up and wait
   // to be enabled.
-  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
       .Times(0);
   EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
   sync_service_->Initialize();
@@ -275,7 +275,7 @@
 }
 
 TEST_F(ProfileSyncServiceStartupCrosTest, StartCrosNoCredentials) {
-  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
       .Times(0);
   EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _)).Times(0);
   pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
@@ -389,7 +389,7 @@
 
   // Disable sync through policy.
   pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
-  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
       .Times(0);
   EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
 
@@ -424,7 +424,7 @@
   // not start automatically because IsFirstSetupComplete() will be false.
   // A new DataTypeManager should not be created.
   Mock::VerifyAndClearExpectations(data_type_manager);
-  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+  EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
       .Times(0);
   pref_service()->ClearPref(syncer::prefs::kSyncManaged);
   EXPECT_FALSE(sync_service_->IsEngineInitialized());
diff --git a/components/browser_sync/profile_sync_service_typed_url_unittest.cc b/components/browser_sync/profile_sync_service_typed_url_unittest.cc
index bab7b5b..da6c619 100644
--- a/components/browser_sync/profile_sync_service_typed_url_unittest.cc
+++ b/components/browser_sync/profile_sync_service_typed_url_unittest.cc
@@ -61,7 +61,7 @@
 static const int EXPIRED_VISIT = -1;
 
 ACTION(ReturnNewDataTypeManager) {
-  return new syncer::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4);
+  return new syncer::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4, arg5);
 }
 
 class HistoryBackendMock : public HistoryBackend {
@@ -249,7 +249,7 @@
       signin->SetAuthenticatedAccountInfo("gaia_id", "test");
       CreateSyncService(std::move(sync_client_), callback);
       EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
-                  CreateDataTypeManager(_, _, _, _, _))
+                  CreateDataTypeManager(_, _, _, _, _, _))
           .WillOnce(ReturnNewDataTypeManager());
 
       profile_sync_service_bundle()->auth_service()->UpdateCredentials(
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index 824304b1..a074ec3b 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -310,7 +310,7 @@
   void ExpectDataTypeManagerCreation(
       int times,
       const FakeDataTypeManager::ConfigureCalled& callback) {
-    EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _))
+    EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
         .Times(times)
         .WillRepeatedly(ReturnNewDataTypeManager(callback));
   }
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc
index 78a12a2..8a7587b 100644
--- a/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -300,7 +300,7 @@
 
     std::unique_ptr<base::Value> value = base::JSONReader::Read(
         pattern_filter_json, base::JSON_ALLOW_TRAILING_COMMAS);
-    if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!value || !value->IsType(base::Value::Type::DICTIONARY)) {
       VLOG(1) << "Ignoring invalid certificate auto select setting. Reason:"
                  " Invalid JSON object: " << pattern_filter_json;
       continue;
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index e1919936..0306c58 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -50,7 +50,7 @@
 
   // TODO(raymes): We should permit different types of base::Value for
   // website settings.
-  return value->GetType() == base::Value::TYPE_DICTIONARY;
+  return value->GetType() == base::Value::Type::DICTIONARY;
 }
 
 }  // namespace
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index 0cb4f39..16e073e 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -431,7 +431,11 @@
   content_settings::SettingInfo info;
   std::unique_ptr<base::Value> v = GetWebsiteSettingInternal(
       primary_url, secondary_url, type, std::string(), &info);
-  DCHECK_EQ(content_settings::SETTING_SOURCE_USER, info.source);
+  if (info.source != content_settings::SETTING_SOURCE_USER) {
+    // Return an invalid pattern if the current setting is not a user setting
+    // and thus can't be changed.
+    return content_settings::PatternPair();
+  }
 
   content_settings::PatternPair patterns = GetPatternsForContentSettingsType(
       primary_url, secondary_url, type);
diff --git a/components/content_settings/core/browser/website_settings_info.cc b/components/content_settings/core/browser/website_settings_info.cc
index 8ee4a32..e793208 100644
--- a/components/content_settings/core/browser/website_settings_info.cc
+++ b/components/content_settings/core/browser/website_settings_info.cc
@@ -48,7 +48,7 @@
   // TODO(raymes): We should migrate the underlying pref to be a dictionary
   // rather than an int.
   DCHECK(!initial_default_value_ ||
-         initial_default_value_->IsType(base::Value::TYPE_INTEGER));
+         initial_default_value_->IsType(base::Value::Type::INTEGER));
 }
 
 WebsiteSettingsInfo::~WebsiteSettingsInfo() {}
diff --git a/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java b/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
index 4d9ae5e..570738fe 100644
--- a/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
+++ b/components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java
@@ -13,6 +13,12 @@
 /**
  * Information about a finished request. Passed to {@link RequestFinishedInfo.Listener}.
  *
+ * To associate the data with the original request, use
+ * {@link ExperimentalUrlRequest.Builder#addRequestAnnotation} or
+ * {@link ExperimentalBidirectionalStream.Builder#addRequestAnnotation} to add a unique identifier
+ * when creating the request, and call {@link #getAnnotations} when the {@link RequestFinishedInfo}
+ * is received to retrieve the identifier.
+ *
  * {@hide} as it's a prototype.
  */
 public abstract class RequestFinishedInfo {
@@ -232,14 +238,14 @@
          * collected.
          */
         @Nullable
-        public abstract Long getSentBytesCount();
+        public abstract Long getSentByteCount();
 
         /**
          * Returns total bytes received over the network transport layer, or {@code null} if not
          * collected. Number of bytes does not include any previous redirects.
          */
         @Nullable
-        public abstract Long getReceivedBytesCount();
+        public abstract Long getReceivedByteCount();
     }
 
     /**
@@ -257,11 +263,22 @@
      */
     public static final int CANCELED = 2;
 
-
-    /** Returns the request's original URL. */
+    /**
+     * Returns the request's original URL.
+     *
+     * @return the request's original URL
+     */
     public abstract String getUrl();
 
-    /** Returns the objects that the caller has supplied when initiating the request. */
+    /**
+     * Returns the objects that the caller has supplied when initiating the request, using
+     * {@link ExperimentalUrlRequest.Builder#addRequestAnnotation} or
+     * {@link ExperimentalBidirectionalStream.Builder#addRequestAnnotation}.
+     * Annotations can be used to associate a {@link RequestFinishedInfo} with the original request
+     * or type of request.
+     *
+     * @return annotations supplied when creating the request
+     */
     public abstract Collection<Object> getAnnotations();
 
     // TODO(klm): Collect and return a chain of Metrics objects for redirect responses.
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java b/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
index 3b39585..90f17c4 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
@@ -116,5 +116,5 @@
      * {@link UrlRequest.Callback#onCanceled onCanceled()} is called.
      * @return a minimum count of bytes received from the network to process this request.
      */
-    public abstract long getReceivedBytesCount();
+    public abstract long getReceivedByteCount();
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
index 780fbe2c7..de0a2e71 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
@@ -491,10 +491,10 @@
     @SuppressWarnings("unused")
     @CalledByNative
     private void onResponseHeadersReceived(int httpStatusCode, String negotiatedProtocol,
-            String[] headers, long receivedBytesCount) {
+            String[] headers, long receivedByteCount) {
         try {
             mResponseInfo = prepareResponseInfoOnNetworkThread(
-                    httpStatusCode, negotiatedProtocol, headers, receivedBytesCount);
+                    httpStatusCode, negotiatedProtocol, headers, receivedByteCount);
         } catch (Exception e) {
             failWithException(new CronetExceptionImpl("Cannot prepare ResponseInfo", null));
             return;
@@ -521,8 +521,8 @@
     @SuppressWarnings("unused")
     @CalledByNative
     private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
-            int initialLimit, long receivedBytesCount) {
-        mResponseInfo.setReceivedBytesCount(receivedBytesCount);
+            int initialLimit, long receivedByteCount) {
+        mResponseInfo.setReceivedByteCount(receivedByteCount);
         if (byteBuffer.position() != initialPosition || byteBuffer.limit() != initialLimit) {
             failWithException(
                     new CronetExceptionImpl("ByteBuffer modified externally during read", null));
@@ -592,9 +592,9 @@
     @SuppressWarnings("unused")
     @CalledByNative
     private void onError(int errorCode, int nativeError, int nativeQuicError, String errorString,
-            long receivedBytesCount) {
+            long receivedByteCount) {
         if (mResponseInfo != null) {
-            mResponseInfo.setReceivedBytesCount(receivedBytesCount);
+            mResponseInfo.setReceivedByteCount(receivedByteCount);
         }
         if (errorCode == NetworkException.ERROR_QUIC_PROTOCOL_FAILED) {
             failWithException(
@@ -631,16 +631,16 @@
     private void onMetricsCollected(long requestStartMs, long dnsStartMs, long dnsEndMs,
             long connectStartMs, long connectEndMs, long sslStartMs, long sslEndMs,
             long sendingStartMs, long sendingEndMs, long pushStartMs, long pushEndMs,
-            long responseStartMs, long requestEndMs, boolean socketReused, long sentBytesCount,
-            long receivedBytesCount) {
+            long responseStartMs, long requestEndMs, boolean socketReused, long sentByteCount,
+            long receivedByteCount) {
         synchronized (mNativeStreamLock) {
             if (mMetrics != null) {
                 throw new IllegalStateException("Metrics collection should only happen once.");
             }
             mMetrics = new CronetMetrics(requestStartMs, dnsStartMs, dnsEndMs, connectStartMs,
                     connectEndMs, sslStartMs, sslEndMs, sendingStartMs, sendingEndMs, pushStartMs,
-                    pushEndMs, responseStartMs, requestEndMs, socketReused, sentBytesCount,
-                    receivedBytesCount);
+                    pushEndMs, responseStartMs, requestEndMs, socketReused, sentByteCount,
+                    receivedByteCount);
             assert mReadState == mWriteState;
             assert (mReadState == State.SUCCESS) || (mReadState == State.ERROR)
                     || (mReadState == State.CANCELED);
@@ -722,11 +722,11 @@
     }
 
     private UrlResponseInfoImpl prepareResponseInfoOnNetworkThread(int httpStatusCode,
-            String negotiatedProtocol, String[] headers, long receivedBytesCount) {
+            String negotiatedProtocol, String[] headers, long receivedByteCount) {
         UrlResponseInfoImpl responseInfo =
                 new UrlResponseInfoImpl(Arrays.asList(mInitialUrl), httpStatusCode, "",
                         headersListFromStrings(headers), false, negotiatedProtocol, null);
-        responseInfo.setReceivedBytesCount(receivedBytesCount);
+        responseInfo.setReceivedByteCount(receivedByteCount);
         return responseInfo;
     }
 
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java
index fc5a14b..9a02eec6 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java
@@ -38,9 +38,9 @@
     @Nullable
     private final Long mTotalTimeMs;
     @Nullable
-    private final Long mSentBytesCount;
+    private final Long mSentByteCount;
     @Nullable
-    private final Long mReceivedBytesCount;
+    private final Long mReceivedByteCount;
 
     @Nullable
     private static Date toDate(long timestamp) {
@@ -61,11 +61,11 @@
      * TODO(mgersh): Delete after the switch to the new API http://crbug.com/629194
      */
     public CronetMetrics(@Nullable Long ttfbMs, @Nullable Long totalTimeMs,
-            @Nullable Long sentBytesCount, @Nullable Long receivedBytesCount) {
+            @Nullable Long sentByteCount, @Nullable Long receivedByteCount) {
         mTtfbMs = ttfbMs;
         mTotalTimeMs = totalTimeMs;
-        mSentBytesCount = sentBytesCount;
-        mReceivedBytesCount = receivedBytesCount;
+        mSentByteCount = sentByteCount;
+        mReceivedByteCount = receivedByteCount;
 
         // Everything else is -1 (translates to null) for now
         mRequestStartMs = -1;
@@ -90,7 +90,7 @@
     public CronetMetrics(long requestStartMs, long dnsStartMs, long dnsEndMs, long connectStartMs,
             long connectEndMs, long sslStartMs, long sslEndMs, long sendingStartMs,
             long sendingEndMs, long pushStartMs, long pushEndMs, long responseStartMs,
-            long requestEndMs, boolean socketReused, long sentBytesCount, long receivedBytesCount) {
+            long requestEndMs, boolean socketReused, long sentByteCount, long receivedByteCount) {
         // Check that no end times are before corresponding start times,
         // or exist when start time doesn't.
         assert checkOrder(dnsStartMs, dnsEndMs);
@@ -119,8 +119,8 @@
         mResponseStartMs = responseStartMs;
         mRequestEndMs = requestEndMs;
         mSocketReused = socketReused;
-        mSentBytesCount = sentBytesCount;
-        mReceivedBytesCount = receivedBytesCount;
+        mSentByteCount = sentByteCount;
+        mReceivedByteCount = receivedByteCount;
 
         // TODO(mgersh): delete these after embedders stop using them http://crbug.com/629194
         if (requestStartMs != -1 && responseStartMs != -1) {
@@ -216,12 +216,12 @@
     }
 
     @Nullable
-    public Long getSentBytesCount() {
-        return mSentBytesCount;
+    public Long getSentByteCount() {
+        return mSentByteCount;
     }
 
     @Nullable
-    public Long getReceivedBytesCount() {
-        return mReceivedBytesCount;
+    public Long getReceivedByteCount() {
+        return mReceivedByteCount;
     }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
index 56ecf16..eb2abaf 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
@@ -73,7 +73,7 @@
      * mCallback.onRedirectReceived is called.
      */
     private final List<String> mUrlChain = new ArrayList<String>();
-    private long mReceivedBytesCountFromRedirects;
+    private long mReceivedByteCountFromRedirects;
 
     private final VersionSafeCallbacks.UrlRequestCallback mCallback;
     private final String mInitialUrl;
@@ -486,7 +486,7 @@
      *
      * @param newLocation Location where request is redirected.
      * @param httpStatusCode from redirect response
-     * @param receivedBytesCount count of bytes received for redirect response
+     * @param receivedByteCount count of bytes received for redirect response
      * @param headers an array of response headers with keys at the even indices
      *         followed by the corresponding values at the odd indices.
      */
@@ -494,11 +494,11 @@
     @CalledByNative
     private void onRedirectReceived(final String newLocation, int httpStatusCode,
             String httpStatusText, String[] headers, boolean wasCached, String negotiatedProtocol,
-            String proxyServer, long receivedBytesCount) {
+            String proxyServer, long receivedByteCount) {
         final UrlResponseInfoImpl responseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode,
                 httpStatusText, headers, wasCached, negotiatedProtocol, proxyServer);
-        mReceivedBytesCountFromRedirects += receivedBytesCount;
-        responseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects);
+        mReceivedByteCountFromRedirects += receivedByteCount;
+        responseInfo.setReceivedByteCount(mReceivedByteCountFromRedirects);
 
         // Have to do this after creating responseInfo.
         mUrlChain.add(newLocation);
@@ -571,13 +571,13 @@
      * @param initialLimit Original limit of byteBuffer when passed to
      *        read(). Used as a minimal check that the buffer hasn't been
      *        modified while reading from the network.
-     * @param receivedBytesCount number of bytes received.
+     * @param receivedByteCount number of bytes received.
      */
     @SuppressWarnings("unused")
     @CalledByNative
     private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
-            int initialLimit, long receivedBytesCount) {
-        mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + receivedBytesCount);
+            int initialLimit, long receivedByteCount) {
+        mResponseInfo.setReceivedByteCount(mReceivedByteCountFromRedirects + receivedByteCount);
         if (byteBuffer.position() != initialPosition || byteBuffer.limit() != initialLimit) {
             failWithException(
                     new CronetExceptionImpl("ByteBuffer modified externally during read", null));
@@ -595,13 +595,13 @@
      * Called when request is completed successfully, no callbacks will be
      * called afterwards.
      *
-     * @param receivedBytesCount number of bytes received.
+     * @param receivedByteCount number of bytes received.
      */
     @SuppressWarnings("unused")
     @CalledByNative
-    private void onSucceeded(long receivedBytesCount) {
+    private void onSucceeded(long receivedByteCount) {
         mFinishedReason = RequestFinishedInfo.SUCCEEDED;
-        mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + receivedBytesCount);
+        mResponseInfo.setReceivedByteCount(mReceivedByteCountFromRedirects + receivedByteCount);
         Runnable task = new Runnable() {
             @Override
             public void run() {
@@ -631,16 +631,15 @@
      *                  NetworkException.ERROR_*}.
      * @param nativeError native net error code.
      * @param errorString textual representation of the error code.
-     * @param receivedBytesCount number of bytes received.
+     * @param receivedByteCount number of bytes received.
      */
     @SuppressWarnings("unused")
     @CalledByNative
     private void onError(int errorCode, int nativeError, int nativeQuicError, String errorString,
-            long receivedBytesCount) {
+            long receivedByteCount) {
         mFinishedReason = RequestFinishedInfo.FAILED;
         if (mResponseInfo != null) {
-            mResponseInfo.setReceivedBytesCount(
-                    mReceivedBytesCountFromRedirects + receivedBytesCount);
+            mResponseInfo.setReceivedByteCount(mReceivedByteCountFromRedirects + receivedByteCount);
         }
         if (errorCode == NetworkException.ERROR_QUIC_PROTOCOL_FAILED) {
             failWithException(new QuicExceptionImpl(
@@ -697,16 +696,16 @@
     private void onMetricsCollected(long requestStartMs, long dnsStartMs, long dnsEndMs,
             long connectStartMs, long connectEndMs, long sslStartMs, long sslEndMs,
             long sendingStartMs, long sendingEndMs, long pushStartMs, long pushEndMs,
-            long responseStartMs, long requestEndMs, boolean socketReused, long sentBytesCount,
-            long receivedBytesCount) {
+            long responseStartMs, long requestEndMs, boolean socketReused, long sentByteCount,
+            long receivedByteCount) {
         synchronized (mUrlRequestAdapterLock) {
             if (mMetrics != null) {
                 throw new IllegalStateException("Metrics collection should only happen once.");
             }
             mMetrics = new CronetMetrics(requestStartMs, dnsStartMs, dnsEndMs, connectStartMs,
                     connectEndMs, sslStartMs, sslEndMs, sendingStartMs, sendingEndMs, pushStartMs,
-                    pushEndMs, responseStartMs, requestEndMs, socketReused, sentBytesCount,
-                    receivedBytesCount);
+                    pushEndMs, responseStartMs, requestEndMs, socketReused, sentByteCount,
+                    receivedByteCount);
         }
         mRequestContext.reportFinished(getRequestFinishedInfo());
     }
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java
index 7a98436..9eaca26b 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java
@@ -28,7 +28,7 @@
     private final boolean mWasCached;
     private final String mNegotiatedProtocol;
     private final String mProxyServer;
-    private final AtomicLong mReceivedBytesCount;
+    private final AtomicLong mReceivedByteCount;
     private final HeaderBlockImpl mHeaders;
 
     /**
@@ -90,7 +90,7 @@
         mWasCached = wasCached;
         mNegotiatedProtocol = negotiatedProtocol;
         mProxyServer = proxyServer;
-        mReceivedBytesCount = new AtomicLong();
+        mReceivedByteCount = new AtomicLong();
     }
 
     @Override
@@ -139,26 +139,26 @@
     }
 
     @Override
-    public long getReceivedBytesCount() {
-        return mReceivedBytesCount.get();
+    public long getReceivedByteCount() {
+        return mReceivedByteCount.get();
     }
 
     @Override
     public String toString() {
         return String.format(Locale.ROOT, "UrlResponseInfo@[%s][%s]: urlChain = %s, "
                         + "httpStatus = %d %s, headers = %s, wasCached = %b, "
-                        + "negotiatedProtocol = %s, proxyServer= %s, receivedBytesCount = %d",
+                        + "negotiatedProtocol = %s, proxyServer= %s, receivedByteCount = %d",
                 // Prevent asserting on the contents of this string
                 Integer.toHexString(System.identityHashCode(this)), getUrl(),
                 getUrlChain().toString(), getHttpStatusCode(), getHttpStatusText(),
                 getAllHeadersAsList().toString(), wasCached(), getNegotiatedProtocol(),
-                getProxyServer(), getReceivedBytesCount());
+                getProxyServer(), getReceivedByteCount());
     }
 
     /**
-     * Sets mReceivedBytesCount. Must not be called after request completion or cancellation.
+     * Sets mReceivedByteCount. Must not be called after request completion or cancellation.
      */
-    public void setReceivedBytesCount(long currentReceivedBytesCount) {
-        mReceivedBytesCount.set(currentReceivedBytesCount);
+    public void setReceivedByteCount(long currentReceivedByteCount) {
+        mReceivedByteCount.set(currentReceivedByteCount);
     }
 }
diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
index 39359b4..cbd5926 100644
--- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
+++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
@@ -77,7 +77,7 @@
         @Override
         public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
             Log.i(TAG, "****** Request Completed, status code is %d, total received bytes is %d",
-                    info.getHttpStatusCode(), info.getReceivedBytesCount());
+                    info.getHttpStatusCode(), info.getReceivedByteCount());
 
             final String receivedData = mBytesReceived.toString();
             final String url = info.getUrl();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
index fc2fd96..870a3d3 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
@@ -87,11 +87,11 @@
         }
         UrlResponseInfoImpl urlResponseInfo = new UrlResponseInfoImpl(
                 Arrays.asList(urls), statusCode, message, headersList, false, "h2", null);
-        urlResponseInfo.setReceivedBytesCount(receivedBytes);
+        urlResponseInfo.setReceivedByteCount(receivedBytes);
         return urlResponseInfo;
     }
 
-    private void runSimpleGetWithExpectedReceivedBytesCount(int expectedReceivedBytes)
+    private void runSimpleGetWithExpectedReceivedByteCount(int expectedReceivedBytes)
             throws Exception {
         String url = Http2TestServer.getEchoMethodUrl();
         TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCallback();
@@ -122,13 +122,13 @@
     @Feature({"Cronet"})
     public void testBuilderCheck() throws Exception {
         if (testingJavaImpl()) {
-            testBuilderCheckJavaImpl();
+            runBuilderCheckJavaImpl();
         } else {
-            testBuilderCheckNativeImpl();
+            runBuilderCheckNativeImpl();
         }
     }
 
-    private void testBuilderCheckNativeImpl() throws Exception {
+    private void runBuilderCheckNativeImpl() throws Exception {
         TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCallback();
         try {
             mTestFramework.mCronetEngine.newBidirectionalStreamBuilder(
@@ -175,7 +175,7 @@
         }
     }
 
-    private void testBuilderCheckJavaImpl() {
+    private void runBuilderCheckJavaImpl() {
         try {
             TestBidirectionalStreamCallback callback = new TestBidirectionalStreamCallback();
             mTestFramework.mCronetEngine.newBidirectionalStreamBuilder(
@@ -212,7 +212,7 @@
     public void testSimpleGet() throws Exception {
         // Since this is the first request on the connection, the expected received bytes count
         // must account for an HPACK dynamic table size update.
-        runSimpleGetWithExpectedReceivedBytesCount(31);
+        runSimpleGetWithExpectedReceivedByteCount(31);
     }
 
     @SmallTest
@@ -1124,7 +1124,7 @@
         // The expected received bytes count is lower than it would be for the first request on the
         // connection, because the server includes an HPACK dynamic table size update only in the
         // first response HEADERS frame.
-        runSimpleGetWithExpectedReceivedBytesCount(27);
+        runSimpleGetWithExpectedReceivedByteCount(27);
     }
 
     @SmallTest
@@ -1213,8 +1213,8 @@
             // connect timing metrics.
             MetricsTestUtil.checkTimingMetrics(metrics, startTime, endTime);
             MetricsTestUtil.checkHasConnectTiming(metrics, startTime, endTime, true);
-            assertTrue(metrics.getSentBytesCount() > 0);
-            assertTrue(metrics.getReceivedBytesCount() > 0);
+            assertTrue(metrics.getSentByteCount() > 0);
+            assertTrue(metrics.getReceivedByteCount() > 0);
         } else if (failureStep == ResponseStep.ON_STREAM_READY) {
             assertNotNull(metrics.getRequestStart());
             MetricsTestUtil.assertAfter(metrics.getRequestStart(), startTime);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
index 540f12b..addc524f 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestBase.java
@@ -161,7 +161,7 @@
         assertEquals(expected.getUrl(), actual.getUrl());
         // Transferred bytes and proxy server are not supported in pure java
         if (!(mCronetTestFramework.mCronetEngine instanceof JavaCronetEngine)) {
-            assertEquals(expected.getReceivedBytesCount(), actual.getReceivedBytesCount());
+            assertEquals(expected.getReceivedByteCount(), actual.getReceivedByteCount());
             assertEquals(expected.getProxyServer(), actual.getProxyServer());
             // This is a place where behavior intentionally differs between native and java
             assertEquals(expected.getNegotiatedProtocol(), actual.getNegotiatedProtocol());
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index 23ce5ab5..afe7a3c3 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -148,7 +148,7 @@
         }
         UrlResponseInfoImpl unknown = new UrlResponseInfoImpl(
                 Arrays.asList(urls), statusCode, message, headersList, false, "unknown", ":0");
-        unknown.setReceivedBytesCount(receivedBytes);
+        unknown.setReceivedByteCount(receivedBytes);
         return unknown;
     }
 
@@ -636,7 +636,7 @@
                 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithFailure(
                         FailurePhase.READ_SYNC, arbitraryNetError));
         assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals(15, callback.mResponseInfo.getReceivedBytesCount());
+        assertEquals(15, callback.mResponseInfo.getReceivedByteCount());
         assertNotNull(callback.mError);
         assertEquals(arbitraryNetError,
                 ((NetworkException) callback.mError).getCronetInternalErrorCode());
@@ -654,7 +654,7 @@
                 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithFailure(
                         FailurePhase.READ_ASYNC, arbitraryNetError));
         assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals(15, callback.mResponseInfo.getReceivedBytesCount());
+        assertEquals(15, callback.mResponseInfo.getReceivedByteCount());
         assertNotNull(callback.mError);
         assertEquals(arbitraryNetError,
                 ((NetworkException) callback.mError).getCronetInternalErrorCode());
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/MetricsTestUtil.java b/components/cronet/android/test/javatests/src/org/chromium/net/MetricsTestUtil.java
index 8b17427..58751493 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/MetricsTestUtil.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/MetricsTestUtil.java
@@ -172,7 +172,7 @@
         assertNull(metrics.getPushStart());
         assertNull(metrics.getPushEnd());
         // Check data use metrics
-        assertTrue(metrics.getSentBytesCount() > 0);
-        assertTrue(metrics.getReceivedBytesCount() > 0);
+        assertTrue(metrics.getSentByteCount() > 0);
+        assertTrue(metrics.getReceivedByteCount() > 0);
     }
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
index 9f62b01..a52e877 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
@@ -95,7 +95,7 @@
         assertIsQuic(callback.mResponseInfo);
         // The total received bytes should be larger than the content length, to account for
         // headers.
-        assertTrue(callback.mResponseInfo.getReceivedBytesCount() > expectedContent.length());
+        assertTrue(callback.mResponseInfo.getReceivedByteCount() > expectedContent.length());
         // This test takes a long time, since the update will only be scheduled
         // after kUpdatePrefsDelayMs in http_server_properties_manager.cc.
         while (true) {
@@ -138,7 +138,7 @@
         assertIsQuic(callback.mResponseInfo);
         // The total received bytes should be larger than the content length, to account for
         // headers.
-        assertTrue(callback2.mResponseInfo.getReceivedBytesCount() > expectedContent.length());
+        assertTrue(callback2.mResponseInfo.getReceivedByteCount() > expectedContent.length());
     }
 
     // Returns whether a file contains a particular string.
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
index 9bef6896..1f671a8 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java
@@ -227,8 +227,8 @@
                 metrics.getRequestEnd().before(endTime) || metrics.getRequestEnd().equals(endTime));
         // Entire request should take more than 0 ms
         assertTrue(metrics.getRequestEnd().getTime() - metrics.getRequestStart().getTime() > 0);
-        assertTrue(metrics.getSentBytesCount() == 0);
-        assertTrue(metrics.getReceivedBytesCount() == 0);
+        assertTrue(metrics.getSentByteCount() == 0);
+        assertTrue(metrics.getReceivedByteCount() == 0);
         mTestFramework.mCronetEngine.shutdown();
     }
 
@@ -309,13 +309,12 @@
         long responseStart = 12;
         long requestEnd = 13;
         boolean socketReused = true;
-        long sentBytesCount = 14;
-        long receivedBytesCount = 15;
+        long sentByteCount = 14;
+        long receivedByteCount = 15;
         // Make sure nothing gets reordered inside the Metrics class
         RequestFinishedInfo.Metrics metrics = new CronetMetrics(requestStart, dnsStart, dnsEnd,
                 connectStart, connectEnd, sslStart, sslEnd, sendingStart, sendingEnd, pushStart,
-                pushEnd, responseStart, requestEnd, socketReused, sentBytesCount,
-                receivedBytesCount);
+                pushEnd, responseStart, requestEnd, socketReused, sentByteCount, receivedByteCount);
         assertEquals(new Date(requestStart), metrics.getRequestStart());
         // -1 timestamp should translate to null
         assertNull(metrics.getDnsEnd());
@@ -329,7 +328,7 @@
         assertEquals(new Date(responseStart), metrics.getResponseStart());
         assertEquals(new Date(requestEnd), metrics.getRequestEnd());
         assertEquals(socketReused, metrics.getSocketReused());
-        assertEquals(sentBytesCount, (long) metrics.getSentBytesCount());
-        assertEquals(receivedBytesCount, (long) metrics.getReceivedBytesCount());
+        assertEquals(sentByteCount, (long) metrics.getSentByteCount());
+        assertEquals(receivedByteCount, (long) metrics.getReceivedByteCount());
     }
 }
diff --git a/components/cryptauth/BUILD.gn b/components/cryptauth/BUILD.gn
index a0dff7c..1bfdc1b 100644
--- a/components/cryptauth/BUILD.gn
+++ b/components/cryptauth/BUILD.gn
@@ -29,8 +29,12 @@
     "cryptauth_gcm_manager.h",
     "cryptauth_gcm_manager_impl.cc",
     "cryptauth_gcm_manager_impl.h",
+    "eid_generator.cc",
+    "eid_generator.h",
     "pref_names.cc",
     "pref_names.h",
+    "remote_device.cc",
+    "remote_device.h",
     "secure_message_delegate.cc",
     "secure_message_delegate.h",
     "switches.cc",
@@ -92,6 +96,7 @@
     "cryptauth_enroller_impl_unittest.cc",
     "cryptauth_enrollment_manager_unittest.cc",
     "cryptauth_gcm_manager_impl_unittest.cc",
+    "eid_generator_unittest.cc",
     "fake_secure_message_delegate_unittest.cc",
     "sync_scheduler_impl_unittest.cc",
   ]
diff --git a/components/cryptauth/DEPS b/components/cryptauth/DEPS
index 3de522e..e6c99c9 100644
--- a/components/cryptauth/DEPS
+++ b/components/cryptauth/DEPS
@@ -1,8 +1,8 @@
 include_rules = [
-  "+crypto",
   "+components/gcm_driver",
   "+components/prefs",
   "+components/proximity_auth",
+  "+crypto",
   "+google_apis",
   "+net",
 ]
diff --git a/components/cryptauth/cryptauth_device_manager.cc b/components/cryptauth/cryptauth_device_manager.cc
index c3051dac..420cabc0 100644
--- a/components/cryptauth/cryptauth_device_manager.cc
+++ b/components/cryptauth/cryptauth_device_manager.cc
@@ -5,11 +5,12 @@
 #include "components/cryptauth/cryptauth_device_manager.h"
 
 #include <stddef.h>
-
+#include <stdexcept>
 #include <utility>
 
 #include "base/base64url.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "components/cryptauth/cryptauth_client.h"
 #include "components/cryptauth/pref_names.h"
 #include "components/cryptauth/sync_scheduler_impl.h"
@@ -37,67 +38,249 @@
 const char kExternalDeviceKeyPublicKey[] = "public_key";
 const char kExternalDeviceKeyDeviceName[] = "device_name";
 const char kExternalDeviceKeyBluetoothAddress[] = "bluetooth_address";
+const char kExternalDeviceKeyUnlockKey[] = "unlock_key";
+const char kExternalDeviceKeyUnlockable[] = "unlockable";
+const char kExternalDeviceKeyLastUpdateTimeMillis[] = "last_update_time_millis";
+const char kExternalDeviceKeyMobileHotspotSupported[] =
+    "mobile_hotspot_supported";
+const char kExternalDeviceKeyDeviceType[] = "device_type";
+const char kExternalDeviceKeyBeaconSeeds[] = "beacon_seeds";
+const char kExternalDeviceKeyBeaconSeedData[] = "beacon_seed_data";
+const char kExternalDeviceKeyBeaconSeedStartMs[] = "beacon_seed_start_ms";
+const char kExternalDeviceKeyBeaconSeedEndMs[] = "beacon_seed_end_ms";
+
+// Converts BeaconSeed protos to a list value that can be stored in user prefs.
+std::unique_ptr<base::ListValue> BeaconSeedsToListValue(
+    const google::protobuf::RepeatedPtrField<cryptauth::BeaconSeed>& seeds) {
+  std::unique_ptr<base::ListValue> list(new base::ListValue());
+
+  for (int i = 0; i < seeds.size(); i++) {
+    cryptauth::BeaconSeed seed = seeds.Get(i);
+
+    if (!seed.has_data()
+        || !seed.has_start_time_millis()
+        || !seed.has_end_time_millis()) {
+      PA_LOG(WARNING) << "Unable to serialize BeaconSeed due to missing data; "
+          << "skipping.";
+      continue;
+    }
+
+    std::unique_ptr<base::DictionaryValue> beacon_seed_value(
+        new base::DictionaryValue());
+
+    // Note: Seed data is already base-64 encoded, so there is no need to
+    // convert it.
+    beacon_seed_value->SetString(kExternalDeviceKeyBeaconSeedData, seed.data());
+
+    // Set the timestamps as string representations of their numeric value
+    // since there is no notion of a base::LongValue.
+    beacon_seed_value->SetString(
+        kExternalDeviceKeyBeaconSeedStartMs,
+        std::to_string(seed.start_time_millis()));
+    beacon_seed_value->SetString(
+        kExternalDeviceKeyBeaconSeedEndMs,
+        std::to_string(seed.end_time_millis()));
+
+    list->Append(std::move(beacon_seed_value));
+  }
+
+  return list;
+}
 
 // Converts an unlock key proto to a dictionary that can be stored in user
 // prefs.
 std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
     const cryptauth::ExternalDeviceInfo& device) {
+  // The device public key is a required value.
+  if (!device.has_public_key()) {
+    return nullptr;
+  }
+
   std::unique_ptr<base::DictionaryValue> dictionary(
       new base::DictionaryValue());
 
-  // We store the device information in Base64Url form because dictionary values
-  // must be valid UTF8 strings.
-  std::string public_key_b64, device_name_b64, bluetooth_address_b64;
+  // Note that the device public key, name, and Bluetooth addresses are stored
+  // in Base64Url form because dictionary values must be valid UTF8 strings.
+
+  std::string public_key_b64;
   base::Base64UrlEncode(device.public_key(),
                         base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                         &public_key_b64);
-  base::Base64UrlEncode(device.friendly_device_name(),
-                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
-                        &device_name_b64);
-  base::Base64UrlEncode(device.bluetooth_address(),
+  dictionary->SetString(kExternalDeviceKeyPublicKey, public_key_b64);
+
+  if (device.has_friendly_device_name()) {
+    std::string device_name_b64;
+    base::Base64UrlEncode(device.friendly_device_name(),
+                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                          &device_name_b64);
+    dictionary->SetString(kExternalDeviceKeyDeviceName, device_name_b64);
+  }
+
+  if (device.has_bluetooth_address()) {
+    std::string bluetooth_address_b64;
+    base::Base64UrlEncode(device.bluetooth_address(),
                         base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                         &bluetooth_address_b64);
+    dictionary->SetString(kExternalDeviceKeyBluetoothAddress,
+                          bluetooth_address_b64);
+  }
 
-  dictionary->SetString(kExternalDeviceKeyPublicKey, public_key_b64);
-  dictionary->SetString(kExternalDeviceKeyDeviceName, device_name_b64);
-  dictionary->SetString(kExternalDeviceKeyBluetoothAddress,
-                        bluetooth_address_b64);
+  if (device.has_unlock_key()) {
+    dictionary->SetBoolean(kExternalDeviceKeyUnlockKey,
+                           device.unlock_key());
+  }
+
+  if (device.has_unlockable()) {
+    dictionary->SetBoolean(kExternalDeviceKeyUnlockable,
+                           device.unlockable());
+  }
+
+  if (device.has_last_update_time_millis()) {
+    dictionary->SetString(kExternalDeviceKeyLastUpdateTimeMillis,
+                          std::to_string(device.last_update_time_millis()));
+  }
+
+  if (device.has_mobile_hotspot_supported()) {
+    dictionary->SetBoolean(kExternalDeviceKeyMobileHotspotSupported,
+                           device.mobile_hotspot_supported());
+  }
+
+  if (device.has_device_type() && DeviceType_IsValid(device.device_type())) {
+    dictionary->SetInteger(kExternalDeviceKeyDeviceType,
+                           device.device_type());
+  }
+
+  std::unique_ptr<base::ListValue> beacon_seed_list =
+      BeaconSeedsToListValue(device.beacon_seeds());
+  dictionary->Set(kExternalDeviceKeyBeaconSeeds, std::move(beacon_seed_list));
+
   return dictionary;
 }
 
+void AddBeaconSeedsToExternalDevice(
+    const base::ListValue& beacon_seeds,
+    cryptauth::ExternalDeviceInfo& external_device) {
+  for (size_t i = 0; i < beacon_seeds.GetSize(); i++) {
+    const base::DictionaryValue* seed_dictionary = nullptr;
+    if (!beacon_seeds.GetDictionary(i, &seed_dictionary)) {
+      PA_LOG(WARNING) << "Unable to retrieve BeaconSeed dictionary; "
+          << "skipping.";
+      continue;
+    }
+
+    std::string data, start_time_millis_str, end_time_millis_str;
+    if (!seed_dictionary->GetString(kExternalDeviceKeyBeaconSeedData, &data)
+        || !seed_dictionary->GetString(
+            kExternalDeviceKeyBeaconSeedStartMs,
+            &start_time_millis_str)
+        || !seed_dictionary->GetString(
+            kExternalDeviceKeyBeaconSeedEndMs,
+            &end_time_millis_str)) {
+      PA_LOG(WARNING) << "Unable to deserialize BeaconSeed due to missing "
+          << "data; skipping.";
+      continue;
+    }
+
+    int64_t start_time_millis, end_time_millis;
+    if (!base::StringToInt64(start_time_millis_str, &start_time_millis)
+        || !base::StringToInt64(end_time_millis_str, &end_time_millis)) {
+      PA_LOG(WARNING) << "Unable to convert stored timestamp to int64_t: "
+          << start_time_millis_str << " or " << end_time_millis_str;
+      continue;
+    }
+
+    cryptauth::BeaconSeed* seed = external_device.add_beacon_seeds();
+    seed->set_data(data);
+    seed->set_start_time_millis(start_time_millis);
+    seed->set_end_time_millis(end_time_millis);
+  }
+}
+
 // Converts an unlock key dictionary stored in user prefs to an
 // ExternalDeviceInfo proto. Returns true if the dictionary is valid, and the
 // parsed proto is written to |external_device|.
 bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
                            cryptauth::ExternalDeviceInfo* external_device) {
-  std::string public_key_b64, device_name_b64, bluetooth_address_b64;
-  if (!dictionary.GetString(kExternalDeviceKeyPublicKey, &public_key_b64) ||
-      !dictionary.GetString(kExternalDeviceKeyDeviceName, &device_name_b64) ||
-      !dictionary.GetString(kExternalDeviceKeyBluetoothAddress,
-                            &bluetooth_address_b64)) {
+  std::string public_key_b64;
+  if (!dictionary.GetString(kExternalDeviceKeyPublicKey, &public_key_b64)) {
+    // The public key is a required field, so if it is absent, there is no
+    // valid data to return.
     return false;
   }
 
-  // We store the device information in Base64Url form because dictionary values
-  // must be valid UTF8 strings.
-  std::string public_key, device_name, bluetooth_address;
+  std::string public_key;
   if (!base::Base64UrlDecode(public_key_b64,
                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-                             &public_key) ||
-      !base::Base64UrlDecode(device_name_b64,
-                             base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-                             &device_name) ||
-      !base::Base64UrlDecode(bluetooth_address_b64,
-                             base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-                             &bluetooth_address)) {
+                             &public_key)) {
+    // The public key is stored as a Base64Url, so if it cannot be decoded,
+    // there is no valid data to return.
     return false;
   }
-
   external_device->set_public_key(public_key);
-  external_device->set_friendly_device_name(device_name);
-  external_device->set_bluetooth_address(bluetooth_address);
-  external_device->set_unlock_key(true);
-  external_device->set_unlockable(false);
+
+  std::string device_name_b64;
+  if (dictionary.GetString(kExternalDeviceKeyDeviceName, &device_name_b64)) {
+    std::string device_name;
+    if (base::Base64UrlDecode(device_name_b64,
+                               base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+                               &device_name)) {
+      external_device->set_friendly_device_name(device_name);
+    }
+  }
+
+  std::string bluetooth_address_b64;
+  if (dictionary.GetString(
+      kExternalDeviceKeyBluetoothAddress, &bluetooth_address_b64)) {
+    std::string bluetooth_address;
+    if (base::Base64UrlDecode(bluetooth_address_b64,
+                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+                              &bluetooth_address)) {
+      external_device->set_bluetooth_address(bluetooth_address);
+    }
+  }
+
+  bool unlock_key;
+  if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key)) {
+    external_device->set_unlock_key(unlock_key);
+  }
+
+  bool unlockable;
+  if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable)) {
+    external_device->set_unlockable(unlockable);
+  }
+
+  std::string last_update_time_millis_str;
+  if (dictionary.GetString(
+      kExternalDeviceKeyLastUpdateTimeMillis, &last_update_time_millis_str)) {
+    int64_t last_update_time_millis;
+    if (base::StringToInt64(
+        last_update_time_millis_str, &last_update_time_millis)) {
+      external_device->set_last_update_time_millis(last_update_time_millis);
+    } else {
+      PA_LOG(WARNING) << "Unable to convert stored update time to int64_t: "
+          << last_update_time_millis_str;
+    }
+  }
+
+  bool mobile_hotspot_supported;
+  if (dictionary.GetBoolean(
+      kExternalDeviceKeyMobileHotspotSupported, &mobile_hotspot_supported)) {
+    external_device->set_mobile_hotspot_supported(mobile_hotspot_supported);
+  }
+
+  int device_type;
+  if (dictionary.GetInteger(kExternalDeviceKeyDeviceType, &device_type)) {
+    if (DeviceType_IsValid(device_type)) {
+      external_device->set_device_type(static_cast<DeviceType>(device_type));
+    }
+  }
+
+  const base::ListValue* beacon_seeds = nullptr;
+  dictionary.GetList(kExternalDeviceKeyBeaconSeeds, &beacon_seeds);
+  if (beacon_seeds) {
+    AddBeaconSeedsToExternalDevice(*beacon_seeds, *external_device);
+  }
+
   return true;
 }
 
diff --git a/components/cryptauth/cryptauth_device_manager_unittest.cc b/components/cryptauth/cryptauth_device_manager_unittest.cc
index 77a051d0..7b0a46e7 100644
--- a/components/cryptauth/cryptauth_device_manager_unittest.cc
+++ b/components/cryptauth/cryptauth_device_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/simple_test_clock.h"
 #include "components/cryptauth/fake_cryptauth_gcm_manager.h"
@@ -45,15 +46,29 @@
 const char kStoredPublicKey[] = "AAPL";
 const char kStoredDeviceName[] = "iPhone 6";
 const char kStoredBluetoothAddress[] = "12:34:56:78:90:AB";
+const bool kStoredUnlockKey = true;
+const bool kStoredUnlockable = false;
 
 // ExternalDeviceInfo fields for the synced unlock key.
 const char kPublicKey1[] = "GOOG";
 const char kDeviceName1[] = "Nexus 5";
 const char kBluetoothAddress1[] = "aa:bb:cc:ee:dd:ff";
+const char kBeaconSeed1Data[] = "beaconSeed1Data";
+const int64_t kBeaconSeed1StartTime = 123456;
+const int64_t kBeaconSeed1EndTime = 123457;
+const char kBeaconSeed2Data[] = "beaconSeed2Data";
+const int64_t kBeaconSeed2StartTime = 234567;
+const int64_t kBeaconSeed2EndTime = 234568;
 
 // ExternalDeviceInfo fields for a non-synced unlockable device.
 const char kPublicKey2[] = "MSFT";
 const char kDeviceName2[] = "Surface Pro 3";
+const char kBeaconSeed3Data[] = "beaconSeed3Data";
+const int64_t kBeaconSeed3StartTime = 123456;
+const int64_t kBeaconSeed3EndTime = 123457;
+const char kBeaconSeed4Data[] = "beaconSeed4Data";
+const int64_t kBeaconSeed4StartTime = 234567;
+const int64_t kBeaconSeed4EndTime = 234568;
 
 // Validates that |unlock_keys| and the corresponding preferences stored by
 // |pref_service| are equal to |expected_unlock_keys|.
@@ -67,13 +82,64 @@
         base::StringPrintf("Compare protos at index=%d", static_cast<int>(i)));
     const auto& expected_unlock_key = expected_unlock_keys[i];
     const auto& unlock_key = unlock_keys.at(i);
-    EXPECT_EQ(expected_unlock_key.public_key(), unlock_key.public_key());
+    EXPECT_TRUE(expected_unlock_key.has_public_key());
+    EXPECT_TRUE(unlock_key.has_public_key());
+    EXPECT_EQ(expected_unlock_key.public_key(),
+              unlock_key.public_key());
+
+    EXPECT_EQ(expected_unlock_key.has_friendly_device_name(),
+              unlock_key.has_friendly_device_name());
     EXPECT_EQ(expected_unlock_key.friendly_device_name(),
               unlock_key.friendly_device_name());
+
+    EXPECT_EQ(expected_unlock_key.has_bluetooth_address(),
+              unlock_key.has_bluetooth_address());
     EXPECT_EQ(expected_unlock_key.bluetooth_address(),
               unlock_key.bluetooth_address());
-    EXPECT_TRUE(expected_unlock_key.unlock_key());
-    EXPECT_FALSE(expected_unlock_key.unlockable());
+
+    EXPECT_EQ(expected_unlock_key.has_unlock_key(),
+              unlock_key.has_unlock_key());
+    EXPECT_EQ(expected_unlock_key.unlock_key(),
+              unlock_key.unlock_key());
+
+    EXPECT_EQ(expected_unlock_key.has_unlockable(),
+              unlock_key.has_unlockable());
+    EXPECT_EQ(expected_unlock_key.unlockable(),
+              unlock_key.unlockable());
+
+    EXPECT_EQ(expected_unlock_key.has_last_update_time_millis(),
+              unlock_key.has_last_update_time_millis());
+    EXPECT_EQ(expected_unlock_key.last_update_time_millis(),
+              unlock_key.last_update_time_millis());
+
+    EXPECT_EQ(expected_unlock_key.has_mobile_hotspot_supported(),
+              unlock_key.has_mobile_hotspot_supported());
+    EXPECT_EQ(expected_unlock_key.mobile_hotspot_supported(),
+              unlock_key.mobile_hotspot_supported());
+
+    EXPECT_EQ(expected_unlock_key.has_device_type(),
+              unlock_key.has_device_type());
+    EXPECT_EQ(expected_unlock_key.device_type(),
+              unlock_key.device_type());
+
+    ASSERT_EQ(expected_unlock_key.beacon_seeds_size(),
+              unlock_key.beacon_seeds_size());
+    for (int i = 0; i < expected_unlock_key.beacon_seeds_size(); i++) {
+      const cryptauth::BeaconSeed expected_seed =
+          expected_unlock_key.beacon_seeds(i);
+      const cryptauth::BeaconSeed seed = unlock_key.beacon_seeds(i);
+      EXPECT_TRUE(expected_seed.has_data());
+      EXPECT_TRUE(seed.has_data());
+      EXPECT_EQ(expected_seed.data(), seed.data());
+
+      EXPECT_TRUE(expected_seed.has_start_time_millis());
+      EXPECT_TRUE(seed.has_start_time_millis());
+      EXPECT_EQ(expected_seed.start_time_millis(), seed.start_time_millis());
+
+      EXPECT_TRUE(expected_seed.has_end_time_millis());
+      EXPECT_TRUE(seed.has_end_time_millis());
+      EXPECT_EQ(expected_seed.end_time_millis(), seed.end_time_millis());
+    }
   }
 
   const base::ListValue* unlock_keys_pref =
@@ -84,31 +150,119 @@
                                     static_cast<int>(i)));
     const base::DictionaryValue* unlock_key_dictionary;
     EXPECT_TRUE(unlock_keys_pref->GetDictionary(i, &unlock_key_dictionary));
-    std::string public_key_b64, device_name_b64, bluetooth_address_b64;
-    ASSERT_TRUE(
-        unlock_key_dictionary->GetString("public_key", &public_key_b64));
-    ASSERT_TRUE(
-        unlock_key_dictionary->GetString("device_name", &device_name_b64));
-    ASSERT_TRUE(unlock_key_dictionary->GetString("bluetooth_address",
-                                                 &bluetooth_address_b64));
-
-    std::string public_key, device_name, bluetooth_address;
-    ASSERT_TRUE(base::Base64UrlDecode(
-        public_key_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-        &public_key));
-    ASSERT_TRUE(base::Base64UrlDecode(
-        device_name_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-        &device_name));
-    ASSERT_TRUE(base::Base64UrlDecode(
-        bluetooth_address_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
-        &bluetooth_address));
 
     const auto& expected_unlock_key = expected_unlock_keys[i];
+
+    std::string public_key_b64, public_key;
+    EXPECT_TRUE(
+        unlock_key_dictionary->GetString("public_key", &public_key_b64));
+    EXPECT_TRUE(base::Base64UrlDecode(
+        public_key_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+        &public_key));
+    EXPECT_TRUE(expected_unlock_key.has_public_key());
     EXPECT_EQ(expected_unlock_key.public_key(), public_key);
-    EXPECT_EQ(expected_unlock_key.friendly_device_name(), device_name);
-    EXPECT_EQ(expected_unlock_key.bluetooth_address(), bluetooth_address);
-    EXPECT_TRUE(expected_unlock_key.unlock_key());
-    EXPECT_FALSE(expected_unlock_key.unlockable());
+
+    std::string device_name_b64, device_name;
+    if (unlock_key_dictionary->GetString("device_name", &device_name_b64)) {
+      EXPECT_TRUE(base::Base64UrlDecode(
+          device_name_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+          &device_name));
+      EXPECT_TRUE(expected_unlock_key.has_friendly_device_name());
+      EXPECT_EQ(expected_unlock_key.friendly_device_name(), device_name);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_friendly_device_name());
+    }
+
+    std::string bluetooth_address_b64, bluetooth_address;
+    if (unlock_key_dictionary->GetString("bluetooth_address",
+                                         &bluetooth_address_b64)) {
+      EXPECT_TRUE(base::Base64UrlDecode(
+          bluetooth_address_b64, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+          &bluetooth_address));
+      EXPECT_TRUE(expected_unlock_key.has_bluetooth_address());
+      EXPECT_EQ(expected_unlock_key.bluetooth_address(), bluetooth_address);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_bluetooth_address());
+    }
+
+    bool unlock_key;
+    if (unlock_key_dictionary->GetBoolean("unlock_key", &unlock_key)) {
+      EXPECT_TRUE(expected_unlock_key.has_unlock_key());
+      EXPECT_EQ(expected_unlock_key.unlock_key(), unlock_key);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_unlock_key());
+    }
+
+    bool unlockable;
+    if (unlock_key_dictionary->GetBoolean("unlockable", &unlockable)) {
+      EXPECT_TRUE(expected_unlock_key.has_unlockable());
+      EXPECT_EQ(expected_unlock_key.unlockable(), unlockable);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_unlockable());
+    }
+
+    std::string last_update_time_millis_str;
+    if (unlock_key_dictionary->GetString("last_update_time_millis",
+                                         &last_update_time_millis_str)) {
+      int64_t last_update_time_millis;
+      EXPECT_TRUE(base::StringToInt64(last_update_time_millis_str,
+                                      &last_update_time_millis));
+      EXPECT_TRUE(expected_unlock_key.has_last_update_time_millis());
+      EXPECT_EQ(expected_unlock_key.last_update_time_millis(),
+                last_update_time_millis);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_last_update_time_millis());
+    }
+
+    bool mobile_hotspot_supported;
+    if (unlock_key_dictionary->GetBoolean("mobile_hotspot_supported",
+                                          &mobile_hotspot_supported)) {
+      EXPECT_TRUE(expected_unlock_key.has_mobile_hotspot_supported());
+      EXPECT_EQ(expected_unlock_key.mobile_hotspot_supported(),
+                mobile_hotspot_supported);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_mobile_hotspot_supported());
+    }
+
+    int device_type;
+    if (unlock_key_dictionary->GetInteger("device_type",
+                                          &device_type)) {
+      EXPECT_TRUE(expected_unlock_key.has_device_type());
+      EXPECT_EQ(expected_unlock_key.device_type(),
+                device_type);
+    } else {
+      EXPECT_FALSE(expected_unlock_key.has_device_type());
+    }
+
+    const base::ListValue* beacon_seeds_from_prefs;
+    if (unlock_key_dictionary->GetList("beacon_seeds",
+                                       &beacon_seeds_from_prefs)) {
+      ASSERT_EQ(
+          (size_t) expected_unlock_key.beacon_seeds_size(),
+          beacon_seeds_from_prefs->GetSize());
+      for (size_t i = 0; i < beacon_seeds_from_prefs->GetSize(); i++) {
+        const base::DictionaryValue* seed;
+        ASSERT_TRUE(beacon_seeds_from_prefs->GetDictionary(i, &seed));
+
+        std::string data, start_ms, end_ms;
+        EXPECT_TRUE(seed->GetString("beacon_seed_data", &data));
+        EXPECT_TRUE(seed->GetString("beacon_seed_start_ms", &start_ms));
+        EXPECT_TRUE(seed->GetString("beacon_seed_end_ms", &end_ms));
+
+        const cryptauth::BeaconSeed& expected_seed =
+            expected_unlock_key.beacon_seeds((int) i);
+        EXPECT_TRUE(expected_seed.has_data());
+        EXPECT_EQ(expected_seed.data(), data);
+
+        EXPECT_TRUE(expected_seed.has_start_time_millis());
+        EXPECT_EQ(expected_seed.start_time_millis(), std::stol(start_ms));
+
+        EXPECT_TRUE(expected_seed.has_end_time_millis());
+        EXPECT_EQ(expected_seed.end_time_millis(), std::stol(end_ms));
+      }
+    } else {
+      EXPECT_FALSE(expected_unlock_key.beacon_seeds_size());
+    }
   }
 }
 
@@ -203,6 +357,10 @@
     unlock_key_dictionary->SetString("device_name", device_name_b64);
     unlock_key_dictionary->SetString("bluetooth_address",
                                      bluetooth_address_b64);
+    unlock_key_dictionary->SetBoolean("unlock_key", kStoredUnlockKey);
+    unlock_key_dictionary->SetBoolean("unlockable", kStoredUnlockable);
+    unlock_key_dictionary->Set("beacon_seeds",
+                               base::WrapUnique(new base::ListValue()));
     {
       ListPrefUpdate update(&pref_service_,
                             prefs::kCryptAuthDeviceSyncUnlockKeys);
@@ -220,12 +378,28 @@
     unlock_key.set_bluetooth_address(kBluetoothAddress1);
     unlock_key.set_unlock_key(true);
     unlock_key.set_unlockable(false);
+    cryptauth::BeaconSeed* seed1 = unlock_key.add_beacon_seeds();
+    seed1->set_data(kBeaconSeed1Data);
+    seed1->set_start_time_millis(kBeaconSeed1StartTime);
+    seed1->set_end_time_millis(kBeaconSeed1EndTime);
+    cryptauth::BeaconSeed* seed2 = unlock_key.add_beacon_seeds();
+    seed2->set_data(kBeaconSeed2Data);
+    seed2->set_start_time_millis(kBeaconSeed2StartTime);
+    seed2->set_end_time_millis(kBeaconSeed2EndTime);
 
     cryptauth::ExternalDeviceInfo unlockable_device;
     unlockable_device.set_public_key(kPublicKey2);
     unlockable_device.set_friendly_device_name(kDeviceName2);
     unlockable_device.set_unlock_key(false);
     unlockable_device.set_unlockable(true);
+    cryptauth::BeaconSeed* seed3 = unlockable_device.add_beacon_seeds();
+    seed3->set_data(kBeaconSeed3Data);
+    seed3->set_start_time_millis(kBeaconSeed3StartTime);
+    seed3->set_end_time_millis(kBeaconSeed3EndTime);
+    cryptauth::BeaconSeed* seed4 = unlockable_device.add_beacon_seeds();
+    seed4->set_data(kBeaconSeed4Data);
+    seed4->set_start_time_millis(kBeaconSeed4StartTime);
+    seed4->set_end_time_millis(kBeaconSeed4EndTime);
 
     get_my_devices_response_.add_devices()->CopyFrom(unlock_key);
     get_my_devices_response_.add_devices()->CopyFrom(unlockable_device);
@@ -529,8 +703,8 @@
   synced_unlock_key.set_public_key(kStoredPublicKey);
   synced_unlock_key.set_friendly_device_name(kStoredDeviceName);
   synced_unlock_key.set_bluetooth_address(kStoredBluetoothAddress);
-  synced_unlock_key.set_unlock_key(true);
-  synced_unlock_key.set_unlockable(false);
+  synced_unlock_key.set_unlock_key(kStoredUnlockKey);
+  synced_unlock_key.set_unlockable(kStoredUnlockable);
   cryptauth::GetMyDevicesResponse get_my_devices_response;
   get_my_devices_response.add_devices()->CopyFrom(synced_unlock_key);
   success_callback_.Run(get_my_devices_response);
@@ -606,4 +780,77 @@
                                   pref_service_);
 }
 
+TEST_F(CryptAuthDeviceManagerTest, SyncDeviceWithNoContents) {
+  device_manager_->Start();
+
+  EXPECT_CALL(*sync_scheduler(), ForceSync());
+  gcm_manager_.PushResyncMessage();
+
+  FireSchedulerForSync(cryptauth::INVOCATION_REASON_SERVER_INITIATED);
+
+  EXPECT_CALL(*this, OnSyncFinishedProxy(
+                         CryptAuthDeviceManager::SyncResult::SUCCESS,
+                         CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+  success_callback_.Run(get_my_devices_response_);
+
+  ExpectUnlockKeysAndPrefAreEqual(std::vector<cryptauth::ExternalDeviceInfo>(
+                                      1, get_my_devices_response_.devices(0)),
+                                  device_manager_->unlock_keys(),
+                                  pref_service_);
+}
+
+TEST_F(CryptAuthDeviceManagerTest, SyncFullyDetailedExternalDeviceInfos) {
+  cryptauth::GetMyDevicesResponse response;
+
+  // First, use a device with only a public key (a public key is the only
+  // required field). This ensures devices work properly when they do not have
+  // all fields filled out.
+  cryptauth::ExternalDeviceInfo device_with_only_public_key;
+  device_with_only_public_key.set_public_key("publicKey1");
+  // Currently, CryptAuthDeviceManager only stores devices which are unlock
+  // keys, so set_unlock_key(true) must be called here for storage to work.
+  // TODO(khorimoto): Remove this when support for storing all types of devices
+  // is added.
+  device_with_only_public_key.set_unlock_key(true);
+  response.add_devices()->CopyFrom(device_with_only_public_key);
+
+  // Second, use a device with all fields filled out. This ensures that all
+  // device details are properly saved.
+  cryptauth::ExternalDeviceInfo device_with_all_fields;
+  device_with_all_fields.set_public_key("publicKey2");
+  device_with_all_fields.set_friendly_device_name("deviceName");
+  device_with_all_fields.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
+  device_with_all_fields.set_unlock_key(true);
+  device_with_all_fields.set_unlockable(true);
+  device_with_all_fields.set_last_update_time_millis(123456789L);
+  device_with_all_fields.set_mobile_hotspot_supported(true);
+  device_with_all_fields.set_device_type(DeviceType::ANDROIDOS);
+  cryptauth::BeaconSeed seed1;
+  seed1.set_data(kBeaconSeed1Data);
+  seed1.set_start_time_millis(kBeaconSeed1StartTime);
+  seed1.set_end_time_millis(kBeaconSeed1EndTime);
+  device_with_all_fields.add_beacon_seeds()->CopyFrom(seed1);
+  cryptauth::BeaconSeed seed2;
+  seed2.set_data(kBeaconSeed2Data);
+  seed2.set_start_time_millis(kBeaconSeed2StartTime);
+  seed2.set_end_time_millis(kBeaconSeed2EndTime);
+  device_with_all_fields.add_beacon_seeds()->CopyFrom(seed2);
+  response.add_devices()->CopyFrom(device_with_all_fields);
+
+  std::vector<cryptauth::ExternalDeviceInfo> expected_unlock_keys;
+  expected_unlock_keys.push_back(device_with_only_public_key);
+  expected_unlock_keys.push_back(device_with_all_fields);
+
+  device_manager_->Start();
+  FireSchedulerForSync(cryptauth::INVOCATION_REASON_PERIODIC);
+  ASSERT_FALSE(success_callback_.is_null());
+  EXPECT_CALL(*this, OnSyncFinishedProxy(
+                         CryptAuthDeviceManager::SyncResult::SUCCESS,
+                         CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+  success_callback_.Run(response);
+
+  ExpectUnlockKeysAndPrefAreEqual(
+      expected_unlock_keys, device_manager_->unlock_keys(), pref_service_);
+}
+
 }  // namespace cryptauth
diff --git a/components/cryptauth/eid_generator.cc b/components/cryptauth/eid_generator.cc
new file mode 100644
index 0000000..069bc18
--- /dev/null
+++ b/components/cryptauth/eid_generator.cc
@@ -0,0 +1,401 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/eid_generator.h"
+
+#include <cstring>
+
+#include "base/base64.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "crypto/sha2.h"
+
+namespace cryptauth {
+
+namespace {
+const int64_t kNoTimestamp = 0;
+const int64_t kMaxPositiveInt64TValue = 0x7FFFFFFF;
+}  // namespace
+
+const int64_t EidGenerator::kNumMsInEidPeriod =
+    base::TimeDelta::FromHours(8).InMilliseconds();
+const int64_t EidGenerator::kNumMsInBeginningOfEidPeriod =
+    base::TimeDelta::FromHours(2).InMilliseconds();
+const int32_t EidGenerator::kNumBytesInEidValue = 2;
+
+std::string EidGenerator::EidComputationHelperImpl::GenerateEidDataForDevice(
+    const std::string& eid_seed,
+    const int64_t start_of_period_timestamp_ms,
+    const std::string* extra_entropy) {
+  // The data to hash is the eid seed, followed by the extra entropy (if it
+  // exists), followed by the timestamp.
+  std::string to_hash = eid_seed;
+  if (extra_entropy) {
+    to_hash += *extra_entropy;
+  }
+  uint64_t timestamp_data =
+      base::HostToNet64(static_cast<uint64_t>(start_of_period_timestamp_ms));
+  to_hash.append(reinterpret_cast<char*>(&timestamp_data), sizeof(uint64_t));
+
+  std::string result = crypto::SHA256HashString(to_hash);
+  result.resize(EidGenerator::kNumBytesInEidValue);
+  return result;
+}
+
+EidGenerator::EidData::EidData(const DataWithTimestamp current_data,
+                               std::unique_ptr<DataWithTimestamp> adjacent_data)
+    : current_data(current_data), adjacent_data(std::move(adjacent_data)) {}
+
+EidGenerator::EidData::~EidData() {}
+
+EidGenerator::EidData::AdjacentDataType
+EidGenerator::EidData::GetAdjacentDataType() const {
+  if (!adjacent_data) {
+    return AdjacentDataType::NONE;
+  }
+
+  if (adjacent_data->start_timestamp_ms < current_data.start_timestamp_ms) {
+    return AdjacentDataType::PAST;
+  }
+
+  return AdjacentDataType::FUTURE;
+}
+
+EidGenerator::EidGenerator()
+    : EidGenerator(base::WrapUnique(new EidComputationHelperImpl()),
+                   base::WrapUnique(new base::DefaultClock())) {}
+
+EidGenerator::EidGenerator(
+    std::unique_ptr<EidComputationHelper> computation_helper,
+    std::unique_ptr<base::Clock> clock)
+    : clock_(std::move(clock)),
+      eid_computation_helper_(std::move(computation_helper)) {}
+
+EidGenerator::DataWithTimestamp::DataWithTimestamp(
+    const std::string& data,
+    const int64_t start_timestamp_ms,
+    const int64_t end_timestamp_ms)
+    : data(data),
+      start_timestamp_ms(start_timestamp_ms),
+      end_timestamp_ms(end_timestamp_ms) {}
+
+EidGenerator::~EidGenerator() {}
+
+std::unique_ptr<EidGenerator::EidData>
+EidGenerator::GenerateBackgroundScanFilter(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  std::unique_ptr<EidPeriodTimestamps> timestamps =
+      GetEidPeriodTimestamps(scanning_device_beacon_seeds);
+  if (!timestamps) {
+    // If the device does not have seeds for the correct period, no EIDs can be
+    // generated.
+    return nullptr;
+  }
+
+  std::unique_ptr<DataWithTimestamp> current_eid = GenerateEidDataWithTimestamp(
+      scanning_device_beacon_seeds,
+      timestamps->current_period_start_timestamp_ms,
+      timestamps->current_period_end_timestamp_ms);
+  if (!current_eid) {
+    // The current EID could not be generated.
+    return nullptr;
+  }
+
+  std::unique_ptr<DataWithTimestamp> adjacent_eid =
+      GenerateEidDataWithTimestamp(
+          scanning_device_beacon_seeds,
+          timestamps->adjacent_period_start_timestamp_ms,
+          timestamps->adjacent_period_end_timestamp_ms);
+  return base::WrapUnique(new EidData(*current_eid, std::move(adjacent_eid)));
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateAdvertisement(
+    const std::string& advertising_device_public_key,
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  std::unique_ptr<EidPeriodTimestamps> timestamps =
+      GetEidPeriodTimestamps(scanning_device_beacon_seeds);
+  if (!timestamps) {
+    return nullptr;
+  }
+
+  return GenerateAdvertisement(advertising_device_public_key,
+                               scanning_device_beacon_seeds,
+                               timestamps->current_period_start_timestamp_ms,
+                               timestamps->current_period_end_timestamp_ms);
+}
+
+RemoteDevice const* EidGenerator::IdentifyRemoteDeviceByAdvertisement(
+    const std::string& advertisement_service_data,
+    const std::vector<RemoteDevice>& device_list,
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  for (const auto& remote_device : device_list) {
+    std::vector<std::string> possible_advertisements =
+        GeneratePossibleAdvertisements(remote_device.public_key,
+                                       scanning_device_beacon_seeds);
+    for (const auto& possible_advertisement : possible_advertisements) {
+      if (advertisement_service_data == possible_advertisement) {
+        return const_cast<RemoteDevice*>(&remote_device);
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+std::vector<std::string> EidGenerator::GeneratePossibleAdvertisements(
+    const std::string& advertising_device_public_key,
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  std::vector<std::string> possible_advertisements;
+
+  std::unique_ptr<EidPeriodTimestamps> timestamps =
+      GetEidPeriodTimestamps(scanning_device_beacon_seeds, true);
+  if (!timestamps) {
+    // If the devices do not have seeds for the correct period, no
+    // advertisements can be generated.
+    return possible_advertisements;
+  }
+
+  if (timestamps->current_period_start_timestamp_ms != kNoTimestamp) {
+    std::unique_ptr<DataWithTimestamp> current_advertisement =
+        GenerateAdvertisement(advertising_device_public_key,
+                              scanning_device_beacon_seeds,
+                              timestamps->current_period_start_timestamp_ms,
+                              timestamps->current_period_end_timestamp_ms);
+    if (current_advertisement) {
+      possible_advertisements.push_back(current_advertisement->data);
+    }
+  }
+
+  std::unique_ptr<DataWithTimestamp> adjacent_advertisement =
+      GenerateAdvertisement(advertising_device_public_key,
+                            scanning_device_beacon_seeds,
+                            timestamps->adjacent_period_start_timestamp_ms,
+                            timestamps->adjacent_period_end_timestamp_ms);
+  if (adjacent_advertisement) {
+    possible_advertisements.push_back(adjacent_advertisement->data);
+  }
+
+  return possible_advertisements;
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateAdvertisement(
+    const std::string& advertising_device_public_key,
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t start_of_period_timestamp_ms,
+    const int64_t end_of_period_timestamp_ms) const {
+  std::unique_ptr<DataWithTimestamp> advertising_device_identifying_data =
+      GenerateEidDataWithTimestamp(
+          scanning_device_beacon_seeds, start_of_period_timestamp_ms,
+          end_of_period_timestamp_ms, &advertising_device_public_key);
+  std::unique_ptr<DataWithTimestamp> scanning_device_identifying_data =
+      GenerateEidDataWithTimestamp(scanning_device_beacon_seeds,
+                                   start_of_period_timestamp_ms,
+                                   end_of_period_timestamp_ms);
+  if (!advertising_device_identifying_data ||
+      !scanning_device_identifying_data) {
+    return nullptr;
+  }
+
+  std::string full_advertisement = scanning_device_identifying_data->data +
+                                   advertising_device_identifying_data->data;
+  return base::WrapUnique(new DataWithTimestamp(full_advertisement,
+                                                start_of_period_timestamp_ms,
+                                                end_of_period_timestamp_ms));
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateEidDataWithTimestamp(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t start_of_period_timestamp_ms,
+    const int64_t end_of_period_timestamp_ms) const {
+  return GenerateEidDataWithTimestamp(scanning_device_beacon_seeds,
+                                      start_of_period_timestamp_ms,
+                                      end_of_period_timestamp_ms, nullptr);
+}
+
+std::unique_ptr<EidGenerator::DataWithTimestamp>
+EidGenerator::GenerateEidDataWithTimestamp(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t start_of_period_timestamp_ms,
+    const int64_t end_of_period_timestamp_ms,
+    const std::string* extra_entropy) const {
+  std::unique_ptr<std::string> eid_seed = GetEidSeedForPeriod(
+      scanning_device_beacon_seeds, start_of_period_timestamp_ms);
+  if (!eid_seed) {
+    return nullptr;
+  }
+
+  std::string eid_data = eid_computation_helper_->GenerateEidDataForDevice(
+      *eid_seed, start_of_period_timestamp_ms, extra_entropy);
+
+  return base::WrapUnique(new DataWithTimestamp(
+      eid_data, start_of_period_timestamp_ms, end_of_period_timestamp_ms));
+}
+
+std::unique_ptr<std::string> EidGenerator::GetEidSeedForPeriod(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t start_of_period_timestamp_ms) const {
+  for (auto seed : scanning_device_beacon_seeds) {
+    if (seed.start_time_millis() <= start_of_period_timestamp_ms &&
+        start_of_period_timestamp_ms < seed.end_time_millis()) {
+      return base::WrapUnique(new std::string(seed.data()));
+    }
+  }
+
+  return nullptr;
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetEidPeriodTimestamps(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const {
+  return GetEidPeriodTimestamps(scanning_device_beacon_seeds, false);
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetEidPeriodTimestamps(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const bool allow_non_current_periods) const {
+  base::Time now = clock_->Now();
+  int64_t current_time_ms = now.ToJavaTime();
+
+  std::unique_ptr<BeaconSeed> current_seed = GetBeaconSeedForCurrentPeriod(
+      scanning_device_beacon_seeds, current_time_ms);
+  if (!current_seed) {
+    if (!allow_non_current_periods) {
+      // No seed existed for the current time.
+      return nullptr;
+    }
+
+    return GetClosestPeriod(scanning_device_beacon_seeds, current_time_ms);
+  }
+
+  // Now that the start of the 14-day EID seed period has been determined, the
+  // current EID period must be determined.
+  for (int64_t start_of_eid_period_ms = current_seed->start_time_millis();
+       start_of_eid_period_ms <= current_seed->end_time_millis();
+       start_of_eid_period_ms += kNumMsInEidPeriod) {
+    int64_t end_of_eid_period_ms = start_of_eid_period_ms + kNumMsInEidPeriod;
+    if (start_of_eid_period_ms <= current_time_ms &&
+        current_time_ms < end_of_eid_period_ms) {
+      int64_t start_of_adjacent_period_ms;
+      int64_t end_of_adjacent_period_ms;
+      if (IsCurrentTimeAtStartOfEidPeriod(
+              start_of_eid_period_ms, end_of_eid_period_ms, current_time_ms)) {
+        // If the current time is at the beginning of the period, the "adjacent
+        // period" is the period before this one.
+        start_of_adjacent_period_ms =
+            start_of_eid_period_ms - kNumMsInEidPeriod;
+        end_of_adjacent_period_ms = start_of_eid_period_ms;
+      } else {
+        // Otherwise, the "adjacent period" is the one after this one.
+        start_of_adjacent_period_ms = end_of_eid_period_ms;
+        end_of_adjacent_period_ms = end_of_eid_period_ms + kNumMsInEidPeriod;
+      }
+
+      return base::WrapUnique(new EidPeriodTimestamps{
+          start_of_eid_period_ms, end_of_eid_period_ms,
+          start_of_adjacent_period_ms, end_of_adjacent_period_ms});
+    }
+  }
+
+  PA_LOG(ERROR) << "Could not find valid EID period for seed. "
+                << "seed.start_timestamp_ms: "
+                << current_seed->start_time_millis()
+                << ", seed.end_timestamp_ms: "
+                << current_seed->end_time_millis();
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<BeaconSeed> EidGenerator::GetBeaconSeedForCurrentPeriod(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t current_time_ms) const {
+  for (auto seed : scanning_device_beacon_seeds) {
+    int64_t seed_period_length_ms =
+        seed.end_time_millis() - seed.start_time_millis();
+    if (seed_period_length_ms % kNumMsInEidPeriod != 0) {
+      PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
+                      << "the rotation length.";
+      continue;
+    }
+
+    if (seed.start_time_millis() <= current_time_ms &&
+        current_time_ms < seed.end_time_millis()) {
+      return base::WrapUnique(new BeaconSeed(seed));
+    }
+  }
+
+  return nullptr;
+}
+
+std::unique_ptr<EidGenerator::EidPeriodTimestamps>
+EidGenerator::GetClosestPeriod(
+    const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+    const int64_t current_time_ms) const {
+  int64_t smallest_diff_so_far_ms = kMaxPositiveInt64TValue;
+  int64_t start_of_period_timestamp_ms = kMaxPositiveInt64TValue;
+  int64_t end_of_period_timestamp_ms = 0;
+
+  for (auto seed : scanning_device_beacon_seeds) {
+    int64_t seed_period_length_ms =
+        seed.end_time_millis() - seed.start_time_millis();
+    if (seed_period_length_ms % kNumMsInEidPeriod != 0) {
+      PA_LOG(WARNING) << "Seed has period length which is not an multiple of "
+                      << "the rotation length.";
+      continue;
+    }
+
+    DCHECK(seed.start_time_millis() > current_time_ms ||
+           current_time_ms >= seed.end_time_millis());
+
+    if (seed.start_time_millis() > current_time_ms) {
+      int64_t diff = seed.start_time_millis() - current_time_ms;
+      if (diff < smallest_diff_so_far_ms) {
+        smallest_diff_so_far_ms = diff;
+        start_of_period_timestamp_ms = seed.start_time_millis();
+        end_of_period_timestamp_ms =
+            seed.start_time_millis() + kNumMsInEidPeriod;
+      }
+    } else if (seed.end_time_millis() <= current_time_ms) {
+      int64_t diff = current_time_ms - seed.end_time_millis();
+      if (diff < smallest_diff_so_far_ms) {
+        smallest_diff_so_far_ms = diff;
+        end_of_period_timestamp_ms = seed.end_time_millis();
+        start_of_period_timestamp_ms =
+            end_of_period_timestamp_ms - kNumMsInEidPeriod;
+      }
+    }
+  }
+
+  if (smallest_diff_so_far_ms < kMaxPositiveInt64TValue &&
+      smallest_diff_so_far_ms > kNumMsInEidPeriod) {
+    return nullptr;
+  }
+
+  return base::WrapUnique(new EidPeriodTimestamps{
+      kNoTimestamp,  // current_period_start_timestamp_ms is unused.
+      kNoTimestamp,  // current_period_end_timestamp_ms is unused.
+      start_of_period_timestamp_ms, end_of_period_timestamp_ms});
+}
+
+bool EidGenerator::IsCurrentTimeAtStartOfEidPeriod(
+    const int64_t start_of_period_timestamp_ms,
+    const int64_t end_of_period_timestamp_ms,
+    const int64_t current_timestamp_ms) {
+  DCHECK(start_of_period_timestamp_ms <= current_timestamp_ms);
+  DCHECK(current_timestamp_ms < end_of_period_timestamp_ms);
+
+  return current_timestamp_ms <
+         start_of_period_timestamp_ms + kNumMsInBeginningOfEidPeriod;
+}
+
+}  // namespace cryptauth
diff --git a/components/cryptauth/eid_generator.h b/components/cryptauth/eid_generator.h
new file mode 100644
index 0000000..f2cd640a
--- /dev/null
+++ b/components/cryptauth/eid_generator.h
@@ -0,0 +1,218 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H
+#define COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/time/clock.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+struct RemoteDevice;
+
+// Generates ephemeral ID (EID) values for BLE communication in ProximityAuth.
+//
+// A peripheral-role device advertises a 4-byte advertisement with two parts: a
+// 2-byte EID which is specific to the central-role device with which it intends
+// to communicate, and a 2-byte EID which is specific to the peripheral-role
+// device.
+//
+// This class uses EID seed values synced from the back-end to generate these
+// EIDs.
+//
+// See go/proximity-auth-ble-advertising.
+class EidGenerator {
+ public:
+  // Stores EID-related data and timestamps at which time this data becomes
+  // active or inactive.
+  struct DataWithTimestamp {
+    DataWithTimestamp(const std::string& data,
+                      const int64_t start_timestamp_ms,
+                      const int64_t end_timestamp_ms);
+
+    bool ContainsTime(const int64_t timestamp_ms) const;
+
+    const std::string data;
+    const int64_t start_timestamp_ms;
+    const int64_t end_timestamp_ms;
+  };
+
+  // Data for both a current and adjacent EID. The current EID *must* be
+  // supplied, but adjacent data may be null. Each EID consists of a 2-byte EID
+  // value paired with the timestamp at which time this value becomes active or
+  // inactive.
+  struct EidData {
+    enum AdjacentDataType { NONE, PAST, FUTURE };
+
+    EidData(const DataWithTimestamp current_data,
+            std::unique_ptr<DataWithTimestamp> adjacent_data);
+    ~EidData();
+
+    AdjacentDataType GetAdjacentDataType() const;
+
+    const DataWithTimestamp current_data;
+    const std::unique_ptr<DataWithTimestamp> adjacent_data;
+  };
+
+  EidGenerator();
+  ~EidGenerator();
+
+  // Generates EID data for the given EID seeds to be used as a background scan
+  // filter. In the normal case, two DataWithTimestamp values are returned, one
+  // for each EID seed rotation period. If data has not been synced from the
+  // backend recently and EID seeds are unavailable, nullptr is returned.
+  std::unique_ptr<EidData> GenerateBackgroundScanFilter(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+  // Generates advertisement data for the given EID seeds. If data has not been
+  // synced from the back-end recently and EID seeds are unavailable, nullptr is
+  // returned.
+  std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
+      const std::string& advertising_device_public_key,
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+  // Generates all possible advertisements that could be created by a device
+  // given that device's public key and the beacon seeds of the device which is
+  // intended to scan for the advertisement.
+  std::vector<std::string> GeneratePossibleAdvertisements(
+      const std::string& advertising_device_public_key,
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+  // Given a list of RemoteDevices, identifies the device which could have
+  // produced the supplied advertisement service data.
+  RemoteDevice const* IdentifyRemoteDeviceByAdvertisement(
+      const std::string& advertisement_service_data,
+      const std::vector<RemoteDevice>& device_list,
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+ private:
+  struct EidPeriodTimestamps {
+    int64_t current_period_start_timestamp_ms;
+    int64_t current_period_end_timestamp_ms;
+    int64_t adjacent_period_start_timestamp_ms;
+    int64_t adjacent_period_end_timestamp_ms;
+  };
+
+  class EidComputationHelper {
+   public:
+    virtual std::string GenerateEidDataForDevice(
+        const std::string& eid_seed,
+        const int64_t start_of_period_timestamp_ms,
+        const std::string* extra_entropy) = 0;
+  };
+
+  class EidComputationHelperImpl : public EidComputationHelper {
+   public:
+    std::string GenerateEidDataForDevice(
+        const std::string& eid_seed,
+        const int64_t start_of_period_timestamp_ms,
+        const std::string* extra_entropy) override;
+  };
+
+  static const int64_t kNumMsInEidPeriod;
+  static const int64_t kNumMsInBeginningOfEidPeriod;
+  static const int32_t kNumBytesInEidValue;
+
+  EidGenerator(std::unique_ptr<EidComputationHelper> computation_helper,
+               std::unique_ptr<base::Clock> clock);
+
+  std::unique_ptr<DataWithTimestamp> GenerateAdvertisement(
+      const std::string& advertising_device_public_key,
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t start_of_period_timestamp_ms,
+      const int64_t end_of_period_timestamp_ms) const;
+
+  std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t start_of_period_timestamp_ms,
+      const int64_t end_of_period_timestamp_ms) const;
+
+  std::unique_ptr<DataWithTimestamp> GenerateEidDataWithTimestamp(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t start_of_period_timestamp_ms,
+      const int64_t end_of_period_timestamp_ms,
+      const std::string* extra_entropy) const;
+
+  std::unique_ptr<std::string> GetEidSeedForPeriod(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t start_of_period_timestamp_ms) const;
+
+  std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds) const;
+
+  std::unique_ptr<EidPeriodTimestamps> GetEidPeriodTimestamps(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const bool allow_non_current_periods) const;
+
+  std::unique_ptr<BeaconSeed> GetBeaconSeedForCurrentPeriod(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t current_time_ms) const;
+
+  std::unique_ptr<EidPeriodTimestamps> GetClosestPeriod(
+      const std::vector<BeaconSeed>& scanning_device_beacon_seeds,
+      const int64_t current_time_ms) const;
+
+  static bool IsCurrentTimeAtStartOfEidPeriod(
+      const int64_t start_of_period_timestamp_ms,
+      const int64_t end_of_period_timestamp_ms,
+      const int64_t current_timestamp_ms);
+
+  std::unique_ptr<base::Clock> clock_;
+
+  std::unique_ptr<EidComputationHelper> eid_computation_helper_;
+
+  friend class CryptAuthEidGeneratorTest;
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGenerateEidDataForDevice_UsingRealEidComputationHelper);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGeneratePossibleAdvertisements_CurrentAndPastAdjacentPeriods);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      testGeneratePossibleAdvertisements_CurrentAndFutureAdjacentPeriods);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGeneratePossibleAdvertisements_OnlyCurrentPeriod);
+  FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+                           TestGeneratePossibleAdvertisements_OnlyFuturePeriod);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture);
+  FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+                           TestGeneratePossibleAdvertisements_OnlyPastPeriod);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestGeneratePossibleAdvertisements_NoAdvertisements_EmptySeeds);
+  FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+                           TestEidComputationHelperImpl_ProducesTwoByteValue);
+  FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+                           TestEidComputationHelperImpl_Deterministic);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestEidComputationHelperImpl_ChangingSeedChangesOutput);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestEidComputationHelperImpl_ChangingTimestampChangesOutput);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      TestEidComputationHelperImpl_ChangingExtraEntropyChangesOutput);
+  FRIEND_TEST_ALL_PREFIXES(
+      CryptAuthEidGeneratorTest,
+      testEidComputationHelper_ChangingTimestampWithLongExtraEntropy);
+  FRIEND_TEST_ALL_PREFIXES(CryptAuthEidGeneratorTest,
+                           TestEidComputationHelper_EnsureTestVectorsPass);
+};
+}
+
+#endif  // COMPONENTS_CRYPTAUTH_BLE_EID_GENERATOR_H
diff --git a/components/cryptauth/eid_generator_unittest.cc b/components/cryptauth/eid_generator_unittest.cc
new file mode 100644
index 0000000..61c78f0d
--- /dev/null
+++ b/components/cryptauth/eid_generator_unittest.cc
@@ -0,0 +1,717 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/eid_generator.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+#include "components/proximity_auth/remote_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::SaveArg;
+
+namespace cryptauth {
+
+namespace {
+const int64_t kNumMsInEidPeriod =
+    base::TimeDelta::FromHours(8).InMilliseconds();
+const int64_t kNumMsInBeginningOfEidPeriod =
+    base::TimeDelta::FromHours(2).InMilliseconds();
+const int32_t kNumBytesInEidValue = 2;
+const int64_t kNumMsInEidSeedPeriod =
+    base::TimeDelta::FromDays(14).InMilliseconds();
+
+// Midnight on 1/1/2020.
+const int64_t kDefaultCurrentPeriodStart = 1577836800000L;
+// 1:43am on 1/1/2020.
+const int64_t kDefaultCurrentTime = 1577843000000L;
+
+// Base64 encoded values for: "firstSeed", "secondSeed", "thirdSeed" and
+// "fourthSeed".
+const std::string kFirstSeed = "Zmlyc3RTZWVk";
+const std::string kSecondSeed = "c2Vjb25kU2VlZA==";
+const std::string kThirdSeed = "dGhpcmRTZWVk";
+const std::string kFourthSeed = "Zm91cnRoU2VlZA==";
+
+const std::string kDefaultAdvertisingDevicePublicKey = "publicKey";
+
+static BeaconSeed CreateBeaconSeed(const std::string& data,
+                                   const int64_t start_timestamp_ms,
+                                   const int64_t end_timestamp_ms) {
+  BeaconSeed seed;
+  seed.set_data(data);
+  seed.set_start_time_millis(start_timestamp_ms);
+  seed.set_end_time_millis(end_timestamp_ms);
+  return seed;
+}
+
+static std::string GenerateFakeEidData(
+    const std::string& eid_seed,
+    const int64_t start_of_period_timestamp_ms,
+    const std::string* extra_entropy) {
+  std::hash<std::string> string_hash;
+  int64_t seed_hash = string_hash(eid_seed);
+  int64_t extra_hash = extra_entropy ? string_hash(*extra_entropy) : 0;
+  int64_t fake_data_xor = seed_hash ^ start_of_period_timestamp_ms ^ extra_hash;
+
+  std::string fake_data(reinterpret_cast<const char*>(&fake_data_xor),
+                        sizeof(fake_data_xor));
+  fake_data.resize(kNumBytesInEidValue);
+
+  return fake_data;
+}
+
+static std::string GenerateFakeAdvertisement(
+    const std::string& scanning_device_eid_seed,
+    const int64_t start_of_period_timestamp_ms,
+    const std::string& advertising_device_public_key) {
+  std::string fake_scanning_eid = GenerateFakeEidData(
+      scanning_device_eid_seed, start_of_period_timestamp_ms, nullptr);
+  std::string fake_advertising_id = GenerateFakeEidData(
+      scanning_device_eid_seed, start_of_period_timestamp_ms,
+      &advertising_device_public_key);
+  std::string fake_advertisement;
+  fake_advertisement.append(fake_scanning_eid);
+  fake_advertisement.append(fake_advertising_id);
+  return fake_advertisement;
+}
+
+static RemoteDevice CreateRemoteDevice(
+    const std::string& public_key) {
+  RemoteDevice remote_device;
+  remote_device.public_key = public_key;
+  return remote_device;
+}
+}  //  namespace
+
+class CryptAuthEidGeneratorTest : public testing::Test {
+ protected:
+  CryptAuthEidGeneratorTest() {
+    scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+        kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+        kDefaultCurrentPeriodStart));
+    scanning_device_beacon_seeds_.push_back(
+        CreateBeaconSeed(kSecondSeed, kDefaultCurrentPeriodStart,
+                         kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod));
+    scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+        kThirdSeed, kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+        kDefaultCurrentPeriodStart + 2 * kNumMsInEidSeedPeriod));
+    scanning_device_beacon_seeds_.push_back(CreateBeaconSeed(
+        kFourthSeed, kDefaultCurrentPeriodStart + 2 * kNumMsInEidSeedPeriod,
+        kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod));
+  }
+
+  class TestEidComputationHelper : public EidGenerator::EidComputationHelper {
+   public:
+    TestEidComputationHelper() {}
+
+    std::string GenerateEidDataForDevice(
+        const std::string& eid_seed,
+        const int64_t start_of_period_timestamp_ms,
+        const std::string* extra_entropy) override {
+      return GenerateFakeEidData(eid_seed, start_of_period_timestamp_ms,
+                                 extra_entropy);
+    }
+  };
+
+  void SetUp() override {
+    test_clock_ = new base::SimpleTestClock();
+    test_computation_helper_ = new TestEidComputationHelper();
+    real_computation_helper_.reset(
+        new EidGenerator::EidComputationHelperImpl());
+
+    SetTestTime(kDefaultCurrentTime);
+
+    eid_generator_.reset(
+        new EidGenerator(base::WrapUnique(test_computation_helper_),
+                         base::WrapUnique(test_clock_)));
+  }
+
+  // TODO(khorimoto): Is there an easier way to do this?
+  void SetTestTime(int64_t timestamp_ms) {
+    base::Time time = base::Time::UnixEpoch() +
+                      base::TimeDelta::FromMilliseconds(timestamp_ms);
+    test_clock_->SetNow(time);
+  }
+
+  std::unique_ptr<EidGenerator> eid_generator_;
+  base::SimpleTestClock* test_clock_;
+  TestEidComputationHelper* test_computation_helper_;
+  std::unique_ptr<EidGenerator::EidComputationHelper> real_computation_helper_;
+  std::vector<BeaconSeed> scanning_device_beacon_seeds_;
+};
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_StartOfPeriod_AnotherSeedInPreviousPeriod) {
+  SetTestTime(kDefaultCurrentTime);
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::PAST);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+            data->current_data.end_timestamp_ms);
+  EXPECT_EQ(
+      GenerateFakeEidData(kSecondSeed, kDefaultCurrentPeriodStart, nullptr),
+      data->current_data.data);
+
+  ASSERT_TRUE(data->adjacent_data);
+  EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+            data->adjacent_data->start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
+  EXPECT_EQ(
+      GenerateFakeEidData(
+          kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod, nullptr),
+      data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_StartOfPeriod_NoSeedBefore) {
+  SetTestTime(kDefaultCurrentTime - kNumMsInEidSeedPeriod);
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::NONE);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+            data->current_data.start_timestamp_ms);
+  EXPECT_EQ(
+      kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
+      data->current_data.end_timestamp_ms);
+  EXPECT_EQ(GenerateFakeEidData(
+                kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+                nullptr),
+            data->current_data.data);
+
+  EXPECT_FALSE(data->adjacent_data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_PastStartOfPeriod) {
+  SetTestTime(kDefaultCurrentTime +
+              base::TimeDelta::FromHours(3).InMilliseconds());
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::FUTURE);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+            data->current_data.end_timestamp_ms);
+  EXPECT_EQ(
+      GenerateFakeEidData(kSecondSeed, kDefaultCurrentPeriodStart, nullptr),
+      data->current_data.data);
+
+  ASSERT_TRUE(data->adjacent_data);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+            data->adjacent_data->start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + 2 * kNumMsInEidPeriod,
+            data->adjacent_data->end_timestamp_ms);
+  EXPECT_EQ(
+      GenerateFakeEidData(
+          kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod, nullptr),
+      data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateEidDataForDevice_EndOfPeriod) {
+  SetTestTime(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - 1);
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::FUTURE);
+
+  EXPECT_EQ(
+      kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+      data->current_data.start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+            data->current_data.end_timestamp_ms);
+  EXPECT_EQ(GenerateFakeEidData(kSecondSeed,
+                                kDefaultCurrentPeriodStart +
+                                    kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+                                nullptr),
+            data->current_data.data);
+
+  ASSERT_TRUE(data->adjacent_data);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+            data->adjacent_data->start_timestamp_ms);
+  EXPECT_EQ(
+      kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod + kNumMsInEidPeriod,
+      data->adjacent_data->end_timestamp_ms);
+  EXPECT_EQ(GenerateFakeEidData(
+                kThirdSeed, kDefaultCurrentPeriodStart + kNumMsInEidSeedPeriod,
+                nullptr),
+            data->adjacent_data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_EndOfPeriod_NoSeedAfter) {
+  SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod - 1);
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::NONE);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod -
+                kNumMsInEidPeriod,
+            data->current_data.start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod,
+            data->current_data.end_timestamp_ms);
+  EXPECT_EQ(GenerateFakeEidData(kFourthSeed, kDefaultCurrentPeriodStart +
+                                                 3 * kNumMsInEidSeedPeriod -
+                                                 kNumMsInEidPeriod,
+                                nullptr),
+            data->current_data.data);
+
+  EXPECT_FALSE(data->adjacent_data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_NoCurrentPeriodSeed) {
+  SetTestTime(kDefaultCurrentPeriodStart + 4 * kNumMsInEidSeedPeriod - 1);
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, testGenerateEidDataForDevice_EmptySeeds) {
+  SetTestTime(kDefaultCurrentTime);
+
+  std::vector<BeaconSeed> empty;
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(empty);
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_InvalidSeed_PeriodNotMultipleOf8Hours) {
+  SetTestTime(kDefaultCurrentTime);
+
+  // Seed has a period of 1ms, but it should have a period of 8 hours.
+  std::vector<BeaconSeed> invalid_seed_vector = {CreateBeaconSeed(
+      kFirstSeed, kDefaultCurrentPeriodStart, kDefaultCurrentPeriodStart + 1)};
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(invalid_seed_vector);
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateEidDataForDevice_UsingRealEidComputationHelper) {
+  test_clock_ = new base::SimpleTestClock();
+  SetTestTime(kDefaultCurrentTime);
+
+  // Use real EidComputationHelper implementation instead of test version.
+  eid_generator_.reset(new EidGenerator(std::move(real_computation_helper_),
+                                        base::WrapUnique(test_clock_)));
+
+  std::unique_ptr<EidGenerator::EidData> data =
+      eid_generator_->GenerateBackgroundScanFilter(
+          scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+  EXPECT_EQ(data->GetAdjacentDataType(),
+            EidGenerator::EidData::AdjacentDataType::PAST);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->current_data.start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+            data->current_data.end_timestamp_ms);
+  // Since this uses the real EidComputationHelper, just make sure the data
+  // exists and has the proper length.
+  EXPECT_EQ((size_t)kNumBytesInEidValue, data->current_data.data.length());
+
+  ASSERT_TRUE(data->adjacent_data);
+  EXPECT_EQ(kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+            data->adjacent_data->start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->adjacent_data->end_timestamp_ms);
+  // Since this uses the real EidComputationHelper, just make sure the data
+  // exists and has the proper length.
+  EXPECT_EQ((size_t)kNumBytesInEidValue, data->adjacent_data->data.length());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateAdvertisementData) {
+  SetTestTime(kDefaultCurrentTime);
+
+  std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+      eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+                                            scanning_device_beacon_seeds_);
+  ASSERT_TRUE(data);
+
+  EXPECT_EQ(kDefaultCurrentPeriodStart, data->start_timestamp_ms);
+  EXPECT_EQ(kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+            data->end_timestamp_ms);
+  EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                      kDefaultAdvertisingDevicePublicKey),
+            data->data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGenerateAdvertisementData_NoSeedForPeriod) {
+  SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+
+  std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+      eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+                                            scanning_device_beacon_seeds_);
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestGenerateAdvertisementData_EmptySeeds) {
+  SetTestTime(kDefaultCurrentTime + 4 * kNumMsInEidSeedPeriod);
+
+  std::vector<BeaconSeed> empty;
+  std::unique_ptr<EidGenerator::DataWithTimestamp> data =
+      eid_generator_->GenerateAdvertisement(kDefaultAdvertisingDevicePublicKey,
+                                            empty);
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_CurrentAndPastAdjacentPeriods) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+  EXPECT_EQ((size_t)2, possible_advertisements.size());
+  EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                      kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[0]);
+  EXPECT_EQ(GenerateFakeAdvertisement(
+                kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidPeriod,
+                kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[1]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       testGeneratePossibleAdvertisements_CurrentAndFutureAdjacentPeriods) {
+  SetTestTime(kDefaultCurrentPeriodStart +
+              base::TimeDelta::FromHours(3).InMilliseconds());
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+  EXPECT_EQ((size_t)2, possible_advertisements.size());
+  EXPECT_EQ(GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                      kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[0]);
+  EXPECT_EQ(GenerateFakeAdvertisement(
+                kSecondSeed, kDefaultCurrentPeriodStart + kNumMsInEidPeriod,
+                kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[1]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_OnlyCurrentPeriod) {
+  SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+  EXPECT_EQ((size_t)1, possible_advertisements.size());
+  EXPECT_EQ(GenerateFakeAdvertisement(
+                kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+                kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[0]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_OnlyFuturePeriod) {
+  SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
+              kNumMsInEidPeriod);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+  EXPECT_EQ((size_t)1, possible_advertisements.size());
+  EXPECT_EQ(GenerateFakeAdvertisement(
+                kFirstSeed, kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod,
+                kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[0]);
+}
+
+TEST_F(
+    CryptAuthEidGeneratorTest,
+    TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInFuture) {
+  SetTestTime(kDefaultCurrentPeriodStart - kNumMsInEidSeedPeriod -
+              kNumMsInEidPeriod - 1);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+  EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_OnlyPastPeriod) {
+  SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
+              kNumMsInEidPeriod);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+
+  EXPECT_EQ((size_t)1, possible_advertisements.size());
+  EXPECT_EQ(GenerateFakeAdvertisement(
+                kFourthSeed, kDefaultCurrentPeriodStart +
+                                 3 * kNumMsInEidSeedPeriod - kNumMsInEidPeriod,
+                kDefaultAdvertisingDevicePublicKey),
+            possible_advertisements[0]);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_NoAdvertisements_SeedsTooFarInPast) {
+  SetTestTime(kDefaultCurrentPeriodStart + 3 * kNumMsInEidSeedPeriod +
+              kNumMsInEidPeriod + 1);
+
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, scanning_device_beacon_seeds_);
+  EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestGeneratePossibleAdvertisements_NoAdvertisements_EmptySeeds) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::vector<BeaconSeed> empty;
+  std::vector<std::string> possible_advertisements =
+      eid_generator_->GeneratePossibleAdvertisements(
+          kDefaultAdvertisingDevicePublicKey, empty);
+  EXPECT_TRUE(possible_advertisements.empty());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_NoDevices) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::string service_data =
+      GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                kDefaultAdvertisingDevicePublicKey);
+
+  std::vector<RemoteDevice> device_list;
+  const RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, device_list, scanning_device_beacon_seeds_);
+  EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_OneDevice_Success) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::string service_data =
+      GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                kDefaultAdvertisingDevicePublicKey);
+
+  RemoteDevice correct_device =
+      CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+  std::vector<RemoteDevice> device_list = {correct_device};
+  const RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, device_list, scanning_device_beacon_seeds_);
+  EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestIdentifyRemoteDevice_OneDevice_Failure) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::string service_data =
+      GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                kDefaultAdvertisingDevicePublicKey);
+
+  RemoteDevice wrong_device =
+      CreateRemoteDevice("wrongPublicKey");
+  std::vector<RemoteDevice> device_list = {wrong_device};
+  const RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, device_list, scanning_device_beacon_seeds_);
+  EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestIdentifyRemoteDevice_MultipleDevices_Success) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::string service_data =
+      GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                kDefaultAdvertisingDevicePublicKey);
+
+  RemoteDevice correct_device =
+      CreateRemoteDevice(kDefaultAdvertisingDevicePublicKey);
+  RemoteDevice wrong_device =
+      CreateRemoteDevice("wrongPublicKey");
+  std::vector<RemoteDevice> device_list = {correct_device,
+                                                           wrong_device};
+  const RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, device_list, scanning_device_beacon_seeds_);
+  EXPECT_EQ(correct_device.public_key, identified_device->public_key);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestIdentifyRemoteDevice_MultipleDevices_Failure) {
+  SetTestTime(kDefaultCurrentPeriodStart);
+
+  std::string service_data =
+      GenerateFakeAdvertisement(kSecondSeed, kDefaultCurrentPeriodStart,
+                                kDefaultAdvertisingDevicePublicKey);
+
+  RemoteDevice wrong_device =
+      CreateRemoteDevice("wrongPublicKey");
+  std::vector<RemoteDevice> device_list = {wrong_device,
+                                                           wrong_device};
+  const RemoteDevice* identified_device =
+      eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+          service_data, device_list, scanning_device_beacon_seeds_);
+  EXPECT_FALSE(identified_device);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestEidComputationHelperImpl_ProducesTwoByteValue) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string eid = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid.length());
+}
+
+TEST_F(CryptAuthEidGeneratorTest, TestEidComputationHelperImpl_Deterministic) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string eid1 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+  std::string eid2 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+  EXPECT_EQ(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestEidComputationHelperImpl_ChangingSeedChangesOutput) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string eid1 = helper.GenerateEidDataForDevice(
+      "eidSeed1", kDefaultCurrentPeriodStart, nullptr);
+  std::string eid2 = helper.GenerateEidDataForDevice(
+      "eidSeed2", kDefaultCurrentPeriodStart, nullptr);
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+  EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestEidComputationHelperImpl_ChangingTimestampChangesOutput) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string eid1 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+  std::string eid2 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart + 1, nullptr);
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+  EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       TestEidComputationHelperImpl_ChangingExtraEntropyChangesOutput) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string eid1 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, nullptr);
+  std::string extra_entropy = "extraEntropy";
+  std::string eid2 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, &extra_entropy);
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid1.length());
+  EXPECT_EQ((size_t)kNumBytesInEidValue, eid2.length());
+  EXPECT_NE(eid1, eid2);
+}
+
+TEST_F(CryptAuthEidGeneratorTest,
+       testEidComputationHelper_ChangingTimestampWithLongExtraEntropy) {
+  EidGenerator::EidComputationHelperImpl helper;
+  std::string long_extra_entropy =
+      "reallyReallyReallyReallyReallyReallyReallyLongExtraEntropy";
+  std::string eid1 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart, &long_extra_entropy);
+  std::string extra_entropy = "extraEntropy";
+  std::string eid2 = helper.GenerateEidDataForDevice(
+      "eidSeed", kDefaultCurrentPeriodStart + 1, &long_extra_entropy);
+  EXPECT_NE(eid1, eid2);
+}
+
+// Tests that "known test vectors" are correct. This ensures that the same test
+// vectors produce the same outputs on other platforms.
+TEST_F(CryptAuthEidGeneratorTest,
+       TestEidComputationHelper_EnsureTestVectorsPass) {
+  const int8_t test_eid_seed1[] = {1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11,
+                                   12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                                   23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
+  const int64_t test_timestamp1 = 2468101214L;
+  const int8_t test_entropy1[] = {2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12,
+                                  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+                                  24, 25, 26, 27, 28, 29, 30, 31, 32, 33};
+
+  std::string test_eid_seed1_str(reinterpret_cast<const char*>(test_eid_seed1),
+                                 sizeof(test_eid_seed1));
+  std::string test_entropy1_str(reinterpret_cast<const char*>(test_entropy1),
+                                sizeof(test_entropy1));
+
+  const int8_t test_eid_seed2[] = {3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+                                   14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+                                   25, 26, 27, 28, 29, 30, 31, 32, 33, 34};
+  const int64_t test_timestamp2 = 51015202530L;
+  const int8_t test_entropy2[] = {4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
+                                  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+                                  26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
+
+  std::string test_eid_seed2_str(reinterpret_cast<const char*>(test_eid_seed2),
+                                 sizeof(test_eid_seed2));
+  std::string test_entropy2_str(reinterpret_cast<const char*>(test_entropy2),
+                                sizeof(test_entropy2));
+
+  EidGenerator::EidComputationHelperImpl helper;
+
+  std::string test_eid_without_entropy1 = helper.GenerateEidDataForDevice(
+      test_eid_seed1_str, test_timestamp1, nullptr);
+  EXPECT_EQ("\x7d\x2c", test_eid_without_entropy1);
+
+  std::string test_eid_with_entropy1 = helper.GenerateEidDataForDevice(
+      test_eid_seed1_str, test_timestamp1, &test_entropy1_str);
+  EXPECT_EQ("\xdc\xf3", test_eid_with_entropy1);
+
+  std::string test_eid_without_entropy2 = helper.GenerateEidDataForDevice(
+      test_eid_seed2_str, test_timestamp2, nullptr);
+  EXPECT_EQ("\x02\xdd", test_eid_without_entropy2);
+
+  std::string test_eid_with_entropy2 = helper.GenerateEidDataForDevice(
+      test_eid_seed2_str, test_timestamp2, &test_entropy2_str);
+  EXPECT_EQ("\xee\xcc", test_eid_with_entropy2);
+}
+
+}  // namespace cryptauth
diff --git a/components/cryptauth/remote_device.cc b/components/cryptauth/remote_device.cc
new file mode 100644
index 0000000..f30aee1
--- /dev/null
+++ b/components/cryptauth/remote_device.cc
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cryptauth/remote_device.h"
+
+namespace cryptauth {
+
+RemoteDevice::RemoteDevice() : bluetooth_type(BLUETOOTH_CLASSIC) {}
+
+RemoteDevice::RemoteDevice(const std::string& user_id,
+                           const std::string& name,
+                           const std::string& public_key,
+                           BluetoothType bluetooth_type,
+                           const std::string& bluetooth_address,
+                           const std::string& persistent_symmetric_key,
+                           std::string sign_in_challenge)
+    : user_id(user_id),
+      name(name),
+      public_key(public_key),
+      bluetooth_type(bluetooth_type),
+      bluetooth_address(bluetooth_address),
+      persistent_symmetric_key(persistent_symmetric_key),
+      sign_in_challenge(sign_in_challenge) {}
+
+RemoteDevice::RemoteDevice(const RemoteDevice& other) = default;
+
+RemoteDevice::~RemoteDevice() {}
+
+}  // namespace cryptauth
diff --git a/components/cryptauth/remote_device.h b/components/cryptauth/remote_device.h
new file mode 100644
index 0000000..a484a2a
--- /dev/null
+++ b/components/cryptauth/remote_device.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PROXIMITY_AUTH_REMOTE_DEVICE_H
+#define COMPONENTS_PROXIMITY_AUTH_REMOTE_DEVICE_H
+
+#include <string>
+#include <vector>
+
+namespace cryptauth {
+
+struct RemoteDevice {
+ public:
+  enum BluetoothType { BLUETOOTH_CLASSIC, BLUETOOTH_LE };
+
+  std::string user_id;
+  std::string name;
+  std::string public_key;
+  BluetoothType bluetooth_type;
+  std::string bluetooth_address;
+  std::string persistent_symmetric_key;
+  std::string sign_in_challenge;
+
+  RemoteDevice();
+  RemoteDevice(const std::string& user_id,
+               const std::string& name,
+               const std::string& public_key,
+               BluetoothType bluetooth_type,
+               const std::string& bluetooth_address,
+               const std::string& persistent_symmetric_key,
+               std::string sign_in_challenge);
+  RemoteDevice(const RemoteDevice& other);
+  ~RemoteDevice();
+};
+
+typedef std::vector<RemoteDevice> RemoteDeviceList;
+
+}  // namespace cryptauth
+
+#endif  // COMPONENTS_PROXIMITY_AUTH_REMOTE_DEVICE_H
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 8ba347f..4a2b45e2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -42,6 +42,10 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_status.h"
 
+#if defined(OS_ANDROID)
+#include "net/android/network_library.h"
+#endif  // OS_ANDROID
+
 using base::FieldTrialList;
 
 namespace {
@@ -300,6 +304,7 @@
       lofi_off_(false),
       network_quality_at_last_query_(NETWORK_QUALITY_AT_LAST_QUERY_UNKNOWN),
       previous_state_lofi_on_(false),
+      is_captive_portal_(false),
       weak_factory_(this) {
   DCHECK(io_task_runner_);
   DCHECK(configurator);
@@ -340,7 +345,17 @@
 
 void DataReductionProxyConfig::ReloadConfig() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  UpdateConfigurator(enabled_by_user_, secure_proxy_allowed_);
+  DCHECK(configurator_);
+
+  const std::vector<net::ProxyServer>& proxies_for_http =
+      config_values_->proxies_for_http();
+  if (enabled_by_user_ && !config_values_->holdback() &&
+      !proxies_for_http.empty()) {
+    configurator_->Enable(!secure_proxy_allowed_ || is_captive_portal_,
+                          proxies_for_http);
+  } else {
+    configurator_->Disable();
+  }
 }
 
 bool DataReductionProxyConfig::WasDataReductionProxyUsed(
@@ -628,10 +643,12 @@
 void DataReductionProxyConfig::SetProxyConfig(bool enabled, bool at_startup) {
   DCHECK(thread_checker_.CalledOnValidThread());
   enabled_by_user_ = enabled;
-  UpdateConfigurator(enabled_by_user_, secure_proxy_allowed_);
+  ReloadConfig();
 
-  // Check if the proxy has been restricted explicitly by the carrier.
   if (enabled) {
+    HandleCaptivePortal();
+
+    // Check if the proxy has been restricted explicitly by the carrier.
     // It is safe to use base::Unretained here, since it gets executed
     // synchronously on the IO thread, and |this| outlives
     // |secure_proxy_checker_|.
@@ -642,16 +659,32 @@
   }
 }
 
-void DataReductionProxyConfig::UpdateConfigurator(bool enabled,
-                                                  bool secure_proxy_allowed) {
-  DCHECK(configurator_);
-  const std::vector<net::ProxyServer>& proxies_for_http =
-      config_values_->proxies_for_http();
-  if (enabled && !config_values_->holdback() && !proxies_for_http.empty()) {
-    configurator_->Enable(!secure_proxy_allowed, proxies_for_http);
-  } else {
-    configurator_->Disable();
-  }
+void DataReductionProxyConfig::HandleCaptivePortal() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  bool is_captive_portal = GetIsCaptivePortal();
+  UMA_HISTOGRAM_BOOLEAN("DataReductionProxy.CaptivePortalDetected.Platform",
+                        is_captive_portal);
+  if (is_captive_portal == is_captive_portal_)
+    return;
+  is_captive_portal_ = is_captive_portal;
+  ReloadConfig();
+}
+
+bool DataReductionProxyConfig::GetIsCaptivePortal() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+#if defined(OS_ANDROID)
+  return net::android::GetIsCaptivePortal();
+#endif  // OS_ANDROID
+  return false;
+}
+
+void DataReductionProxyConfig::UpdateConfigForTesting(
+    bool enabled,
+    bool secure_proxy_allowed) {
+  enabled_by_user_ = enabled;
+  secure_proxy_allowed_ = secure_proxy_allowed;
 }
 
 void DataReductionProxyConfig::HandleSecureProxyCheckResponse(
@@ -707,13 +740,7 @@
     // quality prediction accuracy if there was a change in the IP address.
     network_quality_at_last_query_ = NETWORK_QUALITY_AT_LAST_QUERY_UNKNOWN;
 
-    bool should_use_secure_proxy = true;
-    if (!should_use_secure_proxy && secure_proxy_allowed_) {
-      secure_proxy_allowed_ = false;
-      RecordSecureProxyCheckFetchResult(PROXY_DISABLED_BEFORE_CHECK);
-      ReloadConfig();
-    }
-
+    HandleCaptivePortal();
     // It is safe to use base::Unretained here, since it gets executed
     // synchronously on the IO thread, and |this| outlives
     // |secure_proxy_checker_|.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 2eacf0c..385bf60 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -211,17 +211,13 @@
 
   virtual base::TimeTicks GetTicksNow() const;
 
+  // Updates the Data Reduction Proxy configurator with the current config.
+  void UpdateConfigForTesting(bool enabled, bool restricted);
+
  private:
-  friend class DataReductionProxyConfigTest;
   friend class MockDataReductionProxyConfig;
   friend class TestDataReductionProxyConfig;
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
-                           TestGetDataReductionProxies);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
-                           TestOnIPAddressChanged);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
-                           TestOnIPAddressChanged_SecureProxyDisabledByDefault);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
                            TestSetProxyConfigsHoldback);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
                            AreProxiesBypassed);
@@ -246,9 +242,6 @@
   // NetworkChangeNotifier::IPAddressObserver:
   void OnIPAddressChanged() override;
 
-  // Updates the Data Reduction Proxy configurator with the current config.
-  virtual void UpdateConfigurator(bool enabled, bool restricted);
-
   // Populates the parameters for the Lo-Fi field trial if the session is part
   // of either Lo-Fi enabled or Lo-Fi control field trial group.
   void PopulateAutoLoFiParams();
@@ -306,6 +299,15 @@
   bool IsEffectiveConnectionTypeSlowerThanThreshold(
       net::EffectiveConnectionType effective_connection_type) const;
 
+  // Checks if the current network has captive portal, and handles the result.
+  // If the captive portal probe was blocked on the current network, disables
+  // the use of secure proxies.
+  void HandleCaptivePortal();
+
+  // Returns true if the current network has captive portal. Virtualized
+  // for testing.
+  virtual bool GetIsCaptivePortal() const;
+
   std::unique_ptr<SecureProxyChecker> secure_proxy_checker_;
 
   // Indicates if the secure Data Reduction Proxy can be used or not.
@@ -380,6 +382,10 @@
   // quality prediction is recorded.
   std::vector<base::TimeDelta> lofi_accuracy_recording_intervals_;
 
+  // Set to true if the captive portal probe for the current network has been
+  // blocked.
+  bool is_captive_portal_;
+
   base::WeakPtrFactory<DataReductionProxyConfig> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfig);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index b30a9b9..5431ebf 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -224,7 +224,8 @@
   }
 
   void SetDataReductionProxyEnabled(bool enabled, bool secure_proxy_allowed) {
-    test_context_->config()->SetStateForTest(enabled, secure_proxy_allowed);
+    test_context_->config()->UpdateConfigForTesting(enabled,
+                                                    secure_proxy_allowed);
   }
 
   void ResetBackoffEntryReleaseTime() {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index ac04ec6a..470bc614 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -54,7 +54,8 @@
       tick_clock_(nullptr),
       network_quality_prohibitively_slow_set_(false),
       network_quality_prohibitively_slow_(false),
-      lofi_accuracy_recording_intervals_set_(false) {
+      lofi_accuracy_recording_intervals_set_(false),
+      is_captive_portal_(false) {
   network_interfaces_.reset(new net::NetworkInterfaceList());
 }
 
@@ -89,13 +90,6 @@
   return config_values_.get();
 }
 
-void TestDataReductionProxyConfig::SetStateForTest(
-    bool enabled_by_user,
-    bool secure_proxy_allowed) {
-  enabled_by_user_ = enabled_by_user;
-  secure_proxy_allowed_ = secure_proxy_allowed;
-}
-
 void TestDataReductionProxyConfig::ResetLoFiStatusForTest() {
   lofi_off_ = false;
 }
@@ -157,6 +151,14 @@
   proxy_index_.reset();
 }
 
+void TestDataReductionProxyConfig::SetIsCaptivePortal(bool is_captive_portal) {
+  is_captive_portal_ = is_captive_portal;
+}
+
+bool TestDataReductionProxyConfig::GetIsCaptivePortal() const {
+  return is_captive_portal_;
+}
+
 MockDataReductionProxyConfig::MockDataReductionProxyConfig(
     std::unique_ptr<DataReductionProxyConfigValues> config_values,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
@@ -172,12 +174,6 @@
 MockDataReductionProxyConfig::~MockDataReductionProxyConfig() {
 }
 
-void MockDataReductionProxyConfig::UpdateConfigurator(
-    bool enabled,
-    bool secure_proxy_allowed) {
-  DataReductionProxyConfig::UpdateConfigurator(enabled, secure_proxy_allowed);
-}
-
 void MockDataReductionProxyConfig::ResetLoFiStatusForTest() {
   lofi_off_ = false;
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index f3de4a36..05b59b3 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -74,10 +74,6 @@
   // TODO(jeremyim): Rationalize with test_params().
   DataReductionProxyConfigValues* config_values();
 
-  // Allows tests to set the internal state.
-  void SetStateForTest(bool enabled_by_user,
-                       bool secure_proxy_enabled);
-
   // Resets the Lo-Fi status to default state.
   void ResetLoFiStatusForTest();
 
@@ -118,7 +114,14 @@
   // Resets the behavior of WasDataReductionProxyUsed() calls.
   void ResetWasDataReductionProxyUsed();
 
+  // Sets if the captive portal probe has been blocked for the current network.
+  void SetIsCaptivePortal(bool is_captive_portal);
+
+  using DataReductionProxyConfig::UpdateConfigForTesting;
+
  private:
+  bool GetIsCaptivePortal() const override;
+
   base::TickClock* tick_clock_;
 
   base::Optional<bool> was_data_reduction_proxy_used_;
@@ -133,6 +136,10 @@
   bool lofi_accuracy_recording_intervals_set_;
   std::vector<base::TimeDelta> lofi_accuracy_recording_intervals_;
 
+  // Set to true if the captive portal probe for the current network has been
+  // blocked.
+  bool is_captive_portal_;
+
   DISALLOW_COPY_AND_ASSIGN(TestDataReductionProxyConfig);
 };
 
@@ -172,7 +179,7 @@
       IsNetworkQualityProhibitivelySlow,
       bool(const net::NetworkQualityEstimator* network_quality_estimator));
 
-  void UpdateConfigurator(bool enabled, bool restricted) override;
+  using DataReductionProxyConfig::UpdateConfigForTesting;
 
   // Resets the Lo-Fi status to default state.
   void ResetLoFiStatusForTest();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 07b22bb..0535786 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -36,6 +36,7 @@
 #include "components/variations/variations_associated_data.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
 #include "net/http/http_status_code.h"
 #include "net/log/test_net_log.h"
 #include "net/nqe/effective_connection_type.h"
@@ -79,6 +80,9 @@
   ~DataReductionProxyConfigTest() override {}
 
   void SetUp() override {
+    net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
+    network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+
     test_context_ = DataReductionProxyTestContext::Builder()
                         .WithMockConfig()
                         .WithMockDataReductionProxyService()
@@ -126,6 +130,7 @@
 
   void CheckSecureProxyCheckOnIPChange(
       const std::string& response,
+      bool is_captive_portal,
       int response_code,
       net::URLRequestStatus status,
       SecureProxyCheckFetchResult expected_fetch_result,
@@ -140,7 +145,8 @@
         .Times(1)
         .WillRepeatedly(testing::WithArgs<1>(
             testing::Invoke(&responder, &TestResponder::ExecuteCallback)));
-    config()->OnIPAddressChanged();
+    config()->SetIsCaptivePortal(is_captive_portal);
+    net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
     test_context_->RunUntilIdle();
     EXPECT_EQ(expected_proxies_for_http, GetConfiguredProxiesForHttp());
 
@@ -154,6 +160,11 @@
     }
     histogram_tester.ExpectUniqueSample("DataReductionProxy.ProbeURL",
                                         expected_fetch_result, 1);
+
+    // Recorded on every IP change.
+    histogram_tester.ExpectUniqueSample(
+        "DataReductionProxy.CaptivePortalDetected.Platform", is_captive_portal,
+        1);
   }
 
   void RunUntilIdle() {
@@ -190,38 +201,14 @@
   }
 
  private:
+  std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+
   base::MessageLoopForIO message_loop_;
   std::unique_ptr<DataReductionProxyTestContext> test_context_;
   std::unique_ptr<TestDataReductionProxyParams> expected_params_;
 };
 
-TEST_F(DataReductionProxyConfigTest, TestUpdateConfigurator) {
-  const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
-      "https://secure_origin.net:443", net::ProxyServer::SCHEME_HTTP);
-  const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
-      "insecure_origin.net:80", net::ProxyServer::SCHEME_HTTP);
-  SetProxiesForHttpOnCommandLine({kHttpsProxy, kHttpProxy});
-
-  ResetSettings(true, true, true, false);
-
-  // Expect both secure and insecure proxies to be available when the DRP is
-  // enabled and secure proxies are allowed.
-  config()->UpdateConfigurator(true, true);
-  EXPECT_EQ(std::vector<net::ProxyServer>({kHttpsProxy, kHttpProxy}),
-            GetConfiguredProxiesForHttp());
-
-  // Expect only insecure proxies to be available when the DRP is enabled and
-  // secure proxies are not allowed.
-  config()->UpdateConfigurator(true, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>(1, kHttpProxy),
-            GetConfiguredProxiesForHttp());
-
-  // Expect no proxies to be available when the DRP is disabled.
-  config()->UpdateConfigurator(false, false);
-  EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp());
-}
-
-TEST_F(DataReductionProxyConfigTest, TestUpdateConfiguratorHoldback) {
+TEST_F(DataReductionProxyConfigTest, TestReloadConfigHoldback) {
   const net::ProxyServer kHttpsProxy = net::ProxyServer::FromURI(
       "https://secure_origin.net:443", net::ProxyServer::SCHEME_HTTP);
   const net::ProxyServer kHttpProxy = net::ProxyServer::FromURI(
@@ -230,7 +217,8 @@
 
   ResetSettings(true, true, true, true);
 
-  config()->UpdateConfigurator(true, false);
+  config()->UpdateConfigForTesting(true, false);
+  config()->ReloadConfig();
   EXPECT_EQ(std::vector<net::ProxyServer>(), GetConfiguredProxiesForHttp());
 }
 
@@ -245,19 +233,36 @@
   ResetSettings(true, true, true, false);
 
   // The proxy is enabled initially.
-  config()->enabled_by_user_ = true;
-  config()->secure_proxy_allowed_ = true;
-  config()->UpdateConfigurator(true, true);
+  config()->UpdateConfigForTesting(true, true);
+  config()->ReloadConfig();
 
   // IP address change triggers a secure proxy check that succeeds. Proxy
   // remains unrestricted.
-  CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
+  CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
                                   SUCCEEDED_PROXY_ALREADY_ENABLED,
                                   {kHttpsProxy, kHttpProxy});
 
+  // IP address change triggers a secure proxy check that succeeds but captive
+  // portal fails. Proxy is restricted.
+  CheckSecureProxyCheckOnIPChange("OK", true, net::HTTP_OK, kSuccess,
+                                  SUCCEEDED_PROXY_ALREADY_ENABLED,
+                                  std::vector<net::ProxyServer>(1, kHttpProxy));
+
+  // IP address change triggers a secure proxy check that fails. Proxy is
+  // restricted.
+  CheckSecureProxyCheckOnIPChange("Bad", false, net::HTTP_OK, kSuccess,
+                                  FAILED_PROXY_DISABLED,
+                                  std::vector<net::ProxyServer>(1, kHttpProxy));
+
+  // IP address change triggers a secure proxy check that succeeds. Proxies
+  // are unrestricted.
+  CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
+                                  SUCCEEDED_PROXY_ENABLED,
+                                  {kHttpsProxy, kHttpProxy});
+
   // IP address change triggers a secure proxy check that fails. Proxy is
   // restricted.
-  CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
+  CheckSecureProxyCheckOnIPChange("Bad", true, net::HTTP_OK, kSuccess,
                                   FAILED_PROXY_DISABLED,
                                   std::vector<net::ProxyServer>(1, kHttpProxy));
 
@@ -265,20 +270,20 @@
   // network changing again. This should be ignored, so the proxy should remain
   // unrestricted.
   CheckSecureProxyCheckOnIPChange(
-      std::string(), net::URLFetcher::RESPONSE_CODE_INVALID,
+      std::string(), false, net::URLFetcher::RESPONSE_CODE_INVALID,
       net::URLRequestStatus(net::URLRequestStatus::FAILED,
                             net::ERR_INTERNET_DISCONNECTED),
       INTERNET_DISCONNECTED, std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // IP address change triggers a secure proxy check that fails. Proxy remains
   // restricted.
-  CheckSecureProxyCheckOnIPChange("Bad", net::HTTP_OK, kSuccess,
+  CheckSecureProxyCheckOnIPChange("Bad", false, net::HTTP_OK, kSuccess,
                                   FAILED_PROXY_ALREADY_DISABLED,
                                   std::vector<net::ProxyServer>(1, kHttpProxy));
 
   // IP address change triggers a secure proxy check that succeeds. Proxy is
   // unrestricted.
-  CheckSecureProxyCheckOnIPChange("OK", net::HTTP_OK, kSuccess,
+  CheckSecureProxyCheckOnIPChange("OK", false, net::HTTP_OK, kSuccess,
                                   SUCCEEDED_PROXY_ENABLED,
                                   {kHttpsProxy, kHttpProxy});
 
@@ -286,7 +291,7 @@
   // network changing again. This should be ignored, so the proxy should remain
   // unrestricted.
   CheckSecureProxyCheckOnIPChange(
-      std::string(), net::URLFetcher::RESPONSE_CODE_INVALID,
+      std::string(), false, net::URLFetcher::RESPONSE_CODE_INVALID,
       net::URLRequestStatus(net::URLRequestStatus::FAILED,
                             net::ERR_INTERNET_DISCONNECTED),
       INTERNET_DISCONNECTED, {kHttpsProxy, kHttpProxy});
@@ -294,7 +299,7 @@
   // IP address change triggers a secure proxy check that fails because of a
   // redirect response, e.g. by a captive portal. Proxy is restricted.
   CheckSecureProxyCheckOnIPChange(
-      "Bad", net::HTTP_FOUND,
+      "Bad", false, net::HTTP_FOUND,
       net::URLRequestStatus(net::URLRequestStatus::CANCELED, net::ERR_ABORTED),
       FAILED_PROXY_DISABLED, std::vector<net::ProxyServer>(1, kHttpProxy));
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
index 71cd8350..3e6a34d5 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
@@ -883,8 +883,8 @@
       data_reduction_proxy_config.set_id(1);
     }
     EXPECT_NE(test.use_direct_proxy, data_reduction_proxy_config.is_valid());
-    config()->SetStateForTest(test.enabled_by_user /* enabled */,
-                              false /* at_startup */);
+    config()->UpdateConfigForTesting(test.enabled_by_user /* enabled */,
+                                     false /* at_startup */);
 
     net::ProxyRetryInfoMap empty_proxy_retry_info;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 32b5767..6800c4b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -619,7 +619,7 @@
       data_reduction_proxy_info.UseDirect();
     else
       data_reduction_proxy_info.UseNamedProxy("some.other.proxy");
-    config()->SetStateForTest(test.data_reduction_proxy_enabled, true);
+    config()->UpdateConfigForTesting(test.data_reduction_proxy_enabled, true);
     std::unique_ptr<net::URLRequest> request = context()->CreateRequest(
         GURL("http://www.google.com/"), net::RequestPriority::IDLE, nullptr);
     request->set_method("GET");
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
index b029fd2c..52566c0d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -46,8 +46,8 @@
                                             bool expected_restricted,
                                             bool expected_fallback_restricted) {
     test_context_->SetDataReductionProxyEnabled(initially_enabled);
-    test_context_->config()->SetStateForTest(initially_enabled,
-                                             request_succeeded);
+    test_context_->config()->UpdateConfigForTesting(initially_enabled,
+                                                    request_succeeded);
     ExpectSetProxyPrefs(expected_enabled, false);
     settings_->MaybeActivateDataReductionProxy(false);
     test_context_->RunUntilIdle();
@@ -63,7 +63,7 @@
 TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
   InitPrefMembers();
   // The proxy is disabled initially.
-  test_context_->config()->SetStateForTest(false, true);
+  test_context_->config()->UpdateConfigForTesting(false, true);
 
   EXPECT_FALSE(settings_->IsDataReductionProxyEnabled());
   EXPECT_FALSE(settings_->UpdateDataSavings(std::string(), 0, 0));
@@ -85,7 +85,7 @@
 TEST_F(DataReductionProxySettingsTest, TestCanUseDataReductionProxy) {
   InitPrefMembers();
   // The proxy is disabled initially.
-  test_context_->config()->SetStateForTest(false, true);
+  test_context_->config()->UpdateConfigForTesting(false, true);
 
   GURL http_gurl("http://url.com/");
   EXPECT_FALSE(settings_->CanUseDataReductionProxy(http_gurl));
@@ -248,7 +248,7 @@
           .Build();
 
   // The proxy is enabled initially.
-  drp_test_context->config()->SetStateForTest(true, true);
+  drp_test_context->config()->UpdateConfigForTesting(true, true);
   drp_test_context->InitSettings();
 
   MockDataReductionProxyService* mock_service =
diff --git a/components/data_use_measurement/core/BUILD.gn b/components/data_use_measurement/core/BUILD.gn
index c74f8d42..c76d1e5 100644
--- a/components/data_use_measurement/core/BUILD.gn
+++ b/components/data_use_measurement/core/BUILD.gn
@@ -32,6 +32,10 @@
     "//components/metrics",
     "//net",
   ]
+
+  if (!is_ios) {
+    deps += [ "//components/domain_reliability" ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/components/data_use_measurement/core/DEPS b/components/data_use_measurement/core/DEPS
index de111298..ed27444 100644
--- a/components/data_use_measurement/core/DEPS
+++ b/components/data_use_measurement/core/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+net",
   "+components/metrics",
+  "+components/domain_reliability",
 ]
diff --git a/components/data_use_measurement/core/data_use_measurement.cc b/components/data_use_measurement/core/data_use_measurement.cc
index 5ded37e7..c7f1f695 100644
--- a/components/data_use_measurement/core/data_use_measurement.cc
+++ b/components/data_use_measurement/core/data_use_measurement.cc
@@ -10,6 +10,7 @@
 #include "build/build_config.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/data_use_measurement/core/url_request_classifier.h"
+#include "components/domain_reliability/uploader.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/upload_data_stream.h"
 #include "net/http/http_response_headers.h"
@@ -89,8 +90,18 @@
   DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>(
       request->GetUserData(DataUseUserData::kUserDataKey));
   if (!data_use_user_data) {
-    data_use_user_data = new DataUseUserData(
-        DataUseUserData::ServiceName::NOT_TAGGED, CurrentAppState());
+    DataUseUserData::ServiceName service_name =
+        DataUseUserData::ServiceName::NOT_TAGGED;
+    if (!url_request_classifier_->IsUserRequest(*request) &&
+        domain_reliability::DomainReliabilityUploader::
+            OriginatedFromDomainReliability(*request)) {
+      // Detect if the request originated from DomainReliability.
+      // DataUseUserData::AttachToFetcher() cannot be called from domain
+      // reliability, since it sets userdata on URLFetcher for its purposes.
+      service_name = DataUseUserData::ServiceName::NOT_TAGGED;
+    }
+
+    data_use_user_data = new DataUseUserData(service_name, CurrentAppState());
     request->SetUserData(DataUseUserData::kUserDataKey, data_use_user_data);
   }
 }
diff --git a/components/dom_distiller/core/distiller_page.cc b/components/dom_distiller/core/distiller_page.cc
index fb115d8..7e66e45 100644
--- a/components/dom_distiller/core/distiller_page.cc
+++ b/components/dom_distiller/core/distiller_page.cc
@@ -94,7 +94,7 @@
   std::unique_ptr<dom_distiller::proto::DomDistillerResult> distiller_result(
       new dom_distiller::proto::DomDistillerResult());
   bool found_content;
-  if (value->IsType(base::Value::TYPE_NULL)) {
+  if (value->IsType(base::Value::Type::NONE)) {
     found_content = false;
   } else {
     found_content =
diff --git a/components/dom_distiller/ios/distiller_page_ios.mm b/components/dom_distiller/ios/distiller_page_ios.mm
index a59548ae..8ae4962 100644
--- a/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/components/dom_distiller/ios/distiller_page_ios.mm
@@ -46,24 +46,24 @@
   CFTypeID result_type = CFGetTypeID(wk_result);
   if (result_type == CFStringGetTypeID()) {
     result.reset(new base::StringValue(base::SysNSStringToUTF16(wk_result)));
-    DCHECK(result->IsType(base::Value::TYPE_STRING));
+    DCHECK(result->IsType(base::Value::Type::STRING));
   } else if (result_type == CFNumberGetTypeID()) {
     // Different implementation is here.
     if ([wk_result intValue] != [wk_result doubleValue]) {
       result.reset(new base::FundamentalValue([wk_result doubleValue]));
-      DCHECK(result->IsType(base::Value::TYPE_DOUBLE));
+      DCHECK(result->IsType(base::Value::Type::DOUBLE));
     } else {
       result.reset(new base::FundamentalValue([wk_result intValue]));
-      DCHECK(result->IsType(base::Value::TYPE_INTEGER));
+      DCHECK(result->IsType(base::Value::Type::INTEGER));
     }
     // End of different implementation.
   } else if (result_type == CFBooleanGetTypeID()) {
     result.reset(
         new base::FundamentalValue(static_cast<bool>([wk_result boolValue])));
-    DCHECK(result->IsType(base::Value::TYPE_BOOLEAN));
+    DCHECK(result->IsType(base::Value::Type::BOOLEAN));
   } else if (result_type == CFNullGetTypeID()) {
     result = base::Value::CreateNullValue();
-    DCHECK(result->IsType(base::Value::TYPE_NULL));
+    DCHECK(result->IsType(base::Value::Type::NONE));
   } else if (result_type == CFDictionaryGetTypeID()) {
     std::unique_ptr<base::DictionaryValue> dictionary =
         base::MakeUnique<base::DictionaryValue>();
diff --git a/components/domain_reliability/BUILD.gn b/components/domain_reliability/BUILD.gn
index 52620af..edead27 100644
--- a/components/domain_reliability/BUILD.gn
+++ b/components/domain_reliability/BUILD.gn
@@ -81,7 +81,6 @@
   deps = [
     ":bake_in_configs",
     "//base",
-    "//components/data_use_measurement/core",
     "//components/keyed_service/core",
     "//content/public/common",
     "//net",
diff --git a/components/domain_reliability/DEPS b/components/domain_reliability/DEPS
index b5ddd35..e898ad0 100644
--- a/components/domain_reliability/DEPS
+++ b/components/domain_reliability/DEPS
@@ -4,7 +4,6 @@
 
 include_rules = [
   "+net",
-  "+components/data_use_measurement/core",
   "+components/keyed_service/core",
   "+content/public/common",
 ]
diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc
index 2da1d509..c789be78 100644
--- a/components/domain_reliability/monitor.cc
+++ b/components/domain_reliability/monitor.cc
@@ -287,6 +287,10 @@
 // static
 bool DomainReliabilityMonitor::RequestInfo::ShouldReportRequest(
     const DomainReliabilityMonitor::RequestInfo& request) {
+  // Always report upload requests, even though they have DO_NOT_SEND_COOKIES.
+  if (request.upload_depth > 0)
+    return true;
+
   // Don't report requests that weren't supposed to send cookies.
   if (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES)
     return false;
diff --git a/components/domain_reliability/monitor_unittest.cc b/components/domain_reliability/monitor_unittest.cc
index 6b75144b..8fc589a3 100644
--- a/components/domain_reliability/monitor_unittest.cc
+++ b/components/domain_reliability/monitor_unittest.cc
@@ -255,6 +255,22 @@
   DCHECK(kBakedInJsonConfigs[0] != nullptr);
 }
 
+// Make sure the monitor does log uploads, even though they have
+// LOAD_DO_NOT_SEND_COOKIES.
+TEST_F(DomainReliabilityMonitorTest, Upload) {
+  DomainReliabilityContext* context = CreateAndAddContext();
+
+  RequestInfo request = MakeRequestInfo();
+  request.url = GURL("http://example/");
+  request.load_flags =
+      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
+  request.status = net::URLRequestStatus::FromError(net::ERR_CONNECTION_RESET);
+  request.upload_depth = 1;
+  OnRequestLegComplete(request);
+
+  EXPECT_EQ(1u, CountQueuedBeacons(context));
+}
+
 // Will fail when baked-in configs expire, as a reminder to update them.
 // (Contact juliatuttle@chromium.org if this starts failing.)
 TEST_F(DomainReliabilityMonitorTest, AddBakedInConfigs) {
diff --git a/components/domain_reliability/uploader.cc b/components/domain_reliability/uploader.cc
index 3ded084..f7d4cd9 100644
--- a/components/domain_reliability/uploader.cc
+++ b/components/domain_reliability/uploader.cc
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/supports_user_data.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/domain_reliability/util.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -83,8 +82,6 @@
     std::unique_ptr<net::URLFetcher> owned_fetcher =
         net::URLFetcher::Create(0, upload_url, net::URLFetcher::POST, this);
     net::URLFetcher* fetcher = owned_fetcher.get();
-    data_use_measurement::DataUseUserData::AttachToFetcher(
-        fetcher, data_use_measurement::DataUseUserData::DOMAIN_RELIABILITY);
     fetcher->SetRequestContext(url_request_context_getter_.get());
     fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
                           net::LOAD_DO_NOT_SAVE_COOKIES);
@@ -178,6 +175,12 @@
 }
 
 // static
+bool DomainReliabilityUploader::OriginatedFromDomainReliability(
+    const net::URLRequest& request) {
+  return request.GetUserData(UploadUserData::kUserDataKey) != nullptr;
+}
+
+// static
 int DomainReliabilityUploader::GetURLRequestUploadDepth(
     const net::URLRequest& request) {
   UploadUserData* data = static_cast<UploadUserData*>(
diff --git a/components/domain_reliability/uploader.h b/components/domain_reliability/uploader.h
index 0cba482..6962711c 100644
--- a/components/domain_reliability/uploader.h
+++ b/components/domain_reliability/uploader.h
@@ -55,6 +55,9 @@
       const scoped_refptr<net::URLRequestContextGetter>&
           url_request_context_getter);
 
+  // Returns true if the request originated from domain reliability uploader.
+  static bool OriginatedFromDomainReliability(const net::URLRequest& request);
+
   // Uploads |report_json| to |upload_url| and calls |callback| when the upload
   // has either completed or failed.
   virtual void UploadReport(const std::string& report_json,
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
index f0ed4b1..26b23f3 100644
--- a/components/drive/service/fake_drive_service.cc
+++ b/components/drive/service/fake_drive_service.cc
@@ -260,7 +260,7 @@
 
   // Load JSON data, which must be a dictionary.
   std::unique_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+  CHECK_EQ(base::Value::Type::DICTIONARY, value->GetType());
   app_info_value_.reset(
       static_cast<base::DictionaryValue*>(value.release()));
   return !!app_info_value_;
@@ -288,7 +288,7 @@
   JSONStringValueDeserializer json(app_json);
   std::string error_message;
   std::unique_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+  CHECK_EQ(base::Value::Type::DICTIONARY, value->GetType());
 
   base::ListValue* item_list;
   CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
diff --git a/components/error_page/common/BUILD.gn b/components/error_page/common/BUILD.gn
index 98cd30db..16eaa84 100644
--- a/components/error_page/common/BUILD.gn
+++ b/components/error_page/common/BUILD.gn
@@ -17,7 +17,7 @@
   deps = [
     "//base",
     "//base:i18n",
-    "//components/offline_pages:switches",
+    "//components/offline_pages/core:switches",
     "//components/strings",
     "//components/url_formatter",
     "//net",
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index ef5bf3c..e81da54 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -37,7 +37,7 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "components/offline_pages/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_feature.h"
 #endif
 
 namespace error_page {
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 55100cd..3e2fc4ae 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -162,10 +162,6 @@
     "//ash/resources:ash_test_resources_200_percent",
   ]
 
-  if (use_x11) {
-    deps += [ "//tools/xdisplaycheck" ]
-  }
-
   if (is_linux) {
     deps += [ "//components/exo/wayland:unit_tests" ]
   }
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index d2419a5..8c5ed61 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -792,8 +792,9 @@
                               ? gfx::Rect(contents_surface_size)
                               : gfx::SkIRectToRect(pending_damage_.getBounds());
 
+  const int kRenderPassId = 1;
   std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
-  render_pass->SetAll(cc::RenderPassId(1, 1), gfx::Rect(contents_surface_size),
+  render_pass->SetAll(kRenderPassId, gfx::Rect(contents_surface_size),
                       damage_rect, gfx::Transform(), true);
 
   gfx::Rect quad_rect = gfx::Rect(contents_surface_size);
diff --git a/components/filesystem/directory_impl.cc b/components/filesystem/directory_impl.cc
index 745bcf6..08625f66 100644
--- a/components/filesystem/directory_impl.cc
+++ b/components/filesystem/directory_impl.cc
@@ -17,7 +17,6 @@
 #include "components/filesystem/util.h"
 #include "mojo/common/common_type_converters.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace filesystem {
 
@@ -92,9 +91,9 @@
 void DirectoryImpl::OpenFileHandle(const std::string& raw_path,
                                    uint32_t open_flags,
                                    const OpenFileHandleCallback& callback) {
-  mojom::FileError error = mojom::FileError::OK;
-  mojo::ScopedHandle handle = OpenFileHandleImpl(raw_path, open_flags, &error);
-  callback.Run(error, std::move(handle));
+  base::File file = OpenFileHandleImpl(raw_path, open_flags);
+  mojom::FileError error = GetError(file);
+  callback.Run(error, std::move(file));
 }
 
 void DirectoryImpl::OpenFileHandles(
@@ -105,8 +104,8 @@
   for (const auto& detail : details) {
     mojom::FileOpenResultPtr result(mojom::FileOpenResult::New());
     result->path = detail->path;
-    result->file_handle =
-        OpenFileHandleImpl(detail->path, detail->open_flags, &result->error);
+    result->file_handle = OpenFileHandleImpl(detail->path, detail->open_flags);
+    result->error = GetError(result->file_handle);
     results[i++] = std::move(result);
   }
   callback.Run(std::move(results));
@@ -335,31 +334,21 @@
   callback.Run(mojom::FileError::OK);
 }
 
-mojo::ScopedHandle DirectoryImpl::OpenFileHandleImpl(
-    const std::string& raw_path,
-    uint32_t open_flags,
-    mojom::FileError* error) {
+base::File DirectoryImpl::OpenFileHandleImpl(const std::string& raw_path,
+                                             uint32_t open_flags) {
   base::FilePath path;
-  *error = ValidatePath(raw_path, directory_path_, &path);
-  if (*error != mojom::FileError::OK)
-    return mojo::ScopedHandle();
+  mojom::FileError error = ValidatePath(raw_path, directory_path_, &path);
+  if (error != mojom::FileError::OK)
+    return base::File(static_cast<base::File::Error>(error));
 
   if (base::DirectoryExists(path)) {
     // We must not return directories as files. In the file abstraction, we
     // can fetch raw file descriptors over mojo pipes, and passing a file
     // descriptor to a directory is a sandbox escape on Windows.
-    *error = mojom::FileError::NOT_A_FILE;
-    return mojo::ScopedHandle();
+    return base::File(base::File::FILE_ERROR_NOT_A_FILE);
   }
 
-  base::File base_file(path, open_flags);
-  if (!base_file.IsValid()) {
-    *error = GetError(base_file);
-    return mojo::ScopedHandle();
-  }
-
-  *error = mojom::FileError::OK;
-  return mojo::WrapPlatformFile(base_file.TakePlatformFile());
+  return base::File(path, open_flags);
 }
 
 }  // namespace filesystem
diff --git a/components/filesystem/directory_impl.h b/components/filesystem/directory_impl.h
index 9f1a141..e226b785 100644
--- a/components/filesystem/directory_impl.h
+++ b/components/filesystem/directory_impl.h
@@ -64,9 +64,8 @@
                  const WriteFileCallback& callback) override;
 
  private:
-  mojo::ScopedHandle OpenFileHandleImpl(const std::string& raw_path,
-                                        uint32_t open_flags,
-                                        mojom::FileError* error);
+  base::File OpenFileHandleImpl(const std::string& raw_path,
+                                uint32_t open_flags);
 
   base::FilePath directory_path_;
   scoped_refptr<SharedTempDir> temp_dir_;
diff --git a/components/filesystem/file_impl.cc b/components/filesystem/file_impl.cc
index 25a76f3d..d15c92c 100644
--- a/components/filesystem/file_impl.cc
+++ b/components/filesystem/file_impl.cc
@@ -18,13 +18,11 @@
 #include "components/filesystem/util.h"
 #include "mojo/common/common_type_converters.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 static_assert(sizeof(off_t) <= sizeof(int64_t), "off_t too big");
 static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t too small");
 
 using base::Time;
-using mojo::ScopedHandle;
 
 namespace filesystem {
 namespace {
@@ -327,19 +325,19 @@
 
 void FileImpl::AsHandle(const AsHandleCallback& callback) {
   if (!file_.IsValid()) {
-    callback.Run(GetError(file_), ScopedHandle());
+    callback.Run(GetError(file_), base::File());
     return;
   }
 
   base::File new_file = file_.Duplicate();
   if (!new_file.IsValid()) {
-    callback.Run(GetError(new_file), ScopedHandle());
+    callback.Run(GetError(new_file), base::File());
     return;
   }
 
   base::File::Info info;
   if (!new_file.GetInfo(&info)) {
-    callback.Run(mojom::FileError::FAILED, ScopedHandle());
+    callback.Run(mojom::FileError::FAILED, base::File());
     return;
   }
 
@@ -348,12 +346,11 @@
   // passing a file descriptor to a directory is a sandbox escape on Windows,
   // we should be absolutely paranoid.
   if (info.is_directory) {
-    callback.Run(mojom::FileError::NOT_A_FILE, ScopedHandle());
+    callback.Run(mojom::FileError::NOT_A_FILE, base::File());
     return;
   }
 
-  callback.Run(mojom::FileError::OK,
-               mojo::WrapPlatformFile(new_file.TakePlatformFile()));
+  callback.Run(mojom::FileError::OK, std::move(new_file));
 }
 
 }  // namespace filesystem
diff --git a/components/filesystem/file_impl_unittest.cc b/components/filesystem/file_impl_unittest.cc
index 81544b48..be20bfb 100644
--- a/components/filesystem/file_impl_unittest.cc
+++ b/components/filesystem/file_impl_unittest.cc
@@ -11,7 +11,6 @@
 #include "components/filesystem/files_test_base.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/type_converter.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace filesystem {
 namespace {
@@ -663,21 +662,13 @@
     ASSERT_TRUE(handled);
     EXPECT_EQ(mojom::FileError::OK, error);
 
-    // Fetch the handle
+    // Fetch the file.
     error = mojom::FileError::FAILED;
-    mojo::ScopedHandle handle;
-    handled = file1->AsHandle(&error, &handle);
+    base::File raw_file;
+    handled = file1->AsHandle(&error, &raw_file);
     ASSERT_TRUE(handled);
     EXPECT_EQ(mojom::FileError::OK, error);
 
-    // Pull a file descriptor out of the scoped handle.
-    base::PlatformFile platform_file;
-    MojoResult unwrap_result = mojo::UnwrapPlatformFile(std::move(handle),
-                                                        &platform_file);
-    EXPECT_EQ(MOJO_RESULT_OK, unwrap_result);
-
-    // Pass this raw file descriptor to a base::File.
-    base::File raw_file(platform_file);
     ASSERT_TRUE(raw_file.IsValid());
     EXPECT_EQ(5, raw_file.WriteAtCurrentPos("hello", 5));
   }
diff --git a/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc b/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
index 0a5d660..7f6a85192 100644
--- a/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
+++ b/components/filesystem/public/cpp/prefs/filesystem_json_pref_store.cc
@@ -48,7 +48,7 @@
 namespace {
 
 PersistentPrefStore::PrefReadError HandleReadErrors(const base::Value* value) {
-  if (!value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value->IsType(base::Value::Type::DICTIONARY))
     return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
   return PersistentPrefStore::PREF_READ_ERROR_NONE;
 }
diff --git a/components/filesystem/public/interfaces/BUILD.gn b/components/filesystem/public/interfaces/BUILD.gn
index a1ef7b6..b65d3a7 100644
--- a/components/filesystem/public/interfaces/BUILD.gn
+++ b/components/filesystem/public/interfaces/BUILD.gn
@@ -11,4 +11,7 @@
     "file_system.mojom",
     "types.mojom",
   ]
+  public_deps = [
+    "//mojo/common:common_custom_types",
+  ]
 }
diff --git a/components/filesystem/public/interfaces/directory.mojom b/components/filesystem/public/interfaces/directory.mojom
index 104a62f5..ac3370e 100644
--- a/components/filesystem/public/interfaces/directory.mojom
+++ b/components/filesystem/public/interfaces/directory.mojom
@@ -6,6 +6,7 @@
 
 import "components/filesystem/public/interfaces/file.mojom";
 import "components/filesystem/public/interfaces/types.mojom";
+import "mojo/common/file.mojom";
 
 struct FileOpenDetails {
   string path;
@@ -15,7 +16,7 @@
 struct FileOpenResult {
   string path;
   FileError error;
-  handle file_handle;
+  mojo.common.mojom.File? file_handle;
 };
 
 // This interface provides access to a directory in a "file system", providing
@@ -46,7 +47,7 @@
   // native file descriptor wrapped in a MojoHandle.
   [Sync]
   OpenFileHandle(string path, uint32 open_flags)
-      => (FileError error, handle file_handle);
+      => (FileError error, mojo.common.mojom.File? file_handle);
 
   // Like OpenFileHandle, but opens multiple files.
   [Sync]
diff --git a/components/filesystem/public/interfaces/file.mojom b/components/filesystem/public/interfaces/file.mojom
index 05bce45c..9a4a515 100644
--- a/components/filesystem/public/interfaces/file.mojom
+++ b/components/filesystem/public/interfaces/file.mojom
@@ -10,6 +10,7 @@
 module filesystem.mojom;
 
 import "components/filesystem/public/interfaces/types.mojom";
+import "mojo/common/file.mojom";
 
 // TODO(vtl): Write comments.
 interface File {
@@ -84,5 +85,5 @@
 
   // Returns a handle to the file for raw access.
   [Sync]
-  AsHandle() => (FileError error, handle file_handle);
+  AsHandle() => (FileError error, mojo.common.mojom.File? file_handle);
 };
diff --git a/components/font_service/font_service_app.cc b/components/font_service/font_service_app.cc
index f78b4c2..78f6d4a 100644
--- a/components/font_service/font_service_app.cc
+++ b/components/font_service/font_service_app.cc
@@ -28,18 +28,13 @@
 
 namespace {
 
-mojo::ScopedHandle GetHandleForPath(const base::FilePath& path) {
+base::File GetFileForPath(const base::FilePath& path) {
   if (path.empty())
-    return mojo::ScopedHandle();
+    return base::File();
 
-  mojo::ScopedHandle to_pass;
   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!file.IsValid()) {
-    LOG(WARNING) << "file not valid, path=" << path.value();
-    return mojo::ScopedHandle();
-  }
-
-  return mojo::WrapPlatformFile(file.TakePlatformFile());
+  LOG_IF(WARNING, !file.IsValid()) << "file not valid, path=" << path.value();
+  return file;
 }
 
 }  // namespace
@@ -109,12 +104,12 @@
 
 void FontServiceApp::OpenStream(uint32_t id_number,
                                 const OpenStreamCallback& callback) {
-  mojo::ScopedHandle handle;
+  base::File file;
   if (id_number < static_cast<uint32_t>(paths_.size())) {
-    handle = GetHandleForPath(base::FilePath(paths_[id_number].c_str()));
+    file = GetFileForPath(base::FilePath(paths_[id_number].c_str()));
   }
 
-  callback.Run(std::move(handle));
+  callback.Run(std::move(file));
 }
 
 int FontServiceApp::FindOrAddPath(const SkString& path) {
diff --git a/components/font_service/public/cpp/font_service_thread.cc b/components/font_service/public/cpp/font_service_thread.cc
index 69aba5e..4c14737 100644
--- a/components/font_service/public/cpp/font_service_thread.cc
+++ b/components/font_service/public/cpp/font_service_thread.cc
@@ -10,7 +10,6 @@
 #include "base/files/file.h"
 #include "base/synchronization/waitable_event.h"
 #include "components/font_service/public/cpp/mapped_font_file.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace font_service {
 namespace internal {
@@ -158,15 +157,9 @@
 
 void FontServiceThread::OnOpenStreamComplete(base::WaitableEvent* done_event,
                                              base::File* output_file,
-                                             mojo::ScopedHandle handle) {
+                                             base::File file) {
   pending_waitable_events_.erase(done_event);
-  if (handle.is_valid()) {
-    base::PlatformFile platform_file;
-    CHECK_EQ(mojo::UnwrapPlatformFile(std::move(handle), &platform_file),
-             MOJO_RESULT_OK);
-    *output_file = base::File(platform_file);
-  }
-
+  *output_file = std::move(file);
   done_event->Signal();
 }
 
diff --git a/components/font_service/public/cpp/font_service_thread.h b/components/font_service/public/cpp/font_service_thread.h
index 9f698f8..6cfc862 100644
--- a/components/font_service/public/cpp/font_service_thread.h
+++ b/components/font_service/public/cpp/font_service_thread.h
@@ -80,7 +80,7 @@
                       const uint32_t id_number);
   void OnOpenStreamComplete(base::WaitableEvent* done_event,
                             base::File* output_file,
-                            mojo::ScopedHandle handle);
+                            base::File file);
 
   // Connection to |font_service_| has gone away. Called on the background
   // thread.
diff --git a/components/font_service/public/interfaces/BUILD.gn b/components/font_service/public/interfaces/BUILD.gn
index e60d556..12efaaf 100644
--- a/components/font_service/public/interfaces/BUILD.gn
+++ b/components/font_service/public/interfaces/BUILD.gn
@@ -8,4 +8,8 @@
   sources = [
     "font_service.mojom",
   ]
+
+  public_deps = [
+    "//mojo/common:common_custom_types",
+  ]
 }
diff --git a/components/font_service/public/interfaces/font_service.mojom b/components/font_service/public/interfaces/font_service.mojom
index ea913d48f..9429706 100644
--- a/components/font_service/public/interfaces/font_service.mojom
+++ b/components/font_service/public/interfaces/font_service.mojom
@@ -4,6 +4,8 @@
 
 module font_service.mojom;
 
+import "mojo/common/file.mojom";
+
 enum TypefaceSlant {
   ROMAN = 0,
   ITALIC = 1,
@@ -39,5 +41,5 @@
       (FontIdentity? identity, string family_name, TypefaceStyle style);
 
   // Returns a handle to the raw font specified by |id_number|.
-  OpenStream(uint32 id_number) => (handle font_handle);
+  OpenStream(uint32 id_number) => (mojo.common.mojom.File? font_handle);
 };
diff --git a/components/history/core/browser/web_history_service.cc b/components/history/core/browser/web_history_service.cc
index abe202c..c74348c0 100644
--- a/components/history/core/browser/web_history_service.cc
+++ b/components/history/core/browser/web_history_service.cc
@@ -360,7 +360,7 @@
   if (request->GetResponseCode() == net::HTTP_OK) {
     std::unique_ptr<base::Value> value =
         base::JSONReader::Read(request->GetResponseBody());
-    if (value.get() && value.get()->IsType(base::Value::TYPE_DICTIONARY))
+    if (value.get() && value.get()->IsType(base::Value::Type::DICTIONARY))
       result.reset(static_cast<base::DictionaryValue*>(value.release()));
     else
       DLOG(WARNING) << "Non-JSON response received from history server.";
diff --git a/components/json_schema/json_schema_validator.cc b/components/json_schema/json_schema_validator.cc
index cf3b3da..695b8c96d 100644
--- a/components/json_schema/json_schema_validator.cc
+++ b/components/json_schema/json_schema_validator.cc
@@ -79,25 +79,25 @@
   // binary search.
   static const ExpectedType kExpectedTypes[] = {
     // Note: kRef == "$ref", kSchema == "$schema"
-    { schema::kRef,                     base::Value::TYPE_STRING      },
-    { schema::kSchema,                  base::Value::TYPE_STRING      },
+    { schema::kRef,                     base::Value::Type::STRING      },
+    { schema::kSchema,                  base::Value::Type::STRING      },
 
-    { schema::kAdditionalProperties,    base::Value::TYPE_DICTIONARY  },
-    { schema::kChoices,                 base::Value::TYPE_LIST        },
-    { schema::kDescription,             base::Value::TYPE_STRING      },
-    { schema::kEnum,                    base::Value::TYPE_LIST        },
-    { schema::kId,                      base::Value::TYPE_STRING      },
-    { schema::kMaxItems,                base::Value::TYPE_INTEGER     },
-    { schema::kMaxLength,               base::Value::TYPE_INTEGER     },
-    { schema::kMaximum,                 base::Value::TYPE_DOUBLE      },
-    { schema::kMinItems,                base::Value::TYPE_INTEGER     },
-    { schema::kMinLength,               base::Value::TYPE_INTEGER     },
-    { schema::kMinimum,                 base::Value::TYPE_DOUBLE      },
-    { schema::kOptional,                base::Value::TYPE_BOOLEAN     },
-    { schema::kPattern,                 base::Value::TYPE_STRING      },
-    { schema::kPatternProperties,       base::Value::TYPE_DICTIONARY  },
-    { schema::kProperties,              base::Value::TYPE_DICTIONARY  },
-    { schema::kTitle,                   base::Value::TYPE_STRING      },
+    { schema::kAdditionalProperties,    base::Value::Type::DICTIONARY  },
+    { schema::kChoices,                 base::Value::Type::LIST        },
+    { schema::kDescription,             base::Value::Type::STRING      },
+    { schema::kEnum,                    base::Value::Type::LIST        },
+    { schema::kId,                      base::Value::Type::STRING      },
+    { schema::kMaxItems,                base::Value::Type::INTEGER     },
+    { schema::kMaxLength,               base::Value::Type::INTEGER     },
+    { schema::kMaximum,                 base::Value::Type::DOUBLE      },
+    { schema::kMinItems,                base::Value::Type::INTEGER     },
+    { schema::kMinLength,               base::Value::Type::INTEGER     },
+    { schema::kMinimum,                 base::Value::Type::DOUBLE      },
+    { schema::kOptional,                base::Value::Type::BOOLEAN     },
+    { schema::kPattern,                 base::Value::Type::STRING      },
+    { schema::kPatternProperties,       base::Value::Type::DICTIONARY  },
+    { schema::kProperties,              base::Value::Type::DICTIONARY  },
+    { schema::kTitle,                   base::Value::Type::STRING      },
   };
 
   bool has_type_or_ref = false;
@@ -109,14 +109,14 @@
     // Validate the "type" attribute, which may be a string or a list.
     if (it.key() == schema::kType) {
       switch (it.value().GetType()) {
-        case base::Value::TYPE_STRING:
+        case base::Value::Type::STRING:
           it.value().GetAsString(&string_value);
           if (!IsValidType(string_value)) {
             *error = "Invalid value for type attribute";
             return false;
           }
           break;
-        case base::Value::TYPE_LIST:
+        case base::Value::Type::LIST:
           it.value().GetAsList(&list_value);
           for (size_t i = 0; i < list_value->GetSize(); ++i) {
             if (!list_value->GetString(i, &string_value) ||
@@ -174,16 +174,16 @@
 
     // Integer can be converted to double.
     if (!(it.value().IsType(entry->type) ||
-          (it.value().IsType(base::Value::TYPE_INTEGER) &&
-           entry->type == base::Value::TYPE_DOUBLE))) {
+          (it.value().IsType(base::Value::Type::INTEGER) &&
+           entry->type == base::Value::Type::DOUBLE))) {
       *error = base::StringPrintf("Invalid value for %s attribute",
                                   it.key().c_str());
       return false;
     }
 
-    // base::Value::TYPE_INTEGER attributes must be >= 0.
+    // base::Value::Type::INTEGER attributes must be >= 0.
     // This applies to "minItems", "maxItems", "minLength" and "maxLength".
-    if (it.value().IsType(base::Value::TYPE_INTEGER)) {
+    if (it.value().IsType(base::Value::Type::INTEGER)) {
       int integer_value;
       it.value().GetAsInteger(&integer_value);
       if (integer_value < 0) {
@@ -251,11 +251,11 @@
           return false;
         }
         switch (value->GetType()) {
-          case base::Value::TYPE_NULL:
-          case base::Value::TYPE_BOOLEAN:
-          case base::Value::TYPE_INTEGER:
-          case base::Value::TYPE_DOUBLE:
-          case base::Value::TYPE_STRING:
+          case base::Value::Type::NONE:
+          case base::Value::Type::BOOLEAN:
+          case base::Value::Type::INTEGER:
+          case base::Value::Type::DOUBLE:
+          case base::Value::Type::STRING:
             break;
           default:
             *error = "Invalid value in enum attribute";
@@ -344,13 +344,13 @@
 // static
 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return schema::kNull;
-    case base::Value::TYPE_BOOLEAN:
+    case base::Value::Type::BOOLEAN:
       return schema::kBoolean;
-    case base::Value::TYPE_INTEGER:
+    case base::Value::Type::INTEGER:
       return schema::kInteger;
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value = 0;
       value->GetAsDouble(&double_value);
       if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) &&
@@ -360,11 +360,11 @@
         return schema::kNumber;
       }
     }
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::STRING:
       return schema::kString;
-    case base::Value::TYPE_DICTIONARY:
+    case base::Value::Type::DICTIONARY:
       return schema::kObject;
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::LIST:
       return schema::kArray;
     default:
       NOTREACHED() << "Unexpected value type: " << value->GetType();
@@ -507,8 +507,9 @@
       ValidateArray(static_cast<const base::ListValue*>(instance),
                     schema, path);
     } else if (type == schema::kString) {
-      // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies
-      // GetAsString() can safely be carried out, not that it's a StringValue.
+      // Intentionally NOT downcasting to StringValue*. Type::STRING only
+      // implies GetAsString() can safely be carried out, not that it's a
+      // StringValue.
       ValidateString(instance, schema, path);
     } else if (type == schema::kNumber || type == schema::kInteger) {
       ValidateNumber(instance, schema, path);
@@ -554,17 +555,17 @@
       NOTREACHED();
     }
     switch (choice->GetType()) {
-      case base::Value::TYPE_NULL:
-      case base::Value::TYPE_BOOLEAN:
-      case base::Value::TYPE_STRING:
+      case base::Value::Type::NONE:
+      case base::Value::Type::BOOLEAN:
+      case base::Value::Type::STRING:
         if (instance->Equals(choice))
           return;
         break;
 
-      case base::Value::TYPE_INTEGER:
-      case base::Value::TYPE_DOUBLE:
-        if (instance->IsType(base::Value::TYPE_INTEGER) ||
-            instance->IsType(base::Value::TYPE_DOUBLE)) {
+      case base::Value::Type::INTEGER:
+      case base::Value::Type::DOUBLE:
+        if (instance->IsType(base::Value::Type::INTEGER) ||
+            instance->IsType(base::Value::Type::DOUBLE)) {
           if (GetNumberValue(choice) == GetNumberValue(instance))
             return;
         }
@@ -715,7 +716,7 @@
       CHECK(tuple_type->GetDictionary(i, &item_schema));
       const base::Value* item_value = NULL;
       instance->Get(i, &item_value);
-      if (item_value && item_value->GetType() != base::Value::TYPE_NULL) {
+      if (item_value && item_value->GetType() != base::Value::Type::NONE) {
         Validate(item_value, item_schema, item_path);
       } else {
         bool is_optional = false;
diff --git a/components/json_schema/json_schema_validator_unittest_base.cc b/components/json_schema/json_schema_validator_unittest_base.cc
index a1da991f..6442fb3 100644
--- a/components/json_schema/json_schema_validator_unittest_base.cc
+++ b/components/json_schema/json_schema_validator_unittest_base.cc
@@ -58,12 +58,12 @@
 
 base::ListValue* LoadList(const std::string& filename) {
   return static_cast<base::ListValue*>(
-      LoadValue(filename, base::Value::TYPE_LIST));
+      LoadValue(filename, base::Value::Type::LIST));
 }
 
 base::DictionaryValue* LoadDictionary(const std::string& filename) {
   return static_cast<base::DictionaryValue*>(
-      LoadValue(filename, base::Value::TYPE_DICTIONARY));
+      LoadValue(filename, base::Value::Type::DICTIONARY));
 }
 
 }  // namespace
diff --git a/components/leveldb/leveldb_mojo_proxy.cc b/components/leveldb/leveldb_mojo_proxy.cc
index 67c0521..2a609b0 100644
--- a/components/leveldb/leveldb_mojo_proxy.cc
+++ b/components/leveldb/leveldb_mojo_proxy.cc
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace leveldb {
 
@@ -183,24 +182,16 @@
                                           std::string name,
                                           uint32_t open_flags,
                                           base::File* output_file) {
-  mojo::ScopedHandle handle;
+  base::File file;
   filesystem::mojom::FileError error = filesystem::mojom::FileError::FAILED;
   bool completed =
-      dir->directory->OpenFileHandle(name, open_flags, &error, &handle);
+      dir->directory->OpenFileHandle(name, open_flags, &error, &file);
   DCHECK(completed);
 
   if (error != filesystem::mojom::FileError::OK) {
     *output_file = base::File(static_cast<base::File::Error>(error));
   } else {
-    base::PlatformFile platform_file;
-    MojoResult unwrap_result = mojo::UnwrapPlatformFile(std::move(handle),
-                                                        &platform_file);
-    if (unwrap_result == MOJO_RESULT_OK) {
-      *output_file = base::File(platform_file);
-    } else {
-      NOTREACHED();
-      *output_file = base::File(base::File::Error::FILE_ERROR_FAILED);
-    }
+    *output_file = std::move(file);
   }
 }
 
diff --git a/components/leveldb/public/interfaces/leveldb.mojom b/components/leveldb/public/interfaces/leveldb.mojom
index de4ca94a..cf62f675 100644
--- a/components/leveldb/public/interfaces/leveldb.mojom
+++ b/components/leveldb/public/interfaces/leveldb.mojom
@@ -5,7 +5,7 @@
 module leveldb.mojom;
 
 import "components/filesystem/public/interfaces/directory.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/unguessable_token.mojom";
 
 enum DatabaseError {
   OK,
diff --git a/components/metrics/profiler/profiler_metrics_provider.cc b/components/metrics/profiler/profiler_metrics_provider.cc
index 2ab8c031..3c3ffbad 100644
--- a/components/metrics/profiler/profiler_metrics_provider.cc
+++ b/components/metrics/profiler/profiler_metrics_provider.cc
@@ -4,7 +4,6 @@
 
 #include "components/metrics/profiler/profiler_metrics_provider.h"
 
-#include <ctype.h>
 #include <stddef.h>
 #include <string>
 #include <vector>
@@ -16,23 +15,6 @@
 namespace metrics {
 namespace {
 
-// Maps a thread name by replacing trailing sequence of digits with "*".
-// Examples:
-// 1. "BrowserBlockingWorker1/23857" => "BrowserBlockingWorker1/*"
-// 2. "Chrome_IOThread" => "Chrome_IOThread"
-std::string MapThreadName(const std::string& thread_name) {
-  size_t i = thread_name.length();
-
-  while (i > 0 && isdigit(thread_name[i - 1])) {
-    --i;
-  }
-
-  if (i == thread_name.length())
-    return thread_name;
-
-  return thread_name.substr(0, i) + '*';
-}
-
 // Normalizes a source filename (which is platform- and build-method-dependent)
 // by extracting the last component of the full file name.
 // Example: "c:\b\build\slave\win\build\src\chrome\app\chrome_main.cc" =>
@@ -52,9 +34,9 @@
     ProfilerEventProto::TrackedObject* tracked_object =
         performance_profile->add_tracked_object();
     tracked_object->set_birth_thread_name_hash(
-        MetricsLog::Hash(MapThreadName(task.birth.thread_name)));
+        MetricsLog::Hash(task.birth.sanitized_thread_name));
     tracked_object->set_exec_thread_name_hash(
-        MetricsLog::Hash(MapThreadName(task.death_thread_name)));
+        MetricsLog::Hash(task.death_sanitized_thread_name));
     tracked_object->set_source_file_name_hash(
         MetricsLog::Hash(NormalizeFileName(task.birth.location.file_name)));
     tracked_object->set_source_function_name_hash(
diff --git a/components/metrics/profiler/profiler_metrics_provider_unittest.cc b/components/metrics/profiler/profiler_metrics_provider_unittest.cc
index e2e31b92..050a87ad 100644
--- a/components/metrics/profiler/profiler_metrics_provider_unittest.cc
+++ b/components/metrics/profiler/profiler_metrics_provider_unittest.cc
@@ -32,7 +32,8 @@
     process_data_phase.tasks.back().birth.location.file_name = "a/b/file.h";
     process_data_phase.tasks.back().birth.location.function_name = "function";
     process_data_phase.tasks.back().birth.location.line_number = 1337;
-    process_data_phase.tasks.back().birth.thread_name = "birth_thread";
+    process_data_phase.tasks.back().birth.sanitized_thread_name =
+        "birth_thread";
     process_data_phase.tasks.back().death_data.count = 37;
     process_data_phase.tasks.back().death_data.run_duration_sum = 31;
     process_data_phase.tasks.back().death_data.run_duration_max = 17;
@@ -40,12 +41,13 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 8;
     process_data_phase.tasks.back().death_data.queue_duration_max = 5;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 3;
-    process_data_phase.tasks.back().death_thread_name = "Still_Alive";
+    process_data_phase.tasks.back().death_sanitized_thread_name = "Still_Alive";
     process_data_phase.tasks.push_back(TaskSnapshot());
     process_data_phase.tasks.back().birth.location.file_name = "c\\d\\file2";
     process_data_phase.tasks.back().birth.location.function_name = "function2";
     process_data_phase.tasks.back().birth.location.line_number = 1773;
-    process_data_phase.tasks.back().birth.thread_name = "birth_thread2";
+    process_data_phase.tasks.back().birth.sanitized_thread_name =
+        "birth_thread*";
     process_data_phase.tasks.back().death_data.count = 19;
     process_data_phase.tasks.back().death_data.run_duration_sum = 23;
     process_data_phase.tasks.back().death_data.run_duration_max = 11;
@@ -53,7 +55,8 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 0;
     process_data_phase.tasks.back().death_data.queue_duration_max = 0;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 0;
-    process_data_phase.tasks.back().death_thread_name = "death_thread";
+    process_data_phase.tasks.back().death_sanitized_thread_name =
+        "death_thread";
 
     profiler_metrics_provider.RecordProfilerData(
         process_data_phase, 177, ProfilerEventProto::TrackedObject::BROWSER, 0,
@@ -68,7 +71,8 @@
     process_data_phase.tasks.back().birth.location.file_name = "a/b/file10.h";
     process_data_phase.tasks.back().birth.location.function_name = "function10";
     process_data_phase.tasks.back().birth.location.line_number = 101337;
-    process_data_phase.tasks.back().birth.thread_name = "birth_thread_ten";
+    process_data_phase.tasks.back().birth.sanitized_thread_name =
+        "birth_thread_ten";
     process_data_phase.tasks.back().death_data.count = 1037;
     process_data_phase.tasks.back().death_data.run_duration_sum = 1031;
     process_data_phase.tasks.back().death_data.run_duration_max = 1017;
@@ -76,13 +80,15 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 108;
     process_data_phase.tasks.back().death_data.queue_duration_max = 105;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 103;
-    process_data_phase.tasks.back().death_thread_name = "Already_Dead";
+    process_data_phase.tasks.back().death_sanitized_thread_name =
+        "Already_Dead";
     process_data_phase.tasks.push_back(TaskSnapshot());
     process_data_phase.tasks.back().birth.location.file_name = "c\\d\\file210";
     process_data_phase.tasks.back().birth.location.function_name =
         "function210";
     process_data_phase.tasks.back().birth.location.line_number = 101773;
-    process_data_phase.tasks.back().birth.thread_name = "birth_thread_ten2";
+    process_data_phase.tasks.back().birth.sanitized_thread_name =
+        "birth_thread_ten*";
     process_data_phase.tasks.back().death_data.count = 1019;
     process_data_phase.tasks.back().death_data.run_duration_sum = 1023;
     process_data_phase.tasks.back().death_data.run_duration_max = 1011;
@@ -90,7 +96,8 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 100;
     process_data_phase.tasks.back().death_data.queue_duration_max = 100;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 100;
-    process_data_phase.tasks.back().death_thread_name = "death_thread_ten";
+    process_data_phase.tasks.back().death_sanitized_thread_name =
+        "death_thread_ten";
 
     profiler_metrics_provider.RecordProfilerData(
         process_data_phase, 177, ProfilerEventProto::TrackedObject::BROWSER, 1,
@@ -105,7 +112,8 @@
     process_data_phase.tasks.back().birth.location.file_name = "file3";
     process_data_phase.tasks.back().birth.location.function_name = "function3";
     process_data_phase.tasks.back().birth.location.line_number = 7331;
-    process_data_phase.tasks.back().birth.thread_name = "birth_thread3";
+    process_data_phase.tasks.back().birth.sanitized_thread_name =
+        "birth_thread*";
     process_data_phase.tasks.back().death_data.count = 137;
     process_data_phase.tasks.back().death_data.run_duration_sum = 131;
     process_data_phase.tasks.back().death_data.run_duration_max = 117;
@@ -113,12 +121,13 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 108;
     process_data_phase.tasks.back().death_data.queue_duration_max = 105;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 103;
-    process_data_phase.tasks.back().death_thread_name = "death_thread3";
+    process_data_phase.tasks.back().death_sanitized_thread_name =
+        "death_thread*";
     process_data_phase.tasks.push_back(TaskSnapshot());
     process_data_phase.tasks.back().birth.location.file_name = "";
     process_data_phase.tasks.back().birth.location.function_name = "";
     process_data_phase.tasks.back().birth.location.line_number = 7332;
-    process_data_phase.tasks.back().birth.thread_name = "";
+    process_data_phase.tasks.back().birth.sanitized_thread_name = "";
     process_data_phase.tasks.back().death_data.count = 138;
     process_data_phase.tasks.back().death_data.run_duration_sum = 132;
     process_data_phase.tasks.back().death_data.run_duration_max = 118;
@@ -126,7 +135,7 @@
     process_data_phase.tasks.back().death_data.queue_duration_sum = 109;
     process_data_phase.tasks.back().death_data.queue_duration_max = 106;
     process_data_phase.tasks.back().death_data.queue_duration_sample = 104;
-    process_data_phase.tasks.back().death_thread_name = "";
+    process_data_phase.tasks.back().death_sanitized_thread_name = "";
 
     profiler_metrics_provider.RecordProfilerData(
         process_data_phase, 1177, ProfilerEventProto::TrackedObject::RENDERER,
diff --git a/components/metrics/profiler/tracking_synchronizer_unittest.cc b/components/metrics/profiler/tracking_synchronizer_unittest.cc
index cae6cb20..792d52f 100644
--- a/components/metrics/profiler/tracking_synchronizer_unittest.cc
+++ b/components/metrics/profiler/tracking_synchronizer_unittest.cc
@@ -69,8 +69,8 @@
         EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromMilliseconds(333),
                   attributes.phase_finish);
 
-        EXPECT_EQ("death_thread0",
-                  process_data_phase.tasks[0].death_thread_name);
+        EXPECT_EQ("death_threadA",
+                  process_data_phase.tasks[0].death_sanitized_thread_name);
         EXPECT_EQ(0u, past_events.size());
         break;
 
@@ -83,8 +83,8 @@
         EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromMilliseconds(777),
                   attributes.phase_finish);
 
-        EXPECT_EQ("death_thread1",
-                  process_data_phase.tasks[0].death_thread_name);
+        EXPECT_EQ("death_threadB",
+                  process_data_phase.tasks[0].death_sanitized_thread_name);
         ASSERT_EQ(1u, past_events.size());
         EXPECT_EQ(ProfilerEventProto::EVENT_FIRST_NONEMPTY_PAINT,
                   past_events[0]);
@@ -135,12 +135,12 @@
   tracked_objects::ProcessDataSnapshot profiler_data;
   ProcessDataPhaseSnapshot snapshot0;
   tracked_objects::TaskSnapshot task_snapshot0;
-  task_snapshot0.death_thread_name = "death_thread0";
+  task_snapshot0.death_sanitized_thread_name = "death_threadA";
   snapshot0.tasks.push_back(task_snapshot0);
   ProcessDataPhaseSnapshot snapshot1;
   profiler_data.phased_snapshots[0] = snapshot0;
   tracked_objects::TaskSnapshot task_snapshot1;
-  task_snapshot1.death_thread_name = "death_thread1";
+  task_snapshot1.death_sanitized_thread_name = "death_threadB";
   snapshot1.tasks.push_back(task_snapshot1);
   profiler_data.phased_snapshots[1] = snapshot1;
   profiler_data.process_id = 239;
diff --git a/components/metrics/public/interfaces/call_stack_profile_collector.mojom b/components/metrics/public/interfaces/call_stack_profile_collector.mojom
index 2a66be1..97295c8 100644
--- a/components/metrics/public/interfaces/call_stack_profile_collector.mojom
+++ b/components/metrics/public/interfaces/call_stack_profile_collector.mojom
@@ -4,7 +4,8 @@
 
 module metrics.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file_path.mojom";
+import "mojo/common/time.mojom";
 
 // These structs mirror the corresponding types in base::StackSamplingProfiler.
 
diff --git a/components/metrics_services_manager/metrics_services_manager.cc b/components/metrics_services_manager/metrics_services_manager.cc
index 652edef..c64edc8 100644
--- a/components/metrics_services_manager/metrics_services_manager.cc
+++ b/components/metrics_services_manager/metrics_services_manager.cc
@@ -11,7 +11,7 @@
 #include "components/metrics/metrics_service_client.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics_services_manager/metrics_services_manager_client.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/service/variations_service.h"
 
 namespace metrics_services_manager {
@@ -34,10 +34,10 @@
   return GetMetricsServiceClient()->GetMetricsService();
 }
 
-rappor::RapporService* MetricsServicesManager::GetRapporService() {
+rappor::RapporServiceImpl* MetricsServicesManager::GetRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!rappor_service_) {
-    rappor_service_ = client_->CreateRapporService();
+    rappor_service_ = client_->CreateRapporServiceImpl();
     rappor_service_->Initialize(client_->GetURLRequestContext());
   }
   return rappor_service_.get();
@@ -70,7 +70,7 @@
 void MetricsServicesManager::UpdatePermissions(bool may_record,
                                                bool may_upload) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // Stash the current permissions so that we can update the RapporService
+  // Stash the current permissions so that we can update the RapporServiceImpl
   // correctly when the Rappor preference changes.  The metrics recording
   // preference partially determines the initial rappor setting, and also
   // controls whether FINE metrics are sent.
@@ -85,7 +85,7 @@
 
   if (client_->OnlyDoMetricsRecording()) {
     metrics->StartRecordingForTests();
-    GetRapporService()->Update(
+    GetRapporServiceImpl()->Update(
         rappor::UMA_RAPPOR_GROUP | rappor::SAFEBROWSING_RAPPOR_GROUP, false);
     return;
   }
@@ -119,7 +119,7 @@
   if (client_->IsSafeBrowsingEnabled(on_safe_browsing_update_callback))
     recording_groups |= rappor::SAFEBROWSING_RAPPOR_GROUP;
 #endif  // defined(GOOGLE_CHROME_BUILD)
-  GetRapporService()->Update(recording_groups, may_upload_);
+  GetRapporServiceImpl()->Update(recording_groups, may_upload_);
 }
 
 void MetricsServicesManager::UpdateUploadPermissions(bool may_upload) {
diff --git a/components/metrics_services_manager/metrics_services_manager.h b/components/metrics_services_manager/metrics_services_manager.h
index e70d648..19245c14 100644
--- a/components/metrics_services_manager/metrics_services_manager.h
+++ b/components/metrics_services_manager/metrics_services_manager.h
@@ -22,7 +22,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace variations {
@@ -35,7 +35,7 @@
 
 // MetricsServicesManager is a helper class for embedders that use the various
 // metrics-related services in a Chrome-like fashion: MetricsService (via its
-// client), RapporService and VariationsService.
+// client), RapporServiceImpl and VariationsService.
 class MetricsServicesManager {
  public:
   // Creates the MetricsServicesManager with the given client.
@@ -57,8 +57,8 @@
   // additionally creating the MetricsServiceClient in that case).
   metrics::MetricsService* GetMetricsService();
 
-  // Returns the RapporService, creating it if it hasn't been created yet.
-  rappor::RapporService* GetRapporService();
+  // Returns the RapporServiceImpl, creating it if it hasn't been created yet.
+  rappor::RapporServiceImpl* GetRapporServiceImpl();
 
   // Returns the VariationsService, creating it if it hasn't been created yet.
   variations::VariationsService* GetVariationsService();
@@ -80,7 +80,7 @@
  private:
   // Update the managed services when permissions for recording/uploading
   // metrics change.
-  void UpdateRapporService();
+  void UpdateRapporServiceImpl();
 
   // Returns the MetricsServiceClient, creating it if it hasn't been
   // created yet (and additionally creating the MetricsService in that case).
@@ -106,8 +106,8 @@
   // The MetricsServiceClient. Owns the MetricsService.
   std::unique_ptr<metrics::MetricsServiceClient> metrics_service_client_;
 
-  // The RapporService, for RAPPOR metric uploads.
-  std::unique_ptr<rappor::RapporService> rappor_service_;
+  // The RapporServiceImpl, for RAPPOR metric uploads.
+  std::unique_ptr<rappor::RapporServiceImpl> rappor_service_;
 
   // The VariationsService, for server-side experiments infrastructure.
   std::unique_ptr<variations::VariationsService> variations_service_;
diff --git a/components/metrics_services_manager/metrics_services_manager_client.h b/components/metrics_services_manager/metrics_services_manager_client.h
index 7a5779c..b53d39d 100644
--- a/components/metrics_services_manager/metrics_services_manager_client.h
+++ b/components/metrics_services_manager/metrics_services_manager_client.h
@@ -19,7 +19,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace variations {
@@ -35,7 +35,8 @@
   virtual ~MetricsServicesManagerClient() {}
 
   // Methods that create the various services in the context of the embedder.
-  virtual std::unique_ptr<rappor::RapporService> CreateRapporService() = 0;
+  virtual std::unique_ptr<rappor::RapporServiceImpl>
+  CreateRapporServiceImpl() = 0;
   virtual std::unique_ptr<variations::VariationsService>
   CreateVariationsService() = 0;
   virtual std::unique_ptr<metrics::MetricsServiceClient>
diff --git a/components/nacl/loader/DEPS b/components/nacl/loader/DEPS
index 555682d8..134e343 100644
--- a/components/nacl/loader/DEPS
+++ b/components/nacl/loader/DEPS
@@ -9,6 +9,7 @@
   "+sandbox/linux/services",
   "+sandbox/linux/suid",
   "+sandbox/linux/system_headers",
+  "+sandbox/sandbox_features.h",
   "+sandbox/win/src",
   "+ppapi/c",  # header files only
 
diff --git a/components/nacl/loader/sandbox_linux/BUILD.gn b/components/nacl/loader/sandbox_linux/BUILD.gn
index ce1d404..8f61844 100644
--- a/components/nacl/loader/sandbox_linux/BUILD.gn
+++ b/components/nacl/loader/sandbox_linux/BUILD.gn
@@ -24,14 +24,11 @@
     "//crypto",
     "//ipc",
     "//sandbox",
+    "//sandbox:sandbox_features",
     "//sandbox/linux:sandbox_services_headers",
   ]
 
   if (use_glib) {
     configs += [ "//build/config/linux:glib" ]
   }
-
-  if (use_seccomp_bpf) {
-    defines += [ "USE_SECCOMP_BPF" ]
-  }
 }
diff --git a/components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.cc b/components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.cc
index 3be2e83..9a8d96f 100644
--- a/components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.cc
+++ b/components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.cc
@@ -9,8 +9,9 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "sandbox/sandbox_features.h"
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
 
 #include <errno.h>
 #include <signal.h>
@@ -30,11 +31,11 @@
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 
-#endif  // defined(USE_SECCOMP_BPF)
+#endif  // BUILDFLAG(USE_SECCOMP_BPF)
 
 namespace nacl {
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
 
 namespace {
 
@@ -163,10 +164,10 @@
 
 #error "Seccomp-bpf disabled on supported architecture!"
 
-#endif  // defined(USE_SECCOMP_BPF)
+#endif  // BUILDFLAG(USE_SECCOMP_BPF)
 
 bool InitializeBPFSandbox(base::ScopedFD proc_fd) {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   bool sandbox_is_initialized = content::InitializeSandbox(
       std::unique_ptr<sandbox::bpf_dsl::Policy>(new NaClBPFSandboxPolicy),
       std::move(proc_fd));
@@ -174,7 +175,7 @@
     RunSandboxSanityChecks();
     return true;
   }
-#endif  // defined(USE_SECCOMP_BPF)
+#endif  // BUILDFLAG(USE_SECCOMP_BPF)
   return false;
 }
 
diff --git a/components/nacl/renderer/json_manifest.cc b/components/nacl/renderer/json_manifest.cc
index 8ae2603..dda3933 100644
--- a/components/nacl/renderer/json_manifest.cc
+++ b/components/nacl/renderer/json_manifest.cc
@@ -200,7 +200,7 @@
   // URL was already verified above by IsValidDictionary to be required.
   url_dict->GetWithoutPathExpansion(kUrlKey, &url);
   DCHECK(url);
-  if (!url->IsType(base::Value::TYPE_STRING)) {
+  if (!url->IsType(base::Value::Type::STRING)) {
     std::stringstream error_stream;
     error_stream << parent_key << " property '" << container_key
                  << "' has non-string value '" << *url << "' for key '"
@@ -212,7 +212,7 @@
     const base::Value* opt_level = nullptr;
     url_dict->GetWithoutPathExpansion(kOptLevelKey, &opt_level);
     DCHECK(opt_level);
-    if (!opt_level->IsType(base::Value::TYPE_INTEGER)) {
+    if (!opt_level->IsType(base::Value::Type::INTEGER)) {
       std::stringstream error_stream;
       error_stream << parent_key << " property '" << container_key
                    << "' has non-numeric value '" << *opt_level << "' for key '"
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index 448fc8d66..8d68dfb 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -89,7 +89,7 @@
     "//components/image_fetcher",
     "//components/metrics",
     "//components/ntp_snippets/remote/proto",
-    "//components/offline_pages",
+    "//components/offline_pages/core",
     "//components/physical_web/data_source",
     "//components/sessions",
     "//components/strings",
@@ -141,8 +141,8 @@
     "//components/image_fetcher",
     "//components/leveldb_proto:test_support",
     "//components/ntp_snippets/remote/proto",
-    "//components/offline_pages",
-    "//components/offline_pages:test_support",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core:test_support",
     "//components/physical_web/data_source:test_support",
     "//components/sessions",
     "//components/sessions:test_support",
@@ -171,8 +171,8 @@
   deps = [
     ":ntp_snippets",
     "//base",
-    "//components/offline_pages",
-    "//components/offline_pages:test_support",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core:test_support",
     "//testing/gmock",
   ]
 }
diff --git a/components/ntp_snippets/offline_pages/offline_pages_test_utils.h b/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
index 56f71a3..caa4f9c 100644
--- a/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
+++ b/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
@@ -6,8 +6,8 @@
 #define COMPONENTS_NTP_SNIPPETS_OFFLINE_PAGES_OFFLINE_PAGES_TEST_UTILS_H_
 
 #include "components/ntp_snippets/content_suggestion.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
 
 namespace ntp_snippets {
 namespace test {
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
index 8323590f..1585a67 100644
--- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
+++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
@@ -15,9 +15,9 @@
 #include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/pref_names.h"
 #include "components/ntp_snippets/pref_util.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "grit/components_strings.h"
@@ -208,8 +208,9 @@
 void RecentTabSuggestionsProvider::OfflinePageModelLoaded(
     offline_pages::OfflinePageModel* model) {}
 
-void RecentTabSuggestionsProvider::OfflinePageModelChanged(
-    offline_pages::OfflinePageModel* model) {
+void RecentTabSuggestionsProvider::OfflinePageAdded(
+    offline_pages::OfflinePageModel* model,
+    const offline_pages::OfflinePageItem& added_page) {
   DCHECK_EQ(offline_page_model_, model);
   FetchRecentTabs();
 }
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
index fef26df..d1fe410 100644
--- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
+++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
@@ -18,7 +18,7 @@
 #include "components/ntp_snippets/category_status.h"
 #include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/content_suggestions_provider.h"
-#include "components/offline_pages/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -68,7 +68,9 @@
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
-  void OfflinePageModelChanged(offline_pages::OfflinePageModel* model) override;
+  void OfflinePageAdded(
+      offline_pages::OfflinePageModel* model,
+      const offline_pages::OfflinePageItem& added_page) override;
   void OfflinePageDeleted(int64_t offline_id,
                           const offline_pages::ClientId& client_id) override;
 
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
index 7618c81..c8c3eb5 100644
--- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
+++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
@@ -16,9 +16,9 @@
 #include "components/ntp_snippets/content_suggestions_provider.h"
 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
 #include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/stub_offline_page_model.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -80,12 +80,17 @@
     return ContentSuggestion::ID(recent_tabs_category(), base::IntToString(id));
   }
 
-  void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) {
-    *(model_.mutable_items()) = items;
-    provider_->OfflinePageModelChanged(&model_);
+  void AddOfflinePageToModel(const OfflinePageItem& item) {
+    model_.mutable_items()->push_back(item);
+    provider_->OfflinePageAdded(&model_, item);
   }
 
   void FireOfflinePageDeleted(const OfflinePageItem& item) {
+    auto iter = std::remove(model_.mutable_items()->begin(),
+                            model_.mutable_items()->end(), item);
+    auto end = model_.mutable_items()->end();
+    model_.mutable_items()->erase(iter, end);
+
     provider_->OfflinePageDeleted(item.offline_id, item.client_id);
   }
 
@@ -110,6 +115,11 @@
 };
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) {
+  auto recent_tabs_list = CreateDummyRecentTabs({1, 2});
+  EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(2);
+  for (OfflinePageItem& recent_tab : recent_tabs_list)
+    AddOfflinePageToModel(recent_tab);
+
   EXPECT_CALL(
       *observer(),
       OnNewSuggestions(_, recent_tabs_category(),
@@ -120,17 +130,34 @@
                                     GURL("http://dummy.com/2")),
                            Property(&ContentSuggestion::url,
                                     GURL("http://dummy.com/3")))));
-  FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3}));
+  AddOfflinePageToModel(CreateDummyRecentTab(3));
 }
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByCreationTime) {
   base::Time now = base::Time::Now();
   base::Time yesterday = now - base::TimeDelta::FromDays(1);
   base::Time tomorrow = now + base::TimeDelta::FromDays(1);
+
   std::vector<OfflinePageItem> offline_pages = {
       CreateDummyRecentTab(1, now), CreateDummyRecentTab(2, yesterday),
       CreateDummyRecentTab(3, tomorrow)};
 
+  EXPECT_CALL(
+      *observer(),
+      OnNewSuggestions(_, recent_tabs_category(),
+                       ElementsAre(Property(&ContentSuggestion::url,
+                                            GURL("http://dummy.com/1")))));
+  AddOfflinePageToModel(CreateDummyRecentTab(1, now));
+
+  EXPECT_CALL(
+      *observer(),
+      OnNewSuggestions(
+          _, recent_tabs_category(),
+          ElementsAre(
+              Property(&ContentSuggestion::url, GURL("http://dummy.com/1")),
+              Property(&ContentSuggestion::url, GURL("http://dummy.com/2")))));
+  AddOfflinePageToModel(CreateDummyRecentTab(2, yesterday));
+
   offline_pages[1].last_access_time =
       offline_pages[0].last_access_time + base::TimeDelta::FromHours(1);
 
@@ -144,7 +171,7 @@
                                GURL("http://dummy.com/1")),
                       Property(&ContentSuggestion::url,
                                GURL("http://dummy.com/2")))));
-  FireOfflinePageModelChanged(offline_pages);
+  AddOfflinePageToModel(CreateDummyRecentTab(3, tomorrow));
 }
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) {
@@ -158,7 +185,10 @@
 }
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) {
-  FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3, 4}));
+  EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(4);
+  auto recent_tabs_list = CreateDummyRecentTabs({1, 2, 3, 4});
+  for (OfflinePageItem& recent_tab : recent_tabs_list)
+    AddOfflinePageToModel(recent_tab);
 
   // Dismiss 2 and 3.
   EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
@@ -176,7 +206,8 @@
                            Property(&ContentSuggestion::url,
                                     GURL("http://dummy.com/4")))));
 
-  FireOfflinePageModelChanged(model()->items());
+  AddOfflinePageToModel(ntp_snippets::test::CreateDummyOfflinePageItem(
+      4, offline_pages::kDefaultNamespace));
   Mock::VerifyAndClearExpectations(observer());
 
   // And appear in the dismissed suggestions.
@@ -204,14 +235,17 @@
   // And appear in the reported suggestions for the category again.
   EXPECT_CALL(*observer(),
               OnNewSuggestions(_, recent_tabs_category(), SizeIs(4)));
-  FireOfflinePageModelChanged(model()->items());
+  AddOfflinePageToModel(ntp_snippets::test::CreateDummyOfflinePageItem(
+      5, offline_pages::kDefaultNamespace));
   Mock::VerifyAndClearExpectations(observer());
 }
 
 TEST_F(RecentTabSuggestionsProviderTest,
        ShouldInvalidateWhenOfflinePageDeleted) {
+  EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
   std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
-  FireOfflinePageModelChanged(offline_pages);
+  for (OfflinePageItem& recent_tab : offline_pages)
+    AddOfflinePageToModel(recent_tab);
 
   // Invalidation of suggestion 2 should be forwarded.
   EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(2)));
@@ -219,8 +253,10 @@
 }
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) {
+  EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
   std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
-  FireOfflinePageModelChanged(offline_pages);
+  for (OfflinePageItem& recent_tab : offline_pages)
+    AddOfflinePageToModel(recent_tab);
   EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty());
 
   provider()->DismissSuggestion(GetDummySuggestionId(2));
@@ -231,16 +267,20 @@
 }
 
 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnFetch) {
-  FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3}));
+  EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3);
+  std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3});
+  for (OfflinePageItem& recent_tab : offline_pages)
+    AddOfflinePageToModel(recent_tab);
 
   provider()->DismissSuggestion(GetDummySuggestionId(2));
   provider()->DismissSuggestion(GetDummySuggestionId(3));
   EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(2));
 
-  FireOfflinePageModelChanged(CreateDummyRecentTabs({2}));
+  FireOfflinePageDeleted(offline_pages[0]);
+  FireOfflinePageDeleted(offline_pages[2]);
   EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1));
 
-  FireOfflinePageModelChanged(std::vector<OfflinePageItem>());
+  FireOfflinePageDeleted(offline_pages[1]);
   EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty());
 }
 
@@ -255,13 +295,17 @@
   // We leave IDs different, but make the URLs the same.
   offline_pages[2].url = offline_pages[0].url;
 
+  AddOfflinePageToModel(offline_pages[0]);
+  AddOfflinePageToModel(offline_pages[1]);
+  Mock::VerifyAndClearExpectations(observer());
   EXPECT_CALL(*observer(),
               OnNewSuggestions(
                   _, recent_tabs_category(),
                   UnorderedElementsAre(
                       Property(&ContentSuggestion::publish_date, now),
                       Property(&ContentSuggestion::publish_date, tomorrow))));
-  FireOfflinePageModelChanged(offline_pages);
+
+  AddOfflinePageToModel(offline_pages[2]);
 }
 
 }  // namespace ntp_snippets
diff --git a/components/offline_pages/BUILD.gn b/components/offline_pages/BUILD.gn
deleted file mode 100644
index 123c1e4..0000000
--- a/components/offline_pages/BUILD.gn
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-}
-
-static_library("offline_pages") {
-  sources = [
-    "archive_manager.cc",
-    "archive_manager.h",
-    "client_namespace_constants.cc",
-    "client_namespace_constants.h",
-    "client_policy_controller.cc",
-    "client_policy_controller.h",
-    "offline_event_logger.cc",
-    "offline_event_logger.h",
-    "offline_page_archiver.h",
-    "offline_page_client_policy.h",
-    "offline_page_item.cc",
-    "offline_page_item.h",
-    "offline_page_metadata_store.cc",
-    "offline_page_metadata_store.h",
-    "offline_page_metadata_store_sql.cc",
-    "offline_page_metadata_store_sql.h",
-    "offline_page_model.cc",
-    "offline_page_model.h",
-    "offline_page_model_event_logger.cc",
-    "offline_page_model_event_logger.h",
-    "offline_page_model_impl.cc",
-    "offline_page_model_impl.h",
-    "offline_page_model_query.cc",
-    "offline_page_model_query.h",
-    "offline_page_storage_manager.cc",
-    "offline_page_storage_manager.h",
-    "offline_page_types.h",
-    "offline_store_types.h",
-    "snapshot_controller.cc",
-    "snapshot_controller.h",
-  ]
-
-  deps = [
-    ":switches",
-    "//base",
-    "//components/keyed_service/core",
-    "//net",
-    "//sql:sql",
-    "//url",
-  ]
-}
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "offline_page_test_archiver.cc",
-    "offline_page_test_archiver.h",
-    "offline_page_test_store.cc",
-    "offline_page_test_store.h",
-    "stub_offline_page_model.cc",
-    "stub_offline_page_model.h",
-  ]
-
-  deps = [
-    ":offline_pages",
-    ":switches",
-    "//base",
-    "//components/keyed_service/core",
-    "//testing/gtest",
-    "//url",
-  ]
-}
-
-static_library("switches") {
-  sources = [
-    "offline_page_feature.cc",
-    "offline_page_feature.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/version_info",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "archive_manager_unittest.cc",
-    "client_policy_controller_unittest.cc",
-    "offline_page_metadata_store_impl_unittest.cc",
-    "offline_page_model_event_logger_unittest.cc",
-    "offline_page_model_impl_unittest.cc",
-    "offline_page_model_query_unittest.cc",
-    "offline_page_storage_manager_unittest.cc",
-    "snapshot_controller_unittest.cc",
-  ]
-
-  deps = [
-    ":offline_pages",
-    ":switches",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//sql:sql",
-    "//testing/gtest",
-    "//url",
-  ]
-}
-
-if (is_android) {
-  java_cpp_enum("offline_page_model_enums_java") {
-    sources = [
-      "background/request_notifier.h",
-      "background/request_queue_results.h",
-      "background/save_page_request.h",
-      "offline_page_types.h",
-      "offline_store_types.h",
-    ]
-  }
-}
diff --git a/components/offline_pages/archive_manager.cc b/components/offline_pages/archive_manager.cc
deleted file mode 100644
index 8df6b54..0000000
--- a/components/offline_pages/archive_manager.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
-#include "base/sys_info.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/archive_manager.h"
-
-namespace offline_pages {
-
-namespace {
-
-using StorageStatsCallback =
-    base::Callback<void(const ArchiveManager::StorageStats& storage_stats)>;
-
-void EnsureArchivesDirCreatedImpl(const base::FilePath& archives_dir) {
-  CHECK(base::CreateDirectory(archives_dir));
-}
-
-void ExistsArchiveImpl(const base::FilePath& file_path,
-                       scoped_refptr<base::SequencedTaskRunner> task_runner,
-                       const base::Callback<void(bool)>& callback) {
-  task_runner->PostTask(FROM_HERE,
-                        base::Bind(callback, base::PathExists(file_path)));
-}
-
-void DeleteArchivesImpl(const std::vector<base::FilePath>& file_paths,
-                        scoped_refptr<base::SequencedTaskRunner> task_runner,
-                        const base::Callback<void(bool)>& callback) {
-  bool result = false;
-  for (const auto& file_path : file_paths) {
-    // Make sure delete happens on the left of || so that it is always executed.
-    result = base::DeleteFile(file_path, false) || result;
-  }
-  task_runner->PostTask(FROM_HERE, base::Bind(callback, result));
-}
-
-void GetAllArchivesImpl(
-    const base::FilePath& archive_dir,
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    const base::Callback<void(const std::set<base::FilePath>&)>& callback) {
-  std::set<base::FilePath> archive_paths;
-  base::FileEnumerator file_enumerator(archive_dir, false,
-                                       base::FileEnumerator::FILES);
-  for (base::FilePath archive_path = file_enumerator.Next();
-       !archive_path.empty(); archive_path = file_enumerator.Next()) {
-    archive_paths.insert(archive_path);
-  }
-  task_runner->PostTask(FROM_HERE, base::Bind(callback, archive_paths));
-}
-
-void GetStorageStatsImpl(const base::FilePath& archive_dir,
-                         scoped_refptr<base::SequencedTaskRunner> task_runner,
-                         const StorageStatsCallback& callback) {
-  ArchiveManager::StorageStats storage_stats;
-  storage_stats.free_disk_space =
-      base::SysInfo::AmountOfFreeDiskSpace(archive_dir);
-  storage_stats.total_archives_size = base::ComputeDirectorySize(archive_dir);
-  task_runner->PostTask(FROM_HERE, base::Bind(callback, storage_stats));
-}
-
-}  // namespace
-
-// protected and used for testing.
-ArchiveManager::ArchiveManager() {}
-
-ArchiveManager::ArchiveManager(
-    const base::FilePath& archives_dir,
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
-    : archives_dir_(archives_dir), task_runner_(task_runner) {}
-
-ArchiveManager::~ArchiveManager() {}
-
-void ArchiveManager::EnsureArchivesDirCreated(const base::Closure& callback) {
-  task_runner_->PostTaskAndReply(
-      FROM_HERE, base::Bind(EnsureArchivesDirCreatedImpl, archives_dir_),
-      callback);
-}
-
-void ArchiveManager::ExistsArchive(const base::FilePath& archive_path,
-                                   const base::Callback<void(bool)>& callback) {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(ExistsArchiveImpl, archive_path,
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void ArchiveManager::DeleteArchive(const base::FilePath& archive_path,
-                                   const base::Callback<void(bool)>& callback) {
-  std::vector<base::FilePath> archive_paths = {archive_path};
-  DeleteMultipleArchives(archive_paths, callback);
-}
-
-void ArchiveManager::DeleteMultipleArchives(
-    const std::vector<base::FilePath>& archive_paths,
-    const base::Callback<void(bool)>& callback) {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(DeleteArchivesImpl, archive_paths,
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void ArchiveManager::GetAllArchives(
-    const base::Callback<void(const std::set<base::FilePath>&)>& callback)
-    const {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(GetAllArchivesImpl, archives_dir_,
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void ArchiveManager::GetStorageStats(
-    const StorageStatsCallback& callback) const {
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(GetStorageStatsImpl, archives_dir_,
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/archive_manager.h b/components/offline_pages/archive_manager.h
deleted file mode 100644
index 23e29c1..0000000
--- a/components/offline_pages/archive_manager.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
-
-#include <set>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/files/file_path.h"
-#include "base/memory/ref_counted.h"
-
-namespace base {
-class SequencedTaskRunner;
-}  // namespace base
-
-namespace offline_pages {
-
-// Class that manages all archive files for offline pages. They are all stored
-// in a specific archive directory.
-// All tasks are performed using |task_runner_|.
-class ArchiveManager {
- public:
-  struct StorageStats {
-    int64_t free_disk_space;
-    int64_t total_archives_size;
-  };
-
-  ArchiveManager(const base::FilePath& archives_dir,
-                 const scoped_refptr<base::SequencedTaskRunner>& task_runner);
-  virtual ~ArchiveManager();
-
-  // Creates archives directory if one does not exist yet;
-  virtual void EnsureArchivesDirCreated(const base::Closure& callback);
-
-  // Checks whether an archive with specified |archive_path| exists.
-  virtual void ExistsArchive(const base::FilePath& archive_path,
-                             const base::Callback<void(bool)>& callback);
-
-  // Deletes an archive with specified |archive_path|.
-  // It is considered successful to attempt to delete a file that does not
-  // exist.
-  virtual void DeleteArchive(const base::FilePath& archive_path,
-                             const base::Callback<void(bool)>& callback);
-
-  // Deletes multiple archives that are specified in |archive_paths|.
-  // It is considered successful to attempt to delete a file that does not
-  // exist.
-  virtual void DeleteMultipleArchives(
-      const std::vector<base::FilePath>& archive_paths,
-      const base::Callback<void(bool)>& callback);
-
-  // Lists all archive files in the archive directory.
-  virtual void GetAllArchives(
-      const base::Callback<void(const std::set<base::FilePath>&)>& callback)
-      const;
-
-  // Gets stats about archive storage, i.e. total archive sizes and free disk
-  // space.
-  virtual void GetStorageStats(
-      const base::Callback<void(const StorageStats& storage_sizes)>& callback)
-      const;
-
- protected:
-  ArchiveManager();
-
- private:
-  // Path under which all of the managed archives should be stored.
-  base::FilePath archives_dir_;
-  // Task runner for running file operations.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_ARCHIVE_MANAGER_H_
diff --git a/components/offline_pages/archive_manager_unittest.cc b/components/offline_pages/archive_manager_unittest.cc
deleted file mode 100644
index bc50b4f..0000000
--- a/components/offline_pages/archive_manager_unittest.cc
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/archive_manager.h"
-
-#include <algorithm>
-#include <memory>
-#include <set>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-const base::FilePath::CharType kMissingArchivePath[] = FILE_PATH_LITERAL(
-    "missing_archive.path");
-}  // namespace
-
-enum class CallbackStatus {
-  NOT_CALLED,
-  CALLED_FALSE,
-  CALLED_TRUE,
-};
-
-class ArchiveManagerTest : public testing::Test {
- public:
-  ArchiveManagerTest();
-  void SetUp() override;
-
-  void PumpLoop();
-  void ResetResults();
-
-  void ResetManager(const base::FilePath& file_path);
-  void Callback(bool result);
-  void GetAllArchivesCallback(const std::set<base::FilePath>& archive_paths);
-  void GetStorageStatsCallback(
-      const ArchiveManager::StorageStats& storage_sizes);
-
-  ArchiveManager* manager() { return manager_.get(); }
-  const base::FilePath& temp_path() const { return temp_dir_.GetPath(); }
-  CallbackStatus callback_status() const { return callback_status_; }
-  const std::set<base::FilePath>& last_archive_paths() const {
-    return last_archvie_paths_;
-  }
-  ArchiveManager::StorageStats last_storage_sizes() const {
-    return last_storage_sizes_;
-  }
-
- private:
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-  base::ScopedTempDir temp_dir_;
-
-  std::unique_ptr<ArchiveManager> manager_;
-  CallbackStatus callback_status_;
-  std::set<base::FilePath> last_archvie_paths_;
-  ArchiveManager::StorageStats last_storage_sizes_;
-};
-
-ArchiveManagerTest::ArchiveManagerTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_),
-      callback_status_(CallbackStatus::NOT_CALLED),
-      last_storage_sizes_({0, 0}) {}
-
-void ArchiveManagerTest::SetUp() {
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  ResetManager(temp_dir_.GetPath());
-}
-
-void ArchiveManagerTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void ArchiveManagerTest::ResetResults() {
-  callback_status_ = CallbackStatus::NOT_CALLED;
-  last_archvie_paths_.clear();
-}
-
-void ArchiveManagerTest::ResetManager(const base::FilePath& file_path) {
-  manager_.reset(
-      new ArchiveManager(file_path, base::ThreadTaskRunnerHandle::Get()));
-}
-
-void ArchiveManagerTest::Callback(bool result) {
-  callback_status_ =
-      result ? CallbackStatus::CALLED_TRUE : CallbackStatus::CALLED_FALSE;
-}
-
-void ArchiveManagerTest::GetAllArchivesCallback(
-    const std::set<base::FilePath>& archive_paths) {
-  last_archvie_paths_ = archive_paths;
-}
-
-void ArchiveManagerTest::GetStorageStatsCallback(
-    const ArchiveManager::StorageStats& storage_sizes) {
-  last_storage_sizes_ = storage_sizes;
-}
-
-TEST_F(ArchiveManagerTest, EnsureArchivesDirCreated) {
-  base::FilePath archive_dir =
-      temp_path().Append(FILE_PATH_LITERAL("test_path"));
-  ResetManager(archive_dir);
-  EXPECT_FALSE(base::PathExists(archive_dir));
-
-  // Ensure archives dir exists, when it doesn't.
-  manager()->EnsureArchivesDirCreated(
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this), true));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_TRUE(base::PathExists(archive_dir));
-
-  // Try again when the file already exists.
-  ResetResults();
-  manager()->EnsureArchivesDirCreated(
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this), true));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_TRUE(base::PathExists(archive_dir));
-}
-
-TEST_F(ArchiveManagerTest, ExistsArchive) {
-  base::FilePath archive_path = temp_path().Append(kMissingArchivePath);
-  manager()->ExistsArchive(
-      archive_path,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_FALSE, callback_status());
-
-  ResetResults();
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path));
-
-  manager()->ExistsArchive(
-      archive_path,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-}
-
-TEST_F(ArchiveManagerTest, DeleteMultipleArchives) {
-  base::FilePath archive_path_1;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
-  base::FilePath archive_path_2;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
-  base::FilePath archive_path_3;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
-
-  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
-
-  manager()->DeleteMultipleArchives(
-      archive_paths,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_FALSE(base::PathExists(archive_path_1));
-  EXPECT_FALSE(base::PathExists(archive_path_2));
-  EXPECT_TRUE(base::PathExists(archive_path_3));
-}
-
-TEST_F(ArchiveManagerTest, DeleteMultipleArchivesSomeDoNotExist) {
-  base::FilePath archive_path_1 = temp_path().Append(kMissingArchivePath);
-  base::FilePath archive_path_2;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
-  base::FilePath archive_path_3;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
-
-  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
-
-  EXPECT_FALSE(base::PathExists(archive_path_1));
-
-  manager()->DeleteMultipleArchives(
-      archive_paths,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_FALSE(base::PathExists(archive_path_1));
-  EXPECT_FALSE(base::PathExists(archive_path_2));
-  EXPECT_TRUE(base::PathExists(archive_path_3));
-}
-
-TEST_F(ArchiveManagerTest, DeleteMultipleArchivesNoneExist) {
-  base::FilePath archive_path_1 = temp_path().Append(kMissingArchivePath);
-  base::FilePath archive_path_2 = temp_path().Append(FILE_PATH_LITERAL(
-      "other_missing_file.mhtml"));
-  base::FilePath archive_path_3;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
-
-  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
-
-  EXPECT_FALSE(base::PathExists(archive_path_1));
-  EXPECT_FALSE(base::PathExists(archive_path_2));
-
-  manager()->DeleteMultipleArchives(
-      archive_paths,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_FALSE(base::PathExists(archive_path_1));
-  EXPECT_FALSE(base::PathExists(archive_path_2));
-  EXPECT_TRUE(base::PathExists(archive_path_3));
-}
-
-TEST_F(ArchiveManagerTest, DeleteArchive) {
-  base::FilePath archive_path;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path));
-
-  manager()->DeleteArchive(
-      archive_path,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_FALSE(base::PathExists(archive_path));
-}
-
-TEST_F(ArchiveManagerTest, DeleteArchiveThatDoesNotExist) {
-  base::FilePath archive_path = temp_path().Append(kMissingArchivePath);
-  EXPECT_FALSE(base::PathExists(archive_path));
-
-  manager()->DeleteArchive(
-      archive_path,
-      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
-  EXPECT_FALSE(base::PathExists(archive_path));
-}
-
-TEST_F(ArchiveManagerTest, GetAllArchives) {
-  base::FilePath archive_path_1;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
-  base::FilePath archive_path_2;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
-  base::FilePath archive_path_3;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
-  std::vector<base::FilePath> expected_paths{archive_path_1, archive_path_2,
-                                             archive_path_3};
-  std::sort(expected_paths.begin(), expected_paths.end());
-
-  manager()->GetAllArchives(base::Bind(
-      &ArchiveManagerTest::GetAllArchivesCallback, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(3UL, last_archive_paths().size());
-  std::vector<base::FilePath> actual_paths(last_archive_paths().begin(),
-                                           last_archive_paths().end());
-  // Comparing one to one works because last_archive_paths set is sorted.
-  // Because some windows bots provide abbreviated path (e.g. chrome-bot becomes
-  // CHROME~2), this test only focuses on file name.
-  EXPECT_EQ(expected_paths[0].BaseName(), actual_paths[0].BaseName());
-  EXPECT_EQ(expected_paths[1].BaseName(), actual_paths[1].BaseName());
-  EXPECT_EQ(expected_paths[2].BaseName(), actual_paths[2].BaseName());
-}
-
-TEST_F(ArchiveManagerTest, GetStorageStats) {
-  base::FilePath archive_path_1;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
-  base::FilePath archive_path_2;
-  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
-
-  manager()->GetStorageStats(base::Bind(
-      &ArchiveManagerTest::GetStorageStatsCallback, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_GT(last_storage_sizes().free_disk_space, 0);
-  EXPECT_EQ(last_storage_sizes().total_archives_size,
-            base::ComputeDirectorySize(temp_path()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/BUILD.gn b/components/offline_pages/background/BUILD.gn
deleted file mode 100644
index 06a074f..0000000
--- a/components/offline_pages/background/BUILD.gn
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-}
-
-static_library("background_offliner") {
-  sources = [
-    "add_request_task.cc",
-    "add_request_task.h",
-    "change_requests_state_task.cc",
-    "change_requests_state_task.h",
-    "connection_notifier.cc",
-    "connection_notifier.h",
-    "device_conditions.h",
-    "get_requests_task.cc",
-    "get_requests_task.h",
-    "initialize_store_task.cc",
-    "initialize_store_task.h",
-    "mark_attempt_aborted_task.cc",
-    "mark_attempt_aborted_task.h",
-    "mark_attempt_completed_task.cc",
-    "mark_attempt_completed_task.h",
-    "mark_attempt_started_task.cc",
-    "mark_attempt_started_task.h",
-    "offliner.h",
-    "offliner_factory.h",
-    "offliner_policy.h",
-    "pick_request_task.cc",
-    "pick_request_task.h",
-    "pick_request_task_factory.cc",
-    "pick_request_task_factory.h",
-    "remove_requests_task.cc",
-    "remove_requests_task.h",
-    "request_coordinator.cc",
-    "request_coordinator.h",
-    "request_coordinator_event_logger.cc",
-    "request_coordinator_event_logger.h",
-    "request_notifier.h",
-    "request_queue.cc",
-    "request_queue.h",
-    "request_queue_results.h",
-    "request_queue_store.h",
-    "request_queue_store_sql.cc",
-    "request_queue_store_sql.h",
-    "save_page_request.cc",
-    "save_page_request.h",
-    "scheduler.h",
-    "update_request_task.cc",
-    "update_request_task.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/keyed_service/core",
-    "//components/offline_pages:offline_pages",
-    "//components/offline_pages:switches",
-    "//components/offline_pages/core",
-    "//net",
-    "//sql:sql",
-    "//url",
-  ]
-}
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "network_quality_provider_stub.cc",
-    "network_quality_provider_stub.h",
-    "offliner_factory_stub.cc",
-    "offliner_factory_stub.h",
-    "offliner_stub.cc",
-    "offliner_stub.h",
-    "request_queue_in_memory_store.cc",
-    "request_queue_in_memory_store.h",
-    "scheduler_stub.cc",
-    "scheduler_stub.h",
-  ]
-
-  deps = [
-    ":background_offliner",
-    "//base",
-    "//net",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "add_request_task_unittest.cc",
-    "change_requests_state_task_unittest.cc",
-    "get_requests_task_unittest.cc",
-    "initialize_store_task_unittest.cc",
-    "mark_attempt_aborted_task_unittest.cc",
-    "mark_attempt_completed_task_unittest.cc",
-    "mark_attempt_started_task_unittest.cc",
-    "pick_request_task_unittest.cc",
-    "remove_requests_task_unittest.cc",
-    "request_coordinator_event_logger_unittest.cc",
-    "request_coordinator_unittest.cc",
-    "request_queue_store_unittest.cc",
-    "request_queue_unittest.cc",
-    "save_page_request_unittest.cc",
-  ]
-
-  deps = [
-    ":background_offliner",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//components/offline_pages:offline_pages",
-    "//components/offline_pages:switches",
-    "//testing/gtest",
-    "//url",
-  ]
-}
diff --git a/components/offline_pages/background/add_request_task.cc b/components/offline_pages/background/add_request_task.cc
deleted file mode 100644
index 952f57d..0000000
--- a/components/offline_pages/background/add_request_task.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/add_request_task.h"
-
-#include "base/bind.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-AddRequestTask::AddRequestTask(RequestQueueStore* store,
-                               const SavePageRequest& request,
-                               const RequestQueueStore::AddCallback& callback)
-    : store_(store),
-      request_(request),
-      callback_(callback),
-      weak_ptr_factory_(this) {}
-
-AddRequestTask::~AddRequestTask() {}
-
-void AddRequestTask::Run() {
-  AddRequest();
-}
-
-void AddRequestTask::AddRequest() {
-  store_->AddRequest(request_, base::Bind(&AddRequestTask::CompleteWithResult,
-                                          weak_ptr_factory_.GetWeakPtr()));
-}
-
-void AddRequestTask::CompleteWithResult(ItemActionStatus status) {
-  callback_.Run(status);
-  TaskComplete();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/add_request_task.h b/components/offline_pages/background/add_request_task.h
deleted file mode 100644
index 3b6b0c7e..0000000
--- a/components/offline_pages/background/add_request_task.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_ADD_REQUEST_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_ADD_REQUEST_TASK_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class AddRequestTask : public Task {
- public:
-  AddRequestTask(RequestQueueStore* store,
-                 const SavePageRequest& request,
-                 const RequestQueueStore::AddCallback& callback);
-  ~AddRequestTask() override;
-
-  // Task implementation:
-  void Run() override;
-
- private:
-  // Step 1: Add the request to the store.
-  void AddRequest();
-  // Step 2: Calls the callback with result, completes the task.
-  void CompleteWithResult(ItemActionStatus status);
-
-  // Store from which requests will be read.
-  RequestQueueStore* store_;
-  // Request to be added.
-  SavePageRequest request_;
-  // Callback used to return the read results.
-  RequestQueueStore::AddCallback callback_;
-
-  base::WeakPtrFactory<AddRequestTask> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(AddRequestTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_ADD_REQUEST_TASK_H_
diff --git a/components/offline_pages/background/add_request_task_unittest.cc b/components/offline_pages/background/add_request_task_unittest.cc
deleted file mode 100644
index 38e45e3..0000000
--- a/components/offline_pages/background/add_request_task_unittest.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/add_request_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 44;
-const GURL kUrl1("http://example.com");
-const GURL kUrl2("http://otherexample.com");
-const ClientId kClientId1("download", "1234");
-const ClientId kClientId2("download", "5678");
-}  // namespace
-
-class AddRequestTaskTest : public testing::Test {
- public:
-  AddRequestTaskTest();
-  ~AddRequestTaskTest() override;
-
-  void PumpLoop();
-  void ClearResults();
-
-  void InitializeStore(RequestQueueStore* store);
-
-  void AddRequestDone(ItemActionStatus status);
-
-  void GetRequestsCallback(
-      bool success,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  bool callback_called() const { return callback_called_; }
-
-  ItemActionStatus last_status() const { return status_; }
-
-  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
-    return requests_;
-  }
-
- private:
-  void InitializeStoreDone(bool success);
-
-  bool callback_called_;
-  ItemActionStatus status_;
-  std::vector<std::unique_ptr<SavePageRequest>> requests_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-AddRequestTaskTest::AddRequestTaskTest()
-    : callback_called_(false),
-      status_(ItemActionStatus::NOT_FOUND),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-AddRequestTaskTest::~AddRequestTaskTest() {}
-
-void AddRequestTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void AddRequestTaskTest::ClearResults() {
-  callback_called_ = false;
-  status_ = ItemActionStatus::NOT_FOUND;
-  requests_.clear();
-}
-
-void AddRequestTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&AddRequestTaskTest::InitializeStoreDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void AddRequestTaskTest::AddRequestDone(ItemActionStatus status) {
-  status_ = status;
-  callback_called_ = true;
-}
-
-void AddRequestTaskTest::GetRequestsCallback(
-    bool success,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  requests_ = std::move(requests);
-}
-
-void AddRequestTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-TEST_F(AddRequestTaskTest, AddSingleRequest) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  AddRequestTask task(
-      &store, request_1,
-      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
-
-  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
-                               base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, last_requests().size());
-  EXPECT_EQ(kRequestId1, last_requests().at(0)->request_id());
-  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
-  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
-  EXPECT_EQ(creation_time, last_requests().at(0)->creation_time());
-  EXPECT_TRUE(last_requests().at(0)->user_requested());
-}
-
-TEST_F(AddRequestTaskTest, AddMultipleRequests) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  base::Time creation_time_1 = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
-                            true);
-  AddRequestTask task(
-      &store, request_1,
-      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
-
-  ClearResults();
-  base::Time creation_time_2 = base::Time::Now();
-  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time_2,
-                            true);
-  AddRequestTask task_2(
-      &store, request_2,
-      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
-  task_2.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
-
-  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
-                               base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(2ul, last_requests().size());
-  int request_2_index =
-      last_requests().at(0)->request_id() == kRequestId2 ? 0 : 1;
-  EXPECT_EQ(kRequestId2, last_requests().at(request_2_index)->request_id());
-  EXPECT_EQ(kUrl2, last_requests().at(request_2_index)->url());
-  EXPECT_EQ(kClientId2, last_requests().at(request_2_index)->client_id());
-  EXPECT_EQ(creation_time_2,
-            last_requests().at(request_2_index)->creation_time());
-  EXPECT_TRUE(last_requests().at(request_2_index)->user_requested());
-}
-
-TEST_F(AddRequestTaskTest, AddDuplicateRequest) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  base::Time creation_time_1 = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
-                            true);
-  AddRequestTask task(
-      &store, request_1,
-      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
-
-  ClearResults();
-  base::Time creation_time_2 = base::Time::Now();
-  // This was has the same request ID.
-  SavePageRequest request_2(kRequestId1, kUrl2, kClientId2, creation_time_2,
-                            true);
-  AddRequestTask task_2(
-      &store, request_2,
-      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_EQ(ItemActionStatus::ALREADY_EXISTS, last_status());
-
-  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
-                               base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, last_requests().size());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/change_requests_state_task.cc b/components/offline_pages/background/change_requests_state_task.cc
deleted file mode 100644
index 545d8c7..0000000
--- a/components/offline_pages/background/change_requests_state_task.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/change_requests_state_task.h"
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-ChangeRequestsStateTask::ChangeRequestsStateTask(
-    RequestQueueStore* store,
-    const std::vector<int64_t>& request_ids,
-    const SavePageRequest::RequestState new_state,
-    const RequestQueueStore::UpdateCallback& callback)
-    : store_(store),
-      request_ids_(request_ids.begin(), request_ids.end()),
-      new_state_(new_state),
-      callback_(callback),
-      weak_ptr_factory_(this) {}
-
-ChangeRequestsStateTask::~ChangeRequestsStateTask() {}
-
-void ChangeRequestsStateTask::Run() {
-  ReadRequests();
-}
-
-void ChangeRequestsStateTask::ReadRequests() {
-  std::vector<int64_t> request_ids(request_ids_.begin(), request_ids_.end());
-  store_->GetRequestsByIds(request_ids,
-                           base::Bind(&ChangeRequestsStateTask::UpdateRequests,
-                                      weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ChangeRequestsStateTask::UpdateRequests(
-    std::unique_ptr<UpdateRequestsResult> read_result) {
-  if (read_result->store_state != StoreState::LOADED ||
-      read_result->updated_items.empty()) {
-    UpdateCompleted(std::move(read_result));
-    return;
-  }
-
-  // We are only going to make an update to the items that were found. Statuses
-  // of the missing items will be added at the end.
-  std::vector<SavePageRequest> items_to_update;
-  for (auto request : read_result->updated_items) {
-    request.set_request_state(new_state_);
-    items_to_update.push_back(request);
-  }
-
-  store_->UpdateRequests(items_to_update,
-                         base::Bind(&ChangeRequestsStateTask::UpdateCompleted,
-                                    weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ChangeRequestsStateTask::UpdateCompleted(
-    std::unique_ptr<UpdateRequestsResult> update_result) {
-  // Because the first step might not have found some of the items, we should
-  // look their IDs now and include in the final result as not found.
-
-  // Look up the missing items by removing the items present in the result
-  // statuses from original list of request IDs.
-  for (const auto& id_status_pair : update_result->item_statuses)
-    request_ids_.erase(id_status_pair.first);
-
-  // Update the final result for the items that are left in |request_ids_|, as
-  // these are identified as being missing from the final result.
-  for (int64_t request_id : request_ids_) {
-    update_result->item_statuses.push_back(
-        std::make_pair(request_id, ItemActionStatus::NOT_FOUND));
-  }
-
-  callback_.Run(std::move(update_result));
-  TaskComplete();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/change_requests_state_task.h b/components/offline_pages/background/change_requests_state_task.h
deleted file mode 100644
index 569d3e43..0000000
--- a/components/offline_pages/background/change_requests_state_task.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <unordered_set>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class ChangeRequestsStateTask : public Task {
- public:
-  ChangeRequestsStateTask(RequestQueueStore* store,
-                          const std::vector<int64_t>& request_ids,
-                          const SavePageRequest::RequestState new_state,
-                          const RequestQueueStore::UpdateCallback& callback);
-  ~ChangeRequestsStateTask() override;
-
-  // TaskQueue::Task implementation.
-  void Run() override;
-
- private:
-  // Step 1. Reading the requests.
-  void ReadRequests();
-  // Step 2. Updates available requests.
-  void UpdateRequests(std::unique_ptr<UpdateRequestsResult> read_result);
-  // Step 3. Processes update result, calls callback.
-  void UpdateCompleted(std::unique_ptr<UpdateRequestsResult> update_result);
-
-  // Store that this task updates.
-  RequestQueueStore* store_;
-  // Request IDs to be updated. Kept as a set to remove duplicates and simplify
-  // the look up of requests that are not found in step 3.
-  std::unordered_set<int64_t> request_ids_;
-  // New state to be set on all entries.
-  SavePageRequest::RequestState new_state_;
-  // Callback to complete the task.
-  RequestQueueStore::UpdateCallback callback_;
-
-  base::WeakPtrFactory<ChangeRequestsStateTask> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChangeRequestsStateTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
diff --git a/components/offline_pages/background/change_requests_state_task_unittest.cc b/components/offline_pages/background/change_requests_state_task_unittest.cc
deleted file mode 100644
index ebd4f3f..0000000
--- a/components/offline_pages/background/change_requests_state_task_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/change_requests_state_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 43;
-const int64_t kRequestId3 = 44;
-const GURL kUrl1("http://example.com");
-const GURL kUrl2("http://another-example.com");
-const ClientId kClientId1("bookmark", "1234");
-const ClientId kClientId2("async", "5678");
-}  // namespace
-
-class ChangeRequestsStateTaskTest : public testing::Test {
- public:
-  ChangeRequestsStateTaskTest();
-  ~ChangeRequestsStateTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddItemsToStore(RequestQueueStore* store);
-  void ChangeRequestsStateCallback(
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  UpdateRequestsResult* last_result() const { return result_.get(); }
-
- private:
-  void InitializeStoreDone(bool success);
-  void AddRequestDone(ItemActionStatus status);
-
-  std::unique_ptr<UpdateRequestsResult> result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-ChangeRequestsStateTaskTest::ChangeRequestsStateTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-ChangeRequestsStateTaskTest::~ChangeRequestsStateTaskTest() {}
-
-void ChangeRequestsStateTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void ChangeRequestsStateTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(
-      base::Bind(&ChangeRequestsStateTaskTest::InitializeStoreDone,
-                 base::Unretained(this)));
-  PumpLoop();
-}
-
-void ChangeRequestsStateTaskTest::AddItemsToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  store->AddRequest(request_1,
-                    base::Bind(&ChangeRequestsStateTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
-                            true);
-  store->AddRequest(request_2,
-                    base::Bind(&ChangeRequestsStateTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void ChangeRequestsStateTaskTest::ChangeRequestsStateCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  result_ = std::move(result);
-}
-
-void ChangeRequestsStateTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void ChangeRequestsStateTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(ChangeRequestsStateTaskTest, UpdateWhenStoreEmpty) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1};
-  ChangeRequestsStateTask task(
-      &store, request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(ChangeRequestsStateTaskTest, UpdateSingleItem) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1};
-  ChangeRequestsStateTask task(
-      &store, request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
-            last_result()->updated_items.at(0).request_state());
-}
-
-TEST_F(ChangeRequestsStateTaskTest, UpdateMultipleItems) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
-  ChangeRequestsStateTask task(
-      &store, request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  ASSERT_EQ(2UL, last_result()->item_statuses.size());
-
-  // Calculating the position of the items in the vector here, because it does
-  // not matter, and might be platform dependent.
-  // |index_id_1| is expected to correspond to |kRequestId1|.
-  int index_id_1 =
-      last_result()->item_statuses.at(0).first == kRequestId1 ? 0 : 1;
-  // |index_id_2| is expected to correspond to |kRequestId2|.
-  int index_id_2 = 1 - index_id_1;
-
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(index_id_1).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(index_id_1).second);
-  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(index_id_2).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(index_id_2).second);
-  ASSERT_EQ(2UL, last_result()->updated_items.size());
-  EXPECT_EQ(kRequestId1,
-            last_result()->updated_items.at(index_id_1).request_id());
-  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
-            last_result()->updated_items.at(index_id_1).request_state());
-  EXPECT_EQ(kRequestId2,
-            last_result()->updated_items.at(index_id_2).request_id());
-  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
-            last_result()->updated_items.at(index_id_2).request_state());
-}
-
-TEST_F(ChangeRequestsStateTaskTest, EmptyRequestsList) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  std::vector<int64_t> request_ids;
-  ChangeRequestsStateTask task(
-      &store, request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(0UL, last_result()->item_statuses.size());
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(ChangeRequestsStateTaskTest, UpdateMissingItem) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
-  ChangeRequestsStateTask task(
-      &store, request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  ASSERT_EQ(2UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(kRequestId3, last_result()->item_statuses.at(1).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(1).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
-            last_result()->updated_items.at(0).request_state());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/connection_notifier.cc b/components/offline_pages/background/connection_notifier.cc
deleted file mode 100644
index 82b90a8..0000000
--- a/components/offline_pages/background/connection_notifier.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/connection_notifier.h"
-
-namespace offline_pages {
-
-ConnectionNotifier::ConnectionNotifier(
-    const ConnectionNotifier::ConnectedCallback& callback)
-    : callback_(callback) {
-  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
-}
-
-ConnectionNotifier::~ConnectionNotifier() {
-  net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
-}
-
-void ConnectionNotifier::OnConnectionTypeChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
-  if (type != net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE)
-    callback_.Run();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/connection_notifier.h b/components/offline_pages/background/connection_notifier.h
deleted file mode 100644
index 3e3535a..0000000
--- a/components/offline_pages/background/connection_notifier.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
-
-#include "base/callback.h"
-#include "net/base/network_change_notifier.h"
-
-namespace offline_pages {
-
-class ConnectionNotifier
-    : public net::NetworkChangeNotifier::ConnectionTypeObserver {
- public:
-  // Callback to call when we become connected.
-  typedef base::Callback<void()> ConnectedCallback;
-
-  ConnectionNotifier(const ConnectionNotifier::ConnectedCallback& callback);
-  ~ConnectionNotifier() override;
-
-  // net::NetworkChangeNotifier::ConnectionTypeObserver implementation.
-  void OnConnectionTypeChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override;
-
- private:
-  base::Callback<void()> callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(ConnectionNotifier);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_CONNECTION_NOTIFIER_H_
diff --git a/components/offline_pages/background/device_conditions.h b/components/offline_pages/background/device_conditions.h
deleted file mode 100644
index b96306a..0000000
--- a/components/offline_pages/background/device_conditions.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
-
-#include "net/base/network_change_notifier.h"
-
-namespace offline_pages {
-
-// Device network and power conditions.
-class DeviceConditions {
- public:
-  DeviceConditions(
-      bool power_connected,
-      int battery_percentage,
-      net::NetworkChangeNotifier::ConnectionType net_connection_type)
-      : power_connected_(power_connected),
-        battery_percentage_(battery_percentage),
-        net_connection_type_(net_connection_type) {}
-
-  DeviceConditions()
-      : power_connected_(true), battery_percentage_(75),
-        net_connection_type_(net::NetworkChangeNotifier::CONNECTION_WIFI) {}
-
-  // Returns whether power is connected.
-  bool IsPowerConnected() const { return power_connected_; }
-
-  // Returns percentage of remaining battery power (0-100).
-  int GetBatteryPercentage() const { return battery_percentage_; }
-
-  // Returns the current type of network connection, if any.
-  net::NetworkChangeNotifier::ConnectionType GetNetConnectionType() const {
-    return net_connection_type_;
-  }
-
- private:
-  const bool power_connected_;
-  const int battery_percentage_;
-  const net::NetworkChangeNotifier::ConnectionType net_connection_type_;
-
-  // NOTE: We intentionally allow the default copy constructor and assignment.
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_DEVICE_CONDITIONS_H_
diff --git a/components/offline_pages/background/get_requests_task.cc b/components/offline_pages/background/get_requests_task.cc
deleted file mode 100644
index b1f2021..0000000
--- a/components/offline_pages/background/get_requests_task.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/get_requests_task.h"
-
-#include <vector>
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-GetRequestsTask::GetRequestsTask(
-    RequestQueueStore* store,
-    const RequestQueueStore::GetRequestsCallback& callback)
-    : store_(store), callback_(callback), weak_ptr_factory_(this) {}
-
-GetRequestsTask::~GetRequestsTask() {}
-
-void GetRequestsTask::Run() {
-  ReadRequest();
-}
-
-void GetRequestsTask::ReadRequest() {
-  store_->GetRequests(base::Bind(&GetRequestsTask::CompleteWithResult,
-                                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void GetRequestsTask::CompleteWithResult(
-    bool success,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  callback_.Run(success, std::move(requests));
-  TaskComplete();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/get_requests_task.h b/components/offline_pages/background/get_requests_task.h
deleted file mode 100644
index 7a5a5d1..0000000
--- a/components/offline_pages/background/get_requests_task.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_GET_REQUESTS_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_GET_REQUESTS_TASK_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class GetRequestsTask : public Task {
- public:
-  GetRequestsTask(RequestQueueStore* store,
-                  const RequestQueueStore::GetRequestsCallback& callback);
-  ~GetRequestsTask() override;
-
-  // Task implementation:
-  void Run() override;
-
- private:
-  // Step 1: Read the requests from he store.
-  void ReadRequest();
-  // Step 2: Calls the callback with result, completes the task.
-  void CompleteWithResult(
-      bool success,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  // Store from which requests will be read.
-  RequestQueueStore* store_;
-  // Callback used to return the read results.
-  RequestQueueStore::GetRequestsCallback callback_;
-
-  base::WeakPtrFactory<GetRequestsTask> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(GetRequestsTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_GET_REQUESTS_TASK_H_
diff --git a/components/offline_pages/background/get_requests_task_unittest.cc b/components/offline_pages/background/get_requests_task_unittest.cc
deleted file mode 100644
index bbd2b97..0000000
--- a/components/offline_pages/background/get_requests_task_unittest.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/get_requests_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 44;
-const GURL kUrl1("http://example.com");
-const GURL kUrl2("http://otherexample.com");
-const ClientId kClientId1("download", "1234");
-const ClientId kClientId2("download", "5678");
-}  // namespace
-
-class GetRequestsTaskTest : public testing::Test {
- public:
-  GetRequestsTaskTest();
-  ~GetRequestsTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddItemsToStore(RequestQueueStore* store);
-  void GetRequestsCallback(
-      bool success,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  bool callback_called() const { return callback_called_; }
-
-  bool last_call_successful() const { return success_; }
-
-  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
-    return requests_;
-  }
-
- private:
-  void InitializeStoreDone(bool success);
-  void AddRequestDone(ItemActionStatus status);
-
-  bool callback_called_;
-  bool success_;
-  std::vector<std::unique_ptr<SavePageRequest>> requests_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-GetRequestsTaskTest::GetRequestsTaskTest()
-    : callback_called_(false),
-      success_(false),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-GetRequestsTaskTest::~GetRequestsTaskTest() {}
-
-void GetRequestsTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void GetRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&GetRequestsTaskTest::InitializeStoreDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void GetRequestsTaskTest::AddItemsToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  store->AddRequest(request_1, base::Bind(&GetRequestsTaskTest::AddRequestDone,
-                                          base::Unretained(this)));
-  creation_time = base::Time::Now();
-  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
-                            true);
-  store->AddRequest(request_2, base::Bind(&GetRequestsTaskTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-}
-
-void GetRequestsTaskTest::GetRequestsCallback(
-    bool success,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  callback_called_ = true;
-  success_ = success;
-  requests_ = std::move(requests);
-}
-
-void GetRequestsTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void GetRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(GetRequestsTaskTest, GetFromEmptyStore) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  GetRequestsTask task(&store,
-                       base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
-                                  base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_TRUE(last_call_successful());
-  EXPECT_TRUE(last_requests().empty());
-}
-
-TEST_F(GetRequestsTaskTest, GetMultipleRequests) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemsToStore(&store);
-
-  GetRequestsTask task(&store,
-                       base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
-                                  base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_TRUE(last_call_successful());
-  ASSERT_EQ(2UL, last_requests().size());
-
-  int id_1_index = last_requests().at(0)->request_id() == kRequestId1 ? 0 : 1;
-  int id_2_index = 1 - id_1_index;
-  EXPECT_EQ(kRequestId1, last_requests().at(id_1_index)->request_id());
-  EXPECT_EQ(kRequestId2, last_requests().at(id_2_index)->request_id());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/initialize_store_task.cc b/components/offline_pages/background/initialize_store_task.cc
deleted file mode 100644
index da1a57c..0000000
--- a/components/offline_pages/background/initialize_store_task.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/initialize_store_task.h"
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-const int kRestartAttemptsMaximum = 3;
-
-InitializeStoreTask::InitializeStoreTask(
-    RequestQueueStore* store,
-    const RequestQueueStore::InitializeCallback& callback)
-    : store_(store),
-      reset_attempts_left_(kRestartAttemptsMaximum),
-      callback_(callback),
-      weak_ptr_factory_(this) {}
-
-InitializeStoreTask::~InitializeStoreTask() {}
-
-void InitializeStoreTask::Run() {
-  InitializeStore();
-}
-
-void InitializeStoreTask::InitializeStore() {
-  store_->Initialize(base::Bind(&InitializeStoreTask::CompleteIfSuccessful,
-                                weak_ptr_factory_.GetWeakPtr()));
-}
-
-void InitializeStoreTask::CompleteIfSuccessful(bool success) {
-  if (success) {
-    callback_.Run(true);
-    TaskComplete();
-    return;
-  }
-
-  TryToResetStore();
-}
-
-void InitializeStoreTask::TryToResetStore() {
-  if (reset_attempts_left_ == 0) {
-    callback_.Run(false);
-    TaskComplete();
-    return;
-  }
-
-  reset_attempts_left_--;
-  store_->Reset(base::Bind(&InitializeStoreTask::OnStoreResetDone,
-                           weak_ptr_factory_.GetWeakPtr()));
-}
-
-void InitializeStoreTask::OnStoreResetDone(bool success) {
-  if (success)
-    InitializeStore();
-  else
-    TryToResetStore();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/initialize_store_task.h b/components/offline_pages/background/initialize_store_task.h
deleted file mode 100644
index cd2ef2ab..0000000
--- a/components/offline_pages/background/initialize_store_task.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_INITIALIZE_STORE_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_INITIALIZE_STORE_TASK_H_
-
-#include <stdint.h>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-// Task performing a request queue store initialization and reset if one is
-// necessary. Reset is triggered in case the initialization fails. If reset
-// fails (perhaps not able to delete the folder or raze the database in case of
-// SQLite-backed store) a new attempt can be made. Number of attempts is limited
-// by |reset_attempts_left_|. Successful reset will be followed by another
-// attempt to initialize the database.
-//
-// Task is completed when either the store is correctly initialized or there are
-// not more reset attempts left.
-class InitializeStoreTask : public Task {
- public:
-  InitializeStoreTask(RequestQueueStore* store,
-                      const RequestQueueStore::InitializeCallback& callback);
-  ~InitializeStoreTask() override;
-
-  // TaskQueue::Task implementation.
-  void Run() override;
-
- private:
-  // Step 1. Initialize store.
-  void InitializeStore();
-  // Step 2a. Completes initialization if successful or tries to reset if there
-  // are more attempts left.
-  void CompleteIfSuccessful(bool success);
-  // Step 2b. Reset store in case initialization fails.
-  void TryToResetStore();
-  // Step 3. If 2b was successful go to step 1, else try 2b again.
-  void OnStoreResetDone(bool success);
-
-  // Store that this task initializes.
-  RequestQueueStore* store_;
-  // Number of attempts left to reset and reinitialize the store.
-  int reset_attempts_left_;
-  // Callback to complete the task.
-  RequestQueueStore::InitializeCallback callback_;
-
-  base::WeakPtrFactory<InitializeStoreTask> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(InitializeStoreTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_INITIALIZE_STORE_TASK_H_
diff --git a/components/offline_pages/background/initialize_store_task_unittest.cc b/components/offline_pages/background/initialize_store_task_unittest.cc
deleted file mode 100644
index b5b228bf..0000000
--- a/components/offline_pages/background/initialize_store_task_unittest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/initialize_store_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-using TestScenario = RequestQueueInMemoryStore::TestScenario;
-
-class InitializeStoreTaskTest : public testing::Test {
- public:
-  InitializeStoreTaskTest();
-  ~InitializeStoreTaskTest() override;
-
-  void PumpLoop();
-  void RunNextStep();
-
-  void InitializeCallback(bool success);
-
-  bool callback_called() const { return callback_called_; }
-
-  bool last_call_successful() const { return success_; }
-
- private:
-  bool callback_called_;
-  bool success_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-InitializeStoreTaskTest::InitializeStoreTaskTest()
-    : callback_called_(false),
-      success_(false),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-InitializeStoreTaskTest::~InitializeStoreTaskTest() {}
-
-void InitializeStoreTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void InitializeStoreTaskTest::RunNextStep() {
-  // Only runs tasks that are already scheduled and available. If running these
-  // tasks post new tasks to the runner, they will not be triggered. This allows
-  // for running InitializeStoreTask one step at a time.
-  task_runner_->RunPendingTasks();
-}
-
-void InitializeStoreTaskTest::InitializeCallback(bool success) {
-  callback_called_ = true;
-  success_ = success;
-}
-
-TEST_F(InitializeStoreTaskTest, SuccessfulInitialization) {
-  RequestQueueInMemoryStore store;
-  InitializeStoreTask task(
-      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
-                         base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_TRUE(last_call_successful());
-  EXPECT_EQ(StoreState::LOADED, store.state());
-}
-
-TEST_F(InitializeStoreTaskTest, SuccessfulReset) {
-  RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_SUCCESS);
-  InitializeStoreTask task(
-      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
-                         base::Unretained(this)));
-  task.Run();
-  EXPECT_FALSE(callback_called());
-  EXPECT_EQ(StoreState::FAILED_LOADING, store.state());
-
-  RunNextStep();
-  EXPECT_FALSE(callback_called());
-  EXPECT_EQ(StoreState::NOT_LOADED, store.state());
-
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_TRUE(last_call_successful());
-  EXPECT_EQ(StoreState::LOADED, store.state());
-}
-
-TEST_F(InitializeStoreTaskTest, FailedReset) {
-  RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_FAILED);
-  InitializeStoreTask task(
-      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
-                         base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  EXPECT_TRUE(callback_called());
-  EXPECT_FALSE(last_call_successful());
-  EXPECT_EQ(StoreState::FAILED_RESET, store.state());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_aborted_task.cc b/components/offline_pages/background/mark_attempt_aborted_task.cc
deleted file mode 100644
index 150baac4..0000000
--- a/components/offline_pages/background/mark_attempt_aborted_task.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-MarkAttemptAbortedTask::MarkAttemptAbortedTask(
-    RequestQueueStore* store,
-    int64_t request_id,
-    const RequestQueueStore::UpdateCallback& callback)
-    : UpdateRequestTask(store, request_id, callback) {}
-
-MarkAttemptAbortedTask::~MarkAttemptAbortedTask() {}
-
-void MarkAttemptAbortedTask::UpdateRequestImpl(
-    std::unique_ptr<UpdateRequestsResult> read_result) {
-  if (!ValidateReadResult(read_result.get())) {
-    CompleteWithResult(std::move(read_result));
-    return;
-  }
-
-  // It is perfectly fine to reuse the read_result->updated_items collection, as
-  // it is owned by this callback and will be destroyed when out of scope.
-  read_result->updated_items[0].MarkAttemptAborted();
-  store()->UpdateRequests(
-      read_result->updated_items,
-      base::Bind(&MarkAttemptAbortedTask::CompleteWithResult, GetWeakPtr()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_aborted_task.h b/components/offline_pages/background/mark_attempt_aborted_task.h
deleted file mode 100644
index 11a2c3a..0000000
--- a/components/offline_pages/background/mark_attempt_aborted_task.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
-
-#include <stdint.h>
-
-#include "components/offline_pages/background/update_request_task.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class RequestQueueStore;
-
-class MarkAttemptAbortedTask : public UpdateRequestTask {
- public:
-  MarkAttemptAbortedTask(RequestQueueStore* store,
-                         int64_t request_id,
-                         const RequestQueueStore::UpdateCallback& callback);
-  ~MarkAttemptAbortedTask() override;
-
- protected:
-  // UpdateRequestTask implementation:
-  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
diff --git a/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc b/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc
deleted file mode 100644
index 8dd937b..0000000
--- a/components/offline_pages/background/mark_attempt_aborted_task_unittest.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 44;
-const GURL kUrl1("http://example.com");
-const ClientId kClientId1("download", "1234");
-}  // namespace
-
-class MarkAttemptAbortedTaskTest : public testing::Test {
- public:
-  MarkAttemptAbortedTaskTest();
-  ~MarkAttemptAbortedTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddItemToStore(RequestQueueStore* store);
-  void ChangeRequestsStateCallback(
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  void ClearResults();
-
-  UpdateRequestsResult* last_result() const { return result_.get(); }
-
- private:
-  void InitializeStoreDone(bool success);
-  void AddRequestDone(ItemActionStatus status);
-
-  std::unique_ptr<UpdateRequestsResult> result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-MarkAttemptAbortedTaskTest::MarkAttemptAbortedTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-MarkAttemptAbortedTaskTest::~MarkAttemptAbortedTaskTest() {}
-
-void MarkAttemptAbortedTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void MarkAttemptAbortedTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&MarkAttemptAbortedTaskTest::InitializeStoreDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptAbortedTaskTest::AddItemToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  store->AddRequest(request_1,
-                    base::Bind(&MarkAttemptAbortedTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  result_ = std::move(result);
-}
-
-void MarkAttemptAbortedTaskTest::ClearResults() {
-  result_.reset(nullptr);
-}
-
-void MarkAttemptAbortedTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void MarkAttemptAbortedTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenStoreEmpty) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  MarkAttemptAbortedTask task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenExists) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemToStore(&store);
-
-  // First mark attempt started.
-  MarkAttemptStartedTask start_request_task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  start_request_task.Run();
-  PumpLoop();
-  ClearResults();
-
-  MarkAttemptAbortedTask task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            last_result()->updated_items.at(0).request_state());
-}
-
-TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenItemMissing) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemToStore(&store);
-
-  MarkAttemptAbortedTask task(
-      &store, kRequestId2,
-      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_completed_task.cc b/components/offline_pages/background/mark_attempt_completed_task.cc
deleted file mode 100644
index d3ec900f..0000000
--- a/components/offline_pages/background/mark_attempt_completed_task.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/time/time.h"
-
-namespace offline_pages {
-
-MarkAttemptCompletedTask::MarkAttemptCompletedTask(
-    RequestQueueStore* store,
-    int64_t request_id,
-    const RequestQueueStore::UpdateCallback& callback)
-    : UpdateRequestTask(store, request_id, callback) {}
-
-MarkAttemptCompletedTask::~MarkAttemptCompletedTask() {}
-
-void MarkAttemptCompletedTask::UpdateRequestImpl(
-    std::unique_ptr<UpdateRequestsResult> read_result) {
-  if (!ValidateReadResult(read_result.get())) {
-    CompleteWithResult(std::move(read_result));
-    return;
-  }
-
-  // It is perfectly fine to reuse the read_result->updated_items collection, as
-  // it is owned by this callback and will be destroyed when out of scope.
-  read_result->updated_items[0].MarkAttemptCompleted();
-  store()->UpdateRequests(
-      read_result->updated_items,
-      base::Bind(&MarkAttemptCompletedTask::CompleteWithResult, GetWeakPtr()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_completed_task.h b/components/offline_pages/background/mark_attempt_completed_task.h
deleted file mode 100644
index 7a1d3b3b2..0000000
--- a/components/offline_pages/background/mark_attempt_completed_task.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
-
-#include <stdint.h>
-#include <memory>
-
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/update_request_task.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class RequestQueueStore;
-
-class MarkAttemptCompletedTask : public UpdateRequestTask {
- public:
-  MarkAttemptCompletedTask(RequestQueueStore* store,
-                           int64_t request_id,
-                           const RequestQueueStore::UpdateCallback& callback);
-  ~MarkAttemptCompletedTask() override;
-
- protected:
-  // UpdateRequestTask implementation:
-  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
diff --git a/components/offline_pages/background/mark_attempt_completed_task_unittest.cc b/components/offline_pages/background/mark_attempt_completed_task_unittest.cc
deleted file mode 100644
index b37f388..0000000
--- a/components/offline_pages/background/mark_attempt_completed_task_unittest.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 44;
-const GURL kUrl1("http://example.com");
-const ClientId kClientId1("download", "1234");
-}  // namespace
-
-class MarkAttemptCompletedTaskTest : public testing::Test {
- public:
-  MarkAttemptCompletedTaskTest();
-  ~MarkAttemptCompletedTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddStartedItemToStore(RequestQueueStore* store);
-  void ChangeRequestsStateCallback(
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  UpdateRequestsResult* last_result() const { return result_.get(); }
-
- private:
-  void InitializeStoreDone(bool success);
-  void AddRequestDone(ItemActionStatus status);
-
-  std::unique_ptr<UpdateRequestsResult> result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-MarkAttemptCompletedTaskTest::MarkAttemptCompletedTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-MarkAttemptCompletedTaskTest::~MarkAttemptCompletedTaskTest() {}
-
-void MarkAttemptCompletedTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void MarkAttemptCompletedTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(
-      base::Bind(&MarkAttemptCompletedTaskTest::InitializeStoreDone,
-                 base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptCompletedTaskTest::AddStartedItemToStore(
-    RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  request_1.MarkAttemptStarted(base::Time::Now());
-  store->AddRequest(request_1,
-                    base::Bind(&MarkAttemptCompletedTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  result_ = std::move(result);
-}
-
-void MarkAttemptCompletedTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void MarkAttemptCompletedTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenExists) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddStartedItemToStore(&store);
-
-  MarkAttemptCompletedTask task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(1, last_result()->updated_items.at(0).completed_attempt_count());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            last_result()->updated_items.at(0).request_state());
-}
-
-TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenItemMissing) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  // Add request 1 to the store.
-  AddStartedItemToStore(&store);
-  // Try to mark request 2 (not in the store).
-  MarkAttemptCompletedTask task(
-      &store, kRequestId2,
-      base::Bind(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_started_task.cc b/components/offline_pages/background/mark_attempt_started_task.cc
deleted file mode 100644
index 95776fe..0000000
--- a/components/offline_pages/background/mark_attempt_started_task.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/time/time.h"
-
-namespace offline_pages {
-
-MarkAttemptStartedTask::MarkAttemptStartedTask(
-    RequestQueueStore* store,
-    int64_t request_id,
-    const RequestQueueStore::UpdateCallback& callback)
-    : UpdateRequestTask(store, request_id, callback) {}
-
-MarkAttemptStartedTask::~MarkAttemptStartedTask() {}
-
-void MarkAttemptStartedTask::UpdateRequestImpl(
-    std::unique_ptr<UpdateRequestsResult> read_result) {
-  if (!ValidateReadResult(read_result.get())) {
-    CompleteWithResult(std::move(read_result));
-    return;
-  }
-
-  // It is perfectly fine to reuse the read_result->updated_items collection, as
-  // it is owned by this callback and will be destroyed when out of scope.
-  read_result->updated_items[0].MarkAttemptStarted(base::Time::Now());
-  store()->UpdateRequests(
-      read_result->updated_items,
-      base::Bind(&MarkAttemptStartedTask::CompleteWithResult, GetWeakPtr()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/mark_attempt_started_task.h b/components/offline_pages/background/mark_attempt_started_task.h
deleted file mode 100644
index b0a93c6f..0000000
--- a/components/offline_pages/background/mark_attempt_started_task.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
-
-#include <stdint.h>
-
-#include "components/offline_pages/background/update_request_task.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class RequestQueueStore;
-
-class MarkAttemptStartedTask : public UpdateRequestTask {
- public:
-  MarkAttemptStartedTask(RequestQueueStore* store,
-                         int64_t request_id,
-                         const RequestQueueStore::UpdateCallback& callback);
-  ~MarkAttemptStartedTask() override;
-
- protected:
-  // UpdateRequestTask implementation:
-  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
diff --git a/components/offline_pages/background/mark_attempt_started_task_unittest.cc b/components/offline_pages/background/mark_attempt_started_task_unittest.cc
deleted file mode 100644
index e977fc44..0000000
--- a/components/offline_pages/background/mark_attempt_started_task_unittest.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 44;
-const GURL kUrl1("http://example.com");
-const ClientId kClientId1("download", "1234");
-}  // namespace
-
-class MarkAttemptStartedTaskTest : public testing::Test {
- public:
-  MarkAttemptStartedTaskTest();
-  ~MarkAttemptStartedTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddItemToStore(RequestQueueStore* store);
-  void ChangeRequestsStateCallback(
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  UpdateRequestsResult* last_result() const { return result_.get(); }
-
- private:
-  void InitializeStoreDone(bool success);
-  void AddRequestDone(ItemActionStatus status);
-
-  std::unique_ptr<UpdateRequestsResult> result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-MarkAttemptStartedTaskTest::MarkAttemptStartedTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-MarkAttemptStartedTaskTest::~MarkAttemptStartedTaskTest() {}
-
-void MarkAttemptStartedTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void MarkAttemptStartedTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&MarkAttemptStartedTaskTest::InitializeStoreDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptStartedTaskTest::AddItemToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  store->AddRequest(request_1,
-                    base::Bind(&MarkAttemptStartedTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void MarkAttemptStartedTaskTest::ChangeRequestsStateCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  result_ = std::move(result);
-}
-
-void MarkAttemptStartedTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void MarkAttemptStartedTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenStoreEmpty) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  MarkAttemptStartedTask task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenExists) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemToStore(&store);
-
-  MarkAttemptStartedTask task(
-      &store, kRequestId1,
-      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-
-  // Current time for verification.
-  base::Time before_time = base::Time::Now();
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_LE(before_time,
-            last_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_GE(base::Time::Now(),
-            last_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_EQ(1, last_result()->updated_items.at(0).started_attempt_count());
-  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING,
-            last_result()->updated_items.at(0).request_state());
-}
-
-TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenItemMissing) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddItemToStore(&store);
-
-  MarkAttemptStartedTask task(
-      &store, kRequestId2,
-      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/network_quality_provider_stub.cc b/components/offline_pages/background/network_quality_provider_stub.cc
deleted file mode 100644
index 015faf5..0000000
--- a/components/offline_pages/background/network_quality_provider_stub.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/network_quality_provider_stub.h"
-
-namespace offline_pages {
-
-const char kOfflineNQPKey[] = "OfflineNQP";
-
-NetworkQualityProviderStub::NetworkQualityProviderStub()
-    : connection_type_(
-          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G) {}
-
-NetworkQualityProviderStub::~NetworkQualityProviderStub() {}
-
-// static
-NetworkQualityProviderStub* NetworkQualityProviderStub::GetUserData(
-    base::SupportsUserData* supports_user_data) {
-  return static_cast<NetworkQualityProviderStub*>(
-      supports_user_data->GetUserData(&kOfflineNQPKey));
-}
-
-// static
-void NetworkQualityProviderStub::SetUserData(
-    base::SupportsUserData* supports_user_data,
-    NetworkQualityProviderStub* stub) {
-  DCHECK(supports_user_data);
-  DCHECK(stub);
-  supports_user_data->SetUserData(&kOfflineNQPKey, stub);
-}
-
-void NetworkQualityProviderStub::AddEffectiveConnectionTypeObserver(
-    net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
-
-void NetworkQualityProviderStub::RemoveEffectiveConnectionTypeObserver(
-    net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
-
-net::EffectiveConnectionType
-NetworkQualityProviderStub::GetEffectiveConnectionType() const {
-  return connection_type_;
-}
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/network_quality_provider_stub.h b/components/offline_pages/background/network_quality_provider_stub.h
deleted file mode 100644
index bd71b177..0000000
--- a/components/offline_pages/background/network_quality_provider_stub.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
-
-#include "base/supports_user_data.h"
-#include "net/nqe/effective_connection_type.h"
-#include "net/nqe/network_quality_estimator.h"
-
-namespace offline_pages {
-
-// Test class stubbing out the functionality of NQE::NetworkQualityProvider.
-// It is only used for test support.
-class NetworkQualityProviderStub
-    : public net::NetworkQualityEstimator::NetworkQualityProvider,
-      public base::SupportsUserData::Data {
- public:
-  NetworkQualityProviderStub();
-  ~NetworkQualityProviderStub() override;
-
-  static NetworkQualityProviderStub* GetUserData(
-      base::SupportsUserData* supports_user_data);
-  static void SetUserData(base::SupportsUserData* supports_user_data,
-                          NetworkQualityProviderStub* stub);
-
-  net::EffectiveConnectionType GetEffectiveConnectionType() const override;
-
-  void AddEffectiveConnectionTypeObserver(
-      net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
-      override;
-
-  void RemoveEffectiveConnectionTypeObserver(
-      net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
-      override;
-
-  void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
-    connection_type_ = type;
-  }
-
- private:
-  net::EffectiveConnectionType connection_type_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
diff --git a/components/offline_pages/background/offliner.h b/components/offline_pages/background/offliner.h
deleted file mode 100644
index 7238de9..0000000
--- a/components/offline_pages/background/offliner.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
-
-#include <string>
-
-#include "base/callback.h"
-
-namespace offline_pages {
-
-class SavePageRequest;
-
-// Interface of a class responsible for constructing an offline page given
-// a request with a URL.
-class Offliner {
- public:
-  // Status of processing an offline page request.
-  // WARNING: You must update histograms.xml to match any changes made to
-  // this enum (ie, OfflinePagesBackgroundOfflinerRequestStatus histogram enum).
-  // Also update related switch code in RequestCoordinatorEventLogger.
-  enum RequestStatus {
-    // No status determined/reported yet. Interim status, not sent in callback.
-    UNKNOWN = 0,
-    // Page loaded but not (yet) saved. Interim status, not sent in callback.
-    LOADED = 1,
-    // Offline page snapshot saved.
-    SAVED = 2,
-    // RequestCoordinator canceled request.
-    REQUEST_COORDINATOR_CANCELED = 3,
-    // Prerendering was canceled.
-    PRERENDERING_CANCELED = 4,
-    // Prerendering failed to load page.
-    PRERENDERING_FAILED = 5,
-    // Failed to save loaded page.
-    SAVE_FAILED = 6,
-    // Foreground transition canceled request.
-    FOREGROUND_CANCELED = 7,
-    // RequestCoordinator canceled request attempt per time limit.
-    REQUEST_COORDINATOR_TIMED_OUT = 8,
-    // The loader did not accept/start the request.
-    PRERENDERING_NOT_STARTED = 9,
-    // Prerendering failed with hard error so should not retry the request.
-    PRERENDERING_FAILED_NO_RETRY = 10,
-    // Prerendering failed with some error that suggests we should not continue
-    // processing another request at this time.
-    PRERENDERING_FAILED_NO_NEXT = 11,
-    // NOTE: insert new values above this line and update histogram enum too.
-    STATUS_COUNT
-  };
-
-  // Reports the completion status of a request.
-  // TODO(dougarnett): consider passing back a request id instead of request.
-  typedef base::Callback<void(const SavePageRequest&, RequestStatus)>
-      CompletionCallback;
-
-  Offliner() {}
-  virtual ~Offliner() {}
-
-  // Processes |request| to load and save an offline page.
-  // Returns whether the request was accepted or not. |callback| is guaranteed
-  // to be called if the request was accepted and |Cancel()| is not called.
-  virtual bool LoadAndSave(
-      const SavePageRequest& request,
-      const CompletionCallback& callback) = 0;
-
-  // Clears the currently processing request, if any, and skips running its
-  // CompletionCallback.
-  virtual void Cancel() = 0;
-
-  // TODO(dougarnett): add policy support methods.
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_H_
diff --git a/components/offline_pages/background/offliner_factory.h b/components/offline_pages/background/offliner_factory.h
deleted file mode 100644
index 0cfae0a..0000000
--- a/components/offline_pages/background/offliner_factory.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
-
-#include "base/macros.h"
-
-namespace offline_pages {
-
-class OfflinerPolicy;
-class Offliner;
-
-// An interface to a factory which can create a background offliner.
-// The returned factory is owned by the offliner, which is allowed to cache the
-// factory and return it again later.
-class OfflinerFactory {
- public:
-  OfflinerFactory() {}
-  virtual ~OfflinerFactory() {}
-
-  virtual Offliner* GetOffliner(const OfflinerPolicy* policy) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(OfflinerFactory);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_H_
diff --git a/components/offline_pages/background/offliner_factory_stub.cc b/components/offline_pages/background/offliner_factory_stub.cc
deleted file mode 100644
index f3209069..0000000
--- a/components/offline_pages/background/offliner_factory_stub.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/offliner_factory_stub.h"
-
-#include "components/offline_pages/background/offliner_stub.h"
-
-namespace offline_pages {
-
-OfflinerFactoryStub::OfflinerFactoryStub() : offliner_(nullptr) {}
-
-OfflinerFactoryStub::~OfflinerFactoryStub() {}
-
-Offliner* OfflinerFactoryStub::GetOffliner(const OfflinerPolicy* policy) {
-  if (offliner_.get() == nullptr) {
-    offliner_.reset(new OfflinerStub());
-  }
-  return offliner_.get();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/offliner_factory_stub.h b/components/offline_pages/background/offliner_factory_stub.h
deleted file mode 100644
index e2aec6b0..0000000
--- a/components/offline_pages/background/offliner_factory_stub.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_STUB_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_STUB_H_
-
-#include <memory>
-
-#include "components/offline_pages/background/offliner_factory.h"
-
-namespace offline_pages {
-
-class OfflinerStub;
-
-// Test class stubbing out the functionality of OfflinerFactory.
-// It is only used for test support.
-class OfflinerFactoryStub : public OfflinerFactory {
- public:
-  OfflinerFactoryStub();
-  ~OfflinerFactoryStub() override;
-
-  Offliner* GetOffliner(const OfflinerPolicy* policy) override;
-
- private:
-  std::unique_ptr<OfflinerStub> offliner_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_FACTORY_STUB_H_
diff --git a/components/offline_pages/background/offliner_policy.h b/components/offline_pages/background/offliner_policy.h
deleted file mode 100644
index 7ffd4549..0000000
--- a/components/offline_pages/background/offliner_policy.h
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
-
-namespace {
-// The max number of started tries is to guard against pages that make the
-// prerenderer crash.  It should be greater than or equal to the max number of
-// completed tries.
-const int kMaxStartedTries = 5;
-// The number of max completed tries is based on Gin2G-poor testing showing that
-// we often need about 4 tries with a 2 minute window, or 3 retries with a 3
-// minute window. Also, we count one try now for foreground/disabled requests.
-const int kMaxCompletedTries = 3;
-// By the time we get to a week, the user has forgotten asking for a page.
-const int kRequestExpirationTimeInSeconds = 60 * 60 * 24 * 7;
-
-// Scheduled background processing time limits.
-const int kDozeModeBackgroundServiceWindowSeconds = 60 * 3;
-const int kDefaultBackgroundProcessingTimeBudgetSeconds =
-    kDozeModeBackgroundServiceWindowSeconds - 10;
-const int kSinglePageTimeLimitWhenBackgroundScheduledSeconds =
-    kDozeModeBackgroundServiceWindowSeconds - 10;
-
-// Immediate processing time limits.
-// Note: experiments on GIN-2g-poor show many page requests took 3 or 4
-// attempts in background scheduled mode with timeout of 2 minutes. So for
-// immediate processing mode, give page requests 4 times that limit (8 min).
-// Then budget up to 5 of those requests in processing window.
-const int kSinglePageTimeLimitForImmediateLoadSeconds = 60 * 8;
-const int kImmediateLoadProcessingTimeBudgetSeconds =
-    kSinglePageTimeLimitForImmediateLoadSeconds * 5;
-}  // namespace
-
-namespace offline_pages {
-
-// Policy for the Background Offlining system.  Some policy will belong to the
-// RequestCoordinator, some to the RequestQueue, and some to the Offliner.
-class OfflinerPolicy {
- public:
-  OfflinerPolicy()
-      : prefer_untried_requests_(true),
-        prefer_earlier_requests_(true),
-        retry_count_is_more_important_than_recency_(true),
-        max_started_tries_(kMaxStartedTries),
-        max_completed_tries_(kMaxCompletedTries),
-        background_scheduled_processing_time_budget_(
-            kDefaultBackgroundProcessingTimeBudgetSeconds) {}
-
-  // Constructor for unit tests.
-  OfflinerPolicy(bool prefer_untried,
-                 bool prefer_earlier,
-                 bool prefer_retry_count,
-                 int max_started_tries,
-                 int max_completed_tries,
-                 int background_processing_time_budget)
-      : prefer_untried_requests_(prefer_untried),
-        prefer_earlier_requests_(prefer_earlier),
-        retry_count_is_more_important_than_recency_(prefer_retry_count),
-        max_started_tries_(max_started_tries),
-        max_completed_tries_(max_completed_tries),
-        background_scheduled_processing_time_budget_(
-            background_processing_time_budget) {}
-
-  // TODO(petewil): Numbers here are chosen arbitrarily, do the proper studies
-  // to get good policy numbers. Eventually this should get data from a finch
-  // experiment.
-
-  // Returns true if we should prefer retrying lesser tried requests.
-  bool ShouldPreferUntriedRequests() const { return prefer_untried_requests_; }
-
-  // Returns true if we should prefer older requests of equal number of tries.
-  bool ShouldPreferEarlierRequests() const { return prefer_earlier_requests_; }
-
-  // Returns true if retry count is considered more important than recency in
-  // picking which request to try next.
-  bool RetryCountIsMoreImportantThanRecency() const {
-    return retry_count_is_more_important_than_recency_;
-  }
-
-  // The max number of times we will start a request.  Not all started attempts
-  // will complete.  This may be caused by prerenderer issues or chromium being
-  // swapped out of memory.
-  int GetMaxStartedTries() const { return max_started_tries_; }
-
-  // The max number of times we will retry a request when the attempt
-  // completed, but failed.
-  int GetMaxCompletedTries() const { return max_completed_tries_; }
-
-  bool PowerRequired(bool user_requested) const {
-    return (!user_requested);
-  }
-
-  bool UnmeteredNetworkRequired(bool user_requested) const {
-    return !(user_requested);
-  }
-
-  int BatteryPercentageRequired(bool user_requested) const {
-    if (user_requested)
-      return 0;
-    // This is so low because we require the device to be plugged in and
-    // charging.  If we decide to allow non-user requested pages when not
-    // plugged in, we should raise this somewhat higher.
-    return 25;
-  }
-
-  // How many seconds to keep trying new pages for, before we give up, and
-  // return to the scheduler.
-  // TODO(dougarnett): Consider parameterizing these time limit/budget
-  // calls with processing mode.
-  int GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds() const {
-    return background_scheduled_processing_time_budget_;
-  }
-
-  // How many seconds to keep trying new pages for, before we give up, when
-  // processing started immediately (without scheduler).
-  int GetProcessingTimeBudgetForImmediateLoadInSeconds() const {
-    return kImmediateLoadProcessingTimeBudgetSeconds;
-  }
-
-  // How long do we allow a page to load before giving up on it when
-  // background loading was scheduled.
-  int GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() const {
-    return kSinglePageTimeLimitWhenBackgroundScheduledSeconds;
-  }
-
-  // How long do we allow a page to load before giving up on it when
-  // immediately background loading.
-  int GetSinglePageTimeLimitForImmediateLoadInSeconds() const {
-    return kSinglePageTimeLimitForImmediateLoadSeconds;
-  }
-
-  // How long we allow requests to remain in the system before giving up.
-  int GetRequestExpirationTimeInSeconds() const {
-    return kRequestExpirationTimeInSeconds;
-  }
-
- private:
-  bool prefer_untried_requests_;
-  bool prefer_earlier_requests_;
-  bool retry_count_is_more_important_than_recency_;
-  int max_started_tries_;
-  int max_completed_tries_;
-  int background_scheduled_processing_time_budget_;
-};
-}  // namespace offline_pages
-
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_POLICY_H_
diff --git a/components/offline_pages/background/offliner_stub.cc b/components/offline_pages/background/offliner_stub.cc
deleted file mode 100644
index 7916d17..0000000
--- a/components/offline_pages/background/offliner_stub.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/offliner_stub.h"
-
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-OfflinerStub::OfflinerStub()
-    : disable_loading_(false), enable_callback_(false), cancel_called_(false) {}
-
-OfflinerStub::~OfflinerStub() {}
-
-bool OfflinerStub::LoadAndSave(const SavePageRequest& request,
-                               const CompletionCallback& callback) {
-  if (disable_loading_)
-    return false;
-
-  callback_ = callback;
-
-  // Post the callback on the run loop.
-  if (enable_callback_) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(callback, request, Offliner::RequestStatus::SAVED));
-  }
-  return true;
-}
-
-void OfflinerStub::Cancel() {
-  cancel_called_ = true;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/offliner_stub.h b/components/offline_pages/background/offliner_stub.h
deleted file mode 100644
index 8f5060d..0000000
--- a/components/offline_pages/background/offliner_stub.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_STUB_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_STUB_H_
-
-#include "components/offline_pages/background/offliner.h"
-
-namespace offline_pages {
-
-// Test class stubbing out the functionality of Offliner.
-// It is only used for test support.
-class OfflinerStub : public Offliner {
- public:
-  OfflinerStub();
-  ~OfflinerStub() override;
-
-  bool LoadAndSave(const SavePageRequest& request,
-                   const CompletionCallback& callback) override;
-
-  void Cancel() override;
-
-  void disable_loading() { disable_loading_ = true; }
-
-  void enable_callback(bool enable) { enable_callback_ = enable; }
-
-  bool cancel_called() { return cancel_called_; }
-
- private:
-  CompletionCallback callback_;
-  bool disable_loading_;
-  bool enable_callback_;
-  bool cancel_called_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_OFFLINER_STUB_H_
diff --git a/components/offline_pages/background/pick_request_task.cc b/components/offline_pages/background/pick_request_task.cc
deleted file mode 100644
index dd936f5..0000000
--- a/components/offline_pages/background/pick_request_task.cc
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/pick_request_task.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/time/time.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace {
-template <typename T>
-int signum(T t) {
-  return (T(0) < t) - (t < T(0));
-}
-
-#define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
-}  // namespace
-
-namespace offline_pages {
-
-PickRequestTask::PickRequestTask(RequestQueueStore* store,
-                                 OfflinerPolicy* policy,
-                                 RequestNotifier* notifier,
-                                 RequestCoordinatorEventLogger* event_logger,
-                                 RequestPickedCallback picked_callback,
-                                 RequestNotPickedCallback not_picked_callback,
-                                 RequestCountCallback request_count_callback,
-                                 DeviceConditions& device_conditions,
-                                 const std::set<int64_t>& disabled_requests)
-    : store_(store),
-      policy_(policy),
-      notifier_(notifier),
-      event_logger_(event_logger),
-      picked_callback_(picked_callback),
-      not_picked_callback_(not_picked_callback),
-      request_count_callback_(request_count_callback),
-      disabled_requests_(disabled_requests),
-      weak_ptr_factory_(this) {
-  device_conditions_.reset(new DeviceConditions(device_conditions));
-}
-
-PickRequestTask::~PickRequestTask() {}
-
-void PickRequestTask::Run() {
-  // Get all the requests from the queue, we will classify them in the callback.
-  store_->GetRequests(base::Bind(&PickRequestTask::ChooseAndPrune,
-                                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PickRequestTask::ChooseAndPrune(
-    bool success,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  // If there is nothing to do, return right away.
-  if (requests.empty()) {
-    request_count_callback_.Run(requests.size(), 0);
-    not_picked_callback_.Run(false);
-    TaskComplete();
-    return;
-  }
-
-  // Get the expired requests to be removed from the queue, and the valid ones
-  // from which to pick the next request.
-  std::vector<std::unique_ptr<SavePageRequest>> valid_requests;
-  std::vector<int64_t> expired_request_ids;
-  SplitRequests(std::move(requests), &valid_requests, &expired_request_ids);
-
-  // Continue processing by choosing a request.
-  ChooseRequestAndCallback(std::move(valid_requests));
-
-  // Continue processing by handling expired requests, if any.
-  if (expired_request_ids.size() == 0) {
-    TaskComplete();
-    return;
-  }
-
-  RemoveStaleRequests(std::move(expired_request_ids));
-}
-
-void PickRequestTask::ChooseRequestAndCallback(
-    std::vector<std::unique_ptr<SavePageRequest>> valid_requests) {
-  // Pick the most deserving request for our conditions.
-  const SavePageRequest* picked_request = nullptr;
-
-  RequestCompareFunction comparator = nullptr;
-
-  // Choose which comparison function to use based on policy.
-  if (policy_->RetryCountIsMoreImportantThanRecency())
-    comparator = &PickRequestTask::RetryCountFirstCompareFunction;
-  else
-    comparator = &PickRequestTask::RecencyFirstCompareFunction;
-
-  // TODO(petewil): Consider replacing this bool with a better named enum.
-  bool non_user_requested_tasks_remaining = false;
-
-  size_t available_request_count = 0;
-
-  // Iterate once through the requests, keeping track of best candidate.
-  for (unsigned i = 0; i < valid_requests.size(); ++i) {
-    // If the  request is on the disabled list, skip it.
-    auto search = disabled_requests_.find(valid_requests[i]->request_id());
-    if (search != disabled_requests_.end())
-      continue;
-    // If there are non-user-requested tasks remaining, we need to make sure
-    // that they are scheduled after we run out of user requested tasks. Here we
-    // detect if any exist. If we don't find any user-requested tasks, we will
-    // inform the "not_picked_callback_" that it needs to schedule a task for
-    // non-user-requested items, which have different network and power needs.
-    if (!valid_requests[i]->user_requested())
-      non_user_requested_tasks_remaining = true;
-    if (valid_requests[i]->request_state() ==
-        SavePageRequest::RequestState::AVAILABLE) {
-      available_request_count++;
-    }
-    if (!RequestConditionsSatisfied(valid_requests[i].get()))
-      continue;
-    if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator))
-      picked_request = valid_requests[i].get();
-  }
-
-  // Report the request queue counts.
-  request_count_callback_.Run(valid_requests.size(), available_request_count);
-
-  // If we have a best request to try next, get the request coodinator to
-  // start it.  Otherwise return that we have no candidates.
-  if (picked_request != nullptr)
-    picked_callback_.Run(*picked_request);
-  else
-    not_picked_callback_.Run(non_user_requested_tasks_remaining);
-}
-
-// Continue the async part of the processing by deleting the expired requests.
-// TODO(petewil): Does that really need to be done on the task queue? Hard to
-// see how we need to wait for it before starting the next task. OTOH, we'd hate
-// to do a second slow DB operation to get entries a second time, and waiting
-// until this is done will make sure other gets don't see these old entries.
-// Consider moving this to a fresh task type to clean the queue.
-void PickRequestTask::RemoveStaleRequests(
-    std::vector<int64_t> stale_request_ids) {
-  store_->RemoveRequests(stale_request_ids,
-                         base::Bind(&PickRequestTask::OnRequestsExpired,
-                                    weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PickRequestTask::OnRequestsExpired(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  RequestNotifier::BackgroundSavePageResult save_page_result(
-      RequestNotifier::BackgroundSavePageResult::EXPIRED);
-  for (const auto& request : result->updated_items) {
-    event_logger_->RecordDroppedSavePageRequest(
-        request.client_id().name_space, save_page_result, request.request_id());
-    notifier_->NotifyCompleted(request, save_page_result);
-  }
-
-  // The task is now done, return control to the task queue.
-  TaskComplete();
-}
-
-void PickRequestTask::SplitRequests(
-    std::vector<std::unique_ptr<SavePageRequest>> requests,
-    std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
-    std::vector<int64_t>* expired_request_ids) {
-  for (auto& request : requests) {
-    if (base::Time::Now() - request->creation_time() >=
-        base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
-      expired_request_ids->push_back(request->request_id());
-    } else {
-      valid_requests->push_back(std::move(request));
-    }
-  }
-}
-
-// Filter out requests that don't meet the current conditions.  For instance, if
-// this is a predictive request, and we are not on WiFi, it should be ignored
-// this round.
-bool PickRequestTask::RequestConditionsSatisfied(
-    const SavePageRequest* request) {
-  // If the user did not request the page directly, make sure we are connected
-  // to power and have WiFi and sufficient battery remaining before we take this
-  // request.
-  if (!device_conditions_->IsPowerConnected() &&
-      policy_->PowerRequired(request->user_requested())) {
-    return false;
-  }
-
-  if (device_conditions_->GetNetConnectionType() !=
-          net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI &&
-      policy_->UnmeteredNetworkRequired(request->user_requested())) {
-    return false;
-  }
-
-  if (device_conditions_->GetBatteryPercentage() <
-      policy_->BatteryPercentageRequired(request->user_requested())) {
-    return false;
-  }
-
-  // If we have already started this page the max number of times, it is not
-  // eligible to try again.
-  if (request->started_attempt_count() >= policy_->GetMaxStartedTries())
-    return false;
-
-  // If we have already completed trying this page the max number of times, it
-  // is not eligible to try again.
-  if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries())
-    return false;
-
-  // If the request is paused, do not consider it.
-  if (request->request_state() == SavePageRequest::RequestState::PAUSED)
-    return false;
-
-  // If the request is expired, do not consider it.
-  base::TimeDelta requestAge = base::Time::Now() - request->creation_time();
-  if (requestAge > base::TimeDelta::FromSeconds(
-                       policy_->GetRequestExpirationTimeInSeconds()))
-    return false;
-
-  // If this request is not active yet, return false.
-  // TODO(petewil): If the only reason we return nothing to do is that we have
-  // inactive requests, we still want to try again later after their activation
-  // time elapses, we shouldn't take ourselves completely off the scheduler.
-  if (request->activation_time() > base::Time::Now())
-    return false;
-
-  return true;
-}
-
-// Look at policies to decide which requests to prefer.
-bool PickRequestTask::IsNewRequestBetter(const SavePageRequest* oldRequest,
-                                         const SavePageRequest* newRequest,
-                                         RequestCompareFunction comparator) {
-  // If there is no old request, the new one is better.
-  if (oldRequest == nullptr)
-    return true;
-
-  // User requested pages get priority.
-  if (newRequest->user_requested() && !oldRequest->user_requested())
-    return true;
-
-  // Otherwise, use the comparison function for the current policy, which
-  // returns true if the older request is better.
-  return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest));
-}
-
-// Compare the results, checking request count before recency.  Returns true if
-// left hand side is better, false otherwise.
-bool PickRequestTask::RetryCountFirstCompareFunction(
-    const SavePageRequest* left,
-    const SavePageRequest* right) {
-  // Check the attempt count.
-  int result = CompareRetryCount(left, right);
-
-  if (result != 0)
-    return (result > 0);
-
-  // If we get here, the attempt counts were the same, so check recency.
-  result = CompareCreationTime(left, right);
-
-  return (result > 0);
-}
-
-// Compare the results, checking recency before request count. Returns true if
-// left hand side is better, false otherwise.
-bool PickRequestTask::RecencyFirstCompareFunction(
-    const SavePageRequest* left,
-    const SavePageRequest* right) {
-  // Check the recency.
-  int result = CompareCreationTime(left, right);
-
-  if (result != 0)
-    return (result > 0);
-
-  // If we get here, the recency was the same, so check the attempt count.
-  result = CompareRetryCount(left, right);
-
-  return (result > 0);
-}
-
-// Compare left and right side, returning 1 if the left side is better
-// (preferred by policy), 0 if the same, and -1 if the right side is better.
-int PickRequestTask::CompareRetryCount(const SavePageRequest* left,
-                                       const SavePageRequest* right) {
-  // Check the attempt count.
-  int result = signum(left->completed_attempt_count() -
-                      right->completed_attempt_count());
-
-  // Flip the direction of comparison if policy prefers fewer retries.
-  if (policy_->ShouldPreferUntriedRequests())
-    result *= -1;
-
-  return result;
-}
-
-// Compare left and right side, returning 1 if the left side is better
-// (preferred by policy), 0 if the same, and -1 if the right side is better.
-int PickRequestTask::CompareCreationTime(const SavePageRequest* left,
-                                         const SavePageRequest* right) {
-  // Check the recency.
-  base::TimeDelta difference = left->creation_time() - right->creation_time();
-  int result = signum(difference.InMilliseconds());
-
-  // Flip the direction of comparison if policy prefers fewer retries.
-  if (policy_->ShouldPreferEarlierRequests())
-    result *= -1;
-
-  return result;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/pick_request_task.h b/components/offline_pages/background/pick_request_task.h
deleted file mode 100644
index 56646f5..0000000
--- a/components/offline_pages/background/pick_request_task.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
-
-#include <set>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class DeviceConditions;
-class OfflinerPolicy;
-class PickRequestTask;
-class RequestCoordinatorEventLogger;
-class RequestNotifier;
-class RequestQueueStore;
-
-typedef bool (PickRequestTask::*RequestCompareFunction)(
-    const SavePageRequest* left,
-    const SavePageRequest* right);
-
-class PickRequestTask : public Task {
- public:
-  // Callback to report when a request was available.
-  typedef base::Callback<void(const SavePageRequest& request)>
-      RequestPickedCallback;
-
-  // Callback to report when no request was available.
-  typedef base::Callback<void(bool)> RequestNotPickedCallback;
-
-  // Callback to report available total and available queued request counts.
-  typedef base::Callback<void(size_t, size_t)> RequestCountCallback;
-
-  PickRequestTask(RequestQueueStore* store,
-                  OfflinerPolicy* policy,
-                  RequestNotifier* notifier,
-                  RequestCoordinatorEventLogger* event_logger,
-                  RequestPickedCallback picked_callback,
-                  RequestNotPickedCallback not_picked_callback,
-                  RequestCountCallback request_count_callback,
-                  DeviceConditions& device_conditions,
-                  const std::set<int64_t>& disabled_requests);
-
-  ~PickRequestTask() override;
-
-  // TaskQueue::Task implementation, starts the async chain
-  void Run() override;
-
- private:
-  // Step 1, handle getting the results from the store.
-  void ChooseAndPrune(bool request,
-                      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  // Step 2a. Handle choosing an entry, and calling the right picked callback
-  // and the request count callback.
-  void ChooseRequestAndCallback(
-      std::vector<std::unique_ptr<SavePageRequest>> valid_requests);
-
-  // Step 2b. Handle deleting stale entries and notifying observers.
-  void RemoveStaleRequests(std::vector<int64_t> stale_request_ids);
-
-  // Step 3. Send delete notifications for the expired requests.
-  void OnRequestsExpired(std::unique_ptr<UpdateRequestsResult> result);
-
-  // Helper functions.
-
-  // Split requests into valid and expired categories.
-  void SplitRequests(
-      std::vector<std::unique_ptr<SavePageRequest>> requests,
-      std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
-      std::vector<int64_t>* expired_request_ids);
-
-  // Determine if this request has device conditions appropriate for running it.
-  bool RequestConditionsSatisfied(const SavePageRequest* request);
-
-  // Determine if the new request is preferred under current policies.
-  bool IsNewRequestBetter(const SavePageRequest* oldRequest,
-                          const SavePageRequest* newRequest,
-                          RequestCompareFunction comparator);
-
-  // Returns true if the left hand side is better.
-  bool RetryCountFirstCompareFunction(const SavePageRequest* left,
-                                      const SavePageRequest* right);
-
-  // Returns true if the left hand side is better.
-  bool RecencyFirstCompareFunction(const SavePageRequest* left,
-                                   const SavePageRequest* right);
-
-  // Compare left and right side, returning 1 if the left side is better
-  // (preferred by policy), 0 if the same, and -1 if the right side is better.
-  int CompareRetryCount(const SavePageRequest* left,
-                        const SavePageRequest* right);
-
-  // Compare left and right side, returning 1 if the left side is better
-  // (preferred by policy), 0 if the same, and -1 if the right side is better.
-  int CompareCreationTime(const SavePageRequest* left,
-                          const SavePageRequest* right);
-
-  // Member variables, all pointers are not owned here.
-  RequestQueueStore* store_;
-  OfflinerPolicy* policy_;
-  RequestNotifier* notifier_;
-  RequestCoordinatorEventLogger* event_logger_;
-  RequestPickedCallback picked_callback_;
-  RequestNotPickedCallback not_picked_callback_;
-  RequestCountCallback request_count_callback_;
-  std::unique_ptr<DeviceConditions> device_conditions_;
-  const std::set<int64_t>& disabled_requests_;
-  // Allows us to pass a weak pointer to callbacks.
-  base::WeakPtrFactory<PickRequestTask> weak_ptr_factory_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_H_
diff --git a/components/offline_pages/background/pick_request_task_factory.cc b/components/offline_pages/background/pick_request_task_factory.cc
deleted file mode 100644
index 8d2a00e..0000000
--- a/components/offline_pages/background/pick_request_task_factory.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/pick_request_task_factory.h"
-
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-
-namespace offline_pages {
-
-// Capture the common parameters that we will need to make a pick task,
-// and use them when making tasks.  Create this once each session, and
-// use it to build a picker task when needed.
-PickRequestTaskFactory::PickRequestTaskFactory(
-    OfflinerPolicy* policy,
-    RequestNotifier* notifier,
-    RequestCoordinatorEventLogger* event_logger)
-    : policy_(policy), notifier_(notifier), event_logger_(event_logger) {}
-
-PickRequestTaskFactory::~PickRequestTaskFactory() {}
-
-std::unique_ptr<PickRequestTask> PickRequestTaskFactory::CreatePickerTask(
-    RequestQueueStore* store,
-    const PickRequestTask::RequestPickedCallback& picked_callback,
-    const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
-    const PickRequestTask::RequestCountCallback& request_count_callback,
-    DeviceConditions& conditions,
-    std::set<int64_t>& disabled_requests) {
-  std::unique_ptr<PickRequestTask> task(new PickRequestTask(
-      store, policy_, notifier_, event_logger_, picked_callback,
-      not_picked_callback, request_count_callback, conditions,
-      disabled_requests));
-  return task;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/pick_request_task_factory.h b/components/offline_pages/background/pick_request_task_factory.h
deleted file mode 100644
index 9b125ac..0000000
--- a/components/offline_pages/background/pick_request_task_factory.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
-
-#include <stdint.h>
-
-#include <set>
-
-#include "components/offline_pages/background/pick_request_task.h"
-
-namespace offline_pages {
-
-class OfflinerPolicy;
-class RequestCoordinatorEventLogger;
-class RequestNotifier;
-class RequestQueueStore;
-
-class PickRequestTaskFactory {
- public:
-  PickRequestTaskFactory(OfflinerPolicy* policy,
-                         RequestNotifier* notifier,
-                         RequestCoordinatorEventLogger* event_logger);
-
-  ~PickRequestTaskFactory();
-
-  std::unique_ptr<PickRequestTask> CreatePickerTask(
-      RequestQueueStore* store,
-      const PickRequestTask::RequestPickedCallback& picked_callback,
-      const PickRequestTask::RequestNotPickedCallback& not_picked_callback,
-      const PickRequestTask::RequestCountCallback& request_count_callback,
-      DeviceConditions& conditions,
-      std::set<int64_t>& disabled_requests);
-
- private:
-  // Unowned pointer to the Policy
-  OfflinerPolicy* policy_;
-  // Unowned pointer to the notifier
-  RequestNotifier* notifier_;
-  // Unowned pointer to the EventLogger
-  RequestCoordinatorEventLogger* event_logger_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_PICK_REQUEST_TASK_FACTORY_H_
diff --git a/components/offline_pages/background/pick_request_task_unittest.cc b/components/offline_pages/background/pick_request_task_unittest.cc
deleted file mode 100644
index 0db8fc9..0000000
--- a/components/offline_pages/background/pick_request_task_unittest.cc
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/pick_request_task.h"
-
-#include <memory>
-#include <set>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-// Data for request 1.
-const int64_t kRequestId1 = 17;
-const GURL kUrl1("https://google.com");
-const ClientId kClientId1("bookmark", "1234");
-// Data for request 2.
-const int64_t kRequestId2 = 42;
-const GURL kUrl2("http://nytimes.com");
-const ClientId kClientId2("bookmark", "5678");
-const bool kUserRequested = true;
-const int kAttemptCount = 1;
-const int kMaxStartedTries = 5;
-const int kMaxCompletedTries = 1;
-
-// Constants for policy values - These settings represent the default values.
-const bool kPreferUntried = false;
-const bool kPreferEarlier = true;
-const bool kPreferRetryCount = true;
-const int kBackgroundProcessingTimeBudgetSeconds = 170;
-
-// Default request
-const SavePageRequest kEmptyRequest(0UL,
-                                    GURL(""),
-                                    ClientId("", ""),
-                                    base::Time(),
-                                    true);
-}  // namespace
-
-// Helper class needed by the PickRequestTask
-class RequestNotifierStub : public RequestNotifier {
- public:
-  RequestNotifierStub()
-      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
-
-  void NotifyAdded(const SavePageRequest& request) override {}
-  void NotifyChanged(const SavePageRequest& request) override {}
-
-  void NotifyCompleted(const SavePageRequest& request,
-                       BackgroundSavePageResult status) override {
-    last_expired_request_ = request;
-    last_request_expiration_status_ = status;
-    total_expired_requests_++;
-  }
-
-  const SavePageRequest& last_expired_request() {
-    return last_expired_request_;
-  }
-
-  RequestCoordinator::BackgroundSavePageResult
-  last_request_expiration_status() {
-    return last_request_expiration_status_;
-  }
-
-  int32_t total_expired_requests() { return total_expired_requests_; }
-
- private:
-  BackgroundSavePageResult last_request_expiration_status_;
-  SavePageRequest last_expired_request_;
-  int32_t total_expired_requests_;
-};
-
-class PickRequestTaskTest : public testing::Test {
- public:
-  PickRequestTaskTest();
-
-  ~PickRequestTaskTest() override;
-
-  void SetUp() override;
-
-  void PumpLoop();
-
-  void AddRequestDone(ItemActionStatus status);
-
-  void RequestPicked(const SavePageRequest& request);
-
-  void RequestNotPicked(const bool non_user_requested_tasks_remaining);
-
-  void RequestCountCallback(size_t total_count, size_t available_count);
-
-  void QueueRequests(const SavePageRequest& request1,
-                     const SavePageRequest& request2);
-
-  // Reset the factory and the task using the current policy.
-  void MakeFactoryAndTask();
-
-  RequestNotifierStub* GetNotifier() { return notifier_.get(); }
-
-  PickRequestTask* task() { return task_.get(); }
-
-  void TaskCompletionCallback(Task* completed_task);
-
- protected:
-  void InitializeStoreDone(bool success);
-
-  std::unique_ptr<RequestQueueStore> store_;
-  std::unique_ptr<RequestNotifierStub> notifier_;
-  std::unique_ptr<SavePageRequest> last_picked_;
-  std::unique_ptr<OfflinerPolicy> policy_;
-  RequestCoordinatorEventLogger event_logger_;
-  std::set<int64_t> disabled_requests_;
-  std::unique_ptr<PickRequestTaskFactory> factory_;
-  std::unique_ptr<PickRequestTask> task_;
-  bool request_queue_not_picked_called_;
-  size_t total_request_count_;
-  size_t available_request_count_;
-  bool task_complete_called_;
-
- private:
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-PickRequestTaskTest::PickRequestTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-PickRequestTaskTest::~PickRequestTaskTest() {}
-
-void PickRequestTaskTest::SetUp() {
-  DeviceConditions conditions;
-  store_.reset(new RequestQueueInMemoryStore());
-  policy_.reset(new OfflinerPolicy());
-  notifier_.reset(new RequestNotifierStub());
-  MakeFactoryAndTask();
-  request_queue_not_picked_called_ = false;
-  total_request_count_ = 9999;
-  available_request_count_ = 9999;
-  task_complete_called_ = false;
-  last_picked_.reset();
-
-  store_->Initialize(base::Bind(&PickRequestTaskTest::InitializeStoreDone,
-                                base::Unretained(this)));
-  PumpLoop();
-}
-
-void PickRequestTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void PickRequestTaskTest::TaskCompletionCallback(Task* completed_task) {
-  task_complete_called_ = true;
-}
-
-void PickRequestTaskTest::AddRequestDone(ItemActionStatus status) {}
-
-void PickRequestTaskTest::RequestPicked(const SavePageRequest& request) {
-  last_picked_.reset(new SavePageRequest(request));
-}
-
-void PickRequestTaskTest::RequestNotPicked(
-    const bool non_user_requested_tasks_remaining) {
-  request_queue_not_picked_called_ = true;
-}
-
-void PickRequestTaskTest::RequestCountCallback(size_t total_count,
-                                               size_t available_count) {
-  total_request_count_ = total_count;
-  available_request_count_ = available_count;
-}
-
-// Test helper to queue the two given requests.
-void PickRequestTaskTest::QueueRequests(const SavePageRequest& request1,
-                                        const SavePageRequest& request2) {
-  DeviceConditions conditions;
-  std::set<int64_t> disabled_requests;
-  // Add test requests on the Queue.
-  store_->AddRequest(request1, base::Bind(&PickRequestTaskTest::AddRequestDone,
-                                          base::Unretained(this)));
-  store_->AddRequest(request2, base::Bind(&PickRequestTaskTest::AddRequestDone,
-                                          base::Unretained(this)));
-
-  // Pump the loop to give the async queue the opportunity to do the adds.
-  PumpLoop();
-}
-
-void PickRequestTaskTest::MakeFactoryAndTask() {
-  factory_.reset(new PickRequestTaskFactory(policy_.get(), notifier_.get(),
-                                            &event_logger_));
-  DeviceConditions conditions;
-  task_ = factory_->CreatePickerTask(
-      store_.get(),
-      base::Bind(&PickRequestTaskTest::RequestPicked, base::Unretained(this)),
-      base::Bind(&PickRequestTaskTest::RequestNotPicked,
-                 base::Unretained(this)),
-      base::Bind(&PickRequestTaskTest::RequestCountCallback,
-                 base::Unretained(this)),
-      conditions, disabled_requests_);
-  task_->SetTaskCompletionCallbackForTesting(
-      task_runner_.get(),
-      base::Bind(&PickRequestTaskTest::TaskCompletionCallback,
-                 base::Unretained(this)));
-}
-
-void PickRequestTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
-  task()->Run();
-  PumpLoop();
-
-  // Pump the loop again to give the async queue the opportunity to return
-  // results from the Get operation, and for the picker to call the "QueueEmpty"
-  // callback.
-  PumpLoop();
-
-  EXPECT_TRUE(request_queue_not_picked_called_);
-  EXPECT_EQ((size_t) 0, total_request_count_);
-  EXPECT_EQ((size_t) 0, available_request_count_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestWithHigherRetryCount) {
-  // Set up policy to prefer higher retry count.
-  policy_.reset(new OfflinerPolicy(
-      kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  MakeFactoryAndTask();
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId2, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_EQ((size_t) 2, total_request_count_);
-  EXPECT_EQ((size_t) 2, available_request_count_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestWithSameRetryCountButEarlier) {
-  base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
-                           kUserRequested);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId1, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseEarlierRequest) {
-  // We need a custom policy object prefering recency to retry count.
-  policy_.reset(new OfflinerPolicy(
-      kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
-  MakeFactoryAndTask();
-
-  base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
-                           kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId1, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseSameTimeRequestWithHigherRetryCount) {
-  // We need a custom policy object preferring recency to retry count.
-  policy_.reset(new OfflinerPolicy(
-      kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  MakeFactoryAndTask();
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId2, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestWithLowerRetryCount) {
-  // We need a custom policy object preferring lower retry count.
-  policy_.reset(new OfflinerPolicy(
-      !kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-  MakeFactoryAndTask();
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId1, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseLaterRequest) {
-  // We need a custom policy preferring recency over retry, and later requests.
-  policy_.reset(new OfflinerPolicy(
-      kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
-  MakeFactoryAndTask();
-
-  base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
-                           kUserRequested);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId2, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseNonExpiredRequest) {
-  base::Time creation_time = base::Time::Now();
-  base::Time expired_time =
-      creation_time - base::TimeDelta::FromSeconds(
-                          policy_->GetRequestExpirationTimeInSeconds() + 60);
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time,
-                           kUserRequested);
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId1, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_EQ(kRequestId2, GetNotifier()->last_expired_request().request_id());
-  EXPECT_EQ(RequestNotifier::BackgroundSavePageResult::EXPIRED,
-            GetNotifier()->last_request_expiration_status());
-  EXPECT_EQ(1, GetNotifier()->total_expired_requests());
-  EXPECT_EQ((size_t) 1, total_request_count_);
-  EXPECT_EQ((size_t) 1, available_request_count_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
-  base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(1);
-  base::Time creation_time2 = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
-                           kUserRequested);
-
-  // With default policy settings, we should choose the earlier request.
-  // However, we will make the earlier reqeust exceed the limit.
-  request1.set_started_attempt_count(policy_->GetMaxStartedTries());
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId2, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  // TODO(dougarnett): Counts should be 1 here once requests exceeding start
-  // count get cleaned up from the queue.
-  EXPECT_EQ((size_t) 2, total_request_count_);
-  EXPECT_EQ((size_t) 2, available_request_count_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
-  base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(1);
-  base::Time creation_time2 = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
-                           kUserRequested);
-
-  // With default policy settings, we should choose the earlier request.
-  // However, we will make the earlier reqeust exceed the limit.
-  request1.set_completed_attempt_count(policy_->GetMaxCompletedTries());
-
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId2, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
-  policy_.reset(new OfflinerPolicy(
-      kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
-      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
-
-  // put request 2 on disabled list, ensure request1 picked instead,
-  // even though policy would prefer 2.
-  disabled_requests_.insert(kRequestId2);
-  MakeFactoryAndTask();
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
-                           kUserRequested);
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-
-  // Add test requests on the Queue.
-  QueueRequests(request1, request2);
-
-  task()->Run();
-  PumpLoop();
-
-  // Pump the loop again to give the async queue the opportunity to return
-  // results from the Get operation, and for the picker to call the "picked"
-  // callback.
-  PumpLoop();
-
-  EXPECT_EQ(kRequestId1, last_picked_->request_id());
-  EXPECT_FALSE(request_queue_not_picked_called_);
-  EXPECT_EQ((size_t) 2, total_request_count_);
-  EXPECT_EQ((size_t) 1, available_request_count_);
-  EXPECT_TRUE(task_complete_called_);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/remove_requests_task.cc b/components/offline_pages/background/remove_requests_task.cc
deleted file mode 100644
index 15d45fe..0000000
--- a/components/offline_pages/background/remove_requests_task.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/remove_requests_task.h"
-
-#include "base/bind.h"
-
-namespace offline_pages {
-
-RemoveRequestsTask::RemoveRequestsTask(
-    RequestQueueStore* store,
-    const std::vector<int64_t>& request_ids,
-    const RequestQueueStore::UpdateCallback& callback)
-    : store_(store),
-      request_ids_(request_ids),
-      callback_(callback),
-      weak_ptr_factory_(this) {}
-
-RemoveRequestsTask::~RemoveRequestsTask() {}
-
-void RemoveRequestsTask::Run() {
-  RemoveRequests();
-}
-
-void RemoveRequestsTask::RemoveRequests() {
-  if (request_ids_.empty()) {
-    CompleteEarly(ItemActionStatus::NOT_FOUND);
-    return;
-  }
-
-  store_->RemoveRequests(request_ids_,
-                         base::Bind(&RemoveRequestsTask::CompleteWithResult,
-                                    weak_ptr_factory_.GetWeakPtr()));
-}
-
-void RemoveRequestsTask::CompleteEarly(ItemActionStatus status) {
-  // TODO(fgorski): store_->state() once implemented
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(StoreState::LOADED));
-  for (int64_t request_id : request_ids_)
-    result->item_statuses.push_back(std::make_pair(request_id, status));
-  CompleteWithResult(std::move(result));
-}
-
-void RemoveRequestsTask::CompleteWithResult(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  callback_.Run(std::move(result));
-  TaskComplete();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/remove_requests_task.h b/components/offline_pages/background/remove_requests_task.h
deleted file mode 100644
index a110134e..0000000
--- a/components/offline_pages/background/remove_requests_task.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-class RemoveRequestsTask : public Task {
- public:
-  RemoveRequestsTask(RequestQueueStore* store,
-                     const std::vector<int64_t>& request_ids,
-                     const RequestQueueStore::UpdateCallback& callback);
-  ~RemoveRequestsTask() override;
-
-  // TaskQueue::Task implementation.
-  void Run() override;
-
- private:
-  // Step 1. Removes requests from the store.
-  void RemoveRequests();
-  // Step for early termination, that builds failure result.
-  void CompleteEarly(ItemActionStatus status);
-  // Step 2. Processes update result, calls callback.
-  void CompleteWithResult(std::unique_ptr<UpdateRequestsResult> result);
-
-  // Store that this task updates.
-  RequestQueueStore* store_;
-  // Request IDs to be updated.
-  // TODO(fgorski): perhaps convert to unique_ptr to a vector.
-  std::vector<int64_t> request_ids_;
-  // Callback to complete the task.
-  RequestQueueStore::UpdateCallback callback_;
-
-  base::WeakPtrFactory<RemoveRequestsTask> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemoveRequestsTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REMOVE_REQUESTS_TASK_H_
diff --git a/components/offline_pages/background/remove_requests_task_unittest.cc b/components/offline_pages/background/remove_requests_task_unittest.cc
deleted file mode 100644
index 2bec4df..0000000
--- a/components/offline_pages/background/remove_requests_task_unittest.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/remove_requests_task.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-namespace {
-const int64_t kRequestId1 = 42;
-const int64_t kRequestId2 = 43;
-const int64_t kRequestId3 = 44;
-const GURL kUrl1("http://example.com");
-const GURL kUrl2("http://another-example.com");
-const ClientId kClientId1("bookmark", "1234");
-const ClientId kClientId2("async", "5678");
-}  // namespace
-
-class RemoveRequestsTaskTest : public testing::Test {
- public:
-  RemoveRequestsTaskTest();
-  ~RemoveRequestsTaskTest() override;
-
-  void PumpLoop();
-
-  void InitializeStore(RequestQueueStore* store);
-  void AddRequestsToStore(RequestQueueStore* store);
-  void RemoveRequestsCallback(std::unique_ptr<UpdateRequestsResult> result);
-
-  UpdateRequestsResult* last_result() const { return result_.get(); }
-
- private:
-  void InitializeStoreDone(bool succesS);
-  void AddRequestDone(ItemActionStatus status);
-
-  std::unique_ptr<UpdateRequestsResult> result_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-RemoveRequestsTaskTest::RemoveRequestsTaskTest()
-    : task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-RemoveRequestsTaskTest::~RemoveRequestsTaskTest() {}
-
-void RemoveRequestsTaskTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void RemoveRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&RemoveRequestsTaskTest::InitializeStoreDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void RemoveRequestsTaskTest::AddRequestsToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
-                            true);
-  store->AddRequest(request_1,
-                    base::Bind(&RemoveRequestsTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
-                            true);
-  store->AddRequest(request_2,
-                    base::Bind(&RemoveRequestsTaskTest::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-}
-
-void RemoveRequestsTaskTest::RemoveRequestsCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  result_ = std::move(result);
-}
-
-void RemoveRequestsTaskTest::InitializeStoreDone(bool success) {
-  ASSERT_TRUE(success);
-}
-
-void RemoveRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
-  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
-}
-
-TEST_F(RemoveRequestsTaskTest, RemoveWhenStoreEmpty) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1};
-  RemoveRequestsTask task(
-      &store, request_ids,
-      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(RemoveRequestsTaskTest, RemoveSingleItem) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddRequestsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1};
-  RemoveRequestsTask task(
-      &store, request_ids,
-      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(1UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
-}
-
-TEST_F(RemoveRequestsTaskTest, RemoveMultipleItems) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddRequestsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
-  RemoveRequestsTask task(
-      &store, request_ids,
-      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(2UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(1).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(1).second);
-  EXPECT_EQ(2UL, last_result()->updated_items.size());
-  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
-  EXPECT_EQ(kRequestId2, last_result()->updated_items.at(1).request_id());
-}
-
-TEST_F(RemoveRequestsTaskTest, DeleteWithEmptyIdList) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-
-  std::vector<int64_t> request_ids;
-  RemoveRequestsTask task(
-      &store, request_ids,
-      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(0UL, last_result()->item_statuses.size());
-  EXPECT_EQ(0UL, last_result()->updated_items.size());
-}
-
-TEST_F(RemoveRequestsTaskTest, RemoveMissingItem) {
-  RequestQueueInMemoryStore store;
-  InitializeStore(&store);
-  AddRequestsToStore(&store);
-
-  std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
-  RemoveRequestsTask task(
-      &store, request_ids,
-      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
-                 base::Unretained(this)));
-  task.Run();
-  PumpLoop();
-  ASSERT_TRUE(last_result());
-  EXPECT_EQ(2UL, last_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_result()->item_statuses.at(0).second);
-  EXPECT_EQ(kRequestId3, last_result()->item_statuses.at(1).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            last_result()->item_statuses.at(1).second);
-  EXPECT_EQ(1UL, last_result()->updated_items.size());
-  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator.cc b/components/offline_pages/background/request_coordinator.cc
deleted file mode 100644
index beb881b..0000000
--- a/components/offline_pages/background/request_coordinator.cc
+++ /dev/null
@@ -1,992 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_coordinator.h"
-
-#include <limits>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/sys_info.h"
-#include "base/time/time.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
-
-namespace offline_pages {
-
-namespace {
-const bool kUserRequest = true;
-const bool kStartOfProcessing = true;
-const int kMinDurationSeconds = 1;
-const int kMaxDurationSeconds = 7 * 24 * 60 * 60;  // 7 days
-const int kDurationBuckets = 50;
-const int kDisabledTaskRecheckSeconds = 5;
-
-// TODO(dougarnett): Move to util location and share with model impl.
-std::string AddHistogramSuffix(const ClientId& client_id,
-                               const char* histogram_name) {
-  if (client_id.name_space.empty()) {
-    NOTREACHED();
-    return histogram_name;
-  }
-  std::string adjusted_histogram_name(histogram_name);
-  adjusted_histogram_name += "." + client_id.name_space;
-  return adjusted_histogram_name;
-}
-
-// Records the final request status UMA for an offlining request. This should
-// only be called once per Offliner::LoadAndSave request.
-void RecordOfflinerResultUMA(const ClientId& client_id,
-                             const base::Time& request_creation_time,
-                             Offliner::RequestStatus request_status) {
-  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
-      AddHistogramSuffix(client_id,
-                         "OfflinePages.Background.OfflinerRequestStatus"),
-      1, static_cast<int>(Offliner::RequestStatus::STATUS_COUNT),
-      static_cast<int>(Offliner::RequestStatus::STATUS_COUNT) + 1,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(static_cast<int>(request_status));
-
-  // For successful requests also record time from request to save.
-  if (request_status == Offliner::RequestStatus::SAVED) {
-    // Using regular histogram (with dynamic suffix) rather than time-oriented
-    // one to record samples in seconds rather than milliseconds.
-    base::HistogramBase* histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"),
-        kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
-        base::HistogramBase::kUmaTargetedHistogramFlag);
-    base::TimeDelta duration = base::Time::Now() - request_creation_time;
-    histogram->Add(duration.InSeconds());
-  }
-}
-
-void RecordStartTimeUMA(const SavePageRequest& request) {
-  std::string histogram_name("OfflinePages.Background.TimeToStart");
-  if (base::SysInfo::IsLowEndDevice()) {
-    histogram_name += ".Svelte";
-  }
-
-  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_TIMES
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
-      AddHistogramSuffix(request.client_id(), histogram_name.c_str()),
-      base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  base::TimeDelta duration = base::Time::Now() - request.creation_time();
-  histogram->AddTime(duration);
-}
-
-void RecordCancelTimeUMA(const SavePageRequest& canceled_request) {
-  // Using regular histogram (with dynamic suffix) rather than time-oriented
-  // one to record samples in seconds rather than milliseconds.
-  base::HistogramBase* histogram = base::Histogram::FactoryGet(
-      AddHistogramSuffix(canceled_request.client_id(),
-                         "OfflinePages.Background.TimeToCanceled"),
-      kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  base::TimeDelta duration =
-      base::Time::Now() - canceled_request.creation_time();
-  histogram->Add(duration.InSeconds());
-}
-
-// Records the number of started attempts for completed requests (whether
-// successful or not).
-void RecordAttemptCount(const SavePageRequest& request,
-                        RequestNotifier::BackgroundSavePageResult status) {
-  if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) {
-    // TODO(dougarnett): Also record UMA for completed attempts here.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "OfflinePages.Background.RequestSuccess.StartedAttemptCount",
-        request.started_attempt_count(), 1, 10, 11);
-  } else {
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "OfflinePages.Background.RequestFailure.StartedAttemptCount",
-        request.started_attempt_count(), 1, 10, 11);
-  }
-}
-
-// Record the network quality at request creation time per namespace.
-void RecordSavePageLaterNetworkQuality(
-    const ClientId& client_id,
-    const net::EffectiveConnectionType effective_connection) {
-  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
-      AddHistogramSuffix(
-          client_id,
-          "OfflinePages.Background.EffectiveConnectionType.SavePageLater"),
-      1, net::EFFECTIVE_CONNECTION_TYPE_LAST - 1,
-      net::EFFECTIVE_CONNECTION_TYPE_LAST,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(effective_connection);
-}
-
-// This should use the same algorithm as we use for OfflinePageItem, so the IDs
-// are similar.
-int64_t GenerateOfflineId() {
-  return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
-}
-
-// In case we start processing from SavePageLater, we need a callback, but there
-// is nothing for it to do.
-void EmptySchedulerCallback(bool started) {}
-
-// Returns whether |result| is a successful result for a single request.
-bool IsSingleSuccessResult(const UpdateRequestsResult* result) {
-  return result->store_state == StoreState::LOADED &&
-         result->item_statuses.size() == 1 &&
-         result->item_statuses.at(0).second == ItemActionStatus::SUCCESS;
-}
-
-}  // namespace
-
-RequestCoordinator::RequestCoordinator(
-    std::unique_ptr<OfflinerPolicy> policy,
-    std::unique_ptr<OfflinerFactory> factory,
-    std::unique_ptr<RequestQueue> queue,
-    std::unique_ptr<Scheduler> scheduler,
-    net::NetworkQualityEstimator::NetworkQualityProvider*
-        network_quality_estimator)
-    : is_low_end_device_(base::SysInfo::IsLowEndDevice()),
-      is_busy_(false),
-      is_starting_(false),
-      processing_state_(ProcessingWindowState::STOPPED),
-      use_test_connection_type_(false),
-      test_connection_type_(),
-      offliner_(nullptr),
-      policy_(std::move(policy)),
-      factory_(std::move(factory)),
-      queue_(std::move(queue)),
-      scheduler_(std::move(scheduler)),
-      policy_controller_(new ClientPolicyController()),
-      network_quality_estimator_(network_quality_estimator),
-      active_request_(nullptr),
-      last_offlining_status_(Offliner::RequestStatus::UNKNOWN),
-      scheduler_callback_(base::Bind(&EmptySchedulerCallback)),
-      immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
-      weak_ptr_factory_(this) {
-  DCHECK(policy_ != nullptr);
-  std::unique_ptr<PickRequestTaskFactory> picker_factory(
-      new PickRequestTaskFactory(policy_.get(), this, &event_logger_));
-  queue_->SetPickerFactory(std::move(picker_factory));
-}
-
-RequestCoordinator::~RequestCoordinator() {}
-
-int64_t RequestCoordinator::SavePageLater(const GURL& url,
-                                          const ClientId& client_id,
-                                          bool user_requested,
-                                          RequestAvailability availability) {
-  DVLOG(2) << "URL is " << url << " " << __func__;
-
-  if (!OfflinePageModel::CanSaveURL(url)) {
-    DVLOG(1) << "Not able to save page for requested url: " << url;
-    return 0L;
-  }
-
-  int64_t id = GenerateOfflineId();
-
-  // Build a SavePageRequest.
-  offline_pages::SavePageRequest request(id, url, client_id, base::Time::Now(),
-                                         user_requested);
-
-  // If the download manager is not done with the request, put it on the
-  // disabled list.
-  if (availability == RequestAvailability::DISABLED_FOR_OFFLINER)
-    disabled_requests_.insert(id);
-
-  // Put the request on the request queue.
-  queue_->AddRequest(request,
-                     base::Bind(&RequestCoordinator::AddRequestResultCallback,
-                                weak_ptr_factory_.GetWeakPtr(), availability));
-
-  // Record the network quality when this request is made.
-  if (network_quality_estimator_) {
-    RecordSavePageLaterNetworkQuality(
-        client_id, network_quality_estimator_->GetEffectiveConnectionType());
-  }
-
-  return id;
-}
-void RequestCoordinator::GetAllRequests(const GetRequestsCallback& callback) {
-  // Get all matching requests from the request queue, send them to our
-  // callback.  We bind the namespace and callback to the front of the callback
-  // param set.
-  queue_->GetRequests(base::Bind(&RequestCoordinator::GetQueuedRequestsCallback,
-                                 weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void RequestCoordinator::GetQueuedRequestsCallback(
-    const GetRequestsCallback& callback,
-    GetRequestsResult result,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  callback.Run(std::move(requests));
-}
-
-void RequestCoordinator::StopPrerendering(Offliner::RequestStatus stop_status) {
-  if (offliner_ && is_busy_) {
-    DCHECK(active_request_.get());
-    offliner_->Cancel();
-
-    if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) {
-      // Consider watchdog timeout a completed attempt.
-      SavePageRequest request(*active_request_.get());
-      UpdateRequestForCompletedAttempt(request,
-                                       Offliner::REQUEST_COORDINATOR_TIMED_OUT);
-    } else {
-      // Otherwise consider this stop an aborted attempt.
-      UpdateRequestForAbortedAttempt(*active_request_.get());
-    }
-  }
-
-  // Stopping offliner means it will not call callback so set last status.
-  last_offlining_status_ = stop_status;
-
-  if (active_request_) {
-    event_logger_.RecordOfflinerResult(active_request_->client_id().name_space,
-                                       last_offlining_status_,
-                                       active_request_->request_id());
-    RecordOfflinerResultUMA(active_request_->client_id(),
-                            active_request_->creation_time(),
-                            last_offlining_status_);
-    is_busy_ = false;
-    active_request_.reset();
-  }
-}
-
-void RequestCoordinator::GetRequestsForSchedulingCallback(
-    GetRequestsResult result,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  bool user_requested = false;
-
-  // Examine all requests, if we find a user requested one, we will use the less
-  // restrictive conditions for user_requested requests.  Otherwise we will use
-  // the more restrictive non-user-requested conditions.
-  for (const auto& request : requests) {
-    if (request->user_requested()) {
-      user_requested = true;
-      break;
-    }
-  }
-
-  // In the get callback, determine the least restrictive, and call
-  // GetTriggerConditions based on that.
-  scheduler_->Schedule(GetTriggerConditions(user_requested));
-}
-
-bool RequestCoordinator::CancelActiveRequestIfItMatches(
-    const std::vector<int64_t>& request_ids) {
-  // If we have a request in progress and need to cancel it, call the
-  // pre-renderer to cancel.  TODO Make sure we remove any page created by the
-  // prerenderer if it doesn't get the cancel in time.
-  if (active_request_ != nullptr) {
-    if (request_ids.end() != std::find(request_ids.begin(), request_ids.end(),
-                                       active_request_->request_id())) {
-      StopPrerendering(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED);
-      active_request_.reset(nullptr);
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void RequestCoordinator::UpdateRequestForAbortedAttempt(
-    const SavePageRequest& request) {
-  if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) {
-    const RequestNotifier::BackgroundSavePageResult result(
-        RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED);
-    event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
-                                               result, request.request_id());
-    RemoveAttemptedRequest(request, result);
-  } else {
-    MarkAttemptAborted(request.request_id(), request.client_id().name_space);
-  }
-}
-
-void RequestCoordinator::RemoveAttemptedRequest(
-    const SavePageRequest& request,
-    RequestNotifier::BackgroundSavePageResult result) {
-  std::vector<int64_t> remove_requests;
-  remove_requests.push_back(request.request_id());
-  queue_->RemoveRequests(remove_requests,
-                         base::Bind(&RequestCoordinator::HandleRemovedRequests,
-                                    weak_ptr_factory_.GetWeakPtr(), result));
-  RecordAttemptCount(request, result);
-}
-
-void RequestCoordinator::MarkAttemptAborted(int64_t request_id,
-                                            const std::string& name_space) {
-  queue_->MarkAttemptAborted(
-      request_id,
-      base::Bind(&RequestCoordinator::MarkAttemptDone,
-                 weak_ptr_factory_.GetWeakPtr(), request_id, name_space));
-}
-
-void RequestCoordinator::MarkAttemptDone(
-    int64_t request_id,
-    const std::string& name_space,
-    std::unique_ptr<UpdateRequestsResult> result) {
-  // If the request succeeded, notify observer. If it failed, we can't really
-  // do much, so just log it.
-  if (IsSingleSuccessResult(result.get())) {
-    NotifyChanged(result->updated_items.at(0));
-  } else {
-    DVLOG(1) << "Failed to mark attempt: " << request_id;
-    UpdateRequestResult request_result =
-        result->store_state != StoreState::LOADED
-            ? UpdateRequestResult::STORE_FAILURE
-            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
-    event_logger_.RecordUpdateRequestFailed(name_space, request_result);
-  }
-}
-
-void RequestCoordinator::RemoveRequests(
-    const std::vector<int64_t>& request_ids,
-    const RemoveRequestsCallback& callback) {
-  bool canceled = CancelActiveRequestIfItMatches(request_ids);
-  queue_->RemoveRequests(
-      request_ids,
-      base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
-                 weak_ptr_factory_.GetWeakPtr(), callback,
-                 RequestNotifier::BackgroundSavePageResult::REMOVED));
-
-  // Record the network quality when this request is made.
-  if (network_quality_estimator_) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "OfflinePages.Background.EffectiveConnectionType.RemoveRequests",
-        network_quality_estimator_->GetEffectiveConnectionType(),
-        net::EFFECTIVE_CONNECTION_TYPE_LAST);
-  }
-
-  if (canceled)
-    TryNextRequest(!kStartOfProcessing);
-}
-
-void RequestCoordinator::PauseRequests(
-    const std::vector<int64_t>& request_ids) {
-  bool canceled = CancelActiveRequestIfItMatches(request_ids);
-  queue_->ChangeRequestsState(
-      request_ids, SavePageRequest::RequestState::PAUSED,
-      base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
-                 weak_ptr_factory_.GetWeakPtr()));
-
-  // Record the network quality when this request is made.
-  if (network_quality_estimator_) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "OfflinePages.Background.EffectiveConnectionType.PauseRequests",
-        network_quality_estimator_->GetEffectiveConnectionType(),
-        net::EFFECTIVE_CONNECTION_TYPE_LAST);
-  }
-
-  if (canceled)
-    TryNextRequest(!kStartOfProcessing);
-}
-
-void RequestCoordinator::ResumeRequests(
-    const std::vector<int64_t>& request_ids) {
-  queue_->ChangeRequestsState(
-      request_ids, SavePageRequest::RequestState::AVAILABLE,
-      base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
-                 weak_ptr_factory_.GetWeakPtr()));
-
-  // Record the network quality when this request is made.
-  if (network_quality_estimator_) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "OfflinePages.Background.EffectiveConnectionType.ResumeRequests",
-        network_quality_estimator_->GetEffectiveConnectionType(),
-        net::EFFECTIVE_CONNECTION_TYPE_LAST);
-  }
-
-  // Schedule a task, in case there is not one scheduled.
-  ScheduleAsNeeded();
-}
-
-net::NetworkChangeNotifier::ConnectionType
-RequestCoordinator::GetConnectionType() {
-  // If we have a connection type set for test, use that.
-  if (use_test_connection_type_)
-    return test_connection_type_;
-
-  return net::NetworkChangeNotifier::GetConnectionType();
-}
-
-void RequestCoordinator::AddRequestResultCallback(
-    RequestAvailability availability,
-    AddRequestResult result,
-    const SavePageRequest& request) {
-  NotifyAdded(request);
-  // Inform the scheduler that we have an outstanding task.
-  scheduler_->Schedule(GetTriggerConditions(kUserRequest));
-
-  if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) {
-    // Mark attempt started (presuming it is disabled for background offliner
-    // because foreground offlining is happening).
-    queue_->MarkAttemptStarted(
-        request.request_id(),
-        base::Bind(&RequestCoordinator::MarkAttemptDone,
-                   weak_ptr_factory_.GetWeakPtr(), request.request_id(),
-                   request.client_id().name_space));
-  } else if (request.user_requested()) {
-    StartImmediatelyIfConnected();
-  }
-}
-
-void RequestCoordinator::UpdateMultipleRequestsCallback(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  for (const auto& request : result->updated_items)
-    NotifyChanged(request);
-
-  bool available_user_request = false;
-  for (const auto& request : result->updated_items) {
-    if (!available_user_request && request.user_requested() &&
-        request.request_state() == SavePageRequest::RequestState::AVAILABLE) {
-      available_user_request = true;
-    }
-  }
-
-  if (available_user_request)
-    StartImmediatelyIfConnected();
-}
-
-void RequestCoordinator::HandleRemovedRequestsAndCallback(
-    const RemoveRequestsCallback& callback,
-    RequestNotifier::BackgroundSavePageResult status,
-    std::unique_ptr<UpdateRequestsResult> result) {
-  // TODO(dougarnett): Define status code for user/api cancel and use here
-  // to determine whether to record cancel time UMA.
-  for (const auto& request : result->updated_items)
-    RecordCancelTimeUMA(request);
-  callback.Run(result->item_statuses);
-  HandleRemovedRequests(status, std::move(result));
-}
-
-void RequestCoordinator::HandleRemovedRequests(
-    RequestNotifier::BackgroundSavePageResult status,
-    std::unique_ptr<UpdateRequestsResult> result) {
-  for (const auto& request : result->updated_items)
-    NotifyCompleted(request, status);
-}
-
-void RequestCoordinator::ScheduleAsNeeded() {
-  // Get all requests from queue (there is no filtering mechanism).
-  queue_->GetRequests(
-      base::Bind(&RequestCoordinator::GetRequestsForSchedulingCallback,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void RequestCoordinator::StopProcessing(
-    Offliner::RequestStatus stop_status) {
-  processing_state_ = ProcessingWindowState::STOPPED;
-  StopPrerendering(stop_status);
-
-  // Let the scheduler know we are done processing.
-  scheduler_callback_.Run(true);
-}
-
-void RequestCoordinator::HandleWatchdogTimeout() {
-  Offliner::RequestStatus watchdog_status =
-      Offliner::REQUEST_COORDINATOR_TIMED_OUT;
-  StopPrerendering(watchdog_status);
-  TryNextRequest(!kStartOfProcessing);
-}
-
-// Returns true if the caller should expect a callback, false otherwise. For
-// instance, this would return false if a request is already in progress.
-bool RequestCoordinator::StartProcessing(
-    const DeviceConditions& device_conditions,
-    const base::Callback<void(bool)>& callback) {
-  DVLOG(2) << "Scheduled " << __func__;
-  return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
-                                 device_conditions, callback);
-}
-
-bool RequestCoordinator::StartProcessingInternal(
-    const ProcessingWindowState processing_state,
-    const DeviceConditions& device_conditions,
-    const base::Callback<void(bool)>& callback) {
-  current_conditions_.reset(new DeviceConditions(device_conditions));
-  if (is_starting_ || is_busy_)
-    return false;
-  processing_state_ = processing_state;
-  scheduler_callback_ = callback;
-
-  // Mark the time at which we started processing so we can check our time
-  // budget.
-  operation_start_time_ = base::Time::Now();
-
-  TryNextRequest(kStartOfProcessing);
-
-  return true;
-}
-
-void RequestCoordinator::StartImmediatelyIfConnected() {
-  OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart();
-  UMA_HISTOGRAM_ENUMERATION(
-      "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
-      RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
-}
-
-RequestCoordinator::OfflinerImmediateStartStatus
-RequestCoordinator::TryImmediateStart() {
-  DVLOG(2) << "Immediate " << __func__;
-  // Make sure not already busy processing.
-  if (is_busy_)
-    return OfflinerImmediateStartStatus::BUSY;
-
-  // Make sure we are not on svelte device to start immediately.
-  if (is_low_end_device_ &&
-      !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) {
-    DVLOG(2) << "low end device, returning";
-    // Let the scheduler know we are done processing and failed due to svelte.
-    immediate_schedule_callback_.Run(false);
-    return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE;
-  }
-
-  if (GetConnectionType() ==
-      net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) {
-    RequestConnectedEventForStarting();
-    return OfflinerImmediateStartStatus::NO_CONNECTION;
-  } else {
-    // Clear any pending connected event request since we have connection
-    // and will start processing.
-    ClearConnectedEventRequest();
-  }
-
-  // Start processing with manufactured conservative battery conditions
-  // (i.e., assume no battery).
-  // TODO(dougarnett): Obtain actual battery conditions (from Android/Java).
-
-  DeviceConditions device_conditions(false, 0, GetConnectionType());
-  if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW,
-                              device_conditions, immediate_schedule_callback_))
-    return OfflinerImmediateStartStatus::STARTED;
-  else
-    return OfflinerImmediateStartStatus::NOT_ACCEPTED;
-}
-
-void RequestCoordinator::RequestConnectedEventForStarting() {
-  connection_notifier_.reset(new ConnectionNotifier(
-      base::Bind(&RequestCoordinator::HandleConnectedEventForStarting,
-                 weak_ptr_factory_.GetWeakPtr())));
-}
-
-void RequestCoordinator::ClearConnectedEventRequest() {
-  connection_notifier_.reset(nullptr);
-}
-
-void RequestCoordinator::HandleConnectedEventForStarting() {
-  ClearConnectedEventRequest();
-  StartImmediatelyIfConnected();
-}
-
-void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
-  is_starting_ = true;
-  base::TimeDelta processing_time_budget;
-  if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
-    processing_time_budget = base::TimeDelta::FromSeconds(
-        policy_->GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds());
-  } else {
-    DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
-    processing_time_budget = base::TimeDelta::FromSeconds(
-        policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds());
-  }
-
-  // Determine connection type. If just starting processing, the best source is
-  // from the current device conditions (they are fresh and motivated starting
-  // processing whereas NetworkChangeNotifier may lag reality).
-  net::NetworkChangeNotifier::ConnectionType connection_type;
-  if (is_start_of_processing)
-    connection_type = current_conditions_->GetNetConnectionType();
-  else
-    connection_type = GetConnectionType();
-
-  // If there is no network or no time left in the budget, return to the
-  // scheduler. We do not remove the pending scheduler task that was set
-  // up earlier in case we run out of time, so the background scheduler
-  // will return to us at the next opportunity to run background tasks.
-  if (connection_type ==
-          net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE ||
-      (base::Time::Now() - operation_start_time_) > processing_time_budget) {
-    is_starting_ = false;
-
-    // If we were doing immediate processing, try to start it again
-    // when we get connected.
-    if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW)
-      RequestConnectedEventForStarting();
-
-    // Let the scheduler know we are done processing.
-    // TODO: Make sure the scheduler callback is valid before running it.
-    scheduler_callback_.Run(true);
-    DVLOG(2) << " out of time, giving up. " << __func__;
-
-    return;
-  }
-
-  // Ask request queue to make a new PickRequestTask object, then put it on the
-  // task queue.
-  queue_->PickNextRequest(
-      base::Bind(&RequestCoordinator::RequestPicked,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&RequestCoordinator::RequestNotPicked,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&RequestCoordinator::RequestCounts,
-                 weak_ptr_factory_.GetWeakPtr(), is_start_of_processing),
-      *current_conditions_.get(), disabled_requests_);
-  // TODO(petewil): Verify current_conditions has a good value on all calling
-  // paths.  It is really more of a "last known conditions" than "current
-  // conditions".  Consider having a call to Java to check the current
-  // conditions.
-}
-
-// Called by the request picker when a request has been picked.
-void RequestCoordinator::RequestPicked(const SavePageRequest& request) {
-  DVLOG(2) << request.url() << " " << __func__;
-  is_starting_ = false;
-
-  // Make sure we were not stopped while picking.
-  if (processing_state_ != ProcessingWindowState::STOPPED) {
-    // Send the request on to the offliner.
-    SendRequestToOffliner(request);
-  }
-}
-
-void RequestCoordinator::RequestNotPicked(
-    bool non_user_requested_tasks_remaining) {
-  DVLOG(2) << __func__;
-  is_starting_ = false;
-
-  // Clear the outstanding "safety" task in the scheduler.
-  scheduler_->Unschedule();
-
-  // If disabled tasks remain, post a new safety task for 5 sec from now.
-  if (disabled_requests_.size() > 0) {
-    scheduler_->BackupSchedule(GetTriggerConditions(kUserRequest),
-                               kDisabledTaskRecheckSeconds);
-  } else if (non_user_requested_tasks_remaining) {
-    // If we don't have any of those, check for non-user-requested tasks.
-    scheduler_->Schedule(GetTriggerConditions(!kUserRequest));
-  }
-
-  // Let the scheduler know we are done processing.
-  scheduler_callback_.Run(true);
-}
-
-void RequestCoordinator::RequestCounts(bool is_start_of_processing,
-                                       size_t total_requests,
-                                       size_t available_requests) {
-  // Only capture request counts for the start of processing (not for
-  // continued processing in the same window).
-  if (!is_start_of_processing)
-    return;
-
-  if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
-    if (is_low_end_device_) {
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ScheduledStart.AvailableRequestCount."
-          "Svelte",
-          available_requests);
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ScheduledStart.UnavailableRequestCount."
-          "Svelte",
-          total_requests - available_requests);
-    } else {
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ScheduledStart.AvailableRequestCount",
-          available_requests);
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ScheduledStart.UnavailableRequestCount",
-          total_requests - available_requests);
-    }
-  } else if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW) {
-    if (is_low_end_device_) {
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ImmediateStart.AvailableRequestCount."
-          "Svelte",
-          available_requests);
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ImmediateStart.UnavailableRequestCount."
-          "Svelte",
-          total_requests - available_requests);
-    } else {
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ImmediateStart.AvailableRequestCount",
-          available_requests);
-      UMA_HISTOGRAM_COUNTS_1000(
-          "OfflinePages.Background.ImmediateStart.UnavailableRequestCount",
-          total_requests - available_requests);
-    }
-  }
-}
-
-void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) {
-  // Check that offlining didn't get cancelled while performing some async
-  // steps.
-  if (processing_state_ == ProcessingWindowState::STOPPED)
-    return;
-
-  GetOffliner();
-  if (!offliner_) {
-    DVLOG(0) << "Unable to create Offliner. "
-             << "Cannot background offline page.";
-    return;
-  }
-
-  DCHECK(!is_busy_);
-  is_busy_ = true;
-
-  // Record start time if this is first attempt.
-  if (request.started_attempt_count() == 0) {
-    RecordStartTimeUMA(request);
-  }
-
-  // Mark attempt started in the database and start offliner when completed.
-  queue_->MarkAttemptStarted(
-      request.request_id(),
-      base::Bind(&RequestCoordinator::StartOffliner,
-                 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
-                 request.client_id().name_space));
-}
-
-void RequestCoordinator::StartOffliner(
-    int64_t request_id,
-    const std::string& client_namespace,
-    std::unique_ptr<UpdateRequestsResult> update_result) {
-  if (update_result->store_state != StoreState::LOADED ||
-      update_result->item_statuses.size() != 1 ||
-      update_result->item_statuses.at(0).first != request_id ||
-      update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) {
-    is_busy_ = false;
-    // TODO(fgorski): what is the best result? Do we create a new status?
-    StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
-    DVLOG(1) << "Failed to mark attempt started: " << request_id;
-    UpdateRequestResult request_result =
-        update_result->store_state != StoreState::LOADED
-            ? UpdateRequestResult::STORE_FAILURE
-            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
-    event_logger_.RecordUpdateRequestFailed(client_namespace, request_result);
-    return;
-  }
-
-  // TODO(fgorski): Switch to request_id only, so that this value is not written
-  // back to the store.
-  active_request_.reset(
-      new SavePageRequest(update_result->updated_items.at(0)));
-
-  // Start the load and save process in the offliner (Async).
-  if (offliner_->LoadAndSave(
-          update_result->updated_items.at(0),
-          base::Bind(&RequestCoordinator::OfflinerDoneCallback,
-                     weak_ptr_factory_.GetWeakPtr()))) {
-    base::TimeDelta timeout;
-    if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
-      timeout = base::TimeDelta::FromSeconds(
-          policy_->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds());
-    } else {
-      DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
-      timeout = base::TimeDelta::FromSeconds(
-          policy_->GetSinglePageTimeLimitForImmediateLoadInSeconds());
-    }
-
-    // Inform observer of active request.
-    NotifyChanged(*active_request_.get());
-
-    // Start a watchdog timer to catch pre-renders running too long
-    watchdog_timer_.Start(FROM_HERE, timeout, this,
-                          &RequestCoordinator::HandleWatchdogTimeout);
-  } else {
-    is_busy_ = false;
-    DVLOG(0) << "Unable to start LoadAndSave";
-    StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
-
-    // We need to undo the MarkAttemptStarted that brought us to this
-    // method since we didn't success in starting after all.
-    MarkAttemptAborted(request_id, client_namespace);
-  }
-}
-
-void RequestCoordinator::OfflinerDoneCallback(const SavePageRequest& request,
-                                              Offliner::RequestStatus status) {
-  DVLOG(2) << "offliner finished, saved: "
-           << (status == Offliner::RequestStatus::SAVED)
-           << ", status: " << static_cast<int>(status) << ", " << __func__;
-  DCHECK_NE(status, Offliner::RequestStatus::UNKNOWN);
-  DCHECK_NE(status, Offliner::RequestStatus::LOADED);
-  event_logger_.RecordOfflinerResult(request.client_id().name_space, status,
-                                     request.request_id());
-  last_offlining_status_ = status;
-  RecordOfflinerResultUMA(request.client_id(), request.creation_time(),
-                          last_offlining_status_);
-  watchdog_timer_.Stop();
-  is_busy_ = false;
-  active_request_.reset(nullptr);
-
-  UpdateRequestForCompletedAttempt(request, status);
-  if (ShouldTryNextRequest(status))
-    TryNextRequest(!kStartOfProcessing);
-  else
-    scheduler_callback_.Run(true);
-}
-
-void RequestCoordinator::UpdateRequestForCompletedAttempt(
-    const SavePageRequest& request,
-    Offliner::RequestStatus status) {
-  if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
-      status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
-    // Update the request for the canceled attempt.
-    // TODO(dougarnett): See if we can conclusively identify other attempt
-    // aborted cases to treat this way (eg, for Render Process Killed).
-    UpdateRequestForAbortedAttempt(request);
-  } else if (status == Offliner::RequestStatus::SAVED) {
-    // Remove the request from the queue if it succeeded.
-    RemoveAttemptedRequest(request,
-                           RequestNotifier::BackgroundSavePageResult::SUCCESS);
-  } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
-    RemoveAttemptedRequest(
-        request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
-  } else if (request.completed_attempt_count() + 1 >=
-             policy_->GetMaxCompletedTries()) {
-    // Remove from the request queue if we exceeded max retries. The +1
-    // represents the request that just completed. Since we call
-    // MarkAttemptCompleted within the if branches, the completed_attempt_count
-    // has not yet been updated when we are checking the if condition.
-    const RequestNotifier::BackgroundSavePageResult result(
-        RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
-    event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
-                                               result, request.request_id());
-    RemoveAttemptedRequest(request, result);
-  } else {
-    // If we failed, but are not over the limit, update the request in the
-    // queue.
-    queue_->MarkAttemptCompleted(
-        request.request_id(),
-        base::Bind(&RequestCoordinator::MarkAttemptDone,
-                   weak_ptr_factory_.GetWeakPtr(), request.request_id(),
-                   request.client_id().name_space));
-  }
-}
-
-bool RequestCoordinator::ShouldTryNextRequest(
-    Offliner::RequestStatus previous_request_status) {
-  switch (previous_request_status) {
-    case Offliner::RequestStatus::SAVED:
-    case Offliner::RequestStatus::SAVE_FAILED:
-    case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
-    case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
-    case Offliner::RequestStatus::PRERENDERING_FAILED:
-    case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
-      return true;
-    case Offliner::RequestStatus::FOREGROUND_CANCELED:
-    case Offliner::RequestStatus::PRERENDERING_CANCELED:
-    case Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT:
-      // No further processing in this service window.
-      return false;
-    default:
-      // Make explicit choice about new status codes that actually reach here.
-      // Their default is no further processing in this service window.
-      NOTREACHED();
-      return false;
-  }
-}
-
-void RequestCoordinator::EnableForOffliner(int64_t request_id,
-                                           const ClientId& client_id) {
-  // Since the recent tab helper might call multiple times, ignore subsequent
-  // calls for a particular request_id.
-  if (disabled_requests_.find(request_id) == disabled_requests_.end())
-    return;
-
-  // Clear from disabled list.
-  disabled_requests_.erase(request_id);
-
-  // Mark the request as now in available state.
-  MarkAttemptAborted(request_id, client_id.name_space);
-
-  // If we are not busy, start processing right away.
-  StartImmediatelyIfConnected();
-}
-
-void RequestCoordinator::MarkRequestCompleted(int64_t request_id) {
-  // Since the recent tab helper might call multiple times, ignore subsequent
-  // calls for a particular request_id.
-  if (disabled_requests_.find(request_id) == disabled_requests_.end())
-    return;
-
-  // Clear from disabled list.
-  disabled_requests_.erase(request_id);
-
-  // Remove the request, but send out SUCCEEDED instead of removed.
-  // Note: since it had been disabled, it will not have been active in a
-  // background offliner, so it is not appropriate to TryNextRequest here.
-  std::vector<int64_t> request_ids { request_id };
-  queue_->RemoveRequests(
-      request_ids,
-      base::Bind(&RequestCoordinator::HandleRemovedRequests,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 RequestNotifier::BackgroundSavePageResult::SUCCESS));
-}
-
-const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions(
-    const bool user_requested) {
-  return Scheduler::TriggerConditions(
-      policy_->PowerRequired(user_requested),
-      policy_->BatteryPercentageRequired(user_requested),
-      policy_->UnmeteredNetworkRequired(user_requested));
-}
-
-void RequestCoordinator::AddObserver(Observer* observer) {
-  DCHECK(observer);
-  observers_.AddObserver(observer);
-}
-
-void RequestCoordinator::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void RequestCoordinator::NotifyAdded(const SavePageRequest& request) {
-  for (Observer& observer : observers_)
-    observer.OnAdded(request);
-}
-
-void RequestCoordinator::NotifyCompleted(
-    const SavePageRequest& request,
-    RequestNotifier::BackgroundSavePageResult status) {
-  for (Observer& observer : observers_)
-    observer.OnCompleted(request, status);
-}
-
-void RequestCoordinator::NotifyChanged(const SavePageRequest& request) {
-  for (Observer& observer : observers_)
-    observer.OnChanged(request);
-}
-
-void RequestCoordinator::GetOffliner() {
-  if (!offliner_) {
-    offliner_ = factory_->GetOffliner(policy_.get());
-  }
-}
-
-ClientPolicyController* RequestCoordinator::GetPolicyController() {
-  return policy_controller_.get();
-}
-
-void RequestCoordinator::Shutdown() {
-  network_quality_estimator_ = nullptr;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator.h b/components/offline_pages/background/request_coordinator.h
deleted file mode 100644
index 8b07e33a..0000000
--- a/components/offline_pages/background/request_coordinator.h
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/supports_user_data.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/background/connection_notifier.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/scheduler.h"
-#include "net/nqe/network_quality_estimator.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-struct ClientId;
-class OfflinerPolicy;
-class OfflinerFactory;
-class Offliner;
-class RequestQueue;
-class SavePageRequest;
-class Scheduler;
-class ClientPolicyController;
-
-// Coordinates queueing and processing save page later requests.
-class RequestCoordinator : public KeyedService,
-                           public RequestNotifier,
-                           public base::SupportsUserData {
- public:
-  // Nested observer class.  To make sure that no events are missed, the client
-  // code should first register for notifications, then |GetAllRequests|, and
-  // ignore all events before the return from |GetAllRequests|, and consume
-  // events after the return callback from |GetAllRequests|.
-  class Observer {
-   public:
-    virtual ~Observer() = default;
-
-    virtual void OnAdded(const SavePageRequest& request) = 0;
-    virtual void OnCompleted(
-        const SavePageRequest& request,
-        RequestNotifier::BackgroundSavePageResult status) = 0;
-    virtual void OnChanged(const SavePageRequest& request) = 0;
-  };
-
-  enum class RequestAvailability {
-    ENABLED_FOR_OFFLINER,
-    DISABLED_FOR_OFFLINER,
-  };
-
-  // Callback specifying which request IDs were actually removed.
-  typedef base::Callback<void(const MultipleItemStatuses&)>
-      RemoveRequestsCallback;
-
-  // Callback that receives the response for GetAllRequests.
-  typedef base::Callback<void(std::vector<std::unique_ptr<SavePageRequest>>)>
-      GetRequestsCallback;
-
-  RequestCoordinator(std::unique_ptr<OfflinerPolicy> policy,
-                     std::unique_ptr<OfflinerFactory> factory,
-                     std::unique_ptr<RequestQueue> queue,
-                     std::unique_ptr<Scheduler> scheduler,
-                     net::NetworkQualityEstimator::NetworkQualityProvider*
-                         network_quality_estimator);
-
-  ~RequestCoordinator() override;
-
-  // Queues |request| to later load and save when system conditions allow.
-  // Returns an id if the page could be queued successfully, 0L otherwise.
-  int64_t SavePageLater(const GURL& url,
-                        const ClientId& client_id,
-                        bool user_requested,
-                        RequestAvailability availability);
-
-  // Remove a list of requests by |request_id|.  This removes requests from the
-  // request queue, and cancels an in-progress prerender.
-  void RemoveRequests(const std::vector<int64_t>& request_ids,
-                      const RemoveRequestsCallback& callback);
-
-  // Pause a list of requests by |request_id|.  This will change the state
-  // in the request queue so the request cannot be started.
-  void PauseRequests(const std::vector<int64_t>& request_ids);
-
-  // Resume a list of previously paused requests, making them available.
-  void ResumeRequests(const std::vector<int64_t>& request_ids);
-
-  // Get all save page request items in the callback.
-  void GetAllRequests(const GetRequestsCallback& callback);
-
-  // Starts processing of one or more queued save page later requests.
-  // Returns whether processing was started and that caller should expect
-  // a callback. If processing was already active, returns false.
-  bool StartProcessing(const DeviceConditions& device_conditions,
-                       const base::Callback<void(bool)>& callback);
-
-  // Stops the current request processing if active. This is a way for
-  // caller to abort processing; otherwise, processing will complete on
-  // its own. In either case, the callback will be called when processing
-  // is stopped or complete.
-  void StopProcessing(Offliner::RequestStatus stop_status);
-
-  // Used to denote that the foreground thread is ready for the offliner
-  // to start work on a previously entered, but unavailable request.
-  void EnableForOffliner(int64_t request_id, const ClientId& client_id);
-
-  // If a request that is unavailable to the offliner is finished elsewhere,
-  // (by the tab helper synchronous download), send a notificaiton that it
-  // succeeded through our notificaiton system.
-  void MarkRequestCompleted(int64_t request_id);
-
-  const Scheduler::TriggerConditions GetTriggerConditions(
-      const bool user_requested);
-
-  // A way for tests to set the callback in use when an operation is over.
-  void SetProcessingCallbackForTest(const base::Callback<void(bool)> callback) {
-    scheduler_callback_ = callback;
-  }
-
-  // A way to set the callback which would be called if the request will be
-  // scheduled immediately. Used by testing harness to determine if a request
-  // has been processed.
-  void SetImmediateScheduleCallbackForTest(
-      const base::Callback<void(bool)> callback) {
-    immediate_schedule_callback_ = callback;
-  }
-
-  void StartImmediatelyForTest() { StartImmediatelyIfConnected(); }
-
-  // Observers implementing the RequestCoordinator::Observer interface can
-  // register here to get notifications of changes to request state.  This
-  // pointer is not owned, and it is the callers responsibility to remove the
-  // observer before the observer is deleted.
-  void AddObserver(RequestCoordinator::Observer* observer);
-
-  void RemoveObserver(RequestCoordinator::Observer* observer);
-
-  // Implement RequestNotifier
-  void NotifyAdded(const SavePageRequest& request) override;
-  void NotifyCompleted(
-      const SavePageRequest& request,
-      RequestNotifier::BackgroundSavePageResult status) override;
-  void NotifyChanged(const SavePageRequest& request) override;
-
-  // Returns the request queue used for requests.  Coordinator keeps ownership.
-  RequestQueue* queue() { return queue_.get(); }
-
-  // Return an unowned pointer to the Scheduler.
-  Scheduler* scheduler() { return scheduler_.get(); }
-
-  OfflinerPolicy* policy() { return policy_.get(); }
-
-  ClientPolicyController* GetPolicyController();
-
-  // Returns the status of the most recent offlining.
-  Offliner::RequestStatus last_offlining_status() {
-    return last_offlining_status_;
-  }
-
-  bool is_busy() {
-    return is_busy_;
-  }
-
-  // Returns whether processing is starting (before it is decided to actually
-  // process a request (is_busy()) at this time or not.
-  bool is_starting() { return is_starting_; }
-
-  // Tracks whether the last offlining attempt got canceled.  This is reset by
-  // the next StartProcessing() call.
-  bool is_canceled() {
-    return processing_state_ == ProcessingWindowState::STOPPED;
-  }
-
-  RequestCoordinatorEventLogger* GetLogger() { return &event_logger_; }
-
- private:
-  // Immediate start attempt status code for UMA.
-  // These values are written to logs. New enum values can be added, but
-  // existing enums must never be renumbered or deleted and reused.
-  // For any additions, also update corresponding histogram in histograms.xml.
-  enum OfflinerImmediateStartStatus {
-    // Did start processing request.
-    STARTED = 0,
-    // Already busy processing a request.
-    BUSY = 1,
-    // The Offliner did not accept processing the request.
-    NOT_ACCEPTED = 2,
-    // No current network connection.
-    NO_CONNECTION = 3,
-    // Weak network connection (worse than 2G speed)
-    // according to network quality estimator.
-    WEAK_CONNECTION = 4,
-    // Did not start because this is svelte device.
-    NOT_STARTED_ON_SVELTE = 5,
-    // NOTE: insert new values above this line and update histogram enum too.
-    STATUS_COUNT = 6,
-  };
-
-  enum class ProcessingWindowState {
-    STOPPED,
-    SCHEDULED_WINDOW,
-    IMMEDIATE_WINDOW,
-  };
-
-  // Receives the results of a get from the request queue, and turns that into
-  // SavePageRequest objects for the caller of GetQueuedRequests.
-  void GetQueuedRequestsCallback(
-      const GetRequestsCallback& callback,
-      GetRequestsResult result,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  // Receives the results of a get from the request queue, and turns that into
-  // SavePageRequest objects for the caller of GetQueuedRequests.
-  void GetRequestsForSchedulingCallback(
-      GetRequestsResult result,
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  // Receives the result of add requests to the request queue.
-  void AddRequestResultCallback(RequestAvailability availability,
-                                AddRequestResult result,
-                                const SavePageRequest& request);
-
-  void UpdateMultipleRequestsCallback(
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  void HandleRemovedRequestsAndCallback(
-      const RemoveRequestsCallback& callback,
-      RequestNotifier::BackgroundSavePageResult status,
-      std::unique_ptr<UpdateRequestsResult> result);
-
-  void HandleRemovedRequests(RequestNotifier::BackgroundSavePageResult status,
-                             std::unique_ptr<UpdateRequestsResult> result);
-
-  bool StartProcessingInternal(const ProcessingWindowState processing_state,
-                               const DeviceConditions& device_conditions,
-                               const base::Callback<void(bool)>& callback);
-
-  // Start processing now if connected (but with conservative assumption
-  // as to other device conditions).
-  void StartImmediatelyIfConnected();
-
-  OfflinerImmediateStartStatus TryImmediateStart();
-
-  // Requests a callback upon the next network connection to start processing.
-  void RequestConnectedEventForStarting();
-
-  // Clears the request for connected event if it was set.
-  void ClearConnectedEventRequest();
-
-  // Handles receiving a connection event. Will start immediate processing.
-  void HandleConnectedEventForStarting();
-
-  // Check the request queue, and schedule a task corresponding
-  // to the least restrictive type of request in the queue.
-  void ScheduleAsNeeded();
-
-  // Callback from the request picker when it has chosen our next request.
-  void RequestPicked(const SavePageRequest& request);
-
-  // Callback from the request picker when no more requests are in the queue.
-  // The parameter is a signal for what (if any) conditions to schedule future
-  // processing for.
-  void RequestNotPicked(bool non_user_requested_tasks_remaining);
-
-  // Callback from request picker that receives the current available queued
-  // request count as well as the total queued request count (which may be
-  // different if unavailable requests are queued such as paused requests).
-  // It also receives a flag as to whether this request picking is due to the
-  // start of a request processing window.
-  void RequestCounts(bool is_start_of_processing,
-                     size_t total_requests,
-                     size_t available_requests);
-
-  void HandleWatchdogTimeout();
-
-  // Cancels an in progress pre-rendering, and updates state appropriately.
-  void StopPrerendering(Offliner::RequestStatus stop_status);
-
-  // Marks attempt on the request and sends it to offliner in continuation.
-  void SendRequestToOffliner(const SavePageRequest& request);
-
-  // Continuation of |SendRequestToOffliner| after the request is marked as
-  // started.
-  void StartOffliner(int64_t request_id,
-                     const std::string& client_namespace,
-                     std::unique_ptr<UpdateRequestsResult> update_result);
-
-  // Called by the offliner when an offlining request is completed. (and by
-  // tests).
-  void OfflinerDoneCallback(const SavePageRequest& request,
-                            Offliner::RequestStatus status);
-
-  // Records a completed attempt for the request and update it in the queue
-  // (possibly removing it).
-  void UpdateRequestForCompletedAttempt(const SavePageRequest& request,
-                                        Offliner::RequestStatus status);
-
-  // Returns whether we should try another request based on the outcome
-  // of the previous one.
-  bool ShouldTryNextRequest(Offliner::RequestStatus previous_request_status);
-
-  // Try to find and start offlining an available request.
-  // |is_start_of_processing| identifies if this is the beginning of a
-  // processing window (vs. continuing within a current processing window).
-  void TryNextRequest(bool is_start_of_processing);
-
-  // If there is an active request in the list, cancel that request.
-  bool CancelActiveRequestIfItMatches(const std::vector<int64_t>& request_ids);
-
-  // Records an aborted attempt for the request and update it in the queue
-  // (possibly removing it).
-  void UpdateRequestForAbortedAttempt(const SavePageRequest& request);
-
-  // Remove the attempted request from the queue with status to pass through to
-  // any observers and UMA histogram.
-  void RemoveAttemptedRequest(const SavePageRequest& request,
-                              BackgroundSavePageResult status);
-
-  // Marks the attempt as aborted. This makes the request available again
-  // for offlining.
-  void MarkAttemptAborted(int64_t request_id, const std::string& name_space);
-
-  // Reports change from marking request, reports an error if it fails.
-  void MarkAttemptDone(int64_t request_id,
-                       const std::string& name_space,
-                       std::unique_ptr<UpdateRequestsResult> result);
-
-  // Returns the appropriate offliner to use, getting a new one from the factory
-  // if needed.
-  void GetOffliner();
-
-  // Method to wrap calls to getting the connection type so it can be
-  // changed for tests.
-  net::NetworkChangeNotifier::ConnectionType GetConnectionType();
-
-  void SetNetworkConditionsForTest(
-      net::NetworkChangeNotifier::ConnectionType connection) {
-    use_test_connection_type_ = true;
-    test_connection_type_ = connection;
-  }
-
-  void SetDeviceConditionsForTest(const DeviceConditions& current_conditions) {
-    current_conditions_.reset(new DeviceConditions(current_conditions));
-  }
-
-  // KeyedService implementation:
-  void Shutdown() override;
-
-  friend class RequestCoordinatorTest;
-
-  // Cached value of whether low end device. Overwritable for testing.
-  bool is_low_end_device_;
-
-  // The offliner can only handle one request at a time - if the offliner is
-  // busy, prevent other requests.  This flag marks whether the offliner is in
-  // use.
-  bool is_busy_;
-  // There is more than one path to start processing so this flag is used
-  // to avoid race conditions before is_busy_ is established.
-  bool is_starting_;
-  // Identifies the type of current processing window or if processing stopped.
-  ProcessingWindowState processing_state_;
-  // True if we should use the test connection type instead of the actual type.
-  bool use_test_connection_type_;
-  // For use by tests, a fake network connection type
-  net::NetworkChangeNotifier::ConnectionType test_connection_type_;
-  // Unowned pointer to the current offliner, if any.
-  Offliner* offliner_;
-  base::Time operation_start_time_;
-  // The observers.
-  base::ObserverList<Observer> observers_;
-  // Last known conditions for network, battery
-  std::unique_ptr<DeviceConditions> current_conditions_;
-  // RequestCoordinator takes over ownership of the policy
-  std::unique_ptr<OfflinerPolicy> policy_;
-  // OfflinerFactory.  Used to create offline pages. Owned.
-  std::unique_ptr<OfflinerFactory> factory_;
-  // RequestQueue.  Used to store incoming requests. Owned.
-  std::unique_ptr<RequestQueue> queue_;
-  // Scheduler. Used to request a callback when network is available.  Owned.
-  std::unique_ptr<Scheduler> scheduler_;
-  // Controller of client policies. Owned.
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-  // Unowned pointer to the Network Quality Estimator.
-  net::NetworkQualityEstimator::NetworkQualityProvider*
-      network_quality_estimator_;
-  // Holds copy of the active request, if any.
-  std::unique_ptr<SavePageRequest> active_request_;
-  // Status of the most recent offlining.
-  Offliner::RequestStatus last_offlining_status_;
-  // A set of request_ids that we are holding off until the download manager is
-  // done with them.
-  std::set<int64_t> disabled_requests_;
-  // Calling this returns to the scheduler across the JNI bridge.
-  base::Callback<void(bool)> scheduler_callback_;
-  // Logger to record events.
-  RequestCoordinatorEventLogger event_logger_;
-  // Timer to watch for pre-render attempts running too long.
-  base::OneShotTimer watchdog_timer_;
-  // Callback invoked when an immediate request is done (default empty).
-  base::Callback<void(bool)> immediate_schedule_callback_;
-  // Used for potential immediate processing when we get network connection.
-  std::unique_ptr<ConnectionNotifier> connection_notifier_;
-  // Allows us to pass a weak pointer to callbacks.
-  base::WeakPtrFactory<RequestCoordinator> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(RequestCoordinator);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
diff --git a/components/offline_pages/background/request_coordinator_event_logger.cc b/components/offline_pages/background/request_coordinator_event_logger.cc
deleted file mode 100644
index a638138..0000000
--- a/components/offline_pages/background/request_coordinator_event_logger.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-
-namespace offline_pages {
-
-namespace {
-
-static std::string OfflinerRequestStatusToString(
-    Offliner::RequestStatus request_status) {
-  switch (request_status) {
-    case Offliner::UNKNOWN:
-      return "UNKNOWN";
-    case Offliner::LOADED:
-      return "LOADED";
-    case Offliner::SAVED:
-      return "SAVED";
-    case Offliner::REQUEST_COORDINATOR_CANCELED:
-      return "REQUEST_COORDINATOR_CANCELED";
-    case Offliner::PRERENDERING_CANCELED:
-      return "PRERENDERING_CANCELED";
-    case Offliner::PRERENDERING_FAILED:
-      return "PRERENDERING_FAILED";
-    case Offliner::SAVE_FAILED:
-      return "SAVE_FAILED";
-    case Offliner::FOREGROUND_CANCELED:
-      return "FOREGROUND_CANCELED";
-    case Offliner::REQUEST_COORDINATOR_TIMED_OUT:
-      return "REQUEST_COORDINATOR_TIMED_OUT";
-    case Offliner::PRERENDERING_NOT_STARTED:
-      return "PRERENDERING_NOT_STARTED";
-    case Offliner::PRERENDERING_FAILED_NO_RETRY:
-      return "PRERENDERING_FAILED_NO_RETRY";
-    case Offliner::PRERENDERING_FAILED_NO_NEXT:
-      return "PRERENDERING_FAILED_NO_NEXT";
-    default:
-      NOTREACHED();
-      return std::to_string(static_cast<int>(request_status));
-  }
-}
-
-static std::string BackgroundSavePageResultToString(
-    RequestNotifier::BackgroundSavePageResult result) {
-  switch (result) {
-    case RequestNotifier::BackgroundSavePageResult::SUCCESS:
-      return "SUCCESS";
-    case RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE:
-      return "PRERENDER_FAILURE";
-    case RequestNotifier::BackgroundSavePageResult::PRERENDER_CANCELED:
-      return "PRERENDER_CANCELED";
-    case RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED:
-      return "FOREGROUND_CANCELED";
-    case RequestNotifier::BackgroundSavePageResult::SAVE_FAILED:
-      return "SAVE_FAILED";
-    case RequestNotifier::BackgroundSavePageResult::EXPIRED:
-      return "EXPIRED";
-    case RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED:
-      return "RETRY_COUNT_EXCEEDED";
-    case RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED:
-      return "START_COUNT_EXCEEDED";
-    case RequestNotifier::BackgroundSavePageResult::REMOVED:
-      return "REMOVED";
-    default:
-      NOTREACHED();
-      return std::to_string(static_cast<int>(result));
-  }
-}
-
-static std::string UpdateRequestResultToString(UpdateRequestResult result) {
-  switch (result) {
-    case UpdateRequestResult::SUCCESS:
-      return "SUCCESS";
-    case UpdateRequestResult::STORE_FAILURE:
-      return "STORE_FAILURE";
-    case UpdateRequestResult::REQUEST_DOES_NOT_EXIST:
-      return "REQUEST_DOES_NOT_EXIST";
-    default:
-      NOTREACHED();
-      return std::to_string(static_cast<int>(result));
-  }
-}
-
-}  // namespace
-
-void RequestCoordinatorEventLogger::RecordOfflinerResult(
-    const std::string& name_space,
-    Offliner::RequestStatus new_status,
-    int64_t request_id) {
-  RecordActivity("Background save attempt for " + name_space + ":" +
-                 std::to_string(request_id) + " - " +
-                 OfflinerRequestStatusToString(new_status));
-}
-
-void RequestCoordinatorEventLogger::RecordDroppedSavePageRequest(
-    const std::string& name_space,
-    RequestNotifier::BackgroundSavePageResult result,
-    int64_t request_id) {
-  RecordActivity("Background save request removed " + name_space + ":" +
-                 std::to_string(request_id) + " - " +
-                 BackgroundSavePageResultToString(result));
-}
-
-void RequestCoordinatorEventLogger::RecordUpdateRequestFailed(
-    const std::string& name_space,
-    UpdateRequestResult result) {
-  RecordActivity("Updating queued request for " + name_space + " failed - " +
-                 UpdateRequestResultToString(result));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator_event_logger.h b/components/offline_pages/background/request_coordinator_event_logger.h
deleted file mode 100644
index ec07079..0000000
--- a/components/offline_pages/background/request_coordinator_event_logger.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
-
-#include <stdint.h>
-#include <string>
-
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/offline_event_logger.h"
-
-namespace offline_pages {
-
-class RequestCoordinatorEventLogger : public OfflineEventLogger {
- public:
-  // Records the result of a background task attempt for SavePageRequest
-  // |request_id|.
-  void RecordOfflinerResult(const std::string& name_space,
-                            Offliner::RequestStatus new_status,
-                            int64_t request_id);
-
-  // Records the reason for dropped SavePageRequest |request_id|.
-  void RecordDroppedSavePageRequest(
-      const std::string& name_space,
-      RequestNotifier::BackgroundSavePageResult result,
-      int64_t request_id);
-
-  void RecordUpdateRequestFailed(const std::string& name_space,
-                                 UpdateRequestResult result);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
diff --git a/components/offline_pages/background/request_coordinator_event_logger_unittest.cc b/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
deleted file mode 100644
index eed1f45..0000000
--- a/components/offline_pages/background/request_coordinator_event_logger_unittest.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-
-const char kNamespace[] = "last_n";
-const Offliner::RequestStatus kOfflinerStatus = Offliner::SAVED;
-const RequestNotifier::BackgroundSavePageResult kDroppedResult =
-    RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED;
-const int64_t kId = 1234;
-const UpdateRequestResult kQueueUpdateResult =
-    UpdateRequestResult::STORE_FAILURE;
-
-const char kOfflinerStatusLogString[] =
-    "Background save attempt for last_n:1234 - SAVED";
-const char kDroppedResultLogString[] =
-    "Background save request removed last_n:1234 - START_COUNT_EXCEEDED";
-const char kQueueUpdateResultLogString[] =
-    "Updating queued request for last_n failed - STORE_FAILURE";
-const int kTimeLength = 21;
-
-}  // namespace
-
-TEST(RequestCoordinatorEventLoggerTest, RecordsWhenLoggingIsOn) {
-  RequestCoordinatorEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(true);
-  logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
-  logger.RecordDroppedSavePageRequest(kNamespace, kDroppedResult, kId);
-  logger.RecordUpdateRequestFailed(kNamespace, kQueueUpdateResult);
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(3u, log.size());
-  EXPECT_EQ(std::string(kQueueUpdateResultLogString),
-            log[0].substr(kTimeLength));
-  EXPECT_EQ(std::string(kDroppedResultLogString), log[1].substr(kTimeLength));
-  EXPECT_EQ(std::string(kOfflinerStatusLogString), log[2].substr(kTimeLength));
-}
-
-TEST(RequestCoordinatorEventLoggerTest, RecordsWhenLoggingIsOff) {
-  RequestCoordinatorEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(false);
-  logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(0u, log.size());
-}
-
-TEST(RequestCoordinatorEventLoggerTest, DoesNotExceedMaxSize) {
-  RequestCoordinatorEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(true);
-  for (size_t i = 0; i < kMaxLogCount + 1; ++i) {
-    logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
-  }
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(kMaxLogCount, log.size());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_coordinator_unittest.cc b/components/offline_pages/background/request_coordinator_unittest.cc
deleted file mode 100644
index 5fec208..0000000
--- a/components/offline_pages/background/request_coordinator_unittest.cc
+++ /dev/null
@@ -1,1398 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_coordinator.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/sys_info.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/network_quality_provider_stub.h"
-#include "components/offline_pages/background/offliner.h"
-#include "components/offline_pages/background/offliner_factory.h"
-#include "components/offline_pages/background/offliner_factory_stub.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/offliner_stub.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/background/scheduler.h"
-#include "components/offline_pages/background/scheduler_stub.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-// put test constants here
-const GURL kUrl1("http://universe.com/everything");
-const GURL kUrl2("http://universe.com/toinfinityandbeyond");
-const std::string kClientNamespace("bookmark");
-const std::string kId1("42");
-const std::string kId2("life*universe+everything");
-const ClientId kClientId1(kClientNamespace, kId1);
-const ClientId kClientId2(kClientNamespace, kId2);
-const int kRequestId1(1);
-const int kRequestId2(2);
-const long kTestTimeBudgetSeconds = 200;
-const int kBatteryPercentageHigh = 75;
-const int kMaxCompletedTries = 3;
-const bool kPowerRequired = true;
-const bool kUserRequested = true;
-const int kAttemptCount = 1;
-}  // namespace
-
-class ObserverStub : public RequestCoordinator::Observer {
- public:
-  ObserverStub()
-      : added_called_(false),
-        completed_called_(false),
-        changed_called_(false),
-        last_status_(RequestCoordinator::BackgroundSavePageResult::SUCCESS),
-        state_(SavePageRequest::RequestState::OFFLINING) {}
-
-  void Clear() {
-    added_called_ = false;
-    completed_called_ = false;
-    changed_called_ = false;
-    state_ = SavePageRequest::RequestState::OFFLINING;
-    last_status_ = RequestCoordinator::BackgroundSavePageResult::SUCCESS;
-  }
-
-  void OnAdded(const SavePageRequest& request) override {
-    added_called_ = true;
-  }
-
-  void OnCompleted(
-      const SavePageRequest& request,
-      RequestCoordinator::BackgroundSavePageResult status) override {
-    completed_called_ = true;
-    last_status_ = status;
-  }
-
-  void OnChanged(const SavePageRequest& request) override {
-    changed_called_ = true;
-    state_ = request.request_state();
-  }
-
-  bool added_called() { return added_called_; }
-  bool completed_called() { return completed_called_; }
-  bool changed_called() { return changed_called_; }
-  RequestCoordinator::BackgroundSavePageResult last_status() {
-    return last_status_;
-  }
-  SavePageRequest::RequestState state() { return state_; }
-
- private:
-  bool added_called_;
-  bool completed_called_;
-  bool changed_called_;
-  RequestCoordinator::BackgroundSavePageResult last_status_;
-  SavePageRequest::RequestState state_;
-};
-
-class RequestCoordinatorTest
-    : public testing::Test {
- public:
-  RequestCoordinatorTest();
-  ~RequestCoordinatorTest() override;
-
-  void SetUp() override;
-
-  void PumpLoop();
-
-  RequestCoordinator* coordinator() {
-    return coordinator_.get();
-  }
-
-  bool is_busy() {
-    return coordinator_->is_busy();
-  }
-
-  bool is_starting() { return coordinator_->is_starting(); }
-
-  // Empty callback function.
-  void ImmediateScheduleCallbackFunction(bool result) {
-    immediate_schedule_callback_called_ = true;
-    immediate_schedule_callback_result_ = result;
-  }
-
-  // Callback function which releases a wait for it.
-  void WaitingCallbackFunction(bool result) {
-    waiter_.Signal();
-  }
-
-  net::NetworkChangeNotifier::ConnectionType GetConnectionType() {
-    return coordinator()->GetConnectionType();
-  }
-
-  // Callback for Add requests.
-  void AddRequestDone(AddRequestResult result, const SavePageRequest& request);
-
-  // Callback for getting requests.
-  void GetRequestsDone(GetRequestsResult result,
-                       std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  // Callback for removing requests.
-  void RemoveRequestsDone(const MultipleItemStatuses& results);
-
-  // Callback for getting request statuses.
-  void GetQueuedRequestsDone(
-      std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  void SetupForOfflinerDoneCallbackTest(
-      offline_pages::SavePageRequest* request);
-
-  void SendOfflinerDoneCallback(const SavePageRequest& request,
-                                Offliner::RequestStatus status);
-
-  GetRequestsResult last_get_requests_result() const {
-    return last_get_requests_result_;
-  }
-
-  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
-    return last_requests_;
-  }
-
-  const MultipleItemStatuses& last_remove_results() const {
-    return last_remove_results_;
-  }
-
-  void DisableLoading() {
-    offliner_->disable_loading();
-  }
-
-  void EnableOfflinerCallback(bool enable) {
-    offliner_->enable_callback(enable);
-  }
-
-  void SetNetworkConditionsForTest(
-      net::NetworkChangeNotifier::ConnectionType connection) {
-    coordinator()->SetNetworkConditionsForTest(connection);
-  }
-
-  void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
-    network_quality_estimator_->SetEffectiveConnectionTypeForTest(type);
-  }
-
-  void SetNetworkConnected(bool connected) {
-    if (connected) {
-      SetNetworkConditionsForTest(
-          net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
-      SetEffectiveConnectionTypeForTest(
-          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G);
-    } else {
-      SetNetworkConditionsForTest(
-          net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
-      SetEffectiveConnectionTypeForTest(
-          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
-    }
-  }
-
-  void CallConnectionTypeObserver() {
-    if (coordinator()->connection_notifier_) {
-      coordinator()->connection_notifier_->OnConnectionTypeChanged(
-          GetConnectionType());
-    }
-  }
-
-  void SetIsLowEndDeviceForTest(bool is_low_end_device) {
-    coordinator()->is_low_end_device_ = is_low_end_device;
-  }
-
-  void SetProcessingStateForTest(
-      RequestCoordinator::ProcessingWindowState processing_state) {
-    coordinator()->processing_state_ = processing_state;
-  }
-
-  void SetOperationStartTimeForTest(base::Time start_time) {
-    coordinator()->operation_start_time_ = start_time;
-  }
-
-  void ScheduleForTest() { coordinator_->ScheduleAsNeeded(); }
-
-  void CallRequestNotPicked(bool non_user_requested_tasks_remaining,
-                            bool disabled_tasks_remaining) {
-    if (disabled_tasks_remaining)
-      coordinator_->disabled_requests_.insert(kRequestId1);
-    else
-      coordinator_->disabled_requests_.clear();
-
-    coordinator_->RequestNotPicked(non_user_requested_tasks_remaining);
-  }
-
-  void SetDeviceConditionsForTest(DeviceConditions device_conditions) {
-    coordinator_->SetDeviceConditionsForTest(device_conditions);
-  }
-
-  void WaitForCallback() {
-    waiter_.Wait();
-  }
-
-  void AdvanceClockBy(base::TimeDelta delta) {
-    task_runner_->FastForwardBy(delta);
-  }
-
-  SavePageRequest AddRequest1();
-
-  SavePageRequest AddRequest2();
-
-  Offliner::RequestStatus last_offlining_status() const {
-    return coordinator_->last_offlining_status_;
-  }
-
-  bool OfflinerWasCanceled() const { return offliner_->cancel_called(); }
-
-  ObserverStub observer() { return observer_; }
-
-  DeviceConditions device_conditions() { return device_conditions_; }
-
-  base::Callback<void(bool)> immediate_callback() {
-    return immediate_callback_;
-  }
-
-  base::Callback<void(bool)> waiting_callback() { return waiting_callback_; }
-  bool immediate_schedule_callback_called() const {
-    return immediate_schedule_callback_called_;
-  }
-
-  bool immediate_schedule_callback_result() const {
-    return immediate_schedule_callback_result_;
-  }
-
-  const base::HistogramTester& histograms() const { return histogram_tester_; }
-
- private:
-  GetRequestsResult last_get_requests_result_;
-  MultipleItemStatuses last_remove_results_;
-  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-  std::unique_ptr<NetworkQualityProviderStub> network_quality_estimator_;
-  std::unique_ptr<RequestCoordinator> coordinator_;
-  OfflinerStub* offliner_;
-  base::WaitableEvent waiter_;
-  ObserverStub observer_;
-  bool immediate_schedule_callback_called_;
-  bool immediate_schedule_callback_result_;
-  DeviceConditions device_conditions_;
-  base::Callback<void(bool)> immediate_callback_;
-  base::Callback<void(bool)> waiting_callback_;
-  base::HistogramTester histogram_tester_;
-};
-
-RequestCoordinatorTest::RequestCoordinatorTest()
-    : last_get_requests_result_(GetRequestsResult::STORE_FAILURE),
-      task_runner_(new base::TestMockTimeTaskRunner),
-      task_runner_handle_(task_runner_),
-      offliner_(nullptr),
-      waiter_(base::WaitableEvent::ResetPolicy::MANUAL,
-              base::WaitableEvent::InitialState::NOT_SIGNALED),
-      immediate_schedule_callback_called_(false),
-      immediate_schedule_callback_result_(false),
-      device_conditions_(!kPowerRequired,
-                         kBatteryPercentageHigh,
-                         net::NetworkChangeNotifier::CONNECTION_3G) {}
-
-RequestCoordinatorTest::~RequestCoordinatorTest() {}
-
-void RequestCoordinatorTest::SetUp() {
-  std::unique_ptr<OfflinerPolicy> policy(new OfflinerPolicy());
-  std::unique_ptr<OfflinerFactory> offliner_factory(new OfflinerFactoryStub());
-  // Save the offliner for use by the tests.
-  offliner_ = reinterpret_cast<OfflinerStub*>(
-      offliner_factory->GetOffliner(policy.get()));
-  std::unique_ptr<RequestQueueInMemoryStore>
-      store(new RequestQueueInMemoryStore());
-  std::unique_ptr<RequestQueue> queue(new RequestQueue(std::move(store)));
-  std::unique_ptr<Scheduler> scheduler_stub(new SchedulerStub());
-  network_quality_estimator_.reset(new NetworkQualityProviderStub());
-  coordinator_.reset(new RequestCoordinator(
-      std::move(policy), std::move(offliner_factory), std::move(queue),
-      std::move(scheduler_stub), network_quality_estimator_.get()));
-  coordinator_->AddObserver(&observer_);
-  SetNetworkConnected(true);
-  std::unique_ptr<PickRequestTaskFactory> picker_factory(
-      new PickRequestTaskFactory(
-          coordinator_->policy(),
-          static_cast<RequestNotifier*>(coordinator_.get()),
-          coordinator_->GetLogger()));
-  coordinator_->queue()->SetPickerFactory(std::move(picker_factory));
-  immediate_callback_ =
-      base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
-                 base::Unretained(this));
-  // Override the normal immediate callback with a wait releasing callback.
-  waiting_callback_ = base::Bind(
-      &RequestCoordinatorTest::WaitingCallbackFunction, base::Unretained(this));
-  SetDeviceConditionsForTest(device_conditions_);
-  EnableOfflinerCallback(true);
-}
-
-void RequestCoordinatorTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void RequestCoordinatorTest::GetRequestsDone(
-    GetRequestsResult result,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  last_get_requests_result_ = result;
-  last_requests_ = std::move(requests);
-}
-
-void RequestCoordinatorTest::RemoveRequestsDone(
-    const MultipleItemStatuses& results) {
-  last_remove_results_ = results;
-  waiter_.Signal();
-}
-
-void RequestCoordinatorTest::GetQueuedRequestsDone(
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  last_requests_ = std::move(requests);
-  waiter_.Signal();
-}
-
-void RequestCoordinatorTest::AddRequestDone(AddRequestResult result,
-                                            const SavePageRequest& request) {}
-
-void RequestCoordinatorTest::SetupForOfflinerDoneCallbackTest(
-    offline_pages::SavePageRequest* request) {
-  // Mark request as started and add it to the queue,
-  // then wait for callback to finish.
-  request->MarkAttemptStarted(base::Time::Now());
-  coordinator()->queue()->AddRequest(
-      *request, base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                           base::Unretained(this)));
-  PumpLoop();
-
-  // Override the processing callback for test visiblity.
-  base::Callback<void(bool)> callback =
-      base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
-                 base::Unretained(this));
-  coordinator()->SetProcessingCallbackForTest(callback);
-
-  // Mock that coordinator is in actively processing state starting now.
-  SetProcessingStateForTest(
-      RequestCoordinator::ProcessingWindowState::IMMEDIATE_WINDOW);
-  SetOperationStartTimeForTest(base::Time::Now());
-}
-
-void RequestCoordinatorTest::SendOfflinerDoneCallback(
-    const SavePageRequest& request, Offliner::RequestStatus status) {
-  // Using the fact that the test class is a friend, call to the callback
-  coordinator_->OfflinerDoneCallback(request, status);
-}
-
-SavePageRequest RequestCoordinatorTest::AddRequest1() {
-  offline_pages::SavePageRequest request1(kRequestId1, kUrl1, kClientId1,
-                                          base::Time::Now(), kUserRequested);
-  coordinator()->queue()->AddRequest(
-      request1, base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                           base::Unretained(this)));
-  return request1;
-}
-
-SavePageRequest RequestCoordinatorTest::AddRequest2() {
-  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
-                                          base::Time::Now(), kUserRequested);
-  coordinator()->queue()->AddRequest(
-      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                           base::Unretained(this)));
-  return request2;
-}
-
-TEST_F(RequestCoordinatorTest, StartProcessingWithNoRequests) {
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-  PumpLoop();
-
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Verify queue depth UMA for starting scheduled processing on empty queue.
-  if (base::SysInfo::IsLowEndDevice()) {
-    histograms().ExpectBucketCount(
-        "OfflinePages.Background.ScheduledStart.AvailableRequestCount.Svelte",
-        0, 1);
-  } else {
-    histograms().ExpectBucketCount(
-        "OfflinePages.Background.ScheduledStart.AvailableRequestCount", 0, 1);
-  }
-}
-
-TEST_F(RequestCoordinatorTest, StartProcessingWithRequestInProgress) {
-  // Start processing for this request.
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-
-  // Ensure that the forthcoming request does not finish - we simulate it being
-  // in progress by asking it to skip making the completion callback.
-  EnableOfflinerCallback(false);
-
-  // Sending the request to the offliner should make it busy.
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-  PumpLoop();
-
-  EXPECT_TRUE(is_busy());
-  // Since the offliner is disabled, this callback should not be called.
-  EXPECT_FALSE(immediate_schedule_callback_called());
-
-  // Now trying to start processing on another request should return false.
-  EXPECT_FALSE(coordinator()->StartProcessing(device_conditions(),
-                                              immediate_callback()));
-}
-
-TEST_F(RequestCoordinatorTest, SavePageLater) {
-  // The user-requested request which gets processed by SavePageLater
-  // would invoke user request callback.
-  coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
-
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-
-  // Expect that a request got placed on the queue.
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-
-  // Wait for callbacks to finish, both request queue and offliner.
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Check the request queue is as expected.
-  EXPECT_EQ(1UL, last_requests().size());
-  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
-  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
-
-  // Expect that the scheduler got notified.
-  SchedulerStub* scheduler_stub =
-      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
-  EXPECT_TRUE(scheduler_stub->schedule_called());
-  EXPECT_EQ(coordinator()
-                ->GetTriggerConditions(last_requests()[0]->user_requested())
-                .minimum_battery_percentage,
-            scheduler_stub->conditions()->minimum_battery_percentage);
-
-  // Check that the observer got the notification that a page is available
-  EXPECT_TRUE(observer().added_called());
-
-  // Verify queue depth UMA for starting immediate processing.
-  if (base::SysInfo::IsLowEndDevice()) {
-    histograms().ExpectBucketCount(
-        "OfflinePages.Background.ImmediateStart.AvailableRequestCount.Svelte",
-        1, 1);
-  } else {
-    histograms().ExpectBucketCount(
-        "OfflinePages.Background.ImmediateStart.AvailableRequestCount", 1, 1);
-  }
-}
-
-TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
-  // The user-requested request which gets processed by SavePageLater
-  // would invoke user request callback.
-  coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
-
-  EXPECT_TRUE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER) != 0);
-
-  // Expect that a request got placed on the queue.
-  coordinator()->queue()->GetRequests(
-      base::Bind(&RequestCoordinatorTest::GetRequestsDone,
-                 base::Unretained(this)));
-
-  // Wait for callbacks to finish, both request queue and offliner.
-  PumpLoop();
-
-  // On low-end devices the callback will be called with false since the
-  // processing started but failed due to svelte devices.
-  EXPECT_TRUE(immediate_schedule_callback_called());
-  if (base::SysInfo::IsLowEndDevice()) {
-    EXPECT_FALSE(immediate_schedule_callback_result());
-  } else {
-    EXPECT_TRUE(immediate_schedule_callback_result());
-  }
-
-  // Check the request queue is as expected.
-  EXPECT_EQ(1UL, last_requests().size());
-  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
-  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
-
-  // Expect that the scheduler got notified.
-  SchedulerStub* scheduler_stub = reinterpret_cast<SchedulerStub*>(
-      coordinator()->scheduler());
-  EXPECT_TRUE(scheduler_stub->schedule_called());
-  EXPECT_EQ(coordinator()
-                ->GetTriggerConditions(last_requests()[0]->user_requested())
-                .minimum_battery_percentage,
-            scheduler_stub->conditions()->minimum_battery_percentage);
-
-  // Check that the observer got the notification that a page is available
-  EXPECT_TRUE(observer().added_called());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceeded) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(
-      kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-
-  // Call the OfflinerDoneCallback to simulate the page being completed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Verify the request gets removed from the queue, and wait for callbacks.
-  coordinator()->queue()->GetRequests(
-      base::Bind(&RequestCoordinatorTest::GetRequestsDone,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  // We should not find any requests in the queue anymore.
-  // RequestPicker should *not* have tried to start an additional job,
-  // because the request queue is empty now.
-  EXPECT_EQ(0UL, last_requests().size());
-  // Check that the observer got the notification that we succeeded, and that
-  // the request got removed from the queue.
-  EXPECT_TRUE(observer().completed_called());
-  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::SUCCESS,
-            observer().last_status());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceededButLostNetwork) {
-  // Add a request to the queue and set offliner done callback for it.
-  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-  EnableOfflinerCallback(false);
-
-  // Add a 2nd request to the queue.
-  AddRequest2();
-
-  // Disconnect network.
-  SetNetworkConnected(false);
-
-  // Call the OfflinerDoneCallback to simulate the page being completed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Verify not busy with 2nd request (since no connection).
-  EXPECT_FALSE(is_busy());
-
-  // Now connect network and verify processing starts.
-  SetNetworkConnected(true);
-  CallConnectionTypeObserver();
-  PumpLoop();
-  EXPECT_TRUE(is_busy());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailed) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(
-      kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
-  request.set_completed_attempt_count(kMaxCompletedTries - 1);
-  SetupForOfflinerDoneCallbackTest(&request);
-  // Stop processing before completing the second request on the queue.
-  EnableOfflinerCallback(false);
-
-  // Add second request to the queue to check handling when first fails.
-  AddRequest2();
-  PumpLoop();
-
-  // Call the OfflinerDoneCallback to simulate the request failed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(request,
-                           Offliner::RequestStatus::PRERENDERING_FAILED);
-  PumpLoop();
-
-  // For retriable failure, processing should continue to 2nd request so
-  // no scheduler callback yet.
-  EXPECT_FALSE(immediate_schedule_callback_called());
-
-  // Busy processing 2nd request.
-  EXPECT_TRUE(is_busy());
-
-  coordinator()->queue()->GetRequests(
-      base::Bind(&RequestCoordinatorTest::GetRequestsDone,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  // Now just one request in the queue since failed request removed
-  // (max number of attempts exceeded).
-  EXPECT_EQ(1UL, last_requests().size());
-  // Check that the observer got the notification that we failed (and the
-  // subsequent notification that the request was removed) since we exceeded
-  // retry count.
-  EXPECT_TRUE(observer().completed_called());
-  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED,
-            observer().last_status());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoRetryFailure) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-  EnableOfflinerCallback(false);
-
-  // Add second request to the queue to check handling when first fails.
-  AddRequest2();
-  PumpLoop();
-
-  // Call the OfflinerDoneCallback to simulate the request failed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(
-      request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY);
-  PumpLoop();
-
-  // For no retry failure, processing should continue to 2nd request so
-  // no scheduler callback yet.
-  EXPECT_FALSE(immediate_schedule_callback_called());
-
-  // Busy processing 2nd request.
-  EXPECT_TRUE(is_busy());
-
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Now just one request in the queue since non-retryable failure.
-  EXPECT_EQ(1UL, last_requests().size());
-  // Check that the observer got the notification that we failed (and the
-  // subsequent notification that the request was removed).
-  EXPECT_TRUE(observer().completed_called());
-  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::PRERENDER_FAILURE,
-            observer().last_status());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoNextFailure) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-  EnableOfflinerCallback(false);
-
-  // Add second request to the queue to check handling when first fails.
-  AddRequest2();
-  PumpLoop();
-
-  // Call the OfflinerDoneCallback to simulate the request failed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(
-      request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT);
-  PumpLoop();
-
-  // For no next failure, processing should not continue to 2nd request so
-  // expect scheduler callback.
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Not busy for NO_NEXT failure.
-  EXPECT_FALSE(is_busy());
-
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Both requests still in queue.
-  EXPECT_EQ(2UL, last_requests().size());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(
-      kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-
-  // Call the OfflinerDoneCallback to simulate the request failed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(request,
-                           Offliner::RequestStatus::FOREGROUND_CANCELED);
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Verify the request is not removed from the queue, and wait for callbacks.
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Request no longer in the queue (for single attempt policy).
-  EXPECT_EQ(1UL, last_requests().size());
-  // Verify foreground cancel not counted as an attempt after all.
-  EXPECT_EQ(0L, last_requests().at(0)->completed_attempt_count());
-}
-
-TEST_F(RequestCoordinatorTest, OfflinerDonePrerenderingCancel) {
-  // Add a request to the queue, wait for callbacks to finish.
-  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
-  SetupForOfflinerDoneCallbackTest(&request);
-
-  // Call the OfflinerDoneCallback to simulate the request failed, wait
-  // for callbacks.
-  SendOfflinerDoneCallback(request,
-                           Offliner::RequestStatus::PRERENDERING_CANCELED);
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // Verify the request is not removed from the queue, and wait for callbacks.
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Request still in the queue.
-  EXPECT_EQ(1UL, last_requests().size());
-  // Verify prerendering cancel not counted as an attempt after all.
-  const std::unique_ptr<SavePageRequest>& found_request =
-      last_requests().front();
-  EXPECT_EQ(0L, found_request->completed_attempt_count());
-}
-
-// If one item completes, and there are no more user requeted items left,
-// we should make a scheduler entry for a non-user requested item.
-TEST_F(RequestCoordinatorTest, RequestNotPickedDisabledItemsRemain) {
-  coordinator()->StartProcessing(device_conditions(), immediate_callback());
-  EXPECT_TRUE(is_starting());
-
-  // Call RequestNotPicked, simulating a request on the disabled list.
-  CallRequestNotPicked(false, true);
-  PumpLoop();
-
-  EXPECT_FALSE(is_starting());
-
-  // The scheduler should have been called to schedule the disabled task for
-  // 5 minutes from now.
-  SchedulerStub* scheduler_stub =
-      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
-  EXPECT_TRUE(scheduler_stub->backup_schedule_called());
-  EXPECT_TRUE(scheduler_stub->unschedule_called());
-}
-
-// If one item completes, and there are no more user requeted items left,
-// we should make a scheduler entry for a non-user requested item.
-TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) {
-  coordinator()->StartProcessing(device_conditions(), immediate_callback());
-  EXPECT_TRUE(is_starting());
-
-  // Call RequestNotPicked, and make sure we pick schedule a task for non user
-  // requested conditions, with no tasks on the disabled list.
-  CallRequestNotPicked(true, false);
-  PumpLoop();
-
-  EXPECT_FALSE(is_starting());
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  // The scheduler should have been called to schedule the non-user requested
-  // task.
-  SchedulerStub* scheduler_stub =
-      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
-  EXPECT_TRUE(scheduler_stub->schedule_called());
-  EXPECT_TRUE(scheduler_stub->unschedule_called());
-  const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
-  EXPECT_EQ(conditions->require_power_connected,
-            coordinator()->policy()->PowerRequired(!kUserRequested));
-  EXPECT_EQ(
-      conditions->minimum_battery_percentage,
-      coordinator()->policy()->BatteryPercentageRequired(!kUserRequested));
-  EXPECT_EQ(conditions->require_unmetered_network,
-            coordinator()->policy()->UnmeteredNetworkRequired(!kUserRequested));
-}
-
-TEST_F(RequestCoordinatorTest, SchedulerGetsLeastRestrictiveConditions) {
-  // Put two requests on the queue - The first is user requested, and
-  // the second is not user requested.
-  AddRequest1();
-  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
-                                          base::Time::Now(), !kUserRequested);
-  coordinator()->queue()->AddRequest(
-      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                           base::Unretained(this)));
-  PumpLoop();
-
-  // Trigger the scheduler to schedule for the least restrictive condition.
-  ScheduleForTest();
-  PumpLoop();
-
-  // Expect that the scheduler got notified, and it is at user_requested
-  // priority.
-  SchedulerStub* scheduler_stub =
-      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
-  const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
-  EXPECT_TRUE(scheduler_stub->schedule_called());
-  EXPECT_EQ(conditions->require_power_connected,
-            coordinator()->policy()->PowerRequired(kUserRequested));
-  EXPECT_EQ(conditions->minimum_battery_percentage,
-            coordinator()->policy()->BatteryPercentageRequired(kUserRequested));
-  EXPECT_EQ(conditions->require_unmetered_network,
-            coordinator()->policy()->UnmeteredNetworkRequired(kUserRequested));
-}
-
-TEST_F(RequestCoordinatorTest, StartProcessingWithLoadingDisabled) {
-  // Add a request to the queue, wait for callbacks to finish.
-  AddRequest1();
-  PumpLoop();
-
-  DisableLoading();
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-
-  // Let the async callbacks in the request coordinator run.
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  EXPECT_FALSE(is_starting());
-  EXPECT_EQ(Offliner::PRERENDERING_NOT_STARTED, last_offlining_status());
-}
-
-// This tests a StopProcessing call before we have actually started the
-// prerenderer.
-TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingImmediately) {
-  // Add a request to the queue, wait for callbacks to finish.
-  AddRequest1();
-  PumpLoop();
-
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-  EXPECT_TRUE(is_starting());
-
-  // Now, quick, before it can do much (we haven't called PumpLoop), cancel it.
-  coordinator()->StopProcessing(Offliner::REQUEST_COORDINATOR_CANCELED);
-
-  // Let the async callbacks in the request coordinator run.
-  PumpLoop();
-  EXPECT_TRUE(immediate_schedule_callback_called());
-
-  EXPECT_FALSE(is_starting());
-
-  // OfflinerDoneCallback will not end up getting called with status SAVED,
-  // since we cancelled the event before it called offliner_->LoadAndSave().
-  EXPECT_EQ(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED,
-            last_offlining_status());
-
-  // Since offliner was not started, it will not have seen cancel call.
-  EXPECT_FALSE(OfflinerWasCanceled());
-}
-
-// This tests a StopProcessing call after the prerenderer has been started.
-TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingLater) {
-  // Add a request to the queue, wait for callbacks to finish.
-  AddRequest1();
-  PumpLoop();
-
-  // Ensure the start processing request stops before the completion callback.
-  EnableOfflinerCallback(false);
-
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-  EXPECT_TRUE(is_starting());
-
-  // Let all the async parts of the start processing pipeline run to completion.
-  PumpLoop();
-
-  // Observer called for starting processing.
-  EXPECT_TRUE(observer().changed_called());
-  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
-  observer().Clear();
-
-  // Since the offliner is disabled, this callback should not be called.
-  EXPECT_FALSE(immediate_schedule_callback_called());
-
-  // Coordinator should now be busy.
-  EXPECT_TRUE(is_busy());
-  EXPECT_FALSE(is_starting());
-
-  // Now we cancel it while the prerenderer is busy.
-  coordinator()->StopProcessing(Offliner::REQUEST_COORDINATOR_CANCELED);
-
-  // Let the async callbacks in the cancel run.
-  PumpLoop();
-
-  // Observer called for stopped processing.
-  EXPECT_TRUE(observer().changed_called());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
-  observer().Clear();
-
-  EXPECT_FALSE(is_busy());
-
-  // OfflinerDoneCallback will not end up getting called with status SAVED,
-  // since we cancelled the event before the LoadAndSave completed.
-  EXPECT_EQ(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED,
-            last_offlining_status());
-
-  // Since offliner was started, it will have seen cancel call.
-  EXPECT_TRUE(OfflinerWasCanceled());
-}
-
-// This tests that canceling a request will result in TryNextRequest() getting
-// called.
-TEST_F(RequestCoordinatorTest, RemoveInflightRequest) {
-  // Add a request to the queue, wait for callbacks to finish.
-  AddRequest1();
-  PumpLoop();
-
-  // Ensure the start processing request stops before the completion callback.
-  EnableOfflinerCallback(false);
-
-  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
-                                             immediate_callback()));
-
-  // Let all the async parts of the start processing pipeline run to completion.
-  PumpLoop();
-  // Since the offliner is disabled, this callback should not be called.
-  EXPECT_FALSE(immediate_schedule_callback_called());
-
-  // Remove the request while it is processing.
-  std::vector<int64_t> request_ids{kRequestId1};
-  coordinator()->RemoveRequests(
-      request_ids, base::Bind(&RequestCoordinatorTest::RemoveRequestsDone,
-                              base::Unretained(this)));
-
-  // Let the async callbacks in the cancel run.
-  PumpLoop();
-
-  // Since offliner was started, it will have seen cancel call.
-  EXPECT_TRUE(OfflinerWasCanceled());
-}
-
-TEST_F(RequestCoordinatorTest, MarkRequestCompleted) {
-  int64_t request_id = coordinator()->SavePageLater(
-      kUrl1, kClientId1, kUserRequested,
-      RequestCoordinator::RequestAvailability::DISABLED_FOR_OFFLINER);
-  PumpLoop();
-  EXPECT_NE(request_id, 0l);
-
-  // Verify request added in OFFLINING state.
-  EXPECT_TRUE(observer().added_called());
-  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
-
-  // Call the method under test, making sure we send SUCCESS to the observer.
-  coordinator()->MarkRequestCompleted(request_id);
-  PumpLoop();
-
-  // Our observer should have seen SUCCESS instead of REMOVED.
-  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::SUCCESS,
-            observer().last_status());
-  EXPECT_TRUE(observer().completed_called());
-}
-
-TEST_F(RequestCoordinatorTest, EnableForOffliner) {
-  // Pretend we are on low-end device so immediate start won't happen.
-  SetIsLowEndDeviceForTest(true);
-
-  int64_t request_id = coordinator()->SavePageLater(
-      kUrl1, kClientId1, kUserRequested,
-      RequestCoordinator::RequestAvailability::DISABLED_FOR_OFFLINER);
-  PumpLoop();
-  EXPECT_NE(request_id, 0l);
-
-  // Verify request added and initial change to OFFLINING (in foreground).
-  EXPECT_TRUE(observer().added_called());
-  EXPECT_TRUE(observer().changed_called());
-  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
-  observer().Clear();
-
-  // Ensure that the new request does not finish so we can verify state change.
-  EnableOfflinerCallback(false);
-
-  coordinator()->EnableForOffliner(request_id, kClientId1);
-  PumpLoop();
-
-  // Verify request changed again.
-  EXPECT_TRUE(observer().changed_called());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
-}
-
-TEST_F(RequestCoordinatorTest, WatchdogTimeoutForScheduledProcessing) {
-  // Build a request to use with the pre-renderer, and put it on the queue.
-  offline_pages::SavePageRequest request(
-      kRequestId1, kUrl1, kClientId1, base::Time::Now(), kUserRequested);
-  // Set request to allow one more completed attempt.
-  int max_tries = coordinator()->policy()->GetMaxCompletedTries();
-  request.set_completed_attempt_count(max_tries - 1);
-  coordinator()->queue()->AddRequest(
-      request,
-      base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  // Ensure that the new request does not finish - we simulate it being
-  // in progress by asking it to skip making the completion callback.
-  EnableOfflinerCallback(false);
-
-  // Sending the request to the offliner.
-  EXPECT_TRUE(
-      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
-  PumpLoop();
-
-  // Advance the mock clock far enough to cause a watchdog timeout
-  AdvanceClockBy(base::TimeDelta::FromSeconds(
-      coordinator()
-          ->policy()
-          ->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() + 1));
-  PumpLoop();
-
-  // Wait for timeout to expire.  Use a TaskRunner with a DelayedTaskRunner
-  // which won't time out immediately, so the watchdog thread doesn't kill valid
-  // tasks too soon.
-  WaitForCallback();
-  PumpLoop();
-
-  EXPECT_FALSE(is_starting());
-  EXPECT_FALSE(coordinator()->is_busy());
-  EXPECT_TRUE(OfflinerWasCanceled());
-}
-
-TEST_F(RequestCoordinatorTest, WatchdogTimeoutForImmediateProcessing) {
-  // If low end device, pretend it is not so that immediate start happens.
-  SetIsLowEndDeviceForTest(false);
-
-  // Ensure that the new request does not finish - we simulate it being
-  // in progress by asking it to skip making the completion callback.
-  EnableOfflinerCallback(false);
-
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-  PumpLoop();
-
-  // Verify that immediate start from adding the request did happen.
-  EXPECT_TRUE(coordinator()->is_busy());
-
-  // Advance the mock clock 1 second before the watchdog timeout.
-  AdvanceClockBy(base::TimeDelta::FromSeconds(
-      coordinator()
-          ->policy()
-          ->GetSinglePageTimeLimitForImmediateLoadInSeconds() - 1));
-  PumpLoop();
-
-  // Verify still busy.
-  EXPECT_TRUE(coordinator()->is_busy());
-  EXPECT_FALSE(OfflinerWasCanceled());
-
-  // Advance the mock clock past the watchdog timeout now.
-  AdvanceClockBy(base::TimeDelta::FromSeconds(2));
-  PumpLoop();
-
-  // Verify the request timed out.
-  EXPECT_TRUE(OfflinerWasCanceled());
-}
-
-TEST_F(RequestCoordinatorTest, TimeBudgetExceeded) {
-  EnableOfflinerCallback(false);
-  // Build two requests to use with the pre-renderer, and put it on the queue.
-  AddRequest1();
-  // The second request will have a larger completed attempt count.
-  offline_pages::SavePageRequest request2(kRequestId1 + 1, kUrl1, kClientId1,
-                                          base::Time::Now(), kUserRequested);
-  request2.set_completed_attempt_count(kAttemptCount);
-  coordinator()->queue()->AddRequest(
-      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
-                           base::Unretained(this)));
-  PumpLoop();
-
-  // Sending the request to the offliner.
-  EXPECT_TRUE(
-      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
-  PumpLoop();
-
-  // Advance the mock clock far enough to exceed our time budget.
-  // The first request will time out, and because we are over time budget,
-  // the second request will not be started.
-  AdvanceClockBy(base::TimeDelta::FromSeconds(kTestTimeBudgetSeconds));
-  PumpLoop();
-
-  // TryNextRequest should decide that there is no more work to be done,
-  // and call back to the scheduler, even though there is another request in the
-  // queue.  Both requests should be left in the queue.
-  coordinator()->queue()->GetRequests(
-      base::Bind(&RequestCoordinatorTest::GetRequestsDone,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  // We should find two requests in the queue.
-  // The first request should now have a completed count of 1.
-  EXPECT_EQ(2UL, last_requests().size());
-  EXPECT_EQ(1L, last_requests().at(0)->completed_attempt_count());
-}
-
-TEST_F(RequestCoordinatorTest, TryNextRequestWithNoNetwork) {
-  SavePageRequest request1 = AddRequest1();
-  AddRequest2();
-  PumpLoop();
-
-  // Set up for the call to StartProcessing.
-  EnableOfflinerCallback(false);
-
-  // Sending the request to the offliner.
-  EXPECT_TRUE(
-      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
-  PumpLoop();
-  EXPECT_TRUE(coordinator()->is_busy());
-
-  // Now lose the network connection.
-  SetNetworkConnected(false);
-
-  // Complete first request and then TryNextRequest should decide not
-  // to pick another request (because of no network connection).
-  SendOfflinerDoneCallback(request1, Offliner::RequestStatus::SAVED);
-  PumpLoop();
-
-  // Not starting nor busy with next request.
-  EXPECT_FALSE(coordinator()->is_starting());
-  EXPECT_FALSE(coordinator()->is_busy());
-
-  // Get queued requests.
-  coordinator()->queue()->GetRequests(base::Bind(
-      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // We should find one request in the queue.
-  EXPECT_EQ(1UL, last_requests().size());
-}
-
-TEST_F(RequestCoordinatorTest, GetAllRequests) {
-  // Add two requests to the queue.
-  AddRequest1();
-  AddRequest2();
-  PumpLoop();
-
-  // Start the async status fetching.
-  coordinator()->GetAllRequests(base::Bind(
-      &RequestCoordinatorTest::GetQueuedRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Wait for async get to finish.
-  WaitForCallback();
-  PumpLoop();
-
-  // Check that the statuses found in the callback match what we expect.
-  EXPECT_EQ(2UL, last_requests().size());
-  EXPECT_EQ(kRequestId1, last_requests().at(0)->request_id());
-  EXPECT_EQ(kRequestId2, last_requests().at(1)->request_id());
-}
-
-#if defined(OS_IOS)
-// Flaky on IOS. http://crbug/663311
-#define MAYBE_PauseAndResumeObserver DISABLED_PauseAndResumeObserver
-#else
-#define MAYBE_PauseAndResumeObserver PauseAndResumeObserver
-#endif
-TEST_F(RequestCoordinatorTest, MAYBE_PauseAndResumeObserver) {
-  // Add a request to the queue.
-  AddRequest1();
-  PumpLoop();
-
-  // Pause the request.
-  std::vector<int64_t> request_ids;
-  request_ids.push_back(kRequestId1);
-  coordinator()->PauseRequests(request_ids);
-  PumpLoop();
-
-  EXPECT_TRUE(observer().changed_called());
-  EXPECT_EQ(SavePageRequest::RequestState::PAUSED, observer().state());
-
-  // Clear out the observer before the next call.
-  observer().Clear();
-
-  // Resume the request.
-  coordinator()->ResumeRequests(request_ids);
-  PumpLoop();
-
-  EXPECT_TRUE(observer().changed_called());
-
-  // Now whether request is offlining or just available depends on whether test
-  // is run on svelte device or not.
-  if (base::SysInfo::IsLowEndDevice()) {
-    EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
-  } else {
-    EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
-  }
-}
-
-TEST_F(RequestCoordinatorTest, RemoveRequest) {
-  // Add a request to the queue.
-  AddRequest1();
-  PumpLoop();
-
-  // Remove the request.
-  std::vector<int64_t> request_ids;
-  request_ids.push_back(kRequestId1);
-  coordinator()->RemoveRequests(
-      request_ids, base::Bind(&RequestCoordinatorTest::RemoveRequestsDone,
-                              base::Unretained(this)));
-
-  PumpLoop();
-  WaitForCallback();
-  PumpLoop();
-
-  EXPECT_TRUE(observer().completed_called());
-  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::REMOVED,
-            observer().last_status());
-  EXPECT_EQ(1UL, last_remove_results().size());
-  EXPECT_EQ(kRequestId1, std::get<0>(last_remove_results().at(0)));
-}
-
-TEST_F(RequestCoordinatorTest,
-       SavePageStartsProcessingWhenConnectedAndNotLowEndDevice) {
-  // Turn off the callback so that the request stops before processing in
-  // PumpLoop.
-  EnableOfflinerCallback(false);
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-  PumpLoop();
-
-  // Now whether processing triggered immediately depends on whether test
-  // is run on svelte device or not.
-  if (base::SysInfo::IsLowEndDevice()) {
-    EXPECT_FALSE(is_busy());
-  } else {
-    EXPECT_TRUE(is_busy());
-  }
-}
-
-TEST_F(RequestCoordinatorTest,
-       SavePageStartsProcessingWhenConnectedOnLowEndDeviceIfFlagEnabled) {
-  // Mark device as low-end device.
-  SetIsLowEndDeviceForTest(true);
-  EXPECT_FALSE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
-
-  // Make a request.
-  EXPECT_NE(coordinator()->SavePageLater(
-                kUrl1, kClientId1, kUserRequested,
-                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
-            0);
-  PumpLoop();
-
-  // Verify not immediately busy (since low-end device).
-  EXPECT_FALSE(is_busy());
-
-  // Set feature flag to allow concurrent loads.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      kOfflinePagesSvelteConcurrentLoadingFeature);
-  EXPECT_TRUE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
-
-  // Turn off the callback so that the request stops before processing in
-  // PumpLoop.
-  EnableOfflinerCallback(false);
-
-  // Make another request.
-  EXPECT_NE(coordinator()->SavePageLater(
-                kUrl2, kClientId2, kUserRequested,
-                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
-            0);
-  PumpLoop();
-
-  // Verify immediate processing did start this time.
-  EXPECT_TRUE(is_busy());
-}
-
-TEST_F(RequestCoordinatorTest, SavePageDoesntStartProcessingWhenDisconnected) {
-  // If low end device, pretend it is not so that immediate start allowed.
-  SetIsLowEndDeviceForTest(false);
-
-  SetNetworkConnected(false);
-  EnableOfflinerCallback(false);
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-  PumpLoop();
-  EXPECT_FALSE(is_busy());
-
-  // Now connect network and verify processing starts.
-  SetNetworkConnected(true);
-  CallConnectionTypeObserver();
-  PumpLoop();
-  EXPECT_TRUE(is_busy());
-}
-
-TEST_F(RequestCoordinatorTest,
-       SavePageDoesStartProcessingWhenPoorlyConnected) {
-  // Set specific network type for 2G with poor effective connection.
-  SetNetworkConditionsForTest(
-      net::NetworkChangeNotifier::ConnectionType::CONNECTION_2G);
-  SetEffectiveConnectionTypeForTest(
-      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
-
-  // Turn off the callback so that the request stops before processing in
-  // PumpLoop.
-  EnableOfflinerCallback(false);
-
-  EXPECT_NE(
-      coordinator()->SavePageLater(
-          kUrl1, kClientId1, kUserRequested,
-          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
-  PumpLoop();
-  EXPECT_TRUE(is_busy());
-}
-
-TEST_F(RequestCoordinatorTest,
-       ResumeStartsProcessingWhenConnectedAndNotLowEndDevice) {
-  // Start unconnected.
-  SetNetworkConnected(false);
-
-  // Turn off the callback so that the request stops before processing in
-  // PumpLoop.
-  EnableOfflinerCallback(false);
-
-  // Add a request to the queue.
-  AddRequest1();
-  PumpLoop();
-  EXPECT_FALSE(is_busy());
-
-  // Pause the request.
-  std::vector<int64_t> request_ids;
-  request_ids.push_back(kRequestId1);
-  coordinator()->PauseRequests(request_ids);
-  PumpLoop();
-
-  // Resume the request while disconnected.
-  coordinator()->ResumeRequests(request_ids);
-  PumpLoop();
-  EXPECT_FALSE(is_busy());
-
-  // Pause the request again.
-  coordinator()->PauseRequests(request_ids);
-  PumpLoop();
-
-  // Now simulate reasonable connection.
-  SetNetworkConnected(true);
-
-  // Resume the request while connected.
-  coordinator()->ResumeRequests(request_ids);
-  EXPECT_FALSE(is_busy());
-  PumpLoop();
-
-  // Now whether processing triggered immediately depends on whether test
-  // is run on svelte device or not.
-  if (base::SysInfo::IsLowEndDevice()) {
-    EXPECT_FALSE(is_busy());
-  } else {
-    EXPECT_TRUE(is_busy());
-  }
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_notifier.h b/components/offline_pages/background/request_notifier.h
deleted file mode 100644
index 611eac3..0000000
--- a/components/offline_pages/background/request_notifier.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
-
-namespace offline_pages {
-
-class SavePageRequest;
-
-class RequestNotifier {
- public:
-  // Status to return for failed notifications.
-  // NOTE: for any changes to the enum, please also update related switch code
-  // in RequestCoordinatorEventLogger.
-  // GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages
-  enum class BackgroundSavePageResult {
-    SUCCESS,
-    PRERENDER_FAILURE,
-    PRERENDER_CANCELED,
-    FOREGROUND_CANCELED,
-    SAVE_FAILED,
-    EXPIRED,
-    RETRY_COUNT_EXCEEDED,
-    START_COUNT_EXCEEDED,
-    REMOVED,
-  };
-
-  virtual ~RequestNotifier() = default;
-
-  // Notifies observers that |request| has been added.
-  virtual void NotifyAdded(const SavePageRequest& request) = 0;
-
-  // Notifies observers that |request| has been completed with |status|.
-  virtual void NotifyCompleted(const SavePageRequest& request,
-                               BackgroundSavePageResult status) = 0;
-
-  // Notifies observers that |request| has been changed.
-  virtual void NotifyChanged(const SavePageRequest& request) = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_NOTIFIER_H_
diff --git a/components/offline_pages/background/request_queue.cc b/components/offline_pages/background/request_queue.cc
deleted file mode 100644
index aa43ed8..0000000
--- a/components/offline_pages/background/request_queue.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_queue.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/add_request_task.h"
-#include "components/offline_pages/background/change_requests_state_task.h"
-#include "components/offline_pages/background/get_requests_task.h"
-#include "components/offline_pages/background/initialize_store_task.h"
-#include "components/offline_pages/background/mark_attempt_aborted_task.h"
-#include "components/offline_pages/background/mark_attempt_completed_task.h"
-#include "components/offline_pages/background/mark_attempt_started_task.h"
-#include "components/offline_pages/background/pick_request_task.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/remove_requests_task.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-namespace {
-// Completes the get requests call.
-void GetRequestsDone(const RequestQueue::GetRequestsCallback& callback,
-                     bool success,
-                     std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  GetRequestsResult result =
-      success ? GetRequestsResult::SUCCESS : GetRequestsResult::STORE_FAILURE;
-  // TODO(fgorski): Filter out expired requests based on policy.
-  // This may trigger the purging if necessary.
-  // Also this may be turned into a method on the request queue or add a policy
-  // parameter in the process.
-  callback.Run(result, std::move(requests));
-}
-
-// Completes the add request call.
-void AddRequestDone(const RequestQueue::AddRequestCallback& callback,
-                    const SavePageRequest& request,
-                    ItemActionStatus status) {
-  AddRequestResult result;
-  switch (status) {
-    case ItemActionStatus::SUCCESS:
-      result = AddRequestResult::SUCCESS;
-      break;
-    case ItemActionStatus::ALREADY_EXISTS:
-      result = AddRequestResult::ALREADY_EXISTS;
-      break;
-    case ItemActionStatus::STORE_ERROR:
-      result = AddRequestResult::STORE_FAILURE;
-      break;
-    case ItemActionStatus::NOT_FOUND:
-    default:
-      NOTREACHED();
-      return;
-  }
-  callback.Run(result, request);
-}
-
-}  // namespace
-
-RequestQueue::RequestQueue(std::unique_ptr<RequestQueueStore> store)
-    : store_(std::move(store)), weak_ptr_factory_(this) {
-  Initialize();
-}
-
-RequestQueue::~RequestQueue() {}
-
-void RequestQueue::GetRequests(const GetRequestsCallback& callback) {
-  std::unique_ptr<Task> task(new GetRequestsTask(
-      store_.get(), base::Bind(&GetRequestsDone, callback)));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::AddRequest(const SavePageRequest& request,
-                              const AddRequestCallback& callback) {
-  // TODO(fgorski): check that request makes sense.
-  // TODO(fgorski): check that request does not violate policy.
-  std::unique_ptr<AddRequestTask> task(new AddRequestTask(
-      store_.get(), request, base::Bind(&AddRequestDone, callback, request)));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::RemoveRequests(const std::vector<int64_t>& request_ids,
-                                  const UpdateCallback& callback) {
-  std::unique_ptr<Task> task(
-      new RemoveRequestsTask(store_.get(), request_ids, callback));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::ChangeRequestsState(
-    const std::vector<int64_t>& request_ids,
-    const SavePageRequest::RequestState new_state,
-    const RequestQueue::UpdateCallback& callback) {
-  std::unique_ptr<Task> task(new ChangeRequestsStateTask(
-      store_.get(), request_ids, new_state, callback));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::MarkAttemptStarted(int64_t request_id,
-                                      const UpdateCallback& callback) {
-  std::unique_ptr<Task> task(
-      new MarkAttemptStartedTask(store_.get(), request_id, callback));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::MarkAttemptAborted(int64_t request_id,
-                                      const UpdateCallback& callback) {
-  std::unique_ptr<Task> task(
-      new MarkAttemptAbortedTask(store_.get(), request_id, callback));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::MarkAttemptCompleted(int64_t request_id,
-                                        const UpdateCallback& callback) {
-  std::unique_ptr<Task> task(
-      new MarkAttemptCompletedTask(store_.get(), request_id, callback));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::PickNextRequest(
-    PickRequestTask::RequestPickedCallback picked_callback,
-    PickRequestTask::RequestNotPickedCallback not_picked_callback,
-    PickRequestTask::RequestCountCallback request_count_callback,
-    DeviceConditions& conditions,
-    std::set<int64_t>& disabled_requests) {
-  // Using the PickerContext, create a picker task.
-  std::unique_ptr<Task> task(picker_factory_->CreatePickerTask(
-      store_.get(), picked_callback, not_picked_callback,
-      request_count_callback, conditions, disabled_requests));
-
-  // Queue up the picking task, it will call one of the callbacks when it
-  // completes.
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::Initialize() {
-  std::unique_ptr<Task> task(new InitializeStoreTask(
-      store_.get(), base::Bind(&RequestQueue::InitializeStoreDone,
-                               weak_ptr_factory_.GetWeakPtr())));
-  task_queue_.AddTask(std::move(task));
-}
-
-void RequestQueue::InitializeStoreDone(bool success) {
-  // TODO(fgorski): Result can be ignored for now. Report UMA in future.
-  // No need to pass the result up to RequestCoordinator.
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_queue.h b/components/offline_pages/background/request_queue.h
deleted file mode 100644
index 8aa142516..0000000
--- a/components/offline_pages/background/request_queue.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/pick_request_task.h"
-#include "components/offline_pages/background/pick_request_task_factory.h"
-#include "components/offline_pages/background/request_queue_results.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/core/task_queue.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_store_types.h"
-
-namespace offline_pages {
-
-class RequestQueueStore;
-class PickRequestTaskFactory;
-
-// Class responsible for managing save page requests.
-class RequestQueue {
- public:
-
-  // Callback used for |GetRequests|.
-  typedef base::Callback<void(GetRequestsResult,
-                              std::vector<std::unique_ptr<SavePageRequest>>)>
-      GetRequestsCallback;
-
-  // Callback used for |AddRequest|.
-  typedef base::Callback<void(AddRequestResult, const SavePageRequest& request)>
-      AddRequestCallback;
-
-  // Callback used by |ChangeRequestsState|.
-  typedef base::Callback<void(std::unique_ptr<UpdateRequestsResult>)>
-      UpdateCallback;
-
-  // Callback used by |UdpateRequest|.
-  typedef base::Callback<void(UpdateRequestResult)> UpdateRequestCallback;
-
-  explicit RequestQueue(std::unique_ptr<RequestQueueStore> store);
-  ~RequestQueue();
-
-  // Gets all of the active requests from the store. Calling this method may
-  // schedule purging of the request queue.
-  void GetRequests(const GetRequestsCallback& callback);
-
-  // Adds |request| to the request queue. Result is returned through |callback|.
-  // In case adding the request violates policy, the result will fail with
-  // appropriate result. Callback will also return a copy of a request with all
-  // fields set.
-  void AddRequest(const SavePageRequest& request,
-                  const AddRequestCallback& callback);
-
-  // Removes the requests matching the |request_ids|. Result is returned through
-  // |callback|.  If a request id cannot be removed, this will still remove the
-  // others.
-  void RemoveRequests(const std::vector<int64_t>& request_ids,
-                      const UpdateCallback& callback);
-
-  // Changes the state to |new_state| for requests matching the
-  // |request_ids|. Results are returned through |callback|.
-  void ChangeRequestsState(const std::vector<int64_t>& request_ids,
-                           const SavePageRequest::RequestState new_state,
-                           const UpdateCallback& callback);
-
-  // Marks attempt with |request_id| as started. Results are returned through
-  // |callback|.
-  void MarkAttemptStarted(int64_t request_id, const UpdateCallback& callback);
-
-  // Marks attempt with |request_id| as aborted. Results are returned through
-  // |callback|.
-  void MarkAttemptAborted(int64_t request_id, const UpdateCallback& callback);
-
-  // Marks attempt with |request_id| as completed. The attempt may have
-  // completed with either success or failure (not denoted here). Results
-  // are returned through |callback|.
-  void MarkAttemptCompleted(int64_t request_id, const UpdateCallback& callback);
-
-  // Make a task to pick the next request, and report our choice to the
-  // callbacks.
-  void PickNextRequest(
-      PickRequestTask::RequestPickedCallback picked_callback,
-      PickRequestTask::RequestNotPickedCallback not_picked_callback,
-      PickRequestTask::RequestCountCallback request_count_callback,
-      DeviceConditions& conditions,
-      std::set<int64_t>& disabled_requests);
-
-  // Takes ownership of the factory.  We use a setter to allow users of the
-  // request queue to not need a PickerFactory to create it, since we have lots
-  // of code using the request queue.  The request coordinator will set a
-  // factory before calling PickNextRequest.
-  void SetPickerFactory(std::unique_ptr<PickRequestTaskFactory> factory) {
-    picker_factory_ = std::move(factory);
-  }
-
- private:
-  // Store initialization functions.
-  void Initialize();
-  void InitializeStoreDone(bool success);
-
-  std::unique_ptr<RequestQueueStore> store_;
-
-  // Task queue to serialize store access.
-  TaskQueue task_queue_;
-
-  // Builds PickRequestTask objects.
-  std::unique_ptr<PickRequestTaskFactory> picker_factory_;
-
-  // Allows us to pass a weak pointer to callbacks.
-  base::WeakPtrFactory<RequestQueue> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(RequestQueue);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_H_
diff --git a/components/offline_pages/background/request_queue_in_memory_store.cc b/components/offline_pages/background/request_queue_in_memory_store.cc
deleted file mode 100644
index 780542f3..0000000
--- a/components/offline_pages/background/request_queue_in_memory_store.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-
-#include <unordered_set>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-RequestQueueInMemoryStore::RequestQueueInMemoryStore()
-    : state_(StoreState::NOT_LOADED), scenario_(TestScenario::SUCCESSFUL) {}
-
-RequestQueueInMemoryStore::RequestQueueInMemoryStore(TestScenario scenario)
-    : state_(StoreState::NOT_LOADED), scenario_(scenario) {}
-
-RequestQueueInMemoryStore::~RequestQueueInMemoryStore() {}
-
-void RequestQueueInMemoryStore::Initialize(const InitializeCallback& callback) {
-  if (scenario_ == TestScenario::SUCCESSFUL)
-    state_ = StoreState::LOADED;
-  else
-    state_ = StoreState::FAILED_LOADING;
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, state_ == StoreState::LOADED));
-}
-
-void RequestQueueInMemoryStore::GetRequests(
-    const GetRequestsCallback& callback) {
-  DCHECK_NE(state_, StoreState::NOT_LOADED);
-  std::vector<std::unique_ptr<SavePageRequest>> result_requests;
-  for (const auto& id_request_pair : requests_) {
-    std::unique_ptr<SavePageRequest> request(
-        new SavePageRequest(id_request_pair.second));
-    result_requests.push_back(std::move(request));
-  }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(callback, true, base::Passed(std::move(result_requests))));
-}
-
-void RequestQueueInMemoryStore::GetRequestsByIds(
-    const std::vector<int64_t>& request_ids,
-    const UpdateCallback& callback) {
-  DCHECK_NE(state_, StoreState::NOT_LOADED);
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(state()));
-
-  ItemActionStatus status;
-  // Make sure not to include the same request multiple times, while preserving
-  // the order of non-duplicated IDs in the result.
-  std::unordered_set<int64_t> processed_ids;
-  for (const auto& request_id : request_ids) {
-    if (!processed_ids.insert(request_id).second)
-      continue;
-    RequestsMap::iterator iter = requests_.find(request_id);
-    if (iter != requests_.end()) {
-      status = ItemActionStatus::SUCCESS;
-      result->updated_items.push_back(iter->second);
-    } else {
-      status = ItemActionStatus::NOT_FOUND;
-    }
-    result->item_statuses.push_back(std::make_pair(request_id, status));
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void RequestQueueInMemoryStore::AddRequest(const SavePageRequest& request,
-                                           const AddCallback& callback) {
-  DCHECK_NE(state_, StoreState::NOT_LOADED);
-  RequestsMap::iterator iter = requests_.find(request.request_id());
-  ItemActionStatus status;
-  if (iter == requests_.end()) {
-    requests_.insert(iter, std::make_pair(request.request_id(), request));
-    status = ItemActionStatus::SUCCESS;
-  } else {
-    status = ItemActionStatus::ALREADY_EXISTS;
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, status));
-}
-
-void RequestQueueInMemoryStore::UpdateRequests(
-    const std::vector<SavePageRequest>& requests,
-    const RequestQueue::UpdateCallback& callback) {
-  DCHECK_NE(state_, StoreState::NOT_LOADED);
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(state()));
-
-  ItemActionStatus status;
-  for (const auto& request : requests) {
-    RequestsMap::iterator iter = requests_.find(request.request_id());
-    if (iter != requests_.end()) {
-      status = ItemActionStatus::SUCCESS;
-      iter->second = request;
-      result->updated_items.push_back(request);
-    } else {
-      status = ItemActionStatus::NOT_FOUND;
-    }
-    result->item_statuses.push_back(
-        std::make_pair(request.request_id(), status));
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void RequestQueueInMemoryStore::RemoveRequests(
-    const std::vector<int64_t>& request_ids,
-    const UpdateCallback& callback) {
-  DCHECK_NE(state_, StoreState::NOT_LOADED);
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(StoreState::LOADED));
-
-  ItemActionStatus status;
-  // If we find a request, mark it as succeeded, and put it in the request list.
-  // Otherwise mark it as failed.
-  for (auto request_id : request_ids) {
-    RequestsMap::iterator iter = requests_.find(request_id);
-    if (iter != requests_.end()) {
-      status = ItemActionStatus::SUCCESS;
-      result->updated_items.push_back(iter->second);
-      requests_.erase(iter);
-    } else {
-      status = ItemActionStatus::NOT_FOUND;
-    }
-    result->item_statuses.push_back(std::make_pair(request_id, status));
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void RequestQueueInMemoryStore::Reset(const ResetCallback& callback) {
-  if (scenario_ != TestScenario::LOAD_FAILED_RESET_FAILED) {
-    requests_.clear();
-    state_ = StoreState::NOT_LOADED;
-    scenario_ = TestScenario::SUCCESSFUL;
-  } else {
-    state_ = StoreState::FAILED_RESET;
-  }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, state_ == StoreState::NOT_LOADED));
-}
-
-StoreState RequestQueueInMemoryStore::state() const {
-  return state_;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_queue_in_memory_store.h b/components/offline_pages/background/request_queue_in_memory_store.h
deleted file mode 100644
index 92625bd..0000000
--- a/components/offline_pages/background/request_queue_in_memory_store.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
-
-#include <stdint.h>
-
-#include <map>
-
-#include "base/macros.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-// Interface for classes storing save page requests.
-class RequestQueueInMemoryStore : public RequestQueueStore {
- public:
-  enum class TestScenario {
-    SUCCESSFUL,
-    LOAD_FAILED_RESET_SUCCESS,
-    LOAD_FAILED_RESET_FAILED,
-  };
-
-  RequestQueueInMemoryStore();
-  explicit RequestQueueInMemoryStore(TestScenario scenario);
-  ~RequestQueueInMemoryStore() override;
-
-  // RequestQueueStore implementaiton.
-  void Initialize(const InitializeCallback& callback) override;
-  void GetRequests(const GetRequestsCallback& callback) override;
-  void GetRequestsByIds(const std::vector<int64_t>& request_ids,
-                        const UpdateCallback& callback) override;
-  void AddRequest(const SavePageRequest& offline_page,
-                  const AddCallback& callback) override;
-  void UpdateRequests(const std::vector<SavePageRequest>& requests,
-                      const UpdateCallback& callback) override;
-  void RemoveRequests(const std::vector<int64_t>& request_ids,
-                      const UpdateCallback& callback) override;
-  void Reset(const ResetCallback& callback) override;
-  StoreState state() const override;
-
- private:
-  typedef std::map<int64_t, SavePageRequest> RequestsMap;
-  RequestsMap requests_;
-  StoreState state_;
-  TestScenario scenario_;
-
-  DISALLOW_COPY_AND_ASSIGN(RequestQueueInMemoryStore);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
diff --git a/components/offline_pages/background/request_queue_results.h b/components/offline_pages/background/request_queue_results.h
deleted file mode 100644
index ae5326c..0000000
--- a/components/offline_pages/background/request_queue_results.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
-
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_store_types.h"
-
-namespace offline_pages {
-
-// Extracted from RequestQueue so that we can build types that use these results
-// that RequestQueue depends on (for example, the PickRequestTask).
-typedef StoreUpdateResult<SavePageRequest> UpdateRequestsResult;
-
-enum class GetRequestsResult {
-  SUCCESS,
-  STORE_FAILURE,
-};
-
-enum class AddRequestResult {
-  SUCCESS,
-  STORE_FAILURE,
-  ALREADY_EXISTS,
-  REQUEST_QUOTA_HIT,  // Cannot add a request with this namespace, as it has
-                      // reached a quota of active requests.
-};
-
-// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages.background
-enum class UpdateRequestResult {
-  SUCCESS,
-  STORE_FAILURE,
-  REQUEST_DOES_NOT_EXIST,  // Failed to delete the request because it does not
-                           // exist.
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
diff --git a/components/offline_pages/background/request_queue_store.h b/components/offline_pages/background/request_queue_store.h
deleted file mode 100644
index 723ff8b..0000000
--- a/components/offline_pages/background/request_queue_store.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
-
-#include <stdint.h>
-#include <vector>
-
-#include "base/callback.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_store_types.h"
-
-namespace offline_pages {
-
-// Interface for classes storing save page requests.
-class RequestQueueStore {
- public:
-  enum class UpdateStatus {
-    ADDED,    // Request was added successfully.
-    UPDATED,  // Request was updated successfully.
-    FAILED,   // Add or update attempt failed.
-  };
-
-  using UpdateCallback = RequestQueue::UpdateCallback;
-
-  typedef base::Callback<void(bool /* success */)> InitializeCallback;
-  typedef base::Callback<void(bool /* success */)> ResetCallback;
-  typedef base::Callback<void(
-      bool /* success */,
-      std::vector<std::unique_ptr<SavePageRequest>> /* requests */)>
-      GetRequestsCallback;
-  typedef base::Callback<void(ItemActionStatus)> AddCallback;
-
-  virtual ~RequestQueueStore(){};
-
-  // Initializes the store. Should be called before any other methods.
-  virtual void Initialize(const InitializeCallback& callback) = 0;
-
-  // Gets all of the requests from the store.
-  virtual void GetRequests(const GetRequestsCallback& callback) = 0;
-
-  // Gets requests with specified IDs from the store. UpdateCallback is used
-  // instead of GetRequestsCallback to indicate which requests where not found.
-  virtual void GetRequestsByIds(const std::vector<int64_t>& request_ids,
-                                const UpdateCallback& callback) = 0;
-
-  // Asynchronously adds request in store. Fails if request with the same
-  // offline ID already exists.
-  virtual void AddRequest(const SavePageRequest& offline_page,
-                          const AddCallback& callback) = 0;
-
-  // Asynchronously updates requests in store.
-  virtual void UpdateRequests(const std::vector<SavePageRequest>& requests,
-                              const UpdateCallback& callback) = 0;
-
-  // Asynchronously removes requests from the store using their IDs.
-  // Result of the update, and a number of removed pages is passed in the
-  // callback.
-  // Result of remove should be false, when one of the provided items couldn't
-  // be deleted, e.g. because it was missing.
-  virtual void RemoveRequests(const std::vector<int64_t>& request_ids,
-                              const UpdateCallback& callback) = 0;
-
-  // Resets the store (removes any existing data).
-  virtual void Reset(const ResetCallback& callback) = 0;
-
-  // Gets the store state.
-  virtual StoreState state() const = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_H_
diff --git a/components/offline_pages/background/request_queue_store_sql.cc b/components/offline_pages/background/request_queue_store_sql.cc
deleted file mode 100644
index 5716273..0000000
--- a/components/offline_pages/background/request_queue_store_sql.cc
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_queue_store_sql.h"
-
-#include <unordered_set>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/sequenced_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-#include "sql/transaction.h"
-
-namespace offline_pages {
-
-template class StoreUpdateResult<SavePageRequest>;
-
-namespace {
-
-using SuccessCallback = base::Callback<void(bool)>;
-
-// This is a macro instead of a const so that
-// it can be used inline in other SQL statements below.
-#define REQUEST_QUEUE_TABLE_NAME "request_queue_v1"
-const bool kUserRequested = true;
-
-bool CreateRequestQueueTable(sql::Connection* db) {
-  const char kSql[] = "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME
-                      " (request_id INTEGER PRIMARY KEY NOT NULL,"
-                      " creation_time INTEGER NOT NULL,"
-                      " activation_time INTEGER NOT NULL DEFAULT 0,"
-                      " last_attempt_time INTEGER NOT NULL DEFAULT 0,"
-                      " started_attempt_count INTEGER NOT NULL,"
-                      " completed_attempt_count INTEGER NOT NULL,"
-                      " state INTEGER NOT NULL DEFAULT 0,"
-                      " url VARCHAR NOT NULL,"
-                      " client_namespace VARCHAR NOT NULL,"
-                      " client_id VARCHAR NOT NULL"
-                      ")";
-  return db->Execute(kSql);
-}
-
-bool CreateSchema(sql::Connection* db) {
-  // If there is not already a state column, we need to drop the old table.  We
-  // are choosing to drop instead of upgrade since the feature is not yet
-  // released, so we don't use a transaction to protect existing data, or try to
-  // migrate it.
-  if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "state")) {
-    if (!db->Execute("DROP TABLE IF EXISTS " REQUEST_QUEUE_TABLE_NAME))
-      return false;
-  }
-
-  if (!CreateRequestQueueTable(db))
-    return false;
-
-  // TODO(fgorski): Add indices here.
-  return true;
-}
-
-// Create a save page request from a SQL result.  Expects complete rows with
-// all columns present.  Columns are in order they are defined in select query
-// in |GetOneRequest| method.
-std::unique_ptr<SavePageRequest> MakeSavePageRequest(
-    const sql::Statement& statement) {
-  const int64_t id = statement.ColumnInt64(0);
-  const base::Time creation_time =
-      base::Time::FromInternalValue(statement.ColumnInt64(1));
-  const base::Time activation_time =
-      base::Time::FromInternalValue(statement.ColumnInt64(2));
-  const base::Time last_attempt_time =
-      base::Time::FromInternalValue(statement.ColumnInt64(3));
-  const int64_t started_attempt_count = statement.ColumnInt64(4);
-  const int64_t completed_attempt_count = statement.ColumnInt64(5);
-  const SavePageRequest::RequestState state =
-      static_cast<SavePageRequest::RequestState>(statement.ColumnInt64(6));
-  const GURL url(statement.ColumnString(7));
-  const ClientId client_id(statement.ColumnString(8),
-                           statement.ColumnString(9));
-
-  DVLOG(2) << "making save page request - id " << id << " url " << url
-           << " client_id " << client_id.name_space << "-" << client_id.id
-           << " creation time " << creation_time << " user requested "
-           << kUserRequested;
-
-  std::unique_ptr<SavePageRequest> request(new SavePageRequest(
-      id, url, client_id, creation_time, activation_time, kUserRequested));
-  request->set_last_attempt_time(last_attempt_time);
-  request->set_started_attempt_count(started_attempt_count);
-  request->set_completed_attempt_count(completed_attempt_count);
-  request->set_request_state(state);
-  return request;
-}
-
-// Get a request for a specific id.
-std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
-                                               const int64_t request_id) {
-  const char kSql[] =
-      "SELECT request_id, creation_time, activation_time,"
-      " last_attempt_time, started_attempt_count, completed_attempt_count,"
-      " state, url, client_namespace, client_id"
-      " FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, request_id);
-
-  if (statement.Step())
-    return MakeSavePageRequest(statement);
-  return std::unique_ptr<SavePageRequest>(nullptr);
-}
-
-ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) {
-  const char kSql[] =
-      "DELETE FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, request_id);
-  if (!statement.Run())
-    return ItemActionStatus::STORE_ERROR;
-  else if (db->GetLastChangeCount() == 0)
-    return ItemActionStatus::NOT_FOUND;
-  return ItemActionStatus::SUCCESS;
-}
-
-ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
-  const char kSql[] =
-      "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
-      " (request_id, creation_time, activation_time,"
-      " last_attempt_time, started_attempt_count, completed_attempt_count,"
-      " state, url, client_namespace, client_id)"
-      " VALUES "
-      " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, request.request_id());
-  statement.BindInt64(1, request.creation_time().ToInternalValue());
-  statement.BindInt64(2, request.activation_time().ToInternalValue());
-  statement.BindInt64(3, request.last_attempt_time().ToInternalValue());
-  statement.BindInt64(4, request.started_attempt_count());
-  statement.BindInt64(5, request.completed_attempt_count());
-  statement.BindInt64(6, static_cast<int64_t>(request.request_state()));
-  statement.BindString(7, request.url().spec());
-  statement.BindString(8, request.client_id().name_space);
-  statement.BindString(9, request.client_id().id);
-
-  if (!statement.Run())
-    return ItemActionStatus::STORE_ERROR;
-  if (db->GetLastChangeCount() == 0)
-    return ItemActionStatus::ALREADY_EXISTS;
-  return ItemActionStatus::SUCCESS;
-}
-
-ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
-  const char kSql[] =
-      "UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME
-      " SET creation_time = ?, activation_time = ?, last_attempt_time = ?,"
-      " started_attempt_count = ?, completed_attempt_count = ?, state = ?,"
-      " url = ?, client_namespace = ?, client_id = ?"
-      " WHERE request_id = ?";
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, request.creation_time().ToInternalValue());
-  statement.BindInt64(1, request.activation_time().ToInternalValue());
-  statement.BindInt64(2, request.last_attempt_time().ToInternalValue());
-  statement.BindInt64(3, request.started_attempt_count());
-  statement.BindInt64(4, request.completed_attempt_count());
-  statement.BindInt64(5, static_cast<int64_t>(request.request_state()));
-  statement.BindString(6, request.url().spec());
-  statement.BindString(7, request.client_id().name_space);
-  statement.BindString(8, request.client_id().id);
-  statement.BindInt64(9, request.request_id());
-
-  if (!statement.Run())
-    return ItemActionStatus::STORE_ERROR;
-  if (db->GetLastChangeCount() == 0)
-    return ItemActionStatus::NOT_FOUND;
-  return ItemActionStatus::SUCCESS;
-}
-
-void PostStoreUpdateResultForIds(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    StoreState store_state,
-    const std::vector<int64_t>& item_ids,
-    ItemActionStatus action_status,
-    const RequestQueueStore::UpdateCallback& callback) {
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(store_state));
-  for (const auto& item_id : item_ids)
-    result->item_statuses.push_back(std::make_pair(item_id, action_status));
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void PostStoreErrorForAllRequests(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const std::vector<SavePageRequest>& items,
-    const RequestQueueStore::UpdateCallback& callback) {
-  std::vector<int64_t> item_ids;
-  for (const auto& item : items)
-    item_ids.push_back(item.request_id());
-  PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
-                              ItemActionStatus::STORE_ERROR, callback);
-}
-
-void PostStoreErrorForAllIds(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const std::vector<int64_t>& item_ids,
-    const RequestQueueStore::UpdateCallback& callback) {
-  PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
-                              ItemActionStatus::STORE_ERROR, callback);
-}
-
-bool InitDatabase(sql::Connection* db, const base::FilePath& path) {
-  db->set_page_size(4096);
-  db->set_cache_size(500);
-  db->set_histogram_tag("BackgroundRequestQueue");
-  db->set_exclusive_locking();
-
-  base::File::Error err;
-  if (!base::CreateDirectoryAndGetError(path.DirName(), &err))
-    return false;
-  if (!db->Open(path))
-    return false;
-  db->Preload();
-
-  return CreateSchema(db);
-}
-
-void GetRequestsSync(sql::Connection* db,
-                     scoped_refptr<base::SingleThreadTaskRunner> runner,
-                     const RequestQueueStore::GetRequestsCallback& callback) {
-  const char kSql[] =
-      "SELECT request_id, creation_time, activation_time,"
-      " last_attempt_time, started_attempt_count, completed_attempt_count,"
-      " state, url, client_namespace, client_id"
-      " FROM " REQUEST_QUEUE_TABLE_NAME;
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-
-  std::vector<std::unique_ptr<SavePageRequest>> requests;
-  while (statement.Step())
-    requests.push_back(MakeSavePageRequest(statement));
-
-  runner->PostTask(FROM_HERE, base::Bind(callback, statement.Succeeded(),
-                                         base::Passed(&requests)));
-}
-
-void GetRequestsByIdsSync(sql::Connection* db,
-                          scoped_refptr<base::SingleThreadTaskRunner> runner,
-                          const std::vector<int64_t>& request_ids,
-                          const RequestQueueStore::UpdateCallback& callback) {
-  // TODO(fgorski): Perhaps add metrics here.
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(StoreState::LOADED));
-
-  // If you create a transaction but don't Commit() it is automatically
-  // rolled back by its destructor when it falls out of scope.
-  sql::Transaction transaction(db);
-  if (!transaction.Begin()) {
-    PostStoreErrorForAllIds(runner, request_ids, callback);
-    return;
-  }
-
-  // Make sure not to include the same request multiple times, preserving the
-  // order of non-duplicated IDs in the result.
-  std::unordered_set<int64_t> processed_ids;
-  for (int64_t request_id : request_ids) {
-    if (!processed_ids.insert(request_id).second)
-      continue;
-    std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
-    if (request.get())
-      result->updated_items.push_back(*request);
-    ItemActionStatus status =
-        request.get() ? ItemActionStatus::SUCCESS : ItemActionStatus::NOT_FOUND;
-    result->item_statuses.push_back(std::make_pair(request_id, status));
-  }
-
-  if (!transaction.Commit()) {
-    PostStoreErrorForAllIds(runner, request_ids, callback);
-    return;
-  }
-
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void AddRequestSync(sql::Connection* db,
-                    scoped_refptr<base::SingleThreadTaskRunner> runner,
-                    const SavePageRequest& request,
-                    const RequestQueueStore::AddCallback& callback) {
-  // TODO(fgorski): add UMA metrics here.
-  ItemActionStatus status = Insert(db, request);
-  runner->PostTask(FROM_HERE, base::Bind(callback, status));
-}
-
-void UpdateRequestsSync(sql::Connection* db,
-                        scoped_refptr<base::SingleThreadTaskRunner> runner,
-                        const std::vector<SavePageRequest>& requests,
-                        const RequestQueueStore::UpdateCallback& callback) {
-  // TODO(fgorski): add UMA metrics here.
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(StoreState::LOADED));
-
-  sql::Transaction transaction(db);
-  if (!transaction.Begin()) {
-    PostStoreErrorForAllRequests(runner, requests, callback);
-    return;
-  }
-
-  for (const auto& request : requests) {
-    ItemActionStatus status = Update(db, request);
-    result->item_statuses.push_back(
-        std::make_pair(request.request_id(), status));
-    if (status == ItemActionStatus::SUCCESS)
-      result->updated_items.push_back(request);
-  }
-
-  if (!transaction.Commit()) {
-    PostStoreErrorForAllRequests(runner, requests, callback);
-    return;
-  }
-
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void RemoveRequestsSync(sql::Connection* db,
-                        scoped_refptr<base::SingleThreadTaskRunner> runner,
-                        const std::vector<int64_t>& request_ids,
-                        const RequestQueueStore::UpdateCallback& callback) {
-  // TODO(fgorski): Perhaps add metrics here.
-  std::unique_ptr<UpdateRequestsResult> result(
-      new UpdateRequestsResult(StoreState::LOADED));
-
-  // If you create a transaction but don't Commit() it is automatically
-  // rolled back by its destructor when it falls out of scope.
-  sql::Transaction transaction(db);
-  if (!transaction.Begin()) {
-    PostStoreErrorForAllIds(runner, request_ids, callback);
-    return;
-  }
-
-  // Read the request before we delete it, and if the delete worked, put it on
-  // the queue of requests that got deleted.
-  for (int64_t request_id : request_ids) {
-    std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
-    ItemActionStatus status = DeleteRequestById(db, request_id);
-    result->item_statuses.push_back(std::make_pair(request_id, status));
-    if (status == ItemActionStatus::SUCCESS)
-      result->updated_items.push_back(*request);
-  }
-
-  if (!transaction.Commit()) {
-    PostStoreErrorForAllIds(runner, request_ids, callback);
-    return;
-  }
-
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void OpenConnectionSync(sql::Connection* db,
-                        scoped_refptr<base::SingleThreadTaskRunner> runner,
-                        const base::FilePath& path,
-                        const SuccessCallback& callback) {
-  bool success = InitDatabase(db, path);
-  runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-
-void ResetSync(sql::Connection* db,
-               const base::FilePath& db_file_path,
-               scoped_refptr<base::SingleThreadTaskRunner> runner,
-               const SuccessCallback& callback) {
-  // This method deletes the content of the whole store and reinitializes it.
-  bool success = true;
-  if (db) {
-    success = db->Raze();
-    db->Close();
-  }
-  success = base::DeleteFile(db_file_path, true /* recursive */) && success;
-  runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-
-}  // anonymous namespace
-
-RequestQueueStoreSQL::RequestQueueStoreSQL(
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    const base::FilePath& path)
-    : background_task_runner_(std::move(background_task_runner)),
-      db_file_path_(path.AppendASCII("RequestQueue.db")),
-      state_(StoreState::NOT_LOADED),
-      weak_ptr_factory_(this) {}
-
-RequestQueueStoreSQL::~RequestQueueStoreSQL() {
-  if (db_.get())
-    background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
-}
-
-void RequestQueueStoreSQL::Initialize(const InitializeCallback& callback) {
-  DCHECK(!db_);
-  db_.reset(new sql::Connection());
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&OpenConnectionSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), db_file_path_,
-                 base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone,
-                            weak_ptr_factory_.GetWeakPtr(), callback)));
-}
-
-void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) {
-  DCHECK(db_.get());
-  if (!CheckDb()) {
-    std::vector<std::unique_ptr<SavePageRequest>> requests;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, false, base::Passed(&requests)));
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&GetRequestsSync, db_.get(),
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void RequestQueueStoreSQL::GetRequestsByIds(
-    const std::vector<int64_t>& request_ids,
-    const UpdateCallback& callback) {
-  if (!CheckDb()) {
-    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
-                            callback);
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&GetRequestsByIdsSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
-}
-
-void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request,
-                                      const AddCallback& callback) {
-  if (!CheckDb()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, ItemActionStatus::STORE_ERROR));
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&AddRequestSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), request, callback));
-}
-
-void RequestQueueStoreSQL::UpdateRequests(
-    const std::vector<SavePageRequest>& requests,
-    const UpdateCallback& callback) {
-  if (!CheckDb()) {
-    PostStoreErrorForAllRequests(base::ThreadTaskRunnerHandle::Get(), requests,
-                                 callback);
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateRequestsSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), requests, callback));
-}
-
-void RequestQueueStoreSQL::RemoveRequests(
-    const std::vector<int64_t>& request_ids,
-    const UpdateCallback& callback) {
-  if (!CheckDb()) {
-    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
-                            callback);
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&RemoveRequestsSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
-}
-
-void RequestQueueStoreSQL::Reset(const ResetCallback& callback) {
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&ResetSync, db_.get(), db_file_path_,
-                 base::ThreadTaskRunnerHandle::Get(),
-                 base::Bind(&RequestQueueStoreSQL::OnResetDone,
-                            weak_ptr_factory_.GetWeakPtr(), callback)));
-}
-
-StoreState RequestQueueStoreSQL::state() const {
-  return state_;
-}
-
-void RequestQueueStoreSQL::OnOpenConnectionDone(
-    const InitializeCallback& callback,
-    bool success) {
-  DCHECK(db_.get());
-  state_ = success ? StoreState::LOADED : StoreState::FAILED_LOADING;
-  callback.Run(success);
-}
-
-void RequestQueueStoreSQL::OnResetDone(const ResetCallback& callback,
-                                       bool success) {
-  state_ = success ? StoreState::NOT_LOADED : StoreState::FAILED_RESET;
-  db_.reset();
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, success));
-}
-
-bool RequestQueueStoreSQL::CheckDb() const {
-  return db_ && state_ == StoreState::LOADED;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/request_queue_store_sql.h b/components/offline_pages/background/request_queue_store_sql.h
deleted file mode 100644
index b733f46..0000000
--- a/components/offline_pages/background/request_queue_store_sql.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
-
-#include <stdint.h>
-#include <memory>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace sql {
-class Connection;
-}
-
-namespace offline_pages {
-
-// SQLite implementation of RequestQueueStore.
-class RequestQueueStoreSQL : public RequestQueueStore {
- public:
-  RequestQueueStoreSQL(
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      const base::FilePath& database_dir);
-  ~RequestQueueStoreSQL() override;
-
-  // RequestQueueStore implementation.
-  void Initialize(const InitializeCallback& callback) override;
-  void GetRequests(const GetRequestsCallback& callback) override;
-  // Note: current implementation of this method makes a SQL query per ID. This
-  // is OK as long as number of IDs stays low, which is a typical case.
-  // Implementation should be revisited in case that presumption changes.
-  void GetRequestsByIds(const std::vector<int64_t>& request_ids,
-                        const UpdateCallback& callback) override;
-  void AddRequest(const SavePageRequest& offline_page,
-                  const AddCallback& callback) override;
-  void UpdateRequests(const std::vector<SavePageRequest>& requests,
-                      const UpdateCallback& callback) override;
-  void RemoveRequests(const std::vector<int64_t>& request_ids,
-                      const UpdateCallback& callback) override;
-  void Reset(const ResetCallback& callback) override;
-  StoreState state() const override;
-
- private:
-  // Used to finalize DB connection initialization.
-  void OnOpenConnectionDone(const InitializeCallback& callback, bool success);
-
-  // Used to finalize DB connection reset.
-  void OnResetDone(const ResetCallback& callback, bool success);
-
-  // Helper function to return immediately if no database is found.
-  bool CheckDb() const;
-
-  // Background thread where all SQL access should be run.
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
-  // Path to the database on disk.
-  base::FilePath db_file_path_;
-
-  // Database connection.
-  std::unique_ptr<sql::Connection> db_;
-
-  // State of the store.
-  StoreState state_;
-
-  base::WeakPtrFactory<RequestQueueStoreSQL> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(RequestQueueStoreSQL);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
diff --git a/components/offline_pages/background/request_queue_store_unittest.cc b/components/offline_pages/background/request_queue_store_unittest.cc
deleted file mode 100644
index 8b376e0..0000000
--- a/components/offline_pages/background/request_queue_store_unittest.cc
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_queue_store.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/request_queue.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/request_queue_store_sql.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-using UpdateStatus = RequestQueueStore::UpdateStatus;
-
-namespace {
-const int64_t kRequestId = 42;
-const int64_t kRequestId2 = 44;
-const int64_t kRequestId3 = 47;
-const GURL kUrl("http://example.com");
-const GURL kUrl2("http://another-example.com");
-const ClientId kClientId("bookmark", "1234");
-const ClientId kClientId2("async", "5678");
-const bool kUserRequested = true;
-
-enum class LastResult {
-  RESULT_NONE,
-  RESULT_FALSE,
-  RESULT_TRUE,
-};
-
-}  // namespace
-
-// Class that serves as a base for testing different implementations of the
-// |RequestQueueStore|. Specific implementations extend the templatized version
-// of this class and provide appropriate store factory.
-class RequestQueueStoreTestBase : public testing::Test {
- public:
-  RequestQueueStoreTestBase();
-
-  // Test overrides.
-  void TearDown() override;
-
-  void PumpLoop();
-  void ClearResults();
-  void InitializeStore(RequestQueueStore* store);
-
-  void InitializeCallback(bool success);
-  // Callback used for get requests.
-  void GetRequestsDone(bool result,
-                       std::vector<std::unique_ptr<SavePageRequest>> requests);
-  // Callback used for add/update request.
-  void AddOrUpdateDone(UpdateStatus result);
-  void AddRequestDone(ItemActionStatus status);
-  void UpdateRequestDone(std::unique_ptr<UpdateRequestsResult> result);
-  // Callback used for reset.
-  void ResetDone(bool result);
-
-  LastResult last_result() const { return last_result_; }
-  UpdateStatus last_update_status() const { return last_update_status_; }
-  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
-    return last_requests_;
-  }
-  ItemActionStatus last_add_status() const { return last_add_status_; }
-
-  UpdateRequestsResult* last_update_result() const {
-    return last_update_result_.get();
-  }
-
- protected:
-  base::ScopedTempDir temp_directory_;
-
- private:
-  LastResult last_result_;
-  UpdateStatus last_update_status_;
-  ItemActionStatus last_add_status_;
-  std::unique_ptr<UpdateRequestsResult> last_update_result_;
-  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
-
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-RequestQueueStoreTestBase::RequestQueueStoreTestBase()
-    : last_result_(LastResult::RESULT_NONE),
-      last_update_status_(UpdateStatus::FAILED),
-      last_add_status_(ItemActionStatus::NOT_FOUND),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {
-  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
-}
-
-void RequestQueueStoreTestBase::TearDown() {
-  // Wait for all the pieces of the store to delete itself properly.
-  PumpLoop();
-}
-
-void RequestQueueStoreTestBase::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void RequestQueueStoreTestBase::ClearResults() {
-  last_result_ = LastResult::RESULT_NONE;
-  last_update_status_ = UpdateStatus::FAILED;
-  last_add_status_ = ItemActionStatus::NOT_FOUND;
-  last_requests_.clear();
-  last_update_result_.reset(nullptr);
-}
-
-void RequestQueueStoreTestBase::InitializeStore(RequestQueueStore* store) {
-  store->Initialize(base::Bind(&RequestQueueStoreTestBase::InitializeCallback,
-                               base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ClearResults();
-}
-
-void RequestQueueStoreTestBase::InitializeCallback(bool success) {
-  last_result_ = success ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
-}
-
-void RequestQueueStoreTestBase::GetRequestsDone(
-    bool result,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
-  last_requests_ = std::move(requests);
-}
-
-void RequestQueueStoreTestBase::AddOrUpdateDone(UpdateStatus status) {
-  last_update_status_ = status;
-}
-
-void RequestQueueStoreTestBase::AddRequestDone(ItemActionStatus status) {
-  last_add_status_ = status;
-}
-
-void RequestQueueStoreTestBase::UpdateRequestDone(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  last_update_result_ = std::move(result);
-}
-
-void RequestQueueStoreTestBase::ResetDone(bool result) {
-  last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
-}
-
-// Defines interface for the store factory.
-class RequestQueueStoreFactory {
- public:
-  virtual RequestQueueStore* BuildStore(const base::FilePath& path) = 0;
-};
-
-// Implements a store factory for in memory store.
-class RequestQueueInMemoryStoreFactory : public RequestQueueStoreFactory {
- public:
-  RequestQueueStore* BuildStore(const base::FilePath& path) override {
-    RequestQueueStore* store = new RequestQueueInMemoryStore();
-    return store;
-  }
-};
-
-// Implements a store factory for SQLite based implementation of the store.
-class RequestQueueStoreSQLFactory : public RequestQueueStoreFactory {
- public:
-  RequestQueueStore* BuildStore(const base::FilePath& path) override {
-    RequestQueueStore* store =
-        new RequestQueueStoreSQL(base::ThreadTaskRunnerHandle::Get(), path);
-    return store;
-  }
-};
-
-// Defines a store test fixture templatized by the store factory.
-template <typename T>
-class RequestQueueStoreTest : public RequestQueueStoreTestBase {
- public:
-  std::unique_ptr<RequestQueueStore> BuildStore();
-
- protected:
-  T factory_;
-};
-
-template <typename T>
-std::unique_ptr<RequestQueueStore> RequestQueueStoreTest<T>::BuildStore() {
-  std::unique_ptr<RequestQueueStore> store(
-      factory_.BuildStore(temp_directory_.GetPath()));
-  return store;
-}
-
-// |StoreTypes| lists all factories, based on which the tests will be created.
-typedef testing::Types<RequestQueueInMemoryStoreFactory,
-                       RequestQueueStoreSQLFactory>
-    StoreTypes;
-
-// This portion causes test fixtures to be defined.
-// Notice that in the store we are using "this->" to refer to the methods
-// defined on the |RequestQuieueStoreBaseTest| class. That's by design.
-TYPED_TEST_CASE(RequestQueueStoreTest, StoreTypes);
-
-TYPED_TEST(RequestQueueStoreTest, GetRequestsEmpty) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_TRUE(this->last_requests().empty());
-}
-
-TYPED_TEST(RequestQueueStoreTest, GetRequestsByIds) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
-                           kUserRequested);
-  store->AddRequest(request1,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  store->AddRequest(request2,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  this->PumpLoop();
-  this->ClearResults();
-
-  std::vector<int64_t> request_ids{kRequestId, kRequestId2};
-  store->GetRequestsByIds(
-      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
-                              base::Unretained(this)));
-
-  ASSERT_FALSE(this->last_update_result());
-  this->PumpLoop();
-  ASSERT_TRUE(this->last_update_result());
-  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[0].second);
-  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[1].second);
-  EXPECT_EQ(2UL, this->last_update_result()->updated_items.size());
-  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
-  EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1));
-  this->ClearResults();
-
-  request_ids.clear();
-  request_ids.push_back(kRequestId);
-  request_ids.push_back(kRequestId3);
-  request_ids.push_back(kRequestId);
-
-  store->GetRequestsByIds(
-      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
-                              base::Unretained(this)));
-
-  ASSERT_FALSE(this->last_update_result());
-  this->PumpLoop();
-  ASSERT_TRUE(this->last_update_result());
-  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[0].second);
-  EXPECT_EQ(kRequestId3, this->last_update_result()->item_statuses[1].first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            this->last_update_result()->item_statuses[1].second);
-  EXPECT_EQ(1UL, this->last_update_result()->updated_items.size());
-  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
-}
-
-TYPED_TEST(RequestQueueStoreTest, AddRequest) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-
-  store->AddRequest(request,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status());
-  this->PumpLoop();
-  ASSERT_EQ(ItemActionStatus::SUCCESS, this->last_add_status());
-
-  // Verifying get reqeust results after a request was added.
-  this->ClearResults();
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_EQ(1ul, this->last_requests().size());
-  ASSERT_EQ(request, *(this->last_requests()[0].get()));
-
-  // Verify it is not possible to add the same request twice.
-  this->ClearResults();
-  store->AddRequest(request,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status());
-  this->PumpLoop();
-  ASSERT_EQ(ItemActionStatus::ALREADY_EXISTS, this->last_add_status());
-
-  // Check that there is still only one item in the store.
-  this->ClearResults();
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_EQ(1ul, this->last_requests().size());
-}
-
-TYPED_TEST(RequestQueueStoreTest, UpdateRequest) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest original_request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  store->AddRequest(original_request,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  this->PumpLoop();
-  this->ClearResults();
-
-  base::Time new_creation_time =
-      creation_time + base::TimeDelta::FromMinutes(1);
-  base::Time activation_time = creation_time + base::TimeDelta::FromHours(6);
-  // Try updating an existing request.
-  SavePageRequest updated_request(kRequestId, kUrl, kClientId,
-                                  new_creation_time, activation_time,
-                                  kUserRequested);
-  // Try to update a non-existing request.
-  SavePageRequest updated_request2(kRequestId2, kUrl, kClientId,
-                                   new_creation_time, activation_time,
-                                   kUserRequested);
-  std::vector<SavePageRequest> requests_to_update{updated_request,
-                                                  updated_request2};
-  store->UpdateRequests(
-      requests_to_update,
-      base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
-                 base::Unretained(this)));
-  ASSERT_FALSE(this->last_update_result());
-  this->PumpLoop();
-  ASSERT_TRUE(this->last_update_result());
-  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[0].second);
-  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            this->last_update_result()->item_statuses[1].second);
-  EXPECT_EQ(1UL, this->last_update_result()->updated_items.size());
-  EXPECT_EQ(updated_request,
-            *(this->last_update_result()->updated_items.begin()));
-
-  // Verifying get reqeust results after a request was updated.
-  this->ClearResults();
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_EQ(1ul, this->last_requests().size());
-  ASSERT_EQ(updated_request, *(this->last_requests()[0].get()));
-}
-
-TYPED_TEST(RequestQueueStoreTest, RemoveRequests) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
-                           kUserRequested);
-  store->AddRequest(request1,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  store->AddRequest(request2,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  this->PumpLoop();
-  this->ClearResults();
-
-  std::vector<int64_t> request_ids{kRequestId, kRequestId2};
-  store->RemoveRequests(
-      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
-                              base::Unretained(this)));
-
-  ASSERT_FALSE(this->last_update_result());
-  this->PumpLoop();
-  ASSERT_TRUE(this->last_update_result());
-  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[0].second);
-  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            this->last_update_result()->item_statuses[1].second);
-  EXPECT_EQ(2UL, this->last_update_result()->updated_items.size());
-  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
-  EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1));
-  this->ClearResults();
-
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_TRUE(this->last_requests().empty());
-  this->ClearResults();
-
-  // Try to remove a request that is not in the queue.
-  store->RemoveRequests(
-      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
-                              base::Unretained(this)));
-  ASSERT_FALSE(this->last_update_result());
-  this->PumpLoop();
-  ASSERT_TRUE(this->last_update_result());
-  // When requests are missing, we expect the results to say so, but since they
-  // are missing, no requests should have been returned.
-  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            this->last_update_result()->item_statuses[0].second);
-  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            this->last_update_result()->item_statuses[1].second);
-  EXPECT_EQ(0UL, this->last_update_result()->updated_items.size());
-}
-
-TYPED_TEST(RequestQueueStoreTest, ResetStore) {
-  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest original_request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  store->AddRequest(original_request,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  this->PumpLoop();
-  this->ClearResults();
-
-  store->Reset(base::Bind(&RequestQueueStoreTestBase::ResetDone,
-                          base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  this->ClearResults();
-
-  this->InitializeStore(store.get());
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_TRUE(this->last_requests().empty());
-}
-
-class RequestQueueStoreSQLTest
-    : public RequestQueueStoreTest<RequestQueueStoreSQLFactory> {};
-
-// Makes sure that persistent DB is actually persisting requests across store
-// restarts.
-TEST_F(RequestQueueStoreSQLTest, SaveCloseReopenRead) {
-  std::unique_ptr<RequestQueueStore> store(BuildStore());
-  this->InitializeStore(store.get());
-
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest original_request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  store->AddRequest(original_request,
-                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
-                               base::Unretained(this)));
-  PumpLoop();
-  ClearResults();
-
-  // Resets the store, using the same temp directory. The contents should be
-  // intact. First reset is done separately to release DB lock.
-  store.reset();
-  store = BuildStore();
-  this->InitializeStore(store.get());
-
-  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
-                                base::Unretained(this)));
-  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
-  this->PumpLoop();
-  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
-  ASSERT_EQ(1ul, this->last_requests().size());
-  ASSERT_TRUE(original_request == *(this->last_requests().at(0).get()));
-}
-
-}  // offline_pages
diff --git a/components/offline_pages/background/request_queue_unittest.cc b/components/offline_pages/background/request_queue_unittest.cc
deleted file mode 100644
index 8ba805a..0000000
--- a/components/offline_pages/background/request_queue_unittest.cc
+++ /dev/null
@@ -1,589 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/request_queue.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/background/device_conditions.h"
-#include "components/offline_pages/background/offliner_policy.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/request_coordinator_event_logger.h"
-#include "components/offline_pages/background/request_notifier.h"
-#include "components/offline_pages/background/request_queue_in_memory_store.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-using AddRequestResult = AddRequestResult;
-using GetRequestsResult = GetRequestsResult;
-using UpdateRequestResult = UpdateRequestResult;
-
-namespace {
-// Data for request 1.
-const int64_t kRequestId = 42;
-const GURL kUrl("http://example.com");
-const ClientId kClientId("bookmark", "1234");
-// Data for request 2.
-const int64_t kRequestId2 = 77;
-const GURL kUrl2("http://test.com");
-const ClientId kClientId2("bookmark", "567");
-const bool kUserRequested = true;
-const int64_t kRequestId3 = 99;
-const int kOneWeekInSeconds = 7 * 24 * 60 * 60;
-
-// Default request
-const SavePageRequest kEmptyRequest(0UL,
-                                    GURL(""),
-                                    ClientId("", ""),
-                                    base::Time(),
-                                    true);
-
-}  // namespace
-
-// Helper class needed by the PickRequestTask
-class RequestNotifierStub : public RequestNotifier {
- public:
-  RequestNotifierStub()
-      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
-
-  void NotifyAdded(const SavePageRequest& request) override {}
-  void NotifyChanged(const SavePageRequest& request) override {}
-
-  void NotifyCompleted(const SavePageRequest& request,
-                       BackgroundSavePageResult status) override {
-    last_expired_request_ = request;
-    last_request_expiration_status_ = status;
-    total_expired_requests_++;
-  }
-
-  const SavePageRequest& last_expired_request() {
-    return last_expired_request_;
-  }
-
-  RequestCoordinator::BackgroundSavePageResult
-  last_request_expiration_status() {
-    return last_request_expiration_status_;
-  }
-
-  int32_t total_expired_requests() { return total_expired_requests_; }
-
- private:
-  BackgroundSavePageResult last_request_expiration_status_;
-  SavePageRequest last_expired_request_;
-  int32_t total_expired_requests_;
-};
-
-// TODO(fgorski): Add tests for store failures in add/remove/get.
-class RequestQueueTest : public testing::Test {
- public:
-  RequestQueueTest();
-  ~RequestQueueTest() override;
-
-  // Test overrides.
-  void SetUp() override;
-
-  void PumpLoop();
-
-  // Callback for adding requests.
-  void AddRequestDone(AddRequestResult result, const SavePageRequest& request);
-  // Callback for getting requests.
-  void GetRequestsDone(GetRequestsResult result,
-                       std::vector<std::unique_ptr<SavePageRequest>> requests);
-
-  void UpdateRequestDone(UpdateRequestResult result);
-  void UpdateRequestsDone(std::unique_ptr<UpdateRequestsResult> result);
-
-  void ClearResults();
-
-  RequestQueue* queue() { return queue_.get(); }
-
-  AddRequestResult last_add_result() const { return last_add_result_; }
-  SavePageRequest* last_added_request() {
-    return last_added_request_.get();
-  }
-
-  UpdateRequestResult last_update_result() const { return last_update_result_; }
-
-  GetRequestsResult last_get_requests_result() const {
-    return last_get_requests_result_;
-  }
-
-  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
-    return last_requests_;
-  }
-
-  UpdateRequestsResult* update_requests_result() const {
-    return update_requests_result_.get();
-  }
-
-  void RequestPickedCallback(const SavePageRequest& request) {}
-  void RequestNotPickedCallback(bool non_user_requested_tasks_remain) {}
-  void RequestCountCallback(size_t total_count, size_t available_count) {}
-
- private:
-  AddRequestResult last_add_result_;
-  std::unique_ptr<SavePageRequest> last_added_request_;
-  std::unique_ptr<UpdateRequestsResult> update_requests_result_;
-  UpdateRequestResult last_update_result_;
-
-  GetRequestsResult last_get_requests_result_;
-  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
-
-  std::unique_ptr<RequestQueue> queue_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-RequestQueueTest::RequestQueueTest()
-    : last_add_result_(AddRequestResult::STORE_FAILURE),
-      last_update_result_(UpdateRequestResult::STORE_FAILURE),
-      last_get_requests_result_(GetRequestsResult::STORE_FAILURE),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {}
-
-RequestQueueTest::~RequestQueueTest() {}
-
-void RequestQueueTest::SetUp() {
-  std::unique_ptr<RequestQueueInMemoryStore> store(
-      new RequestQueueInMemoryStore());
-  queue_.reset(new RequestQueue(std::move(store)));
-}
-
-void RequestQueueTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void RequestQueueTest::AddRequestDone(AddRequestResult result,
-                                      const SavePageRequest& request) {
-  last_add_result_ = result;
-  last_added_request_.reset(new SavePageRequest(request));
-}
-
-void RequestQueueTest::GetRequestsDone(
-    GetRequestsResult result,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  last_get_requests_result_ = result;
-  last_requests_ = std::move(requests);
-}
-
-void RequestQueueTest::UpdateRequestDone(UpdateRequestResult result) {
-  last_update_result_ = result;
-}
-
-void RequestQueueTest::UpdateRequestsDone(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  update_requests_result_ = std::move(result);
-}
-
-void RequestQueueTest::ClearResults() {
-  last_add_result_ = AddRequestResult::STORE_FAILURE;
-  last_update_result_ = UpdateRequestResult::STORE_FAILURE;
-  last_get_requests_result_ = GetRequestsResult::STORE_FAILURE;
-  last_added_request_.reset(nullptr);
-  update_requests_result_.reset(nullptr);
-  last_requests_.clear();
-}
-
-TEST_F(RequestQueueTest, GetRequestsEmpty) {
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(0ul, last_requests().size());
-}
-
-TEST_F(RequestQueueTest, AddRequest) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(AddRequestResult::SUCCESS, last_add_result());
-  ASSERT_TRUE(last_added_request());
-  ASSERT_EQ(kRequestId, last_added_request()->request_id());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-}
-
-TEST_F(RequestQueueTest, RemoveRequest) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(kRequestId, last_added_request()->request_id());
-
-  std::vector<int64_t> remove_requests{kRequestId};
-  queue()->RemoveRequests(remove_requests,
-                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                     base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(1ul, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
-  EXPECT_EQ(request, update_requests_result()->updated_items.at(0));
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(0ul, last_requests().size());
-}
-
-TEST_F(RequestQueueTest, RemoveSeveralRequests) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(kRequestId, last_added_request()->request_id());
-
-  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
-                           kUserRequested);
-  queue()->AddRequest(request2, base::Bind(&RequestQueueTest::AddRequestDone,
-                                           base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(kRequestId2, last_added_request()->request_id());
-
-  std::vector<int64_t> remove_requests;
-  remove_requests.push_back(kRequestId);
-  remove_requests.push_back(kRequestId2);
-  remove_requests.push_back(kRequestId3);
-  queue()->RemoveRequests(remove_requests,
-                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                     base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(3ul, update_requests_result()->item_statuses.size());
-  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  ASSERT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  ASSERT_EQ(kRequestId2, update_requests_result()->item_statuses.at(1).first);
-  ASSERT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(1).second);
-  ASSERT_EQ(kRequestId3, update_requests_result()->item_statuses.at(2).first);
-  ASSERT_EQ(ItemActionStatus::NOT_FOUND,
-            update_requests_result()->item_statuses.at(2).second);
-  EXPECT_EQ(2UL, update_requests_result()->updated_items.size());
-  EXPECT_EQ(request, update_requests_result()->updated_items.at(0));
-  EXPECT_EQ(request2, update_requests_result()->updated_items.at(1));
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Verify both requests are no longer in the queue.
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(0ul, last_requests().size());
-}
-
-TEST_F(RequestQueueTest, PauseAndResume) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(kRequestId, last_added_request()->request_id());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-
-  std::vector<int64_t> request_ids;
-  request_ids.push_back(kRequestId);
-
-  // Pause the request.
-  queue()->ChangeRequestsState(request_ids,
-                               SavePageRequest::RequestState::PAUSED,
-                               base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  ASSERT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  ASSERT_EQ(1ul, update_requests_result()->updated_items.size());
-  ASSERT_EQ(SavePageRequest::RequestState::PAUSED,
-            update_requests_result()->updated_items.at(0).request_state());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Verify the request is paused.
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-  ASSERT_EQ(SavePageRequest::RequestState::PAUSED,
-            last_requests().at(0)->request_state());
-
-  // Resume the request.
-  queue()->ChangeRequestsState(request_ids,
-                               SavePageRequest::RequestState::AVAILABLE,
-                               base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  ASSERT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  ASSERT_EQ(1ul, update_requests_result()->updated_items.size());
-  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            update_requests_result()->updated_items.at(0).request_state());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-
-  // Verify the request is no longer paused.
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            last_requests().at(0)->request_state());
-}
-
-// A longer test populating the request queue with more than one item, properly
-// listing multiple items and removing the right item.
-TEST_F(RequestQueueTest, MultipleRequestsAddGetRemove) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request1(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  queue()->AddRequest(request1, base::Bind(&RequestQueueTest::AddRequestDone,
-                                           base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(request1.request_id(), last_added_request()->request_id());
-  SavePageRequest request2(
-      kRequestId2, kUrl2, kClientId2, creation_time, kUserRequested);
-  queue()->AddRequest(request2, base::Bind(&RequestQueueTest::AddRequestDone,
-                                           base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(request2.request_id(), last_added_request()->request_id());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(2ul, last_requests().size());
-
-  std::vector<int64_t> remove_requests;
-  remove_requests.push_back(request1.request_id());
-  queue()->RemoveRequests(remove_requests,
-                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                     base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  ASSERT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-  ASSERT_EQ(request2.request_id(), last_requests().at(0)->request_id());
-}
-
-TEST_F(RequestQueueTest, MarkAttemptStarted) {
-  // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-
-  base::Time before_time = base::Time::Now();
-  // Update the request, ensure it succeeded.
-  queue()->MarkAttemptStarted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
-  EXPECT_LE(before_time,
-            update_requests_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_GE(base::Time::Now(),
-            update_requests_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_EQ(
-      1, update_requests_result()->updated_items.at(0).started_attempt_count());
-  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING,
-            update_requests_result()->updated_items.at(0).request_state());
-
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
-  ASSERT_EQ(1ul, last_requests().size());
-  EXPECT_EQ(update_requests_result()->updated_items.at(0),
-            *last_requests().at(0));
-}
-
-TEST_F(RequestQueueTest, MarkAttempStartedRequestNotPresent) {
-  // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
-  // This request is never put into the queue.
-  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
-                           kUserRequested);
-
-  queue()->MarkAttemptStarted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0ul, update_requests_result()->updated_items.size());
-}
-
-TEST_F(RequestQueueTest, MarkAttemptAborted) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-
-  // Start request.
-  queue()->MarkAttemptStarted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-  ClearResults();
-
-  queue()->MarkAttemptAborted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-
-  ASSERT_TRUE(update_requests_result());
-  EXPECT_EQ(1UL, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            update_requests_result()->updated_items.at(0).request_state());
-}
-
-TEST_F(RequestQueueTest, MarkAttemptAbortedRequestNotPresent) {
-  // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
-  // This request is never put into the queue.
-  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
-                           kUserRequested);
-
-  queue()->MarkAttemptAborted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(0ul, update_requests_result()->updated_items.size());
-}
-
-TEST_F(RequestQueueTest, MarkAttemptCompleted) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
-                                          base::Unretained(this)));
-  PumpLoop();
-
-  // Start request.
-  queue()->MarkAttemptStarted(kRequestId,
-                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                                         base::Unretained(this)));
-  PumpLoop();
-  ClearResults();
-
-  queue()->MarkAttemptCompleted(
-      kRequestId, base::Bind(&RequestQueueTest::UpdateRequestsDone,
-                             base::Unretained(this)));
-  PumpLoop();
-
-  ASSERT_TRUE(update_requests_result());
-  EXPECT_EQ(1UL, update_requests_result()->item_statuses.size());
-  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            update_requests_result()->item_statuses.at(0).second);
-  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
-  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
-            update_requests_result()->updated_items.at(0).request_state());
-}
-
-// Request expiration is detected during the call to pick a request, which
-// is why this test calls PickNextRequest().
-TEST_F(RequestQueueTest, CleanStaleRequests) {
-  // Create a request that is already expired.
-  base::Time creation_time =
-      base::Time::Now() - base::TimeDelta::FromSeconds(2 * kOneWeekInSeconds);
-
-  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
-                                   kUserRequested);
-  queue()->AddRequest(
-      original_request,
-      base::Bind(&RequestQueueTest::AddRequestDone, base::Unretained(this)));
-  this->PumpLoop();
-  this->ClearResults();
-
-  // Set up a picker factory pointing to our fake notifier.
-  OfflinerPolicy policy;
-  RequestNotifierStub notifier;
-  RequestCoordinatorEventLogger event_logger;
-  std::unique_ptr<PickRequestTaskFactory> picker_factory(
-      new PickRequestTaskFactory(&policy, &notifier, &event_logger));
-  queue()->SetPickerFactory(std::move(picker_factory));
-
-  // Do a pick and clean operation, which will remove stale entries.
-  DeviceConditions conditions;
-  std::set<int64_t> disabled_list;
-  queue()->PickNextRequest(
-      base::Bind(&RequestQueueTest::RequestPickedCallback,
-                 base::Unretained(this)),
-      base::Bind(&RequestQueueTest::RequestNotPickedCallback,
-                 base::Unretained(this)),
-      base::Bind(&RequestQueueTest::RequestCountCallback,
-                 base::Unretained(this)),
-      conditions, disabled_list);
-
-  this->PumpLoop();
-
-  // Notifier should have been notified that the request was removed.
-  ASSERT_EQ(notifier.last_expired_request().request_id(), kRequestId);
-  ASSERT_EQ(notifier.last_request_expiration_status(),
-            RequestNotifier::BackgroundSavePageResult::EXPIRED);
-
-  // Doing a get should show no entries left in the queue since the expired
-  // request has been removed.
-  queue()->GetRequests(
-      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
-  this->PumpLoop();
-  ASSERT_EQ(GetRequestsResult::SUCCESS, this->last_get_requests_result());
-  ASSERT_TRUE(this->last_requests().empty());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/save_page_request.cc b/components/offline_pages/background/save_page_request.cc
deleted file mode 100644
index f17b726..0000000
--- a/components/offline_pages/background/save_page_request.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/save_page_request.h"
-
-namespace offline_pages {
-
-SavePageRequest::SavePageRequest(int64_t request_id,
-                                 const GURL& url,
-                                 const ClientId& client_id,
-                                 const base::Time& creation_time,
-                                 const bool user_requested)
-    : request_id_(request_id),
-      url_(url),
-      client_id_(client_id),
-      creation_time_(creation_time),
-      activation_time_(creation_time),
-      started_attempt_count_(0),
-      completed_attempt_count_(0),
-      user_requested_(user_requested),
-      state_(RequestState::AVAILABLE) {}
-
-SavePageRequest::SavePageRequest(int64_t request_id,
-                                 const GURL& url,
-                                 const ClientId& client_id,
-                                 const base::Time& creation_time,
-                                 const base::Time& activation_time,
-                                 const bool user_requested)
-    : request_id_(request_id),
-      url_(url),
-      client_id_(client_id),
-      creation_time_(creation_time),
-      activation_time_(activation_time),
-      started_attempt_count_(0),
-      completed_attempt_count_(0),
-      user_requested_(user_requested),
-      state_(RequestState::AVAILABLE) {}
-
-SavePageRequest::SavePageRequest(const SavePageRequest& other)
-    : request_id_(other.request_id_),
-      url_(other.url_),
-      client_id_(other.client_id_),
-      creation_time_(other.creation_time_),
-      activation_time_(other.activation_time_),
-      started_attempt_count_(other.started_attempt_count_),
-      completed_attempt_count_(other.completed_attempt_count_),
-      last_attempt_time_(other.last_attempt_time_),
-      user_requested_(other.user_requested_),
-      state_(other.state_) {}
-
-SavePageRequest::~SavePageRequest() {}
-
-bool SavePageRequest::operator==(const SavePageRequest& other) const {
-  return request_id_ == other.request_id_ &&
-         url_ == other.url_ &&
-         client_id_ == other.client_id_ &&
-         creation_time_ == other.creation_time_ &&
-         activation_time_ == other.activation_time_ &&
-         started_attempt_count_ == other.started_attempt_count_ &&
-         completed_attempt_count_ == other.completed_attempt_count_ &&
-         last_attempt_time_ == other.last_attempt_time_ &&
-         state_ == other.state_;
-}
-
-void SavePageRequest::MarkAttemptStarted(const base::Time& start_time) {
-  DCHECK_LE(activation_time_, start_time);
-  // TODO(fgorski): As part of introducing policy in GetStatus, we can make a
-  // check here to ensure we only start tasks in status pending, and bail out in
-  // other cases.
-  last_attempt_time_ = start_time;
-  ++started_attempt_count_;
-  state_ = RequestState::OFFLINING;
-}
-
-void SavePageRequest::MarkAttemptCompleted() {
-  ++completed_attempt_count_;
-  state_ = RequestState::AVAILABLE;
-}
-
-void SavePageRequest::MarkAttemptAborted() {
-  DCHECK_GT(started_attempt_count_, 0);
-  // We intentinally do not increment the completed_attempt_count_, since this
-  // was killed before it completed, so we could use the phone or browser for
-  // other things.
-  state_ = RequestState::AVAILABLE;
-}
-
-void SavePageRequest::MarkAttemptPaused() {
-  state_ = RequestState::PAUSED;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/save_page_request.h b/components/offline_pages/background/save_page_request.h
deleted file mode 100644
index 8dcc2f3..0000000
--- a/components/offline_pages/background/save_page_request.h
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
-
-#include <stdint.h>
-
-#include "base/time/time.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-// Class representing a request to save page.
-class SavePageRequest {
- public:
-  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
-  enum class RequestState {
-    AVAILABLE = 0,     // Request can be scheduled when preconditions are met.
-    PAUSED = 1,        // Request is not available until it is unpaused.
-    OFFLINING = 2,     // Request is actively offlining.
-  };
-
-  SavePageRequest(int64_t request_id,
-                  const GURL& url,
-                  const ClientId& client_id,
-                  const base::Time& creation_time,
-                  const bool user_requested);
-  SavePageRequest(int64_t request_id,
-                  const GURL& url,
-                  const ClientId& client_id,
-                  const base::Time& creation_time,
-                  const base::Time& activation_time,
-                  const bool user_requested);
-  SavePageRequest(const SavePageRequest& other);
-  ~SavePageRequest();
-
-  bool operator==(const SavePageRequest& other) const;
-
-  // Updates the |last_attempt_time_| and increments |attempt_count_|.
-  void MarkAttemptStarted(const base::Time& start_time);
-
-  // Marks attempt as completed and clears |last_attempt_time_|.
-  void MarkAttemptCompleted();
-
-  // Marks attempt as aborted. Specifically it clears |last_attempt_time_|
-  // and decrements |attempt_count_|.
-  void MarkAttemptAborted();
-
-  // Mark the attempt as paused.  It is not available for future prerendering
-  // until it has been explicitly unpaused.
-  void MarkAttemptPaused();
-
-  int64_t request_id() const { return request_id_; }
-
-  const GURL& url() const { return url_; }
-
-  const ClientId& client_id() const { return client_id_; }
-
-  RequestState request_state() const { return state_; }
-  void set_request_state(RequestState new_state) { state_ = new_state; }
-
-  const base::Time& creation_time() const { return creation_time_; }
-
-  const base::Time& activation_time() const { return activation_time_; }
-
-  int64_t started_attempt_count() const { return started_attempt_count_; }
-  void set_started_attempt_count(int64_t started_attempt_count) {
-    started_attempt_count_ = started_attempt_count;
-  }
-
-  int64_t completed_attempt_count() const { return completed_attempt_count_; }
-  void set_completed_attempt_count(int64_t completed_attempt_count) {
-    completed_attempt_count_ = completed_attempt_count;
-  }
-
-  const base::Time& last_attempt_time() const { return last_attempt_time_; }
-  void set_last_attempt_time(const base::Time& last_attempt_time) {
-    last_attempt_time_ = last_attempt_time;
-  }
-
-  bool user_requested() const { return user_requested_; }
-
-  void set_user_requested(bool user_requested) {
-    user_requested_ = user_requested;
-  }
-
- private:
-  // ID of this request.
-  int64_t request_id_;
-
-  // Online URL of a page to be offlined.
-  GURL url_;
-
-  // Client ID related to the request. Contains namespace and ID assigned by the
-  // requester.
-  ClientId client_id_;
-
-  // Time when this request was created. (Alternative 2).
-  base::Time creation_time_;
-
-  // Time when this request will become active.
-  base::Time activation_time_;
-
-  // Number of attempts started to get the page.  This may be different than the
-  // number of attempts completed because we could crash.
-  int started_attempt_count_;
-
-  // Number of attempts we actually completed to get the page.
-  int completed_attempt_count_;
-
-  // Timestamp of the last request starting.
-  base::Time last_attempt_time_;
-
-  // Whether the user specifically requested this page (as opposed to a client
-  // like AGSA or Now.)
-  bool user_requested_;
-
-  // The current state of this request
-  RequestState state_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_SAVE_PAGE_REQUEST_H_
diff --git a/components/offline_pages/background/save_page_request_unittest.cc b/components/offline_pages/background/save_page_request_unittest.cc
deleted file mode 100644
index 74da9b6..0000000
--- a/components/offline_pages/background/save_page_request_unittest.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/save_page_request.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-const int64_t kRequestId = 42;
-const GURL kUrl("http://example.com");
-const ClientId kClientId("bookmark", "1234");
-const bool kUserRequested = true;
-}  // namespace
-
-class SavePageRequestTest : public testing::Test {
- public:
-  ~SavePageRequestTest() override;
-};
-
-SavePageRequestTest::~SavePageRequestTest() {}
-
-TEST_F(SavePageRequestTest, CreatePendingReqeust) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(
-      kRequestId, kUrl, kClientId, creation_time, kUserRequested);
-  ASSERT_EQ(kRequestId, request.request_id());
-  ASSERT_EQ(kUrl, request.url());
-  ASSERT_EQ(kClientId, request.client_id());
-  ASSERT_EQ(creation_time, request.creation_time());
-  ASSERT_EQ(creation_time, request.activation_time());
-  ASSERT_EQ(base::Time(), request.last_attempt_time());
-  ASSERT_EQ(0, request.completed_attempt_count());
-  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
-  ASSERT_EQ(0, request.started_attempt_count());
-  ASSERT_EQ(0, request.completed_attempt_count());
-}
-
-TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
-  base::Time creation_time = base::Time::Now();
-  base::Time activation_time = creation_time + base::TimeDelta::FromHours(6);
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          activation_time, kUserRequested);
-
-  base::Time start_time = activation_time + base::TimeDelta::FromHours(3);
-  request.MarkAttemptStarted(start_time);
-
-  // Most things don't change about the request.
-  ASSERT_EQ(kRequestId, request.request_id());
-  ASSERT_EQ(kUrl, request.url());
-  ASSERT_EQ(kClientId, request.client_id());
-  ASSERT_EQ(creation_time, request.creation_time());
-  ASSERT_EQ(activation_time, request.activation_time());
-
-  // Attempt time, attempt count and status will though.
-  ASSERT_EQ(start_time, request.last_attempt_time());
-  ASSERT_EQ(1, request.started_attempt_count());
-  ASSERT_EQ(SavePageRequest::RequestState::OFFLINING,
-            request.request_state());
-
-  request.MarkAttemptCompleted();
-
-  // Again, most things don't change about the request.
-  ASSERT_EQ(kRequestId, request.request_id());
-  ASSERT_EQ(kUrl, request.url());
-  ASSERT_EQ(kClientId, request.client_id());
-  ASSERT_EQ(creation_time, request.creation_time());
-  ASSERT_EQ(activation_time, request.activation_time());
-
-  // Last attempt time and status are updated.
-  ASSERT_EQ(1, request.completed_attempt_count());
-  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
-}
-
-TEST_F(SavePageRequestTest, StartAndAbortRequest) {
-  base::Time creation_time = base::Time::Now();
-  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
-                          kUserRequested);
-
-  base::Time start_time = creation_time + base::TimeDelta::FromHours(3);
-  request.MarkAttemptStarted(start_time);
-
-  // Most things don't change about the request.
-  ASSERT_EQ(kRequestId, request.request_id());
-  ASSERT_EQ(kUrl, request.url());
-  ASSERT_EQ(kClientId, request.client_id());
-  ASSERT_EQ(creation_time, request.creation_time());
-
-  // Attempt time and attempt count will though.
-  ASSERT_EQ(start_time, request.last_attempt_time());
-  ASSERT_EQ(1, request.started_attempt_count());
-  ASSERT_EQ(SavePageRequest::RequestState::OFFLINING,
-            request.request_state());
-
-  request.MarkAttemptAborted();
-
-  // Again, most things don't change about the request.
-  ASSERT_EQ(kRequestId, request.request_id());
-  ASSERT_EQ(kUrl, request.url());
-  ASSERT_EQ(kClientId, request.client_id());
-  ASSERT_EQ(creation_time, request.creation_time());
-
-  // Last attempt time is updated and completed attempt count did not rise.
-  ASSERT_EQ(0, request.completed_attempt_count());
-  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/scheduler.h b/components/offline_pages/background/scheduler.h
deleted file mode 100644
index b4a36fe..0000000
--- a/components/offline_pages/background/scheduler.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
-
-namespace offline_pages {
-
-// Interface of a class responsible for scheduling a task to initiate
-// processing of background offlining requests upon select system conditions
-// (such as having a network connection).
-class Scheduler {
- public:
-  // Defines a set of system conditions to trigger background processing.
-  struct TriggerConditions {
-    TriggerConditions(bool power, int battery, bool unmetered)
-        : require_power_connected(power),
-          minimum_battery_percentage(battery),
-          require_unmetered_network(unmetered) {}
-    bool require_power_connected;
-    int minimum_battery_percentage;
-    bool require_unmetered_network;
-  };
-
-  Scheduler() {}
-  virtual ~Scheduler() {}
-
-  // Schedules the triggering of a task subject to |trigger_conditions|.
-  // This may overwrite any previous scheduled task with a new one for
-  // these conditions. That is, only one set of triggering conditions
-  // is scheduled at a time.
-  virtual void Schedule(const TriggerConditions& trigger_conditions) = 0;
-
-  // Schedules the triggering of a task in case Chromium is killed,
-  // so we can continue processing background download requests.  This will
-  // not overwrite existing tasks.
-  virtual void BackupSchedule(const TriggerConditions& trigger_conditions,
-                              long delay_in_seconds) = 0;
-
-  // Unschedules the currently scheduled task, if any.
-  virtual void Unschedule() = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_H_
diff --git a/components/offline_pages/background/scheduler_stub.cc b/components/offline_pages/background/scheduler_stub.cc
deleted file mode 100644
index 8a2862a..0000000
--- a/components/offline_pages/background/scheduler_stub.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/scheduler_stub.h"
-
-namespace offline_pages {
-
-SchedulerStub::SchedulerStub()
-    : schedule_called_(false),
-      backup_schedule_called_(false),
-      unschedule_called_(false),
-      schedule_delay_(0L),
-      conditions_(false, 0, false) {}
-
-SchedulerStub::~SchedulerStub() {}
-
-void SchedulerStub::Schedule(const TriggerConditions& trigger_conditions) {
-  schedule_called_ = true;
-  conditions_ = trigger_conditions;
-}
-
-void SchedulerStub::BackupSchedule(const TriggerConditions& trigger_conditions,
-                                   long delay_in_seconds) {
-  backup_schedule_called_ = true;
-  schedule_delay_ = delay_in_seconds;
-  conditions_ = trigger_conditions;
-}
-
-void SchedulerStub::Unschedule() {
-  unschedule_called_ = true;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/scheduler_stub.h b/components/offline_pages/background/scheduler_stub.h
deleted file mode 100644
index 209c665..0000000
--- a/components/offline_pages/background/scheduler_stub.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_STUB_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_STUB_H_
-
-#include "components/offline_pages/background/scheduler.h"
-
-namespace offline_pages {
-
-// Test class stubbing out the functionality of Scheduler.
-// It is only used for test support.
-class SchedulerStub : public Scheduler {
- public:
-  SchedulerStub();
-  ~SchedulerStub() override;
-
-  void Schedule(const TriggerConditions& trigger_conditions) override;
-
-  void BackupSchedule(const TriggerConditions& trigger_conditions,
-                      long delay_in_seconds) override;
-
-  // Unschedules the currently scheduled task, if any.
-  void Unschedule() override;
-
-  bool schedule_called() const { return schedule_called_; }
-
-  bool backup_schedule_called() const { return backup_schedule_called_; }
-
-  bool unschedule_called() const { return unschedule_called_; }
-
-  TriggerConditions const* conditions() const { return &conditions_; }
-
- private:
-  bool schedule_called_;
-  bool backup_schedule_called_;
-  bool unschedule_called_;
-  long schedule_delay_;
-  TriggerConditions conditions_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_SCHEDULER_STUB_H_
diff --git a/components/offline_pages/background/update_request_task.cc b/components/offline_pages/background/update_request_task.cc
deleted file mode 100644
index 08ccf1cb..0000000
--- a/components/offline_pages/background/update_request_task.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/background/update_request_task.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/time/time.h"
-
-namespace offline_pages {
-
-UpdateRequestTask::UpdateRequestTask(
-    RequestQueueStore* store,
-    int64_t request_id,
-    const RequestQueueStore::UpdateCallback& callback)
-    : store_(store),
-      request_id_(request_id),
-      callback_(callback),
-      weak_ptr_factory_(this) {}
-
-UpdateRequestTask::~UpdateRequestTask() {}
-
-void UpdateRequestTask::Run() {
-  ReadRequest();
-}
-
-void UpdateRequestTask::ReadRequest() {
-  std::vector<int64_t> request_ids{request_id_};
-  store_->GetRequestsByIds(request_ids,
-                           base::Bind(&UpdateRequestTask::UpdateRequestImpl,
-                                      weak_ptr_factory_.GetWeakPtr()));
-}
-
-void UpdateRequestTask::CompleteWithResult(
-    std::unique_ptr<UpdateRequestsResult> result) {
-  callback_.Run(std::move(result));
-  TaskComplete();
-}
-
-bool UpdateRequestTask::ValidateReadResult(UpdateRequestsResult* result) {
-  return result->store_state == StoreState::LOADED &&
-         result->item_statuses.at(0).first == request_id() &&
-         result->item_statuses.at(0).second == ItemActionStatus::SUCCESS &&
-         result->updated_items.size() == 1 &&
-         result->updated_items.at(0).request_id() == request_id();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/background/update_request_task.h b/components/offline_pages/background/update_request_task.h
deleted file mode 100644
index ad332bd9..0000000
--- a/components/offline_pages/background/update_request_task.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
-#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/background/request_queue_store.h"
-#include "components/offline_pages/core/task.h"
-
-namespace offline_pages {
-
-// Base class for update requests that only work on a single save page request.
-// Derived classes should implement appropriate functionality by overloading
-// |UpdateRequestImpl| method.
-class UpdateRequestTask : public Task {
- public:
-  UpdateRequestTask(RequestQueueStore* store,
-                    int64_t request_id,
-                    const RequestQueueStore::UpdateCallback& callback);
-  ~UpdateRequestTask() override;
-
-  // TaskQueue::Task implementation.
-  void Run() override;
-
- protected:
-  // Step 1. Reading the requests.
-  void ReadRequest();
-  // Step 2. Work is done in the implementation step.
-  virtual void UpdateRequestImpl(
-      std::unique_ptr<UpdateRequestsResult> result) = 0;
-  // Step 3. Completes once update is done.
-  void CompleteWithResult(std::unique_ptr<UpdateRequestsResult> result);
-
-  // Function to uniformly validate read request call for store errors and
-  // presence of the request.
-  bool ValidateReadResult(UpdateRequestsResult* result);
-
-  RequestQueueStore* store() const { return store_; }
-
-  int64_t request_id() const { return request_id_; }
-
-  base::WeakPtr<UpdateRequestTask> GetWeakPtr() {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
- private:
-  // Store that this task updates. Not owned.
-  RequestQueueStore* store_;
-  // Request ID of the request to be started.
-  int64_t request_id_;
-  // Callback to complete the task.
-  RequestQueueStore::UpdateCallback callback_;
-
-  base::WeakPtrFactory<UpdateRequestTask> weak_ptr_factory_;
-  DISALLOW_COPY_AND_ASSIGN(UpdateRequestTask);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_UPDATE_REQUEST_TASK_H_
diff --git a/components/offline_pages/client_namespace_constants.cc b/components/offline_pages/client_namespace_constants.cc
deleted file mode 100644
index d2f11bef..0000000
--- a/components/offline_pages/client_namespace_constants.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/client_namespace_constants.h"
-
-namespace offline_pages {
-
-const char kBookmarkNamespace[] = "bookmark";
-const char kLastNNamespace[] = "last_n";
-const char kAsyncNamespace[] = "async_loading";
-const char kCCTNamespace[] = "custom_tabs";
-const char kDownloadNamespace[] = "download";
-const char kNTPSuggestionsNamespace[] = "ntp_suggestions";
-
-const char kDefaultNamespace[] = "default";
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/client_namespace_constants.h b/components/offline_pages/client_namespace_constants.h
deleted file mode 100644
index 823af09c..0000000
--- a/components/offline_pages/client_namespace_constants.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
-#define COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
-
-#include "build/build_config.h"
-
-namespace offline_pages {
-
-// Any changes to these well-known namespaces should also be reflected in
-// OfflinePagesNamespace (histograms.xml) for consistency.
-extern const char kBookmarkNamespace[];
-extern const char kLastNNamespace[];
-extern const char kAsyncNamespace[];
-extern const char kCCTNamespace[];
-extern const char kDownloadNamespace[];
-extern const char kNTPSuggestionsNamespace[];
-
-// Currently used for fallbacks like tests.
-extern const char kDefaultNamespace[];
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CLIENT_NAMESPACE_CONSTANTS_H_
diff --git a/components/offline_pages/client_policy_controller.cc b/components/offline_pages/client_policy_controller.cc
deleted file mode 100644
index ae223d4..0000000
--- a/components/offline_pages/client_policy_controller.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/client_policy_controller.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-
-using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
-
-namespace offline_pages {
-
-ClientPolicyController::ClientPolicyController() {
-  policies_.clear();
-  // Manually defining client policies for bookmark and last_n.
-  policies_.insert(std::make_pair(
-      kBookmarkNamespace,
-      MakePolicy(kBookmarkNamespace, LifetimeType::TEMPORARY,
-                 base::TimeDelta::FromDays(7), kUnlimitedPages, 1)));
-  policies_.insert(std::make_pair(
-      kLastNNamespace,
-      OfflinePageClientPolicyBuilder(kLastNNamespace, LifetimeType::TEMPORARY,
-                                     kUnlimitedPages, kUnlimitedPages)
-          .SetExpirePeriod(base::TimeDelta::FromDays(2))
-          .SetIsSupportedByRecentTabs(true)
-          .SetIsOnlyShownInOriginalTab(true)
-          .Build()));
-  policies_.insert(std::make_pair(
-      kAsyncNamespace,
-      OfflinePageClientPolicyBuilder(kAsyncNamespace, LifetimeType::PERSISTENT,
-                                     kUnlimitedPages, kUnlimitedPages)
-          .SetIsSupportedByDownload(true)
-          .SetIsRemovedOnCacheReset(false)
-          .Build()));
-  policies_.insert(std::make_pair(
-      kCCTNamespace,
-      MakePolicy(kCCTNamespace, LifetimeType::TEMPORARY,
-                 base::TimeDelta::FromDays(2), kUnlimitedPages, 1)));
-  policies_.insert(std::make_pair(
-      kDownloadNamespace, OfflinePageClientPolicyBuilder(
-                              kDownloadNamespace, LifetimeType::PERSISTENT,
-                              kUnlimitedPages, kUnlimitedPages)
-                              .SetIsRemovedOnCacheReset(false)
-                              .SetIsSupportedByDownload(true)
-                              .Build()));
-  policies_.insert(std::make_pair(
-      kNTPSuggestionsNamespace,
-      OfflinePageClientPolicyBuilder(kNTPSuggestionsNamespace,
-                                     LifetimeType::PERSISTENT, kUnlimitedPages,
-                                     kUnlimitedPages)
-          .SetIsSupportedByDownload(true)
-          .Build()));
-
-  // Fallback policy.
-  policies_.insert(std::make_pair(
-      kDefaultNamespace, MakePolicy(kDefaultNamespace, LifetimeType::TEMPORARY,
-                                    base::TimeDelta::FromDays(1), 10, 1)));
-}
-
-ClientPolicyController::~ClientPolicyController() {}
-
-// static
-const OfflinePageClientPolicy ClientPolicyController::MakePolicy(
-    const std::string& name_space,
-    LifetimeType lifetime_type,
-    const base::TimeDelta& expire_period,
-    size_t page_limit,
-    size_t pages_allowed_per_url) {
-  return OfflinePageClientPolicyBuilder(name_space, lifetime_type, page_limit,
-                                        pages_allowed_per_url)
-      .SetExpirePeriod(expire_period)
-      .Build();
-}
-
-const OfflinePageClientPolicy& ClientPolicyController::GetPolicy(
-    const std::string& name_space) const {
-  const auto& iter = policies_.find(name_space);
-  if (iter != policies_.end())
-    return iter->second;
-  // Fallback when the namespace isn't defined.
-  return policies_.at(kDefaultNamespace);
-}
-
-std::vector<std::string> ClientPolicyController::GetAllNamespaces() const {
-  std::vector<std::string> result;
-  for (const auto& policy_item : policies_)
-    result.emplace_back(policy_item.first);
-
-  return result;
-}
-
-bool ClientPolicyController::IsRemovedOnCacheReset(
-    const std::string& name_space) const {
-  return GetPolicy(name_space).feature_policy.is_removed_on_cache_reset;
-}
-
-bool ClientPolicyController::IsSupportedByDownload(
-    const std::string& name_space) const {
-  return GetPolicy(name_space).feature_policy.is_supported_by_download;
-}
-
-const std::vector<std::string>&
-ClientPolicyController::GetNamespacesSupportedByDownload() const {
-  if (download_namespace_cache_)
-    return *download_namespace_cache_;
-
-  download_namespace_cache_ = base::MakeUnique<std::vector<std::string>>();
-  for (const auto& policy_item : policies_) {
-    if (policy_item.second.feature_policy.is_supported_by_download)
-      download_namespace_cache_->emplace_back(policy_item.first);
-  }
-  return *download_namespace_cache_;
-}
-
-bool ClientPolicyController::IsShownAsRecentlyVisitedSite(
-    const std::string& name_space) const {
-  return GetPolicy(name_space).feature_policy.is_supported_by_recent_tabs;
-}
-
-const std::vector<std::string>&
-ClientPolicyController::GetNamespacesShownAsRecentlyVisitedSite() const {
-  if (recent_tab_namespace_cache_)
-    return *recent_tab_namespace_cache_;
-
-  recent_tab_namespace_cache_ = base::MakeUnique<std::vector<std::string>>();
-  for (const auto& policy_item : policies_) {
-    if (policy_item.second.feature_policy.is_supported_by_recent_tabs)
-      recent_tab_namespace_cache_->emplace_back(policy_item.first);
-  }
-
-  return *recent_tab_namespace_cache_;
-}
-
-bool ClientPolicyController::IsRestrictedToOriginalTab(
-    const std::string& name_space) const {
-  return GetPolicy(name_space).feature_policy.only_shown_in_original_tab;
-}
-
-const std::vector<std::string>&
-ClientPolicyController::GetNamespacesRestrictedToOriginalTab() const {
-  if (show_in_original_tab_cache_)
-    return *show_in_original_tab_cache_;
-
-  show_in_original_tab_cache_ = base::MakeUnique<std::vector<std::string>>();
-  for (const auto& policy_item : policies_) {
-    if (policy_item.second.feature_policy.only_shown_in_original_tab)
-      show_in_original_tab_cache_->emplace_back(policy_item.first);
-  }
-
-  return *show_in_original_tab_cache_;
-}
-
-void ClientPolicyController::AddPolicyForTest(
-    const std::string& name_space,
-    const OfflinePageClientPolicyBuilder& builder) {
-  policies_.insert(std::make_pair(name_space, builder.Build()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/client_policy_controller.h b/components/offline_pages/client_policy_controller.h
deleted file mode 100644
index 8e2d21a..0000000
--- a/components/offline_pages/client_policy_controller.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
-#define COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/time/time.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-
-namespace offline_pages {
-
-// This is the class which is a singleton for offline page model
-// to get client policies based on namespaces.
-class ClientPolicyController {
- public:
-  ClientPolicyController();
-  ~ClientPolicyController();
-
-  // Generates a client policy from the input values.
-  static const OfflinePageClientPolicy MakePolicy(
-      const std::string& name_space,
-      LifetimePolicy::LifetimeType lifetime_type,
-      const base::TimeDelta& expiration_period,
-      size_t page_limit,
-      size_t pages_allowed_per_url);
-
-  // Get the client policy for |name_space|.
-  const OfflinePageClientPolicy& GetPolicy(const std::string& name_space) const;
-
-  // Returns a list of all known namespaces.
-  std::vector<std::string> GetAllNamespaces() const;
-
-  // Returns whether pages for |name_space| should be removed on cache reset.
-  bool IsRemovedOnCacheReset(const std::string& name_space) const;
-
-  // Returns whether pages for |name_space| are shown in Download UI.
-  bool IsSupportedByDownload(const std::string& name_space) const;
-  const std::vector<std::string>& GetNamespacesSupportedByDownload() const;
-
-  // Returns whether pages for |name_space| are shown in recent tabs UI,
-  // currently only available on NTP.
-  bool IsShownAsRecentlyVisitedSite(const std::string& name_space) const;
-  const std::vector<std::string>& GetNamespacesShownAsRecentlyVisitedSite()
-      const;
-
-  // Returns whether pages for |name_space| should never be shown outside the
-  // tab they were generated in.
-  bool IsRestrictedToOriginalTab(const std::string& name_space) const;
-  const std::vector<std::string>& GetNamespacesRestrictedToOriginalTab() const;
-
-  void AddPolicyForTest(const std::string& name_space,
-                        const OfflinePageClientPolicyBuilder& builder);
-
- private:
-  // The map from name_space to a client policy. Will be generated
-  // as pre-defined values for now.
-  std::map<std::string, OfflinePageClientPolicy> policies_;
-
-  // Memoizing results.
-  mutable std::unique_ptr<std::vector<std::string>> download_namespace_cache_;
-  mutable std::unique_ptr<std::vector<std::string>> recent_tab_namespace_cache_;
-  mutable std::unique_ptr<std::vector<std::string>> show_in_original_tab_cache_;
-
-  DISALLOW_COPY_AND_ASSIGN(ClientPolicyController);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_CLIENT_POLICY_CONTROLLER_H_
diff --git a/components/offline_pages/client_policy_controller_unittest.cc b/components/offline_pages/client_policy_controller_unittest.cc
deleted file mode 100644
index 930b1d7..0000000
--- a/components/offline_pages/client_policy_controller_unittest.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/client_policy_controller.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
-
-namespace offline_pages {
-
-namespace {
-const char kUndefinedNamespace[] = "undefined";
-
-bool isTemporary(const OfflinePageClientPolicy& policy) {
-  return policy.lifetime_policy.lifetime_type == LifetimeType::TEMPORARY;
-}
-}  // namespace
-
-class ClientPolicyControllerTest : public testing::Test {
- public:
-  ClientPolicyController* controller() { return controller_.get(); }
-
-  // testing::Test
-  void SetUp() override;
-  void TearDown() override;
-
- protected:
-  void ExpectDownloadSupport(std::string name_space, bool expectation);
-  void ExpectRecentTab(std::string name_space, bool expectation);
-  void ExpectOnlyOriginalTab(std::string name_space, bool expectation);
-
- private:
-  std::unique_ptr<ClientPolicyController> controller_;
-};
-
-void ClientPolicyControllerTest::SetUp() {
-  controller_.reset(new ClientPolicyController());
-}
-
-void ClientPolicyControllerTest::TearDown() {
-  controller_.reset();
-}
-
-void ClientPolicyControllerTest::ExpectDownloadSupport(std::string name_space,
-                                                       bool expectation) {
-  std::vector<std::string> cache =
-      controller()->GetNamespacesSupportedByDownload();
-  auto result = std::find(cache.begin(), cache.end(), name_space);
-  EXPECT_EQ(expectation, result != cache.end());
-  EXPECT_EQ(expectation, controller()->IsSupportedByDownload(name_space));
-}
-
-void ClientPolicyControllerTest::ExpectRecentTab(std::string name_space,
-                                                 bool expectation) {
-  std::vector<std::string> cache =
-      controller()->GetNamespacesShownAsRecentlyVisitedSite();
-  auto result = std::find(cache.begin(), cache.end(), name_space);
-  EXPECT_EQ(expectation, result != cache.end());
-  EXPECT_EQ(expectation,
-            controller()->IsShownAsRecentlyVisitedSite(name_space));
-}
-
-void ClientPolicyControllerTest::ExpectOnlyOriginalTab(std::string name_space,
-                                                       bool expectation) {
-  std::vector<std::string> cache =
-      controller()->GetNamespacesRestrictedToOriginalTab();
-  auto result = std::find(cache.begin(), cache.end(), name_space);
-  EXPECT_EQ(expectation, result != cache.end());
-  EXPECT_EQ(expectation, controller()->IsRestrictedToOriginalTab(name_space));
-}
-
-TEST_F(ClientPolicyControllerTest, FallbackTest) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kUndefinedNamespace);
-  EXPECT_EQ(policy.name_space, kDefaultNamespace);
-  EXPECT_TRUE(isTemporary(policy));
-  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kUndefinedNamespace));
-  ExpectDownloadSupport(kUndefinedNamespace, false);
-  ExpectRecentTab(kUndefinedNamespace, false);
-  ExpectOnlyOriginalTab(kUndefinedNamespace, false);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckBookmarkDefined) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kBookmarkNamespace);
-  EXPECT_EQ(policy.name_space, kBookmarkNamespace);
-  EXPECT_TRUE(isTemporary(policy));
-  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kBookmarkNamespace));
-  ExpectDownloadSupport(kBookmarkNamespace, false);
-  ExpectRecentTab(kBookmarkNamespace, false);
-  ExpectOnlyOriginalTab(kBookmarkNamespace, false);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckLastNDefined) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kLastNNamespace);
-  EXPECT_EQ(policy.name_space, kLastNNamespace);
-  EXPECT_TRUE(isTemporary(policy));
-  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kLastNNamespace));
-  ExpectDownloadSupport(kLastNNamespace, false);
-  ExpectRecentTab(kLastNNamespace, true);
-  ExpectOnlyOriginalTab(kLastNNamespace, true);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckAsyncDefined) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kAsyncNamespace);
-  EXPECT_EQ(policy.name_space, kAsyncNamespace);
-  EXPECT_FALSE(isTemporary(policy));
-  EXPECT_FALSE(controller()->IsRemovedOnCacheReset(kAsyncNamespace));
-  ExpectDownloadSupport(kAsyncNamespace, true);
-  ExpectRecentTab(kAsyncNamespace, false);
-  ExpectOnlyOriginalTab(kAsyncNamespace, false);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckCCTDefined) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kCCTNamespace);
-  EXPECT_EQ(policy.name_space, kCCTNamespace);
-  EXPECT_TRUE(isTemporary(policy));
-  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kCCTNamespace));
-  ExpectDownloadSupport(kCCTNamespace, false);
-  ExpectRecentTab(kCCTNamespace, false);
-  ExpectOnlyOriginalTab(kCCTNamespace, false);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckDownloadDefined) {
-  OfflinePageClientPolicy policy = controller()->GetPolicy(kDownloadNamespace);
-  EXPECT_EQ(policy.name_space, kDownloadNamespace);
-  EXPECT_FALSE(isTemporary(policy));
-  EXPECT_FALSE(controller()->IsRemovedOnCacheReset(kDownloadNamespace));
-  ExpectDownloadSupport(kDownloadNamespace, true);
-  ExpectRecentTab(kDownloadNamespace, false);
-  ExpectOnlyOriginalTab(kDownloadNamespace, false);
-}
-
-TEST_F(ClientPolicyControllerTest, CheckNTPSuggestionsDefined) {
-  OfflinePageClientPolicy policy =
-      controller()->GetPolicy(kNTPSuggestionsNamespace);
-  EXPECT_EQ(policy.name_space, kNTPSuggestionsNamespace);
-  EXPECT_FALSE(isTemporary(policy));
-  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kNTPSuggestionsNamespace));
-  ExpectDownloadSupport(kNTPSuggestionsNamespace, true);
-  ExpectRecentTab(kNTPSuggestionsNamespace, false);
-  ExpectOnlyOriginalTab(kNTPSuggestionsNamespace, false);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn
index d84ddee4..198ddcd 100644
--- a/components/offline_pages/core/BUILD.gn
+++ b/components/offline_pages/core/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -8,6 +8,36 @@
 
 static_library("core") {
   sources = [
+    "archive_manager.cc",
+    "archive_manager.h",
+    "client_namespace_constants.cc",
+    "client_namespace_constants.h",
+    "client_policy_controller.cc",
+    "client_policy_controller.h",
+    "offline_event_logger.cc",
+    "offline_event_logger.h",
+    "offline_page_archiver.h",
+    "offline_page_client_policy.h",
+    "offline_page_item.cc",
+    "offline_page_item.h",
+    "offline_page_metadata_store.cc",
+    "offline_page_metadata_store.h",
+    "offline_page_metadata_store_sql.cc",
+    "offline_page_metadata_store_sql.h",
+    "offline_page_model.cc",
+    "offline_page_model.h",
+    "offline_page_model_event_logger.cc",
+    "offline_page_model_event_logger.h",
+    "offline_page_model_impl.cc",
+    "offline_page_model_impl.h",
+    "offline_page_model_query.cc",
+    "offline_page_model_query.h",
+    "offline_page_storage_manager.cc",
+    "offline_page_storage_manager.h",
+    "offline_page_types.h",
+    "offline_store_types.h",
+    "snapshot_controller.cc",
+    "snapshot_controller.h",
     "task.cc",
     "task.h",
     "task_queue.cc",
@@ -15,35 +45,85 @@
   ]
 
   deps = [
+    ":switches",
     "//base",
+    "//components/keyed_service/core",
+    "//net",
+    "//sql:sql",
+    "//url",
   ]
 }
 
 static_library("test_support") {
   testonly = true
   sources = [
+    "offline_page_test_archiver.cc",
+    "offline_page_test_archiver.h",
+    "offline_page_test_store.cc",
+    "offline_page_test_store.h",
+    "stub_offline_page_model.cc",
+    "stub_offline_page_model.h",
     "test_task.cc",
     "test_task.h",
   ]
 
   deps = [
     ":core",
+    ":switches",
     "//base",
+    "//components/keyed_service/core",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+static_library("switches") {
+  sources = [
+    "offline_page_feature.cc",
+    "offline_page_feature.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/version_info",
   ]
 }
 
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "archive_manager_unittest.cc",
+    "client_policy_controller_unittest.cc",
+    "offline_event_logger_unittest.cc",
+    "offline_page_metadata_store_impl_unittest.cc",
+    "offline_page_model_event_logger_unittest.cc",
+    "offline_page_model_impl_unittest.cc",
+    "offline_page_storage_manager_unittest.cc",
+    "snapshot_controller_unittest.cc",
     "task_queue_unittest.cc",
     "task_unittest.cc",
   ]
 
   deps = [
     ":core",
+    ":switches",
     ":test_support",
     "//base",
     "//base/test:test_support",
-    "//testing/gtest:gtest",
+    "//sql:sql",
+    "//testing/gtest",
+    "//url",
   ]
 }
+
+if (is_android) {
+  java_cpp_enum("offline_page_model_enums_java") {
+    sources = [
+      "background/request_notifier.h",
+      "background/request_queue_results.h",
+      "background/save_page_request.h",
+      "offline_page_types.h",
+      "offline_store_types.h",
+    ]
+  }
+}
diff --git a/components/offline_pages/DEPS b/components/offline_pages/core/DEPS
similarity index 100%
rename from components/offline_pages/DEPS
rename to components/offline_pages/core/DEPS
diff --git a/components/offline_pages/core/archive_manager.cc b/components/offline_pages/core/archive_manager.cc
new file mode 100644
index 0000000..cd817767
--- /dev/null
+++ b/components/offline_pages/core/archive_manager.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/archive_manager.h"
+
+namespace offline_pages {
+
+namespace {
+
+using StorageStatsCallback =
+    base::Callback<void(const ArchiveManager::StorageStats& storage_stats)>;
+
+void EnsureArchivesDirCreatedImpl(const base::FilePath& archives_dir) {
+  CHECK(base::CreateDirectory(archives_dir));
+}
+
+void ExistsArchiveImpl(const base::FilePath& file_path,
+                       scoped_refptr<base::SequencedTaskRunner> task_runner,
+                       const base::Callback<void(bool)>& callback) {
+  task_runner->PostTask(FROM_HERE,
+                        base::Bind(callback, base::PathExists(file_path)));
+}
+
+void DeleteArchivesImpl(const std::vector<base::FilePath>& file_paths,
+                        scoped_refptr<base::SequencedTaskRunner> task_runner,
+                        const base::Callback<void(bool)>& callback) {
+  bool result = false;
+  for (const auto& file_path : file_paths) {
+    // Make sure delete happens on the left of || so that it is always executed.
+    result = base::DeleteFile(file_path, false) || result;
+  }
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+void GetAllArchivesImpl(
+    const base::FilePath& archive_dir,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const base::Callback<void(const std::set<base::FilePath>&)>& callback) {
+  std::set<base::FilePath> archive_paths;
+  base::FileEnumerator file_enumerator(archive_dir, false,
+                                       base::FileEnumerator::FILES);
+  for (base::FilePath archive_path = file_enumerator.Next();
+       !archive_path.empty(); archive_path = file_enumerator.Next()) {
+    archive_paths.insert(archive_path);
+  }
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, archive_paths));
+}
+
+void GetStorageStatsImpl(const base::FilePath& archive_dir,
+                         scoped_refptr<base::SequencedTaskRunner> task_runner,
+                         const StorageStatsCallback& callback) {
+  ArchiveManager::StorageStats storage_stats;
+  storage_stats.free_disk_space =
+      base::SysInfo::AmountOfFreeDiskSpace(archive_dir);
+  storage_stats.total_archives_size = base::ComputeDirectorySize(archive_dir);
+  task_runner->PostTask(FROM_HERE, base::Bind(callback, storage_stats));
+}
+
+}  // namespace
+
+// protected and used for testing.
+ArchiveManager::ArchiveManager() {}
+
+ArchiveManager::ArchiveManager(
+    const base::FilePath& archives_dir,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : archives_dir_(archives_dir), task_runner_(task_runner) {}
+
+ArchiveManager::~ArchiveManager() {}
+
+void ArchiveManager::EnsureArchivesDirCreated(const base::Closure& callback) {
+  task_runner_->PostTaskAndReply(
+      FROM_HERE, base::Bind(EnsureArchivesDirCreatedImpl, archives_dir_),
+      callback);
+}
+
+void ArchiveManager::ExistsArchive(const base::FilePath& archive_path,
+                                   const base::Callback<void(bool)>& callback) {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(ExistsArchiveImpl, archive_path,
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void ArchiveManager::DeleteArchive(const base::FilePath& archive_path,
+                                   const base::Callback<void(bool)>& callback) {
+  std::vector<base::FilePath> archive_paths = {archive_path};
+  DeleteMultipleArchives(archive_paths, callback);
+}
+
+void ArchiveManager::DeleteMultipleArchives(
+    const std::vector<base::FilePath>& archive_paths,
+    const base::Callback<void(bool)>& callback) {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(DeleteArchivesImpl, archive_paths,
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void ArchiveManager::GetAllArchives(
+    const base::Callback<void(const std::set<base::FilePath>&)>& callback)
+    const {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(GetAllArchivesImpl, archives_dir_,
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void ArchiveManager::GetStorageStats(
+    const StorageStatsCallback& callback) const {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(GetStorageStatsImpl, archives_dir_,
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/archive_manager.h b/components/offline_pages/core/archive_manager.h
new file mode 100644
index 0000000..dc92e7d
--- /dev/null
+++ b/components/offline_pages/core/archive_manager.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace offline_pages {
+
+// Class that manages all archive files for offline pages. They are all stored
+// in a specific archive directory.
+// All tasks are performed using |task_runner_|.
+class ArchiveManager {
+ public:
+  struct StorageStats {
+    int64_t free_disk_space;
+    int64_t total_archives_size;
+  };
+
+  ArchiveManager(const base::FilePath& archives_dir,
+                 const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+  virtual ~ArchiveManager();
+
+  // Creates archives directory if one does not exist yet;
+  virtual void EnsureArchivesDirCreated(const base::Closure& callback);
+
+  // Checks whether an archive with specified |archive_path| exists.
+  virtual void ExistsArchive(const base::FilePath& archive_path,
+                             const base::Callback<void(bool)>& callback);
+
+  // Deletes an archive with specified |archive_path|.
+  // It is considered successful to attempt to delete a file that does not
+  // exist.
+  virtual void DeleteArchive(const base::FilePath& archive_path,
+                             const base::Callback<void(bool)>& callback);
+
+  // Deletes multiple archives that are specified in |archive_paths|.
+  // It is considered successful to attempt to delete a file that does not
+  // exist.
+  virtual void DeleteMultipleArchives(
+      const std::vector<base::FilePath>& archive_paths,
+      const base::Callback<void(bool)>& callback);
+
+  // Lists all archive files in the archive directory.
+  virtual void GetAllArchives(
+      const base::Callback<void(const std::set<base::FilePath>&)>& callback)
+      const;
+
+  // Gets stats about archive storage, i.e. total archive sizes and free disk
+  // space.
+  virtual void GetStorageStats(
+      const base::Callback<void(const StorageStats& storage_sizes)>& callback)
+      const;
+
+ protected:
+  ArchiveManager();
+
+ private:
+  // Path under which all of the managed archives should be stored.
+  base::FilePath archives_dir_;
+  // Task runner for running file operations.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_ARCHIVE_MANAGER_H_
diff --git a/components/offline_pages/core/archive_manager_unittest.cc b/components/offline_pages/core/archive_manager_unittest.cc
new file mode 100644
index 0000000..57f868a8
--- /dev/null
+++ b/components/offline_pages/core/archive_manager_unittest.cc
@@ -0,0 +1,274 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/archive_manager.h"
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+const base::FilePath::CharType kMissingArchivePath[] =
+    FILE_PATH_LITERAL("missing_archive.path");
+}  // namespace
+
+enum class CallbackStatus {
+  NOT_CALLED,
+  CALLED_FALSE,
+  CALLED_TRUE,
+};
+
+class ArchiveManagerTest : public testing::Test {
+ public:
+  ArchiveManagerTest();
+  void SetUp() override;
+
+  void PumpLoop();
+  void ResetResults();
+
+  void ResetManager(const base::FilePath& file_path);
+  void Callback(bool result);
+  void GetAllArchivesCallback(const std::set<base::FilePath>& archive_paths);
+  void GetStorageStatsCallback(
+      const ArchiveManager::StorageStats& storage_sizes);
+
+  ArchiveManager* manager() { return manager_.get(); }
+  const base::FilePath& temp_path() const { return temp_dir_.GetPath(); }
+  CallbackStatus callback_status() const { return callback_status_; }
+  const std::set<base::FilePath>& last_archive_paths() const {
+    return last_archvie_paths_;
+  }
+  ArchiveManager::StorageStats last_storage_sizes() const {
+    return last_storage_sizes_;
+  }
+
+ private:
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::ScopedTempDir temp_dir_;
+
+  std::unique_ptr<ArchiveManager> manager_;
+  CallbackStatus callback_status_;
+  std::set<base::FilePath> last_archvie_paths_;
+  ArchiveManager::StorageStats last_storage_sizes_;
+};
+
+ArchiveManagerTest::ArchiveManagerTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_),
+      callback_status_(CallbackStatus::NOT_CALLED),
+      last_storage_sizes_({0, 0}) {}
+
+void ArchiveManagerTest::SetUp() {
+  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  ResetManager(temp_dir_.GetPath());
+}
+
+void ArchiveManagerTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void ArchiveManagerTest::ResetResults() {
+  callback_status_ = CallbackStatus::NOT_CALLED;
+  last_archvie_paths_.clear();
+}
+
+void ArchiveManagerTest::ResetManager(const base::FilePath& file_path) {
+  manager_.reset(
+      new ArchiveManager(file_path, base::ThreadTaskRunnerHandle::Get()));
+}
+
+void ArchiveManagerTest::Callback(bool result) {
+  callback_status_ =
+      result ? CallbackStatus::CALLED_TRUE : CallbackStatus::CALLED_FALSE;
+}
+
+void ArchiveManagerTest::GetAllArchivesCallback(
+    const std::set<base::FilePath>& archive_paths) {
+  last_archvie_paths_ = archive_paths;
+}
+
+void ArchiveManagerTest::GetStorageStatsCallback(
+    const ArchiveManager::StorageStats& storage_sizes) {
+  last_storage_sizes_ = storage_sizes;
+}
+
+TEST_F(ArchiveManagerTest, EnsureArchivesDirCreated) {
+  base::FilePath archive_dir =
+      temp_path().Append(FILE_PATH_LITERAL("test_path"));
+  ResetManager(archive_dir);
+  EXPECT_FALSE(base::PathExists(archive_dir));
+
+  // Ensure archives dir exists, when it doesn't.
+  manager()->EnsureArchivesDirCreated(
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this), true));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_TRUE(base::PathExists(archive_dir));
+
+  // Try again when the file already exists.
+  ResetResults();
+  manager()->EnsureArchivesDirCreated(
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this), true));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_TRUE(base::PathExists(archive_dir));
+}
+
+TEST_F(ArchiveManagerTest, ExistsArchive) {
+  base::FilePath archive_path = temp_path().Append(kMissingArchivePath);
+  manager()->ExistsArchive(
+      archive_path,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_FALSE, callback_status());
+
+  ResetResults();
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path));
+
+  manager()->ExistsArchive(
+      archive_path,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+}
+
+TEST_F(ArchiveManagerTest, DeleteMultipleArchives) {
+  base::FilePath archive_path_1;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
+  base::FilePath archive_path_2;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
+  base::FilePath archive_path_3;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
+
+  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
+
+  manager()->DeleteMultipleArchives(
+      archive_paths,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_FALSE(base::PathExists(archive_path_1));
+  EXPECT_FALSE(base::PathExists(archive_path_2));
+  EXPECT_TRUE(base::PathExists(archive_path_3));
+}
+
+TEST_F(ArchiveManagerTest, DeleteMultipleArchivesSomeDoNotExist) {
+  base::FilePath archive_path_1 = temp_path().Append(kMissingArchivePath);
+  base::FilePath archive_path_2;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
+  base::FilePath archive_path_3;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
+
+  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
+
+  EXPECT_FALSE(base::PathExists(archive_path_1));
+
+  manager()->DeleteMultipleArchives(
+      archive_paths,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_FALSE(base::PathExists(archive_path_1));
+  EXPECT_FALSE(base::PathExists(archive_path_2));
+  EXPECT_TRUE(base::PathExists(archive_path_3));
+}
+
+TEST_F(ArchiveManagerTest, DeleteMultipleArchivesNoneExist) {
+  base::FilePath archive_path_1 = temp_path().Append(kMissingArchivePath);
+  base::FilePath archive_path_2 =
+      temp_path().Append(FILE_PATH_LITERAL("other_missing_file.mhtml"));
+  base::FilePath archive_path_3;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
+
+  std::vector<base::FilePath> archive_paths = {archive_path_1, archive_path_2};
+
+  EXPECT_FALSE(base::PathExists(archive_path_1));
+  EXPECT_FALSE(base::PathExists(archive_path_2));
+
+  manager()->DeleteMultipleArchives(
+      archive_paths,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_FALSE(base::PathExists(archive_path_1));
+  EXPECT_FALSE(base::PathExists(archive_path_2));
+  EXPECT_TRUE(base::PathExists(archive_path_3));
+}
+
+TEST_F(ArchiveManagerTest, DeleteArchive) {
+  base::FilePath archive_path;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path));
+
+  manager()->DeleteArchive(
+      archive_path,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_FALSE(base::PathExists(archive_path));
+}
+
+TEST_F(ArchiveManagerTest, DeleteArchiveThatDoesNotExist) {
+  base::FilePath archive_path = temp_path().Append(kMissingArchivePath);
+  EXPECT_FALSE(base::PathExists(archive_path));
+
+  manager()->DeleteArchive(
+      archive_path,
+      base::Bind(&ArchiveManagerTest::Callback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(CallbackStatus::CALLED_TRUE, callback_status());
+  EXPECT_FALSE(base::PathExists(archive_path));
+}
+
+TEST_F(ArchiveManagerTest, GetAllArchives) {
+  base::FilePath archive_path_1;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
+  base::FilePath archive_path_2;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
+  base::FilePath archive_path_3;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_3));
+  std::vector<base::FilePath> expected_paths{archive_path_1, archive_path_2,
+                                             archive_path_3};
+  std::sort(expected_paths.begin(), expected_paths.end());
+
+  manager()->GetAllArchives(base::Bind(
+      &ArchiveManagerTest::GetAllArchivesCallback, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(3UL, last_archive_paths().size());
+  std::vector<base::FilePath> actual_paths(last_archive_paths().begin(),
+                                           last_archive_paths().end());
+  // Comparing one to one works because last_archive_paths set is sorted.
+  // Because some windows bots provide abbreviated path (e.g. chrome-bot becomes
+  // CHROME~2), this test only focuses on file name.
+  EXPECT_EQ(expected_paths[0].BaseName(), actual_paths[0].BaseName());
+  EXPECT_EQ(expected_paths[1].BaseName(), actual_paths[1].BaseName());
+  EXPECT_EQ(expected_paths[2].BaseName(), actual_paths[2].BaseName());
+}
+
+TEST_F(ArchiveManagerTest, GetStorageStats) {
+  base::FilePath archive_path_1;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_1));
+  base::FilePath archive_path_2;
+  EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_path(), &archive_path_2));
+
+  manager()->GetStorageStats(base::Bind(
+      &ArchiveManagerTest::GetStorageStatsCallback, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_GT(last_storage_sizes().free_disk_space, 0);
+  EXPECT_EQ(last_storage_sizes().total_archives_size,
+            base::ComputeDirectorySize(temp_path()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/BUILD.gn b/components/offline_pages/core/background/BUILD.gn
new file mode 100644
index 0000000..0c79a3b
--- /dev/null
+++ b/components/offline_pages/core/background/BUILD.gn
@@ -0,0 +1,122 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+static_library("background_offliner") {
+  sources = [
+    "add_request_task.cc",
+    "add_request_task.h",
+    "change_requests_state_task.cc",
+    "change_requests_state_task.h",
+    "cleanup_task.cc",
+    "cleanup_task.h",
+    "cleanup_task_factory.cc",
+    "cleanup_task_factory.h",
+    "connection_notifier.cc",
+    "connection_notifier.h",
+    "device_conditions.h",
+    "get_requests_task.cc",
+    "get_requests_task.h",
+    "initialize_store_task.cc",
+    "initialize_store_task.h",
+    "mark_attempt_aborted_task.cc",
+    "mark_attempt_aborted_task.h",
+    "mark_attempt_completed_task.cc",
+    "mark_attempt_completed_task.h",
+    "mark_attempt_started_task.cc",
+    "mark_attempt_started_task.h",
+    "offliner.h",
+    "offliner_factory.h",
+    "offliner_policy.h",
+    "offliner_policy_utils.cc",
+    "offliner_policy_utils.h",
+    "pick_request_task.cc",
+    "pick_request_task.h",
+    "remove_requests_task.cc",
+    "remove_requests_task.h",
+    "request_coordinator.cc",
+    "request_coordinator.h",
+    "request_coordinator_event_logger.cc",
+    "request_coordinator_event_logger.h",
+    "request_notifier.h",
+    "request_queue.cc",
+    "request_queue.h",
+    "request_queue_results.h",
+    "request_queue_store.h",
+    "request_queue_store_sql.cc",
+    "request_queue_store_sql.h",
+    "save_page_request.cc",
+    "save_page_request.h",
+    "scheduler.h",
+    "update_request_task.cc",
+    "update_request_task.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/keyed_service/core",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core:switches",
+    "//net",
+    "//sql:sql",
+    "//url",
+  ]
+}
+
+static_library("test_support") {
+  testonly = true
+  sources = [
+    "network_quality_provider_stub.cc",
+    "network_quality_provider_stub.h",
+    "offliner_factory_stub.cc",
+    "offliner_factory_stub.h",
+    "offliner_stub.cc",
+    "offliner_stub.h",
+    "request_queue_in_memory_store.cc",
+    "request_queue_in_memory_store.h",
+    "scheduler_stub.cc",
+    "scheduler_stub.h",
+  ]
+
+  deps = [
+    ":background_offliner",
+    "//base",
+    "//net",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "add_request_task_unittest.cc",
+    "change_requests_state_task_unittest.cc",
+    "cleanup_task_unittest.cc",
+    "get_requests_task_unittest.cc",
+    "initialize_store_task_unittest.cc",
+    "mark_attempt_aborted_task_unittest.cc",
+    "mark_attempt_completed_task_unittest.cc",
+    "mark_attempt_started_task_unittest.cc",
+    "pick_request_task_unittest.cc",
+    "remove_requests_task_unittest.cc",
+    "request_coordinator_event_logger_unittest.cc",
+    "request_coordinator_unittest.cc",
+    "request_queue_store_unittest.cc",
+    "request_queue_unittest.cc",
+    "save_page_request_unittest.cc",
+  ]
+
+  deps = [
+    ":background_offliner",
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core:switches",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/components/offline_pages/background/DEPS b/components/offline_pages/core/background/DEPS
similarity index 100%
rename from components/offline_pages/background/DEPS
rename to components/offline_pages/core/background/DEPS
diff --git a/components/offline_pages/core/background/add_request_task.cc b/components/offline_pages/core/background/add_request_task.cc
new file mode 100644
index 0000000..822afcb
--- /dev/null
+++ b/components/offline_pages/core/background/add_request_task.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/add_request_task.h"
+
+#include "base/bind.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+AddRequestTask::AddRequestTask(RequestQueueStore* store,
+                               const SavePageRequest& request,
+                               const RequestQueueStore::AddCallback& callback)
+    : store_(store),
+      request_(request),
+      callback_(callback),
+      weak_ptr_factory_(this) {}
+
+AddRequestTask::~AddRequestTask() {}
+
+void AddRequestTask::Run() {
+  AddRequest();
+}
+
+void AddRequestTask::AddRequest() {
+  store_->AddRequest(request_, base::Bind(&AddRequestTask::CompleteWithResult,
+                                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AddRequestTask::CompleteWithResult(ItemActionStatus status) {
+  callback_.Run(status);
+  TaskComplete();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/add_request_task.h b/components/offline_pages/core/background/add_request_task.h
new file mode 100644
index 0000000..9544f74
--- /dev/null
+++ b/components/offline_pages/core/background/add_request_task.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class AddRequestTask : public Task {
+ public:
+  AddRequestTask(RequestQueueStore* store,
+                 const SavePageRequest& request,
+                 const RequestQueueStore::AddCallback& callback);
+  ~AddRequestTask() override;
+
+  // Task implementation:
+  void Run() override;
+
+ private:
+  // Step 1: Add the request to the store.
+  void AddRequest();
+  // Step 2: Calls the callback with result, completes the task.
+  void CompleteWithResult(ItemActionStatus status);
+
+  // Store from which requests will be read.
+  RequestQueueStore* store_;
+  // Request to be added.
+  SavePageRequest request_;
+  // Callback used to return the read results.
+  RequestQueueStore::AddCallback callback_;
+
+  base::WeakPtrFactory<AddRequestTask> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(AddRequestTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_ADD_REQUEST_TASK_H_
diff --git a/components/offline_pages/core/background/add_request_task_unittest.cc b/components/offline_pages/core/background/add_request_task_unittest.cc
new file mode 100644
index 0000000..17b86e19
--- /dev/null
+++ b/components/offline_pages/core/background/add_request_task_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/add_request_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://otherexample.com");
+const ClientId kClientId1("download", "1234");
+const ClientId kClientId2("download", "5678");
+}  // namespace
+
+class AddRequestTaskTest : public testing::Test {
+ public:
+  AddRequestTaskTest();
+  ~AddRequestTaskTest() override;
+
+  void PumpLoop();
+  void ClearResults();
+
+  void InitializeStore(RequestQueueStore* store);
+
+  void AddRequestDone(ItemActionStatus status);
+
+  void GetRequestsCallback(
+      bool success,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  bool callback_called() const { return callback_called_; }
+
+  ItemActionStatus last_status() const { return status_; }
+
+  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+    return requests_;
+  }
+
+ private:
+  void InitializeStoreDone(bool success);
+
+  bool callback_called_;
+  ItemActionStatus status_;
+  std::vector<std::unique_ptr<SavePageRequest>> requests_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+AddRequestTaskTest::AddRequestTaskTest()
+    : callback_called_(false),
+      status_(ItemActionStatus::NOT_FOUND),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+AddRequestTaskTest::~AddRequestTaskTest() {}
+
+void AddRequestTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void AddRequestTaskTest::ClearResults() {
+  callback_called_ = false;
+  status_ = ItemActionStatus::NOT_FOUND;
+  requests_.clear();
+}
+
+void AddRequestTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&AddRequestTaskTest::InitializeStoreDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void AddRequestTaskTest::AddRequestDone(ItemActionStatus status) {
+  status_ = status;
+  callback_called_ = true;
+}
+
+void AddRequestTaskTest::GetRequestsCallback(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  requests_ = std::move(requests);
+}
+
+void AddRequestTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+TEST_F(AddRequestTaskTest, AddSingleRequest) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  AddRequestTask task(
+      &store, request_1,
+      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+                               base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, last_requests().size());
+  EXPECT_EQ(kRequestId1, last_requests().at(0)->request_id());
+  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
+  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
+  EXPECT_EQ(creation_time, last_requests().at(0)->creation_time());
+  EXPECT_TRUE(last_requests().at(0)->user_requested());
+}
+
+TEST_F(AddRequestTaskTest, AddMultipleRequests) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  base::Time creation_time_1 = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
+                            true);
+  AddRequestTask task(
+      &store, request_1,
+      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+  ClearResults();
+  base::Time creation_time_2 = base::Time::Now();
+  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time_2,
+                            true);
+  AddRequestTask task_2(
+      &store, request_2,
+      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+  task_2.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+                               base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(2ul, last_requests().size());
+  int request_2_index =
+      last_requests().at(0)->request_id() == kRequestId2 ? 0 : 1;
+  EXPECT_EQ(kRequestId2, last_requests().at(request_2_index)->request_id());
+  EXPECT_EQ(kUrl2, last_requests().at(request_2_index)->url());
+  EXPECT_EQ(kClientId2, last_requests().at(request_2_index)->client_id());
+  EXPECT_EQ(creation_time_2,
+            last_requests().at(request_2_index)->creation_time());
+  EXPECT_TRUE(last_requests().at(request_2_index)->user_requested());
+}
+
+TEST_F(AddRequestTaskTest, AddDuplicateRequest) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  base::Time creation_time_1 = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
+                            true);
+  AddRequestTask task(
+      &store, request_1,
+      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
+
+  ClearResults();
+  base::Time creation_time_2 = base::Time::Now();
+  // This was has the same request ID.
+  SavePageRequest request_2(kRequestId1, kUrl2, kClientId2, creation_time_2,
+                            true);
+  AddRequestTask task_2(
+      &store, request_2,
+      base::Bind(&AddRequestTaskTest::AddRequestDone, base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_EQ(ItemActionStatus::ALREADY_EXISTS, last_status());
+
+  store.GetRequests(base::Bind(&AddRequestTaskTest::GetRequestsCallback,
+                               base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, last_requests().size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/change_requests_state_task.cc b/components/offline_pages/core/background/change_requests_state_task.cc
new file mode 100644
index 0000000..da54045
--- /dev/null
+++ b/components/offline_pages/core/background/change_requests_state_task.cc
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+ChangeRequestsStateTask::ChangeRequestsStateTask(
+    RequestQueueStore* store,
+    const std::vector<int64_t>& request_ids,
+    const SavePageRequest::RequestState new_state,
+    const RequestQueueStore::UpdateCallback& callback)
+    : store_(store),
+      request_ids_(request_ids.begin(), request_ids.end()),
+      new_state_(new_state),
+      callback_(callback),
+      weak_ptr_factory_(this) {}
+
+ChangeRequestsStateTask::~ChangeRequestsStateTask() {}
+
+void ChangeRequestsStateTask::Run() {
+  ReadRequests();
+}
+
+void ChangeRequestsStateTask::ReadRequests() {
+  std::vector<int64_t> request_ids(request_ids_.begin(), request_ids_.end());
+  store_->GetRequestsByIds(request_ids,
+                           base::Bind(&ChangeRequestsStateTask::UpdateRequests,
+                                      weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ChangeRequestsStateTask::UpdateRequests(
+    std::unique_ptr<UpdateRequestsResult> read_result) {
+  if (read_result->store_state != StoreState::LOADED ||
+      read_result->updated_items.empty()) {
+    UpdateCompleted(std::move(read_result));
+    return;
+  }
+
+  // We are only going to make an update to the items that were found. Statuses
+  // of the missing items will be added at the end.
+  std::vector<SavePageRequest> items_to_update;
+  for (auto request : read_result->updated_items) {
+    request.set_request_state(new_state_);
+    items_to_update.push_back(request);
+  }
+
+  store_->UpdateRequests(items_to_update,
+                         base::Bind(&ChangeRequestsStateTask::UpdateCompleted,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ChangeRequestsStateTask::UpdateCompleted(
+    std::unique_ptr<UpdateRequestsResult> update_result) {
+  // Because the first step might not have found some of the items, we should
+  // look their IDs now and include in the final result as not found.
+
+  // Look up the missing items by removing the items present in the result
+  // statuses from original list of request IDs.
+  for (const auto& id_status_pair : update_result->item_statuses)
+    request_ids_.erase(id_status_pair.first);
+
+  // Update the final result for the items that are left in |request_ids_|, as
+  // these are identified as being missing from the final result.
+  for (int64_t request_id : request_ids_) {
+    update_result->item_statuses.push_back(
+        std::make_pair(request_id, ItemActionStatus::NOT_FOUND));
+  }
+
+  callback_.Run(std::move(update_result));
+  TaskComplete();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/change_requests_state_task.h b/components/offline_pages/core/background/change_requests_state_task.h
new file mode 100644
index 0000000..6354479
--- /dev/null
+++ b/components/offline_pages/core/background/change_requests_state_task.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class ChangeRequestsStateTask : public Task {
+ public:
+  ChangeRequestsStateTask(RequestQueueStore* store,
+                          const std::vector<int64_t>& request_ids,
+                          const SavePageRequest::RequestState new_state,
+                          const RequestQueueStore::UpdateCallback& callback);
+  ~ChangeRequestsStateTask() override;
+
+  // TaskQueue::Task implementation.
+  void Run() override;
+
+ private:
+  // Step 1. Reading the requests.
+  void ReadRequests();
+  // Step 2. Updates available requests.
+  void UpdateRequests(std::unique_ptr<UpdateRequestsResult> read_result);
+  // Step 3. Processes update result, calls callback.
+  void UpdateCompleted(std::unique_ptr<UpdateRequestsResult> update_result);
+
+  // Store that this task updates.
+  RequestQueueStore* store_;
+  // Request IDs to be updated. Kept as a set to remove duplicates and simplify
+  // the look up of requests that are not found in step 3.
+  std::unordered_set<int64_t> request_ids_;
+  // New state to be set on all entries.
+  SavePageRequest::RequestState new_state_;
+  // Callback to complete the task.
+  RequestQueueStore::UpdateCallback callback_;
+
+  base::WeakPtrFactory<ChangeRequestsStateTask> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChangeRequestsStateTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CHANGE_REQUESTS_STATE_TASK_H_
diff --git a/components/offline_pages/core/background/change_requests_state_task_unittest.cc b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
new file mode 100644
index 0000000..98826a2
--- /dev/null
+++ b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 43;
+const int64_t kRequestId3 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://another-example.com");
+const ClientId kClientId1("bookmark", "1234");
+const ClientId kClientId2("async", "5678");
+}  // namespace
+
+class ChangeRequestsStateTaskTest : public testing::Test {
+ public:
+  ChangeRequestsStateTaskTest();
+  ~ChangeRequestsStateTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddItemsToStore(RequestQueueStore* store);
+  void ChangeRequestsStateCallback(
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  UpdateRequestsResult* last_result() const { return result_.get(); }
+
+ private:
+  void InitializeStoreDone(bool success);
+  void AddRequestDone(ItemActionStatus status);
+
+  std::unique_ptr<UpdateRequestsResult> result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+ChangeRequestsStateTaskTest::ChangeRequestsStateTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+ChangeRequestsStateTaskTest::~ChangeRequestsStateTaskTest() {}
+
+void ChangeRequestsStateTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void ChangeRequestsStateTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(
+      base::Bind(&ChangeRequestsStateTaskTest::InitializeStoreDone,
+                 base::Unretained(this)));
+  PumpLoop();
+}
+
+void ChangeRequestsStateTaskTest::AddItemsToStore(RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  store->AddRequest(request_1,
+                    base::Bind(&ChangeRequestsStateTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
+                            true);
+  store->AddRequest(request_2,
+                    base::Bind(&ChangeRequestsStateTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void ChangeRequestsStateTaskTest::ChangeRequestsStateCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  result_ = std::move(result);
+}
+
+void ChangeRequestsStateTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void ChangeRequestsStateTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(ChangeRequestsStateTaskTest, UpdateWhenStoreEmpty) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1};
+  ChangeRequestsStateTask task(
+      &store, request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(ChangeRequestsStateTaskTest, UpdateSingleItem) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1};
+  ChangeRequestsStateTask task(
+      &store, request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+            last_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(ChangeRequestsStateTaskTest, UpdateMultipleItems) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
+  ChangeRequestsStateTask task(
+      &store, request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  ASSERT_EQ(2UL, last_result()->item_statuses.size());
+
+  // Calculating the position of the items in the vector here, because it does
+  // not matter, and might be platform dependent.
+  // |index_id_1| is expected to correspond to |kRequestId1|.
+  int index_id_1 =
+      last_result()->item_statuses.at(0).first == kRequestId1 ? 0 : 1;
+  // |index_id_2| is expected to correspond to |kRequestId2|.
+  int index_id_2 = 1 - index_id_1;
+
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(index_id_1).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(index_id_1).second);
+  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(index_id_2).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(index_id_2).second);
+  ASSERT_EQ(2UL, last_result()->updated_items.size());
+  EXPECT_EQ(kRequestId1,
+            last_result()->updated_items.at(index_id_1).request_id());
+  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+            last_result()->updated_items.at(index_id_1).request_state());
+  EXPECT_EQ(kRequestId2,
+            last_result()->updated_items.at(index_id_2).request_id());
+  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+            last_result()->updated_items.at(index_id_2).request_state());
+}
+
+TEST_F(ChangeRequestsStateTaskTest, EmptyRequestsList) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  std::vector<int64_t> request_ids;
+  ChangeRequestsStateTask task(
+      &store, request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(0UL, last_result()->item_statuses.size());
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(ChangeRequestsStateTaskTest, UpdateMissingItem) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
+  ChangeRequestsStateTask task(
+      &store, request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&ChangeRequestsStateTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  ASSERT_EQ(2UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(kRequestId3, last_result()->item_statuses.at(1).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(1).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(SavePageRequest::RequestState::PAUSED,
+            last_result()->updated_items.at(0).request_state());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/cleanup_task.cc b/components/offline_pages/core/background/cleanup_task.cc
new file mode 100644
index 0000000..ab02e4f0
--- /dev/null
+++ b/components/offline_pages/core/background/cleanup_task.cc
@@ -0,0 +1,109 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+CleanupTask::CleanupTask(RequestQueueStore* store,
+                         OfflinerPolicy* policy,
+                         RequestNotifier* notifier,
+                         RequestCoordinatorEventLogger* event_logger)
+    : store_(store),
+      policy_(policy),
+      notifier_(notifier),
+      event_logger_(event_logger),
+      weak_ptr_factory_(this) {}
+
+CleanupTask::~CleanupTask() {}
+
+void CleanupTask::Run() {
+  GetRequests();
+}
+
+void CleanupTask::GetRequests() {
+  // Get all the requests from the queue, we will classify them in the callback.
+  store_->GetRequests(
+      base::Bind(&CleanupTask::Prune, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CleanupTask::Prune(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  // If there is nothing to do, return right away.
+  if (requests.empty()) {
+    TaskComplete();
+    return;
+  }
+
+  // Get the expired requests to be removed from the queue.
+  std::vector<int64_t> expired_request_ids;
+  GetExpiredRequestIds(std::move(requests), &expired_request_ids);
+
+  // Continue processing by handling expired requests, if any.
+  if (expired_request_ids.size() == 0) {
+    TaskComplete();
+    return;
+  }
+
+  // TODO(petewil): Add UMA saying why we remove them
+  // TODO(petewil): Round trip the reason for deleting through the RQ
+  store_->RemoveRequests(expired_request_ids,
+                         base::Bind(&CleanupTask::OnRequestsExpired,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CleanupTask::OnRequestsExpired(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  RequestNotifier::BackgroundSavePageResult save_page_result(
+      RequestNotifier::BackgroundSavePageResult::EXPIRED);
+  for (const auto& request : result->updated_items) {
+    event_logger_->RecordDroppedSavePageRequest(
+        request.client_id().name_space, save_page_result, request.request_id());
+    notifier_->NotifyCompleted(request, save_page_result);
+  }
+
+  // The task is now done, return control to the task queue.
+  TaskComplete();
+}
+
+void CleanupTask::GetExpiredRequestIds(
+    std::vector<std::unique_ptr<SavePageRequest>> requests,
+    std::vector<int64_t>* expired_request_ids) {
+  for (auto& request : requests) {
+    // Check for requests past their expiration time or with too many tries.  If
+    // it is not still valid, push the request and the reason onto the deletion
+    // list.
+    OfflinerPolicyUtils::RequestExpirationStatus status =
+        OfflinerPolicyUtils::CheckRequestExpirationStatus(request.get(),
+                                                          policy_);
+
+    // TODO(petewil): The strategy of checking the state below relies on a
+    // reconciliation task, so at startup we convert any OFFLINING requests to
+    // AVAILABLE.  Otherwise, requests in the OFFLINING state when chromium dies
+    // will never be cleaned up.
+
+    // If we are not working on this request in an offliner, and it is not
+    // valid, put it on a list for removal.  We make the exception for current
+    // requests because the request might expire after being chosen and before
+    // we call cleanup, and we shouldn't delete the request while offlining it.
+    if (status != OfflinerPolicyUtils::RequestExpirationStatus::VALID &&
+        request->request_state() != SavePageRequest::RequestState::OFFLINING) {
+      // TODO(petewil): Push both request and reason, will need to change type
+      // of list to pairs.
+      expired_request_ids->push_back(request->request_id());
+    }
+  }
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/cleanup_task.h b/components/offline_pages/core/background/cleanup_task.h
new file mode 100644
index 0000000..fbe8e0c
--- /dev/null
+++ b/components/offline_pages/core/background/cleanup_task.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+class CleanupTask : public Task {
+ public:
+  CleanupTask(RequestQueueStore* store,
+              OfflinerPolicy* policy,
+              RequestNotifier* notifier,
+              RequestCoordinatorEventLogger* logger);
+  ~CleanupTask() override;
+
+  // TaskQueue::Task implementation, starts the async chain
+  void Run() override;
+
+ private:
+  // Step 1. get results from the store
+  void GetRequests();
+
+  // Step 2. Prune stale requests
+  void Prune(bool success,
+             std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Step 3. Send delete notifications for the expired requests.
+  void OnRequestsExpired(std::unique_ptr<UpdateRequestsResult> result);
+
+  // Build a list of IDs whose request has expired.
+  void GetExpiredRequestIds(
+      std::vector<std::unique_ptr<SavePageRequest>> requests,
+      std::vector<int64_t>* expired_request_ids);
+
+  // Member variables, all pointers are not owned here.
+  RequestQueueStore* store_;
+  OfflinerPolicy* policy_;
+  RequestNotifier* notifier_;
+  RequestCoordinatorEventLogger* event_logger_;
+  // Allows us to pass a weak pointer to callbacks.
+  base::WeakPtrFactory<CleanupTask> weak_ptr_factory_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_H_
diff --git a/components/offline_pages/core/background/cleanup_task_factory.cc b/components/offline_pages/core/background/cleanup_task_factory.cc
new file mode 100644
index 0000000..dea6657
--- /dev/null
+++ b/components/offline_pages/core/background/cleanup_task_factory.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task_factory.h"
+
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+
+namespace offline_pages {
+
+// Capture the common parameters that we will need to make a pick task,
+// and use them when making tasks.  Create this once each session, and
+// use it to build a picker task when needed.
+CleanupTaskFactory::CleanupTaskFactory(
+    OfflinerPolicy* policy,
+    RequestNotifier* notifier,
+    RequestCoordinatorEventLogger* event_logger)
+    : policy_(policy), notifier_(notifier), event_logger_(event_logger) {
+  DCHECK(policy);
+  DCHECK(notifier);
+  DCHECK(event_logger);
+}
+
+CleanupTaskFactory::~CleanupTaskFactory() {}
+
+std::unique_ptr<CleanupTask> CleanupTaskFactory::CreateCleanupTask(
+    RequestQueueStore* store) {
+  std::unique_ptr<CleanupTask> task(
+      new CleanupTask(store, policy_, notifier_, event_logger_));
+  return task;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/cleanup_task_factory.h b/components/offline_pages/core/background/cleanup_task_factory.h
new file mode 100644
index 0000000..dda4a74
--- /dev/null
+++ b/components/offline_pages/core/background/cleanup_task_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
+
+#include <stdint.h>
+
+#include <set>
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class RequestCoordinatorEventLogger;
+class RequestNotifier;
+class RequestQueueStore;
+
+class CleanupTaskFactory {
+ public:
+  CleanupTaskFactory(OfflinerPolicy* policy,
+                     RequestNotifier* notifier,
+                     RequestCoordinatorEventLogger* event_logger);
+
+  ~CleanupTaskFactory();
+
+  std::unique_ptr<CleanupTask> CreateCleanupTask(RequestQueueStore* store);
+
+ private:
+  // Unowned pointer to the Policy
+  OfflinerPolicy* policy_;
+  // Unowned pointer to the notifier
+  RequestNotifier* notifier_;
+  // Unowned pointer to the EventLogger
+  RequestCoordinatorEventLogger* event_logger_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_CLEANUP_TASK_FACTORY_H_
diff --git a/components/offline_pages/core/background/cleanup_task_unittest.cc b/components/offline_pages/core/background/cleanup_task_unittest.cc
new file mode 100644
index 0000000..acfb33b
--- /dev/null
+++ b/components/offline_pages/core/background/cleanup_task_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/cleanup_task.h"
+
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// Data for request 1.
+const int64_t kRequestId1 = 17;
+const GURL kUrl1("https://google.com");
+const ClientId kClientId1("bookmark", "1234");
+// Data for request 2.
+const int64_t kRequestId2 = 42;
+const GURL kUrl2("http://nytimes.com");
+const ClientId kClientId2("bookmark", "5678");
+const bool kUserRequested = true;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+                                    GURL(""),
+                                    ClientId("", ""),
+                                    base::Time(),
+                                    true);
+}  // namespace
+
+// TODO: Refactor this stub class into its own file, use in Pick Request Task
+// Test too.
+// Helper class needed by the CleanupTask
+class RequestNotifierStub : public RequestNotifier {
+ public:
+  RequestNotifierStub()
+      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
+
+  void NotifyAdded(const SavePageRequest& request) override {}
+  void NotifyChanged(const SavePageRequest& request) override {}
+
+  void NotifyCompleted(const SavePageRequest& request,
+                       BackgroundSavePageResult status) override {
+    last_expired_request_ = request;
+    last_request_expiration_status_ = status;
+    total_expired_requests_++;
+  }
+
+  const SavePageRequest& last_expired_request() {
+    return last_expired_request_;
+  }
+
+  RequestCoordinator::BackgroundSavePageResult
+  last_request_expiration_status() {
+    return last_request_expiration_status_;
+  }
+
+  int32_t total_expired_requests() { return total_expired_requests_; }
+
+ private:
+  BackgroundSavePageResult last_request_expiration_status_;
+  SavePageRequest last_expired_request_;
+  int32_t total_expired_requests_;
+};
+
+class CleanupTaskTest : public testing::Test {
+ public:
+  CleanupTaskTest();
+
+  ~CleanupTaskTest() override;
+
+  void SetUp() override;
+
+  void PumpLoop();
+
+  void AddRequestDone(ItemActionStatus status);
+
+  void GetRequestsCallback(
+      bool success,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  void QueueRequests(const SavePageRequest& request1,
+                     const SavePageRequest& request2);
+
+  // Reset the factory and the task using the current policy.
+  void MakeFactoryAndTask();
+
+  RequestNotifierStub* GetNotifier() { return notifier_.get(); }
+
+  CleanupTask* task() { return task_.get(); }
+  RequestQueueStore* store() { return store_.get(); }
+  OfflinerPolicy* policy() { return policy_.get(); }
+  std::vector<std::unique_ptr<SavePageRequest>>& found_requests() {
+    return found_requests_;
+  }
+
+ protected:
+  void InitializeStoreDone(bool success);
+
+  std::unique_ptr<RequestQueueStore> store_;
+  std::unique_ptr<RequestNotifierStub> notifier_;
+  std::unique_ptr<SavePageRequest> last_picked_;
+  std::unique_ptr<OfflinerPolicy> policy_;
+  RequestCoordinatorEventLogger event_logger_;
+  std::unique_ptr<CleanupTaskFactory> factory_;
+  std::unique_ptr<CleanupTask> task_;
+  std::vector<std::unique_ptr<SavePageRequest>> found_requests_;
+
+ private:
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+CleanupTaskTest::CleanupTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+CleanupTaskTest::~CleanupTaskTest() {}
+
+void CleanupTaskTest::SetUp() {
+  DeviceConditions conditions;
+  store_.reset(new RequestQueueInMemoryStore());
+  policy_.reset(new OfflinerPolicy());
+  notifier_.reset(new RequestNotifierStub());
+  MakeFactoryAndTask();
+
+  store_->Initialize(base::Bind(&CleanupTaskTest::InitializeStoreDone,
+                                base::Unretained(this)));
+  PumpLoop();
+}
+
+void CleanupTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void CleanupTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+void CleanupTaskTest::GetRequestsCallback(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  found_requests_ = std::move(requests);
+}
+
+// Test helper to queue the two given requests.
+void CleanupTaskTest::QueueRequests(const SavePageRequest& request1,
+                                    const SavePageRequest& request2) {
+  DeviceConditions conditions;
+  std::set<int64_t> disabled_requests;
+  // Add test requests on the Queue.
+  store_->AddRequest(request1, base::Bind(&CleanupTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+  store_->AddRequest(request2, base::Bind(&CleanupTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+
+  // Pump the loop to give the async queue the opportunity to do the adds.
+  PumpLoop();
+}
+
+void CleanupTaskTest::MakeFactoryAndTask() {
+  factory_.reset(
+      new CleanupTaskFactory(policy_.get(), notifier_.get(), &event_logger_));
+  DeviceConditions conditions;
+  task_ = factory_->CreateCleanupTask(store_.get());
+}
+
+void CleanupTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+TEST_F(CleanupTaskTest, CleanupExpiredRequest) {
+  base::Time creation_time = base::Time::Now();
+  base::Time expired_time =
+      creation_time - base::TimeDelta::FromSeconds(
+                          policy()->GetRequestExpirationTimeInSeconds() + 10);
+  // Request2 will be expired, request1 will be current.
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time,
+                           kUserRequested);
+  QueueRequests(request1, request2);
+
+  // Initiate cleanup.
+  task()->Run();
+  PumpLoop();
+
+  // See what is left in the queue, should be just the other request.
+  store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(1UL, found_requests().size());
+  EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, CleanupStartCountExceededRequest) {
+  base::Time creation_time = base::Time::Now();
+  // Request2 will have an exceeded start count.
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_started_attempt_count(policy()->GetMaxStartedTries());
+  QueueRequests(request1, request2);
+
+  // Initiate cleanup.
+  task()->Run();
+  PumpLoop();
+
+  // See what is left in the queue, should be just the other request.
+  store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(1UL, found_requests().size());
+  EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, CleanupCompletionCountExceededRequest) {
+  base::Time creation_time = base::Time::Now();
+  // Request2 will have an exceeded completion count.
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+  QueueRequests(request1, request2);
+
+  // Initiate cleanup.
+  task()->Run();
+  PumpLoop();
+
+  // See what is left in the queue, should be just the other request.
+  store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(1UL, found_requests().size());
+  EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+TEST_F(CleanupTaskTest, IgnoreRequestInProgress) {
+  base::Time creation_time = base::Time::Now();
+  // Both requests will have an exceeded completion count.
+  // The first request will be marked as started.
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  request1.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+  request1.MarkAttemptStarted(creation_time);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(policy()->GetMaxCompletedTries());
+  QueueRequests(request1, request2);
+
+  // Initiate cleanup.
+  task()->Run();
+  PumpLoop();
+
+  // See what is left in the queue, request1 should be left in the queue even
+  // though it is expired because it was listed as in-progress while cleaning.
+  // Request2 should have been cleaned out of the queue.
+  store()->GetRequests(base::Bind(&CleanupTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(1UL, found_requests().size());
+  EXPECT_EQ(kRequestId1, found_requests().at(0)->request_id());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/connection_notifier.cc b/components/offline_pages/core/background/connection_notifier.cc
new file mode 100644
index 0000000..6d2f98d
--- /dev/null
+++ b/components/offline_pages/core/background/connection_notifier.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/connection_notifier.h"
+
+namespace offline_pages {
+
+ConnectionNotifier::ConnectionNotifier(
+    const ConnectionNotifier::ConnectedCallback& callback)
+    : callback_(callback) {
+  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
+}
+
+ConnectionNotifier::~ConnectionNotifier() {
+  net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+}
+
+void ConnectionNotifier::OnConnectionTypeChanged(
+    net::NetworkChangeNotifier::ConnectionType type) {
+  if (type != net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE)
+    callback_.Run();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/connection_notifier.h b/components/offline_pages/core/background/connection_notifier.h
new file mode 100644
index 0000000..36730e6
--- /dev/null
+++ b/components/offline_pages/core/background/connection_notifier.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "net/base/network_change_notifier.h"
+
+namespace offline_pages {
+
+class ConnectionNotifier
+    : public net::NetworkChangeNotifier::ConnectionTypeObserver {
+ public:
+  // Callback to call when we become connected.
+  typedef base::Callback<void()> ConnectedCallback;
+
+  ConnectionNotifier(const ConnectionNotifier::ConnectedCallback& callback);
+  ~ConnectionNotifier() override;
+
+  // net::NetworkChangeNotifier::ConnectionTypeObserver implementation.
+  void OnConnectionTypeChanged(
+      net::NetworkChangeNotifier::ConnectionType type) override;
+
+ private:
+  base::Callback<void()> callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionNotifier);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_CONNECTION_NOTIFIER_H_
diff --git a/components/offline_pages/core/background/device_conditions.h b/components/offline_pages/core/background/device_conditions.h
new file mode 100644
index 0000000..25245a5
--- /dev/null
+++ b/components/offline_pages/core/background/device_conditions.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
+
+#include "net/base/network_change_notifier.h"
+
+namespace offline_pages {
+
+// Device network and power conditions.
+class DeviceConditions {
+ public:
+  DeviceConditions(
+      bool power_connected,
+      int battery_percentage,
+      net::NetworkChangeNotifier::ConnectionType net_connection_type)
+      : power_connected_(power_connected),
+        battery_percentage_(battery_percentage),
+        net_connection_type_(net_connection_type) {}
+
+  DeviceConditions()
+      : power_connected_(true),
+        battery_percentage_(75),
+        net_connection_type_(net::NetworkChangeNotifier::CONNECTION_WIFI) {}
+
+  // Returns whether power is connected.
+  bool IsPowerConnected() const { return power_connected_; }
+
+  // Returns percentage of remaining battery power (0-100).
+  int GetBatteryPercentage() const { return battery_percentage_; }
+
+  // Returns the current type of network connection, if any.
+  net::NetworkChangeNotifier::ConnectionType GetNetConnectionType() const {
+    return net_connection_type_;
+  }
+
+ private:
+  const bool power_connected_;
+  const int battery_percentage_;
+  const net::NetworkChangeNotifier::ConnectionType net_connection_type_;
+
+  // NOTE: We intentionally allow the default copy constructor and assignment.
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_DEVICE_CONDITIONS_H_
diff --git a/components/offline_pages/core/background/get_requests_task.cc b/components/offline_pages/core/background/get_requests_task.cc
new file mode 100644
index 0000000..9444a3d
--- /dev/null
+++ b/components/offline_pages/core/background/get_requests_task.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/get_requests_task.h"
+
+#include <vector>
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+GetRequestsTask::GetRequestsTask(
+    RequestQueueStore* store,
+    const RequestQueueStore::GetRequestsCallback& callback)
+    : store_(store), callback_(callback), weak_ptr_factory_(this) {}
+
+GetRequestsTask::~GetRequestsTask() {}
+
+void GetRequestsTask::Run() {
+  ReadRequest();
+}
+
+void GetRequestsTask::ReadRequest() {
+  store_->GetRequests(base::Bind(&GetRequestsTask::CompleteWithResult,
+                                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GetRequestsTask::CompleteWithResult(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  callback_.Run(success, std::move(requests));
+  TaskComplete();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/get_requests_task.h b/components/offline_pages/core/background/get_requests_task.h
new file mode 100644
index 0000000..00333c0
--- /dev/null
+++ b/components/offline_pages/core/background/get_requests_task.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class GetRequestsTask : public Task {
+ public:
+  GetRequestsTask(RequestQueueStore* store,
+                  const RequestQueueStore::GetRequestsCallback& callback);
+  ~GetRequestsTask() override;
+
+  // Task implementation:
+  void Run() override;
+
+ private:
+  // Step 1: Read the requests from he store.
+  void ReadRequest();
+  // Step 2: Calls the callback with result, completes the task.
+  void CompleteWithResult(
+      bool success,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Store from which requests will be read.
+  RequestQueueStore* store_;
+  // Callback used to return the read results.
+  RequestQueueStore::GetRequestsCallback callback_;
+
+  base::WeakPtrFactory<GetRequestsTask> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(GetRequestsTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_GET_REQUESTS_TASK_H_
diff --git a/components/offline_pages/core/background/get_requests_task_unittest.cc b/components/offline_pages/core/background/get_requests_task_unittest.cc
new file mode 100644
index 0000000..9ebfa7c
--- /dev/null
+++ b/components/offline_pages/core/background/get_requests_task_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/get_requests_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://otherexample.com");
+const ClientId kClientId1("download", "1234");
+const ClientId kClientId2("download", "5678");
+}  // namespace
+
+class GetRequestsTaskTest : public testing::Test {
+ public:
+  GetRequestsTaskTest();
+  ~GetRequestsTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddItemsToStore(RequestQueueStore* store);
+  void GetRequestsCallback(
+      bool success,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  bool callback_called() const { return callback_called_; }
+
+  bool last_call_successful() const { return success_; }
+
+  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+    return requests_;
+  }
+
+ private:
+  void InitializeStoreDone(bool success);
+  void AddRequestDone(ItemActionStatus status);
+
+  bool callback_called_;
+  bool success_;
+  std::vector<std::unique_ptr<SavePageRequest>> requests_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+GetRequestsTaskTest::GetRequestsTaskTest()
+    : callback_called_(false),
+      success_(false),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+GetRequestsTaskTest::~GetRequestsTaskTest() {}
+
+void GetRequestsTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void GetRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&GetRequestsTaskTest::InitializeStoreDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void GetRequestsTaskTest::AddItemsToStore(RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  store->AddRequest(request_1, base::Bind(&GetRequestsTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+  creation_time = base::Time::Now();
+  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
+                            true);
+  store->AddRequest(request_2, base::Bind(&GetRequestsTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+}
+
+void GetRequestsTaskTest::GetRequestsCallback(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  callback_called_ = true;
+  success_ = success;
+  requests_ = std::move(requests);
+}
+
+void GetRequestsTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void GetRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(GetRequestsTaskTest, GetFromEmptyStore) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  GetRequestsTask task(&store,
+                       base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(last_call_successful());
+  EXPECT_TRUE(last_requests().empty());
+}
+
+TEST_F(GetRequestsTaskTest, GetMultipleRequests) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemsToStore(&store);
+
+  GetRequestsTask task(&store,
+                       base::Bind(&GetRequestsTaskTest::GetRequestsCallback,
+                                  base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(last_call_successful());
+  ASSERT_EQ(2UL, last_requests().size());
+
+  int id_1_index = last_requests().at(0)->request_id() == kRequestId1 ? 0 : 1;
+  int id_2_index = 1 - id_1_index;
+  EXPECT_EQ(kRequestId1, last_requests().at(id_1_index)->request_id());
+  EXPECT_EQ(kRequestId2, last_requests().at(id_2_index)->request_id());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/initialize_store_task.cc b/components/offline_pages/core/background/initialize_store_task.cc
new file mode 100644
index 0000000..8c680755
--- /dev/null
+++ b/components/offline_pages/core/background/initialize_store_task.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/initialize_store_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+const int kRestartAttemptsMaximum = 3;
+
+InitializeStoreTask::InitializeStoreTask(
+    RequestQueueStore* store,
+    const RequestQueueStore::InitializeCallback& callback)
+    : store_(store),
+      reset_attempts_left_(kRestartAttemptsMaximum),
+      callback_(callback),
+      weak_ptr_factory_(this) {}
+
+InitializeStoreTask::~InitializeStoreTask() {}
+
+void InitializeStoreTask::Run() {
+  InitializeStore();
+}
+
+void InitializeStoreTask::InitializeStore() {
+  store_->Initialize(base::Bind(&InitializeStoreTask::CompleteIfSuccessful,
+                                weak_ptr_factory_.GetWeakPtr()));
+}
+
+void InitializeStoreTask::CompleteIfSuccessful(bool success) {
+  if (success) {
+    callback_.Run(true);
+    TaskComplete();
+    return;
+  }
+
+  TryToResetStore();
+}
+
+void InitializeStoreTask::TryToResetStore() {
+  if (reset_attempts_left_ == 0) {
+    callback_.Run(false);
+    TaskComplete();
+    return;
+  }
+
+  reset_attempts_left_--;
+  store_->Reset(base::Bind(&InitializeStoreTask::OnStoreResetDone,
+                           weak_ptr_factory_.GetWeakPtr()));
+}
+
+void InitializeStoreTask::OnStoreResetDone(bool success) {
+  if (success)
+    InitializeStore();
+  else
+    TryToResetStore();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/initialize_store_task.h b/components/offline_pages/core/background/initialize_store_task.h
new file mode 100644
index 0000000..0cceee8
--- /dev/null
+++ b/components/offline_pages/core/background/initialize_store_task.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
+
+#include <stdint.h>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+// Task performing a request queue store initialization and reset if one is
+// necessary. Reset is triggered in case the initialization fails. If reset
+// fails (perhaps not able to delete the folder or raze the database in case of
+// SQLite-backed store) a new attempt can be made. Number of attempts is limited
+// by |reset_attempts_left_|. Successful reset will be followed by another
+// attempt to initialize the database.
+//
+// Task is completed when either the store is correctly initialized or there are
+// not more reset attempts left.
+class InitializeStoreTask : public Task {
+ public:
+  InitializeStoreTask(RequestQueueStore* store,
+                      const RequestQueueStore::InitializeCallback& callback);
+  ~InitializeStoreTask() override;
+
+  // TaskQueue::Task implementation.
+  void Run() override;
+
+ private:
+  // Step 1. Initialize store.
+  void InitializeStore();
+  // Step 2a. Completes initialization if successful or tries to reset if there
+  // are more attempts left.
+  void CompleteIfSuccessful(bool success);
+  // Step 2b. Reset store in case initialization fails.
+  void TryToResetStore();
+  // Step 3. If 2b was successful go to step 1, else try 2b again.
+  void OnStoreResetDone(bool success);
+
+  // Store that this task initializes.
+  RequestQueueStore* store_;
+  // Number of attempts left to reset and reinitialize the store.
+  int reset_attempts_left_;
+  // Callback to complete the task.
+  RequestQueueStore::InitializeCallback callback_;
+
+  base::WeakPtrFactory<InitializeStoreTask> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(InitializeStoreTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND____CORE_BACKGROUND_INITIALIZE_STORE_TASK_H_
diff --git a/components/offline_pages/core/background/initialize_store_task_unittest.cc b/components/offline_pages/core/background/initialize_store_task_unittest.cc
new file mode 100644
index 0000000..c016b73
--- /dev/null
+++ b/components/offline_pages/core/background/initialize_store_task_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/initialize_store_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+using TestScenario = RequestQueueInMemoryStore::TestScenario;
+
+class InitializeStoreTaskTest : public testing::Test {
+ public:
+  InitializeStoreTaskTest();
+  ~InitializeStoreTaskTest() override;
+
+  void PumpLoop();
+  void RunNextStep();
+
+  void InitializeCallback(bool success);
+
+  bool callback_called() const { return callback_called_; }
+
+  bool last_call_successful() const { return success_; }
+
+ private:
+  bool callback_called_;
+  bool success_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+InitializeStoreTaskTest::InitializeStoreTaskTest()
+    : callback_called_(false),
+      success_(false),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+InitializeStoreTaskTest::~InitializeStoreTaskTest() {}
+
+void InitializeStoreTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void InitializeStoreTaskTest::RunNextStep() {
+  // Only runs tasks that are already scheduled and available. If running these
+  // tasks post new tasks to the runner, they will not be triggered. This allows
+  // for running InitializeStoreTask one step at a time.
+  task_runner_->RunPendingTasks();
+}
+
+void InitializeStoreTaskTest::InitializeCallback(bool success) {
+  callback_called_ = true;
+  success_ = success;
+}
+
+TEST_F(InitializeStoreTaskTest, SuccessfulInitialization) {
+  RequestQueueInMemoryStore store;
+  InitializeStoreTask task(
+      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+                         base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(last_call_successful());
+  EXPECT_EQ(StoreState::LOADED, store.state());
+}
+
+TEST_F(InitializeStoreTaskTest, SuccessfulReset) {
+  RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_SUCCESS);
+  InitializeStoreTask task(
+      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+                         base::Unretained(this)));
+  task.Run();
+  EXPECT_FALSE(callback_called());
+  EXPECT_EQ(StoreState::FAILED_LOADING, store.state());
+
+  RunNextStep();
+  EXPECT_FALSE(callback_called());
+  EXPECT_EQ(StoreState::NOT_LOADED, store.state());
+
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_TRUE(last_call_successful());
+  EXPECT_EQ(StoreState::LOADED, store.state());
+}
+
+TEST_F(InitializeStoreTaskTest, FailedReset) {
+  RequestQueueInMemoryStore store(TestScenario::LOAD_FAILED_RESET_FAILED);
+  InitializeStoreTask task(
+      &store, base::Bind(&InitializeStoreTaskTest::InitializeCallback,
+                         base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  EXPECT_TRUE(callback_called());
+  EXPECT_FALSE(last_call_successful());
+  EXPECT_EQ(StoreState::FAILED_RESET, store.state());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_aborted_task.cc b/components/offline_pages/core/background/mark_attempt_aborted_task.cc
new file mode 100644
index 0000000..007dfa2
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_aborted_task.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+MarkAttemptAbortedTask::MarkAttemptAbortedTask(
+    RequestQueueStore* store,
+    int64_t request_id,
+    const RequestQueueStore::UpdateCallback& callback)
+    : UpdateRequestTask(store, request_id, callback) {}
+
+MarkAttemptAbortedTask::~MarkAttemptAbortedTask() {}
+
+void MarkAttemptAbortedTask::UpdateRequestImpl(
+    std::unique_ptr<UpdateRequestsResult> read_result) {
+  if (!ValidateReadResult(read_result.get())) {
+    CompleteWithResult(std::move(read_result));
+    return;
+  }
+
+  // It is perfectly fine to reuse the read_result->updated_items collection, as
+  // it is owned by this callback and will be destroyed when out of scope.
+  read_result->updated_items[0].MarkAttemptAborted();
+  store()->UpdateRequests(
+      read_result->updated_items,
+      base::Bind(&MarkAttemptAbortedTask::CompleteWithResult, GetWeakPtr()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_aborted_task.h b/components/offline_pages/core/background/mark_attempt_aborted_task.h
new file mode 100644
index 0000000..4deb15f
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_aborted_task.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
+
+#include <stdint.h>
+
+#include "components/offline_pages/core/background/update_request_task.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class RequestQueueStore;
+
+class MarkAttemptAbortedTask : public UpdateRequestTask {
+ public:
+  MarkAttemptAbortedTask(RequestQueueStore* store,
+                         int64_t request_id,
+                         const RequestQueueStore::UpdateCallback& callback);
+  ~MarkAttemptAbortedTask() override;
+
+ protected:
+  // UpdateRequestTask implementation:
+  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_ABORTED_TASK_H_
diff --git a/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
new file mode 100644
index 0000000..0a94c69
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const ClientId kClientId1("download", "1234");
+}  // namespace
+
+class MarkAttemptAbortedTaskTest : public testing::Test {
+ public:
+  MarkAttemptAbortedTaskTest();
+  ~MarkAttemptAbortedTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddItemToStore(RequestQueueStore* store);
+  void ChangeRequestsStateCallback(
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  void ClearResults();
+
+  UpdateRequestsResult* last_result() const { return result_.get(); }
+
+ private:
+  void InitializeStoreDone(bool success);
+  void AddRequestDone(ItemActionStatus status);
+
+  std::unique_ptr<UpdateRequestsResult> result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+MarkAttemptAbortedTaskTest::MarkAttemptAbortedTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+MarkAttemptAbortedTaskTest::~MarkAttemptAbortedTaskTest() {}
+
+void MarkAttemptAbortedTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void MarkAttemptAbortedTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&MarkAttemptAbortedTaskTest::InitializeStoreDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptAbortedTaskTest::AddItemToStore(RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  store->AddRequest(request_1,
+                    base::Bind(&MarkAttemptAbortedTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  result_ = std::move(result);
+}
+
+void MarkAttemptAbortedTaskTest::ClearResults() {
+  result_.reset(nullptr);
+}
+
+void MarkAttemptAbortedTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void MarkAttemptAbortedTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenStoreEmpty) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  MarkAttemptAbortedTask task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenExists) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemToStore(&store);
+
+  // First mark attempt started.
+  MarkAttemptStartedTask start_request_task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  start_request_task.Run();
+  PumpLoop();
+  ClearResults();
+
+  MarkAttemptAbortedTask task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            last_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(MarkAttemptAbortedTaskTest, MarkAttemptAbortedWhenItemMissing) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemToStore(&store);
+
+  MarkAttemptAbortedTask task(
+      &store, kRequestId2,
+      base::Bind(&MarkAttemptAbortedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_completed_task.cc b/components/offline_pages/core/background/mark_attempt_completed_task.cc
new file mode 100644
index 0000000..8f646db
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_completed_task.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/time/time.h"
+
+namespace offline_pages {
+
+MarkAttemptCompletedTask::MarkAttemptCompletedTask(
+    RequestQueueStore* store,
+    int64_t request_id,
+    const RequestQueueStore::UpdateCallback& callback)
+    : UpdateRequestTask(store, request_id, callback) {}
+
+MarkAttemptCompletedTask::~MarkAttemptCompletedTask() {}
+
+void MarkAttemptCompletedTask::UpdateRequestImpl(
+    std::unique_ptr<UpdateRequestsResult> read_result) {
+  if (!ValidateReadResult(read_result.get())) {
+    CompleteWithResult(std::move(read_result));
+    return;
+  }
+
+  // It is perfectly fine to reuse the read_result->updated_items collection, as
+  // it is owned by this callback and will be destroyed when out of scope.
+  read_result->updated_items[0].MarkAttemptCompleted();
+  store()->UpdateRequests(
+      read_result->updated_items,
+      base::Bind(&MarkAttemptCompletedTask::CompleteWithResult, GetWeakPtr()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_completed_task.h b/components/offline_pages/core/background/mark_attempt_completed_task.h
new file mode 100644
index 0000000..f3e3e4c4b
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_completed_task.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/update_request_task.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class RequestQueueStore;
+
+class MarkAttemptCompletedTask : public UpdateRequestTask {
+ public:
+  MarkAttemptCompletedTask(RequestQueueStore* store,
+                           int64_t request_id,
+                           const RequestQueueStore::UpdateCallback& callback);
+  ~MarkAttemptCompletedTask() override;
+
+ protected:
+  // UpdateRequestTask implementation:
+  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_COMPLETED_TASK_H_
diff --git a/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
new file mode 100644
index 0000000..e47046b
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
@@ -0,0 +1,132 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const ClientId kClientId1("download", "1234");
+}  // namespace
+
+class MarkAttemptCompletedTaskTest : public testing::Test {
+ public:
+  MarkAttemptCompletedTaskTest();
+  ~MarkAttemptCompletedTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddStartedItemToStore(RequestQueueStore* store);
+  void ChangeRequestsStateCallback(
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  UpdateRequestsResult* last_result() const { return result_.get(); }
+
+ private:
+  void InitializeStoreDone(bool success);
+  void AddRequestDone(ItemActionStatus status);
+
+  std::unique_ptr<UpdateRequestsResult> result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+MarkAttemptCompletedTaskTest::MarkAttemptCompletedTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+MarkAttemptCompletedTaskTest::~MarkAttemptCompletedTaskTest() {}
+
+void MarkAttemptCompletedTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void MarkAttemptCompletedTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(
+      base::Bind(&MarkAttemptCompletedTaskTest::InitializeStoreDone,
+                 base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptCompletedTaskTest::AddStartedItemToStore(
+    RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  request_1.MarkAttemptStarted(base::Time::Now());
+  store->AddRequest(request_1,
+                    base::Bind(&MarkAttemptCompletedTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  result_ = std::move(result);
+}
+
+void MarkAttemptCompletedTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void MarkAttemptCompletedTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenExists) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddStartedItemToStore(&store);
+
+  MarkAttemptCompletedTask task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(1, last_result()->updated_items.at(0).completed_attempt_count());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            last_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(MarkAttemptCompletedTaskTest, MarkAttemptCompletedWhenItemMissing) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  // Add request 1 to the store.
+  AddStartedItemToStore(&store);
+  // Try to mark request 2 (not in the store).
+  MarkAttemptCompletedTask task(
+      &store, kRequestId2,
+      base::Bind(&MarkAttemptCompletedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_started_task.cc b/components/offline_pages/core/background/mark_attempt_started_task.cc
new file mode 100644
index 0000000..429b67a6
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_started_task.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/time/time.h"
+
+namespace offline_pages {
+
+MarkAttemptStartedTask::MarkAttemptStartedTask(
+    RequestQueueStore* store,
+    int64_t request_id,
+    const RequestQueueStore::UpdateCallback& callback)
+    : UpdateRequestTask(store, request_id, callback) {}
+
+MarkAttemptStartedTask::~MarkAttemptStartedTask() {}
+
+void MarkAttemptStartedTask::UpdateRequestImpl(
+    std::unique_ptr<UpdateRequestsResult> read_result) {
+  if (!ValidateReadResult(read_result.get())) {
+    CompleteWithResult(std::move(read_result));
+    return;
+  }
+
+  // It is perfectly fine to reuse the read_result->updated_items collection, as
+  // it is owned by this callback and will be destroyed when out of scope.
+  read_result->updated_items[0].MarkAttemptStarted(base::Time::Now());
+  store()->UpdateRequests(
+      read_result->updated_items,
+      base::Bind(&MarkAttemptStartedTask::CompleteWithResult, GetWeakPtr()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/mark_attempt_started_task.h b/components/offline_pages/core/background/mark_attempt_started_task.h
new file mode 100644
index 0000000..93673a9
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_started_task.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
+
+#include <stdint.h>
+
+#include "components/offline_pages/core/background/update_request_task.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class RequestQueueStore;
+
+class MarkAttemptStartedTask : public UpdateRequestTask {
+ public:
+  MarkAttemptStartedTask(RequestQueueStore* store,
+                         int64_t request_id,
+                         const RequestQueueStore::UpdateCallback& callback);
+  ~MarkAttemptStartedTask() override;
+
+ protected:
+  // UpdateRequestTask implementation:
+  void UpdateRequestImpl(std::unique_ptr<UpdateRequestsResult> result) override;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_MARK_ATTEMPT_STARTED_TASK_H_
diff --git a/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
new file mode 100644
index 0000000..17da2ca
--- /dev/null
+++ b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 44;
+const GURL kUrl1("http://example.com");
+const ClientId kClientId1("download", "1234");
+}  // namespace
+
+class MarkAttemptStartedTaskTest : public testing::Test {
+ public:
+  MarkAttemptStartedTaskTest();
+  ~MarkAttemptStartedTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddItemToStore(RequestQueueStore* store);
+  void ChangeRequestsStateCallback(
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  UpdateRequestsResult* last_result() const { return result_.get(); }
+
+ private:
+  void InitializeStoreDone(bool success);
+  void AddRequestDone(ItemActionStatus status);
+
+  std::unique_ptr<UpdateRequestsResult> result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+MarkAttemptStartedTaskTest::MarkAttemptStartedTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+MarkAttemptStartedTaskTest::~MarkAttemptStartedTaskTest() {}
+
+void MarkAttemptStartedTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void MarkAttemptStartedTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&MarkAttemptStartedTaskTest::InitializeStoreDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptStartedTaskTest::AddItemToStore(RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  store->AddRequest(request_1,
+                    base::Bind(&MarkAttemptStartedTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void MarkAttemptStartedTaskTest::ChangeRequestsStateCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  result_ = std::move(result);
+}
+
+void MarkAttemptStartedTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void MarkAttemptStartedTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenStoreEmpty) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  MarkAttemptStartedTask task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenExists) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemToStore(&store);
+
+  MarkAttemptStartedTask task(
+      &store, kRequestId1,
+      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+
+  // Current time for verification.
+  base::Time before_time = base::Time::Now();
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_LE(before_time,
+            last_result()->updated_items.at(0).last_attempt_time());
+  EXPECT_GE(base::Time::Now(),
+            last_result()->updated_items.at(0).last_attempt_time());
+  EXPECT_EQ(1, last_result()->updated_items.at(0).started_attempt_count());
+  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING,
+            last_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(MarkAttemptStartedTaskTest, MarkAttemptStartedWhenItemMissing) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddItemToStore(&store);
+
+  MarkAttemptStartedTask task(
+      &store, kRequestId2,
+      base::Bind(&MarkAttemptStartedTaskTest::ChangeRequestsStateCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/network_quality_provider_stub.cc b/components/offline_pages/core/background/network_quality_provider_stub.cc
new file mode 100644
index 0000000..aaca695
--- /dev/null
+++ b/components/offline_pages/core/background/network_quality_provider_stub.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+
+namespace offline_pages {
+
+const char kOfflineNQPKey[] = "OfflineNQP";
+
+NetworkQualityProviderStub::NetworkQualityProviderStub()
+    : connection_type_(
+          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G) {}
+
+NetworkQualityProviderStub::~NetworkQualityProviderStub() {}
+
+// static
+NetworkQualityProviderStub* NetworkQualityProviderStub::GetUserData(
+    base::SupportsUserData* supports_user_data) {
+  return static_cast<NetworkQualityProviderStub*>(
+      supports_user_data->GetUserData(&kOfflineNQPKey));
+}
+
+// static
+void NetworkQualityProviderStub::SetUserData(
+    base::SupportsUserData* supports_user_data,
+    NetworkQualityProviderStub* stub) {
+  DCHECK(supports_user_data);
+  DCHECK(stub);
+  supports_user_data->SetUserData(&kOfflineNQPKey, stub);
+}
+
+void NetworkQualityProviderStub::AddEffectiveConnectionTypeObserver(
+    net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
+
+void NetworkQualityProviderStub::RemoveEffectiveConnectionTypeObserver(
+    net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer) {}
+
+net::EffectiveConnectionType
+NetworkQualityProviderStub::GetEffectiveConnectionType() const {
+  return connection_type_;
+}
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/network_quality_provider_stub.h b/components/offline_pages/core/background/network_quality_provider_stub.h
new file mode 100644
index 0000000..1c13a17
--- /dev/null
+++ b/components/offline_pages/core/background/network_quality_provider_stub.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
+
+#include "base/supports_user_data.h"
+#include "net/nqe/effective_connection_type.h"
+#include "net/nqe/network_quality_estimator.h"
+
+namespace offline_pages {
+
+// Test class stubbing out the functionality of NQE::NetworkQualityProvider.
+// It is only used for test support.
+class NetworkQualityProviderStub
+    : public net::NetworkQualityEstimator::NetworkQualityProvider,
+      public base::SupportsUserData::Data {
+ public:
+  NetworkQualityProviderStub();
+  ~NetworkQualityProviderStub() override;
+
+  static NetworkQualityProviderStub* GetUserData(
+      base::SupportsUserData* supports_user_data);
+  static void SetUserData(base::SupportsUserData* supports_user_data,
+                          NetworkQualityProviderStub* stub);
+
+  net::EffectiveConnectionType GetEffectiveConnectionType() const override;
+
+  void AddEffectiveConnectionTypeObserver(
+      net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
+      override;
+
+  void RemoveEffectiveConnectionTypeObserver(
+      net::NetworkQualityEstimator::EffectiveConnectionTypeObserver* observer)
+      override;
+
+  void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
+    connection_type_ = type;
+  }
+
+ private:
+  net::EffectiveConnectionType connection_type_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_NETWORK_QUALITY_PROVIDER_STUB_H_
diff --git a/components/offline_pages/core/background/offliner.h b/components/offline_pages/core/background/offliner.h
new file mode 100644
index 0000000..6cc51ec
--- /dev/null
+++ b/components/offline_pages/core/background/offliner.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace offline_pages {
+
+class SavePageRequest;
+
+// Interface of a class responsible for constructing an offline page given
+// a request with a URL.
+class Offliner {
+ public:
+  // Status of processing an offline page request.
+  // WARNING: You must update histograms.xml to match any changes made to
+  // this enum (ie, OfflinePagesBackgroundOfflinerRequestStatus histogram enum).
+  // Also update related switch code in RequestCoordinatorEventLogger.
+  enum RequestStatus {
+    // No status determined/reported yet. Interim status, not sent in callback.
+    UNKNOWN = 0,
+    // Page loaded but not (yet) saved. Interim status, not sent in callback.
+    LOADED = 1,
+    // Offline page snapshot saved.
+    SAVED = 2,
+    // RequestCoordinator canceled request.
+    REQUEST_COORDINATOR_CANCELED = 3,
+    // Prerendering was canceled.
+    PRERENDERING_CANCELED = 4,
+    // Prerendering failed to load page.
+    PRERENDERING_FAILED = 5,
+    // Failed to save loaded page.
+    SAVE_FAILED = 6,
+    // Foreground transition canceled request.
+    FOREGROUND_CANCELED = 7,
+    // RequestCoordinator canceled request attempt per time limit.
+    REQUEST_COORDINATOR_TIMED_OUT = 8,
+    // The loader did not accept/start the request.
+    PRERENDERING_NOT_STARTED = 9,
+    // Prerendering failed with hard error so should not retry the request.
+    PRERENDERING_FAILED_NO_RETRY = 10,
+    // Prerendering failed with some error that suggests we should not continue
+    // processing another request at this time.
+    PRERENDERING_FAILED_NO_NEXT = 11,
+    // NOTE: insert new values above this line and update histogram enum too.
+    STATUS_COUNT
+  };
+
+  // Reports the completion status of a request.
+  // TODO(dougarnett): consider passing back a request id instead of request.
+  typedef base::Callback<void(const SavePageRequest&, RequestStatus)>
+      CompletionCallback;
+
+  Offliner() {}
+  virtual ~Offliner() {}
+
+  // Processes |request| to load and save an offline page.
+  // Returns whether the request was accepted or not. |callback| is guaranteed
+  // to be called if the request was accepted and |Cancel()| is not called.
+  virtual bool LoadAndSave(const SavePageRequest& request,
+                           const CompletionCallback& callback) = 0;
+
+  // Clears the currently processing request, if any, and skips running its
+  // CompletionCallback.
+  virtual void Cancel() = 0;
+
+  // TODO(dougarnett): add policy support methods.
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_H_
diff --git a/components/offline_pages/core/background/offliner_factory.h b/components/offline_pages/core/background/offliner_factory.h
new file mode 100644
index 0000000..90daa454
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_factory.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_H_
+
+#include "base/macros.h"
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class Offliner;
+
+// An interface to a factory which can create a background offliner.
+// The returned factory is owned by the offliner, which is allowed to cache the
+// factory and return it again later.
+class OfflinerFactory {
+ public:
+  OfflinerFactory() {}
+  virtual ~OfflinerFactory() {}
+
+  virtual Offliner* GetOffliner(const OfflinerPolicy* policy) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OfflinerFactory);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_H_
diff --git a/components/offline_pages/core/background/offliner_factory_stub.cc b/components/offline_pages/core/background/offliner_factory_stub.cc
new file mode 100644
index 0000000..fada4c36
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_factory_stub.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/offliner_factory_stub.h"
+
+#include "components/offline_pages/core/background/offliner_stub.h"
+
+namespace offline_pages {
+
+OfflinerFactoryStub::OfflinerFactoryStub() : offliner_(nullptr) {}
+
+OfflinerFactoryStub::~OfflinerFactoryStub() {}
+
+Offliner* OfflinerFactoryStub::GetOffliner(const OfflinerPolicy* policy) {
+  if (offliner_.get() == nullptr) {
+    offliner_.reset(new OfflinerStub());
+  }
+  return offliner_.get();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/offliner_factory_stub.h b/components/offline_pages/core/background/offliner_factory_stub.h
new file mode 100644
index 0000000..6045ca8c
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_factory_stub.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_STUB_H_
+
+#include <memory>
+
+#include "components/offline_pages/core/background/offliner_factory.h"
+
+namespace offline_pages {
+
+class OfflinerStub;
+
+// Test class stubbing out the functionality of OfflinerFactory.
+// It is only used for test support.
+class OfflinerFactoryStub : public OfflinerFactory {
+ public:
+  OfflinerFactoryStub();
+  ~OfflinerFactoryStub() override;
+
+  Offliner* GetOffliner(const OfflinerPolicy* policy) override;
+
+ private:
+  std::unique_ptr<OfflinerStub> offliner_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_FACTORY_STUB_H_
diff --git a/components/offline_pages/core/background/offliner_policy.h b/components/offline_pages/core/background/offliner_policy.h
new file mode 100644
index 0000000..3a579ec
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_policy.h
@@ -0,0 +1,148 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
+
+namespace {
+// The max number of started tries is to guard against pages that make the
+// prerenderer crash.  It should be greater than or equal to the max number of
+// completed tries.
+const int kMaxStartedTries = 5;
+// The number of max completed tries is based on Gin2G-poor testing showing that
+// we often need about 4 tries with a 2 minute window, or 3 retries with a 3
+// minute window. Also, we count one try now for foreground/disabled requests.
+const int kMaxCompletedTries = 3;
+// By the time we get to a week, the user has forgotten asking for a page.
+const int kRequestExpirationTimeInSeconds = 60 * 60 * 24 * 7;
+
+// Scheduled background processing time limits.
+const int kDozeModeBackgroundServiceWindowSeconds = 60 * 3;
+const int kDefaultBackgroundProcessingTimeBudgetSeconds =
+    kDozeModeBackgroundServiceWindowSeconds - 10;
+const int kSinglePageTimeLimitWhenBackgroundScheduledSeconds =
+    kDozeModeBackgroundServiceWindowSeconds - 10;
+
+// Immediate processing time limits.
+// Note: experiments on GIN-2g-poor show many page requests took 3 or 4
+// attempts in background scheduled mode with timeout of 2 minutes. So for
+// immediate processing mode, give page requests 4 times that limit (8 min).
+// Then budget up to 5 of those requests in processing window.
+const int kSinglePageTimeLimitForImmediateLoadSeconds = 60 * 8;
+const int kImmediateLoadProcessingTimeBudgetSeconds =
+    kSinglePageTimeLimitForImmediateLoadSeconds * 5;
+}  // namespace
+
+namespace offline_pages {
+
+// Policy for the Background Offlining system.  Some policy will belong to the
+// RequestCoordinator, some to the RequestQueue, and some to the Offliner.
+class OfflinerPolicy {
+ public:
+  OfflinerPolicy()
+      : prefer_untried_requests_(true),
+        prefer_earlier_requests_(true),
+        retry_count_is_more_important_than_recency_(true),
+        max_started_tries_(kMaxStartedTries),
+        max_completed_tries_(kMaxCompletedTries),
+        background_scheduled_processing_time_budget_(
+            kDefaultBackgroundProcessingTimeBudgetSeconds) {}
+
+  // Constructor for unit tests.
+  OfflinerPolicy(bool prefer_untried,
+                 bool prefer_earlier,
+                 bool prefer_retry_count,
+                 int max_started_tries,
+                 int max_completed_tries,
+                 int background_processing_time_budget)
+      : prefer_untried_requests_(prefer_untried),
+        prefer_earlier_requests_(prefer_earlier),
+        retry_count_is_more_important_than_recency_(prefer_retry_count),
+        max_started_tries_(max_started_tries),
+        max_completed_tries_(max_completed_tries),
+        background_scheduled_processing_time_budget_(
+            background_processing_time_budget) {}
+
+  // TODO(petewil): Numbers here are chosen arbitrarily, do the proper studies
+  // to get good policy numbers. Eventually this should get data from a finch
+  // experiment.
+
+  // Returns true if we should prefer retrying lesser tried requests.
+  bool ShouldPreferUntriedRequests() const { return prefer_untried_requests_; }
+
+  // Returns true if we should prefer older requests of equal number of tries.
+  bool ShouldPreferEarlierRequests() const { return prefer_earlier_requests_; }
+
+  // Returns true if retry count is considered more important than recency in
+  // picking which request to try next.
+  bool RetryCountIsMoreImportantThanRecency() const {
+    return retry_count_is_more_important_than_recency_;
+  }
+
+  // The max number of times we will start a request.  Not all started attempts
+  // will complete.  This may be caused by prerenderer issues or chromium being
+  // swapped out of memory.
+  int GetMaxStartedTries() const { return max_started_tries_; }
+
+  // The max number of times we will retry a request when the attempt
+  // completed, but failed.
+  int GetMaxCompletedTries() const { return max_completed_tries_; }
+
+  bool PowerRequired(bool user_requested) const { return (!user_requested); }
+
+  bool UnmeteredNetworkRequired(bool user_requested) const {
+    return !(user_requested);
+  }
+
+  int BatteryPercentageRequired(bool user_requested) const {
+    if (user_requested)
+      return 0;
+    // This is so low because we require the device to be plugged in and
+    // charging.  If we decide to allow non-user requested pages when not
+    // plugged in, we should raise this somewhat higher.
+    return 25;
+  }
+
+  // How many seconds to keep trying new pages for, before we give up, and
+  // return to the scheduler.
+  // TODO(dougarnett): Consider parameterizing these time limit/budget
+  // calls with processing mode.
+  int GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds() const {
+    return background_scheduled_processing_time_budget_;
+  }
+
+  // How many seconds to keep trying new pages for, before we give up, when
+  // processing started immediately (without scheduler).
+  int GetProcessingTimeBudgetForImmediateLoadInSeconds() const {
+    return kImmediateLoadProcessingTimeBudgetSeconds;
+  }
+
+  // How long do we allow a page to load before giving up on it when
+  // background loading was scheduled.
+  int GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() const {
+    return kSinglePageTimeLimitWhenBackgroundScheduledSeconds;
+  }
+
+  // How long do we allow a page to load before giving up on it when
+  // immediately background loading.
+  int GetSinglePageTimeLimitForImmediateLoadInSeconds() const {
+    return kSinglePageTimeLimitForImmediateLoadSeconds;
+  }
+
+  // How long we allow requests to remain in the system before giving up.
+  int GetRequestExpirationTimeInSeconds() const {
+    return kRequestExpirationTimeInSeconds;
+  }
+
+ private:
+  bool prefer_untried_requests_;
+  bool prefer_earlier_requests_;
+  bool retry_count_is_more_important_than_recency_;
+  int max_started_tries_;
+  int max_completed_tries_;
+  int background_scheduled_processing_time_budget_;
+};
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_H_
diff --git a/components/offline_pages/core/background/offliner_policy_utils.cc b/components/offline_pages/core/background/offliner_policy_utils.cc
new file mode 100644
index 0000000..a9383f24
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_policy_utils.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+// static function to check request expiration or cleanup status.
+OfflinerPolicyUtils::RequestExpirationStatus
+OfflinerPolicyUtils::CheckRequestExpirationStatus(
+    const SavePageRequest* request,
+    const OfflinerPolicy* policy) {
+  DCHECK(request);
+  DCHECK(policy);
+
+  if (base::Time::Now() - request->creation_time() >=
+      base::TimeDelta::FromSeconds(
+          policy->GetRequestExpirationTimeInSeconds())) {
+    return RequestExpirationStatus::EXPIRED;
+  }
+  if (request->started_attempt_count() >= policy->GetMaxStartedTries())
+    return RequestExpirationStatus::START_COUNT_EXCEEDED;
+
+  if (request->completed_attempt_count() >= policy->GetMaxCompletedTries())
+    return RequestExpirationStatus::COMPLETION_COUNT_EXCEEDED;
+
+  return RequestExpirationStatus::VALID;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/offliner_policy_utils.h b/components/offline_pages/core/background/offliner_policy_utils.h
new file mode 100644
index 0000000..2fc50c6
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_policy_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
+
+namespace offline_pages {
+
+class OfflinerPolicy;
+class SavePageRequest;
+
+class OfflinerPolicyUtils {
+ public:
+  enum class RequestExpirationStatus {
+    VALID,
+    EXPIRED,
+    START_COUNT_EXCEEDED,
+    COMPLETION_COUNT_EXCEEDED,
+  };
+
+  static RequestExpirationStatus CheckRequestExpirationStatus(
+      const SavePageRequest* request,
+      const OfflinerPolicy* policy);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_POLICY_UTILS_H_
diff --git a/components/offline_pages/core/background/offliner_stub.cc b/components/offline_pages/core/background/offliner_stub.cc
new file mode 100644
index 0000000..0455fe0
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_stub.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/offliner_stub.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+OfflinerStub::OfflinerStub()
+    : disable_loading_(false), enable_callback_(false), cancel_called_(false) {}
+
+OfflinerStub::~OfflinerStub() {}
+
+bool OfflinerStub::LoadAndSave(const SavePageRequest& request,
+                               const CompletionCallback& callback) {
+  if (disable_loading_)
+    return false;
+
+  callback_ = callback;
+
+  // Post the callback on the run loop.
+  if (enable_callback_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(callback, request, Offliner::RequestStatus::SAVED));
+  }
+  return true;
+}
+
+void OfflinerStub::Cancel() {
+  cancel_called_ = true;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/offliner_stub.h b/components/offline_pages/core/background/offliner_stub.h
new file mode 100644
index 0000000..f7922606
--- /dev/null
+++ b/components/offline_pages/core/background/offliner_stub.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
+
+#include "components/offline_pages/core/background/offliner.h"
+
+namespace offline_pages {
+
+// Test class stubbing out the functionality of Offliner.
+// It is only used for test support.
+class OfflinerStub : public Offliner {
+ public:
+  OfflinerStub();
+  ~OfflinerStub() override;
+
+  bool LoadAndSave(const SavePageRequest& request,
+                   const CompletionCallback& callback) override;
+
+  void Cancel() override;
+
+  void disable_loading() { disable_loading_ = true; }
+
+  void enable_callback(bool enable) { enable_callback_ = enable; }
+
+  bool cancel_called() { return cancel_called_; }
+
+ private:
+  CompletionCallback callback_;
+  bool disable_loading_;
+  bool enable_callback_;
+  bool cancel_called_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_OFFLINER_STUB_H_
diff --git a/components/offline_pages/core/background/pick_request_task.cc b/components/offline_pages/core/background/pick_request_task.cc
new file mode 100644
index 0000000..006d08b4
--- /dev/null
+++ b/components/offline_pages/core/background/pick_request_task.cc
@@ -0,0 +1,255 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/pick_request_task.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_policy_utils.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace {
+template <typename T>
+int signum(T t) {
+  return (T(0) < t) - (t < T(0));
+}
+
+bool kCleanupNeeded = true;
+bool kNonUserRequestsFound = true;
+
+#define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
+}  // namespace
+
+namespace offline_pages {
+
+PickRequestTask::PickRequestTask(RequestQueueStore* store,
+                                 OfflinerPolicy* policy,
+                                 RequestPickedCallback picked_callback,
+                                 RequestNotPickedCallback not_picked_callback,
+                                 RequestCountCallback request_count_callback,
+                                 DeviceConditions& device_conditions,
+                                 const std::set<int64_t>& disabled_requests)
+    : store_(store),
+      policy_(policy),
+      picked_callback_(picked_callback),
+      not_picked_callback_(not_picked_callback),
+      request_count_callback_(request_count_callback),
+      disabled_requests_(disabled_requests),
+      weak_ptr_factory_(this) {
+  device_conditions_.reset(new DeviceConditions(device_conditions));
+}
+
+PickRequestTask::~PickRequestTask() {}
+
+void PickRequestTask::Run() {
+  GetRequests();
+}
+
+void PickRequestTask::GetRequests() {
+  // Get all the requests from the queue, we will classify them in the callback.
+  store_->GetRequests(
+      base::Bind(&PickRequestTask::Choose, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PickRequestTask::Choose(
+    bool success,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  // If there is nothing to do, return right away.
+  if (requests.empty()) {
+    request_count_callback_.Run(requests.size(), 0);
+    not_picked_callback_.Run(!kNonUserRequestsFound, !kCleanupNeeded);
+    TaskComplete();
+    return;
+  }
+
+  // Pick the most deserving request for our conditions.
+  const SavePageRequest* picked_request = nullptr;
+
+  RequestCompareFunction comparator = nullptr;
+
+  // Choose which comparison function to use based on policy.
+  if (policy_->RetryCountIsMoreImportantThanRecency())
+    comparator = &PickRequestTask::RetryCountFirstCompareFunction;
+  else
+    comparator = &PickRequestTask::RecencyFirstCompareFunction;
+
+  // TODO(petewil): Consider replacing this bool with a better named enum.
+  bool non_user_requested_tasks_remaining = false;
+  bool cleanup_needed = false;
+
+  size_t available_request_count = 0;
+
+  // Iterate once through the requests, keeping track of best candidate.
+  for (unsigned i = 0; i < requests.size(); ++i) {
+    // If the request is expired or has exceeded the retry count, skip it.
+    if (OfflinerPolicyUtils::CheckRequestExpirationStatus(requests[i].get(),
+                                                          policy_) !=
+        OfflinerPolicyUtils::RequestExpirationStatus::VALID) {
+      cleanup_needed = true;
+      continue;
+    }
+
+    // If the  request is on the disabled list, skip it.
+    auto search = disabled_requests_.find(requests[i]->request_id());
+    if (search != disabled_requests_.end())
+      continue;
+
+    // If there are non-user-requested tasks remaining, we need to make sure
+    // that they are scheduled after we run out of user requested tasks. Here we
+    // detect if any exist. If we don't find any user-requested tasks, we will
+    // inform the "not_picked_callback_" that it needs to schedule a task for
+    // non-user-requested items, which have different network and power needs.
+    if (!requests[i]->user_requested())
+      non_user_requested_tasks_remaining = true;
+    if (requests[i]->request_state() ==
+        SavePageRequest::RequestState::AVAILABLE) {
+      available_request_count++;
+    }
+    if (!RequestConditionsSatisfied(requests[i].get()))
+      continue;
+    if (IsNewRequestBetter(picked_request, requests[i].get(), comparator))
+      picked_request = requests[i].get();
+  }
+
+  // Report the request queue counts.
+  request_count_callback_.Run(requests.size(), available_request_count);
+
+  // If we have a best request to try next, get the request coodinator to
+  // start it.  Otherwise return that we have no candidates.
+  if (picked_request != nullptr) {
+    picked_callback_.Run(*picked_request, cleanup_needed);
+  } else {
+    not_picked_callback_.Run(non_user_requested_tasks_remaining,
+                             cleanup_needed);
+  }
+
+  TaskComplete();
+}
+
+// Filter out requests that don't meet the current conditions.  For instance, if
+// this is a predictive request, and we are not on WiFi, it should be ignored
+// this round.
+bool PickRequestTask::RequestConditionsSatisfied(
+    const SavePageRequest* request) {
+  // If the user did not request the page directly, make sure we are connected
+  // to power and have WiFi and sufficient battery remaining before we take this
+  // request.
+  if (!device_conditions_->IsPowerConnected() &&
+      policy_->PowerRequired(request->user_requested())) {
+    return false;
+  }
+
+  if (device_conditions_->GetNetConnectionType() !=
+          net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI &&
+      policy_->UnmeteredNetworkRequired(request->user_requested())) {
+    return false;
+  }
+
+  if (device_conditions_->GetBatteryPercentage() <
+      policy_->BatteryPercentageRequired(request->user_requested())) {
+    return false;
+  }
+
+  // If the request is paused, do not consider it.
+  if (request->request_state() == SavePageRequest::RequestState::PAUSED)
+    return false;
+
+  // If this request is not active yet, return false.
+  // TODO(petewil): If the only reason we return nothing to do is that we have
+  // inactive requests, we still want to try again later after their activation
+  // time elapses, we shouldn't take ourselves completely off the scheduler.
+  if (request->activation_time() > base::Time::Now())
+    return false;
+
+  return true;
+}
+
+// Look at policies to decide which requests to prefer.
+bool PickRequestTask::IsNewRequestBetter(const SavePageRequest* oldRequest,
+                                         const SavePageRequest* newRequest,
+                                         RequestCompareFunction comparator) {
+  // If there is no old request, the new one is better.
+  if (oldRequest == nullptr)
+    return true;
+
+  // User requested pages get priority.
+  if (newRequest->user_requested() && !oldRequest->user_requested())
+    return true;
+
+  // Otherwise, use the comparison function for the current policy, which
+  // returns true if the older request is better.
+  return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest));
+}
+
+// Compare the results, checking request count before recency.  Returns true if
+// left hand side is better, false otherwise.
+bool PickRequestTask::RetryCountFirstCompareFunction(
+    const SavePageRequest* left,
+    const SavePageRequest* right) {
+  // Check the attempt count.
+  int result = CompareRetryCount(left, right);
+
+  if (result != 0)
+    return (result > 0);
+
+  // If we get here, the attempt counts were the same, so check recency.
+  result = CompareCreationTime(left, right);
+
+  return (result > 0);
+}
+
+// Compare the results, checking recency before request count. Returns true if
+// left hand side is better, false otherwise.
+bool PickRequestTask::RecencyFirstCompareFunction(
+    const SavePageRequest* left,
+    const SavePageRequest* right) {
+  // Check the recency.
+  int result = CompareCreationTime(left, right);
+
+  if (result != 0)
+    return (result > 0);
+
+  // If we get here, the recency was the same, so check the attempt count.
+  result = CompareRetryCount(left, right);
+
+  return (result > 0);
+}
+
+// Compare left and right side, returning 1 if the left side is better
+// (preferred by policy), 0 if the same, and -1 if the right side is better.
+int PickRequestTask::CompareRetryCount(const SavePageRequest* left,
+                                       const SavePageRequest* right) {
+  // Check the attempt count.
+  int result = signum(left->completed_attempt_count() -
+                      right->completed_attempt_count());
+
+  // Flip the direction of comparison if policy prefers fewer retries.
+  if (policy_->ShouldPreferUntriedRequests())
+    result *= -1;
+
+  return result;
+}
+
+// Compare left and right side, returning 1 if the left side is better
+// (preferred by policy), 0 if the same, and -1 if the right side is better.
+int PickRequestTask::CompareCreationTime(const SavePageRequest* left,
+                                         const SavePageRequest* right) {
+  // Check the recency.
+  base::TimeDelta difference = left->creation_time() - right->creation_time();
+  int result = signum(difference.InMilliseconds());
+
+  // Flip the direction of comparison if policy prefers fewer retries.
+  if (policy_->ShouldPreferEarlierRequests())
+    result *= -1;
+
+  return result;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/pick_request_task.h b/components/offline_pages/core/background/pick_request_task.h
new file mode 100644
index 0000000..ef04612
--- /dev/null
+++ b/components/offline_pages/core/background/pick_request_task.h
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
+
+#include <set>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class DeviceConditions;
+class OfflinerPolicy;
+class PickRequestTask;
+class RequestQueueStore;
+
+typedef bool (PickRequestTask::*RequestCompareFunction)(
+    const SavePageRequest* left,
+    const SavePageRequest* right);
+
+class PickRequestTask : public Task {
+ public:
+  // Callback to report when a request was available.
+  typedef base::Callback<void(const SavePageRequest& request,
+                              bool cleanup_needed)>
+      RequestPickedCallback;
+
+  // Callback to report when no request was available.
+  typedef base::Callback<void(bool non_user_requests, bool cleanup_needed)>
+      RequestNotPickedCallback;
+
+  // Callback to report available total and available queued request counts.
+  typedef base::Callback<void(size_t, size_t)> RequestCountCallback;
+
+  PickRequestTask(RequestQueueStore* store,
+                  OfflinerPolicy* policy,
+                  RequestPickedCallback picked_callback,
+                  RequestNotPickedCallback not_picked_callback,
+                  RequestCountCallback request_count_callback,
+                  DeviceConditions& device_conditions,
+                  const std::set<int64_t>& disabled_requests);
+
+  ~PickRequestTask() override;
+
+  // TaskQueue::Task implementation, starts the async chain
+  void Run() override;
+
+ private:
+  // Step 1. get the requests
+  void GetRequests();
+
+  // Step 2. pick a request that we like best from available requests.
+  void Choose(bool get_succeeded,
+              std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Helper functions.
+
+  // Determine if this request has device conditions appropriate for running it.
+  bool RequestConditionsSatisfied(const SavePageRequest* request);
+
+  // Determine if the new request is preferred under current policies.
+  bool IsNewRequestBetter(const SavePageRequest* oldRequest,
+                          const SavePageRequest* newRequest,
+                          RequestCompareFunction comparator);
+
+  // Returns true if the left hand side is better.
+  bool RetryCountFirstCompareFunction(const SavePageRequest* left,
+                                      const SavePageRequest* right);
+
+  // Returns true if the left hand side is better.
+  bool RecencyFirstCompareFunction(const SavePageRequest* left,
+                                   const SavePageRequest* right);
+
+  // Compare left and right side, returning 1 if the left side is better
+  // (preferred by policy), 0 if the same, and -1 if the right side is better.
+  int CompareRetryCount(const SavePageRequest* left,
+                        const SavePageRequest* right);
+
+  // Compare left and right side, returning 1 if the left side is better
+  // (preferred by policy), 0 if the same, and -1 if the right side is better.
+  int CompareCreationTime(const SavePageRequest* left,
+                          const SavePageRequest* right);
+
+  // Member variables, all pointers are not owned here.
+  RequestQueueStore* store_;
+  OfflinerPolicy* policy_;
+  RequestPickedCallback picked_callback_;
+  RequestNotPickedCallback not_picked_callback_;
+  RequestCountCallback request_count_callback_;
+  std::unique_ptr<DeviceConditions> device_conditions_;
+  const std::set<int64_t>& disabled_requests_;
+  // Allows us to pass a weak pointer to callbacks.
+  base::WeakPtrFactory<PickRequestTask> weak_ptr_factory_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_PICK_REQUEST_TASK_H_
diff --git a/components/offline_pages/core/background/pick_request_task_unittest.cc b/components/offline_pages/core/background/pick_request_task_unittest.cc
new file mode 100644
index 0000000..f1e249b
--- /dev/null
+++ b/components/offline_pages/core/background/pick_request_task_unittest.cc
@@ -0,0 +1,493 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/pick_request_task.h"
+
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// Data for request 1.
+const int64_t kRequestId1 = 17;
+const GURL kUrl1("https://google.com");
+const ClientId kClientId1("bookmark", "1234");
+// Data for request 2.
+const int64_t kRequestId2 = 42;
+const GURL kUrl2("http://nytimes.com");
+const ClientId kClientId2("bookmark", "5678");
+const bool kUserRequested = true;
+const int kAttemptCount = 1;
+const int kMaxStartedTries = 5;
+const int kMaxCompletedTries = 1;
+
+// Constants for policy values - These settings represent the default values.
+const bool kPreferUntried = false;
+const bool kPreferEarlier = true;
+const bool kPreferRetryCount = true;
+const int kBackgroundProcessingTimeBudgetSeconds = 170;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+                                    GURL(""),
+                                    ClientId("", ""),
+                                    base::Time(),
+                                    true);
+}  // namespace
+
+// Helper class needed by the PickRequestTask
+class RequestNotifierStub : public RequestNotifier {
+ public:
+  RequestNotifierStub()
+      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
+
+  void NotifyAdded(const SavePageRequest& request) override {}
+  void NotifyChanged(const SavePageRequest& request) override {}
+
+  void NotifyCompleted(const SavePageRequest& request,
+                       BackgroundSavePageResult status) override {
+    last_expired_request_ = request;
+    last_request_expiration_status_ = status;
+    total_expired_requests_++;
+  }
+
+  const SavePageRequest& last_expired_request() {
+    return last_expired_request_;
+  }
+
+  RequestCoordinator::BackgroundSavePageResult
+  last_request_expiration_status() {
+    return last_request_expiration_status_;
+  }
+
+  int32_t total_expired_requests() { return total_expired_requests_; }
+
+ private:
+  BackgroundSavePageResult last_request_expiration_status_;
+  SavePageRequest last_expired_request_;
+  int32_t total_expired_requests_;
+};
+
+class PickRequestTaskTest : public testing::Test {
+ public:
+  PickRequestTaskTest();
+
+  ~PickRequestTaskTest() override;
+
+  void SetUp() override;
+
+  void PumpLoop();
+
+  void AddRequestDone(ItemActionStatus status);
+
+  void RequestPicked(const SavePageRequest& request, bool cleanup_needed);
+
+  void RequestNotPicked(const bool non_user_requested_tasks_remaining,
+                        bool cleanup_needed);
+
+  void RequestCountCallback(size_t total_count, size_t available_count);
+
+  void QueueRequests(const SavePageRequest& request1,
+                     const SavePageRequest& request2);
+
+  // Reset the factory and the task using the current policy.
+  void MakePickRequestTask();
+
+  RequestNotifierStub* GetNotifier() { return notifier_.get(); }
+
+  PickRequestTask* task() { return task_.get(); }
+
+  void TaskCompletionCallback(Task* completed_task);
+
+ protected:
+  void InitializeStoreDone(bool success);
+
+  std::unique_ptr<RequestQueueStore> store_;
+  std::unique_ptr<RequestNotifierStub> notifier_;
+  std::unique_ptr<SavePageRequest> last_picked_;
+  std::unique_ptr<OfflinerPolicy> policy_;
+  RequestCoordinatorEventLogger event_logger_;
+  std::set<int64_t> disabled_requests_;
+  std::unique_ptr<PickRequestTask> task_;
+  bool request_queue_not_picked_called_;
+  bool cleanup_needed_;
+  size_t total_request_count_;
+  size_t available_request_count_;
+  bool task_complete_called_;
+
+ private:
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+PickRequestTaskTest::PickRequestTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+PickRequestTaskTest::~PickRequestTaskTest() {}
+
+void PickRequestTaskTest::SetUp() {
+  DeviceConditions conditions;
+  store_.reset(new RequestQueueInMemoryStore());
+  policy_.reset(new OfflinerPolicy());
+  notifier_.reset(new RequestNotifierStub());
+  MakePickRequestTask();
+  request_queue_not_picked_called_ = false;
+  total_request_count_ = 9999;
+  available_request_count_ = 9999;
+  task_complete_called_ = false;
+  last_picked_.reset();
+  cleanup_needed_ = false;
+
+  store_->Initialize(base::Bind(&PickRequestTaskTest::InitializeStoreDone,
+                                base::Unretained(this)));
+  PumpLoop();
+}
+
+void PickRequestTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void PickRequestTaskTest::TaskCompletionCallback(Task* completed_task) {
+  task_complete_called_ = true;
+}
+
+void PickRequestTaskTest::AddRequestDone(ItemActionStatus status) {}
+
+void PickRequestTaskTest::RequestPicked(const SavePageRequest& request,
+                                        const bool cleanup_needed) {
+  last_picked_.reset(new SavePageRequest(request));
+  cleanup_needed_ = cleanup_needed;
+}
+
+void PickRequestTaskTest::RequestNotPicked(
+    const bool non_user_requested_tasks_remaining,
+    const bool cleanup_needed) {
+  request_queue_not_picked_called_ = true;
+}
+
+void PickRequestTaskTest::RequestCountCallback(size_t total_count,
+                                               size_t available_count) {
+  total_request_count_ = total_count;
+  available_request_count_ = available_count;
+}
+
+// Test helper to queue the two given requests.
+void PickRequestTaskTest::QueueRequests(const SavePageRequest& request1,
+                                        const SavePageRequest& request2) {
+  DeviceConditions conditions;
+  std::set<int64_t> disabled_requests;
+  // Add test requests on the Queue.
+  store_->AddRequest(request1, base::Bind(&PickRequestTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+  store_->AddRequest(request2, base::Bind(&PickRequestTaskTest::AddRequestDone,
+                                          base::Unretained(this)));
+
+  // Pump the loop to give the async queue the opportunity to do the adds.
+  PumpLoop();
+}
+
+void PickRequestTaskTest::MakePickRequestTask() {
+  DeviceConditions conditions;
+  task_.reset(new PickRequestTask(
+      store_.get(), policy_.get(),
+      base::Bind(&PickRequestTaskTest::RequestPicked, base::Unretained(this)),
+      base::Bind(&PickRequestTaskTest::RequestNotPicked,
+                 base::Unretained(this)),
+      base::Bind(&PickRequestTaskTest::RequestCountCallback,
+                 base::Unretained(this)),
+      conditions, disabled_requests_));
+  task_->SetTaskCompletionCallbackForTesting(
+      task_runner_.get(),
+      base::Bind(&PickRequestTaskTest::TaskCompletionCallback,
+                 base::Unretained(this)));
+}
+
+void PickRequestTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+TEST_F(PickRequestTaskTest, PickFromEmptyQueue) {
+  task()->Run();
+  PumpLoop();
+
+  // Pump the loop again to give the async queue the opportunity to return
+  // results from the Get operation, and for the picker to call the "QueueEmpty"
+  // callback.
+  PumpLoop();
+
+  EXPECT_TRUE(request_queue_not_picked_called_);
+  EXPECT_EQ(0UL, total_request_count_);
+  EXPECT_EQ(0UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestWithHigherRetryCount) {
+  // Set up policy to prefer higher retry count.
+  policy_.reset(new OfflinerPolicy(
+      kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
+  MakePickRequestTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(2UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestWithSameRetryCountButEarlier) {
+  base::Time creation_time1 =
+      base::Time::Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
+                           kUserRequested);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId1, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseEarlierRequest) {
+  // We need a custom policy object prefering recency to retry count.
+  policy_.reset(new OfflinerPolicy(
+      kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
+  MakePickRequestTask();
+
+  base::Time creation_time1 =
+      base::Time::Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId1, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseSameTimeRequestWithHigherRetryCount) {
+  // We need a custom policy object preferring recency to retry count.
+  policy_.reset(new OfflinerPolicy(
+      kPreferUntried, kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
+  MakePickRequestTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestWithLowerRetryCount) {
+  // We need a custom policy object preferring lower retry count.
+  policy_.reset(new OfflinerPolicy(
+      !kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
+  MakePickRequestTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId1, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseLaterRequest) {
+  // We need a custom policy preferring recency over retry, and later requests.
+  policy_.reset(new OfflinerPolicy(
+      kPreferUntried, !kPreferEarlier, !kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries, kBackgroundProcessingTimeBudgetSeconds));
+  MakePickRequestTask();
+
+  base::Time creation_time1 =
+      base::Time::Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
+                           kUserRequested);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseNonExpiredRequest) {
+  base::Time creation_time = base::Time::Now();
+  base::Time expired_time =
+      creation_time - base::TimeDelta::FromSeconds(
+                          policy_->GetRequestExpirationTimeInSeconds() + 60);
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, expired_time,
+                           kUserRequested);
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId1, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(1UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+  EXPECT_TRUE(cleanup_needed_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
+  base::Time creation_time1 =
+      base::Time::Now() - base::TimeDelta::FromSeconds(1);
+  base::Time creation_time2 = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
+                           kUserRequested);
+
+  // With default policy settings, we should choose the earlier request.
+  // However, we will make the earlier reqeust exceed the limit.
+  request1.set_started_attempt_count(policy_->GetMaxStartedTries());
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(1UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+  EXPECT_TRUE(cleanup_needed_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
+  base::Time creation_time1 =
+      base::Time::Now() - base::TimeDelta::FromSeconds(1);
+  base::Time creation_time2 = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
+                           kUserRequested);
+
+  // With default policy settings, we should choose the earlier request.
+  // However, we will make the earlier reqeust exceed the limit.
+  request1.set_completed_attempt_count(policy_->GetMaxCompletedTries());
+
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId2, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_TRUE(task_complete_called_);
+  EXPECT_TRUE(cleanup_needed_);
+}
+
+TEST_F(PickRequestTaskTest, ChooseRequestThatIsNotDisabled) {
+  policy_.reset(new OfflinerPolicy(
+      kPreferUntried, kPreferEarlier, kPreferRetryCount, kMaxStartedTries,
+      kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
+
+  // put request 2 on disabled list, ensure request1 picked instead,
+  // even though policy would prefer 2.
+  disabled_requests_.insert(kRequestId2);
+  MakePickRequestTask();
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
+                           kUserRequested);
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+
+  // Add test requests on the Queue.
+  QueueRequests(request1, request2);
+
+  task()->Run();
+  PumpLoop();
+
+  // Pump the loop again to give the async queue the opportunity to return
+  // results from the Get operation, and for the picker to call the "picked"
+  // callback.
+  PumpLoop();
+
+  EXPECT_EQ(kRequestId1, last_picked_->request_id());
+  EXPECT_FALSE(request_queue_not_picked_called_);
+  EXPECT_EQ(2UL, total_request_count_);
+  EXPECT_EQ(1UL, available_request_count_);
+  EXPECT_TRUE(task_complete_called_);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/remove_requests_task.cc b/components/offline_pages/core/background/remove_requests_task.cc
new file mode 100644
index 0000000..f958e67
--- /dev/null
+++ b/components/offline_pages/core/background/remove_requests_task.cc
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/remove_requests_task.h"
+
+#include "base/bind.h"
+
+namespace offline_pages {
+
+RemoveRequestsTask::RemoveRequestsTask(
+    RequestQueueStore* store,
+    const std::vector<int64_t>& request_ids,
+    const RequestQueueStore::UpdateCallback& callback)
+    : store_(store),
+      request_ids_(request_ids),
+      callback_(callback),
+      weak_ptr_factory_(this) {}
+
+RemoveRequestsTask::~RemoveRequestsTask() {}
+
+void RemoveRequestsTask::Run() {
+  RemoveRequests();
+}
+
+void RemoveRequestsTask::RemoveRequests() {
+  if (request_ids_.empty()) {
+    CompleteEarly(ItemActionStatus::NOT_FOUND);
+    return;
+  }
+
+  store_->RemoveRequests(request_ids_,
+                         base::Bind(&RemoveRequestsTask::CompleteWithResult,
+                                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void RemoveRequestsTask::CompleteEarly(ItemActionStatus status) {
+  // TODO(fgorski): store_->state() once implemented
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(StoreState::LOADED));
+  for (int64_t request_id : request_ids_)
+    result->item_statuses.push_back(std::make_pair(request_id, status));
+  CompleteWithResult(std::move(result));
+}
+
+void RemoveRequestsTask::CompleteWithResult(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  callback_.Run(std::move(result));
+  TaskComplete();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/remove_requests_task.h b/components/offline_pages/core/background/remove_requests_task.h
new file mode 100644
index 0000000..6372d3d
--- /dev/null
+++ b/components/offline_pages/core/background/remove_requests_task.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+class RemoveRequestsTask : public Task {
+ public:
+  RemoveRequestsTask(RequestQueueStore* store,
+                     const std::vector<int64_t>& request_ids,
+                     const RequestQueueStore::UpdateCallback& callback);
+  ~RemoveRequestsTask() override;
+
+  // TaskQueue::Task implementation.
+  void Run() override;
+
+ private:
+  // Step 1. Removes requests from the store.
+  void RemoveRequests();
+  // Step for early termination, that builds failure result.
+  void CompleteEarly(ItemActionStatus status);
+  // Step 2. Processes update result, calls callback.
+  void CompleteWithResult(std::unique_ptr<UpdateRequestsResult> result);
+
+  // Store that this task updates.
+  RequestQueueStore* store_;
+  // Request IDs to be updated.
+  // TODO(fgorski): perhaps convert to unique_ptr to a vector.
+  std::vector<int64_t> request_ids_;
+  // Callback to complete the task.
+  RequestQueueStore::UpdateCallback callback_;
+
+  base::WeakPtrFactory<RemoveRequestsTask> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RemoveRequestsTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REMOVE_REQUESTS_TASK_H_
diff --git a/components/offline_pages/core/background/remove_requests_task_unittest.cc b/components/offline_pages/core/background/remove_requests_task_unittest.cc
new file mode 100644
index 0000000..5846dc2
--- /dev/null
+++ b/components/offline_pages/core/background/remove_requests_task_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/remove_requests_task.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+namespace {
+const int64_t kRequestId1 = 42;
+const int64_t kRequestId2 = 43;
+const int64_t kRequestId3 = 44;
+const GURL kUrl1("http://example.com");
+const GURL kUrl2("http://another-example.com");
+const ClientId kClientId1("bookmark", "1234");
+const ClientId kClientId2("async", "5678");
+}  // namespace
+
+class RemoveRequestsTaskTest : public testing::Test {
+ public:
+  RemoveRequestsTaskTest();
+  ~RemoveRequestsTaskTest() override;
+
+  void PumpLoop();
+
+  void InitializeStore(RequestQueueStore* store);
+  void AddRequestsToStore(RequestQueueStore* store);
+  void RemoveRequestsCallback(std::unique_ptr<UpdateRequestsResult> result);
+
+  UpdateRequestsResult* last_result() const { return result_.get(); }
+
+ private:
+  void InitializeStoreDone(bool succesS);
+  void AddRequestDone(ItemActionStatus status);
+
+  std::unique_ptr<UpdateRequestsResult> result_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+RemoveRequestsTaskTest::RemoveRequestsTaskTest()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+RemoveRequestsTaskTest::~RemoveRequestsTaskTest() {}
+
+void RemoveRequestsTaskTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void RemoveRequestsTaskTest::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&RemoveRequestsTaskTest::InitializeStoreDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void RemoveRequestsTaskTest::AddRequestsToStore(RequestQueueStore* store) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
+                            true);
+  store->AddRequest(request_1,
+                    base::Bind(&RemoveRequestsTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
+                            true);
+  store->AddRequest(request_2,
+                    base::Bind(&RemoveRequestsTaskTest::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+}
+
+void RemoveRequestsTaskTest::RemoveRequestsCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  result_ = std::move(result);
+}
+
+void RemoveRequestsTaskTest::InitializeStoreDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+void RemoveRequestsTaskTest::AddRequestDone(ItemActionStatus status) {
+  ASSERT_EQ(ItemActionStatus::SUCCESS, status);
+}
+
+TEST_F(RemoveRequestsTaskTest, RemoveWhenStoreEmpty) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1};
+  RemoveRequestsTask task(
+      &store, request_ids,
+      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(RemoveRequestsTaskTest, RemoveSingleItem) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddRequestsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1};
+  RemoveRequestsTask task(
+      &store, request_ids,
+      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(1UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
+}
+
+TEST_F(RemoveRequestsTaskTest, RemoveMultipleItems) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddRequestsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1, kRequestId2};
+  RemoveRequestsTask task(
+      &store, request_ids,
+      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(2UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(kRequestId2, last_result()->item_statuses.at(1).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(1).second);
+  EXPECT_EQ(2UL, last_result()->updated_items.size());
+  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
+  EXPECT_EQ(kRequestId2, last_result()->updated_items.at(1).request_id());
+}
+
+TEST_F(RemoveRequestsTaskTest, DeleteWithEmptyIdList) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+
+  std::vector<int64_t> request_ids;
+  RemoveRequestsTask task(
+      &store, request_ids,
+      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(0UL, last_result()->item_statuses.size());
+  EXPECT_EQ(0UL, last_result()->updated_items.size());
+}
+
+TEST_F(RemoveRequestsTaskTest, RemoveMissingItem) {
+  RequestQueueInMemoryStore store;
+  InitializeStore(&store);
+  AddRequestsToStore(&store);
+
+  std::vector<int64_t> request_ids{kRequestId1, kRequestId3};
+  RemoveRequestsTask task(
+      &store, request_ids,
+      base::Bind(&RemoveRequestsTaskTest::RemoveRequestsCallback,
+                 base::Unretained(this)));
+  task.Run();
+  PumpLoop();
+  ASSERT_TRUE(last_result());
+  EXPECT_EQ(2UL, last_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId1, last_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_result()->item_statuses.at(0).second);
+  EXPECT_EQ(kRequestId3, last_result()->item_statuses.at(1).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            last_result()->item_statuses.at(1).second);
+  EXPECT_EQ(1UL, last_result()->updated_items.size());
+  EXPECT_EQ(kRequestId1, last_result()->updated_items.at(0).request_id());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator.cc b/components/offline_pages/core/background/request_coordinator.cc
new file mode 100644
index 0000000..9c5f8a4
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator.cc
@@ -0,0 +1,1003 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_coordinator.h"
+
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/sys_info.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/background/offliner_factory.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+
+namespace offline_pages {
+
+namespace {
+const bool kUserRequest = true;
+const bool kStartOfProcessing = true;
+const int kMinDurationSeconds = 1;
+const int kMaxDurationSeconds = 7 * 24 * 60 * 60;  // 7 days
+const int kDurationBuckets = 50;
+const int kDisabledTaskRecheckSeconds = 5;
+
+// TODO(dougarnett): Move to util location and share with model impl.
+std::string AddHistogramSuffix(const ClientId& client_id,
+                               const char* histogram_name) {
+  if (client_id.name_space.empty()) {
+    NOTREACHED();
+    return histogram_name;
+  }
+  std::string adjusted_histogram_name(histogram_name);
+  adjusted_histogram_name += "." + client_id.name_space;
+  return adjusted_histogram_name;
+}
+
+// Records the final request status UMA for an offlining request. This should
+// only be called once per Offliner::LoadAndSave request.
+void RecordOfflinerResultUMA(const ClientId& client_id,
+                             const base::Time& request_creation_time,
+                             Offliner::RequestStatus request_status) {
+  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+      AddHistogramSuffix(client_id,
+                         "OfflinePages.Background.OfflinerRequestStatus"),
+      1, static_cast<int>(Offliner::RequestStatus::STATUS_COUNT),
+      static_cast<int>(Offliner::RequestStatus::STATUS_COUNT) + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(static_cast<int>(request_status));
+
+  // For successful requests also record time from request to save.
+  if (request_status == Offliner::RequestStatus::SAVED) {
+    // Using regular histogram (with dynamic suffix) rather than time-oriented
+    // one to record samples in seconds rather than milliseconds.
+    base::HistogramBase* histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"),
+        kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
+        base::HistogramBase::kUmaTargetedHistogramFlag);
+    base::TimeDelta duration = base::Time::Now() - request_creation_time;
+    histogram->Add(duration.InSeconds());
+  }
+}
+
+void RecordStartTimeUMA(const SavePageRequest& request) {
+  std::string histogram_name("OfflinePages.Background.TimeToStart");
+  if (base::SysInfo::IsLowEndDevice()) {
+    histogram_name += ".Svelte";
+  }
+
+  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_TIMES
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
+      AddHistogramSuffix(request.client_id(), histogram_name.c_str()),
+      base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  base::TimeDelta duration = base::Time::Now() - request.creation_time();
+  histogram->AddTime(duration);
+}
+
+void RecordCancelTimeUMA(const SavePageRequest& canceled_request) {
+  // Using regular histogram (with dynamic suffix) rather than time-oriented
+  // one to record samples in seconds rather than milliseconds.
+  base::HistogramBase* histogram = base::Histogram::FactoryGet(
+      AddHistogramSuffix(canceled_request.client_id(),
+                         "OfflinePages.Background.TimeToCanceled"),
+      kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  base::TimeDelta duration =
+      base::Time::Now() - canceled_request.creation_time();
+  histogram->Add(duration.InSeconds());
+}
+
+// Records the number of started attempts for completed requests (whether
+// successful or not).
+void RecordAttemptCount(const SavePageRequest& request,
+                        RequestNotifier::BackgroundSavePageResult status) {
+  if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) {
+    // TODO(dougarnett): Also record UMA for completed attempts here.
+    UMA_HISTOGRAM_CUSTOM_COUNTS(
+        "OfflinePages.Background.RequestSuccess.StartedAttemptCount",
+        request.started_attempt_count(), 1, 10, 11);
+  } else {
+    UMA_HISTOGRAM_CUSTOM_COUNTS(
+        "OfflinePages.Background.RequestFailure.StartedAttemptCount",
+        request.started_attempt_count(), 1, 10, 11);
+  }
+}
+
+// Record the network quality at request creation time per namespace.
+void RecordSavePageLaterNetworkQuality(
+    const ClientId& client_id,
+    const net::EffectiveConnectionType effective_connection) {
+  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+      AddHistogramSuffix(
+          client_id,
+          "OfflinePages.Background.EffectiveConnectionType.SavePageLater"),
+      1, net::EFFECTIVE_CONNECTION_TYPE_LAST - 1,
+      net::EFFECTIVE_CONNECTION_TYPE_LAST,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(effective_connection);
+}
+
+// This should use the same algorithm as we use for OfflinePageItem, so the IDs
+// are similar.
+int64_t GenerateOfflineId() {
+  return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
+}
+
+// In case we start processing from SavePageLater, we need a callback, but there
+// is nothing for it to do.
+void EmptySchedulerCallback(bool started) {}
+
+// Returns whether |result| is a successful result for a single request.
+bool IsSingleSuccessResult(const UpdateRequestsResult* result) {
+  return result->store_state == StoreState::LOADED &&
+         result->item_statuses.size() == 1 &&
+         result->item_statuses.at(0).second == ItemActionStatus::SUCCESS;
+}
+
+}  // namespace
+
+RequestCoordinator::RequestCoordinator(
+    std::unique_ptr<OfflinerPolicy> policy,
+    std::unique_ptr<OfflinerFactory> factory,
+    std::unique_ptr<RequestQueue> queue,
+    std::unique_ptr<Scheduler> scheduler,
+    net::NetworkQualityEstimator::NetworkQualityProvider*
+        network_quality_estimator)
+    : is_low_end_device_(base::SysInfo::IsLowEndDevice()),
+      is_busy_(false),
+      is_starting_(false),
+      processing_state_(ProcessingWindowState::STOPPED),
+      use_test_connection_type_(false),
+      test_connection_type_(),
+      offliner_(nullptr),
+      policy_(std::move(policy)),
+      factory_(std::move(factory)),
+      queue_(std::move(queue)),
+      scheduler_(std::move(scheduler)),
+      policy_controller_(new ClientPolicyController()),
+      network_quality_estimator_(network_quality_estimator),
+      active_request_(nullptr),
+      last_offlining_status_(Offliner::RequestStatus::UNKNOWN),
+      scheduler_callback_(base::Bind(&EmptySchedulerCallback)),
+      immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
+      weak_ptr_factory_(this) {
+  DCHECK(policy_ != nullptr);
+  std::unique_ptr<CleanupTaskFactory> cleanup_factory(
+      new CleanupTaskFactory(policy_.get(), this, &event_logger_));
+  queue_->SetCleanupFactory(std::move(cleanup_factory));
+  // Do a cleanup at startup time.
+  queue_->CleanupRequestQueue();
+}
+
+RequestCoordinator::~RequestCoordinator() {}
+
+int64_t RequestCoordinator::SavePageLater(const GURL& url,
+                                          const ClientId& client_id,
+                                          bool user_requested,
+                                          RequestAvailability availability) {
+  DVLOG(2) << "URL is " << url << " " << __func__;
+
+  if (!OfflinePageModel::CanSaveURL(url)) {
+    DVLOG(1) << "Not able to save page for requested url: " << url;
+    return 0L;
+  }
+
+  int64_t id = GenerateOfflineId();
+
+  // Build a SavePageRequest.
+  offline_pages::SavePageRequest request(id, url, client_id, base::Time::Now(),
+                                         user_requested);
+
+  // If the download manager is not done with the request, put it on the
+  // disabled list.
+  if (availability == RequestAvailability::DISABLED_FOR_OFFLINER)
+    disabled_requests_.insert(id);
+
+  // Put the request on the request queue.
+  queue_->AddRequest(request,
+                     base::Bind(&RequestCoordinator::AddRequestResultCallback,
+                                weak_ptr_factory_.GetWeakPtr(), availability));
+
+  // Record the network quality when this request is made.
+  if (network_quality_estimator_) {
+    RecordSavePageLaterNetworkQuality(
+        client_id, network_quality_estimator_->GetEffectiveConnectionType());
+  }
+
+  return id;
+}
+void RequestCoordinator::GetAllRequests(const GetRequestsCallback& callback) {
+  // Get all matching requests from the request queue, send them to our
+  // callback.  We bind the namespace and callback to the front of the callback
+  // param set.
+  queue_->GetRequests(base::Bind(&RequestCoordinator::GetQueuedRequestsCallback,
+                                 weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void RequestCoordinator::GetQueuedRequestsCallback(
+    const GetRequestsCallback& callback,
+    GetRequestsResult result,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  callback.Run(std::move(requests));
+}
+
+void RequestCoordinator::StopPrerendering(Offliner::RequestStatus stop_status) {
+  if (offliner_ && is_busy_) {
+    DCHECK(active_request_.get());
+    offliner_->Cancel();
+
+    if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) {
+      // Consider watchdog timeout a completed attempt.
+      SavePageRequest request(*active_request_.get());
+      UpdateRequestForCompletedAttempt(request,
+                                       Offliner::REQUEST_COORDINATOR_TIMED_OUT);
+    } else {
+      // Otherwise consider this stop an aborted attempt.
+      UpdateRequestForAbortedAttempt(*active_request_.get());
+    }
+  }
+
+  // Stopping offliner means it will not call callback so set last status.
+  last_offlining_status_ = stop_status;
+
+  if (active_request_) {
+    event_logger_.RecordOfflinerResult(active_request_->client_id().name_space,
+                                       last_offlining_status_,
+                                       active_request_->request_id());
+    RecordOfflinerResultUMA(active_request_->client_id(),
+                            active_request_->creation_time(),
+                            last_offlining_status_);
+    is_busy_ = false;
+    active_request_.reset();
+  }
+}
+
+void RequestCoordinator::GetRequestsForSchedulingCallback(
+    GetRequestsResult result,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  bool user_requested = false;
+
+  // Examine all requests, if we find a user requested one, we will use the less
+  // restrictive conditions for user_requested requests.  Otherwise we will use
+  // the more restrictive non-user-requested conditions.
+  for (const auto& request : requests) {
+    if (request->user_requested()) {
+      user_requested = true;
+      break;
+    }
+  }
+
+  // In the get callback, determine the least restrictive, and call
+  // GetTriggerConditions based on that.
+  scheduler_->Schedule(GetTriggerConditions(user_requested));
+}
+
+bool RequestCoordinator::CancelActiveRequestIfItMatches(
+    const std::vector<int64_t>& request_ids) {
+  // If we have a request in progress and need to cancel it, call the
+  // pre-renderer to cancel.  TODO Make sure we remove any page created by the
+  // prerenderer if it doesn't get the cancel in time.
+  if (active_request_ != nullptr) {
+    if (request_ids.end() != std::find(request_ids.begin(), request_ids.end(),
+                                       active_request_->request_id())) {
+      StopPrerendering(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED);
+      active_request_.reset(nullptr);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void RequestCoordinator::UpdateRequestForAbortedAttempt(
+    const SavePageRequest& request) {
+  if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) {
+    const RequestNotifier::BackgroundSavePageResult result(
+        RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED);
+    event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
+                                               result, request.request_id());
+    RemoveAttemptedRequest(request, result);
+  } else {
+    MarkAttemptAborted(request.request_id(), request.client_id().name_space);
+  }
+}
+
+void RequestCoordinator::RemoveAttemptedRequest(
+    const SavePageRequest& request,
+    RequestNotifier::BackgroundSavePageResult result) {
+  std::vector<int64_t> remove_requests;
+  remove_requests.push_back(request.request_id());
+  queue_->RemoveRequests(remove_requests,
+                         base::Bind(&RequestCoordinator::HandleRemovedRequests,
+                                    weak_ptr_factory_.GetWeakPtr(), result));
+  RecordAttemptCount(request, result);
+}
+
+void RequestCoordinator::MarkAttemptAborted(int64_t request_id,
+                                            const std::string& name_space) {
+  queue_->MarkAttemptAborted(
+      request_id,
+      base::Bind(&RequestCoordinator::MarkAttemptDone,
+                 weak_ptr_factory_.GetWeakPtr(), request_id, name_space));
+}
+
+void RequestCoordinator::MarkAttemptDone(
+    int64_t request_id,
+    const std::string& name_space,
+    std::unique_ptr<UpdateRequestsResult> result) {
+  // If the request succeeded, notify observer. If it failed, we can't really
+  // do much, so just log it.
+  if (IsSingleSuccessResult(result.get())) {
+    NotifyChanged(result->updated_items.at(0));
+  } else {
+    DVLOG(1) << "Failed to mark attempt: " << request_id;
+    UpdateRequestResult request_result =
+        result->store_state != StoreState::LOADED
+            ? UpdateRequestResult::STORE_FAILURE
+            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+    event_logger_.RecordUpdateRequestFailed(name_space, request_result);
+  }
+}
+
+void RequestCoordinator::RemoveRequests(
+    const std::vector<int64_t>& request_ids,
+    const RemoveRequestsCallback& callback) {
+  bool canceled = CancelActiveRequestIfItMatches(request_ids);
+  queue_->RemoveRequests(
+      request_ids,
+      base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
+                 weak_ptr_factory_.GetWeakPtr(), callback,
+                 RequestNotifier::BackgroundSavePageResult::REMOVED));
+
+  // Record the network quality when this request is made.
+  if (network_quality_estimator_) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "OfflinePages.Background.EffectiveConnectionType.RemoveRequests",
+        network_quality_estimator_->GetEffectiveConnectionType(),
+        net::EFFECTIVE_CONNECTION_TYPE_LAST);
+  }
+
+  if (canceled)
+    TryNextRequest(!kStartOfProcessing);
+}
+
+void RequestCoordinator::PauseRequests(
+    const std::vector<int64_t>& request_ids) {
+  bool canceled = CancelActiveRequestIfItMatches(request_ids);
+  queue_->ChangeRequestsState(
+      request_ids, SavePageRequest::RequestState::PAUSED,
+      base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  // Record the network quality when this request is made.
+  if (network_quality_estimator_) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "OfflinePages.Background.EffectiveConnectionType.PauseRequests",
+        network_quality_estimator_->GetEffectiveConnectionType(),
+        net::EFFECTIVE_CONNECTION_TYPE_LAST);
+  }
+
+  if (canceled)
+    TryNextRequest(!kStartOfProcessing);
+}
+
+void RequestCoordinator::ResumeRequests(
+    const std::vector<int64_t>& request_ids) {
+  queue_->ChangeRequestsState(
+      request_ids, SavePageRequest::RequestState::AVAILABLE,
+      base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  // Record the network quality when this request is made.
+  if (network_quality_estimator_) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "OfflinePages.Background.EffectiveConnectionType.ResumeRequests",
+        network_quality_estimator_->GetEffectiveConnectionType(),
+        net::EFFECTIVE_CONNECTION_TYPE_LAST);
+  }
+
+  // Schedule a task, in case there is not one scheduled.
+  ScheduleAsNeeded();
+}
+
+net::NetworkChangeNotifier::ConnectionType
+RequestCoordinator::GetConnectionType() {
+  // If we have a connection type set for test, use that.
+  if (use_test_connection_type_)
+    return test_connection_type_;
+
+  return net::NetworkChangeNotifier::GetConnectionType();
+}
+
+void RequestCoordinator::AddRequestResultCallback(
+    RequestAvailability availability,
+    AddRequestResult result,
+    const SavePageRequest& request) {
+  NotifyAdded(request);
+  // Inform the scheduler that we have an outstanding task.
+  scheduler_->Schedule(GetTriggerConditions(kUserRequest));
+
+  if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) {
+    // Mark attempt started (presuming it is disabled for background offliner
+    // because foreground offlining is happening).
+    queue_->MarkAttemptStarted(
+        request.request_id(),
+        base::Bind(&RequestCoordinator::MarkAttemptDone,
+                   weak_ptr_factory_.GetWeakPtr(), request.request_id(),
+                   request.client_id().name_space));
+  } else if (request.user_requested()) {
+    StartImmediatelyIfConnected();
+  }
+}
+
+void RequestCoordinator::UpdateMultipleRequestsCallback(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  for (const auto& request : result->updated_items)
+    NotifyChanged(request);
+
+  bool available_user_request = false;
+  for (const auto& request : result->updated_items) {
+    if (!available_user_request && request.user_requested() &&
+        request.request_state() == SavePageRequest::RequestState::AVAILABLE) {
+      available_user_request = true;
+    }
+  }
+
+  if (available_user_request)
+    StartImmediatelyIfConnected();
+}
+
+void RequestCoordinator::HandleRemovedRequestsAndCallback(
+    const RemoveRequestsCallback& callback,
+    RequestNotifier::BackgroundSavePageResult status,
+    std::unique_ptr<UpdateRequestsResult> result) {
+  // TODO(dougarnett): Define status code for user/api cancel and use here
+  // to determine whether to record cancel time UMA.
+  for (const auto& request : result->updated_items)
+    RecordCancelTimeUMA(request);
+  callback.Run(result->item_statuses);
+  HandleRemovedRequests(status, std::move(result));
+}
+
+void RequestCoordinator::HandleRemovedRequests(
+    RequestNotifier::BackgroundSavePageResult status,
+    std::unique_ptr<UpdateRequestsResult> result) {
+  for (const auto& request : result->updated_items)
+    NotifyCompleted(request, status);
+}
+
+void RequestCoordinator::ScheduleAsNeeded() {
+  // Get all requests from queue (there is no filtering mechanism).
+  queue_->GetRequests(
+      base::Bind(&RequestCoordinator::GetRequestsForSchedulingCallback,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void RequestCoordinator::StopProcessing(Offliner::RequestStatus stop_status) {
+  processing_state_ = ProcessingWindowState::STOPPED;
+  StopPrerendering(stop_status);
+
+  // Let the scheduler know we are done processing.
+  scheduler_callback_.Run(true);
+}
+
+void RequestCoordinator::HandleWatchdogTimeout() {
+  Offliner::RequestStatus watchdog_status =
+      Offliner::REQUEST_COORDINATOR_TIMED_OUT;
+  StopPrerendering(watchdog_status);
+  TryNextRequest(!kStartOfProcessing);
+}
+
+// Returns true if the caller should expect a callback, false otherwise. For
+// instance, this would return false if a request is already in progress.
+bool RequestCoordinator::StartProcessing(
+    const DeviceConditions& device_conditions,
+    const base::Callback<void(bool)>& callback) {
+  DVLOG(2) << "Scheduled " << __func__;
+  return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
+                                 device_conditions, callback);
+}
+
+bool RequestCoordinator::StartProcessingInternal(
+    const ProcessingWindowState processing_state,
+    const DeviceConditions& device_conditions,
+    const base::Callback<void(bool)>& callback) {
+  current_conditions_.reset(new DeviceConditions(device_conditions));
+  if (is_starting_ || is_busy_)
+    return false;
+  processing_state_ = processing_state;
+  scheduler_callback_ = callback;
+
+  // Mark the time at which we started processing so we can check our time
+  // budget.
+  operation_start_time_ = base::Time::Now();
+
+  TryNextRequest(kStartOfProcessing);
+
+  return true;
+}
+
+void RequestCoordinator::StartImmediatelyIfConnected() {
+  OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart();
+  UMA_HISTOGRAM_ENUMERATION(
+      "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
+      RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
+}
+
+RequestCoordinator::OfflinerImmediateStartStatus
+RequestCoordinator::TryImmediateStart() {
+  DVLOG(2) << "Immediate " << __func__;
+  // Make sure not already busy processing.
+  if (is_busy_)
+    return OfflinerImmediateStartStatus::BUSY;
+
+  // Make sure we are not on svelte device to start immediately.
+  if (is_low_end_device_ &&
+      !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) {
+    DVLOG(2) << "low end device, returning";
+    // Let the scheduler know we are done processing and failed due to svelte.
+    immediate_schedule_callback_.Run(false);
+    return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE;
+  }
+
+  if (GetConnectionType() ==
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) {
+    RequestConnectedEventForStarting();
+    return OfflinerImmediateStartStatus::NO_CONNECTION;
+  } else {
+    // Clear any pending connected event request since we have connection
+    // and will start processing.
+    ClearConnectedEventRequest();
+  }
+
+  // Start processing with manufactured conservative battery conditions
+  // (i.e., assume no battery).
+  // TODO(dougarnett): Obtain actual battery conditions (from Android/Java).
+
+  DeviceConditions device_conditions(false, 0, GetConnectionType());
+  if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW,
+                              device_conditions, immediate_schedule_callback_))
+    return OfflinerImmediateStartStatus::STARTED;
+  else
+    return OfflinerImmediateStartStatus::NOT_ACCEPTED;
+}
+
+void RequestCoordinator::RequestConnectedEventForStarting() {
+  connection_notifier_.reset(new ConnectionNotifier(
+      base::Bind(&RequestCoordinator::HandleConnectedEventForStarting,
+                 weak_ptr_factory_.GetWeakPtr())));
+}
+
+void RequestCoordinator::ClearConnectedEventRequest() {
+  connection_notifier_.reset(nullptr);
+}
+
+void RequestCoordinator::HandleConnectedEventForStarting() {
+  ClearConnectedEventRequest();
+  StartImmediatelyIfConnected();
+}
+
+void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
+  is_starting_ = true;
+  base::TimeDelta processing_time_budget;
+  if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
+    processing_time_budget = base::TimeDelta::FromSeconds(
+        policy_->GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds());
+  } else {
+    DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
+    processing_time_budget = base::TimeDelta::FromSeconds(
+        policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds());
+  }
+
+  // Determine connection type. If just starting processing, the best source is
+  // from the current device conditions (they are fresh and motivated starting
+  // processing whereas NetworkChangeNotifier may lag reality).
+  net::NetworkChangeNotifier::ConnectionType connection_type;
+  if (is_start_of_processing)
+    connection_type = current_conditions_->GetNetConnectionType();
+  else
+    connection_type = GetConnectionType();
+
+  // If there is no network or no time left in the budget, return to the
+  // scheduler. We do not remove the pending scheduler task that was set
+  // up earlier in case we run out of time, so the background scheduler
+  // will return to us at the next opportunity to run background tasks.
+  if (connection_type ==
+          net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE ||
+      (base::Time::Now() - operation_start_time_) > processing_time_budget) {
+    is_starting_ = false;
+
+    // If we were doing immediate processing, try to start it again
+    // when we get connected.
+    if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW)
+      RequestConnectedEventForStarting();
+
+    // Let the scheduler know we are done processing.
+    // TODO: Make sure the scheduler callback is valid before running it.
+    scheduler_callback_.Run(true);
+    DVLOG(2) << " out of time, giving up. " << __func__;
+
+    return;
+  }
+
+  // Ask request queue to make a new PickRequestTask object, then put it on the
+  // task queue.
+  queue_->PickNextRequest(
+      policy_.get(), base::Bind(&RequestCoordinator::RequestPicked,
+                                weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&RequestCoordinator::RequestNotPicked,
+                 weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&RequestCoordinator::RequestCounts,
+                 weak_ptr_factory_.GetWeakPtr(), is_start_of_processing),
+      *current_conditions_.get(), disabled_requests_);
+  // TODO(petewil): Verify current_conditions has a good value on all calling
+  // paths.  It is really more of a "last known conditions" than "current
+  // conditions".  Consider having a call to Java to check the current
+  // conditions.
+}
+
+// Called by the request picker when a request has been picked.
+void RequestCoordinator::RequestPicked(const SavePageRequest& request,
+                                       bool cleanup_needed) {
+  DVLOG(2) << request.url() << " " << __func__;
+  is_starting_ = false;
+
+  // Make sure we were not stopped while picking.
+  if (processing_state_ != ProcessingWindowState::STOPPED) {
+    // Send the request on to the offliner.
+    SendRequestToOffliner(request);
+  }
+
+  // Schedule a queue cleanup if needed.
+  if (cleanup_needed)
+    queue_->CleanupRequestQueue();
+}
+
+void RequestCoordinator::RequestNotPicked(
+    bool non_user_requested_tasks_remaining,
+    bool cleanup_needed) {
+  DVLOG(2) << __func__;
+  is_starting_ = false;
+
+  // Clear the outstanding "safety" task in the scheduler.
+  scheduler_->Unschedule();
+
+  // If disabled tasks remain, post a new safety task for 5 sec from now.
+  if (disabled_requests_.size() > 0) {
+    scheduler_->BackupSchedule(GetTriggerConditions(kUserRequest),
+                               kDisabledTaskRecheckSeconds);
+  } else if (non_user_requested_tasks_remaining) {
+    // If we don't have any of those, check for non-user-requested tasks.
+    scheduler_->Schedule(GetTriggerConditions(!kUserRequest));
+  }
+
+  // Schedule a queue cleanup if needed.
+  if (cleanup_needed)
+    queue_->CleanupRequestQueue();
+
+  // Let the scheduler know we are done processing.
+  scheduler_callback_.Run(true);
+}
+
+void RequestCoordinator::RequestCounts(bool is_start_of_processing,
+                                       size_t total_requests,
+                                       size_t available_requests) {
+  // Only capture request counts for the start of processing (not for
+  // continued processing in the same window).
+  if (!is_start_of_processing)
+    return;
+
+  if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
+    if (is_low_end_device_) {
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ScheduledStart.AvailableRequestCount."
+          "Svelte",
+          available_requests);
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ScheduledStart.UnavailableRequestCount."
+          "Svelte",
+          total_requests - available_requests);
+    } else {
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ScheduledStart.AvailableRequestCount",
+          available_requests);
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ScheduledStart.UnavailableRequestCount",
+          total_requests - available_requests);
+    }
+  } else if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW) {
+    if (is_low_end_device_) {
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ImmediateStart.AvailableRequestCount."
+          "Svelte",
+          available_requests);
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ImmediateStart.UnavailableRequestCount."
+          "Svelte",
+          total_requests - available_requests);
+    } else {
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ImmediateStart.AvailableRequestCount",
+          available_requests);
+      UMA_HISTOGRAM_COUNTS_1000(
+          "OfflinePages.Background.ImmediateStart.UnavailableRequestCount",
+          total_requests - available_requests);
+    }
+  }
+}
+
+void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) {
+  // Check that offlining didn't get cancelled while performing some async
+  // steps.
+  if (processing_state_ == ProcessingWindowState::STOPPED)
+    return;
+
+  GetOffliner();
+  if (!offliner_) {
+    DVLOG(0) << "Unable to create Offliner. "
+             << "Cannot background offline page.";
+    return;
+  }
+
+  DCHECK(!is_busy_);
+  is_busy_ = true;
+
+  // Record start time if this is first attempt.
+  if (request.started_attempt_count() == 0) {
+    RecordStartTimeUMA(request);
+  }
+
+  // Mark attempt started in the database and start offliner when completed.
+  queue_->MarkAttemptStarted(
+      request.request_id(),
+      base::Bind(&RequestCoordinator::StartOffliner,
+                 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
+                 request.client_id().name_space));
+}
+
+void RequestCoordinator::StartOffliner(
+    int64_t request_id,
+    const std::string& client_namespace,
+    std::unique_ptr<UpdateRequestsResult> update_result) {
+  if (update_result->store_state != StoreState::LOADED ||
+      update_result->item_statuses.size() != 1 ||
+      update_result->item_statuses.at(0).first != request_id ||
+      update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) {
+    is_busy_ = false;
+    // TODO(fgorski): what is the best result? Do we create a new status?
+    StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
+    DVLOG(1) << "Failed to mark attempt started: " << request_id;
+    UpdateRequestResult request_result =
+        update_result->store_state != StoreState::LOADED
+            ? UpdateRequestResult::STORE_FAILURE
+            : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
+    event_logger_.RecordUpdateRequestFailed(client_namespace, request_result);
+    return;
+  }
+
+  // TODO(fgorski): Switch to request_id only, so that this value is not written
+  // back to the store.
+  active_request_.reset(
+      new SavePageRequest(update_result->updated_items.at(0)));
+
+  // Start the load and save process in the offliner (Async).
+  if (offliner_->LoadAndSave(
+          update_result->updated_items.at(0),
+          base::Bind(&RequestCoordinator::OfflinerDoneCallback,
+                     weak_ptr_factory_.GetWeakPtr()))) {
+    base::TimeDelta timeout;
+    if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
+      timeout = base::TimeDelta::FromSeconds(
+          policy_->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds());
+    } else {
+      DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
+      timeout = base::TimeDelta::FromSeconds(
+          policy_->GetSinglePageTimeLimitForImmediateLoadInSeconds());
+    }
+
+    // Inform observer of active request.
+    NotifyChanged(*active_request_.get());
+
+    // Start a watchdog timer to catch pre-renders running too long
+    watchdog_timer_.Start(FROM_HERE, timeout, this,
+                          &RequestCoordinator::HandleWatchdogTimeout);
+  } else {
+    is_busy_ = false;
+    DVLOG(0) << "Unable to start LoadAndSave";
+    StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
+
+    // We need to undo the MarkAttemptStarted that brought us to this
+    // method since we didn't success in starting after all.
+    MarkAttemptAborted(request_id, client_namespace);
+  }
+}
+
+void RequestCoordinator::OfflinerDoneCallback(const SavePageRequest& request,
+                                              Offliner::RequestStatus status) {
+  DVLOG(2) << "offliner finished, saved: "
+           << (status == Offliner::RequestStatus::SAVED)
+           << ", status: " << static_cast<int>(status) << ", " << __func__;
+  DCHECK_NE(status, Offliner::RequestStatus::UNKNOWN);
+  DCHECK_NE(status, Offliner::RequestStatus::LOADED);
+  event_logger_.RecordOfflinerResult(request.client_id().name_space, status,
+                                     request.request_id());
+  last_offlining_status_ = status;
+  RecordOfflinerResultUMA(request.client_id(), request.creation_time(),
+                          last_offlining_status_);
+  watchdog_timer_.Stop();
+  is_busy_ = false;
+  active_request_.reset(nullptr);
+
+  UpdateRequestForCompletedAttempt(request, status);
+  if (ShouldTryNextRequest(status))
+    TryNextRequest(!kStartOfProcessing);
+  else
+    scheduler_callback_.Run(true);
+}
+
+void RequestCoordinator::UpdateRequestForCompletedAttempt(
+    const SavePageRequest& request,
+    Offliner::RequestStatus status) {
+  if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
+      status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
+    // Update the request for the canceled attempt.
+    // TODO(dougarnett): See if we can conclusively identify other attempt
+    // aborted cases to treat this way (eg, for Render Process Killed).
+    UpdateRequestForAbortedAttempt(request);
+  } else if (status == Offliner::RequestStatus::SAVED) {
+    // Remove the request from the queue if it succeeded.
+    RemoveAttemptedRequest(request,
+                           RequestNotifier::BackgroundSavePageResult::SUCCESS);
+  } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
+    RemoveAttemptedRequest(
+        request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
+  } else if (request.completed_attempt_count() + 1 >=
+             policy_->GetMaxCompletedTries()) {
+    // Remove from the request queue if we exceeded max retries. The +1
+    // represents the request that just completed. Since we call
+    // MarkAttemptCompleted within the if branches, the completed_attempt_count
+    // has not yet been updated when we are checking the if condition.
+    const RequestNotifier::BackgroundSavePageResult result(
+        RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
+    event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
+                                               result, request.request_id());
+    RemoveAttemptedRequest(request, result);
+  } else {
+    // If we failed, but are not over the limit, update the request in the
+    // queue.
+    queue_->MarkAttemptCompleted(
+        request.request_id(),
+        base::Bind(&RequestCoordinator::MarkAttemptDone,
+                   weak_ptr_factory_.GetWeakPtr(), request.request_id(),
+                   request.client_id().name_space));
+  }
+}
+
+bool RequestCoordinator::ShouldTryNextRequest(
+    Offliner::RequestStatus previous_request_status) {
+  switch (previous_request_status) {
+    case Offliner::RequestStatus::SAVED:
+    case Offliner::RequestStatus::SAVE_FAILED:
+    case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
+    case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
+    case Offliner::RequestStatus::PRERENDERING_FAILED:
+    case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
+      return true;
+    case Offliner::RequestStatus::FOREGROUND_CANCELED:
+    case Offliner::RequestStatus::PRERENDERING_CANCELED:
+    case Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT:
+      // No further processing in this service window.
+      return false;
+    default:
+      // Make explicit choice about new status codes that actually reach here.
+      // Their default is no further processing in this service window.
+      NOTREACHED();
+      return false;
+  }
+}
+
+void RequestCoordinator::EnableForOffliner(int64_t request_id,
+                                           const ClientId& client_id) {
+  // Since the recent tab helper might call multiple times, ignore subsequent
+  // calls for a particular request_id.
+  if (disabled_requests_.find(request_id) == disabled_requests_.end())
+    return;
+
+  // Clear from disabled list.
+  disabled_requests_.erase(request_id);
+
+  // Mark the request as now in available state.
+  MarkAttemptAborted(request_id, client_id.name_space);
+
+  // If we are not busy, start processing right away.
+  StartImmediatelyIfConnected();
+}
+
+void RequestCoordinator::MarkRequestCompleted(int64_t request_id) {
+  // Since the recent tab helper might call multiple times, ignore subsequent
+  // calls for a particular request_id.
+  if (disabled_requests_.find(request_id) == disabled_requests_.end())
+    return;
+
+  // Clear from disabled list.
+  disabled_requests_.erase(request_id);
+
+  // Remove the request, but send out SUCCEEDED instead of removed.
+  // Note: since it had been disabled, it will not have been active in a
+  // background offliner, so it is not appropriate to TryNextRequest here.
+  std::vector<int64_t> request_ids { request_id };
+  queue_->RemoveRequests(
+      request_ids,
+      base::Bind(&RequestCoordinator::HandleRemovedRequests,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 RequestNotifier::BackgroundSavePageResult::SUCCESS));
+}
+
+const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions(
+    const bool user_requested) {
+  return Scheduler::TriggerConditions(
+      policy_->PowerRequired(user_requested),
+      policy_->BatteryPercentageRequired(user_requested),
+      policy_->UnmeteredNetworkRequired(user_requested));
+}
+
+void RequestCoordinator::AddObserver(Observer* observer) {
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+}
+
+void RequestCoordinator::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void RequestCoordinator::NotifyAdded(const SavePageRequest& request) {
+  for (Observer& observer : observers_)
+    observer.OnAdded(request);
+}
+
+void RequestCoordinator::NotifyCompleted(
+    const SavePageRequest& request,
+    RequestNotifier::BackgroundSavePageResult status) {
+  for (Observer& observer : observers_)
+    observer.OnCompleted(request, status);
+}
+
+void RequestCoordinator::NotifyChanged(const SavePageRequest& request) {
+  for (Observer& observer : observers_)
+    observer.OnChanged(request);
+}
+
+void RequestCoordinator::GetOffliner() {
+  if (!offliner_) {
+    offliner_ = factory_->GetOffliner(policy_.get());
+  }
+}
+
+ClientPolicyController* RequestCoordinator::GetPolicyController() {
+  return policy_controller_.get();
+}
+
+void RequestCoordinator::Shutdown() {
+  network_quality_estimator_ = nullptr;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator.h b/components/offline_pages/core/background/request_coordinator.h
new file mode 100644
index 0000000..60a85f8f
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator.h
@@ -0,0 +1,426 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/supports_user_data.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/offline_pages/core/background/connection_notifier.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/scheduler.h"
+#include "net/nqe/network_quality_estimator.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+struct ClientId;
+class OfflinerPolicy;
+class OfflinerFactory;
+class Offliner;
+class RequestQueue;
+class SavePageRequest;
+class Scheduler;
+class ClientPolicyController;
+
+// Coordinates queueing and processing save page later requests.
+class RequestCoordinator : public KeyedService,
+                           public RequestNotifier,
+                           public base::SupportsUserData {
+ public:
+  // Nested observer class.  To make sure that no events are missed, the client
+  // code should first register for notifications, then |GetAllRequests|, and
+  // ignore all events before the return from |GetAllRequests|, and consume
+  // events after the return callback from |GetAllRequests|.
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    virtual void OnAdded(const SavePageRequest& request) = 0;
+    virtual void OnCompleted(
+        const SavePageRequest& request,
+        RequestNotifier::BackgroundSavePageResult status) = 0;
+    virtual void OnChanged(const SavePageRequest& request) = 0;
+  };
+
+  enum class RequestAvailability {
+    ENABLED_FOR_OFFLINER,
+    DISABLED_FOR_OFFLINER,
+  };
+
+  // Callback specifying which request IDs were actually removed.
+  typedef base::Callback<void(const MultipleItemStatuses&)>
+      RemoveRequestsCallback;
+
+  // Callback that receives the response for GetAllRequests.
+  typedef base::Callback<void(std::vector<std::unique_ptr<SavePageRequest>>)>
+      GetRequestsCallback;
+
+  RequestCoordinator(std::unique_ptr<OfflinerPolicy> policy,
+                     std::unique_ptr<OfflinerFactory> factory,
+                     std::unique_ptr<RequestQueue> queue,
+                     std::unique_ptr<Scheduler> scheduler,
+                     net::NetworkQualityEstimator::NetworkQualityProvider*
+                         network_quality_estimator);
+
+  ~RequestCoordinator() override;
+
+  // Queues |request| to later load and save when system conditions allow.
+  // Returns an id if the page could be queued successfully, 0L otherwise.
+  int64_t SavePageLater(const GURL& url,
+                        const ClientId& client_id,
+                        bool user_requested,
+                        RequestAvailability availability);
+
+  // Remove a list of requests by |request_id|.  This removes requests from the
+  // request queue, and cancels an in-progress prerender.
+  void RemoveRequests(const std::vector<int64_t>& request_ids,
+                      const RemoveRequestsCallback& callback);
+
+  // Pause a list of requests by |request_id|.  This will change the state
+  // in the request queue so the request cannot be started.
+  void PauseRequests(const std::vector<int64_t>& request_ids);
+
+  // Resume a list of previously paused requests, making them available.
+  void ResumeRequests(const std::vector<int64_t>& request_ids);
+
+  // Get all save page request items in the callback.
+  void GetAllRequests(const GetRequestsCallback& callback);
+
+  // Starts processing of one or more queued save page later requests.
+  // Returns whether processing was started and that caller should expect
+  // a callback. If processing was already active, returns false.
+  bool StartProcessing(const DeviceConditions& device_conditions,
+                       const base::Callback<void(bool)>& callback);
+
+  // Stops the current request processing if active. This is a way for
+  // caller to abort processing; otherwise, processing will complete on
+  // its own. In either case, the callback will be called when processing
+  // is stopped or complete.
+  void StopProcessing(Offliner::RequestStatus stop_status);
+
+  // Used to denote that the foreground thread is ready for the offliner
+  // to start work on a previously entered, but unavailable request.
+  void EnableForOffliner(int64_t request_id, const ClientId& client_id);
+
+  // If a request that is unavailable to the offliner is finished elsewhere,
+  // (by the tab helper synchronous download), send a notificaiton that it
+  // succeeded through our notificaiton system.
+  void MarkRequestCompleted(int64_t request_id);
+
+  const Scheduler::TriggerConditions GetTriggerConditions(
+      const bool user_requested);
+
+  // A way for tests to set the callback in use when an operation is over.
+  void SetProcessingCallbackForTest(const base::Callback<void(bool)> callback) {
+    scheduler_callback_ = callback;
+  }
+
+  // A way to set the callback which would be called if the request will be
+  // scheduled immediately. Used by testing harness to determine if a request
+  // has been processed.
+  void SetImmediateScheduleCallbackForTest(
+      const base::Callback<void(bool)> callback) {
+    immediate_schedule_callback_ = callback;
+  }
+
+  void StartImmediatelyForTest() { StartImmediatelyIfConnected(); }
+
+  // Observers implementing the RequestCoordinator::Observer interface can
+  // register here to get notifications of changes to request state.  This
+  // pointer is not owned, and it is the callers responsibility to remove the
+  // observer before the observer is deleted.
+  void AddObserver(RequestCoordinator::Observer* observer);
+
+  void RemoveObserver(RequestCoordinator::Observer* observer);
+
+  // Implement RequestNotifier
+  void NotifyAdded(const SavePageRequest& request) override;
+  void NotifyCompleted(
+      const SavePageRequest& request,
+      RequestNotifier::BackgroundSavePageResult status) override;
+  void NotifyChanged(const SavePageRequest& request) override;
+
+  // Returns the request queue used for requests.  Coordinator keeps ownership.
+  RequestQueue* queue() { return queue_.get(); }
+
+  // Return an unowned pointer to the Scheduler.
+  Scheduler* scheduler() { return scheduler_.get(); }
+
+  OfflinerPolicy* policy() { return policy_.get(); }
+
+  ClientPolicyController* GetPolicyController();
+
+  // Returns the status of the most recent offlining.
+  Offliner::RequestStatus last_offlining_status() {
+    return last_offlining_status_;
+  }
+
+  bool is_busy() { return is_busy_; }
+
+  // Returns whether processing is starting (before it is decided to actually
+  // process a request (is_busy()) at this time or not.
+  bool is_starting() { return is_starting_; }
+
+  // Tracks whether the last offlining attempt got canceled.  This is reset by
+  // the next StartProcessing() call.
+  bool is_canceled() {
+    return processing_state_ == ProcessingWindowState::STOPPED;
+  }
+
+  RequestCoordinatorEventLogger* GetLogger() { return &event_logger_; }
+
+ private:
+  // Immediate start attempt status code for UMA.
+  // These values are written to logs. New enum values can be added, but
+  // existing enums must never be renumbered or deleted and reused.
+  // For any additions, also update corresponding histogram in histograms.xml.
+  enum OfflinerImmediateStartStatus {
+    // Did start processing request.
+    STARTED = 0,
+    // Already busy processing a request.
+    BUSY = 1,
+    // The Offliner did not accept processing the request.
+    NOT_ACCEPTED = 2,
+    // No current network connection.
+    NO_CONNECTION = 3,
+    // Weak network connection (worse than 2G speed)
+    // according to network quality estimator.
+    WEAK_CONNECTION = 4,
+    // Did not start because this is svelte device.
+    NOT_STARTED_ON_SVELTE = 5,
+    // NOTE: insert new values above this line and update histogram enum too.
+    STATUS_COUNT = 6,
+  };
+
+  enum class ProcessingWindowState {
+    STOPPED,
+    SCHEDULED_WINDOW,
+    IMMEDIATE_WINDOW,
+  };
+
+  // Receives the results of a get from the request queue, and turns that into
+  // SavePageRequest objects for the caller of GetQueuedRequests.
+  void GetQueuedRequestsCallback(
+      const GetRequestsCallback& callback,
+      GetRequestsResult result,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Receives the results of a get from the request queue, and turns that into
+  // SavePageRequest objects for the caller of GetQueuedRequests.
+  void GetRequestsForSchedulingCallback(
+      GetRequestsResult result,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Receives the result of add requests to the request queue.
+  void AddRequestResultCallback(RequestAvailability availability,
+                                AddRequestResult result,
+                                const SavePageRequest& request);
+
+  void UpdateMultipleRequestsCallback(
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  void HandleRemovedRequestsAndCallback(
+      const RemoveRequestsCallback& callback,
+      RequestNotifier::BackgroundSavePageResult status,
+      std::unique_ptr<UpdateRequestsResult> result);
+
+  void HandleRemovedRequests(RequestNotifier::BackgroundSavePageResult status,
+                             std::unique_ptr<UpdateRequestsResult> result);
+
+  bool StartProcessingInternal(const ProcessingWindowState processing_state,
+                               const DeviceConditions& device_conditions,
+                               const base::Callback<void(bool)>& callback);
+
+  // Start processing now if connected (but with conservative assumption
+  // as to other device conditions).
+  void StartImmediatelyIfConnected();
+
+  OfflinerImmediateStartStatus TryImmediateStart();
+
+  // Requests a callback upon the next network connection to start processing.
+  void RequestConnectedEventForStarting();
+
+  // Clears the request for connected event if it was set.
+  void ClearConnectedEventRequest();
+
+  // Handles receiving a connection event. Will start immediate processing.
+  void HandleConnectedEventForStarting();
+
+  // Check the request queue, and schedule a task corresponding
+  // to the least restrictive type of request in the queue.
+  void ScheduleAsNeeded();
+
+  // Callback from the request picker when it has chosen our next request.
+  void RequestPicked(const SavePageRequest& request, bool cleanup_needed);
+
+  // Callback from the request picker when no more requests are in the queue.
+  // The parameter is a signal for what (if any) conditions to schedule future
+  // processing for.
+  void RequestNotPicked(bool non_user_requested_tasks_remaining,
+                        bool cleanup_needed);
+
+  // Callback from request picker that receives the current available queued
+  // request count as well as the total queued request count (which may be
+  // different if unavailable requests are queued such as paused requests).
+  // It also receives a flag as to whether this request picking is due to the
+  // start of a request processing window.
+  void RequestCounts(bool is_start_of_processing,
+                     size_t total_requests,
+                     size_t available_requests);
+
+  void HandleWatchdogTimeout();
+
+  // Cancels an in progress pre-rendering, and updates state appropriately.
+  void StopPrerendering(Offliner::RequestStatus stop_status);
+
+  // Marks attempt on the request and sends it to offliner in continuation.
+  void SendRequestToOffliner(const SavePageRequest& request);
+
+  // Continuation of |SendRequestToOffliner| after the request is marked as
+  // started.
+  void StartOffliner(int64_t request_id,
+                     const std::string& client_namespace,
+                     std::unique_ptr<UpdateRequestsResult> update_result);
+
+  // Called by the offliner when an offlining request is completed. (and by
+  // tests).
+  void OfflinerDoneCallback(const SavePageRequest& request,
+                            Offliner::RequestStatus status);
+
+  // Records a completed attempt for the request and update it in the queue
+  // (possibly removing it).
+  void UpdateRequestForCompletedAttempt(const SavePageRequest& request,
+                                        Offliner::RequestStatus status);
+
+  // Returns whether we should try another request based on the outcome
+  // of the previous one.
+  bool ShouldTryNextRequest(Offliner::RequestStatus previous_request_status);
+
+  // Try to find and start offlining an available request.
+  // |is_start_of_processing| identifies if this is the beginning of a
+  // processing window (vs. continuing within a current processing window).
+  void TryNextRequest(bool is_start_of_processing);
+
+  // If there is an active request in the list, cancel that request.
+  bool CancelActiveRequestIfItMatches(const std::vector<int64_t>& request_ids);
+
+  // Records an aborted attempt for the request and update it in the queue
+  // (possibly removing it).
+  void UpdateRequestForAbortedAttempt(const SavePageRequest& request);
+
+  // Remove the attempted request from the queue with status to pass through to
+  // any observers and UMA histogram.
+  void RemoveAttemptedRequest(const SavePageRequest& request,
+                              BackgroundSavePageResult status);
+
+  // Marks the attempt as aborted. This makes the request available again
+  // for offlining.
+  void MarkAttemptAborted(int64_t request_id, const std::string& name_space);
+
+  // Reports change from marking request, reports an error if it fails.
+  void MarkAttemptDone(int64_t request_id,
+                       const std::string& name_space,
+                       std::unique_ptr<UpdateRequestsResult> result);
+
+  // Returns the appropriate offliner to use, getting a new one from the factory
+  // if needed.
+  void GetOffliner();
+
+  // Method to wrap calls to getting the connection type so it can be
+  // changed for tests.
+  net::NetworkChangeNotifier::ConnectionType GetConnectionType();
+
+  void SetNetworkConditionsForTest(
+      net::NetworkChangeNotifier::ConnectionType connection) {
+    use_test_connection_type_ = true;
+    test_connection_type_ = connection;
+  }
+
+  void SetDeviceConditionsForTest(const DeviceConditions& current_conditions) {
+    current_conditions_.reset(new DeviceConditions(current_conditions));
+  }
+
+  // KeyedService implementation:
+  void Shutdown() override;
+
+  friend class RequestCoordinatorTest;
+
+  // Cached value of whether low end device. Overwritable for testing.
+  bool is_low_end_device_;
+
+  // The offliner can only handle one request at a time - if the offliner is
+  // busy, prevent other requests.  This flag marks whether the offliner is in
+  // use.
+  bool is_busy_;
+  // There is more than one path to start processing so this flag is used
+  // to avoid race conditions before is_busy_ is established.
+  bool is_starting_;
+  // Identifies the type of current processing window or if processing stopped.
+  ProcessingWindowState processing_state_;
+  // True if we should use the test connection type instead of the actual type.
+  bool use_test_connection_type_;
+  // For use by tests, a fake network connection type
+  net::NetworkChangeNotifier::ConnectionType test_connection_type_;
+  // Unowned pointer to the current offliner, if any.
+  Offliner* offliner_;
+  base::Time operation_start_time_;
+  // The observers.
+  base::ObserverList<Observer> observers_;
+  // Last known conditions for network, battery
+  std::unique_ptr<DeviceConditions> current_conditions_;
+  // RequestCoordinator takes over ownership of the policy
+  std::unique_ptr<OfflinerPolicy> policy_;
+  // OfflinerFactory.  Used to create offline pages. Owned.
+  std::unique_ptr<OfflinerFactory> factory_;
+  // RequestQueue.  Used to store incoming requests. Owned.
+  std::unique_ptr<RequestQueue> queue_;
+  // Scheduler. Used to request a callback when network is available.  Owned.
+  std::unique_ptr<Scheduler> scheduler_;
+  // Controller of client policies. Owned.
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+  // Unowned pointer to the Network Quality Estimator.
+  net::NetworkQualityEstimator::NetworkQualityProvider*
+      network_quality_estimator_;
+  // Holds copy of the active request, if any.
+  std::unique_ptr<SavePageRequest> active_request_;
+  // Status of the most recent offlining.
+  Offliner::RequestStatus last_offlining_status_;
+  // A set of request_ids that we are holding off until the download manager is
+  // done with them.
+  std::set<int64_t> disabled_requests_;
+  // Calling this returns to the scheduler across the JNI bridge.
+  base::Callback<void(bool)> scheduler_callback_;
+  // Logger to record events.
+  RequestCoordinatorEventLogger event_logger_;
+  // Timer to watch for pre-render attempts running too long.
+  base::OneShotTimer watchdog_timer_;
+  // Callback invoked when an immediate request is done (default empty).
+  base::Callback<void(bool)> immediate_schedule_callback_;
+  // Used for potential immediate processing when we get network connection.
+  std::unique_ptr<ConnectionNotifier> connection_notifier_;
+  // Allows us to pass a weak pointer to callbacks.
+  base::WeakPtrFactory<RequestCoordinator> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestCoordinator);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
diff --git a/components/offline_pages/core/background/request_coordinator_event_logger.cc b/components/offline_pages/core/background/request_coordinator_event_logger.cc
new file mode 100644
index 0000000..17be1fc6
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator_event_logger.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+
+namespace offline_pages {
+
+namespace {
+
+static std::string OfflinerRequestStatusToString(
+    Offliner::RequestStatus request_status) {
+  switch (request_status) {
+    case Offliner::UNKNOWN:
+      return "UNKNOWN";
+    case Offliner::LOADED:
+      return "LOADED";
+    case Offliner::SAVED:
+      return "SAVED";
+    case Offliner::REQUEST_COORDINATOR_CANCELED:
+      return "REQUEST_COORDINATOR_CANCELED";
+    case Offliner::PRERENDERING_CANCELED:
+      return "PRERENDERING_CANCELED";
+    case Offliner::PRERENDERING_FAILED:
+      return "PRERENDERING_FAILED";
+    case Offliner::SAVE_FAILED:
+      return "SAVE_FAILED";
+    case Offliner::FOREGROUND_CANCELED:
+      return "FOREGROUND_CANCELED";
+    case Offliner::REQUEST_COORDINATOR_TIMED_OUT:
+      return "REQUEST_COORDINATOR_TIMED_OUT";
+    case Offliner::PRERENDERING_NOT_STARTED:
+      return "PRERENDERING_NOT_STARTED";
+    case Offliner::PRERENDERING_FAILED_NO_RETRY:
+      return "PRERENDERING_FAILED_NO_RETRY";
+    case Offliner::PRERENDERING_FAILED_NO_NEXT:
+      return "PRERENDERING_FAILED_NO_NEXT";
+    default:
+      NOTREACHED();
+      return std::to_string(static_cast<int>(request_status));
+  }
+}
+
+static std::string BackgroundSavePageResultToString(
+    RequestNotifier::BackgroundSavePageResult result) {
+  switch (result) {
+    case RequestNotifier::BackgroundSavePageResult::SUCCESS:
+      return "SUCCESS";
+    case RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE:
+      return "PRERENDER_FAILURE";
+    case RequestNotifier::BackgroundSavePageResult::PRERENDER_CANCELED:
+      return "PRERENDER_CANCELED";
+    case RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED:
+      return "FOREGROUND_CANCELED";
+    case RequestNotifier::BackgroundSavePageResult::SAVE_FAILED:
+      return "SAVE_FAILED";
+    case RequestNotifier::BackgroundSavePageResult::EXPIRED:
+      return "EXPIRED";
+    case RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED:
+      return "RETRY_COUNT_EXCEEDED";
+    case RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED:
+      return "START_COUNT_EXCEEDED";
+    case RequestNotifier::BackgroundSavePageResult::REMOVED:
+      return "REMOVED";
+    default:
+      NOTREACHED();
+      return std::to_string(static_cast<int>(result));
+  }
+}
+
+static std::string UpdateRequestResultToString(UpdateRequestResult result) {
+  switch (result) {
+    case UpdateRequestResult::SUCCESS:
+      return "SUCCESS";
+    case UpdateRequestResult::STORE_FAILURE:
+      return "STORE_FAILURE";
+    case UpdateRequestResult::REQUEST_DOES_NOT_EXIST:
+      return "REQUEST_DOES_NOT_EXIST";
+    default:
+      NOTREACHED();
+      return std::to_string(static_cast<int>(result));
+  }
+}
+
+}  // namespace
+
+void RequestCoordinatorEventLogger::RecordOfflinerResult(
+    const std::string& name_space,
+    Offliner::RequestStatus new_status,
+    int64_t request_id) {
+  RecordActivity("Background save attempt for " + name_space + ":" +
+                 std::to_string(request_id) + " - " +
+                 OfflinerRequestStatusToString(new_status));
+}
+
+void RequestCoordinatorEventLogger::RecordDroppedSavePageRequest(
+    const std::string& name_space,
+    RequestNotifier::BackgroundSavePageResult result,
+    int64_t request_id) {
+  RecordActivity("Background save request removed " + name_space + ":" +
+                 std::to_string(request_id) + " - " +
+                 BackgroundSavePageResultToString(result));
+}
+
+void RequestCoordinatorEventLogger::RecordUpdateRequestFailed(
+    const std::string& name_space,
+    UpdateRequestResult result) {
+  RecordActivity("Updating queued request for " + name_space + " failed - " +
+                 UpdateRequestResultToString(result));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator_event_logger.h b/components/offline_pages/core/background/request_coordinator_event_logger.h
new file mode 100644
index 0000000..92bd009
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator_event_logger.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+
+namespace offline_pages {
+
+class RequestCoordinatorEventLogger : public OfflineEventLogger {
+ public:
+  // Records the result of a background task attempt for SavePageRequest
+  // |request_id|.
+  void RecordOfflinerResult(const std::string& name_space,
+                            Offliner::RequestStatus new_status,
+                            int64_t request_id);
+
+  // Records the reason for dropped SavePageRequest |request_id|.
+  void RecordDroppedSavePageRequest(
+      const std::string& name_space,
+      RequestNotifier::BackgroundSavePageResult result,
+      int64_t request_id);
+
+  void RecordUpdateRequestFailed(const std::string& name_space,
+                                 UpdateRequestResult result);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_EVENT_LOGGER_H_
diff --git a/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc b/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc
new file mode 100644
index 0000000..8aa7f6c
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator_event_logger_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+
+const char kNamespace[] = "last_n";
+const Offliner::RequestStatus kOfflinerStatus = Offliner::SAVED;
+const RequestNotifier::BackgroundSavePageResult kDroppedResult =
+    RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED;
+const int64_t kId = 1234;
+const UpdateRequestResult kQueueUpdateResult =
+    UpdateRequestResult::STORE_FAILURE;
+
+const char kOfflinerStatusLogString[] =
+    "Background save attempt for last_n:1234 - SAVED";
+const char kDroppedResultLogString[] =
+    "Background save request removed last_n:1234 - START_COUNT_EXCEEDED";
+const char kQueueUpdateResultLogString[] =
+    "Updating queued request for last_n failed - STORE_FAILURE";
+const int kTimeLength = 21;
+
+}  // namespace
+
+TEST(RequestCoordinatorEventLoggerTest, RecordsWhenLoggingIsOn) {
+  RequestCoordinatorEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(true);
+  logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
+  logger.RecordDroppedSavePageRequest(kNamespace, kDroppedResult, kId);
+  logger.RecordUpdateRequestFailed(kNamespace, kQueueUpdateResult);
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(3u, log.size());
+  EXPECT_EQ(std::string(kQueueUpdateResultLogString),
+            log[0].substr(kTimeLength));
+  EXPECT_EQ(std::string(kDroppedResultLogString), log[1].substr(kTimeLength));
+  EXPECT_EQ(std::string(kOfflinerStatusLogString), log[2].substr(kTimeLength));
+}
+
+TEST(RequestCoordinatorEventLoggerTest, RecordsWhenLoggingIsOff) {
+  RequestCoordinatorEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(false);
+  logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(0u, log.size());
+}
+
+TEST(RequestCoordinatorEventLoggerTest, DoesNotExceedMaxSize) {
+  RequestCoordinatorEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(true);
+  for (size_t i = 0; i < kMaxLogCount + 1; ++i) {
+    logger.RecordOfflinerResult(kNamespace, kOfflinerStatus, kId);
+  }
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(kMaxLogCount, log.size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
new file mode 100644
index 0000000..45dddbd4
--- /dev/null
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -0,0 +1,1378 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_coordinator.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/network_quality_provider_stub.h"
+#include "components/offline_pages/core/background/offliner.h"
+#include "components/offline_pages/core/background/offliner_factory.h"
+#include "components/offline_pages/core/background/offliner_factory_stub.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/offliner_stub.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/background/scheduler.h"
+#include "components/offline_pages/core/background/scheduler_stub.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// put test constants here
+const GURL kUrl1("http://universe.com/everything");
+const GURL kUrl2("http://universe.com/toinfinityandbeyond");
+const std::string kClientNamespace("bookmark");
+const std::string kId1("42");
+const std::string kId2("life*universe+everything");
+const ClientId kClientId1(kClientNamespace, kId1);
+const ClientId kClientId2(kClientNamespace, kId2);
+const int kRequestId1(1);
+const int kRequestId2(2);
+const long kTestTimeBudgetSeconds = 200;
+const int kBatteryPercentageHigh = 75;
+const int kMaxCompletedTries = 3;
+const bool kPowerRequired = true;
+const bool kUserRequested = true;
+const int kAttemptCount = 1;
+}  // namespace
+
+class ObserverStub : public RequestCoordinator::Observer {
+ public:
+  ObserverStub()
+      : added_called_(false),
+        completed_called_(false),
+        changed_called_(false),
+        last_status_(RequestCoordinator::BackgroundSavePageResult::SUCCESS),
+        state_(SavePageRequest::RequestState::OFFLINING) {}
+
+  void Clear() {
+    added_called_ = false;
+    completed_called_ = false;
+    changed_called_ = false;
+    state_ = SavePageRequest::RequestState::OFFLINING;
+    last_status_ = RequestCoordinator::BackgroundSavePageResult::SUCCESS;
+  }
+
+  void OnAdded(const SavePageRequest& request) override {
+    added_called_ = true;
+  }
+
+  void OnCompleted(
+      const SavePageRequest& request,
+      RequestCoordinator::BackgroundSavePageResult status) override {
+    completed_called_ = true;
+    last_status_ = status;
+  }
+
+  void OnChanged(const SavePageRequest& request) override {
+    changed_called_ = true;
+    state_ = request.request_state();
+  }
+
+  bool added_called() { return added_called_; }
+  bool completed_called() { return completed_called_; }
+  bool changed_called() { return changed_called_; }
+  RequestCoordinator::BackgroundSavePageResult last_status() {
+    return last_status_;
+  }
+  SavePageRequest::RequestState state() { return state_; }
+
+ private:
+  bool added_called_;
+  bool completed_called_;
+  bool changed_called_;
+  RequestCoordinator::BackgroundSavePageResult last_status_;
+  SavePageRequest::RequestState state_;
+};
+
+class RequestCoordinatorTest : public testing::Test {
+ public:
+  RequestCoordinatorTest();
+  ~RequestCoordinatorTest() override;
+
+  void SetUp() override;
+
+  void PumpLoop();
+
+  RequestCoordinator* coordinator() { return coordinator_.get(); }
+
+  bool is_busy() { return coordinator_->is_busy(); }
+
+  bool is_starting() { return coordinator_->is_starting(); }
+
+  // Empty callback function.
+  void ImmediateScheduleCallbackFunction(bool result) {
+    immediate_schedule_callback_called_ = true;
+    immediate_schedule_callback_result_ = result;
+  }
+
+  // Callback function which releases a wait for it.
+  void WaitingCallbackFunction(bool result) { waiter_.Signal(); }
+
+  net::NetworkChangeNotifier::ConnectionType GetConnectionType() {
+    return coordinator()->GetConnectionType();
+  }
+
+  // Callback for Add requests.
+  void AddRequestDone(AddRequestResult result, const SavePageRequest& request);
+
+  // Callback for getting requests.
+  void GetRequestsDone(GetRequestsResult result,
+                       std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  // Callback for removing requests.
+  void RemoveRequestsDone(const MultipleItemStatuses& results);
+
+  // Callback for getting request statuses.
+  void GetQueuedRequestsDone(
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  void SetupForOfflinerDoneCallbackTest(
+      offline_pages::SavePageRequest* request);
+
+  void SendOfflinerDoneCallback(const SavePageRequest& request,
+                                Offliner::RequestStatus status);
+
+  GetRequestsResult last_get_requests_result() const {
+    return last_get_requests_result_;
+  }
+
+  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+    return last_requests_;
+  }
+
+  const MultipleItemStatuses& last_remove_results() const {
+    return last_remove_results_;
+  }
+
+  void DisableLoading() { offliner_->disable_loading(); }
+
+  void EnableOfflinerCallback(bool enable) {
+    offliner_->enable_callback(enable);
+  }
+
+  void SetNetworkConditionsForTest(
+      net::NetworkChangeNotifier::ConnectionType connection) {
+    coordinator()->SetNetworkConditionsForTest(connection);
+  }
+
+  void SetEffectiveConnectionTypeForTest(net::EffectiveConnectionType type) {
+    network_quality_estimator_->SetEffectiveConnectionTypeForTest(type);
+  }
+
+  void SetNetworkConnected(bool connected) {
+    if (connected) {
+      SetNetworkConditionsForTest(
+          net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
+      SetEffectiveConnectionTypeForTest(
+          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G);
+    } else {
+      SetNetworkConditionsForTest(
+          net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
+      SetEffectiveConnectionTypeForTest(
+          net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
+    }
+  }
+
+  void CallConnectionTypeObserver() {
+    if (coordinator()->connection_notifier_) {
+      coordinator()->connection_notifier_->OnConnectionTypeChanged(
+          GetConnectionType());
+    }
+  }
+
+  void SetIsLowEndDeviceForTest(bool is_low_end_device) {
+    coordinator()->is_low_end_device_ = is_low_end_device;
+  }
+
+  void SetProcessingStateForTest(
+      RequestCoordinator::ProcessingWindowState processing_state) {
+    coordinator()->processing_state_ = processing_state;
+  }
+
+  void SetOperationStartTimeForTest(base::Time start_time) {
+    coordinator()->operation_start_time_ = start_time;
+  }
+
+  void ScheduleForTest() { coordinator_->ScheduleAsNeeded(); }
+
+  void CallRequestNotPicked(bool non_user_requested_tasks_remaining,
+                            bool disabled_tasks_remaining) {
+    if (disabled_tasks_remaining)
+      coordinator_->disabled_requests_.insert(kRequestId1);
+    else
+      coordinator_->disabled_requests_.clear();
+
+    coordinator_->RequestNotPicked(non_user_requested_tasks_remaining, false);
+  }
+
+  void SetDeviceConditionsForTest(DeviceConditions device_conditions) {
+    coordinator_->SetDeviceConditionsForTest(device_conditions);
+  }
+
+  void WaitForCallback() { waiter_.Wait(); }
+
+  void AdvanceClockBy(base::TimeDelta delta) {
+    task_runner_->FastForwardBy(delta);
+  }
+
+  SavePageRequest AddRequest1();
+
+  SavePageRequest AddRequest2();
+
+  Offliner::RequestStatus last_offlining_status() const {
+    return coordinator_->last_offlining_status_;
+  }
+
+  bool OfflinerWasCanceled() const { return offliner_->cancel_called(); }
+
+  ObserverStub observer() { return observer_; }
+
+  DeviceConditions device_conditions() { return device_conditions_; }
+
+  base::Callback<void(bool)> immediate_callback() {
+    return immediate_callback_;
+  }
+
+  base::Callback<void(bool)> waiting_callback() { return waiting_callback_; }
+  bool immediate_schedule_callback_called() const {
+    return immediate_schedule_callback_called_;
+  }
+
+  bool immediate_schedule_callback_result() const {
+    return immediate_schedule_callback_result_;
+  }
+
+  const base::HistogramTester& histograms() const { return histogram_tester_; }
+
+ private:
+  GetRequestsResult last_get_requests_result_;
+  MultipleItemStatuses last_remove_results_;
+  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  std::unique_ptr<NetworkQualityProviderStub> network_quality_estimator_;
+  std::unique_ptr<RequestCoordinator> coordinator_;
+  OfflinerStub* offliner_;
+  base::WaitableEvent waiter_;
+  ObserverStub observer_;
+  bool immediate_schedule_callback_called_;
+  bool immediate_schedule_callback_result_;
+  DeviceConditions device_conditions_;
+  base::Callback<void(bool)> immediate_callback_;
+  base::Callback<void(bool)> waiting_callback_;
+  base::HistogramTester histogram_tester_;
+};
+
+RequestCoordinatorTest::RequestCoordinatorTest()
+    : last_get_requests_result_(GetRequestsResult::STORE_FAILURE),
+      task_runner_(new base::TestMockTimeTaskRunner),
+      task_runner_handle_(task_runner_),
+      offliner_(nullptr),
+      waiter_(base::WaitableEvent::ResetPolicy::MANUAL,
+              base::WaitableEvent::InitialState::NOT_SIGNALED),
+      immediate_schedule_callback_called_(false),
+      immediate_schedule_callback_result_(false),
+      device_conditions_(!kPowerRequired,
+                         kBatteryPercentageHigh,
+                         net::NetworkChangeNotifier::CONNECTION_3G) {}
+
+RequestCoordinatorTest::~RequestCoordinatorTest() {}
+
+void RequestCoordinatorTest::SetUp() {
+  std::unique_ptr<OfflinerPolicy> policy(new OfflinerPolicy());
+  std::unique_ptr<OfflinerFactory> offliner_factory(new OfflinerFactoryStub());
+  // Save the offliner for use by the tests.
+  offliner_ = reinterpret_cast<OfflinerStub*>(
+      offliner_factory->GetOffliner(policy.get()));
+  std::unique_ptr<RequestQueueInMemoryStore> store(
+      new RequestQueueInMemoryStore());
+  std::unique_ptr<RequestQueue> queue(new RequestQueue(std::move(store)));
+  std::unique_ptr<Scheduler> scheduler_stub(new SchedulerStub());
+  network_quality_estimator_.reset(new NetworkQualityProviderStub());
+  coordinator_.reset(new RequestCoordinator(
+      std::move(policy), std::move(offliner_factory), std::move(queue),
+      std::move(scheduler_stub), network_quality_estimator_.get()));
+  coordinator_->AddObserver(&observer_);
+  SetNetworkConnected(true);
+  immediate_callback_ =
+      base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
+                 base::Unretained(this));
+  // Override the normal immediate callback with a wait releasing callback.
+  waiting_callback_ = base::Bind(
+      &RequestCoordinatorTest::WaitingCallbackFunction, base::Unretained(this));
+  SetDeviceConditionsForTest(device_conditions_);
+  EnableOfflinerCallback(true);
+}
+
+void RequestCoordinatorTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void RequestCoordinatorTest::GetRequestsDone(
+    GetRequestsResult result,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  last_get_requests_result_ = result;
+  last_requests_ = std::move(requests);
+}
+
+void RequestCoordinatorTest::RemoveRequestsDone(
+    const MultipleItemStatuses& results) {
+  last_remove_results_ = results;
+  waiter_.Signal();
+}
+
+void RequestCoordinatorTest::GetQueuedRequestsDone(
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  last_requests_ = std::move(requests);
+  waiter_.Signal();
+}
+
+void RequestCoordinatorTest::AddRequestDone(AddRequestResult result,
+                                            const SavePageRequest& request) {}
+
+void RequestCoordinatorTest::SetupForOfflinerDoneCallbackTest(
+    offline_pages::SavePageRequest* request) {
+  // Mark request as started and add it to the queue,
+  // then wait for callback to finish.
+  request->MarkAttemptStarted(base::Time::Now());
+  coordinator()->queue()->AddRequest(
+      *request, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                           base::Unretained(this)));
+  PumpLoop();
+
+  // Override the processing callback for test visiblity.
+  base::Callback<void(bool)> callback =
+      base::Bind(&RequestCoordinatorTest::ImmediateScheduleCallbackFunction,
+                 base::Unretained(this));
+  coordinator()->SetProcessingCallbackForTest(callback);
+
+  // Mock that coordinator is in actively processing state starting now.
+  SetProcessingStateForTest(
+      RequestCoordinator::ProcessingWindowState::IMMEDIATE_WINDOW);
+  SetOperationStartTimeForTest(base::Time::Now());
+}
+
+void RequestCoordinatorTest::SendOfflinerDoneCallback(
+    const SavePageRequest& request,
+    Offliner::RequestStatus status) {
+  // Using the fact that the test class is a friend, call to the callback
+  coordinator_->OfflinerDoneCallback(request, status);
+}
+
+SavePageRequest RequestCoordinatorTest::AddRequest1() {
+  offline_pages::SavePageRequest request1(kRequestId1, kUrl1, kClientId1,
+                                          base::Time::Now(), kUserRequested);
+  coordinator()->queue()->AddRequest(
+      request1, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                           base::Unretained(this)));
+  return request1;
+}
+
+SavePageRequest RequestCoordinatorTest::AddRequest2() {
+  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
+                                          base::Time::Now(), kUserRequested);
+  coordinator()->queue()->AddRequest(
+      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                           base::Unretained(this)));
+  return request2;
+}
+
+TEST_F(RequestCoordinatorTest, StartProcessingWithNoRequests) {
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+  PumpLoop();
+
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Verify queue depth UMA for starting scheduled processing on empty queue.
+  if (base::SysInfo::IsLowEndDevice()) {
+    histograms().ExpectBucketCount(
+        "OfflinePages.Background.ScheduledStart.AvailableRequestCount.Svelte",
+        0, 1);
+  } else {
+    histograms().ExpectBucketCount(
+        "OfflinePages.Background.ScheduledStart.AvailableRequestCount", 0, 1);
+  }
+}
+
+TEST_F(RequestCoordinatorTest, StartProcessingWithRequestInProgress) {
+  // Start processing for this request.
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+
+  // Ensure that the forthcoming request does not finish - we simulate it being
+  // in progress by asking it to skip making the completion callback.
+  EnableOfflinerCallback(false);
+
+  // Sending the request to the offliner should make it busy.
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+  PumpLoop();
+
+  EXPECT_TRUE(is_busy());
+  // Since the offliner is disabled, this callback should not be called.
+  EXPECT_FALSE(immediate_schedule_callback_called());
+
+  // Now trying to start processing on another request should return false.
+  EXPECT_FALSE(coordinator()->StartProcessing(device_conditions(),
+                                              immediate_callback()));
+}
+
+TEST_F(RequestCoordinatorTest, SavePageLater) {
+  // The user-requested request which gets processed by SavePageLater
+  // would invoke user request callback.
+  coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
+
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+
+  // Expect that a request got placed on the queue.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+
+  // Wait for callbacks to finish, both request queue and offliner.
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Check the request queue is as expected.
+  EXPECT_EQ(1UL, last_requests().size());
+  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
+  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
+
+  // Expect that the scheduler got notified.
+  SchedulerStub* scheduler_stub =
+      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+  EXPECT_TRUE(scheduler_stub->schedule_called());
+  EXPECT_EQ(coordinator()
+                ->GetTriggerConditions(last_requests()[0]->user_requested())
+                .minimum_battery_percentage,
+            scheduler_stub->conditions()->minimum_battery_percentage);
+
+  // Check that the observer got the notification that a page is available
+  EXPECT_TRUE(observer().added_called());
+
+  // Verify queue depth UMA for starting immediate processing.
+  if (base::SysInfo::IsLowEndDevice()) {
+    histograms().ExpectBucketCount(
+        "OfflinePages.Background.ImmediateStart.AvailableRequestCount.Svelte",
+        1, 1);
+  } else {
+    histograms().ExpectBucketCount(
+        "OfflinePages.Background.ImmediateStart.AvailableRequestCount", 1, 1);
+  }
+}
+
+TEST_F(RequestCoordinatorTest, SavePageLaterFailed) {
+  // The user-requested request which gets processed by SavePageLater
+  // would invoke user request callback.
+  coordinator()->SetImmediateScheduleCallbackForTest(immediate_callback());
+
+  EXPECT_TRUE(
+      coordinator()->SavePageLater(
+          kUrl1, kClientId1, kUserRequested,
+          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER) != 0);
+
+  // Expect that a request got placed on the queue.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+
+  // Wait for callbacks to finish, both request queue and offliner.
+  PumpLoop();
+
+  // On low-end devices the callback will be called with false since the
+  // processing started but failed due to svelte devices.
+  EXPECT_TRUE(immediate_schedule_callback_called());
+  if (base::SysInfo::IsLowEndDevice()) {
+    EXPECT_FALSE(immediate_schedule_callback_result());
+  } else {
+    EXPECT_TRUE(immediate_schedule_callback_result());
+  }
+
+  // Check the request queue is as expected.
+  EXPECT_EQ(1UL, last_requests().size());
+  EXPECT_EQ(kUrl1, last_requests().at(0)->url());
+  EXPECT_EQ(kClientId1, last_requests().at(0)->client_id());
+
+  // Expect that the scheduler got notified.
+  SchedulerStub* scheduler_stub =
+      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+  EXPECT_TRUE(scheduler_stub->schedule_called());
+  EXPECT_EQ(coordinator()
+                ->GetTriggerConditions(last_requests()[0]->user_requested())
+                .minimum_battery_percentage,
+            scheduler_stub->conditions()->minimum_battery_percentage);
+
+  // Check that the observer got the notification that a page is available
+  EXPECT_TRUE(observer().added_called());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceeded) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+
+  // Call the OfflinerDoneCallback to simulate the page being completed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Verify the request gets removed from the queue, and wait for callbacks.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // We should not find any requests in the queue anymore.
+  // RequestPicker should *not* have tried to start an additional job,
+  // because the request queue is empty now.
+  EXPECT_EQ(0UL, last_requests().size());
+  // Check that the observer got the notification that we succeeded, and that
+  // the request got removed from the queue.
+  EXPECT_TRUE(observer().completed_called());
+  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::SUCCESS,
+            observer().last_status());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceededButLostNetwork) {
+  // Add a request to the queue and set offliner done callback for it.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+  EnableOfflinerCallback(false);
+
+  // Add a 2nd request to the queue.
+  AddRequest2();
+
+  // Disconnect network.
+  SetNetworkConnected(false);
+
+  // Call the OfflinerDoneCallback to simulate the page being completed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(request, Offliner::RequestStatus::SAVED);
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Verify not busy with 2nd request (since no connection).
+  EXPECT_FALSE(is_busy());
+
+  // Now connect network and verify processing starts.
+  SetNetworkConnected(true);
+  CallConnectionTypeObserver();
+  PumpLoop();
+  EXPECT_TRUE(is_busy());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailed) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  request.set_completed_attempt_count(kMaxCompletedTries - 1);
+  SetupForOfflinerDoneCallbackTest(&request);
+  // Stop processing before completing the second request on the queue.
+  EnableOfflinerCallback(false);
+
+  // Add second request to the queue to check handling when first fails.
+  AddRequest2();
+  PumpLoop();
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(request,
+                           Offliner::RequestStatus::PRERENDERING_FAILED);
+  PumpLoop();
+
+  // For retriable failure, processing should continue to 2nd request so
+  // no scheduler callback yet.
+  EXPECT_FALSE(immediate_schedule_callback_called());
+
+  // Busy processing 2nd request.
+  EXPECT_TRUE(is_busy());
+
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Now just one request in the queue since failed request removed
+  // (max number of attempts exceeded).
+  EXPECT_EQ(1UL, last_requests().size());
+  // Check that the observer got the notification that we failed (and the
+  // subsequent notification that the request was removed) since we exceeded
+  // retry count.
+  EXPECT_TRUE(observer().completed_called());
+  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED,
+            observer().last_status());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoRetryFailure) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+  EnableOfflinerCallback(false);
+
+  // Add second request to the queue to check handling when first fails.
+  AddRequest2();
+  PumpLoop();
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(
+      request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY);
+  PumpLoop();
+
+  // For no retry failure, processing should continue to 2nd request so
+  // no scheduler callback yet.
+  EXPECT_FALSE(immediate_schedule_callback_called());
+
+  // Busy processing 2nd request.
+  EXPECT_TRUE(is_busy());
+
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Now just one request in the queue since non-retryable failure.
+  EXPECT_EQ(1UL, last_requests().size());
+  // Check that the observer got the notification that we failed (and the
+  // subsequent notification that the request was removed).
+  EXPECT_TRUE(observer().completed_called());
+  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::PRERENDER_FAILURE,
+            observer().last_status());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoNextFailure) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+  EnableOfflinerCallback(false);
+
+  // Add second request to the queue to check handling when first fails.
+  AddRequest2();
+  PumpLoop();
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(
+      request, Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT);
+  PumpLoop();
+
+  // For no next failure, processing should not continue to 2nd request so
+  // expect scheduler callback.
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Not busy for NO_NEXT failure.
+  EXPECT_FALSE(is_busy());
+
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Both requests still in queue.
+  EXPECT_EQ(2UL, last_requests().size());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(request,
+                           Offliner::RequestStatus::FOREGROUND_CANCELED);
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Verify the request is not removed from the queue, and wait for callbacks.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Request no longer in the queue (for single attempt policy).
+  EXPECT_EQ(1UL, last_requests().size());
+  // Verify foreground cancel not counted as an attempt after all.
+  EXPECT_EQ(0L, last_requests().at(0)->completed_attempt_count());
+}
+
+TEST_F(RequestCoordinatorTest, OfflinerDonePrerenderingCancel) {
+  // Add a request to the queue, wait for callbacks to finish.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  SetupForOfflinerDoneCallbackTest(&request);
+
+  // Call the OfflinerDoneCallback to simulate the request failed, wait
+  // for callbacks.
+  SendOfflinerDoneCallback(request,
+                           Offliner::RequestStatus::PRERENDERING_CANCELED);
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // Verify the request is not removed from the queue, and wait for callbacks.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Request still in the queue.
+  EXPECT_EQ(1UL, last_requests().size());
+  // Verify prerendering cancel not counted as an attempt after all.
+  const std::unique_ptr<SavePageRequest>& found_request =
+      last_requests().front();
+  EXPECT_EQ(0L, found_request->completed_attempt_count());
+}
+
+// If one item completes, and there are no more user requeted items left,
+// we should make a scheduler entry for a non-user requested item.
+TEST_F(RequestCoordinatorTest, RequestNotPickedDisabledItemsRemain) {
+  coordinator()->StartProcessing(device_conditions(), immediate_callback());
+  EXPECT_TRUE(is_starting());
+
+  // Call RequestNotPicked, simulating a request on the disabled list.
+  CallRequestNotPicked(false, true);
+  PumpLoop();
+
+  EXPECT_FALSE(is_starting());
+
+  // The scheduler should have been called to schedule the disabled task for
+  // 5 minutes from now.
+  SchedulerStub* scheduler_stub =
+      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+  EXPECT_TRUE(scheduler_stub->backup_schedule_called());
+  EXPECT_TRUE(scheduler_stub->unschedule_called());
+}
+
+// If one item completes, and there are no more user requeted items left,
+// we should make a scheduler entry for a non-user requested item.
+TEST_F(RequestCoordinatorTest, RequestNotPickedNonUserRequestedItemsRemain) {
+  coordinator()->StartProcessing(device_conditions(), immediate_callback());
+  EXPECT_TRUE(is_starting());
+
+  // Call RequestNotPicked, and make sure we pick schedule a task for non user
+  // requested conditions, with no tasks on the disabled list.
+  CallRequestNotPicked(true, false);
+  PumpLoop();
+
+  EXPECT_FALSE(is_starting());
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  // The scheduler should have been called to schedule the non-user requested
+  // task.
+  SchedulerStub* scheduler_stub =
+      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+  EXPECT_TRUE(scheduler_stub->schedule_called());
+  EXPECT_TRUE(scheduler_stub->unschedule_called());
+  const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
+  EXPECT_EQ(conditions->require_power_connected,
+            coordinator()->policy()->PowerRequired(!kUserRequested));
+  EXPECT_EQ(
+      conditions->minimum_battery_percentage,
+      coordinator()->policy()->BatteryPercentageRequired(!kUserRequested));
+  EXPECT_EQ(conditions->require_unmetered_network,
+            coordinator()->policy()->UnmeteredNetworkRequired(!kUserRequested));
+}
+
+TEST_F(RequestCoordinatorTest, SchedulerGetsLeastRestrictiveConditions) {
+  // Put two requests on the queue - The first is user requested, and
+  // the second is not user requested.
+  AddRequest1();
+  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
+                                          base::Time::Now(), !kUserRequested);
+  coordinator()->queue()->AddRequest(
+      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                           base::Unretained(this)));
+  PumpLoop();
+
+  // Trigger the scheduler to schedule for the least restrictive condition.
+  ScheduleForTest();
+  PumpLoop();
+
+  // Expect that the scheduler got notified, and it is at user_requested
+  // priority.
+  SchedulerStub* scheduler_stub =
+      reinterpret_cast<SchedulerStub*>(coordinator()->scheduler());
+  const Scheduler::TriggerConditions* conditions = scheduler_stub->conditions();
+  EXPECT_TRUE(scheduler_stub->schedule_called());
+  EXPECT_EQ(conditions->require_power_connected,
+            coordinator()->policy()->PowerRequired(kUserRequested));
+  EXPECT_EQ(conditions->minimum_battery_percentage,
+            coordinator()->policy()->BatteryPercentageRequired(kUserRequested));
+  EXPECT_EQ(conditions->require_unmetered_network,
+            coordinator()->policy()->UnmeteredNetworkRequired(kUserRequested));
+}
+
+TEST_F(RequestCoordinatorTest, StartProcessingWithLoadingDisabled) {
+  // Add a request to the queue, wait for callbacks to finish.
+  AddRequest1();
+  PumpLoop();
+
+  DisableLoading();
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+
+  // Let the async callbacks in the request coordinator run.
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  EXPECT_FALSE(is_starting());
+  EXPECT_EQ(Offliner::PRERENDERING_NOT_STARTED, last_offlining_status());
+}
+
+// This tests a StopProcessing call before we have actually started the
+// prerenderer.
+TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingImmediately) {
+  // Add a request to the queue, wait for callbacks to finish.
+  AddRequest1();
+  PumpLoop();
+
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+  EXPECT_TRUE(is_starting());
+
+  // Now, quick, before it can do much (we haven't called PumpLoop), cancel it.
+  coordinator()->StopProcessing(Offliner::REQUEST_COORDINATOR_CANCELED);
+
+  // Let the async callbacks in the request coordinator run.
+  PumpLoop();
+  EXPECT_TRUE(immediate_schedule_callback_called());
+
+  EXPECT_FALSE(is_starting());
+
+  // OfflinerDoneCallback will not end up getting called with status SAVED,
+  // since we cancelled the event before it called offliner_->LoadAndSave().
+  EXPECT_EQ(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED,
+            last_offlining_status());
+
+  // Since offliner was not started, it will not have seen cancel call.
+  EXPECT_FALSE(OfflinerWasCanceled());
+}
+
+// This tests a StopProcessing call after the prerenderer has been started.
+TEST_F(RequestCoordinatorTest, StartProcessingThenStopProcessingLater) {
+  // Add a request to the queue, wait for callbacks to finish.
+  AddRequest1();
+  PumpLoop();
+
+  // Ensure the start processing request stops before the completion callback.
+  EnableOfflinerCallback(false);
+
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+  EXPECT_TRUE(is_starting());
+
+  // Let all the async parts of the start processing pipeline run to completion.
+  PumpLoop();
+
+  // Observer called for starting processing.
+  EXPECT_TRUE(observer().changed_called());
+  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
+  observer().Clear();
+
+  // Since the offliner is disabled, this callback should not be called.
+  EXPECT_FALSE(immediate_schedule_callback_called());
+
+  // Coordinator should now be busy.
+  EXPECT_TRUE(is_busy());
+  EXPECT_FALSE(is_starting());
+
+  // Now we cancel it while the prerenderer is busy.
+  coordinator()->StopProcessing(Offliner::REQUEST_COORDINATOR_CANCELED);
+
+  // Let the async callbacks in the cancel run.
+  PumpLoop();
+
+  // Observer called for stopped processing.
+  EXPECT_TRUE(observer().changed_called());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
+  observer().Clear();
+
+  EXPECT_FALSE(is_busy());
+
+  // OfflinerDoneCallback will not end up getting called with status SAVED,
+  // since we cancelled the event before the LoadAndSave completed.
+  EXPECT_EQ(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED,
+            last_offlining_status());
+
+  // Since offliner was started, it will have seen cancel call.
+  EXPECT_TRUE(OfflinerWasCanceled());
+}
+
+// This tests that canceling a request will result in TryNextRequest() getting
+// called.
+TEST_F(RequestCoordinatorTest, RemoveInflightRequest) {
+  // Add a request to the queue, wait for callbacks to finish.
+  AddRequest1();
+  PumpLoop();
+
+  // Ensure the start processing request stops before the completion callback.
+  EnableOfflinerCallback(false);
+
+  EXPECT_TRUE(coordinator()->StartProcessing(device_conditions(),
+                                             immediate_callback()));
+
+  // Let all the async parts of the start processing pipeline run to completion.
+  PumpLoop();
+  // Since the offliner is disabled, this callback should not be called.
+  EXPECT_FALSE(immediate_schedule_callback_called());
+
+  // Remove the request while it is processing.
+  std::vector<int64_t> request_ids{kRequestId1};
+  coordinator()->RemoveRequests(
+      request_ids, base::Bind(&RequestCoordinatorTest::RemoveRequestsDone,
+                              base::Unretained(this)));
+
+  // Let the async callbacks in the cancel run.
+  PumpLoop();
+
+  // Since offliner was started, it will have seen cancel call.
+  EXPECT_TRUE(OfflinerWasCanceled());
+}
+
+TEST_F(RequestCoordinatorTest, MarkRequestCompleted) {
+  int64_t request_id = coordinator()->SavePageLater(
+      kUrl1, kClientId1, kUserRequested,
+      RequestCoordinator::RequestAvailability::DISABLED_FOR_OFFLINER);
+  PumpLoop();
+  EXPECT_NE(request_id, 0l);
+
+  // Verify request added in OFFLINING state.
+  EXPECT_TRUE(observer().added_called());
+  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
+
+  // Call the method under test, making sure we send SUCCESS to the observer.
+  coordinator()->MarkRequestCompleted(request_id);
+  PumpLoop();
+
+  // Our observer should have seen SUCCESS instead of REMOVED.
+  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::SUCCESS,
+            observer().last_status());
+  EXPECT_TRUE(observer().completed_called());
+}
+
+TEST_F(RequestCoordinatorTest, EnableForOffliner) {
+  // Pretend we are on low-end device so immediate start won't happen.
+  SetIsLowEndDeviceForTest(true);
+
+  int64_t request_id = coordinator()->SavePageLater(
+      kUrl1, kClientId1, kUserRequested,
+      RequestCoordinator::RequestAvailability::DISABLED_FOR_OFFLINER);
+  PumpLoop();
+  EXPECT_NE(request_id, 0l);
+
+  // Verify request added and initial change to OFFLINING (in foreground).
+  EXPECT_TRUE(observer().added_called());
+  EXPECT_TRUE(observer().changed_called());
+  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
+  observer().Clear();
+
+  // Ensure that the new request does not finish so we can verify state change.
+  EnableOfflinerCallback(false);
+
+  coordinator()->EnableForOffliner(request_id, kClientId1);
+  PumpLoop();
+
+  // Verify request changed again.
+  EXPECT_TRUE(observer().changed_called());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
+}
+
+TEST_F(RequestCoordinatorTest, WatchdogTimeoutForScheduledProcessing) {
+  // Build a request to use with the pre-renderer, and put it on the queue.
+  offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
+                                         base::Time::Now(), kUserRequested);
+  // Set request to allow one more completed attempt.
+  int max_tries = coordinator()->policy()->GetMaxCompletedTries();
+  request.set_completed_attempt_count(max_tries - 1);
+  coordinator()->queue()->AddRequest(
+      request, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                          base::Unretained(this)));
+  PumpLoop();
+
+  // Ensure that the new request does not finish - we simulate it being
+  // in progress by asking it to skip making the completion callback.
+  EnableOfflinerCallback(false);
+
+  // Sending the request to the offliner.
+  EXPECT_TRUE(
+      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+  PumpLoop();
+
+  // Advance the mock clock far enough to cause a watchdog timeout
+  AdvanceClockBy(base::TimeDelta::FromSeconds(
+      coordinator()
+          ->policy()
+          ->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds() +
+      1));
+  PumpLoop();
+
+  // Wait for timeout to expire.  Use a TaskRunner with a DelayedTaskRunner
+  // which won't time out immediately, so the watchdog thread doesn't kill valid
+  // tasks too soon.
+  WaitForCallback();
+  PumpLoop();
+
+  EXPECT_FALSE(is_starting());
+  EXPECT_FALSE(coordinator()->is_busy());
+  EXPECT_TRUE(OfflinerWasCanceled());
+}
+
+TEST_F(RequestCoordinatorTest, WatchdogTimeoutForImmediateProcessing) {
+  // If low end device, pretend it is not so that immediate start happens.
+  SetIsLowEndDeviceForTest(false);
+
+  // Ensure that the new request does not finish - we simulate it being
+  // in progress by asking it to skip making the completion callback.
+  EnableOfflinerCallback(false);
+
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+  PumpLoop();
+
+  // Verify that immediate start from adding the request did happen.
+  EXPECT_TRUE(coordinator()->is_busy());
+
+  // Advance the mock clock 1 second before the watchdog timeout.
+  AdvanceClockBy(base::TimeDelta::FromSeconds(
+      coordinator()
+          ->policy()
+          ->GetSinglePageTimeLimitForImmediateLoadInSeconds() -
+      1));
+  PumpLoop();
+
+  // Verify still busy.
+  EXPECT_TRUE(coordinator()->is_busy());
+  EXPECT_FALSE(OfflinerWasCanceled());
+
+  // Advance the mock clock past the watchdog timeout now.
+  AdvanceClockBy(base::TimeDelta::FromSeconds(2));
+  PumpLoop();
+
+  // Verify the request timed out.
+  EXPECT_TRUE(OfflinerWasCanceled());
+}
+
+TEST_F(RequestCoordinatorTest, TimeBudgetExceeded) {
+  EnableOfflinerCallback(false);
+  // Build two requests to use with the pre-renderer, and put it on the queue.
+  AddRequest1();
+  // The second request will have a larger completed attempt count.
+  offline_pages::SavePageRequest request2(kRequestId1 + 1, kUrl1, kClientId1,
+                                          base::Time::Now(), kUserRequested);
+  request2.set_completed_attempt_count(kAttemptCount);
+  coordinator()->queue()->AddRequest(
+      request2, base::Bind(&RequestCoordinatorTest::AddRequestDone,
+                           base::Unretained(this)));
+  PumpLoop();
+
+  // Sending the request to the offliner.
+  EXPECT_TRUE(
+      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+  PumpLoop();
+
+  // Advance the mock clock far enough to exceed our time budget.
+  // The first request will time out, and because we are over time budget,
+  // the second request will not be started.
+  AdvanceClockBy(base::TimeDelta::FromSeconds(kTestTimeBudgetSeconds));
+  PumpLoop();
+
+  // TryNextRequest should decide that there is no more work to be done,
+  // and call back to the scheduler, even though there is another request in the
+  // queue.  Both requests should be left in the queue.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // We should find two requests in the queue.
+  // The first request should now have a completed count of 1.
+  EXPECT_EQ(2UL, last_requests().size());
+  EXPECT_EQ(1L, last_requests().at(0)->completed_attempt_count());
+}
+
+TEST_F(RequestCoordinatorTest, TryNextRequestWithNoNetwork) {
+  SavePageRequest request1 = AddRequest1();
+  AddRequest2();
+  PumpLoop();
+
+  // Set up for the call to StartProcessing.
+  EnableOfflinerCallback(false);
+
+  // Sending the request to the offliner.
+  EXPECT_TRUE(
+      coordinator()->StartProcessing(device_conditions(), waiting_callback()));
+  PumpLoop();
+  EXPECT_TRUE(coordinator()->is_busy());
+
+  // Now lose the network connection.
+  SetNetworkConnected(false);
+
+  // Complete first request and then TryNextRequest should decide not
+  // to pick another request (because of no network connection).
+  SendOfflinerDoneCallback(request1, Offliner::RequestStatus::SAVED);
+  PumpLoop();
+
+  // Not starting nor busy with next request.
+  EXPECT_FALSE(coordinator()->is_starting());
+  EXPECT_FALSE(coordinator()->is_busy());
+
+  // Get queued requests.
+  coordinator()->queue()->GetRequests(base::Bind(
+      &RequestCoordinatorTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // We should find one request in the queue.
+  EXPECT_EQ(1UL, last_requests().size());
+}
+
+TEST_F(RequestCoordinatorTest, GetAllRequests) {
+  // Add two requests to the queue.
+  AddRequest1();
+  AddRequest2();
+  PumpLoop();
+
+  // Start the async status fetching.
+  coordinator()->GetAllRequests(base::Bind(
+      &RequestCoordinatorTest::GetQueuedRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Wait for async get to finish.
+  WaitForCallback();
+  PumpLoop();
+
+  // Check that the statuses found in the callback match what we expect.
+  EXPECT_EQ(2UL, last_requests().size());
+  EXPECT_EQ(kRequestId1, last_requests().at(0)->request_id());
+  EXPECT_EQ(kRequestId2, last_requests().at(1)->request_id());
+}
+
+#if defined(OS_IOS)
+// Flaky on IOS. http://crbug/663311
+#define MAYBE_PauseAndResumeObserver DISABLED_PauseAndResumeObserver
+#else
+#define MAYBE_PauseAndResumeObserver PauseAndResumeObserver
+#endif
+TEST_F(RequestCoordinatorTest, MAYBE_PauseAndResumeObserver) {
+  // Add a request to the queue.
+  AddRequest1();
+  PumpLoop();
+
+  // Pause the request.
+  std::vector<int64_t> request_ids;
+  request_ids.push_back(kRequestId1);
+  coordinator()->PauseRequests(request_ids);
+  PumpLoop();
+
+  EXPECT_TRUE(observer().changed_called());
+  EXPECT_EQ(SavePageRequest::RequestState::PAUSED, observer().state());
+
+  // Clear out the observer before the next call.
+  observer().Clear();
+
+  // Resume the request.
+  coordinator()->ResumeRequests(request_ids);
+  PumpLoop();
+
+  EXPECT_TRUE(observer().changed_called());
+
+  // Now whether request is offlining or just available depends on whether test
+  // is run on svelte device or not.
+  if (base::SysInfo::IsLowEndDevice()) {
+    EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE, observer().state());
+  } else {
+    EXPECT_EQ(SavePageRequest::RequestState::OFFLINING, observer().state());
+  }
+}
+
+TEST_F(RequestCoordinatorTest, RemoveRequest) {
+  // Add a request to the queue.
+  AddRequest1();
+  PumpLoop();
+
+  // Remove the request.
+  std::vector<int64_t> request_ids;
+  request_ids.push_back(kRequestId1);
+  coordinator()->RemoveRequests(
+      request_ids, base::Bind(&RequestCoordinatorTest::RemoveRequestsDone,
+                              base::Unretained(this)));
+
+  PumpLoop();
+  WaitForCallback();
+  PumpLoop();
+
+  EXPECT_TRUE(observer().completed_called());
+  EXPECT_EQ(RequestCoordinator::BackgroundSavePageResult::REMOVED,
+            observer().last_status());
+  EXPECT_EQ(1UL, last_remove_results().size());
+  EXPECT_EQ(kRequestId1, std::get<0>(last_remove_results().at(0)));
+}
+
+TEST_F(RequestCoordinatorTest,
+       SavePageStartsProcessingWhenConnectedAndNotLowEndDevice) {
+  // Turn off the callback so that the request stops before processing in
+  // PumpLoop.
+  EnableOfflinerCallback(false);
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+  PumpLoop();
+
+  // Now whether processing triggered immediately depends on whether test
+  // is run on svelte device or not.
+  if (base::SysInfo::IsLowEndDevice()) {
+    EXPECT_FALSE(is_busy());
+  } else {
+    EXPECT_TRUE(is_busy());
+  }
+}
+
+TEST_F(RequestCoordinatorTest,
+       SavePageStartsProcessingWhenConnectedOnLowEndDeviceIfFlagEnabled) {
+  // Mark device as low-end device.
+  SetIsLowEndDeviceForTest(true);
+  EXPECT_FALSE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
+
+  // Make a request.
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+  PumpLoop();
+
+  // Verify not immediately busy (since low-end device).
+  EXPECT_FALSE(is_busy());
+
+  // Set feature flag to allow concurrent loads.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kOfflinePagesSvelteConcurrentLoadingFeature);
+  EXPECT_TRUE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
+
+  // Turn off the callback so that the request stops before processing in
+  // PumpLoop.
+  EnableOfflinerCallback(false);
+
+  // Make another request.
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl2, kClientId2, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+  PumpLoop();
+
+  // Verify immediate processing did start this time.
+  EXPECT_TRUE(is_busy());
+}
+
+TEST_F(RequestCoordinatorTest, SavePageDoesntStartProcessingWhenDisconnected) {
+  // If low end device, pretend it is not so that immediate start allowed.
+  SetIsLowEndDeviceForTest(false);
+
+  SetNetworkConnected(false);
+  EnableOfflinerCallback(false);
+  EXPECT_NE(
+      coordinator()->SavePageLater(
+          kUrl1, kClientId1, kUserRequested,
+          RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER), 0);
+  PumpLoop();
+  EXPECT_FALSE(is_busy());
+
+  // Now connect network and verify processing starts.
+  SetNetworkConnected(true);
+  CallConnectionTypeObserver();
+  PumpLoop();
+  EXPECT_TRUE(is_busy());
+}
+
+TEST_F(RequestCoordinatorTest,
+       SavePageDoesStartProcessingWhenPoorlyConnected) {
+  // Set specific network type for 2G with poor effective connection.
+  SetNetworkConditionsForTest(
+      net::NetworkChangeNotifier::ConnectionType::CONNECTION_2G);
+  SetEffectiveConnectionTypeForTest(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+
+  // Turn off the callback so that the request stops before processing in
+  // PumpLoop.
+  EnableOfflinerCallback(false);
+
+  EXPECT_NE(coordinator()->SavePageLater(
+                kUrl1, kClientId1, kUserRequested,
+                RequestCoordinator::RequestAvailability::ENABLED_FOR_OFFLINER),
+            0);
+  PumpLoop();
+  EXPECT_TRUE(is_busy());
+}
+
+TEST_F(RequestCoordinatorTest,
+       ResumeStartsProcessingWhenConnectedAndNotLowEndDevice) {
+  // Start unconnected.
+  SetNetworkConnected(false);
+
+  // Turn off the callback so that the request stops before processing in
+  // PumpLoop.
+  EnableOfflinerCallback(false);
+
+  // Add a request to the queue.
+  AddRequest1();
+  PumpLoop();
+  EXPECT_FALSE(is_busy());
+
+  // Pause the request.
+  std::vector<int64_t> request_ids;
+  request_ids.push_back(kRequestId1);
+  coordinator()->PauseRequests(request_ids);
+  PumpLoop();
+
+  // Resume the request while disconnected.
+  coordinator()->ResumeRequests(request_ids);
+  PumpLoop();
+  EXPECT_FALSE(is_busy());
+
+  // Pause the request again.
+  coordinator()->PauseRequests(request_ids);
+  PumpLoop();
+
+  // Now simulate reasonable connection.
+  SetNetworkConnected(true);
+
+  // Resume the request while connected.
+  coordinator()->ResumeRequests(request_ids);
+  EXPECT_FALSE(is_busy());
+  PumpLoop();
+
+  // Now whether processing triggered immediately depends on whether test
+  // is run on svelte device or not.
+  if (base::SysInfo::IsLowEndDevice()) {
+    EXPECT_FALSE(is_busy());
+  } else {
+    EXPECT_TRUE(is_busy());
+  }
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_notifier.h b/components/offline_pages/core/background/request_notifier.h
new file mode 100644
index 0000000..ff721845
--- /dev/null
+++ b/components/offline_pages/core/background/request_notifier.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
+
+namespace offline_pages {
+
+class SavePageRequest;
+
+class RequestNotifier {
+ public:
+  // Status to return for failed notifications.
+  // NOTE: for any changes to the enum, please also update related switch code
+  // in RequestCoordinatorEventLogger.
+  // GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages
+  enum class BackgroundSavePageResult {
+    SUCCESS,
+    PRERENDER_FAILURE,
+    PRERENDER_CANCELED,
+    FOREGROUND_CANCELED,
+    SAVE_FAILED,
+    EXPIRED,
+    RETRY_COUNT_EXCEEDED,
+    START_COUNT_EXCEEDED,
+    REMOVED,
+  };
+
+  virtual ~RequestNotifier() = default;
+
+  // Notifies observers that |request| has been added.
+  virtual void NotifyAdded(const SavePageRequest& request) = 0;
+
+  // Notifies observers that |request| has been completed with |status|.
+  virtual void NotifyCompleted(const SavePageRequest& request,
+                               BackgroundSavePageResult status) = 0;
+
+  // Notifies observers that |request| has been changed.
+  virtual void NotifyChanged(const SavePageRequest& request) = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_NOTIFIER_H_
diff --git a/components/offline_pages/core/background/request_queue.cc b/components/offline_pages/core/background/request_queue.cc
new file mode 100644
index 0000000..77580356
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue.cc
@@ -0,0 +1,161 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_queue.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/add_request_task.h"
+#include "components/offline_pages/core/background/change_requests_state_task.h"
+#include "components/offline_pages/core/background/get_requests_task.h"
+#include "components/offline_pages/core/background/initialize_store_task.h"
+#include "components/offline_pages/core/background/mark_attempt_aborted_task.h"
+#include "components/offline_pages/core/background/mark_attempt_completed_task.h"
+#include "components/offline_pages/core/background/mark_attempt_started_task.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
+#include "components/offline_pages/core/background/remove_requests_task.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+namespace {
+// Completes the get requests call.
+void GetRequestsDone(const RequestQueue::GetRequestsCallback& callback,
+                     bool success,
+                     std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  GetRequestsResult result =
+      success ? GetRequestsResult::SUCCESS : GetRequestsResult::STORE_FAILURE;
+  // TODO(fgorski): Filter out expired requests based on policy.
+  // This may trigger the purging if necessary.
+  // Also this may be turned into a method on the request queue or add a policy
+  // parameter in the process.
+  callback.Run(result, std::move(requests));
+}
+
+// Completes the add request call.
+void AddRequestDone(const RequestQueue::AddRequestCallback& callback,
+                    const SavePageRequest& request,
+                    ItemActionStatus status) {
+  AddRequestResult result;
+  switch (status) {
+    case ItemActionStatus::SUCCESS:
+      result = AddRequestResult::SUCCESS;
+      break;
+    case ItemActionStatus::ALREADY_EXISTS:
+      result = AddRequestResult::ALREADY_EXISTS;
+      break;
+    case ItemActionStatus::STORE_ERROR:
+      result = AddRequestResult::STORE_FAILURE;
+      break;
+    case ItemActionStatus::NOT_FOUND:
+    default:
+      NOTREACHED();
+      return;
+  }
+  callback.Run(result, request);
+}
+
+}  // namespace
+
+RequestQueue::RequestQueue(std::unique_ptr<RequestQueueStore> store)
+    : store_(std::move(store)), weak_ptr_factory_(this) {
+  Initialize();
+}
+
+RequestQueue::~RequestQueue() {}
+
+void RequestQueue::GetRequests(const GetRequestsCallback& callback) {
+  std::unique_ptr<Task> task(new GetRequestsTask(
+      store_.get(), base::Bind(&GetRequestsDone, callback)));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::AddRequest(const SavePageRequest& request,
+                              const AddRequestCallback& callback) {
+  // TODO(fgorski): check that request makes sense.
+  // TODO(fgorski): check that request does not violate policy.
+  std::unique_ptr<AddRequestTask> task(new AddRequestTask(
+      store_.get(), request, base::Bind(&AddRequestDone, callback, request)));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::RemoveRequests(const std::vector<int64_t>& request_ids,
+                                  const UpdateCallback& callback) {
+  std::unique_ptr<Task> task(
+      new RemoveRequestsTask(store_.get(), request_ids, callback));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::ChangeRequestsState(
+    const std::vector<int64_t>& request_ids,
+    const SavePageRequest::RequestState new_state,
+    const RequestQueue::UpdateCallback& callback) {
+  std::unique_ptr<Task> task(new ChangeRequestsStateTask(
+      store_.get(), request_ids, new_state, callback));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::MarkAttemptStarted(int64_t request_id,
+                                      const UpdateCallback& callback) {
+  std::unique_ptr<Task> task(
+      new MarkAttemptStartedTask(store_.get(), request_id, callback));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::MarkAttemptAborted(int64_t request_id,
+                                      const UpdateCallback& callback) {
+  std::unique_ptr<Task> task(
+      new MarkAttemptAbortedTask(store_.get(), request_id, callback));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::MarkAttemptCompleted(int64_t request_id,
+                                        const UpdateCallback& callback) {
+  std::unique_ptr<Task> task(
+      new MarkAttemptCompletedTask(store_.get(), request_id, callback));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::PickNextRequest(
+    OfflinerPolicy* policy,
+    PickRequestTask::RequestPickedCallback picked_callback,
+    PickRequestTask::RequestNotPickedCallback not_picked_callback,
+    PickRequestTask::RequestCountCallback request_count_callback,
+    DeviceConditions& conditions,
+    std::set<int64_t>& disabled_requests) {
+  // Using the PickerContext, create a picker task.
+  std::unique_ptr<Task> task(new PickRequestTask(
+      store_.get(), policy, picked_callback, not_picked_callback,
+      request_count_callback, conditions, disabled_requests));
+
+  // Queue up the picking task, it will call one of the callbacks when it
+  // completes.
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::CleanupRequestQueue() {
+  // Create a cleanup task.
+  std::unique_ptr<Task> task(cleanup_factory_->CreateCleanupTask(store_.get()));
+
+  // Queue up the cleanup task.
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::Initialize() {
+  std::unique_ptr<Task> task(new InitializeStoreTask(
+      store_.get(), base::Bind(&RequestQueue::InitializeStoreDone,
+                               weak_ptr_factory_.GetWeakPtr())));
+  task_queue_.AddTask(std::move(task));
+}
+
+void RequestQueue::InitializeStoreDone(bool success) {
+  // TODO(fgorski): Result can be ignored for now. Report UMA in future.
+  // No need to pass the result up to RequestCoordinator.
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_queue.h b/components/offline_pages/core/background/request_queue.h
new file mode 100644
index 0000000..caa1134
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue.h
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/cleanup_task_factory.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/pick_request_task.h"
+#include "components/offline_pages/core/background/request_queue_results.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_store_types.h"
+#include "components/offline_pages/core/task_queue.h"
+
+namespace offline_pages {
+
+class CleanupTaskFactory;
+class RequestQueueStore;
+
+// Class responsible for managing save page requests.
+class RequestQueue {
+ public:
+  // Callback used for |GetRequests|.
+  typedef base::Callback<void(GetRequestsResult,
+                              std::vector<std::unique_ptr<SavePageRequest>>)>
+      GetRequestsCallback;
+
+  // Callback used for |AddRequest|.
+  typedef base::Callback<void(AddRequestResult, const SavePageRequest& request)>
+      AddRequestCallback;
+
+  // Callback used by |ChangeRequestsState|.
+  typedef base::Callback<void(std::unique_ptr<UpdateRequestsResult>)>
+      UpdateCallback;
+
+  // Callback used by |UdpateRequest|.
+  typedef base::Callback<void(UpdateRequestResult)> UpdateRequestCallback;
+
+  explicit RequestQueue(std::unique_ptr<RequestQueueStore> store);
+  ~RequestQueue();
+
+  // Gets all of the active requests from the store. Calling this method may
+  // schedule purging of the request queue.
+  void GetRequests(const GetRequestsCallback& callback);
+
+  // Adds |request| to the request queue. Result is returned through |callback|.
+  // In case adding the request violates policy, the result will fail with
+  // appropriate result. Callback will also return a copy of a request with all
+  // fields set.
+  void AddRequest(const SavePageRequest& request,
+                  const AddRequestCallback& callback);
+
+  // Removes the requests matching the |request_ids|. Result is returned through
+  // |callback|.  If a request id cannot be removed, this will still remove the
+  // others.
+  void RemoveRequests(const std::vector<int64_t>& request_ids,
+                      const UpdateCallback& callback);
+
+  // Changes the state to |new_state| for requests matching the
+  // |request_ids|. Results are returned through |callback|.
+  void ChangeRequestsState(const std::vector<int64_t>& request_ids,
+                           const SavePageRequest::RequestState new_state,
+                           const UpdateCallback& callback);
+
+  // Marks attempt with |request_id| as started. Results are returned through
+  // |callback|.
+  void MarkAttemptStarted(int64_t request_id, const UpdateCallback& callback);
+
+  // Marks attempt with |request_id| as aborted. Results are returned through
+  // |callback|.
+  void MarkAttemptAborted(int64_t request_id, const UpdateCallback& callback);
+
+  // Marks attempt with |request_id| as completed. The attempt may have
+  // completed with either success or failure (not denoted here). Results
+  // are returned through |callback|.
+  void MarkAttemptCompleted(int64_t request_id, const UpdateCallback& callback);
+
+  // Make a task to pick the next request, and report our choice to the
+  // callbacks.
+  void PickNextRequest(
+      OfflinerPolicy* policy,
+      PickRequestTask::RequestPickedCallback picked_callback,
+      PickRequestTask::RequestNotPickedCallback not_picked_callback,
+      PickRequestTask::RequestCountCallback request_count_callback,
+      DeviceConditions& conditions,
+      std::set<int64_t>& disabled_requests);
+
+  // Cleanup requests that have expired, exceeded the start or completed retry
+  // limit.
+  void CleanupRequestQueue();
+
+  // Takes ownership of the factory.  We use a setter to allow users of the
+  // request queue to not need a CleanupFactory to create it, since we have lots
+  // of code using the request queue.  The request coordinator will set a
+  // factory before calling CleanupRequestQueue.
+  void SetCleanupFactory(std::unique_ptr<CleanupTaskFactory> factory) {
+    cleanup_factory_ = std::move(factory);
+  }
+
+ private:
+  // Store initialization functions.
+  void Initialize();
+  void InitializeStoreDone(bool success);
+
+  std::unique_ptr<RequestQueueStore> store_;
+
+  // Task queue to serialize store access.
+  TaskQueue task_queue_;
+
+  // Builds CleanupTask objects.
+  std::unique_ptr<CleanupTaskFactory> cleanup_factory_;
+
+  // Allows us to pass a weak pointer to callbacks.
+  base::WeakPtrFactory<RequestQueue> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestQueue);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_H_
diff --git a/components/offline_pages/core/background/request_queue_in_memory_store.cc b/components/offline_pages/core/background/request_queue_in_memory_store.cc
new file mode 100644
index 0000000..69e06b8
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_in_memory_store.cc
@@ -0,0 +1,158 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+
+#include <unordered_set>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+RequestQueueInMemoryStore::RequestQueueInMemoryStore()
+    : state_(StoreState::NOT_LOADED), scenario_(TestScenario::SUCCESSFUL) {}
+
+RequestQueueInMemoryStore::RequestQueueInMemoryStore(TestScenario scenario)
+    : state_(StoreState::NOT_LOADED), scenario_(scenario) {}
+
+RequestQueueInMemoryStore::~RequestQueueInMemoryStore() {}
+
+void RequestQueueInMemoryStore::Initialize(const InitializeCallback& callback) {
+  if (scenario_ == TestScenario::SUCCESSFUL)
+    state_ = StoreState::LOADED;
+  else
+    state_ = StoreState::FAILED_LOADING;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, state_ == StoreState::LOADED));
+}
+
+void RequestQueueInMemoryStore::GetRequests(
+    const GetRequestsCallback& callback) {
+  DCHECK_NE(state_, StoreState::NOT_LOADED);
+  std::vector<std::unique_ptr<SavePageRequest>> result_requests;
+  for (const auto& id_request_pair : requests_) {
+    std::unique_ptr<SavePageRequest> request(
+        new SavePageRequest(id_request_pair.second));
+    result_requests.push_back(std::move(request));
+  }
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(callback, true, base::Passed(std::move(result_requests))));
+}
+
+void RequestQueueInMemoryStore::GetRequestsByIds(
+    const std::vector<int64_t>& request_ids,
+    const UpdateCallback& callback) {
+  DCHECK_NE(state_, StoreState::NOT_LOADED);
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(state()));
+
+  ItemActionStatus status;
+  // Make sure not to include the same request multiple times, while preserving
+  // the order of non-duplicated IDs in the result.
+  std::unordered_set<int64_t> processed_ids;
+  for (const auto& request_id : request_ids) {
+    if (!processed_ids.insert(request_id).second)
+      continue;
+    RequestsMap::iterator iter = requests_.find(request_id);
+    if (iter != requests_.end()) {
+      status = ItemActionStatus::SUCCESS;
+      result->updated_items.push_back(iter->second);
+    } else {
+      status = ItemActionStatus::NOT_FOUND;
+    }
+    result->item_statuses.push_back(std::make_pair(request_id, status));
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void RequestQueueInMemoryStore::AddRequest(const SavePageRequest& request,
+                                           const AddCallback& callback) {
+  DCHECK_NE(state_, StoreState::NOT_LOADED);
+  RequestsMap::iterator iter = requests_.find(request.request_id());
+  ItemActionStatus status;
+  if (iter == requests_.end()) {
+    requests_.insert(iter, std::make_pair(request.request_id(), request));
+    status = ItemActionStatus::SUCCESS;
+  } else {
+    status = ItemActionStatus::ALREADY_EXISTS;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                base::Bind(callback, status));
+}
+
+void RequestQueueInMemoryStore::UpdateRequests(
+    const std::vector<SavePageRequest>& requests,
+    const RequestQueue::UpdateCallback& callback) {
+  DCHECK_NE(state_, StoreState::NOT_LOADED);
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(state()));
+
+  ItemActionStatus status;
+  for (const auto& request : requests) {
+    RequestsMap::iterator iter = requests_.find(request.request_id());
+    if (iter != requests_.end()) {
+      status = ItemActionStatus::SUCCESS;
+      iter->second = request;
+      result->updated_items.push_back(request);
+    } else {
+      status = ItemActionStatus::NOT_FOUND;
+    }
+    result->item_statuses.push_back(
+        std::make_pair(request.request_id(), status));
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void RequestQueueInMemoryStore::RemoveRequests(
+    const std::vector<int64_t>& request_ids,
+    const UpdateCallback& callback) {
+  DCHECK_NE(state_, StoreState::NOT_LOADED);
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(StoreState::LOADED));
+
+  ItemActionStatus status;
+  // If we find a request, mark it as succeeded, and put it in the request list.
+  // Otherwise mark it as failed.
+  for (auto request_id : request_ids) {
+    RequestsMap::iterator iter = requests_.find(request_id);
+    if (iter != requests_.end()) {
+      status = ItemActionStatus::SUCCESS;
+      result->updated_items.push_back(iter->second);
+      requests_.erase(iter);
+    } else {
+      status = ItemActionStatus::NOT_FOUND;
+    }
+    result->item_statuses.push_back(std::make_pair(request_id, status));
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void RequestQueueInMemoryStore::Reset(const ResetCallback& callback) {
+  if (scenario_ != TestScenario::LOAD_FAILED_RESET_FAILED) {
+    requests_.clear();
+    state_ = StoreState::NOT_LOADED;
+    scenario_ = TestScenario::SUCCESSFUL;
+  } else {
+    state_ = StoreState::FAILED_RESET;
+  }
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, state_ == StoreState::NOT_LOADED));
+}
+
+StoreState RequestQueueInMemoryStore::state() const {
+  return state_;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_queue_in_memory_store.h b/components/offline_pages/core/background/request_queue_in_memory_store.h
new file mode 100644
index 0000000..907a6a9
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_in_memory_store.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+
+#include "base/macros.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+// Interface for classes storing save page requests.
+class RequestQueueInMemoryStore : public RequestQueueStore {
+ public:
+  enum class TestScenario {
+    SUCCESSFUL,
+    LOAD_FAILED_RESET_SUCCESS,
+    LOAD_FAILED_RESET_FAILED,
+  };
+
+  RequestQueueInMemoryStore();
+  explicit RequestQueueInMemoryStore(TestScenario scenario);
+  ~RequestQueueInMemoryStore() override;
+
+  // RequestQueueStore implementaiton.
+  void Initialize(const InitializeCallback& callback) override;
+  void GetRequests(const GetRequestsCallback& callback) override;
+  void GetRequestsByIds(const std::vector<int64_t>& request_ids,
+                        const UpdateCallback& callback) override;
+  void AddRequest(const SavePageRequest& offline_page,
+                  const AddCallback& callback) override;
+  void UpdateRequests(const std::vector<SavePageRequest>& requests,
+                      const UpdateCallback& callback) override;
+  void RemoveRequests(const std::vector<int64_t>& request_ids,
+                      const UpdateCallback& callback) override;
+  void Reset(const ResetCallback& callback) override;
+  StoreState state() const override;
+
+ private:
+  typedef std::map<int64_t, SavePageRequest> RequestsMap;
+  RequestsMap requests_;
+  StoreState state_;
+  TestScenario scenario_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestQueueInMemoryStore);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_IN_MEMORY_STORE_H_
diff --git a/components/offline_pages/core/background/request_queue_results.h b/components/offline_pages/core/background/request_queue_results.h
new file mode 100644
index 0000000..5468fd2e
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_results.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
+#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
+
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_store_types.h"
+
+namespace offline_pages {
+
+// Extracted from RequestQueue so that we can build types that use these results
+// that RequestQueue depends on (for example, the PickRequestTask).
+typedef StoreUpdateResult<SavePageRequest> UpdateRequestsResult;
+
+enum class GetRequestsResult {
+  SUCCESS,
+  STORE_FAILURE,
+};
+
+enum class AddRequestResult {
+  SUCCESS,
+  STORE_FAILURE,
+  ALREADY_EXISTS,
+  REQUEST_QUOTA_HIT,  // Cannot add a request with this namespace, as it has
+                      // reached a quota of active requests.
+};
+
+// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages.background
+enum class UpdateRequestResult {
+  SUCCESS,
+  STORE_FAILURE,
+  REQUEST_DOES_NOT_EXIST,  // Failed to delete the request because it does not
+                           // exist.
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_QUEUE_RESULTS_H_
diff --git a/components/offline_pages/core/background/request_queue_store.h b/components/offline_pages/core/background/request_queue_store.h
new file mode 100644
index 0000000..5bf27385
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_store.h
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "base/callback.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_store_types.h"
+
+namespace offline_pages {
+
+// Interface for classes storing save page requests.
+class RequestQueueStore {
+ public:
+  enum class UpdateStatus {
+    ADDED,    // Request was added successfully.
+    UPDATED,  // Request was updated successfully.
+    FAILED,   // Add or update attempt failed.
+  };
+
+  using UpdateCallback = RequestQueue::UpdateCallback;
+
+  typedef base::Callback<void(bool /* success */)> InitializeCallback;
+  typedef base::Callback<void(bool /* success */)> ResetCallback;
+  typedef base::Callback<void(
+      bool /* success */,
+      std::vector<std::unique_ptr<SavePageRequest>> /* requests */)>
+      GetRequestsCallback;
+  typedef base::Callback<void(ItemActionStatus)> AddCallback;
+
+  virtual ~RequestQueueStore(){};
+
+  // Initializes the store. Should be called before any other methods.
+  virtual void Initialize(const InitializeCallback& callback) = 0;
+
+  // Gets all of the requests from the store.
+  virtual void GetRequests(const GetRequestsCallback& callback) = 0;
+
+  // Gets requests with specified IDs from the store. UpdateCallback is used
+  // instead of GetRequestsCallback to indicate which requests where not found.
+  virtual void GetRequestsByIds(const std::vector<int64_t>& request_ids,
+                                const UpdateCallback& callback) = 0;
+
+  // Asynchronously adds request in store. Fails if request with the same
+  // offline ID already exists.
+  virtual void AddRequest(const SavePageRequest& offline_page,
+                          const AddCallback& callback) = 0;
+
+  // Asynchronously updates requests in store.
+  virtual void UpdateRequests(const std::vector<SavePageRequest>& requests,
+                              const UpdateCallback& callback) = 0;
+
+  // Asynchronously removes requests from the store using their IDs.
+  // Result of the update, and a number of removed pages is passed in the
+  // callback.
+  // Result of remove should be false, when one of the provided items couldn't
+  // be deleted, e.g. because it was missing.
+  virtual void RemoveRequests(const std::vector<int64_t>& request_ids,
+                              const UpdateCallback& callback) = 0;
+
+  // Resets the store (removes any existing data).
+  virtual void Reset(const ResetCallback& callback) = 0;
+
+  // Gets the store state.
+  virtual StoreState state() const = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_H_
diff --git a/components/offline_pages/core/background/request_queue_store_sql.cc b/components/offline_pages/core/background/request_queue_store_sql.cc
new file mode 100644
index 0000000..5e5672a
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_store_sql.cc
@@ -0,0 +1,517 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
+
+#include <unordered_set>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace offline_pages {
+
+template class StoreUpdateResult<SavePageRequest>;
+
+namespace {
+
+using SuccessCallback = base::Callback<void(bool)>;
+
+// This is a macro instead of a const so that
+// it can be used inline in other SQL statements below.
+#define REQUEST_QUEUE_TABLE_NAME "request_queue_v1"
+const bool kUserRequested = true;
+
+bool CreateRequestQueueTable(sql::Connection* db) {
+  const char kSql[] = "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME
+                      " (request_id INTEGER PRIMARY KEY NOT NULL,"
+                      " creation_time INTEGER NOT NULL,"
+                      " activation_time INTEGER NOT NULL DEFAULT 0,"
+                      " last_attempt_time INTEGER NOT NULL DEFAULT 0,"
+                      " started_attempt_count INTEGER NOT NULL,"
+                      " completed_attempt_count INTEGER NOT NULL,"
+                      " state INTEGER NOT NULL DEFAULT 0,"
+                      " url VARCHAR NOT NULL,"
+                      " client_namespace VARCHAR NOT NULL,"
+                      " client_id VARCHAR NOT NULL"
+                      ")";
+  return db->Execute(kSql);
+}
+
+bool CreateSchema(sql::Connection* db) {
+  // If there is not already a state column, we need to drop the old table.  We
+  // are choosing to drop instead of upgrade since the feature is not yet
+  // released, so we don't use a transaction to protect existing data, or try to
+  // migrate it.
+  if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "state")) {
+    if (!db->Execute("DROP TABLE IF EXISTS " REQUEST_QUEUE_TABLE_NAME))
+      return false;
+  }
+
+  if (!CreateRequestQueueTable(db))
+    return false;
+
+  // TODO(fgorski): Add indices here.
+  return true;
+}
+
+// Create a save page request from a SQL result.  Expects complete rows with
+// all columns present.  Columns are in order they are defined in select query
+// in |GetOneRequest| method.
+std::unique_ptr<SavePageRequest> MakeSavePageRequest(
+    const sql::Statement& statement) {
+  const int64_t id = statement.ColumnInt64(0);
+  const base::Time creation_time =
+      base::Time::FromInternalValue(statement.ColumnInt64(1));
+  const base::Time activation_time =
+      base::Time::FromInternalValue(statement.ColumnInt64(2));
+  const base::Time last_attempt_time =
+      base::Time::FromInternalValue(statement.ColumnInt64(3));
+  const int64_t started_attempt_count = statement.ColumnInt64(4);
+  const int64_t completed_attempt_count = statement.ColumnInt64(5);
+  const SavePageRequest::RequestState state =
+      static_cast<SavePageRequest::RequestState>(statement.ColumnInt64(6));
+  const GURL url(statement.ColumnString(7));
+  const ClientId client_id(statement.ColumnString(8),
+                           statement.ColumnString(9));
+
+  DVLOG(2) << "making save page request - id " << id << " url " << url
+           << " client_id " << client_id.name_space << "-" << client_id.id
+           << " creation time " << creation_time << " user requested "
+           << kUserRequested;
+
+  std::unique_ptr<SavePageRequest> request(new SavePageRequest(
+      id, url, client_id, creation_time, activation_time, kUserRequested));
+  request->set_last_attempt_time(last_attempt_time);
+  request->set_started_attempt_count(started_attempt_count);
+  request->set_completed_attempt_count(completed_attempt_count);
+  request->set_request_state(state);
+  return request;
+}
+
+// Get a request for a specific id.
+std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
+                                               const int64_t request_id) {
+  const char kSql[] =
+      "SELECT request_id, creation_time, activation_time,"
+      " last_attempt_time, started_attempt_count, completed_attempt_count,"
+      " state, url, client_namespace, client_id"
+      " FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, request_id);
+
+  if (statement.Step())
+    return MakeSavePageRequest(statement);
+  return std::unique_ptr<SavePageRequest>(nullptr);
+}
+
+ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) {
+  const char kSql[] =
+      "DELETE FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, request_id);
+  if (!statement.Run())
+    return ItemActionStatus::STORE_ERROR;
+  else if (db->GetLastChangeCount() == 0)
+    return ItemActionStatus::NOT_FOUND;
+  return ItemActionStatus::SUCCESS;
+}
+
+ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
+  const char kSql[] =
+      "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
+      " (request_id, creation_time, activation_time,"
+      " last_attempt_time, started_attempt_count, completed_attempt_count,"
+      " state, url, client_namespace, client_id)"
+      " VALUES "
+      " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, request.request_id());
+  statement.BindInt64(1, request.creation_time().ToInternalValue());
+  statement.BindInt64(2, request.activation_time().ToInternalValue());
+  statement.BindInt64(3, request.last_attempt_time().ToInternalValue());
+  statement.BindInt64(4, request.started_attempt_count());
+  statement.BindInt64(5, request.completed_attempt_count());
+  statement.BindInt64(6, static_cast<int64_t>(request.request_state()));
+  statement.BindString(7, request.url().spec());
+  statement.BindString(8, request.client_id().name_space);
+  statement.BindString(9, request.client_id().id);
+
+  if (!statement.Run())
+    return ItemActionStatus::STORE_ERROR;
+  if (db->GetLastChangeCount() == 0)
+    return ItemActionStatus::ALREADY_EXISTS;
+  return ItemActionStatus::SUCCESS;
+}
+
+ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
+  const char kSql[] =
+      "UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME
+      " SET creation_time = ?, activation_time = ?, last_attempt_time = ?,"
+      " started_attempt_count = ?, completed_attempt_count = ?, state = ?,"
+      " url = ?, client_namespace = ?, client_id = ?"
+      " WHERE request_id = ?";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, request.creation_time().ToInternalValue());
+  statement.BindInt64(1, request.activation_time().ToInternalValue());
+  statement.BindInt64(2, request.last_attempt_time().ToInternalValue());
+  statement.BindInt64(3, request.started_attempt_count());
+  statement.BindInt64(4, request.completed_attempt_count());
+  statement.BindInt64(5, static_cast<int64_t>(request.request_state()));
+  statement.BindString(6, request.url().spec());
+  statement.BindString(7, request.client_id().name_space);
+  statement.BindString(8, request.client_id().id);
+  statement.BindInt64(9, request.request_id());
+
+  if (!statement.Run())
+    return ItemActionStatus::STORE_ERROR;
+  if (db->GetLastChangeCount() == 0)
+    return ItemActionStatus::NOT_FOUND;
+  return ItemActionStatus::SUCCESS;
+}
+
+void PostStoreUpdateResultForIds(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    StoreState store_state,
+    const std::vector<int64_t>& item_ids,
+    ItemActionStatus action_status,
+    const RequestQueueStore::UpdateCallback& callback) {
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(store_state));
+  for (const auto& item_id : item_ids)
+    result->item_statuses.push_back(std::make_pair(item_id, action_status));
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void PostStoreErrorForAllRequests(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const std::vector<SavePageRequest>& items,
+    const RequestQueueStore::UpdateCallback& callback) {
+  std::vector<int64_t> item_ids;
+  for (const auto& item : items)
+    item_ids.push_back(item.request_id());
+  PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
+                              ItemActionStatus::STORE_ERROR, callback);
+}
+
+void PostStoreErrorForAllIds(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const std::vector<int64_t>& item_ids,
+    const RequestQueueStore::UpdateCallback& callback) {
+  PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
+                              ItemActionStatus::STORE_ERROR, callback);
+}
+
+bool InitDatabase(sql::Connection* db, const base::FilePath& path) {
+  db->set_page_size(4096);
+  db->set_cache_size(500);
+  db->set_histogram_tag("BackgroundRequestQueue");
+  db->set_exclusive_locking();
+
+  base::File::Error err;
+  if (!base::CreateDirectoryAndGetError(path.DirName(), &err))
+    return false;
+  if (!db->Open(path))
+    return false;
+  db->Preload();
+
+  return CreateSchema(db);
+}
+
+void GetRequestsSync(sql::Connection* db,
+                     scoped_refptr<base::SingleThreadTaskRunner> runner,
+                     const RequestQueueStore::GetRequestsCallback& callback) {
+  const char kSql[] =
+      "SELECT request_id, creation_time, activation_time,"
+      " last_attempt_time, started_attempt_count, completed_attempt_count,"
+      " state, url, client_namespace, client_id"
+      " FROM " REQUEST_QUEUE_TABLE_NAME;
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+  std::vector<std::unique_ptr<SavePageRequest>> requests;
+  while (statement.Step())
+    requests.push_back(MakeSavePageRequest(statement));
+
+  runner->PostTask(FROM_HERE, base::Bind(callback, statement.Succeeded(),
+                                         base::Passed(&requests)));
+}
+
+void GetRequestsByIdsSync(sql::Connection* db,
+                          scoped_refptr<base::SingleThreadTaskRunner> runner,
+                          const std::vector<int64_t>& request_ids,
+                          const RequestQueueStore::UpdateCallback& callback) {
+  // TODO(fgorski): Perhaps add metrics here.
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(StoreState::LOADED));
+
+  // If you create a transaction but don't Commit() it is automatically
+  // rolled back by its destructor when it falls out of scope.
+  sql::Transaction transaction(db);
+  if (!transaction.Begin()) {
+    PostStoreErrorForAllIds(runner, request_ids, callback);
+    return;
+  }
+
+  // Make sure not to include the same request multiple times, preserving the
+  // order of non-duplicated IDs in the result.
+  std::unordered_set<int64_t> processed_ids;
+  for (int64_t request_id : request_ids) {
+    if (!processed_ids.insert(request_id).second)
+      continue;
+    std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
+    if (request.get())
+      result->updated_items.push_back(*request);
+    ItemActionStatus status =
+        request.get() ? ItemActionStatus::SUCCESS : ItemActionStatus::NOT_FOUND;
+    result->item_statuses.push_back(std::make_pair(request_id, status));
+  }
+
+  if (!transaction.Commit()) {
+    PostStoreErrorForAllIds(runner, request_ids, callback);
+    return;
+  }
+
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void AddRequestSync(sql::Connection* db,
+                    scoped_refptr<base::SingleThreadTaskRunner> runner,
+                    const SavePageRequest& request,
+                    const RequestQueueStore::AddCallback& callback) {
+  // TODO(fgorski): add UMA metrics here.
+  ItemActionStatus status = Insert(db, request);
+  runner->PostTask(FROM_HERE, base::Bind(callback, status));
+}
+
+void UpdateRequestsSync(sql::Connection* db,
+                        scoped_refptr<base::SingleThreadTaskRunner> runner,
+                        const std::vector<SavePageRequest>& requests,
+                        const RequestQueueStore::UpdateCallback& callback) {
+  // TODO(fgorski): add UMA metrics here.
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(StoreState::LOADED));
+
+  sql::Transaction transaction(db);
+  if (!transaction.Begin()) {
+    PostStoreErrorForAllRequests(runner, requests, callback);
+    return;
+  }
+
+  for (const auto& request : requests) {
+    ItemActionStatus status = Update(db, request);
+    result->item_statuses.push_back(
+        std::make_pair(request.request_id(), status));
+    if (status == ItemActionStatus::SUCCESS)
+      result->updated_items.push_back(request);
+  }
+
+  if (!transaction.Commit()) {
+    PostStoreErrorForAllRequests(runner, requests, callback);
+    return;
+  }
+
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void RemoveRequestsSync(sql::Connection* db,
+                        scoped_refptr<base::SingleThreadTaskRunner> runner,
+                        const std::vector<int64_t>& request_ids,
+                        const RequestQueueStore::UpdateCallback& callback) {
+  // TODO(fgorski): Perhaps add metrics here.
+  std::unique_ptr<UpdateRequestsResult> result(
+      new UpdateRequestsResult(StoreState::LOADED));
+
+  // If you create a transaction but don't Commit() it is automatically
+  // rolled back by its destructor when it falls out of scope.
+  sql::Transaction transaction(db);
+  if (!transaction.Begin()) {
+    PostStoreErrorForAllIds(runner, request_ids, callback);
+    return;
+  }
+
+  // Read the request before we delete it, and if the delete worked, put it on
+  // the queue of requests that got deleted.
+  for (int64_t request_id : request_ids) {
+    std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
+    ItemActionStatus status = DeleteRequestById(db, request_id);
+    result->item_statuses.push_back(std::make_pair(request_id, status));
+    if (status == ItemActionStatus::SUCCESS)
+      result->updated_items.push_back(*request);
+  }
+
+  if (!transaction.Commit()) {
+    PostStoreErrorForAllIds(runner, request_ids, callback);
+    return;
+  }
+
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void OpenConnectionSync(sql::Connection* db,
+                        scoped_refptr<base::SingleThreadTaskRunner> runner,
+                        const base::FilePath& path,
+                        const SuccessCallback& callback) {
+  bool success = InitDatabase(db, path);
+  runner->PostTask(FROM_HERE, base::Bind(callback, success));
+}
+
+void ResetSync(sql::Connection* db,
+               const base::FilePath& db_file_path,
+               scoped_refptr<base::SingleThreadTaskRunner> runner,
+               const SuccessCallback& callback) {
+  // This method deletes the content of the whole store and reinitializes it.
+  bool success = true;
+  if (db) {
+    success = db->Raze();
+    db->Close();
+  }
+  success = base::DeleteFile(db_file_path, true /* recursive */) && success;
+  runner->PostTask(FROM_HERE, base::Bind(callback, success));
+}
+
+}  // anonymous namespace
+
+RequestQueueStoreSQL::RequestQueueStoreSQL(
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+    const base::FilePath& path)
+    : background_task_runner_(std::move(background_task_runner)),
+      db_file_path_(path.AppendASCII("RequestQueue.db")),
+      state_(StoreState::NOT_LOADED),
+      weak_ptr_factory_(this) {}
+
+RequestQueueStoreSQL::~RequestQueueStoreSQL() {
+  if (db_.get())
+    background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
+}
+
+void RequestQueueStoreSQL::Initialize(const InitializeCallback& callback) {
+  DCHECK(!db_);
+  db_.reset(new sql::Connection());
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&OpenConnectionSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), db_file_path_,
+                 base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone,
+                            weak_ptr_factory_.GetWeakPtr(), callback)));
+}
+
+void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) {
+  DCHECK(db_.get());
+  if (!CheckDb()) {
+    std::vector<std::unique_ptr<SavePageRequest>> requests;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, false, base::Passed(&requests)));
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&GetRequestsSync, db_.get(),
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void RequestQueueStoreSQL::GetRequestsByIds(
+    const std::vector<int64_t>& request_ids,
+    const UpdateCallback& callback) {
+  if (!CheckDb()) {
+    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
+                            callback);
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&GetRequestsByIdsSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
+}
+
+void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request,
+                                      const AddCallback& callback) {
+  if (!CheckDb()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, ItemActionStatus::STORE_ERROR));
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&AddRequestSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), request, callback));
+}
+
+void RequestQueueStoreSQL::UpdateRequests(
+    const std::vector<SavePageRequest>& requests,
+    const UpdateCallback& callback) {
+  if (!CheckDb()) {
+    PostStoreErrorForAllRequests(base::ThreadTaskRunnerHandle::Get(), requests,
+                                 callback);
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateRequestsSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), requests, callback));
+}
+
+void RequestQueueStoreSQL::RemoveRequests(
+    const std::vector<int64_t>& request_ids,
+    const UpdateCallback& callback) {
+  if (!CheckDb()) {
+    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
+                            callback);
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&RemoveRequestsSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
+}
+
+void RequestQueueStoreSQL::Reset(const ResetCallback& callback) {
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ResetSync, db_.get(), db_file_path_,
+                 base::ThreadTaskRunnerHandle::Get(),
+                 base::Bind(&RequestQueueStoreSQL::OnResetDone,
+                            weak_ptr_factory_.GetWeakPtr(), callback)));
+}
+
+StoreState RequestQueueStoreSQL::state() const {
+  return state_;
+}
+
+void RequestQueueStoreSQL::OnOpenConnectionDone(
+    const InitializeCallback& callback,
+    bool success) {
+  DCHECK(db_.get());
+  state_ = success ? StoreState::LOADED : StoreState::FAILED_LOADING;
+  callback.Run(success);
+}
+
+void RequestQueueStoreSQL::OnResetDone(const ResetCallback& callback,
+                                       bool success) {
+  state_ = success ? StoreState::NOT_LOADED : StoreState::FAILED_RESET;
+  db_.reset();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                base::Bind(callback, success));
+}
+
+bool RequestQueueStoreSQL::CheckDb() const {
+  return db_ && state_ == StoreState::LOADED;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/request_queue_store_sql.h b/components/offline_pages/core/background/request_queue_store_sql.h
new file mode 100644
index 0000000..6bd9b9a
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_store_sql.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
+
+#include <stdint.h>
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace sql {
+class Connection;
+}
+
+namespace offline_pages {
+
+// SQLite implementation of RequestQueueStore.
+class RequestQueueStoreSQL : public RequestQueueStore {
+ public:
+  RequestQueueStoreSQL(
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      const base::FilePath& database_dir);
+  ~RequestQueueStoreSQL() override;
+
+  // RequestQueueStore implementation.
+  void Initialize(const InitializeCallback& callback) override;
+  void GetRequests(const GetRequestsCallback& callback) override;
+  // Note: current implementation of this method makes a SQL query per ID. This
+  // is OK as long as number of IDs stays low, which is a typical case.
+  // Implementation should be revisited in case that presumption changes.
+  void GetRequestsByIds(const std::vector<int64_t>& request_ids,
+                        const UpdateCallback& callback) override;
+  void AddRequest(const SavePageRequest& offline_page,
+                  const AddCallback& callback) override;
+  void UpdateRequests(const std::vector<SavePageRequest>& requests,
+                      const UpdateCallback& callback) override;
+  void RemoveRequests(const std::vector<int64_t>& request_ids,
+                      const UpdateCallback& callback) override;
+  void Reset(const ResetCallback& callback) override;
+  StoreState state() const override;
+
+ private:
+  // Used to finalize DB connection initialization.
+  void OnOpenConnectionDone(const InitializeCallback& callback, bool success);
+
+  // Used to finalize DB connection reset.
+  void OnResetDone(const ResetCallback& callback, bool success);
+
+  // Helper function to return immediately if no database is found.
+  bool CheckDb() const;
+
+  // Background thread where all SQL access should be run.
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+  // Path to the database on disk.
+  base::FilePath db_file_path_;
+
+  // Database connection.
+  std::unique_ptr<sql::Connection> db_;
+
+  // State of the store.
+  StoreState state_;
+
+  base::WeakPtrFactory<RequestQueueStoreSQL> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(RequestQueueStoreSQL);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_QUEUE_STORE_SQL_H_
diff --git a/components/offline_pages/core/background/request_queue_store_unittest.cc b/components/offline_pages/core/background/request_queue_store_unittest.cc
new file mode 100644
index 0000000..7d697c6
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_store_unittest.cc
@@ -0,0 +1,503 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_queue_store.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/request_queue.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/request_queue_store_sql.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+using UpdateStatus = RequestQueueStore::UpdateStatus;
+
+namespace {
+const int64_t kRequestId = 42;
+const int64_t kRequestId2 = 44;
+const int64_t kRequestId3 = 47;
+const GURL kUrl("http://example.com");
+const GURL kUrl2("http://another-example.com");
+const ClientId kClientId("bookmark", "1234");
+const ClientId kClientId2("async", "5678");
+const bool kUserRequested = true;
+
+enum class LastResult {
+  RESULT_NONE,
+  RESULT_FALSE,
+  RESULT_TRUE,
+};
+
+}  // namespace
+
+// Class that serves as a base for testing different implementations of the
+// |RequestQueueStore|. Specific implementations extend the templatized version
+// of this class and provide appropriate store factory.
+class RequestQueueStoreTestBase : public testing::Test {
+ public:
+  RequestQueueStoreTestBase();
+
+  // Test overrides.
+  void TearDown() override;
+
+  void PumpLoop();
+  void ClearResults();
+  void InitializeStore(RequestQueueStore* store);
+
+  void InitializeCallback(bool success);
+  // Callback used for get requests.
+  void GetRequestsDone(bool result,
+                       std::vector<std::unique_ptr<SavePageRequest>> requests);
+  // Callback used for add/update request.
+  void AddOrUpdateDone(UpdateStatus result);
+  void AddRequestDone(ItemActionStatus status);
+  void UpdateRequestDone(std::unique_ptr<UpdateRequestsResult> result);
+  // Callback used for reset.
+  void ResetDone(bool result);
+
+  LastResult last_result() const { return last_result_; }
+  UpdateStatus last_update_status() const { return last_update_status_; }
+  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+    return last_requests_;
+  }
+  ItemActionStatus last_add_status() const { return last_add_status_; }
+
+  UpdateRequestsResult* last_update_result() const {
+    return last_update_result_.get();
+  }
+
+ protected:
+  base::ScopedTempDir temp_directory_;
+
+ private:
+  LastResult last_result_;
+  UpdateStatus last_update_status_;
+  ItemActionStatus last_add_status_;
+  std::unique_ptr<UpdateRequestsResult> last_update_result_;
+  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+RequestQueueStoreTestBase::RequestQueueStoreTestBase()
+    : last_result_(LastResult::RESULT_NONE),
+      last_update_status_(UpdateStatus::FAILED),
+      last_add_status_(ItemActionStatus::NOT_FOUND),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {
+  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
+}
+
+void RequestQueueStoreTestBase::TearDown() {
+  // Wait for all the pieces of the store to delete itself properly.
+  PumpLoop();
+}
+
+void RequestQueueStoreTestBase::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void RequestQueueStoreTestBase::ClearResults() {
+  last_result_ = LastResult::RESULT_NONE;
+  last_update_status_ = UpdateStatus::FAILED;
+  last_add_status_ = ItemActionStatus::NOT_FOUND;
+  last_requests_.clear();
+  last_update_result_.reset(nullptr);
+}
+
+void RequestQueueStoreTestBase::InitializeStore(RequestQueueStore* store) {
+  store->Initialize(base::Bind(&RequestQueueStoreTestBase::InitializeCallback,
+                               base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ClearResults();
+}
+
+void RequestQueueStoreTestBase::InitializeCallback(bool success) {
+  last_result_ = success ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
+}
+
+void RequestQueueStoreTestBase::GetRequestsDone(
+    bool result,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
+  last_requests_ = std::move(requests);
+}
+
+void RequestQueueStoreTestBase::AddOrUpdateDone(UpdateStatus status) {
+  last_update_status_ = status;
+}
+
+void RequestQueueStoreTestBase::AddRequestDone(ItemActionStatus status) {
+  last_add_status_ = status;
+}
+
+void RequestQueueStoreTestBase::UpdateRequestDone(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  last_update_result_ = std::move(result);
+}
+
+void RequestQueueStoreTestBase::ResetDone(bool result) {
+  last_result_ = result ? LastResult::RESULT_TRUE : LastResult::RESULT_FALSE;
+}
+
+// Defines interface for the store factory.
+class RequestQueueStoreFactory {
+ public:
+  virtual RequestQueueStore* BuildStore(const base::FilePath& path) = 0;
+};
+
+// Implements a store factory for in memory store.
+class RequestQueueInMemoryStoreFactory : public RequestQueueStoreFactory {
+ public:
+  RequestQueueStore* BuildStore(const base::FilePath& path) override {
+    RequestQueueStore* store = new RequestQueueInMemoryStore();
+    return store;
+  }
+};
+
+// Implements a store factory for SQLite based implementation of the store.
+class RequestQueueStoreSQLFactory : public RequestQueueStoreFactory {
+ public:
+  RequestQueueStore* BuildStore(const base::FilePath& path) override {
+    RequestQueueStore* store =
+        new RequestQueueStoreSQL(base::ThreadTaskRunnerHandle::Get(), path);
+    return store;
+  }
+};
+
+// Defines a store test fixture templatized by the store factory.
+template <typename T>
+class RequestQueueStoreTest : public RequestQueueStoreTestBase {
+ public:
+  std::unique_ptr<RequestQueueStore> BuildStore();
+
+ protected:
+  T factory_;
+};
+
+template <typename T>
+std::unique_ptr<RequestQueueStore> RequestQueueStoreTest<T>::BuildStore() {
+  std::unique_ptr<RequestQueueStore> store(
+      factory_.BuildStore(temp_directory_.GetPath()));
+  return store;
+}
+
+// |StoreTypes| lists all factories, based on which the tests will be created.
+typedef testing::Types<RequestQueueInMemoryStoreFactory,
+                       RequestQueueStoreSQLFactory>
+    StoreTypes;
+
+// This portion causes test fixtures to be defined.
+// Notice that in the store we are using "this->" to refer to the methods
+// defined on the |RequestQuieueStoreBaseTest| class. That's by design.
+TYPED_TEST_CASE(RequestQueueStoreTest, StoreTypes);
+
+TYPED_TEST(RequestQueueStoreTest, GetRequestsEmpty) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_TRUE(this->last_requests().empty());
+}
+
+TYPED_TEST(RequestQueueStoreTest, GetRequestsByIds) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+                           kUserRequested);
+  store->AddRequest(request1,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  store->AddRequest(request2,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  std::vector<int64_t> request_ids{kRequestId, kRequestId2};
+  store->GetRequestsByIds(
+      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
+                              base::Unretained(this)));
+
+  ASSERT_FALSE(this->last_update_result());
+  this->PumpLoop();
+  ASSERT_TRUE(this->last_update_result());
+  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[0].second);
+  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[1].second);
+  EXPECT_EQ(2UL, this->last_update_result()->updated_items.size());
+  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
+  EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1));
+  this->ClearResults();
+
+  request_ids.clear();
+  request_ids.push_back(kRequestId);
+  request_ids.push_back(kRequestId3);
+  request_ids.push_back(kRequestId);
+
+  store->GetRequestsByIds(
+      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
+                              base::Unretained(this)));
+
+  ASSERT_FALSE(this->last_update_result());
+  this->PumpLoop();
+  ASSERT_TRUE(this->last_update_result());
+  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[0].second);
+  EXPECT_EQ(kRequestId3, this->last_update_result()->item_statuses[1].first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            this->last_update_result()->item_statuses[1].second);
+  EXPECT_EQ(1UL, this->last_update_result()->updated_items.size());
+  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
+}
+
+TYPED_TEST(RequestQueueStoreTest, AddRequest) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+
+  store->AddRequest(request,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status());
+  this->PumpLoop();
+  ASSERT_EQ(ItemActionStatus::SUCCESS, this->last_add_status());
+
+  // Verifying get reqeust results after a request was added.
+  this->ClearResults();
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_EQ(1ul, this->last_requests().size());
+  ASSERT_EQ(request, *(this->last_requests()[0].get()));
+
+  // Verify it is not possible to add the same request twice.
+  this->ClearResults();
+  store->AddRequest(request,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  ASSERT_EQ(ItemActionStatus::NOT_FOUND, this->last_add_status());
+  this->PumpLoop();
+  ASSERT_EQ(ItemActionStatus::ALREADY_EXISTS, this->last_add_status());
+
+  // Check that there is still only one item in the store.
+  this->ClearResults();
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_EQ(1ul, this->last_requests().size());
+}
+
+TYPED_TEST(RequestQueueStoreTest, UpdateRequest) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+                                   kUserRequested);
+  store->AddRequest(original_request,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  base::Time new_creation_time =
+      creation_time + base::TimeDelta::FromMinutes(1);
+  base::Time activation_time = creation_time + base::TimeDelta::FromHours(6);
+  // Try updating an existing request.
+  SavePageRequest updated_request(kRequestId, kUrl, kClientId,
+                                  new_creation_time, activation_time,
+                                  kUserRequested);
+  // Try to update a non-existing request.
+  SavePageRequest updated_request2(kRequestId2, kUrl, kClientId,
+                                   new_creation_time, activation_time,
+                                   kUserRequested);
+  std::vector<SavePageRequest> requests_to_update{updated_request,
+                                                  updated_request2};
+  store->UpdateRequests(
+      requests_to_update,
+      base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
+                 base::Unretained(this)));
+  ASSERT_FALSE(this->last_update_result());
+  this->PumpLoop();
+  ASSERT_TRUE(this->last_update_result());
+  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[0].second);
+  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            this->last_update_result()->item_statuses[1].second);
+  EXPECT_EQ(1UL, this->last_update_result()->updated_items.size());
+  EXPECT_EQ(updated_request,
+            *(this->last_update_result()->updated_items.begin()));
+
+  // Verifying get reqeust results after a request was updated.
+  this->ClearResults();
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_EQ(1ul, this->last_requests().size());
+  ASSERT_EQ(updated_request, *(this->last_requests()[0].get()));
+}
+
+TYPED_TEST(RequestQueueStoreTest, RemoveRequests) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+                           kUserRequested);
+  store->AddRequest(request1,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  store->AddRequest(request2,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  std::vector<int64_t> request_ids{kRequestId, kRequestId2};
+  store->RemoveRequests(
+      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
+                              base::Unretained(this)));
+
+  ASSERT_FALSE(this->last_update_result());
+  this->PumpLoop();
+  ASSERT_TRUE(this->last_update_result());
+  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[0].second);
+  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            this->last_update_result()->item_statuses[1].second);
+  EXPECT_EQ(2UL, this->last_update_result()->updated_items.size());
+  EXPECT_EQ(request1, this->last_update_result()->updated_items.at(0));
+  EXPECT_EQ(request2, this->last_update_result()->updated_items.at(1));
+  this->ClearResults();
+
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_TRUE(this->last_requests().empty());
+  this->ClearResults();
+
+  // Try to remove a request that is not in the queue.
+  store->RemoveRequests(
+      request_ids, base::Bind(&RequestQueueStoreTestBase::UpdateRequestDone,
+                              base::Unretained(this)));
+  ASSERT_FALSE(this->last_update_result());
+  this->PumpLoop();
+  ASSERT_TRUE(this->last_update_result());
+  // When requests are missing, we expect the results to say so, but since they
+  // are missing, no requests should have been returned.
+  EXPECT_EQ(2UL, this->last_update_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, this->last_update_result()->item_statuses[0].first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            this->last_update_result()->item_statuses[0].second);
+  EXPECT_EQ(kRequestId2, this->last_update_result()->item_statuses[1].first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            this->last_update_result()->item_statuses[1].second);
+  EXPECT_EQ(0UL, this->last_update_result()->updated_items.size());
+}
+
+TYPED_TEST(RequestQueueStoreTest, ResetStore) {
+  std::unique_ptr<RequestQueueStore> store(this->BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+                                   kUserRequested);
+  store->AddRequest(original_request,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  store->Reset(base::Bind(&RequestQueueStoreTestBase::ResetDone,
+                          base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  this->ClearResults();
+
+  this->InitializeStore(store.get());
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_TRUE(this->last_requests().empty());
+}
+
+class RequestQueueStoreSQLTest
+    : public RequestQueueStoreTest<RequestQueueStoreSQLFactory> {};
+
+// Makes sure that persistent DB is actually persisting requests across store
+// restarts.
+TEST_F(RequestQueueStoreSQLTest, SaveCloseReopenRead) {
+  std::unique_ptr<RequestQueueStore> store(BuildStore());
+  this->InitializeStore(store.get());
+
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+                                   kUserRequested);
+  store->AddRequest(original_request,
+                    base::Bind(&RequestQueueStoreTestBase::AddRequestDone,
+                               base::Unretained(this)));
+  PumpLoop();
+  ClearResults();
+
+  // Resets the store, using the same temp directory. The contents should be
+  // intact. First reset is done separately to release DB lock.
+  store.reset();
+  store = BuildStore();
+  this->InitializeStore(store.get());
+
+  store->GetRequests(base::Bind(&RequestQueueStoreTestBase::GetRequestsDone,
+                                base::Unretained(this)));
+  ASSERT_EQ(LastResult::RESULT_NONE, this->last_result());
+  this->PumpLoop();
+  ASSERT_EQ(LastResult::RESULT_TRUE, this->last_result());
+  ASSERT_EQ(1ul, this->last_requests().size());
+  ASSERT_TRUE(original_request == *(this->last_requests().at(0).get()));
+}
+
+}  // offline_pages
diff --git a/components/offline_pages/core/background/request_queue_unittest.cc b/components/offline_pages/core/background/request_queue_unittest.cc
new file mode 100644
index 0000000..3f5dc4556
--- /dev/null
+++ b/components/offline_pages/core/background/request_queue_unittest.cc
@@ -0,0 +1,578 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/request_queue.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/background/device_conditions.h"
+#include "components/offline_pages/core/background/offliner_policy.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
+#include "components/offline_pages/core/background/request_notifier.h"
+#include "components/offline_pages/core/background/request_queue_in_memory_store.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+using AddRequestResult = AddRequestResult;
+using GetRequestsResult = GetRequestsResult;
+using UpdateRequestResult = UpdateRequestResult;
+
+namespace {
+// Data for request 1.
+const int64_t kRequestId = 42;
+const GURL kUrl("http://example.com");
+const ClientId kClientId("bookmark", "1234");
+// Data for request 2.
+const int64_t kRequestId2 = 77;
+const GURL kUrl2("http://test.com");
+const ClientId kClientId2("bookmark", "567");
+const bool kUserRequested = true;
+const int64_t kRequestId3 = 99;
+const int kOneWeekInSeconds = 7 * 24 * 60 * 60;
+
+// Default request
+const SavePageRequest kEmptyRequest(0UL,
+                                    GURL(""),
+                                    ClientId("", ""),
+                                    base::Time(),
+                                    true);
+
+}  // namespace
+
+// Helper class needed by the PickRequestTask
+class RequestNotifierStub : public RequestNotifier {
+ public:
+  RequestNotifierStub()
+      : last_expired_request_(kEmptyRequest), total_expired_requests_(0) {}
+
+  void NotifyAdded(const SavePageRequest& request) override {}
+  void NotifyChanged(const SavePageRequest& request) override {}
+
+  void NotifyCompleted(const SavePageRequest& request,
+                       BackgroundSavePageResult status) override {
+    last_expired_request_ = request;
+    last_request_expiration_status_ = status;
+    total_expired_requests_++;
+  }
+
+  const SavePageRequest& last_expired_request() {
+    return last_expired_request_;
+  }
+
+  RequestCoordinator::BackgroundSavePageResult
+  last_request_expiration_status() {
+    return last_request_expiration_status_;
+  }
+
+  int32_t total_expired_requests() { return total_expired_requests_; }
+
+ private:
+  BackgroundSavePageResult last_request_expiration_status_;
+  SavePageRequest last_expired_request_;
+  int32_t total_expired_requests_;
+};
+
+// TODO(fgorski): Add tests for store failures in add/remove/get.
+class RequestQueueTest : public testing::Test {
+ public:
+  RequestQueueTest();
+  ~RequestQueueTest() override;
+
+  // Test overrides.
+  void SetUp() override;
+
+  void PumpLoop();
+
+  // Callback for adding requests.
+  void AddRequestDone(AddRequestResult result, const SavePageRequest& request);
+  // Callback for getting requests.
+  void GetRequestsDone(GetRequestsResult result,
+                       std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  void UpdateRequestDone(UpdateRequestResult result);
+  void UpdateRequestsDone(std::unique_ptr<UpdateRequestsResult> result);
+
+  void ClearResults();
+
+  RequestQueue* queue() { return queue_.get(); }
+
+  AddRequestResult last_add_result() const { return last_add_result_; }
+  SavePageRequest* last_added_request() { return last_added_request_.get(); }
+
+  UpdateRequestResult last_update_result() const { return last_update_result_; }
+
+  GetRequestsResult last_get_requests_result() const {
+    return last_get_requests_result_;
+  }
+
+  const std::vector<std::unique_ptr<SavePageRequest>>& last_requests() const {
+    return last_requests_;
+  }
+
+  UpdateRequestsResult* update_requests_result() const {
+    return update_requests_result_.get();
+  }
+
+  void RequestPickedCallback(const SavePageRequest& request) {}
+  void RequestNotPickedCallback(bool non_user_requested_tasks_remain) {}
+  void RequestCountCallback(size_t total_count, size_t available_count) {}
+
+ private:
+  AddRequestResult last_add_result_;
+  std::unique_ptr<SavePageRequest> last_added_request_;
+  std::unique_ptr<UpdateRequestsResult> update_requests_result_;
+  UpdateRequestResult last_update_result_;
+
+  GetRequestsResult last_get_requests_result_;
+  std::vector<std::unique_ptr<SavePageRequest>> last_requests_;
+
+  std::unique_ptr<RequestQueue> queue_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+RequestQueueTest::RequestQueueTest()
+    : last_add_result_(AddRequestResult::STORE_FAILURE),
+      last_update_result_(UpdateRequestResult::STORE_FAILURE),
+      last_get_requests_result_(GetRequestsResult::STORE_FAILURE),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {}
+
+RequestQueueTest::~RequestQueueTest() {}
+
+void RequestQueueTest::SetUp() {
+  std::unique_ptr<RequestQueueInMemoryStore> store(
+      new RequestQueueInMemoryStore());
+  queue_.reset(new RequestQueue(std::move(store)));
+}
+
+void RequestQueueTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void RequestQueueTest::AddRequestDone(AddRequestResult result,
+                                      const SavePageRequest& request) {
+  last_add_result_ = result;
+  last_added_request_.reset(new SavePageRequest(request));
+}
+
+void RequestQueueTest::GetRequestsDone(
+    GetRequestsResult result,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  last_get_requests_result_ = result;
+  last_requests_ = std::move(requests);
+}
+
+void RequestQueueTest::UpdateRequestDone(UpdateRequestResult result) {
+  last_update_result_ = result;
+}
+
+void RequestQueueTest::UpdateRequestsDone(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  update_requests_result_ = std::move(result);
+}
+
+void RequestQueueTest::ClearResults() {
+  last_add_result_ = AddRequestResult::STORE_FAILURE;
+  last_update_result_ = UpdateRequestResult::STORE_FAILURE;
+  last_get_requests_result_ = GetRequestsResult::STORE_FAILURE;
+  last_added_request_.reset(nullptr);
+  update_requests_result_.reset(nullptr);
+  last_requests_.clear();
+}
+
+TEST_F(RequestQueueTest, GetRequestsEmpty) {
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(0ul, last_requests().size());
+}
+
+TEST_F(RequestQueueTest, AddRequest) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(AddRequestResult::SUCCESS, last_add_result());
+  ASSERT_TRUE(last_added_request());
+  ASSERT_EQ(kRequestId, last_added_request()->request_id());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+}
+
+TEST_F(RequestQueueTest, RemoveRequest) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(kRequestId, last_added_request()->request_id());
+
+  std::vector<int64_t> remove_requests{kRequestId};
+  queue()->RemoveRequests(remove_requests,
+                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                     base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(1ul, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
+  EXPECT_EQ(request, update_requests_result()->updated_items.at(0));
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(0ul, last_requests().size());
+}
+
+TEST_F(RequestQueueTest, RemoveSeveralRequests) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(kRequestId, last_added_request()->request_id());
+
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  queue()->AddRequest(request2, base::Bind(&RequestQueueTest::AddRequestDone,
+                                           base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(kRequestId2, last_added_request()->request_id());
+
+  std::vector<int64_t> remove_requests;
+  remove_requests.push_back(kRequestId);
+  remove_requests.push_back(kRequestId2);
+  remove_requests.push_back(kRequestId3);
+  queue()->RemoveRequests(remove_requests,
+                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                     base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(3ul, update_requests_result()->item_statuses.size());
+  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  ASSERT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  ASSERT_EQ(kRequestId2, update_requests_result()->item_statuses.at(1).first);
+  ASSERT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(1).second);
+  ASSERT_EQ(kRequestId3, update_requests_result()->item_statuses.at(2).first);
+  ASSERT_EQ(ItemActionStatus::NOT_FOUND,
+            update_requests_result()->item_statuses.at(2).second);
+  EXPECT_EQ(2UL, update_requests_result()->updated_items.size());
+  EXPECT_EQ(request, update_requests_result()->updated_items.at(0));
+  EXPECT_EQ(request2, update_requests_result()->updated_items.at(1));
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Verify both requests are no longer in the queue.
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(0ul, last_requests().size());
+}
+
+TEST_F(RequestQueueTest, PauseAndResume) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(kRequestId, last_added_request()->request_id());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+
+  std::vector<int64_t> request_ids;
+  request_ids.push_back(kRequestId);
+
+  // Pause the request.
+  queue()->ChangeRequestsState(request_ids,
+                               SavePageRequest::RequestState::PAUSED,
+                               base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  ASSERT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  ASSERT_EQ(1ul, update_requests_result()->updated_items.size());
+  ASSERT_EQ(SavePageRequest::RequestState::PAUSED,
+            update_requests_result()->updated_items.at(0).request_state());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Verify the request is paused.
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+  ASSERT_EQ(SavePageRequest::RequestState::PAUSED,
+            last_requests().at(0)->request_state());
+
+  // Resume the request.
+  queue()->ChangeRequestsState(request_ids,
+                               SavePageRequest::RequestState::AVAILABLE,
+                               base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  ASSERT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  ASSERT_EQ(1ul, update_requests_result()->updated_items.size());
+  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            update_requests_result()->updated_items.at(0).request_state());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+
+  // Verify the request is no longer paused.
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            last_requests().at(0)->request_state());
+}
+
+// A longer test populating the request queue with more than one item, properly
+// listing multiple items and removing the right item.
+TEST_F(RequestQueueTest, MultipleRequestsAddGetRemove) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+                           kUserRequested);
+  queue()->AddRequest(request1, base::Bind(&RequestQueueTest::AddRequestDone,
+                                           base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(request1.request_id(), last_added_request()->request_id());
+  SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
+                           kUserRequested);
+  queue()->AddRequest(request2, base::Bind(&RequestQueueTest::AddRequestDone,
+                                           base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(request2.request_id(), last_added_request()->request_id());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(2ul, last_requests().size());
+
+  std::vector<int64_t> remove_requests;
+  remove_requests.push_back(request1.request_id());
+  queue()->RemoveRequests(remove_requests,
+                          base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                     base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  ASSERT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  ASSERT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+  ASSERT_EQ(request2.request_id(), last_requests().at(0)->request_id());
+}
+
+TEST_F(RequestQueueTest, MarkAttemptStarted) {
+  // First add a request.  Retry count will be set to 0.
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+
+  base::Time before_time = base::Time::Now();
+  // Update the request, ensure it succeeded.
+  queue()->MarkAttemptStarted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
+  EXPECT_LE(before_time,
+            update_requests_result()->updated_items.at(0).last_attempt_time());
+  EXPECT_GE(base::Time::Now(),
+            update_requests_result()->updated_items.at(0).last_attempt_time());
+  EXPECT_EQ(
+      1, update_requests_result()->updated_items.at(0).started_attempt_count());
+  EXPECT_EQ(SavePageRequest::RequestState::OFFLINING,
+            update_requests_result()->updated_items.at(0).request_state());
+
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(GetRequestsResult::SUCCESS, last_get_requests_result());
+  ASSERT_EQ(1ul, last_requests().size());
+  EXPECT_EQ(update_requests_result()->updated_items.at(0),
+            *last_requests().at(0));
+}
+
+TEST_F(RequestQueueTest, MarkAttempStartedRequestNotPresent) {
+  // First add a request.  Retry count will be set to 0.
+  base::Time creation_time = base::Time::Now();
+  // This request is never put into the queue.
+  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+                           kUserRequested);
+
+  queue()->MarkAttemptStarted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0ul, update_requests_result()->updated_items.size());
+}
+
+TEST_F(RequestQueueTest, MarkAttemptAborted) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+
+  // Start request.
+  queue()->MarkAttemptStarted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+  ClearResults();
+
+  queue()->MarkAttemptAborted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+
+  ASSERT_TRUE(update_requests_result());
+  EXPECT_EQ(1UL, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            update_requests_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(RequestQueueTest, MarkAttemptAbortedRequestNotPresent) {
+  // First add a request.  Retry count will be set to 0.
+  base::Time creation_time = base::Time::Now();
+  // This request is never put into the queue.
+  SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
+                           kUserRequested);
+
+  queue()->MarkAttemptAborted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+  ASSERT_EQ(1ul, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::NOT_FOUND,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(0ul, update_requests_result()->updated_items.size());
+}
+
+TEST_F(RequestQueueTest, MarkAttemptCompleted) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  queue()->AddRequest(request, base::Bind(&RequestQueueTest::AddRequestDone,
+                                          base::Unretained(this)));
+  PumpLoop();
+
+  // Start request.
+  queue()->MarkAttemptStarted(kRequestId,
+                              base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                                         base::Unretained(this)));
+  PumpLoop();
+  ClearResults();
+
+  queue()->MarkAttemptCompleted(
+      kRequestId, base::Bind(&RequestQueueTest::UpdateRequestsDone,
+                             base::Unretained(this)));
+  PumpLoop();
+
+  ASSERT_TRUE(update_requests_result());
+  EXPECT_EQ(1UL, update_requests_result()->item_statuses.size());
+  EXPECT_EQ(kRequestId, update_requests_result()->item_statuses.at(0).first);
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            update_requests_result()->item_statuses.at(0).second);
+  EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
+  EXPECT_EQ(SavePageRequest::RequestState::AVAILABLE,
+            update_requests_result()->updated_items.at(0).request_state());
+}
+
+TEST_F(RequestQueueTest, CleanStaleRequests) {
+  // Create a request that is already expired.
+  base::Time creation_time =
+      base::Time::Now() - base::TimeDelta::FromSeconds(2 * kOneWeekInSeconds);
+
+  SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
+                                   kUserRequested);
+  queue()->AddRequest(
+      original_request,
+      base::Bind(&RequestQueueTest::AddRequestDone, base::Unretained(this)));
+  this->PumpLoop();
+  this->ClearResults();
+
+  // Set up a picker factory pointing to our fake notifier.
+  OfflinerPolicy policy;
+  RequestNotifierStub notifier;
+  RequestCoordinatorEventLogger event_logger;
+  std::unique_ptr<CleanupTaskFactory> cleanup_factory(
+      new CleanupTaskFactory(&policy, &notifier, &event_logger));
+  queue()->SetCleanupFactory(std::move(cleanup_factory));
+
+  // Do a pick and clean operation, which will remove stale entries.
+  DeviceConditions conditions;
+  std::set<int64_t> disabled_list;
+  queue()->CleanupRequestQueue();
+
+  this->PumpLoop();
+
+  // Notifier should have been notified that the request was removed.
+  ASSERT_EQ(notifier.last_expired_request().request_id(), kRequestId);
+  ASSERT_EQ(notifier.last_request_expiration_status(),
+            RequestNotifier::BackgroundSavePageResult::EXPIRED);
+
+  // Doing a get should show no entries left in the queue since the expired
+  // request has been removed.
+  queue()->GetRequests(
+      base::Bind(&RequestQueueTest::GetRequestsDone, base::Unretained(this)));
+  this->PumpLoop();
+  ASSERT_EQ(GetRequestsResult::SUCCESS, this->last_get_requests_result());
+  ASSERT_TRUE(this->last_requests().empty());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/save_page_request.cc b/components/offline_pages/core/background/save_page_request.cc
new file mode 100644
index 0000000..6e3b36a
--- /dev/null
+++ b/components/offline_pages/core/background/save_page_request.cc
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/save_page_request.h"
+
+namespace offline_pages {
+
+SavePageRequest::SavePageRequest(int64_t request_id,
+                                 const GURL& url,
+                                 const ClientId& client_id,
+                                 const base::Time& creation_time,
+                                 const bool user_requested)
+    : request_id_(request_id),
+      url_(url),
+      client_id_(client_id),
+      creation_time_(creation_time),
+      activation_time_(creation_time),
+      started_attempt_count_(0),
+      completed_attempt_count_(0),
+      user_requested_(user_requested),
+      state_(RequestState::AVAILABLE) {}
+
+SavePageRequest::SavePageRequest(int64_t request_id,
+                                 const GURL& url,
+                                 const ClientId& client_id,
+                                 const base::Time& creation_time,
+                                 const base::Time& activation_time,
+                                 const bool user_requested)
+    : request_id_(request_id),
+      url_(url),
+      client_id_(client_id),
+      creation_time_(creation_time),
+      activation_time_(activation_time),
+      started_attempt_count_(0),
+      completed_attempt_count_(0),
+      user_requested_(user_requested),
+      state_(RequestState::AVAILABLE) {}
+
+SavePageRequest::SavePageRequest(const SavePageRequest& other)
+    : request_id_(other.request_id_),
+      url_(other.url_),
+      client_id_(other.client_id_),
+      creation_time_(other.creation_time_),
+      activation_time_(other.activation_time_),
+      started_attempt_count_(other.started_attempt_count_),
+      completed_attempt_count_(other.completed_attempt_count_),
+      last_attempt_time_(other.last_attempt_time_),
+      user_requested_(other.user_requested_),
+      state_(other.state_) {}
+
+SavePageRequest::~SavePageRequest() {}
+
+bool SavePageRequest::operator==(const SavePageRequest& other) const {
+  return request_id_ == other.request_id_ && url_ == other.url_ &&
+         client_id_ == other.client_id_ &&
+         creation_time_ == other.creation_time_ &&
+         activation_time_ == other.activation_time_ &&
+         started_attempt_count_ == other.started_attempt_count_ &&
+         completed_attempt_count_ == other.completed_attempt_count_ &&
+         last_attempt_time_ == other.last_attempt_time_ &&
+         state_ == other.state_;
+}
+
+void SavePageRequest::MarkAttemptStarted(const base::Time& start_time) {
+  DCHECK_LE(activation_time_, start_time);
+  // TODO(fgorski): As part of introducing policy in GetStatus, we can make a
+  // check here to ensure we only start tasks in status pending, and bail out in
+  // other cases.
+  last_attempt_time_ = start_time;
+  ++started_attempt_count_;
+  state_ = RequestState::OFFLINING;
+}
+
+void SavePageRequest::MarkAttemptCompleted() {
+  ++completed_attempt_count_;
+  state_ = RequestState::AVAILABLE;
+}
+
+void SavePageRequest::MarkAttemptAborted() {
+  DCHECK_GT(started_attempt_count_, 0);
+  // We intentinally do not increment the completed_attempt_count_, since this
+  // was killed before it completed, so we could use the phone or browser for
+  // other things.
+  state_ = RequestState::AVAILABLE;
+}
+
+void SavePageRequest::MarkAttemptPaused() {
+  state_ = RequestState::PAUSED;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/save_page_request.h b/components/offline_pages/core/background/save_page_request.h
new file mode 100644
index 0000000..ab5a760
--- /dev/null
+++ b/components/offline_pages/core/background/save_page_request.h
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+// Class representing a request to save page.
+class SavePageRequest {
+ public:
+  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
+  enum class RequestState {
+    AVAILABLE = 0,  // Request can be scheduled when preconditions are met.
+    PAUSED = 1,     // Request is not available until it is unpaused.
+    OFFLINING = 2,  // Request is actively offlining.
+  };
+
+  SavePageRequest(int64_t request_id,
+                  const GURL& url,
+                  const ClientId& client_id,
+                  const base::Time& creation_time,
+                  const bool user_requested);
+  SavePageRequest(int64_t request_id,
+                  const GURL& url,
+                  const ClientId& client_id,
+                  const base::Time& creation_time,
+                  const base::Time& activation_time,
+                  const bool user_requested);
+  SavePageRequest(const SavePageRequest& other);
+  ~SavePageRequest();
+
+  bool operator==(const SavePageRequest& other) const;
+
+  // Updates the |last_attempt_time_| and increments |attempt_count_|.
+  void MarkAttemptStarted(const base::Time& start_time);
+
+  // Marks attempt as completed and clears |last_attempt_time_|.
+  void MarkAttemptCompleted();
+
+  // Marks attempt as aborted. Specifically it clears |last_attempt_time_|
+  // and decrements |attempt_count_|.
+  void MarkAttemptAborted();
+
+  // Mark the attempt as paused.  It is not available for future prerendering
+  // until it has been explicitly unpaused.
+  void MarkAttemptPaused();
+
+  int64_t request_id() const { return request_id_; }
+
+  const GURL& url() const { return url_; }
+
+  const ClientId& client_id() const { return client_id_; }
+
+  RequestState request_state() const { return state_; }
+  void set_request_state(RequestState new_state) { state_ = new_state; }
+
+  const base::Time& creation_time() const { return creation_time_; }
+
+  const base::Time& activation_time() const { return activation_time_; }
+
+  int64_t started_attempt_count() const { return started_attempt_count_; }
+  void set_started_attempt_count(int64_t started_attempt_count) {
+    started_attempt_count_ = started_attempt_count;
+  }
+
+  int64_t completed_attempt_count() const { return completed_attempt_count_; }
+  void set_completed_attempt_count(int64_t completed_attempt_count) {
+    completed_attempt_count_ = completed_attempt_count;
+  }
+
+  const base::Time& last_attempt_time() const { return last_attempt_time_; }
+  void set_last_attempt_time(const base::Time& last_attempt_time) {
+    last_attempt_time_ = last_attempt_time;
+  }
+
+  bool user_requested() const { return user_requested_; }
+
+  void set_user_requested(bool user_requested) {
+    user_requested_ = user_requested;
+  }
+
+ private:
+  // ID of this request.
+  int64_t request_id_;
+
+  // Online URL of a page to be offlined.
+  GURL url_;
+
+  // Client ID related to the request. Contains namespace and ID assigned by the
+  // requester.
+  ClientId client_id_;
+
+  // Time when this request was created. (Alternative 2).
+  base::Time creation_time_;
+
+  // Time when this request will become active.
+  base::Time activation_time_;
+
+  // Number of attempts started to get the page.  This may be different than the
+  // number of attempts completed because we could crash.
+  int started_attempt_count_;
+
+  // Number of attempts we actually completed to get the page.
+  int completed_attempt_count_;
+
+  // Timestamp of the last request starting.
+  base::Time last_attempt_time_;
+
+  // Whether the user specifically requested this page (as opposed to a client
+  // like AGSA or Now.)
+  bool user_requested_;
+
+  // The current state of this request
+  RequestState state_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SAVE_PAGE_REQUEST_H_
diff --git a/components/offline_pages/core/background/save_page_request_unittest.cc b/components/offline_pages/core/background/save_page_request_unittest.cc
new file mode 100644
index 0000000..7fc2dcd
--- /dev/null
+++ b/components/offline_pages/core/background/save_page_request_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/save_page_request.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+const int64_t kRequestId = 42;
+const GURL kUrl("http://example.com");
+const ClientId kClientId("bookmark", "1234");
+const bool kUserRequested = true;
+}  // namespace
+
+class SavePageRequestTest : public testing::Test {
+ public:
+  ~SavePageRequestTest() override;
+};
+
+SavePageRequestTest::~SavePageRequestTest() {}
+
+TEST_F(SavePageRequestTest, CreatePendingReqeust) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+  ASSERT_EQ(kRequestId, request.request_id());
+  ASSERT_EQ(kUrl, request.url());
+  ASSERT_EQ(kClientId, request.client_id());
+  ASSERT_EQ(creation_time, request.creation_time());
+  ASSERT_EQ(creation_time, request.activation_time());
+  ASSERT_EQ(base::Time(), request.last_attempt_time());
+  ASSERT_EQ(0, request.completed_attempt_count());
+  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
+  ASSERT_EQ(0, request.started_attempt_count());
+  ASSERT_EQ(0, request.completed_attempt_count());
+}
+
+TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
+  base::Time creation_time = base::Time::Now();
+  base::Time activation_time = creation_time + base::TimeDelta::FromHours(6);
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          activation_time, kUserRequested);
+
+  base::Time start_time = activation_time + base::TimeDelta::FromHours(3);
+  request.MarkAttemptStarted(start_time);
+
+  // Most things don't change about the request.
+  ASSERT_EQ(kRequestId, request.request_id());
+  ASSERT_EQ(kUrl, request.url());
+  ASSERT_EQ(kClientId, request.client_id());
+  ASSERT_EQ(creation_time, request.creation_time());
+  ASSERT_EQ(activation_time, request.activation_time());
+
+  // Attempt time, attempt count and status will though.
+  ASSERT_EQ(start_time, request.last_attempt_time());
+  ASSERT_EQ(1, request.started_attempt_count());
+  ASSERT_EQ(SavePageRequest::RequestState::OFFLINING, request.request_state());
+
+  request.MarkAttemptCompleted();
+
+  // Again, most things don't change about the request.
+  ASSERT_EQ(kRequestId, request.request_id());
+  ASSERT_EQ(kUrl, request.url());
+  ASSERT_EQ(kClientId, request.client_id());
+  ASSERT_EQ(creation_time, request.creation_time());
+  ASSERT_EQ(activation_time, request.activation_time());
+
+  // Last attempt time and status are updated.
+  ASSERT_EQ(1, request.completed_attempt_count());
+  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
+}
+
+TEST_F(SavePageRequestTest, StartAndAbortRequest) {
+  base::Time creation_time = base::Time::Now();
+  SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
+                          kUserRequested);
+
+  base::Time start_time = creation_time + base::TimeDelta::FromHours(3);
+  request.MarkAttemptStarted(start_time);
+
+  // Most things don't change about the request.
+  ASSERT_EQ(kRequestId, request.request_id());
+  ASSERT_EQ(kUrl, request.url());
+  ASSERT_EQ(kClientId, request.client_id());
+  ASSERT_EQ(creation_time, request.creation_time());
+
+  // Attempt time and attempt count will though.
+  ASSERT_EQ(start_time, request.last_attempt_time());
+  ASSERT_EQ(1, request.started_attempt_count());
+  ASSERT_EQ(SavePageRequest::RequestState::OFFLINING, request.request_state());
+
+  request.MarkAttemptAborted();
+
+  // Again, most things don't change about the request.
+  ASSERT_EQ(kRequestId, request.request_id());
+  ASSERT_EQ(kUrl, request.url());
+  ASSERT_EQ(kClientId, request.client_id());
+  ASSERT_EQ(creation_time, request.creation_time());
+
+  // Last attempt time is updated and completed attempt count did not rise.
+  ASSERT_EQ(0, request.completed_attempt_count());
+  ASSERT_EQ(SavePageRequest::RequestState::AVAILABLE, request.request_state());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/scheduler.h b/components/offline_pages/core/background/scheduler.h
new file mode 100644
index 0000000..7ce0a45
--- /dev/null
+++ b/components/offline_pages/core/background/scheduler.h
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
+
+namespace offline_pages {
+
+// Interface of a class responsible for scheduling a task to initiate
+// processing of background offlining requests upon select system conditions
+// (such as having a network connection).
+class Scheduler {
+ public:
+  // Defines a set of system conditions to trigger background processing.
+  struct TriggerConditions {
+    TriggerConditions(bool power, int battery, bool unmetered)
+        : require_power_connected(power),
+          minimum_battery_percentage(battery),
+          require_unmetered_network(unmetered) {}
+    bool require_power_connected;
+    int minimum_battery_percentage;
+    bool require_unmetered_network;
+  };
+
+  Scheduler() {}
+  virtual ~Scheduler() {}
+
+  // Schedules the triggering of a task subject to |trigger_conditions|.
+  // This may overwrite any previous scheduled task with a new one for
+  // these conditions. That is, only one set of triggering conditions
+  // is scheduled at a time.
+  virtual void Schedule(const TriggerConditions& trigger_conditions) = 0;
+
+  // Schedules the triggering of a task in case Chromium is killed,
+  // so we can continue processing background download requests.  This will
+  // not overwrite existing tasks.
+  virtual void BackupSchedule(const TriggerConditions& trigger_conditions,
+                              long delay_in_seconds) = 0;
+
+  // Unschedules the currently scheduled task, if any.
+  virtual void Unschedule() = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
diff --git a/components/offline_pages/core/background/scheduler_stub.cc b/components/offline_pages/core/background/scheduler_stub.cc
new file mode 100644
index 0000000..278536aa
--- /dev/null
+++ b/components/offline_pages/core/background/scheduler_stub.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/scheduler_stub.h"
+
+namespace offline_pages {
+
+SchedulerStub::SchedulerStub()
+    : schedule_called_(false),
+      backup_schedule_called_(false),
+      unschedule_called_(false),
+      schedule_delay_(0L),
+      conditions_(false, 0, false) {}
+
+SchedulerStub::~SchedulerStub() {}
+
+void SchedulerStub::Schedule(const TriggerConditions& trigger_conditions) {
+  schedule_called_ = true;
+  conditions_ = trigger_conditions;
+}
+
+void SchedulerStub::BackupSchedule(const TriggerConditions& trigger_conditions,
+                                   long delay_in_seconds) {
+  backup_schedule_called_ = true;
+  schedule_delay_ = delay_in_seconds;
+  conditions_ = trigger_conditions;
+}
+
+void SchedulerStub::Unschedule() {
+  unschedule_called_ = true;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/scheduler_stub.h b/components/offline_pages/core/background/scheduler_stub.h
new file mode 100644
index 0000000..7300468
--- /dev/null
+++ b/components/offline_pages/core/background/scheduler_stub.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
+
+#include "components/offline_pages/core/background/scheduler.h"
+
+namespace offline_pages {
+
+// Test class stubbing out the functionality of Scheduler.
+// It is only used for test support.
+class SchedulerStub : public Scheduler {
+ public:
+  SchedulerStub();
+  ~SchedulerStub() override;
+
+  void Schedule(const TriggerConditions& trigger_conditions) override;
+
+  void BackupSchedule(const TriggerConditions& trigger_conditions,
+                      long delay_in_seconds) override;
+
+  // Unschedules the currently scheduled task, if any.
+  void Unschedule() override;
+
+  bool schedule_called() const { return schedule_called_; }
+
+  bool backup_schedule_called() const { return backup_schedule_called_; }
+
+  bool unschedule_called() const { return unschedule_called_; }
+
+  TriggerConditions const* conditions() const { return &conditions_; }
+
+ private:
+  bool schedule_called_;
+  bool backup_schedule_called_;
+  bool unschedule_called_;
+  long schedule_delay_;
+  TriggerConditions conditions_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
diff --git a/components/offline_pages/core/background/update_request_task.cc b/components/offline_pages/core/background/update_request_task.cc
new file mode 100644
index 0000000..ac5592f
--- /dev/null
+++ b/components/offline_pages/core/background/update_request_task.cc
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/background/update_request_task.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/time/time.h"
+
+namespace offline_pages {
+
+UpdateRequestTask::UpdateRequestTask(
+    RequestQueueStore* store,
+    int64_t request_id,
+    const RequestQueueStore::UpdateCallback& callback)
+    : store_(store),
+      request_id_(request_id),
+      callback_(callback),
+      weak_ptr_factory_(this) {}
+
+UpdateRequestTask::~UpdateRequestTask() {}
+
+void UpdateRequestTask::Run() {
+  ReadRequest();
+}
+
+void UpdateRequestTask::ReadRequest() {
+  std::vector<int64_t> request_ids{request_id_};
+  store_->GetRequestsByIds(request_ids,
+                           base::Bind(&UpdateRequestTask::UpdateRequestImpl,
+                                      weak_ptr_factory_.GetWeakPtr()));
+}
+
+void UpdateRequestTask::CompleteWithResult(
+    std::unique_ptr<UpdateRequestsResult> result) {
+  callback_.Run(std::move(result));
+  TaskComplete();
+}
+
+bool UpdateRequestTask::ValidateReadResult(UpdateRequestsResult* result) {
+  return result->store_state == StoreState::LOADED &&
+         result->item_statuses.at(0).first == request_id() &&
+         result->item_statuses.at(0).second == ItemActionStatus::SUCCESS &&
+         result->updated_items.size() == 1 &&
+         result->updated_items.at(0).request_id() == request_id();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/background/update_request_task.h b/components/offline_pages/core/background/update_request_task.h
new file mode 100644
index 0000000..bef04f5b
--- /dev/null
+++ b/components/offline_pages/core/background/update_request_task.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/background/request_queue_store.h"
+#include "components/offline_pages/core/task.h"
+
+namespace offline_pages {
+
+// Base class for update requests that only work on a single save page request.
+// Derived classes should implement appropriate functionality by overloading
+// |UpdateRequestImpl| method.
+class UpdateRequestTask : public Task {
+ public:
+  UpdateRequestTask(RequestQueueStore* store,
+                    int64_t request_id,
+                    const RequestQueueStore::UpdateCallback& callback);
+  ~UpdateRequestTask() override;
+
+  // TaskQueue::Task implementation.
+  void Run() override;
+
+ protected:
+  // Step 1. Reading the requests.
+  void ReadRequest();
+  // Step 2. Work is done in the implementation step.
+  virtual void UpdateRequestImpl(
+      std::unique_ptr<UpdateRequestsResult> result) = 0;
+  // Step 3. Completes once update is done.
+  void CompleteWithResult(std::unique_ptr<UpdateRequestsResult> result);
+
+  // Function to uniformly validate read request call for store errors and
+  // presence of the request.
+  bool ValidateReadResult(UpdateRequestsResult* result);
+
+  RequestQueueStore* store() const { return store_; }
+
+  int64_t request_id() const { return request_id_; }
+
+  base::WeakPtr<UpdateRequestTask> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  // Store that this task updates. Not owned.
+  RequestQueueStore* store_;
+  // Request ID of the request to be started.
+  int64_t request_id_;
+  // Callback to complete the task.
+  RequestQueueStore::UpdateCallback callback_;
+
+  base::WeakPtrFactory<UpdateRequestTask> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(UpdateRequestTask);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_UPDATE_REQUEST_TASK_H_
diff --git a/components/offline_pages/core/client_namespace_constants.cc b/components/offline_pages/core/client_namespace_constants.cc
new file mode 100644
index 0000000..1a1385f
--- /dev/null
+++ b/components/offline_pages/core/client_namespace_constants.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/client_namespace_constants.h"
+
+namespace offline_pages {
+
+const char kBookmarkNamespace[] = "bookmark";
+const char kLastNNamespace[] = "last_n";
+const char kAsyncNamespace[] = "async_loading";
+const char kCCTNamespace[] = "custom_tabs";
+const char kDownloadNamespace[] = "download";
+const char kNTPSuggestionsNamespace[] = "ntp_suggestions";
+
+const char kDefaultNamespace[] = "default";
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/client_namespace_constants.h b/components/offline_pages/core/client_namespace_constants.h
new file mode 100644
index 0000000..7991a51
--- /dev/null
+++ b/components/offline_pages/core/client_namespace_constants.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
+
+#include "build/build_config.h"
+
+namespace offline_pages {
+
+// Any changes to these well-known namespaces should also be reflected in
+// OfflinePagesNamespace (histograms.xml) for consistency.
+extern const char kBookmarkNamespace[];
+extern const char kLastNNamespace[];
+extern const char kAsyncNamespace[];
+extern const char kCCTNamespace[];
+extern const char kDownloadNamespace[];
+extern const char kNTPSuggestionsNamespace[];
+
+// Currently used for fallbacks like tests.
+extern const char kDefaultNamespace[];
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_NAMESPACE_CONSTANTS_H_
diff --git a/components/offline_pages/core/client_policy_controller.cc b/components/offline_pages/core/client_policy_controller.cc
new file mode 100644
index 0000000..c2db051
--- /dev/null
+++ b/components/offline_pages/core/client_policy_controller.cc
@@ -0,0 +1,163 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/client_policy_controller.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+
+using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
+
+namespace offline_pages {
+
+ClientPolicyController::ClientPolicyController() {
+  policies_.clear();
+  // Manually defining client policies for bookmark and last_n.
+  policies_.insert(std::make_pair(
+      kBookmarkNamespace,
+      MakePolicy(kBookmarkNamespace, LifetimeType::TEMPORARY,
+                 base::TimeDelta::FromDays(7), kUnlimitedPages, 1)));
+  policies_.insert(std::make_pair(
+      kLastNNamespace,
+      OfflinePageClientPolicyBuilder(kLastNNamespace, LifetimeType::TEMPORARY,
+                                     kUnlimitedPages, kUnlimitedPages)
+          .SetExpirePeriod(base::TimeDelta::FromDays(2))
+          .SetIsSupportedByRecentTabs(true)
+          .SetIsOnlyShownInOriginalTab(true)
+          .Build()));
+  policies_.insert(std::make_pair(
+      kAsyncNamespace,
+      OfflinePageClientPolicyBuilder(kAsyncNamespace, LifetimeType::PERSISTENT,
+                                     kUnlimitedPages, kUnlimitedPages)
+          .SetIsSupportedByDownload(true)
+          .SetIsRemovedOnCacheReset(false)
+          .Build()));
+  policies_.insert(std::make_pair(
+      kCCTNamespace,
+      MakePolicy(kCCTNamespace, LifetimeType::TEMPORARY,
+                 base::TimeDelta::FromDays(2), kUnlimitedPages, 1)));
+  policies_.insert(std::make_pair(
+      kDownloadNamespace, OfflinePageClientPolicyBuilder(
+                              kDownloadNamespace, LifetimeType::PERSISTENT,
+                              kUnlimitedPages, kUnlimitedPages)
+                              .SetIsRemovedOnCacheReset(false)
+                              .SetIsSupportedByDownload(true)
+                              .Build()));
+  policies_.insert(std::make_pair(
+      kNTPSuggestionsNamespace,
+      OfflinePageClientPolicyBuilder(kNTPSuggestionsNamespace,
+                                     LifetimeType::PERSISTENT, kUnlimitedPages,
+                                     kUnlimitedPages)
+          .SetIsSupportedByDownload(true)
+          .Build()));
+
+  // Fallback policy.
+  policies_.insert(std::make_pair(
+      kDefaultNamespace, MakePolicy(kDefaultNamespace, LifetimeType::TEMPORARY,
+                                    base::TimeDelta::FromDays(1), 10, 1)));
+}
+
+ClientPolicyController::~ClientPolicyController() {}
+
+// static
+const OfflinePageClientPolicy ClientPolicyController::MakePolicy(
+    const std::string& name_space,
+    LifetimeType lifetime_type,
+    const base::TimeDelta& expire_period,
+    size_t page_limit,
+    size_t pages_allowed_per_url) {
+  return OfflinePageClientPolicyBuilder(name_space, lifetime_type, page_limit,
+                                        pages_allowed_per_url)
+      .SetExpirePeriod(expire_period)
+      .Build();
+}
+
+const OfflinePageClientPolicy& ClientPolicyController::GetPolicy(
+    const std::string& name_space) const {
+  const auto& iter = policies_.find(name_space);
+  if (iter != policies_.end())
+    return iter->second;
+  // Fallback when the namespace isn't defined.
+  return policies_.at(kDefaultNamespace);
+}
+
+std::vector<std::string> ClientPolicyController::GetAllNamespaces() const {
+  std::vector<std::string> result;
+  for (const auto& policy_item : policies_)
+    result.emplace_back(policy_item.first);
+
+  return result;
+}
+
+bool ClientPolicyController::IsRemovedOnCacheReset(
+    const std::string& name_space) const {
+  return GetPolicy(name_space).feature_policy.is_removed_on_cache_reset;
+}
+
+bool ClientPolicyController::IsSupportedByDownload(
+    const std::string& name_space) const {
+  return GetPolicy(name_space).feature_policy.is_supported_by_download;
+}
+
+const std::vector<std::string>&
+ClientPolicyController::GetNamespacesSupportedByDownload() const {
+  if (download_namespace_cache_)
+    return *download_namespace_cache_;
+
+  download_namespace_cache_ = base::MakeUnique<std::vector<std::string>>();
+  for (const auto& policy_item : policies_) {
+    if (policy_item.second.feature_policy.is_supported_by_download)
+      download_namespace_cache_->emplace_back(policy_item.first);
+  }
+  return *download_namespace_cache_;
+}
+
+bool ClientPolicyController::IsShownAsRecentlyVisitedSite(
+    const std::string& name_space) const {
+  return GetPolicy(name_space).feature_policy.is_supported_by_recent_tabs;
+}
+
+const std::vector<std::string>&
+ClientPolicyController::GetNamespacesShownAsRecentlyVisitedSite() const {
+  if (recent_tab_namespace_cache_)
+    return *recent_tab_namespace_cache_;
+
+  recent_tab_namespace_cache_ = base::MakeUnique<std::vector<std::string>>();
+  for (const auto& policy_item : policies_) {
+    if (policy_item.second.feature_policy.is_supported_by_recent_tabs)
+      recent_tab_namespace_cache_->emplace_back(policy_item.first);
+  }
+
+  return *recent_tab_namespace_cache_;
+}
+
+bool ClientPolicyController::IsRestrictedToOriginalTab(
+    const std::string& name_space) const {
+  return GetPolicy(name_space).feature_policy.only_shown_in_original_tab;
+}
+
+const std::vector<std::string>&
+ClientPolicyController::GetNamespacesRestrictedToOriginalTab() const {
+  if (show_in_original_tab_cache_)
+    return *show_in_original_tab_cache_;
+
+  show_in_original_tab_cache_ = base::MakeUnique<std::vector<std::string>>();
+  for (const auto& policy_item : policies_) {
+    if (policy_item.second.feature_policy.only_shown_in_original_tab)
+      show_in_original_tab_cache_->emplace_back(policy_item.first);
+  }
+
+  return *show_in_original_tab_cache_;
+}
+
+void ClientPolicyController::AddPolicyForTest(
+    const std::string& name_space,
+    const OfflinePageClientPolicyBuilder& builder) {
+  policies_.insert(std::make_pair(name_space, builder.Build()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/client_policy_controller.h b/components/offline_pages/core/client_policy_controller.h
new file mode 100644
index 0000000..5e5cd214
--- /dev/null
+++ b/components/offline_pages/core/client_policy_controller.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
+
+namespace offline_pages {
+
+// This is the class which is a singleton for offline page model
+// to get client policies based on namespaces.
+class ClientPolicyController {
+ public:
+  ClientPolicyController();
+  ~ClientPolicyController();
+
+  // Generates a client policy from the input values.
+  static const OfflinePageClientPolicy MakePolicy(
+      const std::string& name_space,
+      LifetimePolicy::LifetimeType lifetime_type,
+      const base::TimeDelta& expiration_period,
+      size_t page_limit,
+      size_t pages_allowed_per_url);
+
+  // Get the client policy for |name_space|.
+  const OfflinePageClientPolicy& GetPolicy(const std::string& name_space) const;
+
+  // Returns a list of all known namespaces.
+  std::vector<std::string> GetAllNamespaces() const;
+
+  // Returns whether pages for |name_space| should be removed on cache reset.
+  bool IsRemovedOnCacheReset(const std::string& name_space) const;
+
+  // Returns whether pages for |name_space| are shown in Download UI.
+  bool IsSupportedByDownload(const std::string& name_space) const;
+  const std::vector<std::string>& GetNamespacesSupportedByDownload() const;
+
+  // Returns whether pages for |name_space| are shown in recent tabs UI,
+  // currently only available on NTP.
+  bool IsShownAsRecentlyVisitedSite(const std::string& name_space) const;
+  const std::vector<std::string>& GetNamespacesShownAsRecentlyVisitedSite()
+      const;
+
+  // Returns whether pages for |name_space| should never be shown outside the
+  // tab they were generated in.
+  bool IsRestrictedToOriginalTab(const std::string& name_space) const;
+  const std::vector<std::string>& GetNamespacesRestrictedToOriginalTab() const;
+
+  void AddPolicyForTest(const std::string& name_space,
+                        const OfflinePageClientPolicyBuilder& builder);
+
+ private:
+  // The map from name_space to a client policy. Will be generated
+  // as pre-defined values for now.
+  std::map<std::string, OfflinePageClientPolicy> policies_;
+
+  // Memoizing results.
+  mutable std::unique_ptr<std::vector<std::string>> download_namespace_cache_;
+  mutable std::unique_ptr<std::vector<std::string>> recent_tab_namespace_cache_;
+  mutable std::unique_ptr<std::vector<std::string>> show_in_original_tab_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientPolicyController);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_CLIENT_POLICY_CONTROLLER_H_
diff --git a/components/offline_pages/core/client_policy_controller_unittest.cc b/components/offline_pages/core/client_policy_controller_unittest.cc
new file mode 100644
index 0000000..e3721a8c
--- /dev/null
+++ b/components/offline_pages/core/client_policy_controller_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/client_policy_controller.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
+
+namespace offline_pages {
+
+namespace {
+const char kUndefinedNamespace[] = "undefined";
+
+bool isTemporary(const OfflinePageClientPolicy& policy) {
+  return policy.lifetime_policy.lifetime_type == LifetimeType::TEMPORARY;
+}
+}  // namespace
+
+class ClientPolicyControllerTest : public testing::Test {
+ public:
+  ClientPolicyController* controller() { return controller_.get(); }
+
+  // testing::Test
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  void ExpectDownloadSupport(std::string name_space, bool expectation);
+  void ExpectRecentTab(std::string name_space, bool expectation);
+  void ExpectOnlyOriginalTab(std::string name_space, bool expectation);
+
+ private:
+  std::unique_ptr<ClientPolicyController> controller_;
+};
+
+void ClientPolicyControllerTest::SetUp() {
+  controller_.reset(new ClientPolicyController());
+}
+
+void ClientPolicyControllerTest::TearDown() {
+  controller_.reset();
+}
+
+void ClientPolicyControllerTest::ExpectDownloadSupport(std::string name_space,
+                                                       bool expectation) {
+  std::vector<std::string> cache =
+      controller()->GetNamespacesSupportedByDownload();
+  auto result = std::find(cache.begin(), cache.end(), name_space);
+  EXPECT_EQ(expectation, result != cache.end());
+  EXPECT_EQ(expectation, controller()->IsSupportedByDownload(name_space));
+}
+
+void ClientPolicyControllerTest::ExpectRecentTab(std::string name_space,
+                                                 bool expectation) {
+  std::vector<std::string> cache =
+      controller()->GetNamespacesShownAsRecentlyVisitedSite();
+  auto result = std::find(cache.begin(), cache.end(), name_space);
+  EXPECT_EQ(expectation, result != cache.end());
+  EXPECT_EQ(expectation,
+            controller()->IsShownAsRecentlyVisitedSite(name_space));
+}
+
+void ClientPolicyControllerTest::ExpectOnlyOriginalTab(std::string name_space,
+                                                       bool expectation) {
+  std::vector<std::string> cache =
+      controller()->GetNamespacesRestrictedToOriginalTab();
+  auto result = std::find(cache.begin(), cache.end(), name_space);
+  EXPECT_EQ(expectation, result != cache.end());
+  EXPECT_EQ(expectation, controller()->IsRestrictedToOriginalTab(name_space));
+}
+
+TEST_F(ClientPolicyControllerTest, FallbackTest) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kUndefinedNamespace);
+  EXPECT_EQ(policy.name_space, kDefaultNamespace);
+  EXPECT_TRUE(isTemporary(policy));
+  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kUndefinedNamespace));
+  ExpectDownloadSupport(kUndefinedNamespace, false);
+  ExpectRecentTab(kUndefinedNamespace, false);
+  ExpectOnlyOriginalTab(kUndefinedNamespace, false);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckBookmarkDefined) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kBookmarkNamespace);
+  EXPECT_EQ(policy.name_space, kBookmarkNamespace);
+  EXPECT_TRUE(isTemporary(policy));
+  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kBookmarkNamespace));
+  ExpectDownloadSupport(kBookmarkNamespace, false);
+  ExpectRecentTab(kBookmarkNamespace, false);
+  ExpectOnlyOriginalTab(kBookmarkNamespace, false);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckLastNDefined) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kLastNNamespace);
+  EXPECT_EQ(policy.name_space, kLastNNamespace);
+  EXPECT_TRUE(isTemporary(policy));
+  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kLastNNamespace));
+  ExpectDownloadSupport(kLastNNamespace, false);
+  ExpectRecentTab(kLastNNamespace, true);
+  ExpectOnlyOriginalTab(kLastNNamespace, true);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckAsyncDefined) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kAsyncNamespace);
+  EXPECT_EQ(policy.name_space, kAsyncNamespace);
+  EXPECT_FALSE(isTemporary(policy));
+  EXPECT_FALSE(controller()->IsRemovedOnCacheReset(kAsyncNamespace));
+  ExpectDownloadSupport(kAsyncNamespace, true);
+  ExpectRecentTab(kAsyncNamespace, false);
+  ExpectOnlyOriginalTab(kAsyncNamespace, false);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckCCTDefined) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kCCTNamespace);
+  EXPECT_EQ(policy.name_space, kCCTNamespace);
+  EXPECT_TRUE(isTemporary(policy));
+  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kCCTNamespace));
+  ExpectDownloadSupport(kCCTNamespace, false);
+  ExpectRecentTab(kCCTNamespace, false);
+  ExpectOnlyOriginalTab(kCCTNamespace, false);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckDownloadDefined) {
+  OfflinePageClientPolicy policy = controller()->GetPolicy(kDownloadNamespace);
+  EXPECT_EQ(policy.name_space, kDownloadNamespace);
+  EXPECT_FALSE(isTemporary(policy));
+  EXPECT_FALSE(controller()->IsRemovedOnCacheReset(kDownloadNamespace));
+  ExpectDownloadSupport(kDownloadNamespace, true);
+  ExpectRecentTab(kDownloadNamespace, false);
+  ExpectOnlyOriginalTab(kDownloadNamespace, false);
+}
+
+TEST_F(ClientPolicyControllerTest, CheckNTPSuggestionsDefined) {
+  OfflinePageClientPolicy policy =
+      controller()->GetPolicy(kNTPSuggestionsNamespace);
+  EXPECT_EQ(policy.name_space, kNTPSuggestionsNamespace);
+  EXPECT_FALSE(isTemporary(policy));
+  EXPECT_TRUE(controller()->IsRemovedOnCacheReset(kNTPSuggestionsNamespace));
+  ExpectDownloadSupport(kNTPSuggestionsNamespace, true);
+  ExpectRecentTab(kNTPSuggestionsNamespace, false);
+  ExpectOnlyOriginalTab(kNTPSuggestionsNamespace, false);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/BUILD.gn b/components/offline_pages/core/downloads/BUILD.gn
new file mode 100644
index 0000000..ad8ea055
--- /dev/null
+++ b/components/offline_pages/core/downloads/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+static_library("offline_pages_ui_adapter") {
+  sources = [
+    "download_notifying_observer.cc",
+    "download_notifying_observer.h",
+    "download_ui_adapter.cc",
+    "download_ui_adapter.h",
+    "download_ui_item.cc",
+    "download_ui_item.h",
+    "offline_page_download_notifier.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core/background:background_offliner",
+    "//url",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "download_notifying_observer_unittest.cc",
+    "download_ui_adapter_unittest.cc",
+  ]
+
+  deps = [
+    ":offline_pages_ui_adapter",
+    "//base",
+    "//base/test:test_support",
+    "//components/offline_pages/core",
+    "//components/offline_pages/core:test_support",
+    "//components/offline_pages/core/background:background_offliner",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/offline_pages/core/downloads/download_notifying_observer.cc b/components/offline_pages/core/downloads/download_notifying_observer.cc
new file mode 100644
index 0000000..cca7406a
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_notifying_observer.cc
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
+
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
+#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
+
+namespace offline_pages {
+namespace {
+int kUserDataKey;  // Only address is used.
+}  // namespace
+
+DownloadNotifyingObserver::DownloadNotifyingObserver(
+    std::unique_ptr<OfflinePageDownloadNotifier> notifier,
+    ClientPolicyController* policy_controller)
+    : notifier_(std::move(notifier)), policy_controller_(policy_controller) {}
+
+DownloadNotifyingObserver::~DownloadNotifyingObserver() {}
+
+// static
+DownloadNotifyingObserver* DownloadNotifyingObserver::GetFromRequestCoordinator(
+    RequestCoordinator* request_coordinator) {
+  DCHECK(request_coordinator);
+  return static_cast<DownloadNotifyingObserver*>(
+      request_coordinator->GetUserData(&kUserDataKey));
+}
+
+// static
+void DownloadNotifyingObserver::CreateAndStartObserving(
+    RequestCoordinator* request_coordinator,
+    std::unique_ptr<OfflinePageDownloadNotifier> notifier) {
+  DCHECK(request_coordinator);
+  DCHECK(notifier.get());
+  DownloadNotifyingObserver* observer = new DownloadNotifyingObserver(
+      std::move(notifier), request_coordinator->GetPolicyController());
+  request_coordinator->AddObserver(observer);
+  // |request_coordinator| takes ownership of observer here.
+  request_coordinator->SetUserData(&kUserDataKey, observer);
+}
+
+void DownloadNotifyingObserver::OnAdded(const SavePageRequest& request) {
+  DCHECK(notifier_.get());
+  if (!IsVisibleInUI(request.client_id()))
+    return;
+
+  // Calling Progress ensures notification is created in lieu of specific
+  // Add/Create call.
+  notifier_->NotifyDownloadProgress(DownloadUIItem(request));
+
+  // Now we need to update the notification if it is not active/offlining.
+  if (request.request_state() != SavePageRequest::RequestState::OFFLINING)
+    NotifyRequestStateChange(request);
+}
+
+void DownloadNotifyingObserver::OnChanged(const SavePageRequest& request) {
+  DCHECK(notifier_.get());
+  if (!IsVisibleInUI(request.client_id()))
+    return;
+  NotifyRequestStateChange(request);
+}
+
+void DownloadNotifyingObserver::OnCompleted(
+    const SavePageRequest& request,
+    RequestCoordinator::BackgroundSavePageResult status) {
+  DCHECK(notifier_.get());
+  if (!IsVisibleInUI(request.client_id()))
+    return;
+  if (status == RequestCoordinator::BackgroundSavePageResult::SUCCESS)
+    notifier_->NotifyDownloadSuccessful(DownloadUIItem(request));
+  else if (status == RequestCoordinator::BackgroundSavePageResult::REMOVED)
+    notifier_->NotifyDownloadCanceled(DownloadUIItem(request));
+  else
+    notifier_->NotifyDownloadFailed(DownloadUIItem(request));
+}
+
+bool DownloadNotifyingObserver::IsVisibleInUI(const ClientId& page) {
+  return policy_controller_->IsSupportedByDownload(page.name_space) &&
+         base::IsValidGUID(page.id);
+}
+
+// Calls the appropriate notifier method depending upon the state of the
+// request. For example, an AVAILABLE request is not active (aka, pending)
+// which the notifier understands as an Interrupted operation vs. one that
+// has Progress or is Paused.
+void DownloadNotifyingObserver::NotifyRequestStateChange(
+    const SavePageRequest& request) {
+  if (request.request_state() == SavePageRequest::RequestState::PAUSED)
+    notifier_->NotifyDownloadPaused(DownloadUIItem(request));
+  else if (request.request_state() == SavePageRequest::RequestState::AVAILABLE)
+    notifier_->NotifyDownloadInterrupted(DownloadUIItem(request));
+  else
+    notifier_->NotifyDownloadProgress(DownloadUIItem(request));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_notifying_observer.h b/components/offline_pages/core/downloads/download_notifying_observer.h
new file mode 100644
index 0000000..6980102
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_notifying_observer.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
+
+#include <memory>
+
+#include "base/guid.h"
+#include "base/macros.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+
+namespace offline_pages {
+
+struct ClientId;
+struct OfflinePageDownloadNotifier;
+class ClientPolicyController;
+class SavePageRequest;
+
+// Class observing the save page requests and issuing corresponding user
+// notifications as requests are added or updated.
+class DownloadNotifyingObserver : public RequestCoordinator::Observer,
+                                  public base::SupportsUserData::Data {
+ public:
+  ~DownloadNotifyingObserver() override;
+
+  static DownloadNotifyingObserver* GetFromRequestCoordinator(
+      RequestCoordinator* request_coordinator);
+  static void CreateAndStartObserving(
+      RequestCoordinator* request_coordinator,
+      std::unique_ptr<OfflinePageDownloadNotifier> notifier);
+
+  // RequestCoordinator::Observer implementation:
+  void OnAdded(const SavePageRequest& request) override;
+  void OnChanged(const SavePageRequest& request) override;
+  void OnCompleted(
+      const SavePageRequest& request,
+      RequestCoordinator::BackgroundSavePageResult status) override;
+
+ private:
+  friend class DownloadNotifyingObserverTest;
+
+  DownloadNotifyingObserver(
+      std::unique_ptr<OfflinePageDownloadNotifier> notifier,
+      ClientPolicyController* policy_controller);
+
+  bool IsVisibleInUI(const ClientId& id);
+
+  void NotifyRequestStateChange(const SavePageRequest& request);
+
+  // Used to issue notifications related to save page requests.
+  std::unique_ptr<OfflinePageDownloadNotifier> notifier_;
+  // Used to determine policy-related permissions. Not owned.
+  ClientPolicyController* policy_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadNotifyingObserver);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
diff --git a/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc b/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
new file mode 100644
index 0000000..75d1f89
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
@@ -0,0 +1,284 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/downloads/download_notifying_observer.h"
+
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+static const int64_t kTestOfflineId = 42L;
+static const char kTestUrl[] = "http://foo.com/bar";
+static const char kTestGuid[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
+static const ClientId kTestClientId(kDownloadNamespace, kTestGuid);
+static const base::Time kTestCreationTime = base::Time::Now();
+static const bool kTestUserRequested = true;
+
+enum class LastNotificationType {
+  NONE,
+  DOWNLOAD_SUCCESSFUL,
+  DOWNLOAD_FAILED,
+  DOWNLOAD_PROGRESS,
+  DOWNLOAD_PAUSED,
+  DOWNLOAD_INTERRUPTED,
+  DOWNLOAD_CANCELED,
+};
+
+class TestNotifier : public OfflinePageDownloadNotifier {
+ public:
+  TestNotifier();
+  ~TestNotifier() override;
+
+  // OfflinePageDownloadNotifier implementation:
+  void NotifyDownloadSuccessful(const DownloadUIItem& item) override;
+  void NotifyDownloadFailed(const DownloadUIItem& item) override;
+  void NotifyDownloadProgress(const DownloadUIItem& item) override;
+  void NotifyDownloadPaused(const DownloadUIItem& item) override;
+  void NotifyDownloadInterrupted(const DownloadUIItem& item) override;
+  void NotifyDownloadCanceled(const DownloadUIItem& item) override;
+
+  void Reset();
+
+  LastNotificationType last_notification_type() const {
+    return last_notification_type_;
+  }
+
+  DownloadUIItem* download_item() const { return download_item_.get(); }
+
+ private:
+  LastNotificationType last_notification_type_;
+  std::unique_ptr<DownloadUIItem> download_item_;
+};
+
+TestNotifier::TestNotifier()
+    : last_notification_type_(LastNotificationType::NONE) {}
+
+TestNotifier::~TestNotifier() {}
+
+void TestNotifier::NotifyDownloadSuccessful(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_SUCCESSFUL;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::NotifyDownloadFailed(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_FAILED;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::NotifyDownloadProgress(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_PROGRESS;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::NotifyDownloadPaused(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_PAUSED;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::NotifyDownloadInterrupted(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_INTERRUPTED;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::NotifyDownloadCanceled(const DownloadUIItem& item) {
+  last_notification_type_ = LastNotificationType::DOWNLOAD_CANCELED;
+  download_item_.reset(new DownloadUIItem(item));
+}
+
+void TestNotifier::Reset() {
+  last_notification_type_ = LastNotificationType::NONE;
+  download_item_.reset(nullptr);
+}
+
+}  // namespace
+
+class DownloadNotifyingObserverTest : public testing::Test {
+ public:
+  DownloadNotifyingObserverTest();
+  ~DownloadNotifyingObserverTest() override;
+
+  // testing::Test implementation:
+  void SetUp() override;
+
+  TestNotifier* notifier() const { return notifier_; }
+  DownloadNotifyingObserver* observer() { return observer_.get(); }
+
+ private:
+  TestNotifier* notifier_;
+  std::unique_ptr<DownloadNotifyingObserver> observer_;
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+};
+
+DownloadNotifyingObserverTest::DownloadNotifyingObserverTest() {}
+
+DownloadNotifyingObserverTest::~DownloadNotifyingObserverTest() {}
+
+void DownloadNotifyingObserverTest::SetUp() {
+  std::unique_ptr<TestNotifier> notifier(new TestNotifier);
+  policy_controller_.reset(new ClientPolicyController());
+  notifier_ = notifier.get();
+  observer_.reset(new DownloadNotifyingObserver(std::move(notifier),
+                                                policy_controller_.get()));
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnAddedAsAvailable) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
+  observer()->OnAdded(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnAddedAsOffling) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::OFFLINING);
+  observer()->OnAdded(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnAddedAsPaused) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::PAUSED);
+  observer()->OnAdded(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnChangedToPaused) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::PAUSED);
+  observer()->OnChanged(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnChangedToAvailable) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
+  observer()->OnChanged(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnChangedToOfflining) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  request.set_request_state(SavePageRequest::RequestState::OFFLINING);
+  observer()->OnChanged(request);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnCompletedSuccess) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  observer()->OnCompleted(request,
+                          RequestNotifier::BackgroundSavePageResult::SUCCESS);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_SUCCESSFUL,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnCompletedCanceled) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  observer()->OnCompleted(request,
+                          RequestNotifier::BackgroundSavePageResult::REMOVED);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_CANCELED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, OnCompletedFailure) {
+  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
+                          kTestCreationTime, kTestUserRequested);
+  observer()->OnCompleted(
+      request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+
+  notifier()->Reset();
+  observer()->OnCompleted(
+      request, RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+
+  notifier()->Reset();
+  observer()->OnCompleted(
+      request, RequestNotifier::BackgroundSavePageResult::SAVE_FAILED);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+
+  notifier()->Reset();
+  observer()->OnCompleted(request,
+                          RequestNotifier::BackgroundSavePageResult::EXPIRED);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+
+  notifier()->Reset();
+  observer()->OnCompleted(
+      request, RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
+  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
+            notifier()->last_notification_type());
+  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
+  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
+  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
+}
+
+TEST_F(DownloadNotifyingObserverTest, NamespacesNotVisibleInUI) {
+  std::vector<std::string> name_spaces = {kBookmarkNamespace, kLastNNamespace,
+                                          kCCTNamespace, kDefaultNamespace};
+
+  for (auto name_space : name_spaces) {
+    ClientId invisible_client_id(name_space, kTestGuid);
+    SavePageRequest request(kTestOfflineId, GURL(kTestUrl), invisible_client_id,
+                            kTestCreationTime, kTestUserRequested);
+    observer()->OnAdded(request);
+    EXPECT_EQ(LastNotificationType::NONE, notifier()->last_notification_type());
+  }
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.cc b/components/offline_pages/core/downloads/download_ui_adapter.cc
new file mode 100644
index 0000000..053dce7d
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -0,0 +1,214 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+
+namespace offline_pages {
+
+namespace {
+const char kDownloadUIAdapterKey[] = "download-ui-adapter";
+}
+
+DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page)
+    : ui_item(base::MakeUnique<DownloadUIItem>(page)),
+      offline_id(page.offline_id) {}
+
+DownloadUIAdapter::ItemInfo::~ItemInfo() {}
+
+DownloadUIAdapter::DownloadUIAdapter(OfflinePageModel* model)
+    : model_(model),
+      state_(State::NOT_LOADED),
+      observers_count_(0),
+      weak_ptr_factory_(this) {}
+
+DownloadUIAdapter::~DownloadUIAdapter() {}
+
+// static
+DownloadUIAdapter* DownloadUIAdapter::FromOfflinePageModel(
+    OfflinePageModel* offline_page_model) {
+  DownloadUIAdapter* adapter = static_cast<DownloadUIAdapter*>(
+      offline_page_model->GetUserData(kDownloadUIAdapterKey));
+  if (!adapter) {
+    adapter = new DownloadUIAdapter(offline_page_model);
+    offline_page_model->SetUserData(kDownloadUIAdapterKey, adapter);
+  }
+  return adapter;
+}
+
+void DownloadUIAdapter::AddObserver(Observer* observer) {
+  DCHECK(observer);
+  if (observers_.HasObserver(observer))
+    return;
+  if (observers_count_ == 0)
+    LoadCache();
+  observers_.AddObserver(observer);
+  ++observers_count_;
+  // If the items are already loaded, post the notification right away.
+  // Don't just invoke it from here to avoid reentrancy in the client.
+  if (state_ == State::LOADED) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&DownloadUIAdapter::NotifyItemsLoaded,
+                   weak_ptr_factory_.GetWeakPtr(), base::Unretained(observer)));
+  }
+}
+
+void DownloadUIAdapter::RemoveObserver(Observer* observer) {
+  DCHECK(observer);
+  if (!observers_.HasObserver(observer))
+    return;
+  observers_.RemoveObserver(observer);
+  --observers_count_;
+  // Once the last observer is gone, clear cached data.
+  if (observers_count_ == 0)
+    ClearCache();
+}
+
+void DownloadUIAdapter::OfflinePageModelLoaded(OfflinePageModel* model) {
+  // This signal is not used here.
+}
+
+void DownloadUIAdapter::OfflinePageAdded(OfflinePageModel* model,
+                                         const OfflinePageItem& added_page) {
+  DCHECK(model == model_);
+  model_->GetAllPages(base::Bind(&DownloadUIAdapter::OnOfflinePagesChanged,
+                                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DownloadUIAdapter::OfflinePageDeleted(int64_t offline_id,
+                                           const ClientId& client_id) {
+  if (!IsVisibleInUI(client_id))
+    return;
+  std::string guid = client_id.id;
+  DownloadUIItems::const_iterator it = items_.find(guid);
+  if (it == items_.end())
+    return;
+  items_.erase(it);
+  for (Observer& observer : observers_)
+    observer.ItemDeleted(guid);
+}
+
+std::vector<const DownloadUIItem*> DownloadUIAdapter::GetAllItems() const {
+  std::vector<const DownloadUIItem*> result;
+  for (const auto& item : items_)
+    result.push_back(item.second->ui_item.get());
+  return result;
+}
+
+const DownloadUIItem* DownloadUIAdapter::GetItem(
+    const std::string& guid) const {
+  DownloadUIItems::const_iterator it = items_.find(guid);
+  if (it == items_.end())
+    return nullptr;
+  return it->second->ui_item.get();
+}
+
+void DownloadUIAdapter::DeleteItem(const std::string& guid) {
+  // TODO(dimich): Also remove pending request from RequestQueue.
+  DownloadUIItems::const_iterator it = items_.find(guid);
+  if (it == items_.end())
+    return;
+
+  std::vector<int64_t> page_ids;
+  page_ids.push_back(it->second->offline_id);
+  model_->DeletePagesByOfflineId(
+      page_ids, base::Bind(&DownloadUIAdapter::OnDeletePagesDone,
+                           weak_ptr_factory_.GetWeakPtr()));
+}
+
+int64_t DownloadUIAdapter::GetOfflineIdByGuid(const std::string& guid) const {
+  // TODO(dimich): when requests are also in the cache, filter them out.
+  // Requests do not yet have offline ID.
+  DownloadUIItems::const_iterator it = items_.find(guid);
+  if (it != items_.end())
+    return it->second->offline_id;
+  return 0;
+}
+
+// Note that several LoadCache calls may be issued before the async GetAllPages
+// comes back.
+void DownloadUIAdapter::LoadCache() {
+  // TODO(dimich): Add fetching from RequestQueue as well.
+  state_ = State::LOADING;
+  model_->GetAllPages(base::Bind(&DownloadUIAdapter::OnOfflinePagesLoaded,
+                                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DownloadUIAdapter::ClearCache() {
+  // Once loaded, this class starts to observe the model. Only remove observer
+  // if it was added.
+  if (state_ == State::LOADED)
+    model_->RemoveObserver(this);
+  items_.clear();
+  state_ = State::NOT_LOADED;
+}
+
+void DownloadUIAdapter::OnOfflinePagesLoaded(
+    const MultipleOfflinePageItemResult& pages) {
+  // If multiple observers register quickly, the cache might be already loaded
+  // by the previous LoadCache call. At the same time, if all observers already
+  // left, there is no reason to populate the cache.
+  if (state_ != State::LOADING)
+    return;
+  for (const auto& page : pages) {
+    if (IsVisibleInUI(page.client_id)) {
+      std::string guid = page.client_id.id;
+      DCHECK(items_.find(guid) == items_.end());
+      items_[guid] = base::MakeUnique<ItemInfo>(page);
+    }
+  }
+  model_->AddObserver(this);
+  state_ = State::LOADED;
+  for (Observer& observer : observers_)
+    observer.ItemsLoaded();
+}
+
+void DownloadUIAdapter::NotifyItemsLoaded(Observer* observer) {
+  if (observer && observers_.HasObserver(observer))
+    observer->ItemsLoaded();
+}
+
+// This method is only called by OPM when a single item added.
+// TODO(dimich): change OPM to have real OnPageAdded/OnPageUpdated and
+// simplify this code.
+void DownloadUIAdapter::OnOfflinePagesChanged(
+    const MultipleOfflinePageItemResult& pages) {
+  std::vector<std::string> added_guids;
+  for (const auto& page : pages) {
+    if (!IsVisibleInUI(page.client_id))  // Item should be filtered out.
+      continue;
+    const std::string& guid = page.client_id.id;
+    if (items_.find(guid) != items_.end())  // Item already exists.
+      continue;
+    items_[guid] = base::MakeUnique<ItemInfo>(page);
+    added_guids.push_back(guid);
+  }
+  for (auto& guid : added_guids) {
+    const DownloadUIItem& item = *(items_.find(guid)->second->ui_item.get());
+    for (Observer& observer : observers_)
+      observer.ItemAdded(item);
+  }
+}
+
+void DownloadUIAdapter::OnDeletePagesDone(DeletePageResult result) {
+  // TODO(dimich): Consider adding UMA to record user actions.
+}
+
+bool DownloadUIAdapter::IsVisibleInUI(const ClientId& client_id) {
+  const std::string& name_space = client_id.name_space;
+  return model_->GetPolicyController()->IsSupportedByDownload(name_space) &&
+         base::IsValidGUID(client_id.id);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.h b/components/offline_pages/core/downloads/download_ui_adapter.h
new file mode 100644
index 0000000..2c953a2
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "base/supports_user_data.h"
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_types.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+// C++ side of the UI Adapter. Mimics DownloadManager/Item/History (since we
+// share UI with Downloads).
+// An instance of this class is owned by OfflinePageModel and is shared between
+// UI components if needed. It manages the cache of DownloadUIItems, so after
+// initial load the UI components can synchronously pull the whoel list or any
+// item by its guid.
+class DownloadUIAdapter : public OfflinePageModel::Observer,
+                          public base::SupportsUserData::Data {
+ public:
+  // Observer, normally implemented by UI or a Bridge.
+  class Observer {
+   public:
+    // Invoked when UI items are loaded. GetAllItems/GetItem can now be used.
+    // Must be listened for in order to start getting the items.
+    // If the items are already loaded by the time observer is added, this
+    // callback will be posted right away.
+    virtual void ItemsLoaded() = 0;
+
+    // Invoked when the UI Item was added, usually as a request to download.
+    virtual void ItemAdded(const DownloadUIItem& item) = 0;
+
+    // Invoked when the UI Item was updated. Only guid of the item is guaranteed
+    // to survive the update, all other fields can change.
+    virtual void ItemUpdated(const DownloadUIItem& item) = 0;
+
+    // Invoked when the UI Item was deleted. At this point, only guid remains.
+    virtual void ItemDeleted(const std::string& guid) = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  explicit DownloadUIAdapter(OfflinePageModel* model);
+  ~DownloadUIAdapter() override;
+
+  static DownloadUIAdapter* FromOfflinePageModel(
+      OfflinePageModel* offline_page_model);
+
+  // Checks a client ID for proper namespace and ID format to be shown in the
+  // Downloads Home UI.
+  bool IsVisibleInUI(const ClientId& page);
+
+  // This adapter is potentially shared by UI elements, each of which adds
+  // itself as an observer.
+  // When the last observer si removed, cached list of items is destroyed and
+  // next time the initial loading will take longer.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Returns all UI items. The list contains references to items in the cache
+  // and has to be used synchronously.
+  std::vector<const DownloadUIItem*> GetAllItems() const;
+  // May return nullptr if item with specified guid does not exist.
+  const DownloadUIItem* GetItem(const std::string& guid) const;
+
+  // Commands from UI. Start async operations, result is observable
+  // via Observer or directly by the user (as in 'open').
+  void DeleteItem(const std::string& guid);
+  int64_t GetOfflineIdByGuid(const std::string& guid) const;
+
+  // OfflinePageModel::Observer
+  void OfflinePageModelLoaded(OfflinePageModel* model) override;
+  void OfflinePageAdded(OfflinePageModel* model,
+                        const OfflinePageItem& added_page) override;
+  void OfflinePageDeleted(int64_t offline_id,
+                          const ClientId& client_id) override;
+
+ private:
+  enum class State { NOT_LOADED, LOADING, LOADED };
+
+  struct ItemInfo {
+    explicit ItemInfo(const OfflinePageItem& page);
+    ~ItemInfo();
+
+    std::unique_ptr<DownloadUIItem> ui_item;
+    // Additional cached data, not exposed to UI through DownloadUIItem.
+    int64_t offline_id;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ItemInfo);
+  };
+
+  typedef std::map<std::string, std::unique_ptr<ItemInfo>> DownloadUIItems;
+
+  void LoadCache();
+  void ClearCache();
+
+  // Task callbacks.
+  void OnOfflinePagesLoaded(const MultipleOfflinePageItemResult& pages);
+  void NotifyItemsLoaded(Observer* observer);
+  void OnOfflinePagesChanged(const MultipleOfflinePageItemResult& pages);
+  void OnDeletePagesDone(DeletePageResult result);
+
+  // Always valid, this class is a member of the model.
+  OfflinePageModel* model_;
+
+  State state_;
+
+  // The cache of UI items. The key is DownloadUIItem.guid.
+  DownloadUIItems items_;
+
+  // The observers.
+  base::ObserverList<Observer> observers_;
+  int observers_count_;
+
+  base::WeakPtrFactory<DownloadUIAdapter> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadUIAdapter);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
diff --git a/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
new file mode 100644
index 0000000..8ad39c5b
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
@@ -0,0 +1,282 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/downloads/download_ui_adapter.h"
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/stub_offline_page_model.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+// Constants for a test OfflinePageItem.
+static const int kTestOfflineId1 = 1;
+static const int kTestOfflineId2 = 2;
+static const int kTestOfflineId3 = 3;
+static const char kTestUrl[] = "http://foo.com/bar.mhtml";
+static const char kTestGuid1[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
+static const char kTestGuid2[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc2";
+static const char kTestBadGuid[] = "ccccccc-cccc-0ccc-0ccc-ccccccccccc0";
+static const ClientId kTestClientIdOtherNamespace(kLastNNamespace, kTestGuid1);
+static const ClientId kTestClientIdOtherGuid(kLastNNamespace, kTestBadGuid);
+static const ClientId kTestClientId1(kAsyncNamespace, kTestGuid1);
+static const ClientId kTestClientId2(kAsyncNamespace, kTestGuid2);
+static const base::FilePath kTestFilePath =
+    base::FilePath(FILE_PATH_LITERAL("foo/bar.mhtml"));
+static const int kFileSize = 1000;
+static const base::Time kTestCreationTime = base::Time::Now();
+static const base::string16 kTestTitle = base::ASCIIToUTF16("test title");
+}  // namespace
+
+// Mock OfflinePageModel for testing the SavePage calls.
+class MockOfflinePageModel : public StubOfflinePageModel {
+ public:
+  MockOfflinePageModel(base::TestMockTimeTaskRunner* task_runner)
+      : observer_(nullptr),
+        task_runner_(task_runner),
+        policy_controller_(new ClientPolicyController()) {
+    adapter.reset(new DownloadUIAdapter(this));
+    // Add one page.
+    OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientId1,
+                         kTestFilePath, kFileSize, kTestCreationTime);
+    page.title = kTestTitle;
+    pages[kTestOfflineId1] = page;
+  }
+
+  ~MockOfflinePageModel() override {}
+
+  // OfflinePageModel overrides.
+  void AddObserver(Observer* observer) override {
+    EXPECT_TRUE(observer != nullptr);
+    observer_ = observer;
+  }
+
+  void RemoveObserver(Observer* observer) override {
+    EXPECT_TRUE(observer != nullptr);
+    EXPECT_EQ(observer, observer_);
+    observer_ = nullptr;
+  }
+
+  // PostTask instead of just running callback to simpulate the real class.
+  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override {
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(&MockOfflinePageModel::GetAllPagesImpl,
+                                      base::Unretained(this), callback));
+  }
+
+  void GetAllPagesImpl(const MultipleOfflinePageItemCallback& callback) {
+    std::vector<OfflinePageItem> result;
+    for (const auto& page : pages)
+      result.push_back(page.second);
+    callback.Run(result);
+  }
+
+  void DeletePageAndNotifyAdapter(const std::string& guid) {
+    for (const auto& page : pages) {
+      if (page.second.client_id.id == guid) {
+        observer_->OfflinePageDeleted(page.second.offline_id,
+                                      page.second.client_id);
+        pages.erase(page.first);
+        return;
+      }
+    }
+  }
+
+  void AddPageAndNotifyAdapter(const OfflinePageItem& page) {
+    EXPECT_EQ(pages.end(), pages.find(page.offline_id));
+    pages[page.offline_id] = page;
+    observer_->OfflinePageAdded(this, page);
+  }
+
+  ClientPolicyController* GetPolicyController() override {
+    return policy_controller_.get();
+  }
+
+  // Normally, OfflinePageModel owns this adapter, so lets test it this way.
+  std::unique_ptr<DownloadUIAdapter> adapter;
+
+  std::map<int64_t, OfflinePageItem> pages;
+
+ private:
+  OfflinePageModel::Observer* observer_;
+  base::TestMockTimeTaskRunner* task_runner_;
+  // Normally owned by OfflinePageModel.
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockOfflinePageModel);
+};
+
+class DownloadUIAdapterTest : public testing::Test,
+                              public DownloadUIAdapter::Observer {
+ public:
+  DownloadUIAdapterTest();
+  ~DownloadUIAdapterTest() override;
+
+  // testing::Test
+  void SetUp() override;
+
+  // DownloadUIAdapter::Observer
+  void ItemsLoaded() override;
+  void ItemAdded(const DownloadUIItem& item) override;
+  void ItemUpdated(const DownloadUIItem& item) override;
+  void ItemDeleted(const std::string& guid) override;
+
+  // Runs until all of the tasks that are not delayed are gone from the task
+  // queue.
+  void PumpLoop();
+
+  bool items_loaded;
+  std::vector<std::string> added_guids, updated_guids, deleted_guids;
+  std::unique_ptr<MockOfflinePageModel> model;
+
+ private:
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+};
+
+DownloadUIAdapterTest::DownloadUIAdapterTest()
+    : items_loaded(false), task_runner_(new base::TestMockTimeTaskRunner) {}
+
+DownloadUIAdapterTest::~DownloadUIAdapterTest() {}
+
+void DownloadUIAdapterTest::SetUp() {
+  model.reset(new MockOfflinePageModel(task_runner_.get()));
+  model->adapter->AddObserver(this);
+}
+
+void DownloadUIAdapterTest::ItemsLoaded() {
+  items_loaded = true;
+}
+
+void DownloadUIAdapterTest::ItemAdded(const DownloadUIItem& item) {
+  added_guids.push_back(item.guid);
+}
+
+void DownloadUIAdapterTest::ItemUpdated(const DownloadUIItem& item) {
+  updated_guids.push_back(item.guid);
+}
+
+void DownloadUIAdapterTest::ItemDeleted(const std::string& guid) {
+  deleted_guids.push_back(guid);
+}
+
+void DownloadUIAdapterTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadUIAdapterTest, InitialLoad) {
+  EXPECT_NE(nullptr, model->adapter);
+  EXPECT_FALSE(items_loaded);
+  PumpLoop();
+  EXPECT_TRUE(items_loaded);
+  const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1);
+  EXPECT_NE(nullptr, item);
+}
+
+TEST_F(DownloadUIAdapterTest, InitialItemConversion) {
+  EXPECT_EQ(1UL, model->pages.size());
+  EXPECT_EQ(kTestGuid1, model->pages[kTestOfflineId1].client_id.id);
+  PumpLoop();
+  const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1);
+  EXPECT_EQ(kTestGuid1, item->guid);
+  EXPECT_EQ(kTestUrl, item->url.spec());
+  EXPECT_EQ(kTestFilePath, item->target_path);
+  EXPECT_EQ(kTestCreationTime, item->start_time);
+  EXPECT_EQ(kFileSize, item->total_bytes);
+  EXPECT_EQ(kTestTitle, item->title);
+}
+
+TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) {
+  PumpLoop();
+  // Add page, notify adapter.
+  OfflinePageItem page(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
+                       base::FilePath(kTestFilePath), kFileSize,
+                       kTestCreationTime);
+  model->AddPageAndNotifyAdapter(page);
+  PumpLoop();
+  EXPECT_EQ(1UL, added_guids.size());
+  EXPECT_EQ(kTestGuid2, added_guids[0]);
+  // Remove pages, notify adapter.
+  model->DeletePageAndNotifyAdapter(kTestGuid1);
+  model->DeletePageAndNotifyAdapter(kTestGuid2);
+  PumpLoop();
+  EXPECT_EQ(2UL, deleted_guids.size());
+  EXPECT_EQ(kTestGuid1, deleted_guids[0]);
+  EXPECT_EQ(kTestGuid2, deleted_guids[1]);
+}
+
+TEST_F(DownloadUIAdapterTest, ItemWithWrongNamespace) {
+  PumpLoop();
+  OfflinePageItem page1(
+      GURL(kTestUrl), kTestOfflineId2, kTestClientIdOtherNamespace,
+      base::FilePath(kTestFilePath), kFileSize, kTestCreationTime);
+  model->AddPageAndNotifyAdapter(page1);
+  PumpLoop();
+  // Should not add the page with wrong namespace.
+  EXPECT_EQ(0UL, added_guids.size());
+
+  OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId3, kTestClientIdOtherGuid,
+                        base::FilePath(kTestFilePath), kFileSize,
+                        kTestCreationTime);
+  model->AddPageAndNotifyAdapter(page2);
+  PumpLoop();
+  // Should not add the page with wrong guid.
+  EXPECT_EQ(0UL, added_guids.size());
+}
+
+TEST_F(DownloadUIAdapterTest, ItemUpdated) {
+  PumpLoop();
+  // Clear the initial page and replace it with updated one.
+  model->pages.clear();
+  // Add page with the same offline_id/guid, notify adapter.
+  // This should generate 'updated' notification.
+  OfflinePageItem page1(GURL(kTestUrl), kTestOfflineId1, kTestClientId1,
+                        base::FilePath(kTestFilePath), kFileSize,
+                        kTestCreationTime);
+  // Add a new page which did not exist before.
+  OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
+                        base::FilePath(kTestFilePath), kFileSize,
+                        kTestCreationTime);
+  model->AddPageAndNotifyAdapter(page1);
+  model->AddPageAndNotifyAdapter(page2);
+  PumpLoop();
+  EXPECT_EQ(1UL, added_guids.size());
+  EXPECT_EQ(kTestGuid2, added_guids[0]);
+  // TODO(dimich): we currently don't report updated items since OPM doesn't
+  // have support for that. Add as needed, this will have to be updated when
+  // support is added.
+  EXPECT_EQ(0UL, updated_guids.size());
+}
+
+TEST_F(DownloadUIAdapterTest, NoHangingLoad) {
+  EXPECT_NE(nullptr, model->adapter);
+  EXPECT_FALSE(items_loaded);
+  // Removal of last observer causes cache unload of not-yet-loaded cache.
+  model->adapter->RemoveObserver(this);
+  // This will complete async fetch of items, but...
+  PumpLoop();
+  // items should not be loaded when there is no observers!
+  EXPECT_FALSE(items_loaded);
+  // This should not crash.
+  model->adapter->AddObserver(this);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_ui_item.cc b/components/offline_pages/core/downloads/download_ui_item.cc
new file mode 100644
index 0000000..c5bdef82
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_ui_item.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_item.h"
+
+namespace offline_pages {
+
+DownloadUIItem::DownloadUIItem() : total_bytes(0) {}
+
+DownloadUIItem::DownloadUIItem(const OfflinePageItem& page)
+    : guid(page.client_id.id),
+      url(page.url),
+      title(page.title),
+      target_path(page.file_path),
+      start_time(page.creation_time),
+      total_bytes(page.file_size) {}
+
+DownloadUIItem::DownloadUIItem(const SavePageRequest& request)
+    : guid(request.client_id().id),
+      url(request.url()),
+      start_time(request.creation_time()),
+      total_bytes(-1L) {}
+
+DownloadUIItem::DownloadUIItem(const DownloadUIItem& other)
+    : guid(other.guid),
+      url(other.url),
+      title(other.title),
+      target_path(other.target_path),
+      start_time(other.start_time),
+      total_bytes(other.total_bytes) {}
+
+DownloadUIItem::~DownloadUIItem() {}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_ui_item.h b/components/offline_pages/core/downloads/download_ui_item.h
new file mode 100644
index 0000000..aea7cf5cc
--- /dev/null
+++ b/components/offline_pages/core/downloads/download_ui_item.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+struct OfflinePageItem;
+class SavePageRequest;
+
+struct DownloadUIItem {
+ public:
+  DownloadUIItem();
+  explicit DownloadUIItem(const OfflinePageItem& page);
+  explicit DownloadUIItem(const SavePageRequest& request);
+  DownloadUIItem(const DownloadUIItem& other);
+  ~DownloadUIItem();
+
+  // Unique id.
+  std::string guid;
+
+  // The URL of the captured page.
+  GURL url;
+
+  // The Title of the captured page, if any. It can be empty string either
+  // because the page is not yet fully loaded, or because it doesn't have any.
+  base::string16 title;
+
+  // The file path to the archive with a local copy of the page.
+  base::FilePath target_path;
+
+  // The time when the offline archive was created.
+  base::Time start_time;
+
+  // The size of the offline copy.
+  int64_t total_bytes;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
diff --git a/components/offline_pages/core/downloads/offline_page_download_notifier.h b/components/offline_pages/core/downloads/offline_page_download_notifier.h
new file mode 100644
index 0000000..fefabe4
--- /dev/null
+++ b/components/offline_pages/core/downloads/offline_page_download_notifier.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
+
+#include "components/offline_pages/core/downloads/download_ui_item.h"
+
+namespace offline_pages {
+
+struct OfflinePageDownloadNotifier {
+ public:
+  virtual ~OfflinePageDownloadNotifier() = default;
+
+  // Reports that |item| has completed successfully.
+  virtual void NotifyDownloadSuccessful(const DownloadUIItem& item) = 0;
+
+  // Reports that |item| has completed unsuccessfully.
+  virtual void NotifyDownloadFailed(const DownloadUIItem& item) = 0;
+
+  // Reports that |item| is active and possibly making progress.
+  virtual void NotifyDownloadProgress(const DownloadUIItem& item) = 0;
+
+  // Reports that |item| has been paused (and so it is not active).
+  virtual void NotifyDownloadPaused(const DownloadUIItem& item) = 0;
+
+  // Reports that any progress on |item| has been interrupted. It is pending
+  // or available for another attempt when conditions allows.
+  virtual void NotifyDownloadInterrupted(const DownloadUIItem& item) = 0;
+
+  // Reports that |item| has been canceled.
+  virtual void NotifyDownloadCanceled(const DownloadUIItem& item) = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
diff --git a/components/offline_pages/core/offline_event_logger.cc b/components/offline_pages/core/offline_event_logger.cc
new file mode 100644
index 0000000..0fb38f36
--- /dev/null
+++ b/components/offline_pages/core/offline_event_logger.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+
+namespace offline_pages {
+
+extern const size_t kMaxLogCount = 50;
+
+OfflineEventLogger::OfflineEventLogger()
+    : activities_(0), is_logging_(false), client_(nullptr) {}
+
+OfflineEventLogger::~OfflineEventLogger() {}
+
+void OfflineEventLogger::Clear() {
+  activities_.clear();
+}
+
+void OfflineEventLogger::SetIsLogging(bool is_logging) {
+  is_logging_ = is_logging;
+}
+
+bool OfflineEventLogger::GetIsLogging() {
+  return is_logging_;
+}
+
+void OfflineEventLogger::GetLogs(std::vector<std::string>* records) {
+  DCHECK(records);
+  records->insert(records->end(), activities_.begin(), activities_.end());
+}
+
+void OfflineEventLogger::RecordActivity(const std::string& activity) {
+  if (!is_logging_ || activity.empty())
+    return;
+
+  base::Time::Exploded current_time;
+  base::Time::Now().LocalExplode(&current_time);
+
+  std::string date_string = base::StringPrintf(
+      "%d %02d %02d %02d:%02d:%02d", current_time.year, current_time.month,
+      current_time.day_of_month, current_time.hour, current_time.minute,
+      current_time.second);
+
+  std::string log_message = date_string + ": " + activity;
+  if (client_)
+    client_->CustomLog(log_message);
+
+  if (activities_.size() == kMaxLogCount)
+    activities_.pop_back();
+
+  activities_.push_front(log_message);
+}
+
+void OfflineEventLogger::SetClient(Client* client) {
+  DCHECK(client);
+  SetIsLogging(true);
+  client_ = client;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_event_logger.h b/components/offline_pages/core/offline_event_logger.h
new file mode 100644
index 0000000..40bb9c4
--- /dev/null
+++ b/components/offline_pages/core/offline_event_logger.h
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+namespace offline_pages {
+
+// Maximum number of recorded Logs to keep track of at any moment. Defined in
+// offline_event_logger.cc.
+extern const size_t kMaxLogCount;
+
+// Facilitates the logging of events. Subclasses should create methods that
+// call RecordActivity to write into the log. |SetIsLogging|, |GetLogs|, and
+// |Clear| are called from the chrome://offline-internals page.
+//
+// Logging should be done by calling the corresponding subclass methods when
+// a loggable event occurs (i.e. when status has changed for a save request
+// or when an offlined page has been accessed/saved).
+//
+// This log only keeps track of the last |kMaxLogCount| events.
+class OfflineEventLogger {
+ public:
+  // This client interface should be implemented by the class which provides the
+  // ability to pipe the log somewhere else (Eg. a java class which can write
+  // logs into a file). It's optional and uses SetClient() to attach the client
+  // to the event logger instance.
+  class Client {
+   public:
+    virtual ~Client(){};
+    virtual void CustomLog(const std::string& message) = 0;
+  };
+
+  OfflineEventLogger();
+
+  ~OfflineEventLogger();
+
+  // Clears the recorded activities.
+  void Clear();
+
+  // Turns logging on/off.
+  void SetIsLogging(bool is_logging);
+
+  // Returns whether we are currently logging.
+  bool GetIsLogging();
+
+  // Dumps the current activity list into |records|.
+  void GetLogs(std::vector<std::string>* records);
+
+  // Write the activity into the cycling log if we are currently logging.
+  void RecordActivity(const std::string& activity);
+
+  // Sets the client for custom logging process if needed.
+  void SetClient(Client* client);
+
+ private:
+  // Recorded offline page activities.
+  std::deque<std::string> activities_;
+
+  // Whether we are currently recording logs or not.
+  bool is_logging_;
+
+  // Not owned.
+  Client* client_;
+};
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_EVENT_LOGGER_H_
diff --git a/components/offline_pages/core/offline_event_logger_unittest.cc b/components/offline_pages/core/offline_event_logger_unittest.cc
new file mode 100644
index 0000000..d82ede9
--- /dev/null
+++ b/components/offline_pages/core/offline_event_logger_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_event_logger.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+const char kMessage[] = "Message is ";
+const int kTimeLength = 21;
+
+class EventLoggerTestClient : public OfflineEventLogger::Client {
+ public:
+  void CustomLog(const std::string& message) override {
+    last_log_message_ = message;
+  }
+
+  const std::string& last_log_message() { return last_log_message_; }
+
+ private:
+  std::string last_log_message_;
+};
+
+}  // namespace
+
+TEST(OfflineEventLoggerTest, SettingClientEnableLogging) {
+  OfflineEventLogger logger;
+  EventLoggerTestClient client;
+  logger.SetClient(&client);
+  EXPECT_TRUE(logger.GetIsLogging());
+}
+
+TEST(OfflineEventLoggerTest, SettingClientAndLog) {
+  OfflineEventLogger logger;
+  EventLoggerTestClient client;
+  logger.SetClient(&client);
+
+  logger.SetIsLogging(true);
+  for (size_t i = 0; i < kMaxLogCount + 1; ++i)
+    logger.RecordActivity(kMessage + std::to_string(i));
+  std::vector<std::string> log;
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(kMaxLogCount, log.size());
+  EXPECT_EQ(client.last_log_message(), log[0]);
+  EXPECT_EQ(std::string(kMessage) + std::to_string(kMaxLogCount),
+            client.last_log_message().substr(kTimeLength));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_archiver.h b/components/offline_pages/core/offline_page_archiver.h
new file mode 100644
index 0000000..c224ef95
--- /dev/null
+++ b/components/offline_pages/core/offline_page_archiver.h
@@ -0,0 +1,81 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+// Interface of a class responsible for creation of the archive for offline use.
+//
+// Archiver will be implemented by embedder and may have additional methods that
+// are not interesting from the perspective of OfflinePageModel. Example of such
+// extra information or capability is a way to enumerate available WebContents
+// to find the one that needs to be used to create archive (or to map it to the
+// URL passed in CreateArchive in some other way).
+//
+// Archiver will be responsible for naming the file that is being saved (it has
+// URL, title and the whole page content at its disposal). For that it should be
+// also configured with the path where the archives are stored.
+//
+// Archiver should be able to archive multiple pages in parallel, as these are
+// asynchronous calls carried out by some other component.
+//
+// If archiver gets two consecutive requests to archive the same page (may be
+// run in parallel) it can generate 2 different names for files and save the
+// same page separately, as if these were 2 completely unrelated pages. It is up
+// to the caller (e.g. OfflinePageModel) to make sure that situation like that
+// does not happen.
+//
+// If the page is not completely loaded, it is up to the implementation of the
+// archiver whether to respond with ERROR_CONTENT_UNAVAILBLE, wait longer to
+// actually snapshot a complete page, or snapshot whatever is available at that
+// point in time (what the user sees).
+//
+// TODO(fgorski): Add ability to delete archive.
+// TODO(fgorski): Add ability to check that archive exists.
+// TODO(fgorski): Add ability to refresh an existing archive in one step.
+// TODO(fgorski): Add ability to identify all of the archives in the directory,
+// to enable to model to reconcile the archives.
+class OfflinePageArchiver {
+ public:
+  // Results of the archive creation.
+  enum class ArchiverResult {
+    SUCCESSFULLY_CREATED,           // Archive created successfully.
+    ERROR_DEVICE_FULL,              // Cannot save the archive - device is full.
+    ERROR_CANCELED,                 // Caller canceled the request.
+    ERROR_CONTENT_UNAVAILABLE,      // Content to archive is not available.
+    ERROR_ARCHIVE_CREATION_FAILED,  // Creation of archive failed.
+    ERROR_SECURITY_CERTIFICATE,     // Page was loaded on secure connection, but
+                                    // there was a security error.
+  };
+
+  typedef base::Callback<void(OfflinePageArchiver* /* archiver */,
+                              ArchiverResult /* result */,
+                              const GURL& /* url */,
+                              const base::FilePath& /* file_path */,
+                              const base::string16& /* title */,
+                              int64_t /* file_size */)>
+      CreateArchiveCallback;
+
+  virtual ~OfflinePageArchiver() {}
+
+  // Starts creating the archive in the |archives_dir| with |archive_id| added
+  // to the archive filename. Once archive is created |callback| will be called
+  // with the result and additional information.
+  virtual void CreateArchive(const base::FilePath& archives_dir,
+                             int64_t archive_id,
+                             const CreateArchiveCallback& callback) = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
diff --git a/components/offline_pages/core/offline_page_client_policy.h b/components/offline_pages/core/offline_page_client_policy.h
new file mode 100644
index 0000000..79c3e916
--- /dev/null
+++ b/components/offline_pages/core/offline_page_client_policy.h
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/time/time.h"
+
+namespace offline_pages {
+
+static const size_t kUnlimitedPages = 0;
+
+// The struct describing the lifetime policy of offline pages.
+// The following behaviors are controlled by policy:
+//    a. Persistency of the offline page.
+//    b. Expiration time of an offline page
+//    c. Limit of number of pages offline.
+struct LifetimePolicy {
+  // Type of the client, indicating where the archived page would be saved
+  // and whether it could be kept indefinitely.
+  enum class LifetimeType {
+    TEMPORARY,
+    PERSISTENT,
+  };
+
+  // Type of the page generated by the client.
+  LifetimeType lifetime_type;
+
+  // The time after which the page expires.
+  // A TimeDelta of 0 means no expiration.
+  base::TimeDelta expiration_period;
+
+  // The maximum number of pages allowed to be saved by the namespace.
+  // kUnlimitedPages (defined above) means no limit set.
+  size_t page_limit;
+
+  LifetimePolicy(LifetimeType init_lifetime_type, size_t init_page_limit)
+      : lifetime_type(init_lifetime_type),
+        expiration_period(base::TimeDelta::FromDays(0)),
+        page_limit(init_page_limit){};
+};
+
+// The struct describing feature set of the offline pages.
+struct FeaturePolicy {
+  // Whether pages are shown in download ui.
+  bool is_supported_by_download;
+  // Whether pages are shown in recent tabs ui.
+  bool is_supported_by_recent_tabs;
+  // Whether pages should only be viewed in the tab they were generated in.
+  bool only_shown_in_original_tab;
+  // Whether pages are removed on user-initiated cache reset. Defaults to true.
+  bool is_removed_on_cache_reset;
+
+  FeaturePolicy()
+      : is_supported_by_download(false),
+        is_supported_by_recent_tabs(false),
+        only_shown_in_original_tab(false),
+        is_removed_on_cache_reset(true){};
+};
+
+// The struct describing policies for various namespaces (Bookmark, Last-N etc.)
+// used by offline page model. The name_space is supposed to be key, so that
+// it's sufficient to compare name_space only when doing comparisons.
+struct OfflinePageClientPolicy {
+  // Namespace to which the policy applied.
+  std::string name_space;
+
+  // Policy to control the lifetime of a page generated by this namespace.
+  LifetimePolicy lifetime_policy;
+
+  // How many pages for the same online URL can be stored at any time.
+  // kUnlimitedPages means there's no limit.
+  size_t pages_allowed_per_url;
+
+  FeaturePolicy feature_policy;
+
+  OfflinePageClientPolicy(std::string namespace_val,
+                          LifetimePolicy lifetime_policy_val,
+                          size_t pages_allowed_per_url_val,
+                          FeaturePolicy feature_policy_val)
+      : name_space(namespace_val),
+        lifetime_policy(lifetime_policy_val),
+        pages_allowed_per_url(pages_allowed_per_url_val),
+        feature_policy(feature_policy_val){};
+
+  OfflinePageClientPolicy(std::string namespace_val,
+                          LifetimePolicy lifetime_policy_val,
+                          size_t pages_allowed_per_url_val)
+      : OfflinePageClientPolicy(namespace_val,
+                                lifetime_policy_val,
+                                pages_allowed_per_url_val,
+                                FeaturePolicy()){};
+};
+
+class OfflinePageClientPolicyBuilder {
+ public:
+  OfflinePageClientPolicyBuilder(const std::string& name_space,
+                                 LifetimePolicy::LifetimeType lifetime_type,
+                                 size_t page_limit,
+                                 size_t pages_allowed_per_url)
+      : policy_(
+            OfflinePageClientPolicy(name_space,
+                                    LifetimePolicy(lifetime_type, page_limit),
+                                    pages_allowed_per_url)){};
+
+  ~OfflinePageClientPolicyBuilder() {}
+
+  // Calling build does not reset the object inside.
+  const OfflinePageClientPolicy Build() const { return policy_; }
+
+  OfflinePageClientPolicyBuilder& SetExpirePeriod(
+      const base::TimeDelta& expire_period) {
+    policy_.lifetime_policy.expiration_period = expire_period;
+    return *this;
+  }
+
+  OfflinePageClientPolicyBuilder& SetIsSupportedByDownload(
+      const bool is_downloaded) {
+    policy_.feature_policy.is_supported_by_download = is_downloaded;
+    return *this;
+  }
+
+  OfflinePageClientPolicyBuilder& SetIsSupportedByRecentTabs(
+      const bool is_recent_tabs) {
+    policy_.feature_policy.is_supported_by_recent_tabs = is_recent_tabs;
+    return *this;
+  }
+
+  OfflinePageClientPolicyBuilder& SetIsRemovedOnCacheReset(
+      const bool removed_on_cache_reset) {
+    policy_.feature_policy.is_removed_on_cache_reset = removed_on_cache_reset;
+    return *this;
+  }
+
+  OfflinePageClientPolicyBuilder& SetIsOnlyShownInOriginalTab(
+      const bool only_shown_in_original_tab) {
+    policy_.feature_policy.only_shown_in_original_tab =
+        only_shown_in_original_tab;
+    return *this;
+  }
+
+ private:
+  OfflinePageClientPolicy policy_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageClientPolicyBuilder);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_CLIENT_POLICY_H_
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
new file mode 100644
index 0000000..ef2b4700
--- /dev/null
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_feature.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+
+namespace offline_pages {
+
+const base::Feature kOfflineBookmarksFeature{"OfflineBookmarks",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kOffliningRecentPagesFeature{
+    "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kOfflinePagesSharingFeature{
+    "OfflinePagesSharing", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature{
+    "OfflinePagesSvelteConcurrentLoading", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kBackgroundLoaderForDownloadsFeature{
+    "BackgroundLoadingForDownloads", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kOfflinePagesAsyncDownloadFeature{
+    "OfflinePagesAsyncDownload", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kNewBackgroundLoaderFeature {
+    "BackgroundLoader", base::FEATURE_DISABLED_BY_DEFAULT
+};
+
+bool IsOfflineBookmarksEnabled() {
+  return base::FeatureList::IsEnabled(kOfflineBookmarksFeature);
+}
+
+bool IsOffliningRecentPagesEnabled() {
+  return base::FeatureList::IsEnabled(kOffliningRecentPagesFeature);
+}
+
+bool IsOfflinePagesSvelteConcurrentLoadingEnabled() {
+  return base::FeatureList::IsEnabled(
+      kOfflinePagesSvelteConcurrentLoadingFeature);
+}
+
+bool IsOfflinePagesCTEnabled() {
+  return base::FeatureList::IsEnabled(kOfflinePagesCTFeature);
+}
+
+bool IsOfflinePagesSharingEnabled() {
+  return base::FeatureList::IsEnabled(kOfflinePagesSharingFeature);
+}
+
+bool IsBackgroundLoaderForDownloadsEnabled() {
+  return base::FeatureList::IsEnabled(kBackgroundLoaderForDownloadsFeature);
+}
+
+bool IsOfflinePagesAsyncDownloadEnabled() {
+  return base::FeatureList::IsEnabled(kOfflinePagesAsyncDownloadFeature);
+}
+
+bool ShouldUseNewBackgroundLoader() {
+  return base::FeatureList::IsEnabled(kNewBackgroundLoaderFeature);
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
new file mode 100644
index 0000000..3210afb
--- /dev/null
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_FEATURE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_FEATURE_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+
+namespace offline_pages {
+
+extern const base::Feature kOfflineBookmarksFeature;
+extern const base::Feature kOffliningRecentPagesFeature;
+extern const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature;
+extern const base::Feature kOfflinePagesCTFeature;
+extern const base::Feature kOfflinePagesSharingFeature;
+extern const base::Feature kBackgroundLoaderForDownloadsFeature;
+extern const base::Feature kOfflinePagesAsyncDownloadFeature;
+extern const base::Feature kNewBackgroundLoaderFeature;
+
+// Returns true if saving bookmarked pages for offline viewing is enabled.
+bool IsOfflineBookmarksEnabled();
+
+// Returns true if offlining of recent pages (aka 'Last N pages') is enabled.
+bool IsOffliningRecentPagesEnabled();
+
+// Returns true if offline CT features are enabled.  See crbug.com/620421.
+bool IsOfflinePagesCTEnabled();
+
+// Returns true if offline page sharing is enabled.
+bool IsOfflinePagesSharingEnabled();
+
+// Returns true if saving a foreground tab that is taking too long using the
+// background scheduler is enabled.
+bool IsBackgroundLoaderForDownloadsEnabled();
+
+// Returns true if concurrent background loading is enabled for svelte.
+bool IsOfflinePagesSvelteConcurrentLoadingEnabled();
+
+// Returns true if downloading a page asynchonously is enabled.
+bool IsOfflinePagesAsyncDownloadEnabled();
+
+// Returns true if we should use background loader rather than prerenderer
+// to offline pages.
+bool ShouldUseNewBackgroundLoader();
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
diff --git a/components/offline_pages/core/offline_page_item.cc b/components/offline_pages/core/offline_page_item.cc
new file mode 100644
index 0000000..7d83079
--- /dev/null
+++ b/components/offline_pages/core/offline_page_item.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_item.h"
+
+namespace offline_pages {
+
+ClientId::ClientId() : name_space(""), id("") {}
+
+ClientId::ClientId(std::string name_space, std::string id)
+    : name_space(name_space), id(id) {}
+
+bool ClientId::operator==(const ClientId& client_id) const {
+  return name_space == client_id.name_space && id == client_id.id;
+}
+
+bool ClientId::operator<(const ClientId& client_id) const {
+  if (name_space == client_id.name_space)
+    return (id < client_id.id);
+
+  return name_space < client_id.name_space;
+}
+
+OfflinePageItem::OfflinePageItem()
+    : file_size(0), access_count(0), flags(NO_FLAG) {}
+
+OfflinePageItem::OfflinePageItem(const GURL& url,
+                                 int64_t offline_id,
+                                 const ClientId& client_id,
+                                 const base::FilePath& file_path,
+                                 int64_t file_size)
+    : url(url),
+      offline_id(offline_id),
+      client_id(client_id),
+      file_path(file_path),
+      file_size(file_size),
+      access_count(0),
+      flags(NO_FLAG) {}
+
+OfflinePageItem::OfflinePageItem(const GURL& url,
+                                 int64_t offline_id,
+                                 const ClientId& client_id,
+                                 const base::FilePath& file_path,
+                                 int64_t file_size,
+                                 const base::Time& creation_time)
+    : url(url),
+      offline_id(offline_id),
+      client_id(client_id),
+      file_path(file_path),
+      file_size(file_size),
+      creation_time(creation_time),
+      last_access_time(creation_time),
+      access_count(0),
+      flags(NO_FLAG) {}
+
+OfflinePageItem::OfflinePageItem(const OfflinePageItem& other) = default;
+
+OfflinePageItem::~OfflinePageItem() {}
+
+bool OfflinePageItem::operator==(const OfflinePageItem& other) const {
+  return url == other.url && offline_id == other.offline_id &&
+         client_id == other.client_id && file_path == other.file_path &&
+         creation_time == other.creation_time &&
+         last_access_time == other.last_access_time &&
+         access_count == other.access_count &&
+         title == other.title &&
+         flags == other.flags &&
+         original_url == other.original_url;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_item.h b/components/offline_pages/core/offline_page_item.h
new file mode 100644
index 0000000..bbc2380e
--- /dev/null
+++ b/components/offline_pages/core/offline_page_item.h
@@ -0,0 +1,91 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+struct ClientId {
+  // The namespace for the id (of course 'namespace' is a reserved word, so...)
+  std::string name_space;
+  // The id in the client's namespace.  Opaque to us.
+  std::string id;
+
+  ClientId();
+  ClientId(std::string name_space, std::string id);
+
+  bool operator==(const ClientId& client_id) const;
+
+  bool operator<(const ClientId& client_id) const;
+};
+
+// Metadata of the offline page.
+struct OfflinePageItem {
+ public:
+  // Note that this should match with Flags enum in offline_pages.proto.
+  enum Flags {
+    NO_FLAG = 0,
+    MARKED_FOR_DELETION = 0x1,
+  };
+
+  OfflinePageItem();
+  OfflinePageItem(const GURL& url,
+                  int64_t offline_id,
+                  const ClientId& client_id,
+                  const base::FilePath& file_path,
+                  int64_t file_size);
+  OfflinePageItem(const GURL& url,
+                  int64_t offline_id,
+                  const ClientId& client_id,
+                  const base::FilePath& file_path,
+                  int64_t file_size,
+                  const base::Time& creation_time);
+  OfflinePageItem(const OfflinePageItem& other);
+  ~OfflinePageItem();
+
+  bool operator==(const OfflinePageItem& other) const;
+
+  // The URL of the page. This is the last committed URL. In the case that
+  // redirects occur, access |original_url| for the original URL.
+  GURL url;
+  // The primary key/ID for this page in offline pages internal database.
+  int64_t offline_id;
+
+  // The Client ID (external) related to the offline page. This is opaque
+  // to our system, but useful for users of offline pages who want to map
+  // their ids to our saved pages.
+  ClientId client_id;
+
+  // The file path to the archive with a local copy of the page.
+  base::FilePath file_path;
+  // The size of the offline copy.
+  int64_t file_size;
+  // The time when the offline archive was created.
+  base::Time creation_time;
+  // The time when the offline archive was last accessed.
+  base::Time last_access_time;
+  // Number of times that the offline archive has been accessed.
+  int access_count;
+  // The title of the page at the time it was saved.
+  base::string16 title;
+  // Flags about the state and behavior of the offline page.
+  Flags flags;
+  // The original URL of the page if not empty. Otherwise, this is set to empty
+  // and |url| should be accessed instead.
+  GURL original_url;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ITEM_H_
diff --git a/components/offline_pages/core/offline_page_metadata_store.cc b/components/offline_pages/core/offline_page_metadata_store.cc
new file mode 100644
index 0000000..761d3498
--- /dev/null
+++ b/components/offline_pages/core/offline_page_metadata_store.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+
+namespace offline_pages {
+
+template class StoreUpdateResult<OfflinePageItem>;
+
+OfflinePageMetadataStore::~OfflinePageMetadataStore() {
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_metadata_store.h b/components/offline_pages/core/offline_page_metadata_store.h
new file mode 100644
index 0000000..d96fce75
--- /dev/null
+++ b/components/offline_pages/core/offline_page_metadata_store.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/callback.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_store_types.h"
+
+namespace offline_pages {
+
+typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult;
+
+// OfflinePageMetadataStore keeps metadata for the offline pages.
+// Ability to create multiple instances of the store as well as behavior of
+// asynchronous operations when the object is being destroyed, before such
+// operation finishes will depend on implementation. It should be possible to
+// issue multiple asynchronous operations in parallel.
+class OfflinePageMetadataStore {
+ public:
+  // This enum is used in an UMA histogram. Hence the entries here shouldn't
+  // be deleted or re-ordered and new ones should be added to the end.
+  enum LoadStatus {
+    LOAD_SUCCEEDED,
+    STORE_INIT_FAILED,
+    STORE_LOAD_FAILED,
+    DATA_PARSING_FAILED,
+
+    // NOTE: always keep this entry at the end.
+    LOAD_STATUS_COUNT
+  };
+
+  typedef base::Callback<void(bool /* success */)> InitializeCallback;
+  typedef base::Callback<void(bool /* success */)> ResetCallback;
+  typedef base::Callback<void(const std::vector<OfflinePageItem>&)>
+      LoadCallback;
+  typedef base::Callback<void(ItemActionStatus)> AddCallback;
+  typedef base::Callback<void(std::unique_ptr<OfflinePagesUpdateResult>)>
+      UpdateCallback;
+
+  virtual ~OfflinePageMetadataStore();
+
+  // Initializes the store. Should be called before any other methods.
+  virtual void Initialize(const InitializeCallback& callback) = 0;
+
+  // Get all of the offline pages from the store.
+  virtual void GetOfflinePages(const LoadCallback& callback) = 0;
+
+  // Asynchronously adds an offline page item metadata to the store.
+  virtual void AddOfflinePage(const OfflinePageItem& offline_page,
+                              const AddCallback& callback) = 0;
+
+  // Asynchronously updates a set of offline page items in the store.
+  virtual void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
+                                  const UpdateCallback& callback) = 0;
+
+  // Asynchronously removes offline page metadata from the store.
+  // Result of the update is passed in callback.
+  virtual void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
+                                  const UpdateCallback& callback) = 0;
+
+  // Resets the store.
+  virtual void Reset(const ResetCallback& callback) = 0;
+
+  // Gets the store state.
+  virtual StoreState state() const = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
diff --git a/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc b/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc
new file mode 100644
index 0000000..36abbcc
--- /dev/null
+++ b/components/offline_pages/core/offline_page_metadata_store_impl_unittest.cc
@@ -0,0 +1,985 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+
+#define OFFLINE_PAGES_TABLE_V1 "offlinepages_v1"
+
+const char kTestClientNamespace[] = "CLIENT_NAMESPACE";
+const char kTestURL[] = "https://example.com";
+const char kOriginalTestURL[] = "https://example.com/foo";
+const ClientId kTestClientId1(kTestClientNamespace, "1234");
+const ClientId kTestClientId2(kTestClientNamespace, "5678");
+const base::FilePath::CharType kFilePath[] =
+    FILE_PATH_LITERAL("/offline_pages/example_com.mhtml");
+int64_t kFileSize = 234567LL;
+int64_t kOfflineId = 12345LL;
+
+// Build a store with outdated schema to simulate the upgrading process.
+// TODO(romax): move it to sql_unittests.
+void BuildTestStoreWithSchemaFromM52(const base::FilePath& file) {
+  sql::Connection connection;
+  ASSERT_TRUE(
+      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+  ASSERT_TRUE(connection.is_open());
+  ASSERT_TRUE(connection.BeginTransaction());
+  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
+                                 "creation_time INTEGER NOT NULL, "
+                                 "file_size INTEGER NOT NULL, "
+                                 "version INTEGER NOT NULL, "
+                                 "last_access_time INTEGER NOT NULL, "
+                                 "access_count INTEGER NOT NULL, "
+                                 "status INTEGER NOT NULL DEFAULT 0, "
+                                 "user_initiated INTEGER, "
+                                 "client_namespace VARCHAR NOT NULL, "
+                                 "client_id VARCHAR NOT NULL, "
+                                 "online_url VARCHAR NOT NULL, "
+                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
+                                 "file_path VARCHAR NOT NULL "
+                                 ")"));
+  ASSERT_TRUE(connection.CommitTransaction());
+  sql::Statement statement(connection.GetUniqueStatement(
+      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+      "(offline_id, creation_time, file_size, version, "
+      "last_access_time, access_count, client_namespace, "
+      "client_id, online_url, file_path) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+  statement.BindInt64(0, kOfflineId);
+  statement.BindInt(1, 0);
+  statement.BindInt64(2, kFileSize);
+  statement.BindInt(3, 0);
+  statement.BindInt(4, 0);
+  statement.BindInt(5, 1);
+  statement.BindCString(6, kTestClientNamespace);
+  statement.BindString(7, kTestClientId2.id);
+  statement.BindCString(8, kTestURL);
+  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
+  ASSERT_TRUE(statement.Run());
+  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+  ASSERT_FALSE(
+      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
+}
+
+void BuildTestStoreWithSchemaFromM53(const base::FilePath& file) {
+  sql::Connection connection;
+  ASSERT_TRUE(
+      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+  ASSERT_TRUE(connection.is_open());
+  ASSERT_TRUE(connection.BeginTransaction());
+  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
+                                 "creation_time INTEGER NOT NULL, "
+                                 "file_size INTEGER NOT NULL, "
+                                 "version INTEGER NOT NULL, "
+                                 "last_access_time INTEGER NOT NULL, "
+                                 "access_count INTEGER NOT NULL, "
+                                 "status INTEGER NOT NULL DEFAULT 0, "
+                                 "user_initiated INTEGER, "
+                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
+                                 "client_namespace VARCHAR NOT NULL, "
+                                 "client_id VARCHAR NOT NULL, "
+                                 "online_url VARCHAR NOT NULL, "
+                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
+                                 "file_path VARCHAR NOT NULL "
+                                 ")"));
+  ASSERT_TRUE(connection.CommitTransaction());
+  sql::Statement statement(connection.GetUniqueStatement(
+      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+      "(offline_id, creation_time, file_size, version, "
+      "last_access_time, access_count, client_namespace, "
+      "client_id, online_url, file_path, expiration_time) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+  statement.BindInt64(0, kOfflineId);
+  statement.BindInt(1, 0);
+  statement.BindInt64(2, kFileSize);
+  statement.BindInt(3, 0);
+  statement.BindInt(4, 0);
+  statement.BindInt(5, 1);
+  statement.BindCString(6, kTestClientNamespace);
+  statement.BindString(7, kTestClientId2.id);
+  statement.BindCString(8, kTestURL);
+  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
+  statement.BindInt64(10, base::Time::Now().ToInternalValue());
+  ASSERT_TRUE(statement.Run());
+  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+  ASSERT_FALSE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "title"));
+}
+
+void BuildTestStoreWithSchemaFromM54(const base::FilePath& file) {
+  sql::Connection connection;
+  ASSERT_TRUE(
+      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+  ASSERT_TRUE(connection.is_open());
+  ASSERT_TRUE(connection.BeginTransaction());
+  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
+                                 "creation_time INTEGER NOT NULL, "
+                                 "file_size INTEGER NOT NULL, "
+                                 "version INTEGER NOT NULL, "
+                                 "last_access_time INTEGER NOT NULL, "
+                                 "access_count INTEGER NOT NULL, "
+                                 "status INTEGER NOT NULL DEFAULT 0, "
+                                 "user_initiated INTEGER, "
+                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
+                                 "client_namespace VARCHAR NOT NULL, "
+                                 "client_id VARCHAR NOT NULL, "
+                                 "online_url VARCHAR NOT NULL, "
+                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
+                                 "file_path VARCHAR NOT NULL, "
+                                 "title VARCHAR NOT NULL DEFAULT ''"
+                                 ")"));
+  ASSERT_TRUE(connection.CommitTransaction());
+  sql::Statement statement(connection.GetUniqueStatement(
+      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+      "(offline_id, creation_time, file_size, version, "
+      "last_access_time, access_count, client_namespace, "
+      "client_id, online_url, file_path, expiration_time, title) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+  statement.BindInt64(0, kOfflineId);
+  statement.BindInt(1, 0);
+  statement.BindInt64(2, kFileSize);
+  statement.BindInt(3, 0);
+  statement.BindInt(4, 0);
+  statement.BindInt(5, 1);
+  statement.BindCString(6, kTestClientNamespace);
+  statement.BindString(7, kTestClientId2.id);
+  statement.BindCString(8, kTestURL);
+  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
+  statement.BindInt64(10, base::Time::Now().ToInternalValue());
+  statement.BindString16(11, base::UTF8ToUTF16("Test title"));
+  ASSERT_TRUE(statement.Run());
+  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "version"));
+  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "status"));
+  ASSERT_TRUE(
+      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "user_initiated"));
+  ASSERT_TRUE(
+      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "offline_url"));
+}
+
+void BuildTestStoreWithSchemaFromM55(const base::FilePath& file) {
+  sql::Connection connection;
+  ASSERT_TRUE(
+      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+  ASSERT_TRUE(connection.is_open());
+  ASSERT_TRUE(connection.BeginTransaction());
+  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
+                                 "creation_time INTEGER NOT NULL, "
+                                 "file_size INTEGER NOT NULL, "
+                                 "last_access_time INTEGER NOT NULL, "
+                                 "access_count INTEGER NOT NULL, "
+                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
+                                 "client_namespace VARCHAR NOT NULL, "
+                                 "client_id VARCHAR NOT NULL, "
+                                 "online_url VARCHAR NOT NULL, "
+                                 "file_path VARCHAR NOT NULL, "
+                                 "title VARCHAR NOT NULL DEFAULT ''"
+                                 ")"));
+  ASSERT_TRUE(connection.CommitTransaction());
+  sql::Statement statement(connection.GetUniqueStatement(
+      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+      "(offline_id, creation_time, file_size, "
+      "last_access_time, access_count, client_namespace, "
+      "client_id, online_url, file_path, expiration_time, title) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+  statement.BindInt64(0, kOfflineId);
+  statement.BindInt(1, 0);
+  statement.BindInt64(2, kFileSize);
+  statement.BindInt(3, 0);
+  statement.BindInt(4, 1);
+  statement.BindCString(5, kTestClientNamespace);
+  statement.BindString(6, kTestClientId2.id);
+  statement.BindCString(7, kTestURL);
+  statement.BindString(8, base::FilePath(kFilePath).MaybeAsASCII());
+  statement.BindInt64(9, base::Time::Now().ToInternalValue());
+  statement.BindString16(10, base::UTF8ToUTF16("Test title"));
+  ASSERT_TRUE(statement.Run());
+  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "title"));
+  ASSERT_FALSE(
+      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "original_url"));
+}
+
+void BuildTestStoreWithSchemaFromM56(const base::FilePath& file) {
+  sql::Connection connection;
+  ASSERT_TRUE(
+      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
+  ASSERT_TRUE(connection.is_open());
+  ASSERT_TRUE(connection.BeginTransaction());
+  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
+                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
+                                 "creation_time INTEGER NOT NULL, "
+                                 "file_size INTEGER NOT NULL, "
+                                 "last_access_time INTEGER NOT NULL, "
+                                 "access_count INTEGER NOT NULL, "
+                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
+                                 "client_namespace VARCHAR NOT NULL, "
+                                 "client_id VARCHAR NOT NULL, "
+                                 "online_url VARCHAR NOT NULL, "
+                                 "file_path VARCHAR NOT NULL, "
+                                 "title VARCHAR NOT NULL DEFAULT '', "
+                                 "original_url VARCHAR NOT NULL DEFAULT ''"
+                                 ")"));
+  ASSERT_TRUE(connection.CommitTransaction());
+  sql::Statement statement(connection.GetUniqueStatement(
+      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
+      "(offline_id, creation_time, file_size, "
+      "last_access_time, access_count, client_namespace, "
+      "client_id, online_url, file_path, expiration_time, title, original_url) "
+      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+  statement.BindInt64(0, kOfflineId);
+  statement.BindInt(1, 0);
+  statement.BindInt64(2, kFileSize);
+  statement.BindInt(3, 0);
+  statement.BindInt(4, 1);
+  statement.BindCString(5, kTestClientNamespace);
+  statement.BindString(6, kTestClientId2.id);
+  statement.BindCString(7, kTestURL);
+  statement.BindString(8, base::FilePath(kFilePath).MaybeAsASCII());
+  statement.BindInt64(9, base::Time::Now().ToInternalValue());
+  statement.BindString16(10, base::UTF8ToUTF16("Test title"));
+  statement.BindCString(11, kOriginalTestURL);
+  ASSERT_TRUE(statement.Run());
+  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
+  ASSERT_TRUE(
+      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
+}
+
+class OfflinePageMetadataStoreFactory {
+ public:
+  OfflinePageMetadataStore* BuildStore(const base::FilePath& file_path) {
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+
+  OfflinePageMetadataStore* BuildStoreM52(const base::FilePath& file_path) {
+    BuildTestStoreWithSchemaFromM52(file_path);
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+
+  OfflinePageMetadataStore* BuildStoreM53(const base::FilePath& file_path) {
+    BuildTestStoreWithSchemaFromM53(file_path);
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+
+  OfflinePageMetadataStore* BuildStoreM54(const base::FilePath& file_path) {
+    BuildTestStoreWithSchemaFromM54(file_path);
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+
+  OfflinePageMetadataStore* BuildStoreM55(const base::FilePath& file_path) {
+    BuildTestStoreWithSchemaFromM55(file_path);
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+
+  OfflinePageMetadataStore* BuildStoreM56(const base::FilePath& file_path) {
+    BuildTestStoreWithSchemaFromM56(file_path);
+    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
+        base::ThreadTaskRunnerHandle::Get(), file_path);
+    return store;
+  }
+};
+
+enum CalledCallback { NONE, LOAD, ADD, UPDATE, REMOVE, RESET };
+enum Status { STATUS_NONE, STATUS_TRUE, STATUS_FALSE };
+
+class OfflinePageMetadataStoreTest : public testing::Test {
+ public:
+  OfflinePageMetadataStoreTest();
+  ~OfflinePageMetadataStoreTest() override;
+
+  void TearDown() override {
+    // Wait for all the pieces of the store to delete itself properly.
+    PumpLoop();
+  }
+
+  std::unique_ptr<OfflinePageMetadataStore> BuildStore();
+  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM52();
+  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM53();
+  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM54();
+  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM55();
+  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM56();
+
+  void PumpLoop();
+
+  void InitializeCallback(bool success);
+  void GetOfflinePagesCallback(
+      const std::vector<OfflinePageItem>& offline_pages);
+  void AddCallback(ItemActionStatus status);
+  void UpdateCallback(CalledCallback called_callback,
+                      std::unique_ptr<OfflinePagesUpdateResult> result);
+  void ResetCallback(bool success);
+
+  void ClearResults();
+
+  OfflinePageItem CheckThatStoreHasOneItem();
+  void CheckThatOfflinePageCanBeSaved(
+      std::unique_ptr<OfflinePageMetadataStore> store);
+
+  OfflinePagesUpdateResult* last_update_result() {
+    return last_update_result_.get();
+  }
+
+ protected:
+  CalledCallback last_called_callback_;
+  Status last_status_;
+  std::unique_ptr<OfflinePagesUpdateResult> last_update_result_;
+  std::vector<OfflinePageItem> offline_pages_;
+  OfflinePageMetadataStoreFactory factory_;
+
+  base::ScopedTempDir temp_directory_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+};
+
+OfflinePageMetadataStoreTest::OfflinePageMetadataStoreTest()
+    : last_called_callback_(NONE),
+      last_status_(STATUS_NONE),
+      task_runner_(new base::TestSimpleTaskRunner),
+      task_runner_handle_(task_runner_) {
+  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
+}
+
+OfflinePageMetadataStoreTest::~OfflinePageMetadataStoreTest() {}
+
+void OfflinePageMetadataStoreTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void OfflinePageMetadataStoreTest::InitializeCallback(bool success) {
+  last_status_ = success ? STATUS_TRUE : STATUS_FALSE;
+}
+
+void OfflinePageMetadataStoreTest::GetOfflinePagesCallback(
+    const std::vector<OfflinePageItem>& offline_pages) {
+  last_called_callback_ = LOAD;
+  offline_pages_.swap(const_cast<std::vector<OfflinePageItem>&>(offline_pages));
+}
+
+void OfflinePageMetadataStoreTest::AddCallback(ItemActionStatus status) {
+  last_called_callback_ = ADD;
+  // TODO(fgorski): Add specific add status.
+  // last_item_status_ = status;
+  last_status_ =
+      status == ItemActionStatus::SUCCESS ? STATUS_TRUE : STATUS_FALSE;
+}
+
+void OfflinePageMetadataStoreTest::UpdateCallback(
+    CalledCallback called_callback,
+    std::unique_ptr<OfflinePagesUpdateResult> result) {
+  last_called_callback_ = called_callback;
+  last_status_ = result->updated_items.size() > 0 ? STATUS_TRUE : STATUS_FALSE;
+  last_update_result_ = std::move(result);
+}
+
+void OfflinePageMetadataStoreTest::ResetCallback(bool success) {
+  last_called_callback_ = RESET;
+  last_status_ = success ? STATUS_TRUE : STATUS_FALSE;
+}
+
+void OfflinePageMetadataStoreTest::ClearResults() {
+  last_called_callback_ = NONE;
+  last_status_ = STATUS_NONE;
+  offline_pages_.clear();
+  last_update_result_.reset(nullptr);
+}
+
+OfflinePageItem OfflinePageMetadataStoreTest::CheckThatStoreHasOneItem() {
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  EXPECT_EQ(1U, offline_pages_.size());
+
+  return offline_pages_[0];
+}
+
+void OfflinePageMetadataStoreTest::CheckThatOfflinePageCanBeSaved(
+    std::unique_ptr<OfflinePageMetadataStore> store) {
+  size_t store_size = offline_pages_.size();
+  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
+                               base::FilePath(kFilePath), kFileSize);
+  offline_page.title = base::UTF8ToUTF16("a title");
+  offline_page.original_url = GURL(kOriginalTestURL);
+
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ClearResults();
+
+  // Close the store first to ensure file lock is removed.
+  store.reset();
+  store = BuildStore();
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ASSERT_EQ(store_size + 1, offline_pages_.size());
+  if (store_size > 0 &&
+      offline_pages_[0].offline_id != offline_page.offline_id) {
+    std::swap(offline_pages_[0], offline_pages_[1]);
+  }
+  EXPECT_EQ(offline_page, offline_pages_[0]);
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStore() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStore(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM52() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStoreM52(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM53() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStoreM53(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM54() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStoreM54(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM55() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStoreM55(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM56() {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      factory_.BuildStoreM56(temp_directory_.GetPath()));
+  PumpLoop();
+  store->Initialize(
+      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  return store;
+}
+
+// Loads empty store and makes sure that there are no offline pages stored in
+// it.
+TEST_F(OfflinePageMetadataStoreTest, LoadEmptyStore) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  EXPECT_EQ(0U, offline_pages_.size());
+}
+
+TEST_F(OfflinePageMetadataStoreTest, GetOfflinePagesFromInvalidStore) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+  OfflinePageMetadataStoreSQL* sql_store =
+      static_cast<OfflinePageMetadataStoreSQL*>(store.get());
+
+  sql_store->SetStateForTesting(StoreState::NOT_LOADED, false);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+  EXPECT_EQ(StoreState::NOT_LOADED, store->state());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::FAILED_LOADING, false);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+  EXPECT_EQ(StoreState::FAILED_LOADING, store->state());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::FAILED_RESET, false);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+  EXPECT_EQ(StoreState::FAILED_RESET, store->state());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::LOADED, true);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::NOT_LOADED, true);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::FAILED_LOADING, false);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+
+  ClearResults();
+  sql_store->SetStateForTesting(StoreState::FAILED_RESET, false);
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0UL, offline_pages_.size());
+}
+
+// Loads a store which has an outdated schema.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion52Store) {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      BuildStoreWithSchemaFromM52());
+
+  OfflinePageItem item = CheckThatStoreHasOneItem();
+  CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
+// Loads a store which has an outdated schema.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion53Store) {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      BuildStoreWithSchemaFromM53());
+
+  OfflinePageItem item = CheckThatStoreHasOneItem();
+  CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
+// Loads a string with schema from M54.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion54Store) {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      BuildStoreWithSchemaFromM54());
+
+  OfflinePageItem item = CheckThatStoreHasOneItem();
+  CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
+// Loads a string with schema from M55.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion55Store) {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      BuildStoreWithSchemaFromM55());
+
+  OfflinePageItem item = CheckThatStoreHasOneItem();
+  CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
+// Loads a string with schema from M56.
+// This test case would crash if it's not handling correctly when we're loading
+// old version stores.
+// TODO(romax): Move this to sql_unittest.
+TEST_F(OfflinePageMetadataStoreTest, LoadVersion56Store) {
+  std::unique_ptr<OfflinePageMetadataStore> store(
+      BuildStoreWithSchemaFromM56());
+
+  OfflinePageItem item = CheckThatStoreHasOneItem();
+  CheckThatOfflinePageCanBeSaved(std::move(store));
+}
+
+// Adds metadata of an offline page into a store and then opens the store
+// again to make sure that stored metadata survives store restarts.
+TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) {
+  CheckThatOfflinePageCanBeSaved(BuildStore());
+}
+
+TEST_F(OfflinePageMetadataStoreTest, AddSameOfflinePageTwice) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
+                               base::FilePath(kFilePath), kFileSize);
+  offline_page.title = base::UTF8ToUTF16("a title");
+
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ClearResults();
+
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_FALSE, last_status_);
+}
+
+// Tests removing offline page metadata from the store, for which it first adds
+// metadata of an offline page.
+TEST_F(OfflinePageMetadataStoreTest, RemoveOfflinePage) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  // Add an offline page.
+  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
+                               base::FilePath(kFilePath), kFileSize);
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  // Get all pages from the store.
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(1U, offline_pages_.size());
+
+  // Remove the offline page.
+  std::vector<int64_t> ids_to_remove;
+  ids_to_remove.push_back(offline_page.offline_id);
+  store->RemoveOfflinePages(
+      ids_to_remove, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
+                                base::Unretained(this), REMOVE));
+  PumpLoop();
+  EXPECT_EQ(REMOVE, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ASSERT_TRUE(last_update_result() != nullptr);
+  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_update_result()->item_statuses.begin()->second);
+  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
+  EXPECT_EQ(offline_page, *(last_update_result()->updated_items.begin()));
+
+  ClearResults();
+
+  // Get all pages from the store.
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(0U, offline_pages_.size());
+
+  ClearResults();
+
+  // Close and reload the store.
+  store.reset();
+  store = BuildStore();
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  EXPECT_EQ(0U, offline_pages_.size());
+}
+
+// Adds metadata of multiple offline pages into a store and removes some.
+TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  // Add an offline page.
+  OfflinePageItem offline_page_1(GURL(kTestURL), 12345LL, kTestClientId1,
+                                 base::FilePath(kFilePath), kFileSize);
+  store->AddOfflinePage(offline_page_1,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  // Add anther offline page.
+  base::FilePath file_path_2 =
+      base::FilePath(FILE_PATH_LITERAL("//other.page.com.mhtml"));
+  OfflinePageItem offline_page_2(GURL("https://other.page.com"), 5678LL,
+                                 kTestClientId2, file_path_2, 12345,
+                                 base::Time::Now());
+  offline_page_2.original_url = GURL("https://example.com/bar");
+  store->AddOfflinePage(offline_page_2,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  // Get all pages from the store.
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(2U, offline_pages_.size());
+
+  // Remove the offline page.
+  std::vector<int64_t> ids_to_remove;
+  ids_to_remove.push_back(offline_page_1.offline_id);
+  store->RemoveOfflinePages(
+      ids_to_remove, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
+                                base::Unretained(this), REMOVE));
+  PumpLoop();
+  EXPECT_EQ(REMOVE, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ASSERT_TRUE(last_update_result() != nullptr);
+  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_update_result()->item_statuses.begin()->second);
+  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
+  EXPECT_EQ(offline_page_1, *(last_update_result()->updated_items.begin()));
+
+  ClearResults();
+
+  // Close and reload the store.
+  store.reset();
+  store = BuildStore();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ASSERT_EQ(1U, offline_pages_.size());
+  EXPECT_EQ(offline_page_2.url, offline_pages_[0].url);
+  EXPECT_EQ(offline_page_2.offline_id, offline_pages_[0].offline_id);
+  EXPECT_EQ(offline_page_2.file_path, offline_pages_[0].file_path);
+  EXPECT_EQ(offline_page_2.file_size, offline_pages_[0].file_size);
+  EXPECT_EQ(offline_page_2.creation_time, offline_pages_[0].creation_time);
+  EXPECT_EQ(offline_page_2.last_access_time,
+            offline_pages_[0].last_access_time);
+  EXPECT_EQ(offline_page_2.access_count, offline_pages_[0].access_count);
+  EXPECT_EQ(offline_page_2.client_id, offline_pages_[0].client_id);
+}
+
+// Tests updating offline page metadata from the store.
+TEST_F(OfflinePageMetadataStoreTest, UpdateOfflinePage) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  // First, adds a fresh page.
+  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
+                               base::FilePath(kFilePath), kFileSize);
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  ASSERT_EQ(1U, offline_pages_.size());
+  EXPECT_EQ(offline_page, offline_pages_[0]);
+
+  // Then update some data.
+  offline_page.file_size = kFileSize + 1;
+  offline_page.access_count++;
+  offline_page.original_url = GURL("https://example.com/bar");
+  std::vector<OfflinePageItem> items_to_update;
+  items_to_update.push_back(offline_page);
+  store->UpdateOfflinePages(
+      items_to_update, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
+                                  base::Unretained(this), UPDATE));
+  PumpLoop();
+  EXPECT_EQ(UPDATE, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+  ASSERT_TRUE(last_update_result() != nullptr);
+  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
+  EXPECT_EQ(ItemActionStatus::SUCCESS,
+            last_update_result()->item_statuses.begin()->second);
+  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
+  EXPECT_EQ(offline_page, *(last_update_result()->updated_items.begin()));
+
+  ClearResults();
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  ASSERT_EQ(1U, offline_pages_.size());
+  EXPECT_EQ(offline_page, offline_pages_[0]);
+}
+
+TEST_F(OfflinePageMetadataStoreTest, ClearAllOfflinePages) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  // Add 2 offline pages.
+  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
+                               base::FilePath(kFilePath), kFileSize);
+  store->AddOfflinePage(offline_page,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  OfflinePageItem offline_page2(GURL("http://test.com"), 5678LL, kTestClientId2,
+                                base::FilePath(kFilePath), kFileSize);
+  store->AddOfflinePage(offline_page2,
+                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
+                                   base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(ADD, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  // Get all pages from the store.
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  EXPECT_EQ(2U, offline_pages_.size());
+
+  // Clear all records from the store.
+  store->Reset(base::Bind(&OfflinePageMetadataStoreTest::ResetCallback,
+                          base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(RESET, last_called_callback_);
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+
+  ClearResults();
+
+  // Get all pages from the store.
+  store->GetOfflinePages(
+      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
+                 base::Unretained(this)));
+  PumpLoop();
+
+  EXPECT_EQ(LOAD, last_called_callback_);
+  ASSERT_EQ(0U, offline_pages_.size());
+}
+
+TEST_F(OfflinePageMetadataStoreTest, ResetStore) {
+  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
+
+  store->Reset(base::Bind(&OfflinePageMetadataStoreTest::ResetCallback,
+                          base::Unretained(this)));
+  PumpLoop();
+  EXPECT_EQ(STATUS_TRUE, last_status_);
+}
+
+}  // namespace
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_metadata_store_sql.cc b/components/offline_pages/core/offline_page_metadata_store_sql.cc
new file mode 100644
index 0000000..c8e3ee0e
--- /dev/null
+++ b/components/offline_pages/core/offline_page_metadata_store_sql.cc
@@ -0,0 +1,615 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace offline_pages {
+
+namespace {
+
+// This is a macro instead of a const so that
+// it can be used inline in other SQL statements below.
+#define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1"
+
+bool CreateOfflinePagesTable(sql::Connection* db) {
+  const char kSql[] = "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
+                      "(offline_id INTEGER PRIMARY KEY NOT NULL,"
+                      " creation_time INTEGER NOT NULL,"
+                      " file_size INTEGER NOT NULL,"
+                      " last_access_time INTEGER NOT NULL,"
+                      " access_count INTEGER NOT NULL,"
+                      " client_namespace VARCHAR NOT NULL,"
+                      " client_id VARCHAR NOT NULL,"
+                      " online_url VARCHAR NOT NULL,"
+                      " file_path VARCHAR NOT NULL,"
+                      " title VARCHAR NOT NULL DEFAULT '',"
+                      " original_url VARCHAR NOT NULL DEFAULT ''"
+                      ")";
+  return db->Execute(kSql);
+}
+
+bool UpgradeWithQuery(sql::Connection* db, const char* upgrade_sql) {
+  if (!db->Execute("ALTER TABLE " OFFLINE_PAGES_TABLE_NAME
+                   " RENAME TO temp_" OFFLINE_PAGES_TABLE_NAME)) {
+    return false;
+  }
+  if (!CreateOfflinePagesTable(db))
+    return false;
+  if (!db->Execute(upgrade_sql))
+    return false;
+  if (!db->Execute("DROP TABLE IF EXISTS temp_" OFFLINE_PAGES_TABLE_NAME))
+    return false;
+  return true;
+}
+
+bool UpgradeFrom52(sql::Connection* db) {
+  const char kSql[] =
+      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, "
+      "online_url, file_path) "
+      "SELECT "
+      "offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, "
+      "online_url, file_path "
+      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+  return UpgradeWithQuery(db, kSql);
+}
+
+bool UpgradeFrom53(sql::Connection* db) {
+  const char kSql[] =
+      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path) "
+      "SELECT "
+      "offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path "
+      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+  return UpgradeWithQuery(db, kSql);
+}
+
+bool UpgradeFrom54(sql::Connection* db) {
+  const char kSql[] =
+      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title) "
+      "SELECT "
+      "offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title "
+      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+  return UpgradeWithQuery(db, kSql);
+}
+
+bool UpgradeFrom55(sql::Connection* db) {
+  const char kSql[] =
+      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title) "
+      "SELECT "
+      "offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title "
+      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+  return UpgradeWithQuery(db, kSql);
+}
+
+bool UpgradeFrom56(sql::Connection* db) {
+  const char kSql[] =
+      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title, original_url) "
+      "SELECT "
+      "offline_id, creation_time, file_size, last_access_time, "
+      "access_count, client_namespace, client_id, online_url, "
+      "file_path, title, original_url "
+      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
+  return UpgradeWithQuery(db, kSql);
+}
+
+bool CreateSchema(sql::Connection* db) {
+  // If you create a transaction but don't Commit() it is automatically
+  // rolled back by its destructor when it falls out of scope.
+  sql::Transaction transaction(db);
+  if (!transaction.Begin())
+    return false;
+
+  if (!db->DoesTableExist(OFFLINE_PAGES_TABLE_NAME)) {
+    if (!CreateOfflinePagesTable(db))
+      return false;
+  }
+
+  // Upgrade section. Details are described in the header file.
+  if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time") &&
+      !db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
+    if (!UpgradeFrom52(db))
+      return false;
+  } else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
+    if (!UpgradeFrom53(db))
+      return false;
+  } else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "offline_url")) {
+    if (!UpgradeFrom54(db))
+      return false;
+  } else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "original_url")) {
+    if (!UpgradeFrom55(db))
+      return false;
+  } else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time")) {
+    if (!UpgradeFrom56(db))
+      return false;
+  }
+
+  // TODO(fgorski): Add indices here.
+  return transaction.Commit();
+}
+
+bool DeleteByOfflineId(sql::Connection* db, int64_t offline_id) {
+  static const char kSql[] =
+      "DELETE FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id=?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, offline_id);
+  return statement.Run();
+}
+
+base::FilePath GetPathFromUTF8String(const std::string& path_string) {
+#if defined(OS_POSIX)
+  return base::FilePath(path_string);
+#elif defined(OS_WIN)
+  return base::FilePath(base::UTF8ToWide(path_string));
+#else
+#error Unknown OS
+#endif
+}
+
+std::string GetUTF8StringFromPath(const base::FilePath& path) {
+#if defined(OS_POSIX)
+  return path.value();
+#elif defined(OS_WIN)
+  return base::WideToUTF8(path.value());
+#else
+#error Unknown OS
+#endif
+}
+
+// Create an offline page item from a SQL result.  Expects complete rows with
+// all columns present.
+OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
+  int64_t id = statement->ColumnInt64(0);
+  base::Time creation_time =
+      base::Time::FromInternalValue(statement->ColumnInt64(1));
+  int64_t file_size = statement->ColumnInt64(2);
+  base::Time last_access_time =
+      base::Time::FromInternalValue(statement->ColumnInt64(3));
+  int access_count = statement->ColumnInt(4);
+  ClientId client_id(statement->ColumnString(5), statement->ColumnString(6));
+  GURL url(statement->ColumnString(7));
+  base::FilePath path(GetPathFromUTF8String(statement->ColumnString(8)));
+  base::string16 title = statement->ColumnString16(9);
+  GURL original_url(statement->ColumnString(10));
+
+  OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
+  item.last_access_time = last_access_time;
+  item.access_count = access_count;
+  item.title = title;
+  item.original_url = original_url;
+  return item;
+}
+
+ItemActionStatus Insert(sql::Connection* db, const OfflinePageItem& item) {
+  // Using 'INSERT OR FAIL' or 'INSERT OR ABORT' in the query below causes debug
+  // builds to DLOG.
+  const char kSql[] =
+      "INSERT OR IGNORE INTO " OFFLINE_PAGES_TABLE_NAME
+      " (offline_id, online_url, client_namespace, client_id, file_path, "
+      "file_size, creation_time, last_access_time, access_count, "
+      "title, original_url)"
+      " VALUES "
+      " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, item.offline_id);
+  statement.BindString(1, item.url.spec());
+  statement.BindString(2, item.client_id.name_space);
+  statement.BindString(3, item.client_id.id);
+  statement.BindString(4, GetUTF8StringFromPath(item.file_path));
+  statement.BindInt64(5, item.file_size);
+  statement.BindInt64(6, item.creation_time.ToInternalValue());
+  statement.BindInt64(7, item.last_access_time.ToInternalValue());
+  statement.BindInt(8, item.access_count);
+  statement.BindString16(9, item.title);
+  statement.BindString(10, item.original_url.spec());
+  if (!statement.Run())
+    return ItemActionStatus::STORE_ERROR;
+  if (db->GetLastChangeCount() == 0)
+    return ItemActionStatus::ALREADY_EXISTS;
+  return ItemActionStatus::SUCCESS;
+}
+
+bool Update(sql::Connection* db, const OfflinePageItem& item) {
+  const char kSql[] =
+      "UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
+      " SET online_url = ?, client_namespace = ?, client_id = ?, file_path = ?,"
+      " file_size = ?, creation_time = ?, last_access_time = ?,"
+      " access_count = ?, title = ?, original_url = ?"
+      " WHERE offline_id = ?";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindString(0, item.url.spec());
+  statement.BindString(1, item.client_id.name_space);
+  statement.BindString(2, item.client_id.id);
+  statement.BindString(3, GetUTF8StringFromPath(item.file_path));
+  statement.BindInt64(4, item.file_size);
+  statement.BindInt64(5, item.creation_time.ToInternalValue());
+  statement.BindInt64(6, item.last_access_time.ToInternalValue());
+  statement.BindInt(7, item.access_count);
+  statement.BindString16(8, item.title);
+  statement.BindString(9, item.original_url.spec());
+  statement.BindInt64(10, item.offline_id);
+  return statement.Run() && db->GetLastChangeCount() > 0;
+}
+
+bool InitDatabase(sql::Connection* db, base::FilePath path) {
+  db->set_page_size(4096);
+  db->set_cache_size(500);
+  db->set_histogram_tag("OfflinePageMetadata");
+  db->set_exclusive_locking();
+
+  base::File::Error err;
+  if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) {
+    LOG(ERROR) << "Failed to create offline pages db directory: "
+               << base::File::ErrorToString(err);
+    return false;
+  }
+  if (!db->Open(path)) {
+    LOG(ERROR) << "Failed to open database";
+    return false;
+  }
+  db->Preload();
+
+  return CreateSchema(db);
+}
+
+void NotifyLoadResult(scoped_refptr<base::SingleThreadTaskRunner> runner,
+                      const OfflinePageMetadataStore::LoadCallback& callback,
+                      OfflinePageMetadataStore::LoadStatus status,
+                      const std::vector<OfflinePageItem>& result) {
+  // TODO(fgorski): Switch to SQL specific UMA metrics.
+  UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status,
+                            OfflinePageMetadataStore::LOAD_STATUS_COUNT);
+  if (status == OfflinePageMetadataStore::LOAD_SUCCEEDED) {
+    UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount",
+                         static_cast<int32_t>(result.size()));
+  } else {
+    DVLOG(1) << "Offline pages database loading failed: " << status;
+  }
+  runner->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+void OpenConnectionSync(sql::Connection* db,
+                        scoped_refptr<base::SingleThreadTaskRunner> runner,
+                        const base::FilePath& path,
+                        const base::Callback<void(bool)>& callback) {
+  bool success = InitDatabase(db, path);
+  runner->PostTask(FROM_HERE, base::Bind(callback, success));
+}
+
+bool GetPageByOfflineIdSync(sql::Connection* db,
+                            int64_t offline_id,
+                            OfflinePageItem* item) {
+  const char kSql[] =
+      "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id = ?";
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, offline_id);
+
+  if (statement.Step()) {
+    *item = MakeOfflinePageItem(&statement);
+    return true;
+  }
+
+  return false;
+}
+
+void GetOfflinePagesSync(
+    sql::Connection* db,
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const OfflinePageMetadataStore::LoadCallback& callback) {
+  const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME;
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+  std::vector<OfflinePageItem> result;
+  while (statement.Step())
+    result.push_back(MakeOfflinePageItem(&statement));
+
+  if (statement.Succeeded()) {
+    NotifyLoadResult(runner, callback, OfflinePageMetadataStore::LOAD_SUCCEEDED,
+                     result);
+  } else {
+    result.clear();
+    NotifyLoadResult(runner, callback,
+                     OfflinePageMetadataStore::STORE_LOAD_FAILED, result);
+  }
+}
+
+void AddOfflinePageSync(sql::Connection* db,
+                        scoped_refptr<base::SingleThreadTaskRunner> runner,
+                        const OfflinePageItem& offline_page,
+                        const OfflinePageMetadataStore::AddCallback& callback) {
+  ItemActionStatus status = Insert(db, offline_page);
+  runner->PostTask(FROM_HERE, base::Bind(callback, status));
+}
+
+void PostStoreUpdateResultForIds(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    StoreState store_state,
+    const std::vector<int64_t>& offline_ids,
+    ItemActionStatus action_status,
+    const OfflinePageMetadataStore::UpdateCallback& callback) {
+  std::unique_ptr<OfflinePagesUpdateResult> result(
+      new OfflinePagesUpdateResult(store_state));
+  for (const auto& offline_id : offline_ids)
+    result->item_statuses.push_back(std::make_pair(offline_id, action_status));
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void PostStoreErrorForAllPages(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const std::vector<OfflinePageItem>& pages,
+    const OfflinePageMetadataStore::UpdateCallback& callback) {
+  std::vector<int64_t> offline_ids;
+  for (const auto& page : pages)
+    offline_ids.push_back(page.offline_id);
+  PostStoreUpdateResultForIds(runner, StoreState::LOADED, offline_ids,
+                              ItemActionStatus::STORE_ERROR, callback);
+}
+
+void PostStoreErrorForAllIds(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const std::vector<int64_t>& offline_ids,
+    const OfflinePageMetadataStore::UpdateCallback& callback) {
+  PostStoreUpdateResultForIds(runner, StoreState::LOADED, offline_ids,
+                              ItemActionStatus::STORE_ERROR, callback);
+}
+
+void UpdateOfflinePagesSync(
+    sql::Connection* db,
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const std::vector<OfflinePageItem>& pages,
+    const OfflinePageMetadataStore::UpdateCallback& callback) {
+  std::unique_ptr<OfflinePagesUpdateResult> result(
+      new OfflinePagesUpdateResult(StoreState::LOADED));
+
+  sql::Transaction transaction(db);
+  if (!transaction.Begin()) {
+    PostStoreErrorForAllPages(runner, pages, callback);
+    return;
+  }
+
+  for (const auto& page : pages) {
+    if (Update(db, page)) {
+      result->updated_items.push_back(page);
+      result->item_statuses.push_back(
+          std::make_pair(page.offline_id, ItemActionStatus::SUCCESS));
+    } else {
+      result->item_statuses.push_back(
+          std::make_pair(page.offline_id, ItemActionStatus::NOT_FOUND));
+    }
+  }
+
+  if (!transaction.Commit()) {
+    PostStoreErrorForAllPages(runner, pages, callback);
+    return;
+  }
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void RemoveOfflinePagesSync(
+    const std::vector<int64_t>& offline_ids,
+    sql::Connection* db,
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    const OfflinePageMetadataStore::UpdateCallback& callback) {
+  // TODO(fgorski): Perhaps add metrics here.
+  std::unique_ptr<OfflinePagesUpdateResult> result(
+      new OfflinePagesUpdateResult(StoreState::LOADED));
+
+  // If you create a transaction but don't Commit() it is automatically
+  // rolled back by its destructor when it falls out of scope.
+  sql::Transaction transaction(db);
+  if (!transaction.Begin()) {
+    PostStoreErrorForAllIds(runner, offline_ids, callback);
+    return;
+  }
+
+  for (int64_t offline_id : offline_ids) {
+    OfflinePageItem page;
+    ItemActionStatus status;
+    if (!GetPageByOfflineIdSync(db, offline_id, &page)) {
+      status = ItemActionStatus::NOT_FOUND;
+    } else if (!DeleteByOfflineId(db, offline_id)) {
+      status = ItemActionStatus::STORE_ERROR;
+    } else {
+      status = ItemActionStatus::SUCCESS;
+      result->updated_items.push_back(page);
+    }
+
+    result->item_statuses.push_back(std::make_pair(offline_id, status));
+  }
+
+  if (!transaction.Commit()) {
+    PostStoreErrorForAllIds(runner, offline_ids, callback);
+    return;
+  }
+
+  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
+}
+
+void ResetSync(sql::Connection* db,
+               const base::FilePath& db_file_path,
+               scoped_refptr<base::SingleThreadTaskRunner> runner,
+               const base::Callback<void(bool)>& callback) {
+  // This method deletes the content of the whole store and reinitializes it.
+  bool success = true;
+  if (db) {
+    success = db->Raze();
+    db->Close();
+  }
+  success = base::DeleteFile(db_file_path, true /*recursive*/) && success;
+  runner->PostTask(FROM_HERE, base::Bind(callback, success));
+}
+
+}  // anonymous namespace
+
+OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+    const base::FilePath& path)
+    : background_task_runner_(std::move(background_task_runner)),
+      db_file_path_(path.AppendASCII("OfflinePages.db")),
+      state_(StoreState::NOT_LOADED),
+      weak_ptr_factory_(this) {
+}
+
+OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {
+  if (db_.get() &&
+      !background_task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
+    DLOG(WARNING) << "SQL database will not be deleted.";
+  }
+}
+
+void OfflinePageMetadataStoreSQL::Initialize(
+    const InitializeCallback& callback) {
+  DCHECK(!db_);
+  db_.reset(new sql::Connection());
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&OpenConnectionSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), db_file_path_,
+                 base::Bind(&OfflinePageMetadataStoreSQL::OnOpenConnectionDone,
+                            weak_ptr_factory_.GetWeakPtr(), callback)));
+}
+
+void OfflinePageMetadataStoreSQL::GetOfflinePages(
+    const LoadCallback& callback) {
+  if (!CheckDb()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, std::vector<OfflinePageItem>()));
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&GetOfflinePagesSync, db_.get(),
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void OfflinePageMetadataStoreSQL::AddOfflinePage(
+    const OfflinePageItem& offline_page,
+    const AddCallback& callback) {
+  if (!CheckDb()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, ItemActionStatus::STORE_ERROR));
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&AddOfflinePageSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), offline_page, callback));
+}
+
+void OfflinePageMetadataStoreSQL::UpdateOfflinePages(
+    const std::vector<OfflinePageItem>& pages,
+    const UpdateCallback& callback) {
+  if (!CheckDb()) {
+    PostStoreErrorForAllPages(base::ThreadTaskRunnerHandle::Get(), pages,
+                              callback);
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&UpdateOfflinePagesSync, db_.get(),
+                 base::ThreadTaskRunnerHandle::Get(), pages, callback));
+}
+
+void OfflinePageMetadataStoreSQL::RemoveOfflinePages(
+    const std::vector<int64_t>& offline_ids,
+    const UpdateCallback& callback) {
+  if (!CheckDb()) {
+    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), offline_ids,
+                            callback);
+    return;
+  }
+
+  if (offline_ids.empty()) {
+    // Nothing to do, but post a callback instead of calling directly
+    // to preserve the async style behavior to prevent bugs.
+    PostStoreUpdateResultForIds(
+        base::ThreadTaskRunnerHandle::Get(), state(), offline_ids,
+        ItemActionStatus::NOT_FOUND /* will be ignored */, callback);
+    return;
+  }
+
+  background_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&RemoveOfflinePagesSync, offline_ids, db_.get(),
+                            base::ThreadTaskRunnerHandle::Get(), callback));
+}
+
+void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) {
+  background_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ResetSync, db_.get(), db_file_path_,
+                 base::ThreadTaskRunnerHandle::Get(),
+                 base::Bind(&OfflinePageMetadataStoreSQL::OnResetDone,
+                            weak_ptr_factory_.GetWeakPtr(), callback)));
+}
+
+StoreState OfflinePageMetadataStoreSQL::state() const {
+  return state_;
+}
+
+void OfflinePageMetadataStoreSQL::SetStateForTesting(StoreState state,
+                                                     bool reset_db) {
+  state_ = state;
+  if (reset_db)
+    db_.reset(nullptr);
+}
+
+void OfflinePageMetadataStoreSQL::OnOpenConnectionDone(
+    const InitializeCallback& callback,
+    bool success) {
+  DCHECK(db_.get());
+  state_ = success ? StoreState::LOADED : StoreState::FAILED_LOADING;
+  callback.Run(success);
+}
+
+void OfflinePageMetadataStoreSQL::OnResetDone(const ResetCallback& callback,
+                                              bool success) {
+  state_ = success ? StoreState::NOT_LOADED : StoreState::FAILED_RESET;
+  db_.reset();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                base::Bind(callback, success));
+}
+
+bool OfflinePageMetadataStoreSQL::CheckDb() const {
+  return db_ && state_ == StoreState::LOADED;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_metadata_store_sql.h b/components/offline_pages/core/offline_page_metadata_store_sql.h
new file mode 100644
index 0000000..0426526
--- /dev/null
+++ b/components/offline_pages/core/offline_page_metadata_store_sql.h
@@ -0,0 +1,105 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace sql {
+class Connection;
+}
+
+namespace offline_pages {
+
+// OfflinePageMetadataStoreSQL is an instance of OfflinePageMetadataStore
+// which is implemented using a SQLite database.
+//
+// This store has a history of schema updates in pretty much every release.
+// Original schema was delivered in M52. Since then, the following changes
+// happened:
+// * In M53 expiration_time was added,
+// * In M54 title was added,
+// * In M55 we dropped the following fields (never used): version, status,
+//   offline_url, user_initiated.
+// * In M56 original_url was added.
+// * In M57 expiration_time was dropped. Existing expired pages would be
+//   removed when metadata consistency check happens.
+//
+// Here is a procedure to update the schema for this store:
+// * Decide how to detect that the store is on a particular version, which
+//   typically means that a certain field exists or is missing. This happens in
+//   Upgrade section of |CreateSchema|
+// * Work out appropriate change and apply it to all existing upgrade paths. In
+//   the interest of performing a single update of the store, it upgrades from a
+//   detected version to the current one. This means that when making a change,
+//   more than a single query may have to be updated (in case of fields being
+//   removed or needed to be initialized to a specific, non-default value).
+//   Such approach is preferred to doing N updates for every changed version on
+//   a startup after browser update.
+// * New upgrade method should specify which version it is upgrading from, e.g.
+//   |UpgradeFrom54|.
+// * Upgrade should use |UpgradeWithQuery| and simply specify SQL command to
+//   move data from old table (prefixed by temp_) to the new one.
+class OfflinePageMetadataStoreSQL : public OfflinePageMetadataStore {
+ public:
+  OfflinePageMetadataStoreSQL(
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      const base::FilePath& database_dir);
+  ~OfflinePageMetadataStoreSQL() override;
+
+  // Implementation methods.
+  void Initialize(const InitializeCallback& callback) override;
+  void GetOfflinePages(const LoadCallback& callback) override;
+  void AddOfflinePage(const OfflinePageItem& offline_page,
+                      const AddCallback& callback) override;
+  void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
+                          const UpdateCallback& callback) override;
+  void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
+                          const UpdateCallback& callback) override;
+  void Reset(const ResetCallback& callback) override;
+  StoreState state() const override;
+
+  // Helper function used to force incorrect state for testing purposes.
+  void SetStateForTesting(StoreState state, bool reset_db);
+
+ private:
+  // Used to conclude opening/resetting DB connection.
+  void OnOpenConnectionDone(const InitializeCallback& callback, bool success);
+  void OnResetDone(const ResetCallback& callback, bool success);
+
+  // Checks whether a valid DB connection is present and store state is LOADED.
+  bool CheckDb() const;
+
+  // Background thread where all SQL access should be run.
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+  // Path to the database on disk.
+  base::FilePath db_file_path_;
+
+  // Database connection.
+  std::unique_ptr<sql::Connection> db_;
+
+  // State of the store.
+  StoreState state_;
+
+  base::WeakPtrFactory<OfflinePageMetadataStoreSQL> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageMetadataStoreSQL);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
diff --git a/components/offline_pages/core/offline_page_model.cc b/components/offline_pages/core/offline_page_model.cc
new file mode 100644
index 0000000..4b0d281
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model.h"
+
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+const int64_t OfflinePageModel::kInvalidOfflineId;
+
+OfflinePageModel::SavePageParams::SavePageParams()
+    : proposed_offline_id(OfflinePageModel::kInvalidOfflineId) {
+}
+
+OfflinePageModel::SavePageParams::SavePageParams(const SavePageParams& other) {
+  url = other.url;
+  client_id = other.client_id;
+  proposed_offline_id = other.proposed_offline_id;
+  original_url = other.original_url;
+}
+
+// static
+bool OfflinePageModel::CanSaveURL(const GURL& url) {
+  return url.is_valid() && url.SchemeIsHTTPOrHTTPS();
+}
+
+OfflinePageModel::OfflinePageModel() {}
+
+OfflinePageModel::~OfflinePageModel() {}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model.h b/components/offline_pages/core/offline_page_model.h
new file mode 100644
index 0000000..584d81a
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model.h
@@ -0,0 +1,183 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/supports_user_data.h"
+#include "components/offline_pages/core/offline_event_logger.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
+
+class GURL;
+
+namespace offline_pages {
+
+struct ClientId;
+
+// Service for saving pages offline, storing the offline copy and metadata, and
+// retrieving them upon request.
+//
+// Example usage:
+//   class ArchiverImpl : public OfflinePageArchiver {
+//     // This is a class that knows how to create archiver
+//     void CreateArchiver(...) override;
+//     ...
+//   }
+//
+//   // In code using the OfflinePagesModel to save a page:
+//   std::unique_ptr<ArchiverImpl> archiver(new ArchiverImpl());
+//   // Callback is of type SavePageCallback.
+//   model->SavePage(url, std::move(archiver), callback);
+//
+// TODO(fgorski): Things to describe:
+// * how to cancel requests and what to expect
+class OfflinePageModel : public base::SupportsUserData {
+ public:
+  // Controls how to search on differnt URLs for pages.
+  enum class URLSearchMode {
+    // Match against the last committed URL only.
+    SEARCH_BY_FINAL_URL_ONLY,
+    // Match against all stored URLs, including the last committed URL and
+    // the original request URL.
+    SEARCH_BY_ALL_URLS
+  };
+
+  // Describes the parameters to control how to save a page.
+  struct SavePageParams {
+    SavePageParams();
+    SavePageParams(const SavePageParams& other);
+
+    // The last committed URL of the page to save.
+    GURL url;
+
+    // The identification used by the client.
+    ClientId client_id;
+
+    // Used for the offline_id for the saved file if non-zero. If it is
+    // kInvalidOfflineId, a new, random ID will be generated.
+    int64_t proposed_offline_id;
+
+    // The original URL of the page to save. Empty if no redirect occurs.
+    GURL original_url;
+  };
+
+  // Observer of the OfflinePageModel.
+  class Observer {
+   public:
+    // Invoked when the model has finished loading.
+    virtual void OfflinePageModelLoaded(OfflinePageModel* model) = 0;
+
+    // Invoked when the model is being updated due to adding an offline page.
+    virtual void OfflinePageAdded(OfflinePageModel* model,
+                                  const OfflinePageItem& added_page) = 0;
+
+    // Invoked when an offline copy related to |offline_id| was deleted.
+    virtual void OfflinePageDeleted(int64_t offline_id,
+                                    const ClientId& client_id) = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  using CheckPagesExistOfflineResult =
+      offline_pages::CheckPagesExistOfflineResult;
+  using MultipleOfflinePageItemResult =
+      offline_pages::MultipleOfflinePageItemResult;
+  using DeletePageResult = offline_pages::DeletePageResult;
+  using SavePageResult = offline_pages::SavePageResult;
+
+  // Returns true if saving an offline page may be attempted for |url|.
+  static bool CanSaveURL(const GURL& url);
+
+  OfflinePageModel();
+  ~OfflinePageModel() override;
+
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  static const int64_t kInvalidOfflineId = 0;
+
+  // Attempts to save a page offline per |save_page_params|. Requires that the
+  // model is loaded.  Generates a new offline id or uses the proposed offline
+  // id in |save_page_params| and returns it.
+  virtual void SavePage(const SavePageParams& save_page_params,
+                        std::unique_ptr<OfflinePageArchiver> archiver,
+                        const SavePageCallback& callback) = 0;
+
+  // Marks that the offline page related to the passed |offline_id| has been
+  // accessed. Its access info, including last access time and access count,
+  // will be updated. Requires that the model is loaded.
+  virtual void MarkPageAccessed(int64_t offline_id) = 0;
+
+  // Deletes pages based on |offline_ids|.
+  virtual void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
+                                      const DeletePageCallback& callback) = 0;
+
+  // Deletes all pages associated with any of |client_ids|.
+  virtual void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
+                                      const DeletePageCallback& callback) = 0;
+
+  virtual void GetPagesMatchingQuery(
+      std::unique_ptr<OfflinePageModelQuery> query,
+      const MultipleOfflinePageItemCallback& callback) = 0;
+
+  // Retrieves all pages associated with any of |client_ids|.
+  virtual void GetPagesByClientIds(
+      const std::vector<ClientId>& client_ids,
+      const MultipleOfflinePageItemCallback& callback) = 0;
+
+  // Deletes cached offline pages matching the URL predicate.
+  virtual void DeleteCachedPagesByURLPredicate(
+      const UrlPredicate& predicate,
+      const DeletePageCallback& callback) = 0;
+
+  // Returns via callback all GURLs in |urls| that are equal to the online URL
+  // of any offline page.
+  virtual void CheckPagesExistOffline(
+      const std::set<GURL>& urls,
+      const CheckPagesExistOfflineCallback& callback) = 0;
+
+  // Gets all offline pages.
+  virtual void GetAllPages(const MultipleOfflinePageItemCallback& callback) = 0;
+
+  // Gets all offline ids where the offline page has the matching client id.
+  virtual void GetOfflineIdsForClientId(
+      const ClientId& client_id,
+      const MultipleOfflineIdCallback& callback) = 0;
+
+  // Returns zero or one offline pages associated with a specified |offline_id|.
+  virtual void GetPageByOfflineId(
+      int64_t offline_id,
+      const SingleOfflinePageItemCallback& callback) = 0;
+
+  // Returns the offline pages that are related to |url|. |url_search_mode|
+  // controls how the url match is done. See URLSearchMode for more details.
+  virtual void GetPagesByURL(
+      const GURL& url,
+      URLSearchMode url_search_mode,
+      const MultipleOfflinePageItemCallback& callback) = 0;
+
+  // Returns the policy controller.
+  virtual ClientPolicyController* GetPolicyController() = 0;
+
+  // TODO(dougarnett): Remove this and its uses.
+  virtual bool is_loaded() const = 0;
+
+  // Returns the logger. Ownership is retained by the model.
+  virtual OfflineEventLogger* GetLogger() = 0;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_H_
diff --git a/components/offline_pages/core/offline_page_model_event_logger.cc b/components/offline_pages/core/offline_page_model_event_logger.cc
new file mode 100644
index 0000000..fe688a1
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_event_logger.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
+
+namespace offline_pages {
+
+void OfflinePageModelEventLogger::RecordPageSaved(const std::string& name_space,
+                                                  const std::string& url,
+                                                  const std::string& id) {
+  RecordActivity(url + " is saved at " + name_space + " with id " + id);
+}
+
+void OfflinePageModelEventLogger::RecordPageDeleted(const std::string& id) {
+  RecordActivity("Page with ID " + id + " has been deleted");
+}
+
+void OfflinePageModelEventLogger::RecordPageExpired(const std::string& id) {
+  RecordActivity("Page with ID " + id + " has been expired");
+}
+
+void OfflinePageModelEventLogger::RecordStoreClearError() {
+  RecordActivity("Offline store clear failed");
+}
+
+void OfflinePageModelEventLogger::RecordStoreCleared() {
+  RecordActivity("Offline store cleared");
+}
+
+void OfflinePageModelEventLogger::RecordStoreReloadError() {
+  RecordActivity("There was an error reloading the offline store");
+}
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model_event_logger.h b/components/offline_pages/core/offline_page_model_event_logger.h
new file mode 100644
index 0000000..fce8967
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_event_logger.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
+
+#include "components/offline_pages/core/offline_event_logger.h"
+
+namespace offline_pages {
+
+class OfflinePageModelEventLogger : public OfflineEventLogger {
+ public:
+  // Records that a page has been saved for |name_space| with |url|
+  // and |offline_id|.
+  void RecordPageSaved(const std::string& name_space,
+                       const std::string& url,
+                       const std::string& offline_id);
+
+  // Records that a page with |offline_id| has been deleted.
+  void RecordPageDeleted(const std::string& offline_id);
+
+  // Records that a page with |offline_id| has been expired.
+  void RecordPageExpired(const std::string& offline_id);
+
+  // Records that the offline store has been cleared.
+  void RecordStoreCleared();
+
+  // Records that there was an error when clearing the offline store.
+  void RecordStoreClearError();
+
+  // Records that there was an error when reloading the offline store.
+  void RecordStoreReloadError();
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
diff --git a/components/offline_pages/core/offline_page_model_event_logger_unittest.cc b/components/offline_pages/core/offline_page_model_event_logger_unittest.cc
new file mode 100644
index 0000000..c623a0ff
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_event_logger_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+namespace {
+
+const char kNamespace[] = "last_n";
+const char kUrl[] = "http://www.wikipedia.org";
+const char kOfflineId[] = "foobar";
+const int kTimeLength = 21;
+const char kPageSaved[] =
+    "http://www.wikipedia.org is saved at last_n with id foobar";
+const char kPageDeleted[] = "Page with ID foobar has been deleted";
+const char kPageExpired[] = "Page with ID foobar has been expired";
+const char kRecordStoreClearError[] = "Offline store clear failed";
+const char kRecordStoreCleared[] = "Offline store cleared";
+const char kRecordStoreReloadError[] =
+    "There was an error reloading the offline store";
+
+}  // namespace
+
+TEST(OfflinePageModelEventLoggerTest, RecordsWhenLoggingIsOn) {
+  OfflinePageModelEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(true);
+  logger.RecordStoreCleared();
+  logger.RecordPageSaved(kNamespace, kUrl, kOfflineId);
+  logger.RecordPageDeleted(kOfflineId);
+  logger.RecordPageExpired(kOfflineId);
+  logger.RecordStoreClearError();
+  logger.RecordStoreReloadError();
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(6u, log.size());
+  EXPECT_EQ(std::string(kRecordStoreCleared), log[5].substr(kTimeLength));
+  EXPECT_EQ(std::string(kPageSaved), log[4].substr(kTimeLength));
+  EXPECT_EQ(std::string(kPageDeleted), log[3].substr(kTimeLength));
+  EXPECT_EQ(std::string(kPageExpired), log[2].substr(kTimeLength));
+  EXPECT_EQ(std::string(kRecordStoreClearError), log[1].substr(kTimeLength));
+  EXPECT_EQ(std::string(kRecordStoreReloadError), log[0].substr(kTimeLength));
+}
+
+TEST(OfflinePageModelEventLoggerTest, DoesNotRecordWhenLoggingIsOff) {
+  OfflinePageModelEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(false);
+  logger.RecordStoreCleared();
+  logger.RecordPageSaved(kNamespace, kUrl, kOfflineId);
+  logger.RecordPageDeleted(kOfflineId);
+  logger.RecordPageExpired(kOfflineId);
+  logger.RecordStoreClearError();
+  logger.RecordStoreReloadError();
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(0u, log.size());
+}
+
+TEST(OfflinePageModelEventLoggerTest, DoesNotExceedMaxSize) {
+  OfflinePageModelEventLogger logger;
+  std::vector<std::string> log;
+
+  logger.SetIsLogging(true);
+  for (size_t i = 0; i < kMaxLogCount + 1; ++i) {
+    logger.RecordStoreCleared();
+  }
+  logger.GetLogs(&log);
+
+  EXPECT_EQ(kMaxLogCount, log.size());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model_impl.cc b/components/offline_pages/core/offline_page_model_impl.cc
new file mode 100644
index 0000000..011dfbe
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_impl.cc
@@ -0,0 +1,1094 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model_impl.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "url/gurl.h"
+
+using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult;
+using ClearStorageCallback =
+    offline_pages::OfflinePageStorageManager::ClearStorageCallback;
+using ClearStorageResult =
+    offline_pages::OfflinePageStorageManager::ClearStorageResult;
+
+namespace offline_pages {
+
+namespace {
+
+// The delay used to schedule the first clear storage request for storage
+// manager after the model is loaded.
+const base::TimeDelta kStorageManagerStartingDelay =
+    base::TimeDelta::FromSeconds(20);
+
+int64_t GenerateOfflineId() {
+  return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
+}
+
+// The maximum histogram size for the metrics that measure time between views of
+// a given page.
+const base::TimeDelta kMaxOpenedPageHistogramBucket =
+    base::TimeDelta::FromDays(90);
+
+SavePageResult ToSavePageResult(ArchiverResult archiver_result) {
+  SavePageResult result;
+  switch (archiver_result) {
+    case ArchiverResult::SUCCESSFULLY_CREATED:
+      result = SavePageResult::SUCCESS;
+      break;
+    case ArchiverResult::ERROR_DEVICE_FULL:
+      result = SavePageResult::DEVICE_FULL;
+      break;
+    case ArchiverResult::ERROR_CONTENT_UNAVAILABLE:
+      result = SavePageResult::CONTENT_UNAVAILABLE;
+      break;
+    case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED:
+      result = SavePageResult::ARCHIVE_CREATION_FAILED;
+      break;
+    case ArchiverResult::ERROR_CANCELED:
+      result = SavePageResult::CANCELLED;
+      break;
+    case ArchiverResult::ERROR_SECURITY_CERTIFICATE:
+      result = SavePageResult::SECURITY_CERTIFICATE_ERROR;
+      break;
+    default:
+      NOTREACHED();
+      result = SavePageResult::CONTENT_UNAVAILABLE;
+  }
+  return result;
+}
+
+std::string AddHistogramSuffix(const ClientId& client_id,
+                               const char* histogram_name) {
+  if (client_id.name_space.empty()) {
+    NOTREACHED();
+    return histogram_name;
+  }
+  std::string adjusted_histogram_name(histogram_name);
+  adjusted_histogram_name += ".";
+  adjusted_histogram_name += client_id.name_space;
+  return adjusted_histogram_name;
+}
+
+void ReportStorageHistogramsAfterSave(
+    const ArchiveManager::StorageStats& storage_stats) {
+  const int kMB = 1024 * 1024;
+  int free_disk_space_mb =
+      static_cast<int>(storage_stats.free_disk_space / kMB);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.SavePage.FreeSpaceMB",
+                              free_disk_space_mb, 1, 500000, 50);
+
+  int total_page_size_mb =
+      static_cast<int>(storage_stats.total_archives_size / kMB);
+  UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb);
+}
+
+void ReportStorageHistogramsAfterDelete(
+    const ArchiveManager::StorageStats& storage_stats) {
+  const int kMB = 1024 * 1024;
+  int free_disk_space_mb =
+      static_cast<int>(storage_stats.free_disk_space / kMB);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DeletePage.FreeSpaceMB",
+                              free_disk_space_mb, 1, 500000, 50);
+
+  int total_page_size_mb =
+      static_cast<int>(storage_stats.total_archives_size / kMB);
+  UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb);
+
+  if (storage_stats.free_disk_space > 0) {
+    int percentage_of_free = static_cast<int>(
+        1.0 * storage_stats.total_archives_size /
+        (storage_stats.total_archives_size + storage_stats.free_disk_space) *
+        100);
+    UMA_HISTOGRAM_PERCENTAGE(
+        "OfflinePages.DeletePage.TotalPageSizeAsPercentageOfFreeSpace",
+        percentage_of_free);
+  }
+}
+
+void ReportSavePageResultHistogramAfterSave(const ClientId& client_id,
+                                            SavePageResult result) {
+  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+      AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"), 1,
+      static_cast<int>(SavePageResult::RESULT_COUNT),
+      static_cast<int>(SavePageResult::RESULT_COUNT) + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(static_cast<int>(result));
+}
+
+// Goes through the list of offline pages, compiling the following two metrics:
+//   - a count of the pages with the same URL
+//   - The difference between the |created_before| time and the creation time of
+//     the page with the closest creation time before |created_before|.
+// Returns true if there was a page that was saved before |created_before| with
+// a matching URL.
+bool GetMatchingURLCountAndMostRecentCreationTime(
+    const std::map<int64_t, OfflinePageItem>& offline_pages,
+    std::string name_space,
+    const GURL& url,
+    base::Time created_before,
+    int* matching_url_count,
+    base::TimeDelta* most_recent_creation_time) {
+  int count = 0;
+
+  // Create a time that is very old, so that any valid time will be newer than
+  // it.
+  base::Time latest_time;
+  bool matching_page = false;
+
+  for (auto& id_page_pair : offline_pages) {
+    if (id_page_pair.second.client_id.name_space == name_space &&
+        url == id_page_pair.second.url) {
+      count++;
+      base::Time page_creation_time = id_page_pair.second.creation_time;
+      if (page_creation_time < created_before &&
+          page_creation_time > latest_time) {
+        latest_time = page_creation_time;
+        matching_page = true;
+      }
+    }
+  }
+
+  if (matching_url_count != nullptr)
+    *matching_url_count = count;
+  if (most_recent_creation_time != nullptr && latest_time != base::Time())
+    *most_recent_creation_time = created_before - latest_time;
+
+  return matching_page;
+}
+
+void ReportPageHistogramAfterSave(
+    ClientPolicyController* policy_controller_,
+    const std::map<int64_t, OfflinePageItem>& offline_pages,
+    const OfflinePageItem& offline_page,
+    const base::Time& save_time) {
+  DCHECK(policy_controller_);
+  // The histogram below is an expansion of the UMA_HISTOGRAM_TIMES
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
+      AddHistogramSuffix(offline_page.client_id, "OfflinePages.SavePageTime"),
+      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(10),
+      50, base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->AddTime(save_time - offline_page.creation_time);
+
+  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  // Reported as Kb between 1Kb and 10Mb.
+  histogram = base::Histogram::FactoryGet(
+      AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"), 1,
+      10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(offline_page.file_size / 1024);
+
+  if (policy_controller_->IsSupportedByDownload(
+          offline_page.client_id.name_space)) {
+    int matching_url_count;
+    base::TimeDelta time_since_most_recent_duplicate;
+    if (GetMatchingURLCountAndMostRecentCreationTime(
+            offline_pages, offline_page.client_id.name_space, offline_page.url,
+            offline_page.creation_time, &matching_url_count,
+            &time_since_most_recent_duplicate)) {
+      // Using CUSTOM_COUNTS instead of time-oriented histogram to record
+      // samples in seconds rather than milliseconds.
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved",
+          time_since_most_recent_duplicate.InSeconds(),
+          base::TimeDelta::FromSeconds(1).InSeconds(),
+          base::TimeDelta::FromDays(7).InSeconds(), 50);
+    }
+    UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DownloadSavedPageDuplicateCount",
+                                matching_url_count, 1, 20, 10);
+  }
+}
+
+void ReportPageHistogramsAfterDelete(
+    const std::map<int64_t, OfflinePageItem>& offline_pages,
+    const std::vector<OfflinePageItem>& deleted_pages,
+    const base::Time& delete_time) {
+  const int max_minutes = base::TimeDelta::FromDays(365).InMinutes();
+  int64_t total_size = 0;
+
+  for (const auto& page : deleted_pages) {
+    total_size += page.file_size;
+    ClientId client_id = page.client_id;
+
+    if (client_id.name_space == kDownloadNamespace) {
+      int remaining_pages_with_url;
+      GetMatchingURLCountAndMostRecentCreationTime(
+          offline_pages, page.client_id.name_space, page.url, base::Time::Max(),
+          &remaining_pages_with_url, nullptr);
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "OfflinePages.DownloadDeletedPageDuplicateCount",
+          remaining_pages_with_url, 1, 20, 10);
+    }
+
+    // The histograms below are an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+    // macro adapted to allow for a dynamically suffixed histogram name.
+    // Note: The factory creates and owns the histogram.
+    base::HistogramBase* histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"), 1,
+        max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
+    histogram->Add((delete_time - page.creation_time).InMinutes());
+
+    histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id,
+                           "OfflinePages.DeletePage.TimeSinceLastOpen"),
+        1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
+    histogram->Add((delete_time - page.last_access_time).InMinutes());
+
+    histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id,
+                           "OfflinePages.DeletePage.LastOpenToCreated"),
+        1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
+    histogram->Add((page.last_access_time - page.creation_time).InMinutes());
+
+    // Reported as Kb between 1Kb and 10Mb.
+    histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"), 1,
+        10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+    histogram->Add(page.file_size / 1024);
+
+    histogram = base::Histogram::FactoryGet(
+        AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"), 1,
+        1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
+    histogram->Add(page.access_count);
+  }
+
+  if (deleted_pages.size() > 1) {
+    UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count",
+                         static_cast<int32_t>(deleted_pages.size()));
+    UMA_HISTOGRAM_MEMORY_KB("OfflinePages.BatchDelete.TotalPageSize",
+                            total_size / 1024);
+  }
+}
+
+void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item,
+                                     const base::Time& access_time) {
+  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::Histogram::FactoryGet(
+      AddHistogramSuffix(offline_page_item.client_id,
+                         offline_page_item.access_count == 0
+                             ? "OfflinePages.FirstOpenSinceCreated"
+                             : "OfflinePages.OpenSinceLastOpen"),
+      1, kMaxOpenedPageHistogramBucket.InMinutes(), 50,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram->Add(
+      (access_time - offline_page_item.last_access_time).InMinutes());
+}
+
+}  // namespace
+
+// protected
+OfflinePageModelImpl::OfflinePageModelImpl()
+    : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {}
+
+OfflinePageModelImpl::OfflinePageModelImpl(
+    std::unique_ptr<OfflinePageMetadataStore> store,
+    const base::FilePath& archives_dir,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : store_(std::move(store)),
+      archives_dir_(archives_dir),
+      is_loaded_(false),
+      policy_controller_(new ClientPolicyController()),
+      archive_manager_(new ArchiveManager(archives_dir, task_runner)),
+      testing_clock_(nullptr),
+      weak_ptr_factory_(this) {
+  archive_manager_->EnsureArchivesDirCreated(
+      base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone,
+                 weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()));
+}
+
+OfflinePageModelImpl::~OfflinePageModelImpl() {}
+
+void OfflinePageModelImpl::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void OfflinePageModelImpl::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void OfflinePageModelImpl::SavePage(
+    const SavePageParams& save_page_params,
+    std::unique_ptr<OfflinePageArchiver> archiver,
+    const SavePageCallback& callback) {
+  DCHECK(is_loaded_);
+
+  // Skip saving the page that is not intended to be saved, like local file
+  // page.
+  if (!OfflinePageModel::CanSaveURL(save_page_params.url)) {
+    InformSavePageDone(callback, SavePageResult::SKIPPED,
+                       save_page_params.client_id, kInvalidOfflineId);
+    return;
+  }
+
+  // The web contents is not available if archiver is not created and passed.
+  if (!archiver.get()) {
+    InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE,
+                       save_page_params.client_id, kInvalidOfflineId);
+    return;
+  }
+
+  // If we already have an offline id, use it.  If not, generate one.
+  int64_t offline_id = save_page_params.proposed_offline_id;
+  if (offline_id == kInvalidOfflineId)
+    offline_id = GenerateOfflineId();
+
+  archiver->CreateArchive(
+      archives_dir_, offline_id,
+      base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone,
+                 weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id,
+                 GetCurrentTime(), callback));
+  pending_archivers_.push_back(std::move(archiver));
+}
+
+void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) {
+  RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone,
+                           weak_ptr_factory_.GetWeakPtr(), offline_id));
+}
+
+void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) {
+  DCHECK(is_loaded_);
+
+  auto iter = offline_pages_.find(offline_id);
+  if (iter == offline_pages_.end())
+    return;
+
+  // Make a copy of the cached item and update it. The cached item should only
+  // be updated upon the successful store operation.
+  OfflinePageItem offline_page_item = iter->second;
+
+  ReportPageHistogramsAfterAccess(offline_page_item, GetCurrentTime());
+
+  offline_page_item.last_access_time = GetCurrentTime();
+  offline_page_item.access_count++;
+
+  std::vector<OfflinePageItem> items = {offline_page_item};
+  store_->UpdateOfflinePages(
+      items, base::Bind(&OfflinePageModelImpl::OnMarkPageAccesseDone,
+                        weak_ptr_factory_.GetWeakPtr(), offline_page_item));
+}
+
+void OfflinePageModelImpl::DeletePagesByOfflineId(
+    const std::vector<int64_t>& offline_ids,
+    const DeletePageCallback& callback) {
+  RunWhenLoaded(base::Bind(&OfflinePageModelImpl::DoDeletePagesByOfflineId,
+                           weak_ptr_factory_.GetWeakPtr(), offline_ids,
+                           callback));
+}
+
+void OfflinePageModelImpl::DoDeletePagesByOfflineId(
+    const std::vector<int64_t>& offline_ids,
+    const DeletePageCallback& callback) {
+  DCHECK(is_loaded_);
+
+  std::vector<base::FilePath> paths_to_delete;
+  for (const auto& offline_id : offline_ids) {
+    auto iter = offline_pages_.find(offline_id);
+    if (iter != offline_pages_.end()) {
+      paths_to_delete.push_back(iter->second.file_path);
+    }
+  }
+
+  // If there're no pages to delete, return early.
+  if (paths_to_delete.empty()) {
+    InformDeletePageDone(callback, DeletePageResult::SUCCESS);
+    return;
+  }
+
+  archive_manager_->DeleteMultipleArchives(
+      paths_to_delete,
+      base::Bind(&OfflinePageModelImpl::OnDeleteArchiveFilesDone,
+                 weak_ptr_factory_.GetWeakPtr(), offline_ids, callback));
+}
+
+void OfflinePageModelImpl::DeletePagesByClientIds(
+    const std::vector<ClientId>& client_ids,
+    const DeletePageCallback& callback) {
+  OfflinePageModelQueryBuilder builder;
+  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+                       client_ids);
+  auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages,
+                                 weak_ptr_factory_.GetWeakPtr(), callback);
+  RunWhenLoaded(base::Bind(
+      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+      weak_ptr_factory_.GetWeakPtr(),
+      base::Passed(builder.Build(GetPolicyController())), delete_pages));
+}
+
+void OfflinePageModelImpl::DeletePages(
+    const DeletePageCallback& callback,
+    const MultipleOfflinePageItemResult& pages) {
+  DCHECK(is_loaded_);
+
+  std::vector<int64_t> offline_ids;
+  for (auto& page : pages)
+    offline_ids.emplace_back(page.offline_id);
+
+  DoDeletePagesByOfflineId(offline_ids, callback);
+}
+
+void OfflinePageModelImpl::GetPagesMatchingQuery(
+    std::unique_ptr<OfflinePageModelQuery> query,
+    const MultipleOfflinePageItemCallback& callback) {
+  RunWhenLoaded(base::Bind(
+      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+      weak_ptr_factory_.GetWeakPtr(), base::Passed(&query), callback));
+}
+
+void OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone(
+    std::unique_ptr<OfflinePageModelQuery> query,
+    const MultipleOfflinePageItemCallback& callback) {
+  DCHECK(query);
+  DCHECK(is_loaded_);
+
+  MultipleOfflinePageItemResult offline_pages_result;
+
+  for (const auto& id_page_pair : offline_pages_) {
+    if (query->Matches(id_page_pair.second))
+      offline_pages_result.emplace_back(id_page_pair.second);
+  }
+
+  callback.Run(offline_pages_result);
+}
+
+void OfflinePageModelImpl::GetPagesByClientIds(
+    const std::vector<ClientId>& client_ids,
+    const MultipleOfflinePageItemCallback& callback) {
+  OfflinePageModelQueryBuilder builder;
+  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+                       client_ids);
+  RunWhenLoaded(
+      base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::Passed(builder.Build(GetPolicyController())), callback));
+}
+
+void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate(
+    const UrlPredicate& predicate,
+    const DeletePageCallback& callback) {
+  RunWhenLoaded(
+      base::Bind(&OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate,
+                 weak_ptr_factory_.GetWeakPtr(), predicate, callback));
+}
+
+void OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate(
+    const UrlPredicate& predicate,
+    const DeletePageCallback& callback) {
+  DCHECK(is_loaded_);
+
+  std::vector<int64_t> offline_ids;
+  for (const auto& id_page_pair : offline_pages_) {
+    if (IsRemovedOnCacheReset(id_page_pair.second) &&
+        predicate.Run(id_page_pair.second.url)) {
+      offline_ids.push_back(id_page_pair.first);
+    }
+  }
+  DoDeletePagesByOfflineId(offline_ids, callback);
+}
+
+void OfflinePageModelImpl::CheckPagesExistOffline(
+    const std::set<GURL>& urls,
+    const CheckPagesExistOfflineCallback& callback) {
+  OfflinePageModelQueryBuilder builder;
+  builder
+      .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+               std::vector<GURL>(urls.begin(), urls.end()))
+      .RequireRestrictedToOriginalTab(
+          OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING);
+  auto pages_to_urls = base::Bind(
+      [](const CheckPagesExistOfflineCallback& callback,
+         const MultipleOfflinePageItemResult& pages) {
+        CheckPagesExistOfflineResult result;
+        for (auto& page : pages)
+          result.insert(page.url);
+        callback.Run(result);
+      },
+      callback);
+  RunWhenLoaded(base::Bind(
+      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+      weak_ptr_factory_.GetWeakPtr(),
+      base::Passed(builder.Build(GetPolicyController())), pages_to_urls));
+}
+
+void OfflinePageModelImpl::GetAllPages(
+    const MultipleOfflinePageItemCallback& callback) {
+  OfflinePageModelQueryBuilder builder;
+  RunWhenLoaded(
+      base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 base::Passed(builder.Build(GetPolicyController())), callback));
+}
+
+void OfflinePageModelImpl::GetOfflineIdsForClientId(
+    const ClientId& client_id,
+    const MultipleOfflineIdCallback& callback) {
+  RunWhenLoaded(
+      base::Bind(&OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone,
+                 weak_ptr_factory_.GetWeakPtr(), client_id, callback));
+}
+
+void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone(
+    const ClientId& client_id,
+    const MultipleOfflineIdCallback& callback) const {
+  DCHECK(is_loaded_);
+  callback.Run(MaybeGetOfflineIdsForClientId(client_id));
+}
+
+const std::vector<int64_t> OfflinePageModelImpl::MaybeGetOfflineIdsForClientId(
+    const ClientId& client_id) const {
+  DCHECK(is_loaded_);
+  std::vector<int64_t> results;
+
+  // We want only all pages, including those marked for deletion.
+  // TODO(fgorski): actually use an index rather than linear scan.
+  for (const auto& id_page_pair : offline_pages_) {
+    if (id_page_pair.second.client_id == client_id)
+      results.push_back(id_page_pair.second.offline_id);
+  }
+  return results;
+}
+
+void OfflinePageModelImpl::GetPageByOfflineId(
+    int64_t offline_id,
+    const SingleOfflinePageItemCallback& callback) {
+  std::vector<int64_t> query_ids;
+  query_ids.emplace_back(offline_id);
+
+  OfflinePageModelQueryBuilder builder;
+  builder.SetOfflinePageIds(
+      OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, query_ids);
+
+  auto multiple_callback = base::Bind(
+      [](const SingleOfflinePageItemCallback& callback,
+         const MultipleOfflinePageItemResult& result) {
+        DCHECK_LE(result.size(), 1U);
+        if (result.empty()) {
+          callback.Run(nullptr);
+        } else {
+          callback.Run(&result[0]);
+        }
+      },
+      callback);
+
+  RunWhenLoaded(base::Bind(
+      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
+      weak_ptr_factory_.GetWeakPtr(),
+      base::Passed(builder.Build(GetPolicyController())), multiple_callback));
+}
+
+void OfflinePageModelImpl::GetPagesByURL(
+    const GURL& url,
+    URLSearchMode url_search_mode,
+    const MultipleOfflinePageItemCallback& callback) {
+  RunWhenLoaded(
+      base::Bind(&OfflinePageModelImpl::GetPagesByURLWhenLoadDone,
+                 weak_ptr_factory_.GetWeakPtr(), url,
+                 url_search_mode, callback));
+}
+
+void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
+    const GURL& url,
+    URLSearchMode url_search_mode,
+    const MultipleOfflinePageItemCallback& callback) const {
+  DCHECK(is_loaded_);
+  std::vector<OfflinePageItem> result;
+
+  GURL::Replacements remove_params;
+  remove_params.ClearRef();
+
+  GURL url_without_fragment =
+      url.ReplaceComponents(remove_params);
+
+  for (const auto& id_page_pair : offline_pages_) {
+    // First, search by last committed URL with fragment stripped.
+    if (url_without_fragment ==
+            id_page_pair.second.url.ReplaceComponents(remove_params)) {
+      result.push_back(id_page_pair.second);
+      continue;
+    }
+    // Then, search by original request URL if |url_search_mode| wants it.
+    // Note that we want to do the exact match with fragment included. This is
+    // because original URL is used for redirect purpose and it is always safer
+    // to support the exact redirect.
+    if (url_search_mode == URLSearchMode::SEARCH_BY_ALL_URLS &&
+        url == id_page_pair.second.original_url) {
+      result.push_back(id_page_pair.second);
+    }
+  }
+
+  callback.Run(result);
+}
+
+void OfflinePageModelImpl::CheckMetadataConsistency() {
+  archive_manager_->GetAllArchives(
+      base::Bind(&OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+ClientPolicyController* OfflinePageModelImpl::GetPolicyController() {
+  return policy_controller_.get();
+}
+
+OfflinePageMetadataStore* OfflinePageModelImpl::GetStoreForTesting() {
+  return store_.get();
+}
+
+OfflinePageStorageManager* OfflinePageModelImpl::GetStorageManager() {
+  return storage_manager_.get();
+}
+
+bool OfflinePageModelImpl::is_loaded() const {
+  return is_loaded_;
+}
+
+OfflineEventLogger* OfflinePageModelImpl::GetLogger() {
+  return &offline_event_logger_;
+}
+
+void OfflinePageModelImpl::OnCreateArchiveDone(
+    const SavePageParams& save_page_params,
+    int64_t offline_id,
+    const base::Time& start_time,
+    const SavePageCallback& callback,
+    OfflinePageArchiver* archiver,
+    ArchiverResult archiver_result,
+    const GURL& url,
+    const base::FilePath& file_path,
+    const base::string16& title,
+    int64_t file_size) {
+  if (save_page_params.url != url) {
+    DVLOG(1) << "Saved URL does not match requested URL.";
+    // TODO(fgorski): We have created an archive for a wrong URL. It should be
+    // deleted from here, once archiver has the right functionality.
+    InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
+                       save_page_params.client_id, offline_id);
+    DeletePendingArchiver(archiver);
+    return;
+  }
+
+  if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
+    SavePageResult result = ToSavePageResult(archiver_result);
+    InformSavePageDone(
+        callback, result, save_page_params.client_id, offline_id);
+    DeletePendingArchiver(archiver);
+    return;
+  }
+  OfflinePageItem offline_page_item(url, offline_id, save_page_params.client_id,
+                                    file_path, file_size, start_time);
+  offline_page_item.title = title;
+  offline_page_item.original_url = save_page_params.original_url;
+  store_->AddOfflinePage(offline_page_item,
+                         base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
+                                    weak_ptr_factory_.GetWeakPtr(), archiver,
+                                    callback, offline_page_item));
+}
+
+void OfflinePageModelImpl::OnAddOfflinePageDone(
+    OfflinePageArchiver* archiver,
+    const SavePageCallback& callback,
+    const OfflinePageItem& offline_page,
+    ItemActionStatus status) {
+  SavePageResult result;
+
+  if (status == ItemActionStatus::SUCCESS) {
+    offline_pages_[offline_page.offline_id] = offline_page;
+    result = SavePageResult::SUCCESS;
+    ReportPageHistogramAfterSave(policy_controller_.get(), offline_pages_,
+                                 offline_page, GetCurrentTime());
+    offline_event_logger_.RecordPageSaved(
+        offline_page.client_id.name_space, offline_page.url.spec(),
+        std::to_string(offline_page.offline_id));
+  } else if (status == ItemActionStatus::ALREADY_EXISTS) {
+    result = SavePageResult::ALREADY_EXISTS;
+  } else {
+    result = SavePageResult::STORE_FAILURE;
+  }
+  InformSavePageDone(callback, result, offline_page.client_id,
+                     offline_page.offline_id);
+  if (result == SavePageResult::SUCCESS) {
+    DeleteExistingPagesWithSameURL(offline_page);
+  } else {
+    PostClearStorageIfNeededTask(false /* delayed */);
+  }
+
+  DeletePendingArchiver(archiver);
+
+  // We don't want to notify observers if the add failed.
+  if (result != SavePageResult::SUCCESS)
+    return;
+
+  for (Observer& observer : observers_)
+    observer.OfflinePageAdded(this, offline_page);
+}
+
+void OfflinePageModelImpl::OnMarkPageAccesseDone(
+    const OfflinePageItem& offline_page_item,
+    std::unique_ptr<OfflinePagesUpdateResult> result) {
+  // Update the item in the cache only upon success.
+  if (result->updated_items.size() > 0)
+    offline_pages_[offline_page_item.offline_id] = offline_page_item;
+
+  // No need to fire OfflinePageModelChanged event since updating access info
+  // should not have any impact to the UI.
+}
+
+void OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone(
+    const base::TimeTicks& start_time) {
+  UMA_HISTOGRAM_TIMES("OfflinePages.Model.ArchiveDirCreationTime",
+                      base::TimeTicks::Now() - start_time);
+
+  const int kResetAttemptsLeft = 1;
+  store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
+                                weak_ptr_factory_.GetWeakPtr(), start_time,
+                                kResetAttemptsLeft));
+}
+
+void OfflinePageModelImpl::OnStoreInitialized(const base::TimeTicks& start_time,
+                                              int reset_attempts_left,
+                                              bool success) {
+  if (success) {
+    DCHECK_EQ(store_->state(), StoreState::LOADED);
+    store_->GetOfflinePages(
+        base::Bind(&OfflinePageModelImpl::OnInitialGetOfflinePagesDone,
+                   weak_ptr_factory_.GetWeakPtr(), start_time));
+    return;
+  }
+
+  DCHECK_EQ(store_->state(), StoreState::FAILED_LOADING);
+  // If there are no more reset attempts left, stop here.
+  if (reset_attempts_left == 0) {
+    FinalizeModelLoad();
+    return;
+  }
+
+  // Otherwise reduce the remaining attempts counter and reset store.
+  store_->Reset(base::Bind(&OfflinePageModelImpl::OnStoreResetDone,
+                           weak_ptr_factory_.GetWeakPtr(), start_time,
+                           reset_attempts_left - 1));
+}
+
+void OfflinePageModelImpl::OnStoreResetDone(const base::TimeTicks& start_time,
+                                            int reset_attempts_left,
+                                            bool success) {
+  if (success) {
+    DCHECK_EQ(store_->state(), StoreState::NOT_LOADED);
+    store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
+                                  weak_ptr_factory_.GetWeakPtr(), start_time,
+                                  reset_attempts_left));
+    return;
+  }
+
+  DCHECK_EQ(store_->state(), StoreState::FAILED_RESET);
+  FinalizeModelLoad();
+}
+
+void OfflinePageModelImpl::OnInitialGetOfflinePagesDone(
+    const base::TimeTicks& start_time,
+    const std::vector<OfflinePageItem>& offline_pages) {
+  DCHECK(!is_loaded_);
+
+  UMA_HISTOGRAM_TIMES("OfflinePages.Model.ConstructionToLoadedEventTime",
+                      base::TimeTicks::Now() - start_time);
+
+  CacheLoadedData(offline_pages);
+  FinalizeModelLoad();
+
+  // Ensure necessary cleanup operations are started.
+  CheckMetadataConsistency();
+}
+
+void OfflinePageModelImpl::FinalizeModelLoad() {
+  is_loaded_ = true;
+
+  // All actions below are meant to be taken regardless of successful load of
+  // the store.
+
+  // Inform observers the load is done.
+  for (Observer& observer : observers_)
+    observer.OfflinePageModelLoaded(this);
+
+  // Run all the delayed tasks.
+  for (const auto& delayed_task : delayed_tasks_)
+    delayed_task.Run();
+  delayed_tasks_.clear();
+
+  // Clear storage.
+  PostClearStorageIfNeededTask(true /* delayed */);
+}
+
+void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback,
+                                              SavePageResult result,
+                                              const ClientId& client_id,
+                                              int64_t offline_id) {
+  ReportSavePageResultHistogramAfterSave(client_id, result);
+  archive_manager_->GetStorageStats(
+      base::Bind(&ReportStorageHistogramsAfterSave));
+  callback.Run(result, offline_id);
+}
+
+void OfflinePageModelImpl::DeleteExistingPagesWithSameURL(
+    const OfflinePageItem& offline_page) {
+  // Remove existing pages generated by the same policy and with same url.
+  size_t pages_allowed =
+      policy_controller_->GetPolicy(offline_page.client_id.name_space)
+          .pages_allowed_per_url;
+  if (pages_allowed == kUnlimitedPages)
+    return;
+  GetPagesByURL(
+      offline_page.url,
+      URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
+      base::Bind(&OfflinePageModelImpl::OnPagesFoundWithSameURL,
+                 weak_ptr_factory_.GetWeakPtr(), offline_page, pages_allowed));
+}
+
+void OfflinePageModelImpl::OnPagesFoundWithSameURL(
+    const OfflinePageItem& offline_page,
+    size_t pages_allowed,
+    const MultipleOfflinePageItemResult& items) {
+  std::vector<OfflinePageItem> pages_to_delete;
+  for (const auto& item : items) {
+    if (item.offline_id != offline_page.offline_id &&
+        item.client_id.name_space == offline_page.client_id.name_space) {
+      pages_to_delete.push_back(item);
+    }
+  }
+  // Only keep |pages_allowed|-1 of most fresh pages and delete others, by
+  // sorting pages with the oldest  ones first and resize the vector.
+  if (pages_to_delete.size() >= pages_allowed) {
+    sort(pages_to_delete.begin(), pages_to_delete.end(),
+         [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
+           return a.last_access_time < b.last_access_time;
+         });
+    pages_to_delete.resize(pages_to_delete.size() - pages_allowed + 1);
+  }
+  std::vector<int64_t> page_ids_to_delete;
+  for (const auto& item : pages_to_delete)
+    page_ids_to_delete.push_back(item.offline_id);
+  DeletePagesByOfflineId(
+      page_ids_to_delete,
+      base::Bind(&OfflinePageModelImpl::OnDeleteOldPagesWithSameURL,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OfflinePageModelImpl::OnDeleteOldPagesWithSameURL(
+    DeletePageResult result) {
+  // TODO(romax) Add UMAs for failure cases.
+  PostClearStorageIfNeededTask(false /* delayed */);
+}
+
+void OfflinePageModelImpl::DeletePendingArchiver(
+    OfflinePageArchiver* archiver) {
+  pending_archivers_.erase(std::find(pending_archivers_.begin(),
+                                     pending_archivers_.end(), archiver));
+}
+
+void OfflinePageModelImpl::OnDeleteArchiveFilesDone(
+    const std::vector<int64_t>& offline_ids,
+    const DeletePageCallback& callback,
+    bool success) {
+  if (!success) {
+    InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE);
+    return;
+  }
+
+  store_->RemoveOfflinePages(
+      offline_ids, base::Bind(&OfflinePageModelImpl::OnRemoveOfflinePagesDone,
+                              weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void OfflinePageModelImpl::OnRemoveOfflinePagesDone(
+    const DeletePageCallback& callback,
+    std::unique_ptr<OfflinePagesUpdateResult> result) {
+  ReportPageHistogramsAfterDelete(offline_pages_, result->updated_items,
+                                  GetCurrentTime());
+
+  // This part of the loop is explicitly broken out, as it should be gone in
+  // fully asynchronous code.
+  for (const auto& page : result->updated_items) {
+    int64_t offline_id = page.offline_id;
+    offline_event_logger_.RecordPageDeleted(std::to_string(offline_id));
+    auto iter = offline_pages_.find(offline_id);
+    if (iter == offline_pages_.end())
+      continue;
+    offline_pages_.erase(iter);
+  }
+
+  for (const auto& page : result->updated_items) {
+    for (Observer& observer : observers_)
+      observer.OfflinePageDeleted(page.offline_id, page.client_id);
+  }
+
+  // TODO(fgorski): React the FAILED_INITIALIZATION, FAILED_RESET here.
+  // TODO(fgorski): We need a better callback interface for the Remove action on
+  // the this class. Currently removing an item that does not exist is
+  // considered a success, but not called out as such to the caller.
+  DeletePageResult delete_result;
+  if (result->store_state == StoreState::LOADED)
+    delete_result = DeletePageResult::SUCCESS;
+  else
+    delete_result = DeletePageResult::STORE_FAILURE;
+
+  InformDeletePageDone(callback, delete_result);
+}
+
+void OfflinePageModelImpl::InformDeletePageDone(
+    const DeletePageCallback& callback,
+    DeletePageResult result) {
+  UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult",
+                            static_cast<int>(result),
+                            static_cast<int>(DeletePageResult::RESULT_COUNT));
+  archive_manager_->GetStorageStats(
+      base::Bind(&ReportStorageHistogramsAfterDelete));
+  if (!callback.is_null())
+    callback.Run(result);
+}
+
+void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths(
+    const std::set<base::FilePath>& archive_paths) {
+  DeletePagesMissingArchiveFile(archive_paths);
+  DeleteOrphanedArchives(archive_paths);
+}
+
+void OfflinePageModelImpl::DeletePagesMissingArchiveFile(
+    const std::set<base::FilePath>& archive_paths) {
+  std::vector<int64_t> ids_of_pages_missing_archive_file;
+  for (const auto& id_page_pair : offline_pages_) {
+    if (archive_paths.count(id_page_pair.second.file_path) == 0UL)
+      ids_of_pages_missing_archive_file.push_back(id_page_pair.first);
+  }
+
+  if (ids_of_pages_missing_archive_file.empty())
+    return;
+
+  DeletePagesByOfflineId(
+      ids_of_pages_missing_archive_file,
+      base::Bind(&OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 ids_of_pages_missing_archive_file));
+}
+
+void OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone(
+    const std::vector<int64_t>& offline_ids,
+    DeletePageResult result) {
+  UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount",
+                       static_cast<int32_t>(offline_ids.size()));
+  UMA_HISTOGRAM_ENUMERATION(
+      "OfflinePages.Consistency.DeletePagesMissingArchiveFileResult",
+      static_cast<int>(result),
+      static_cast<int>(DeletePageResult::RESULT_COUNT));
+}
+
+void OfflinePageModelImpl::DeleteOrphanedArchives(
+    const std::set<base::FilePath>& archive_paths) {
+  // Archives are considered orphaned unless they are pointed to by some pages.
+  std::set<base::FilePath> orphaned_archive_set(archive_paths);
+  for (const auto& id_page_pair : offline_pages_)
+    orphaned_archive_set.erase(id_page_pair.second.file_path);
+
+  if (orphaned_archive_set.empty())
+    return;
+
+  std::vector<base::FilePath> orphaned_archives(orphaned_archive_set.begin(),
+                                                orphaned_archive_set.end());
+  archive_manager_->DeleteMultipleArchives(
+      orphaned_archives,
+      base::Bind(&OfflinePageModelImpl::OnDeleteOrphanedArchivesDone,
+                 weak_ptr_factory_.GetWeakPtr(), orphaned_archives));
+}
+
+void OfflinePageModelImpl::OnDeleteOrphanedArchivesDone(
+    const std::vector<base::FilePath>& archives,
+    bool success) {
+  UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.OrphanedArchivesCount",
+                       static_cast<int32_t>(archives.size()));
+  UMA_HISTOGRAM_BOOLEAN("OfflinePages.Consistency.DeleteOrphanedArchivesResult",
+                        success);
+}
+
+void OfflinePageModelImpl::CacheLoadedData(
+    const std::vector<OfflinePageItem>& offline_pages) {
+  offline_pages_.clear();
+  for (const auto& offline_page : offline_pages)
+    offline_pages_[offline_page.offline_id] = offline_page;
+}
+
+void OfflinePageModelImpl::ClearStorageIfNeeded(
+    const ClearStorageCallback& callback) {
+  // Create Storage Manager if necessary.
+  if (!storage_manager_) {
+    storage_manager_.reset(new OfflinePageStorageManager(
+        this, GetPolicyController(), archive_manager_.get()));
+  }
+  storage_manager_->ClearPagesIfNeeded(callback);
+}
+
+void OfflinePageModelImpl::OnStorageCleared(size_t deleted_page_count,
+                                            ClearStorageResult result) {
+  UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult",
+                            static_cast<int>(result),
+                            static_cast<int>(ClearStorageResult::RESULT_COUNT));
+  if (deleted_page_count > 0) {
+    UMA_HISTOGRAM_COUNTS("OfflinePages.ClearStorageBatchSize",
+                         static_cast<int32_t>(deleted_page_count));
+  }
+}
+
+void OfflinePageModelImpl::PostClearStorageIfNeededTask(bool delayed) {
+  base::TimeDelta delay =
+      delayed ? kStorageManagerStartingDelay : base::TimeDelta();
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            base::Bind(&OfflinePageModelImpl::OnStorageCleared,
+                                       weak_ptr_factory_.GetWeakPtr())),
+      delay);
+}
+
+bool OfflinePageModelImpl::IsRemovedOnCacheReset(
+    const OfflinePageItem& offline_page) const {
+  return policy_controller_->IsRemovedOnCacheReset(
+      offline_page.client_id.name_space);
+}
+
+void OfflinePageModelImpl::RunWhenLoaded(const base::Closure& task) {
+  if (!is_loaded_) {
+    delayed_tasks_.push_back(task);
+    return;
+  }
+
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task);
+}
+
+base::Time OfflinePageModelImpl::GetCurrentTime() const {
+  return testing_clock_ ? testing_clock_->Now() : base::Time::Now();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model_impl.h b/components/offline_pages/core/offline_page_model_impl.h
new file mode 100644
index 0000000..15846d5
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_impl.h
@@ -0,0 +1,296 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_page_model_event_logger.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
+#include "components/offline_pages/core/offline_store_types.h"
+
+class GURL;
+namespace base {
+class Clock;
+class SequencedTaskRunner;
+class TimeTicks;
+}  // namespace base
+
+namespace offline_pages {
+
+struct ClientId;
+struct OfflinePageItem;
+
+class ArchiveManager;
+class ClientPolicyController;
+class OfflinePageModelQuery;
+class OfflinePageStorageManager;
+
+// Implementation of service for saving pages offline, storing the offline
+// copy and metadata, and retrieving them upon request.
+class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
+ public:
+  // All blocking calls/disk access will happen on the provided |task_runner|.
+  OfflinePageModelImpl(
+      std::unique_ptr<OfflinePageMetadataStore> store,
+      const base::FilePath& archives_dir,
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+  ~OfflinePageModelImpl() override;
+
+  // Implemented methods:
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  void SavePage(const SavePageParams& save_page_params,
+                std::unique_ptr<OfflinePageArchiver> archiver,
+                const SavePageCallback& callback) override;
+  void MarkPageAccessed(int64_t offline_id) override;
+  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
+                              const DeletePageCallback& callback) override;
+  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
+                              const DeletePageCallback& callback) override;
+
+  void GetPagesMatchingQuery(
+      std::unique_ptr<OfflinePageModelQuery> query,
+      const MultipleOfflinePageItemCallback& callback) override;
+
+  void GetPagesByClientIds(
+      const std::vector<ClientId>& client_ids,
+      const MultipleOfflinePageItemCallback& callback) override;
+
+  void DeleteCachedPagesByURLPredicate(
+      const UrlPredicate& predicate,
+      const DeletePageCallback& callback) override;
+  void CheckPagesExistOffline(
+      const std::set<GURL>& urls,
+      const CheckPagesExistOfflineCallback& callback) override;
+  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
+  void GetOfflineIdsForClientId(
+      const ClientId& client_id,
+      const MultipleOfflineIdCallback& callback) override;
+  void GetPageByOfflineId(
+      int64_t offline_id,
+      const SingleOfflinePageItemCallback& callback) override;
+  void GetPagesByURL(
+      const GURL& url,
+      URLSearchMode url_search_mode,
+      const MultipleOfflinePageItemCallback& callback) override;
+  ClientPolicyController* GetPolicyController() override;
+
+  // Methods for testing only:
+  OfflinePageMetadataStore* GetStoreForTesting();
+  void set_testing_clock(base::Clock* clock) { testing_clock_ = clock; }
+
+  OfflinePageStorageManager* GetStorageManager();
+
+  bool is_loaded() const override;
+
+  OfflineEventLogger* GetLogger() override;
+
+ protected:
+  // Adding a protected constructor for testing-only purposes in
+  // offline_page_storage_manager_unittest.cc
+  OfflinePageModelImpl();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(OfflinePageModelImplTest, MarkPageForDeletion);
+
+  typedef ScopedVector<OfflinePageArchiver> PendingArchivers;
+
+  // Callback for ensuring archive directory is created.
+  void OnEnsureArchivesDirCreatedDone(const base::TimeTicks& start_time);
+
+  void GetPagesMatchingQueryWhenLoadDone(
+      std::unique_ptr<OfflinePageModelQuery> query,
+      const MultipleOfflinePageItemCallback& callback);
+  void GetOfflineIdsForClientIdWhenLoadDone(
+      const ClientId& client_id,
+      const MultipleOfflineIdCallback& callback) const;
+  const std::vector<int64_t> MaybeGetOfflineIdsForClientId(
+      const ClientId& client_id) const;
+  void GetPagesByURLWhenLoadDone(
+      const GURL& url,
+      URLSearchMode url_search_mode,
+      const MultipleOfflinePageItemCallback& callback) const;
+  void MarkPageAccessedWhenLoadDone(int64_t offline_id);
+
+  // Check the consistency between metadata store and archives on disk,
+  // would delete the metadata entries which don't have an associated
+  // archive and the archives which doesn't have a metadata in the store.
+  // The expired pages (from previous versions) would also be cleared
+  // during this process.
+  void CheckMetadataConsistency();
+
+  // Callback for loading pages from the offline page metadata store.
+  void OnStoreInitialized(const base::TimeTicks& start_time,
+                          int reset_attempts_left,
+                          bool success);
+  void OnStoreResetDone(const base::TimeTicks& start_time,
+                        int reset_attempts_left,
+                        bool success);
+  void OnInitialGetOfflinePagesDone(
+      const base::TimeTicks& start_time,
+      const std::vector<OfflinePageItem>& offline_pages);
+  void FinalizeModelLoad();
+
+  // Steps for saving a page offline.
+  void OnCreateArchiveDone(const SavePageParams& save_page_params,
+                           int64_t offline_id,
+                           const base::Time& start_time,
+                           const SavePageCallback& callback,
+                           OfflinePageArchiver* archiver,
+                           OfflinePageArchiver::ArchiverResult result,
+                           const GURL& url,
+                           const base::FilePath& file_path,
+                           const base::string16& title,
+                           int64_t file_size);
+  void OnAddOfflinePageDone(OfflinePageArchiver* archiver,
+                            const SavePageCallback& callback,
+                            const OfflinePageItem& offline_page,
+                            ItemActionStatus status);
+  void InformSavePageDone(const SavePageCallback& callback,
+                          SavePageResult result,
+                          const ClientId& client_id,
+                          int64_t offline_id);
+  void DeletePendingArchiver(OfflinePageArchiver* archiver);
+
+  // Steps for deleting files and data for an offline page.
+  void OnDeleteArchiveFilesDone(const std::vector<int64_t>& offline_ids,
+                                const DeletePageCallback& callback,
+                                bool success);
+  void OnRemoveOfflinePagesDone(
+      const DeletePageCallback& callback,
+      std::unique_ptr<OfflinePagesUpdateResult> result);
+  void InformDeletePageDone(const DeletePageCallback& callback,
+                            DeletePageResult result);
+
+  void OnMarkPageAccesseDone(const OfflinePageItem& offline_page_item,
+                             std::unique_ptr<OfflinePagesUpdateResult> result);
+
+  // Callbacks for checking metadata consistency.
+  void CheckMetadataConsistencyForArchivePaths(
+      const std::set<base::FilePath>& archive_paths);
+  // Callbacks which would be called after orphaned archives are deleted.
+  // Orphaned archives are the files on disk which are not pointed to by any of
+  // the page entries in the metadata store.
+  void DeletePagesMissingArchiveFile(
+      const std::set<base::FilePath>& archive_paths);
+  void OnDeletePagesMissingArchiveFileDone(
+      const std::vector<int64_t>& offline_ids,
+      DeletePageResult result);
+  void DeleteOrphanedArchives(const std::set<base::FilePath>& archive_paths);
+  void OnDeleteOrphanedArchivesDone(const std::vector<base::FilePath>& archives,
+                                    bool success);
+
+  // Callbacks for deleting pages with same URL when saving pages.
+  void DeleteExistingPagesWithSameURL(const OfflinePageItem& offline_page);
+  void OnPagesFoundWithSameURL(const OfflinePageItem& offline_page,
+                               size_t pages_allowed,
+                               const MultipleOfflinePageItemResult& items);
+  void OnDeleteOldPagesWithSameURL(DeletePageResult result);
+
+  void CacheLoadedData(const std::vector<OfflinePageItem>& offline_pages);
+
+  // Actually does the work of deleting, requires the model is loaded.
+  void DoDeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
+                                const DeletePageCallback& callback);
+
+  // Actually does the work of deleting, requires the model is loaded.
+  void DeletePages(const DeletePageCallback& callback,
+                   const MultipleOfflinePageItemResult& items);
+
+  void DoGetPagesByClientIds(const std::vector<ClientId>& client_ids,
+                             const MultipleOfflinePageItemCallback& callback);
+
+  // Similar to DoDeletePagesByOfflineId, does actual work of deleting, and
+  // requires that the model is loaded.
+  void DoDeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
+                                         const DeletePageCallback& callback);
+
+  // Clears expired pages if there are any or we're running out of storage.
+  void ClearStorageIfNeeded(
+      const OfflinePageStorageManager::ClearStorageCallback& callback);
+
+  // Callback completing storage clearing.
+  void OnStorageCleared(size_t cleared_page_count,
+                        OfflinePageStorageManager::ClearStorageResult result);
+
+  // Post task to clear storage.
+  void PostClearStorageIfNeededTask(bool delayed);
+
+  // Check if |offline_page| should be removed on cache reset by user.
+  bool IsRemovedOnCacheReset(const OfflinePageItem& offline_page) const;
+
+  void RunWhenLoaded(const base::Closure& job);
+
+  base::Time GetCurrentTime() const;
+
+  // Persistent store for offline page metadata.
+  std::unique_ptr<OfflinePageMetadataStore> store_;
+
+  // Location where all of the archive files will be stored.
+  base::FilePath archives_dir_;
+
+  // The observers.
+  base::ObserverList<Observer> observers_;
+
+  bool is_loaded_;
+
+  // In memory copy of the offline page metadata, keyed by offline IDs.
+  std::map<int64_t, OfflinePageItem> offline_pages_;
+
+  // Pending archivers owned by this model.
+  PendingArchivers pending_archivers_;
+
+  // Delayed tasks that should be invoked after the loading is done.
+  std::vector<base::Closure> delayed_tasks_;
+
+  // Controller of the client policies.
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+
+  // Manager for the storage consumed by archives and responsible for
+  // automatic page clearing.
+  std::unique_ptr<OfflinePageStorageManager> storage_manager_;
+
+  // Manager for the offline archive files and directory.
+  std::unique_ptr<ArchiveManager> archive_manager_;
+
+  // Logger to facilitate recording of events.
+  OfflinePageModelEventLogger offline_event_logger_;
+
+  // Clock for getting time in testing code. The setter is responsible to reset
+  // it once it is not longer needed.
+  base::Clock* testing_clock_;
+
+  base::WeakPtrFactory<OfflinePageModelImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelImpl);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_IMPL_H_
diff --git a/components/offline_pages/core/offline_page_model_impl_unittest.cc b/components/offline_pages/core/offline_page_model_impl_unittest.cc
new file mode 100644
index 0000000..678cc70
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_impl_unittest.cc
@@ -0,0 +1,1318 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model_impl.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+#include "components/offline_pages/core/offline_page_test_archiver.h"
+#include "components/offline_pages/core/offline_page_test_store.h"
+#include "components/offline_pages/core/offline_page_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+namespace {
+const char kTestClientNamespace[] = "default";
+const char kUserRequestedNamespace[] = "download";
+const GURL kTestUrl("http://example.com");
+const GURL kTestUrl2("http://other.page.com");
+const GURL kTestUrl3("http://test.xyz");
+const GURL kTestUrl4("http://page.net");
+const GURL kFileUrl("file:///foo");
+const GURL kTestUrlWithFragment("http://example.com#frag");
+const GURL kTestUrl2WithFragment("http://other.page.com#frag");
+const GURL kTestUrl2WithFragment2("http://other.page.com#frag2");
+const ClientId kTestClientId1(kTestClientNamespace, "1234");
+const ClientId kTestClientId2(kTestClientNamespace, "5678");
+const ClientId kTestClientId3(kTestClientNamespace, "42");
+const ClientId kTestUserRequestedClientId(kUserRequestedNamespace, "714");
+const int64_t kTestFileSize = 876543LL;
+const base::string16 kTestTitle = base::UTF8ToUTF16("a title");
+
+bool URLSpecContains(std::string contains_value, const GURL& url) {
+  std::string spec = url.spec();
+  return spec.find(contains_value) != std::string::npos;
+}
+
+}  // namespace
+
+class OfflinePageModelImplTest
+    : public testing::Test,
+      public OfflinePageModel::Observer,
+      public OfflinePageTestArchiver::Observer,
+      public base::SupportsWeakPtr<OfflinePageModelImplTest> {
+ public:
+  OfflinePageModelImplTest();
+  ~OfflinePageModelImplTest() override;
+
+  void SetUp() override;
+  void TearDown() override;
+
+  // OfflinePageModel::Observer implementation.
+  void OfflinePageModelLoaded(OfflinePageModel* model) override;
+  void OfflinePageAdded(OfflinePageModel* model,
+                        const OfflinePageItem& added_page) override;
+  void OfflinePageDeleted(int64_t offline_id,
+                          const ClientId& client_id) override;
+
+  // OfflinePageTestArchiver::Observer implementation.
+  void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
+
+  // OfflinePageModel callbacks.
+  void OnSavePageDone(SavePageResult result, int64_t offline_id);
+  void OnDeletePageDone(DeletePageResult result);
+  void OnCheckPagesExistOfflineDone(const CheckPagesExistOfflineResult& result);
+  void OnGetOfflineIdsForClientIdDone(MultipleOfflineIdResult* storage,
+                                      const MultipleOfflineIdResult& result);
+  void OnGetSingleOfflinePageItemResult(
+      std::unique_ptr<OfflinePageItem>* storage,
+      const OfflinePageItem* result);
+  void OnGetMultipleOfflinePageItemsResult(
+      MultipleOfflinePageItemResult* storage,
+      const MultipleOfflinePageItemResult& result);
+  void OnPagesExpired(bool result);
+
+  // OfflinePageMetadataStore callbacks.
+  void OnStoreUpdateDone(bool /* success */);
+
+  std::unique_ptr<OfflinePageTestArchiver> BuildArchiver(
+      const GURL& url,
+      OfflinePageArchiver::ArchiverResult result);
+  std::unique_ptr<OfflinePageMetadataStore> BuildStore();
+  std::unique_ptr<OfflinePageModelImpl> BuildModel(
+      std::unique_ptr<OfflinePageMetadataStore> store);
+  void ResetModel();
+
+  // Utility methods.
+  // Runs until all of the tasks that are not delayed are gone from the task
+  // queue.
+  void PumpLoop();
+  // Fast-forwards virtual time by |delta|, causing tasks with a remaining
+  // delay less than or equal to |delta| to be executed.
+  void FastForwardBy(base::TimeDelta delta);
+  void ResetResults();
+
+  OfflinePageTestStore* GetStore();
+
+  MultipleOfflinePageItemResult GetAllPages();
+  MultipleOfflinePageItemResult GetPagesByClientIds(
+      const std::vector<ClientId>& client_ids);
+  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids);
+
+  // Saves the page without waiting for it to finish.
+  void SavePageWithArchiverAsync(
+      const GURL& url,
+      const ClientId& client_id,
+      const GURL& original_url,
+      std::unique_ptr<OfflinePageArchiver> archiver);
+
+  // All 3 methods below will wait for the save to finish.
+  void SavePageWithArchiver(
+      const GURL& url,
+      const ClientId& client_id,
+      std::unique_ptr<OfflinePageArchiver> archiver);
+  void SavePageWithArchiverResult(const GURL& url,
+                                  const ClientId& client_id,
+                                  OfflinePageArchiver::ArchiverResult result);
+  // Returns the offline ID of the saved page.
+  std::pair<SavePageResult, int64_t> SavePage(const GURL& url,
+                                              const ClientId& client_id);
+
+  void DeletePage(int64_t offline_id, const DeletePageCallback& callback) {
+    std::vector<int64_t> offline_ids;
+    offline_ids.push_back(offline_id);
+    model()->DeletePagesByOfflineId(offline_ids, callback);
+  }
+
+  bool HasPages(std::string name_space);
+
+  CheckPagesExistOfflineResult CheckPagesExistOffline(std::set<GURL>);
+
+  MultipleOfflineIdResult GetOfflineIdsForClientId(const ClientId& client_id);
+
+  std::unique_ptr<OfflinePageItem> GetPageByOfflineId(int64_t offline_id);
+
+  MultipleOfflinePageItemResult GetPagesByFinalURL(const GURL& url);
+  MultipleOfflinePageItemResult GetPagesByAllURLS(const GURL& url);
+
+  OfflinePageModelImpl* model() { return model_.get(); }
+
+  int64_t last_save_offline_id() const { return last_save_offline_id_; }
+
+  SavePageResult last_save_result() const { return last_save_result_; }
+
+  DeletePageResult last_delete_result() const { return last_delete_result_; }
+
+  int64_t last_deleted_offline_id() const { return last_deleted_offline_id_; }
+
+  ClientId last_deleted_client_id() const { return last_deleted_client_id_; }
+
+  const base::FilePath& last_archiver_path() { return last_archiver_path_; }
+
+  int last_cleared_pages_count() const { return last_cleared_pages_count_; }
+
+  DeletePageResult last_clear_page_result() const {
+    return last_clear_page_result_;
+  }
+
+  bool last_expire_page_result() const { return last_expire_page_result_; }
+
+  const base::HistogramTester& histograms() const { return histogram_tester_; }
+
+ private:
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  base::ThreadTaskRunnerHandle task_runner_handle_;
+  base::ScopedTempDir temp_dir_;
+
+  std::unique_ptr<OfflinePageModelImpl> model_;
+  SavePageResult last_save_result_;
+  int64_t last_save_offline_id_;
+  DeletePageResult last_delete_result_;
+  base::FilePath last_archiver_path_;
+  int64_t last_deleted_offline_id_;
+  ClientId last_deleted_client_id_;
+  CheckPagesExistOfflineResult last_pages_exist_result_;
+  int last_cleared_pages_count_;
+  DeletePageResult last_clear_page_result_;
+  bool last_expire_page_result_;
+
+  base::HistogramTester histogram_tester_;
+};
+
+OfflinePageModelImplTest::OfflinePageModelImplTest()
+    : task_runner_(new base::TestMockTimeTaskRunner),
+      task_runner_handle_(task_runner_),
+      last_save_result_(SavePageResult::CANCELLED),
+      last_save_offline_id_(-1),
+      last_delete_result_(DeletePageResult::CANCELLED),
+      last_deleted_offline_id_(-1) {}
+
+OfflinePageModelImplTest::~OfflinePageModelImplTest() {}
+
+void OfflinePageModelImplTest::SetUp() {
+  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  model_ = BuildModel(BuildStore());
+  model_->AddObserver(this);
+  PumpLoop();
+}
+
+void OfflinePageModelImplTest::TearDown() {
+  model_->RemoveObserver(this);
+  model_.reset();
+  PumpLoop();
+}
+
+void OfflinePageModelImplTest::OfflinePageModelLoaded(OfflinePageModel* model) {
+  ASSERT_EQ(model_.get(), model);
+}
+
+void OfflinePageModelImplTest::OfflinePageDeleted(int64_t offline_id,
+                                                  const ClientId& client_id) {
+  last_deleted_offline_id_ = offline_id;
+  last_deleted_client_id_ = client_id;
+}
+
+void OfflinePageModelImplTest::OfflinePageAdded(
+    OfflinePageModel* model,
+    const OfflinePageItem& added_page) {
+  ASSERT_EQ(model_.get(), model);
+}
+
+void OfflinePageModelImplTest::SetLastPathCreatedByArchiver(
+    const base::FilePath& file_path) {
+  last_archiver_path_ = file_path;
+}
+
+void OfflinePageModelImplTest::OnSavePageDone(SavePageResult result,
+                                              int64_t offline_id) {
+  last_save_result_ = result;
+  last_save_offline_id_ = offline_id;
+}
+
+void OfflinePageModelImplTest::OnDeletePageDone(DeletePageResult result) {
+  last_delete_result_ = result;
+}
+
+void OfflinePageModelImplTest::OnCheckPagesExistOfflineDone(
+    const CheckPagesExistOfflineResult& result) {
+  last_pages_exist_result_ = result;
+}
+
+void OfflinePageModelImplTest::OnStoreUpdateDone(bool /* success - ignored */) {
+}
+
+std::unique_ptr<OfflinePageTestArchiver>
+OfflinePageModelImplTest::BuildArchiver(
+    const GURL& url,
+    OfflinePageArchiver::ArchiverResult result) {
+  return std::unique_ptr<OfflinePageTestArchiver>(
+      new OfflinePageTestArchiver(this, url, result, kTestTitle, kTestFileSize,
+                                  base::ThreadTaskRunnerHandle::Get()));
+}
+
+std::unique_ptr<OfflinePageMetadataStore>
+OfflinePageModelImplTest::BuildStore() {
+  return std::unique_ptr<OfflinePageMetadataStore>(
+      new OfflinePageTestStore(base::ThreadTaskRunnerHandle::Get()));
+}
+
+std::unique_ptr<OfflinePageModelImpl> OfflinePageModelImplTest::BuildModel(
+    std::unique_ptr<OfflinePageMetadataStore> store) {
+  return std::unique_ptr<OfflinePageModelImpl>(
+      new OfflinePageModelImpl(std::move(store), temp_dir_.GetPath(),
+                               base::ThreadTaskRunnerHandle::Get()));
+}
+
+void OfflinePageModelImplTest::ResetModel() {
+  model_->RemoveObserver(this);
+  OfflinePageTestStore* old_store = GetStore();
+  std::unique_ptr<OfflinePageMetadataStore> new_store(
+      new OfflinePageTestStore(*old_store));
+  model_ = BuildModel(std::move(new_store));
+  model_->AddObserver(this);
+  PumpLoop();
+}
+
+void OfflinePageModelImplTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void OfflinePageModelImplTest::FastForwardBy(base::TimeDelta delta) {
+  task_runner_->FastForwardBy(delta);
+}
+
+void OfflinePageModelImplTest::ResetResults() {
+  last_save_result_ = SavePageResult::CANCELLED;
+  last_delete_result_ = DeletePageResult::CANCELLED;
+  last_archiver_path_.clear();
+  last_pages_exist_result_.clear();
+  last_cleared_pages_count_ = 0;
+}
+
+OfflinePageTestStore* OfflinePageModelImplTest::GetStore() {
+  return static_cast<OfflinePageTestStore*>(model()->GetStoreForTesting());
+}
+
+void OfflinePageModelImplTest::SavePageWithArchiverAsync(
+    const GURL& url,
+    const ClientId& client_id,
+    const GURL& original_url,
+    std::unique_ptr<OfflinePageArchiver> archiver) {
+  OfflinePageModel::SavePageParams save_page_params;
+  save_page_params.url = url;
+  save_page_params.client_id = client_id;
+  save_page_params.original_url = original_url;
+  model()->SavePage(
+      save_page_params,
+      std::move(archiver),
+      base::Bind(&OfflinePageModelImplTest::OnSavePageDone, AsWeakPtr()));
+}
+
+void OfflinePageModelImplTest::SavePageWithArchiver(
+    const GURL& url,
+    const ClientId& client_id,
+    std::unique_ptr<OfflinePageArchiver> archiver) {
+  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+  PumpLoop();
+}
+
+void OfflinePageModelImplTest::SavePageWithArchiverResult(
+    const GURL& url,
+    const ClientId& client_id,
+    OfflinePageArchiver::ArchiverResult result) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(url, result));
+  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+  PumpLoop();
+}
+
+std::pair<SavePageResult, int64_t>
+OfflinePageModelImplTest::SavePage(const GURL& url, const ClientId& client_id) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+      url, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
+  PumpLoop();
+  return std::make_pair(last_save_result_, last_save_offline_id_);
+}
+
+MultipleOfflinePageItemResult OfflinePageModelImplTest::GetAllPages() {
+  MultipleOfflinePageItemResult result;
+  model()->GetAllPages(
+      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByClientIds(
+    const std::vector<ClientId>& client_ids) {
+  MultipleOfflinePageItemResult result;
+  model()->GetPagesByClientIds(
+      client_ids,
+      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+void OfflinePageModelImplTest::DeletePagesByClientIds(
+    const std::vector<ClientId>& client_ids) {
+  model()->DeletePagesByClientIds(
+      client_ids,
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+  PumpLoop();
+}
+
+CheckPagesExistOfflineResult OfflinePageModelImplTest::CheckPagesExistOffline(
+    std::set<GURL> pages) {
+  model()->CheckPagesExistOffline(
+      pages, base::Bind(&OfflinePageModelImplTest::OnCheckPagesExistOfflineDone,
+                        AsWeakPtr()));
+  PumpLoop();
+  return last_pages_exist_result_;
+}
+
+MultipleOfflineIdResult OfflinePageModelImplTest::GetOfflineIdsForClientId(
+    const ClientId& client_id) {
+  MultipleOfflineIdResult result;
+  model()->GetOfflineIdsForClientId(
+      client_id,
+      base::Bind(&OfflinePageModelImplTest::OnGetOfflineIdsForClientIdDone,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+void OfflinePageModelImplTest::OnGetOfflineIdsForClientIdDone(
+    MultipleOfflineIdResult* storage,
+    const MultipleOfflineIdResult& result) {
+  *storage = result;
+}
+
+std::unique_ptr<OfflinePageItem> OfflinePageModelImplTest::GetPageByOfflineId(
+    int64_t offline_id) {
+  std::unique_ptr<OfflinePageItem> result = nullptr;
+  model()->GetPageByOfflineId(
+      offline_id,
+      base::Bind(&OfflinePageModelImplTest::OnGetSingleOfflinePageItemResult,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+void OfflinePageModelImplTest::OnGetSingleOfflinePageItemResult(
+    std::unique_ptr<OfflinePageItem>* storage,
+    const OfflinePageItem* result) {
+  if (result == nullptr) {
+    storage->reset(nullptr);
+    return;
+  }
+
+  *storage = base::MakeUnique<OfflinePageItem>(*result);
+}
+
+void OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult(
+    MultipleOfflinePageItemResult* storage,
+    const MultipleOfflinePageItemResult& result) {
+  *storage = result;
+}
+
+void OfflinePageModelImplTest::OnPagesExpired(bool result) {
+  last_expire_page_result_ = result;
+}
+
+MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByFinalURL(
+    const GURL& url) {
+  MultipleOfflinePageItemResult result;
+  model()->GetPagesByURL(
+      url,
+      OfflinePageModel::URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
+      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByAllURLS(
+    const GURL& url) {
+  MultipleOfflinePageItemResult result;
+  model()->GetPagesByURL(
+      url,
+      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
+      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+                 AsWeakPtr(), base::Unretained(&result)));
+  PumpLoop();
+  return result;
+}
+
+bool OfflinePageModelImplTest::HasPages(std::string name_space) {
+  MultipleOfflinePageItemResult all_pages = GetAllPages();
+  for (const auto& page : all_pages) {
+    if (page.client_id.name_space == name_space)
+      return true;
+  }
+
+  return false;
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageSuccessful) {
+  EXPECT_FALSE(HasPages(kTestClientNamespace));
+
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  SavePageWithArchiverAsync(
+      kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
+  PumpLoop();
+  EXPECT_TRUE(HasPages(kTestClientNamespace));
+
+  OfflinePageTestStore* store = GetStore();
+  EXPECT_EQ(kTestUrl, store->last_saved_page().url);
+  EXPECT_EQ(kTestClientId1.id, store->last_saved_page().client_id.id);
+  EXPECT_EQ(kTestClientId1.name_space,
+            store->last_saved_page().client_id.name_space);
+  // Save last_archiver_path since it will be referred to later.
+  base::FilePath archiver_path = last_archiver_path();
+  EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
+  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  ResetResults();
+
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  ASSERT_EQ(1UL, offline_pages.size());
+  EXPECT_EQ(kTestUrl, offline_pages[0].url);
+  EXPECT_EQ(kTestClientId1.id, offline_pages[0].client_id.id);
+  EXPECT_EQ(kTestClientId1.name_space, offline_pages[0].client_id.name_space);
+  EXPECT_EQ(archiver_path, offline_pages[0].file_path);
+  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
+  EXPECT_EQ(0, offline_pages[0].access_count);
+  EXPECT_EQ(0, offline_pages[0].flags);
+  EXPECT_EQ(kTestTitle, offline_pages[0].title);
+  EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverCancelled) {
+  SavePageWithArchiverResult(
+      kTestUrl, kTestClientId1,
+      OfflinePageArchiver::ArchiverResult::ERROR_CANCELED);
+  EXPECT_EQ(SavePageResult::CANCELLED, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverDeviceFull) {
+  SavePageWithArchiverResult(
+      kTestUrl, kTestClientId1,
+      OfflinePageArchiver::ArchiverResult::ERROR_DEVICE_FULL);
+  EXPECT_EQ(SavePageResult::DEVICE_FULL, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverContentUnavailable) {
+  SavePageWithArchiverResult(
+      kTestUrl, kTestClientId1,
+      OfflinePageArchiver::ArchiverResult::ERROR_CONTENT_UNAVAILABLE);
+  EXPECT_EQ(SavePageResult::CONTENT_UNAVAILABLE, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineCreationFailed) {
+  SavePageWithArchiverResult(
+      kTestUrl, kTestClientId1,
+      OfflinePageArchiver::ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED);
+  EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverReturnedWrongUrl) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(
+      BuildArchiver(GURL("http://other.random.url.com"),
+                    OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  SavePageWithArchiver(kTestUrl, kTestClientId1, std::move(archiver));
+  EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineCreationStoreWriteFailure) {
+  GetStore()->set_test_scenario(
+      OfflinePageTestStore::TestScenario::WRITE_FAILED);
+  SavePageWithArchiverResult(
+      kTestUrl, kTestClientId1,
+      OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED);
+  EXPECT_EQ(SavePageResult::STORE_FAILURE, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageLocalFileFailed) {
+  // Don't create archiver since it will not be needed for pages that are not
+  // going to be saved.
+  SavePageWithArchiver(
+      kFileUrl, kTestClientId1, std::unique_ptr<OfflinePageTestArchiver>());
+  EXPECT_EQ(SavePageResult::SKIPPED, last_save_result());
+}
+
+TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  // archiver_ptr will be valid until after first PumpLoop() call after
+  // CompleteCreateArchive() is called.
+  OfflinePageTestArchiver* archiver_ptr = archiver.get();
+  archiver_ptr->set_delayed(true);
+  SavePageWithArchiverAsync(
+      kTestUrl, kTestClientId1, GURL(), std::move(archiver));
+  EXPECT_TRUE(archiver_ptr->create_archive_called());
+
+  // Request to save another page.
+  SavePage(kTestUrl2, kTestClientId2);
+
+  OfflinePageTestStore* store = GetStore();
+
+  EXPECT_EQ(kTestUrl2, store->last_saved_page().url);
+  EXPECT_EQ(kTestClientId2, store->last_saved_page().client_id);
+  base::FilePath archiver_path2 = last_archiver_path();
+  EXPECT_EQ(archiver_path2, store->last_saved_page().file_path);
+  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+
+  ResetResults();
+
+  archiver_ptr->CompleteCreateArchive();
+  // After this pump loop archiver_ptr is invalid.
+  PumpLoop();
+
+  EXPECT_EQ(kTestUrl, store->last_saved_page().url);
+  EXPECT_EQ(kTestClientId1, store->last_saved_page().client_id);
+  base::FilePath archiver_path = last_archiver_path();
+  EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
+  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+
+  ResetResults();
+
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  EXPECT_EQ(2UL, offline_pages.size());
+  // Offline IDs are random, so the order of the pages is also random
+  // So load in the right page for the validation below.
+  const OfflinePageItem* page1 = nullptr;
+  const OfflinePageItem* page2 = nullptr;
+  if (offline_pages[0].client_id == kTestClientId1) {
+    page1 = &offline_pages[0];
+    page2 = &offline_pages[1];
+  } else {
+    page1 = &offline_pages[1];
+    page2 = &offline_pages[0];
+  }
+
+  EXPECT_EQ(kTestUrl, page1->url);
+  EXPECT_EQ(kTestClientId1, page1->client_id);
+  EXPECT_EQ(archiver_path, page1->file_path);
+  EXPECT_EQ(kTestFileSize, page1->file_size);
+  EXPECT_EQ(0, page1->access_count);
+  EXPECT_EQ(0, page1->flags);
+  EXPECT_EQ(kTestUrl2, page2->url);
+  EXPECT_EQ(kTestClientId2, page2->client_id);
+  EXPECT_EQ(archiver_path2, page2->file_path);
+  EXPECT_EQ(kTestFileSize, page2->file_size);
+  EXPECT_EQ(0, page2->access_count);
+  EXPECT_EQ(0, page2->flags);
+}
+
+TEST_F(OfflinePageModelImplTest, MarkPageAccessed) {
+  SavePage(kTestUrl, kTestClientId1);
+
+  // This will increase access_count by one.
+  model()->MarkPageAccessed(last_save_offline_id());
+  PumpLoop();
+
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  EXPECT_EQ(1UL, offline_pages.size());
+  EXPECT_EQ(kTestUrl, offline_pages[0].url);
+  EXPECT_EQ(kTestClientId1, offline_pages[0].client_id);
+  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
+  EXPECT_EQ(1, offline_pages[0].access_count);
+}
+
+TEST_F(OfflinePageModelImplTest, GetAllPagesStoreEmpty) {
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  EXPECT_EQ(0UL, offline_pages.size());
+}
+
+TEST_F(OfflinePageModelImplTest, DeletePageSuccessful) {
+  OfflinePageTestStore* store = GetStore();
+
+  // Save one page.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(1u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Save another page.
+  SavePage(kTestUrl2, kTestClientId2);
+  int64_t offline2 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(2u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Delete one page.
+  DeletePage(offline1, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                  AsWeakPtr()));
+
+  PumpLoop();
+
+  EXPECT_EQ(last_deleted_offline_id(), offline1);
+  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  ASSERT_EQ(1u, store->GetAllPages().size());
+  EXPECT_EQ(kTestUrl2, store->GetAllPages()[0].url);
+
+  // Delete another page.
+  DeletePage(offline2, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                  AsWeakPtr()));
+
+  ResetResults();
+
+  PumpLoop();
+
+  EXPECT_EQ(last_deleted_offline_id(), offline2);
+  EXPECT_EQ(last_deleted_client_id(), kTestClientId2);
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  EXPECT_EQ(0u, store->GetAllPages().size());
+}
+
+TEST_F(OfflinePageModelImplTest, DeleteCachedPageByPredicateUserRequested) {
+  OfflinePageTestStore* store = GetStore();
+
+  // Save one page.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(1u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Save an user-requested page in same domain.
+  SavePage(kTestUrl, kTestUserRequestedClientId);
+  int64_t offline2 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(2u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Delete the second page.
+  model()->DeleteCachedPagesByURLPredicate(
+      base::Bind(&URLSpecContains, "example.com"),
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+
+  PumpLoop();
+
+  EXPECT_EQ(last_deleted_offline_id(), offline1);
+  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  ASSERT_EQ(1u, store->GetAllPages().size());
+  EXPECT_EQ(kTestUrl, store->GetAllPages()[0].url);
+  EXPECT_EQ(offline2, store->GetAllPages()[0].offline_id);
+}
+
+TEST_F(OfflinePageModelImplTest, DeleteCachedPageByPredicate) {
+  OfflinePageTestStore* store = GetStore();
+
+  // Save one page.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(1u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Save another page.
+  SavePage(kTestUrl2, kTestClientId2);
+  int64_t offline2 = last_save_offline_id();
+  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
+  EXPECT_EQ(2u, store->GetAllPages().size());
+
+  ResetResults();
+
+  // Delete the second page.
+  model()->DeleteCachedPagesByURLPredicate(
+      base::Bind(&URLSpecContains, "page.com"),
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+
+  PumpLoop();
+
+  EXPECT_EQ(last_deleted_offline_id(), offline2);
+  EXPECT_EQ(last_deleted_client_id(), kTestClientId2);
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  ASSERT_EQ(1u, store->GetAllPages().size());
+  EXPECT_EQ(kTestUrl, store->GetAllPages()[0].url);
+
+  ResetResults();
+
+  // Delete the first page.
+  model()->DeleteCachedPagesByURLPredicate(
+      base::Bind(&URLSpecContains, "example.com"),
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+
+  PumpLoop();
+
+  EXPECT_EQ(last_deleted_offline_id(), offline1);
+  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  EXPECT_EQ(0u, store->GetAllPages().size());
+}
+
+TEST_F(OfflinePageModelImplTest, DeletePageNotFound) {
+  DeletePage(1234LL, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                AsWeakPtr()));
+  PumpLoop();
+
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+
+  ResetResults();
+
+  model()->DeleteCachedPagesByURLPredicate(
+      base::Bind(&URLSpecContains, "page.com"),
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+  PumpLoop();
+
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+}
+
+TEST_F(OfflinePageModelImplTest, DeletePageStoreFailureOnRemove) {
+  // Save a page.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline_id = last_save_offline_id();
+  ResetResults();
+
+  // Try to delete this page.
+  GetStore()->set_test_scenario(
+      OfflinePageTestStore::TestScenario::REMOVE_FAILED);
+  DeletePage(offline_id, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                    AsWeakPtr()));
+  PumpLoop();
+  EXPECT_EQ(DeletePageResult::STORE_FAILURE, last_delete_result());
+}
+
+TEST_F(OfflinePageModelImplTest, DetectThatOfflineCopyIsMissing) {
+  // Save a page.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline_id = last_save_offline_id();
+
+  ResetResults();
+
+  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
+
+  // Delete the offline copy of the page.
+  base::DeleteFile(page->file_path, false);
+
+  // Resetting the model will cause a consistency check.
+  ResetModel();
+
+  PumpLoop();
+
+  // Check if the page has been expired.
+  EXPECT_EQ(0UL, GetAllPages().size());
+}
+
+TEST_F(OfflinePageModelImplTest, DetectThatOfflineCopyIsMissingAfterLoad) {
+  // Save a page.
+  SavePage(kTestUrl, kTestClientId1);
+  PumpLoop();
+  int64_t offline_id = last_save_offline_id();
+
+  ResetResults();
+
+  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
+  // Delete the offline copy of the page and check the metadata.
+  base::DeleteFile(page->file_path, false);
+  // Reseting the model should trigger the metadata consistency check as well.
+  ResetModel();
+  PumpLoop();
+
+  // Check if the page has been expired.
+  EXPECT_EQ(0UL, GetAllPages().size());
+}
+
+TEST_F(OfflinePageModelImplTest, DetectThatHeadlessPageIsDeleted) {
+  // Save a page.
+  SavePage(kTestUrl, kTestClientId1);
+  PumpLoop();
+  int64_t offline_id = last_save_offline_id();
+
+  ResetResults();
+  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
+  base::FilePath path = page->file_path;
+  EXPECT_TRUE(base::PathExists(path));
+  GetStore()->ClearAllPages();
+
+  EXPECT_TRUE(base::PathExists(path));
+  // Since we've manually changed the store, we have to reload the model to
+  // actually refresh the in-memory copy in model. Otherwise GetAllPages() would
+  // still have the page we saved above.
+  ResetModel();
+  PumpLoop();
+
+  EXPECT_EQ(0UL, GetAllPages().size());
+  EXPECT_FALSE(base::PathExists(path));
+}
+
+TEST_F(OfflinePageModelImplTest, DeleteMultiplePages) {
+  OfflinePageTestStore* store = GetStore();
+
+  // Save 3 pages.
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+
+  SavePage(kTestUrl2, kTestClientId2);
+  int64_t offline2 = last_save_offline_id();
+
+  SavePage(kTestUrl3, kTestClientId3);
+  PumpLoop();
+
+  EXPECT_EQ(3u, store->GetAllPages().size());
+
+  // Delete multiple pages.
+  std::vector<int64_t> ids_to_delete;
+  ids_to_delete.push_back(offline2);
+  ids_to_delete.push_back(offline1);
+  ids_to_delete.push_back(23434LL);  // Non-existent ID.
+  model()->DeletePagesByOfflineId(
+      ids_to_delete,
+      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
+  PumpLoop();
+
+  // Success is expected if at least one page is deleted successfully.
+  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
+  EXPECT_EQ(1u, store->GetAllPages().size());
+}
+
+TEST_F(OfflinePageModelImplTest, GetPageByOfflineId) {
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  SavePage(kTestUrl2, kTestClientId2);
+  int64_t offline2 = last_save_offline_id();
+
+  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline1);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(kTestUrl, page->url);
+  EXPECT_EQ(kTestClientId1, page->client_id);
+  EXPECT_EQ(kTestFileSize, page->file_size);
+
+  page = GetPageByOfflineId(offline2);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(kTestUrl2, page->url);
+  EXPECT_EQ(kTestClientId2, page->client_id);
+  EXPECT_EQ(kTestFileSize, page->file_size);
+
+  page = GetPageByOfflineId(-42);
+  EXPECT_FALSE(page);
+}
+
+TEST_F(OfflinePageModelImplTest, GetPagesByFinalURL) {
+  SavePage(kTestUrl, kTestClientId1);
+  SavePage(kTestUrl2, kTestClientId2);
+
+  MultipleOfflinePageItemResult pages = GetPagesByFinalURL(kTestUrl2);
+  EXPECT_EQ(1U, pages.size());
+  EXPECT_EQ(kTestUrl2, pages[0].url);
+  EXPECT_EQ(kTestClientId2, pages[0].client_id);
+
+  pages = GetPagesByFinalURL(kTestUrl);
+  EXPECT_EQ(1U, pages.size());
+  EXPECT_EQ(kTestUrl, pages[0].url);
+  EXPECT_EQ(kTestClientId1, pages[0].client_id);
+
+  pages = GetPagesByFinalURL(GURL("http://foo"));
+  EXPECT_EQ(0U, pages.size());
+}
+
+TEST_F(OfflinePageModelImplTest, GetPagesByFinalURLWithFragmentStripped) {
+  SavePage(kTestUrl, kTestClientId1);
+  SavePage(kTestUrl2WithFragment, kTestClientId2);
+
+  MultipleOfflinePageItemResult pages =
+      GetPagesByFinalURL(kTestUrlWithFragment);
+  EXPECT_EQ(1U, pages.size());
+  EXPECT_EQ(kTestUrl, pages[0].url);
+  EXPECT_EQ(kTestClientId1, pages[0].client_id);
+
+  pages = GetPagesByFinalURL(kTestUrl2);
+  EXPECT_EQ(1U, pages.size());
+  EXPECT_EQ(kTestUrl2WithFragment, pages[0].url);
+  EXPECT_EQ(kTestClientId2, pages[0].client_id);
+
+  pages = GetPagesByFinalURL(kTestUrl2WithFragment2);
+  EXPECT_EQ(1U, pages.size());
+  EXPECT_EQ(kTestUrl2WithFragment, pages[0].url);
+  EXPECT_EQ(kTestClientId2, pages[0].client_id);
+}
+
+TEST_F(OfflinePageModelImplTest, GetPagesByAllURLS) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  SavePageWithArchiverAsync(
+      kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
+  PumpLoop();
+
+  SavePage(kTestUrl2, kTestClientId2);
+
+  MultipleOfflinePageItemResult pages = GetPagesByAllURLS(kTestUrl2);
+  ASSERT_EQ(2U, pages.size());
+  // Validates the items regardless their order.
+  int i = -1;
+  if (pages[0].url == kTestUrl2)
+    i = 0;
+  else if (pages[1].url == kTestUrl2)
+    i = 1;
+  ASSERT_NE(-1, i);
+  EXPECT_EQ(kTestUrl2, pages[i].url);
+  EXPECT_EQ(kTestClientId2, pages[i].client_id);
+  EXPECT_EQ(GURL(), pages[i].original_url);
+  EXPECT_EQ(kTestUrl, pages[1 - i].url);
+  EXPECT_EQ(kTestClientId1, pages[1 - i].client_id);
+  EXPECT_EQ(kTestUrl2, pages[1 - i].original_url);
+}
+
+TEST_F(OfflinePageModelImplTest, CheckPagesExistOffline) {
+  SavePage(kTestUrl, kTestClientId1);
+  SavePage(kTestUrl2, kTestClientId2);
+
+  // TODO(dewittj): Remove the "Last N" restriction in favor of a better query
+  // interface.  See https://crbug.com/622763 for information.
+  const ClientId last_n_client_id(kLastNNamespace, "1234");
+  SavePage(kTestUrl3, last_n_client_id);
+
+  std::set<GURL> input;
+  input.insert(kTestUrl);
+  input.insert(kTestUrl2);
+  input.insert(kTestUrl3);
+  input.insert(kTestUrl4);
+
+  CheckPagesExistOfflineResult existing_pages = CheckPagesExistOffline(input);
+  EXPECT_EQ(2U, existing_pages.size());
+  EXPECT_NE(existing_pages.end(), existing_pages.find(kTestUrl));
+  EXPECT_NE(existing_pages.end(), existing_pages.find(kTestUrl2));
+  EXPECT_EQ(existing_pages.end(), existing_pages.find(kTestUrl3));
+  EXPECT_EQ(existing_pages.end(), existing_pages.find(kTestUrl4));
+}
+
+TEST_F(OfflinePageModelImplTest, CanSaveURL) {
+  EXPECT_TRUE(OfflinePageModel::CanSaveURL(GURL("http://foo")));
+  EXPECT_TRUE(OfflinePageModel::CanSaveURL(GURL("https://foo")));
+  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("file:///foo")));
+  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("data:image/png;base64,ab")));
+  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("chrome://version")));
+  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("chrome-native://newtab/")));
+  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("/invalid/url.mhtml")));
+}
+
+TEST_F(OfflinePageModelImplTest, SaveRetrieveMultipleClientIds) {
+  EXPECT_FALSE(HasPages(kTestClientNamespace));
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  EXPECT_TRUE(HasPages(kTestClientNamespace));
+
+  SavePage(kTestUrl2, kTestClientId1);
+  int64_t offline2 = last_save_offline_id();
+
+  EXPECT_NE(offline1, offline2);
+
+  const std::vector<int64_t> ids = GetOfflineIdsForClientId(kTestClientId1);
+
+  EXPECT_EQ(2UL, ids.size());
+
+  std::set<int64_t> id_set;
+  for (size_t i = 0; i < ids.size(); i++) {
+    id_set.insert(ids[i]);
+  }
+
+  EXPECT_TRUE(id_set.find(offline1) != id_set.end());
+  EXPECT_TRUE(id_set.find(offline2) != id_set.end());
+}
+
+TEST_F(OfflinePageModelImplTest, SaveMultiplePagesWithSameURLBySameClientId) {
+  EXPECT_FALSE(HasPages(kTestClientNamespace));
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline1 = last_save_offline_id();
+  EXPECT_TRUE(HasPages(kTestClientNamespace));
+
+  SavePage(kTestUrl, kTestClientId1);
+  int64_t offline2 = last_save_offline_id();
+
+  EXPECT_NE(offline1, offline2);
+
+  const std::vector<int64_t> ids = GetOfflineIdsForClientId(kTestClientId1);
+
+  EXPECT_EQ(1UL, ids.size());
+
+  std::set<int64_t> id_set;
+  for (size_t i = 0; i < ids.size(); i++) {
+    id_set.insert(ids[i]);
+  }
+
+  EXPECT_TRUE(id_set.find(offline2) != id_set.end());
+}
+
+TEST_F(OfflinePageModelImplTest, DownloadMetrics) {
+  EXPECT_FALSE(HasPages(kUserRequestedNamespace));
+  SavePage(kTestUrl, kTestUserRequestedClientId);
+  histograms().ExpectUniqueSample(
+      "OfflinePages.DownloadSavedPageDuplicateCount", 1, 1);
+  FastForwardBy(base::TimeDelta::FromMinutes(1));
+  histograms().ExpectTotalCount(
+      "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", 0);
+  SavePage(kTestUrl, kTestUserRequestedClientId);
+  histograms().ExpectTotalCount("OfflinePages.DownloadSavedPageDuplicateCount",
+                                2);
+  histograms().ExpectBucketCount("OfflinePages.DownloadSavedPageDuplicateCount",
+                                 2, 1);
+  histograms().ExpectBucketCount("OfflinePages.DownloadSavedPageDuplicateCount",
+                                 1, 1);
+  histograms().ExpectTotalCount(
+      "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", 1);
+  histograms().ExpectTotalCount(
+      "OfflinePages.DownloadDeletedPageDuplicateCount", 0);
+
+  // void DeletePage(int64_t offline_id, const DeletePageCallback& callback) {
+  const std::vector<int64_t> ids =
+      GetOfflineIdsForClientId(kTestUserRequestedClientId);
+  ASSERT_EQ(2U, ids.size());
+
+  DeletePage(ids[0], base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                AsWeakPtr()));
+  PumpLoop();
+  histograms().ExpectUniqueSample(
+      "OfflinePages.DownloadDeletedPageDuplicateCount", 2, 1);
+  DeletePage(ids[1], base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
+                                AsWeakPtr()));
+  PumpLoop();
+  // No change when we delete the last page.
+  histograms().ExpectTotalCount(
+      "OfflinePages.DownloadDeletedPageDuplicateCount", 2);
+  histograms().ExpectBucketCount(
+      "OfflinePages.DownloadDeletedPageDuplicateCount", 1, 1);
+  histograms().ExpectBucketCount(
+      "OfflinePages.DownloadDeletedPageDuplicateCount", 2, 1);
+}
+
+TEST_F(OfflinePageModelImplTest, GetPagesByClientIds) {
+  // We will save 2 pages.
+  std::pair<SavePageResult, int64_t> saved_pages[3];
+  saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
+  saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
+
+  for (const auto& save_result : saved_pages) {
+    ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
+              std::get<0>(save_result));
+  }
+
+  std::vector<ClientId> client_ids = {kTestClientId2};
+  std::vector<OfflinePageItem> offline_pages = GetPagesByClientIds(client_ids);
+  EXPECT_EQ(1U, offline_pages.size());
+
+  const OfflinePageItem& item = offline_pages[0];
+  EXPECT_EQ(kTestClientId2.name_space, item.client_id.name_space);
+  EXPECT_EQ(kTestClientId2.id, item.client_id.id);
+  EXPECT_EQ(kTestUrl2, item.url);
+}
+
+TEST_F(OfflinePageModelImplTest, DeletePagesByClientIds) {
+  // We will save 3 pages.
+  std::pair<SavePageResult, int64_t> saved_pages[3];
+  saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
+  saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
+  saved_pages[2] = SavePage(kTestUrl3, kTestClientId3);
+
+  for (const auto& save_result : saved_pages) {
+    ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
+              std::get<0>(save_result));
+  }
+
+  std::vector<ClientId> client_ids = {kTestClientId1, kTestClientId2};
+  DeletePagesByClientIds(client_ids);
+  std::vector<OfflinePageItem> offline_pages = GetAllPages();
+  ASSERT_EQ(1U, offline_pages.size());
+
+  const OfflinePageItem& item = offline_pages[0];
+  EXPECT_EQ(kTestClientId3.name_space, item.client_id.name_space);
+  EXPECT_EQ(kTestClientId3.id, item.client_id.id);
+  EXPECT_EQ(kTestUrl3, item.url);
+}
+
+TEST_F(OfflinePageModelImplTest, CustomTabsNamespace) {
+  SavePage(kTestUrl, ClientId(kCCTNamespace, "123"));
+  std::string histogram_name = "OfflinePages.SavePageResult.";
+  histogram_name += kCCTNamespace;
+
+  histograms().ExpectUniqueSample(histogram_name,
+                                  static_cast<int>(SavePageResult::SUCCESS), 1);
+}
+
+TEST_F(OfflinePageModelImplTest, DownloadNamespace) {
+  SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
+  std::string histogram_name = "OfflinePages.SavePageResult.";
+  histogram_name += kDownloadNamespace;
+
+  histograms().ExpectUniqueSample(histogram_name,
+                                  static_cast<int>(SavePageResult::SUCCESS), 1);
+}
+
+TEST_F(OfflinePageModelImplTest, NewTabPageNamespace) {
+  SavePage(kTestUrl, ClientId(kNTPSuggestionsNamespace, "123"));
+  std::string histogram_name = "OfflinePages.SavePageResult.";
+  histogram_name += kNTPSuggestionsNamespace;
+
+  histograms().ExpectUniqueSample(histogram_name,
+                                  static_cast<int>(SavePageResult::SUCCESS), 1);
+}
+
+TEST_F(OfflinePageModelImplTest, StoreResetSuccessful) {
+  GetStore()->set_test_scenario(
+      OfflinePageTestStore::TestScenario::LOAD_FAILED_RESET_SUCCESS);
+  ResetModel();
+
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  EXPECT_TRUE(model()->is_loaded());
+  EXPECT_EQ(StoreState::LOADED, GetStore()->state());
+  EXPECT_EQ(0UL, offline_pages.size());
+
+  std::pair<SavePageResult, int64_t> result =
+      SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
+
+  EXPECT_EQ(SavePageResult::SUCCESS, result.first);
+}
+
+TEST_F(OfflinePageModelImplTest, StoreResetFailed) {
+  GetStore()->set_test_scenario(
+      OfflinePageTestStore::TestScenario::LOAD_FAILED_RESET_FAILED);
+  ResetModel();
+
+  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
+
+  EXPECT_TRUE(model()->is_loaded());
+  EXPECT_EQ(StoreState::FAILED_RESET, GetStore()->state());
+  EXPECT_EQ(0UL, offline_pages.size());
+
+  ResetResults();
+  std::pair<SavePageResult, int64_t> result =
+      SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
+
+  EXPECT_EQ(SavePageResult::STORE_FAILURE, result.first);
+}
+
+TEST_F(OfflinePageModelImplTest, GetPagesMatchingQuery) {
+  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
+      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
+  SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2,
+                            std::move(archiver));
+  PumpLoop();
+
+  std::vector<ClientId> client_ids{kTestClientId1};
+  OfflinePageModelQueryBuilder builder;
+  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
+                       client_ids);
+
+  MultipleOfflinePageItemResult offline_pages;
+  model()->GetPagesMatchingQuery(
+      builder.Build(model()->GetPolicyController()),
+      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
+                 AsWeakPtr(), base::Unretained(&offline_pages)));
+  PumpLoop();
+
+  ASSERT_EQ(1UL, offline_pages.size());
+  EXPECT_EQ(kTestUrl, offline_pages[0].url);
+  EXPECT_EQ(kTestClientId1.id, offline_pages[0].client_id.id);
+  EXPECT_EQ(kTestClientId1.name_space, offline_pages[0].client_id.name_space);
+  EXPECT_EQ(last_archiver_path(), offline_pages[0].file_path);
+  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
+  EXPECT_EQ(0, offline_pages[0].access_count);
+  EXPECT_EQ(0, offline_pages[0].flags);
+  EXPECT_EQ(kTestTitle, offline_pages[0].title);
+  EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
+}
+
+TEST(CommandLineFlagsTest, OfflineBookmarks) {
+  // Disabled by default.
+  EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
+
+  // Check if feature is correctly enabled by command-line flag.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kOfflineBookmarksFeature);
+  EXPECT_TRUE(offline_pages::IsOfflineBookmarksEnabled());
+}
+
+TEST(CommandLineFlagsTest, OffliningRecentPages) {
+  // Enable offline bookmarks feature first.
+  // TODO(dimich): once offline pages are enabled by default, remove this.
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
+      new base::test::ScopedFeatureList);
+  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
+
+  // This feature is still disabled by default.
+  EXPECT_FALSE(offline_pages::IsOffliningRecentPagesEnabled());
+
+  // Check if feature is correctly enabled by command-line flag.
+  scoped_feature_list.reset(new base::test::ScopedFeatureList);
+  scoped_feature_list->InitFromCommandLine(
+      std::string(kOfflineBookmarksFeature.name) + "," +
+          kOffliningRecentPagesFeature.name,
+      "");
+  EXPECT_TRUE(offline_pages::IsOffliningRecentPagesEnabled());
+}
+
+TEST(CommandLineFlagsTest, OfflinePagesSharing) {
+  // Enable offline bookmarks feature first.
+  // TODO(dimich): once offline pages are enabled by default, remove this.
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
+      new base::test::ScopedFeatureList);
+  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
+
+  // This feature is still disabled by default.
+  EXPECT_FALSE(offline_pages::IsOfflinePagesSharingEnabled());
+
+  // Check if feature is correctly enabled by command-line flag.
+  scoped_feature_list.reset(new base::test::ScopedFeatureList);
+  scoped_feature_list->InitFromCommandLine(
+      std::string(kOfflineBookmarksFeature.name) + "," +
+          kOfflinePagesSharingFeature.name,
+      "");
+  EXPECT_TRUE(offline_pages::IsOfflinePagesSharingEnabled());
+}
+
+TEST(CommandLineFlagsTest, OfflinePagesSvelteConcurrentLoading) {
+  // This feature is disabled by default.
+  EXPECT_FALSE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
+
+  // Check if feature is correctly enabled by command-line flag.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kOfflinePagesSvelteConcurrentLoadingFeature);
+  EXPECT_TRUE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model_query.cc b/components/offline_pages/core/offline_page_model_query.cc
new file mode 100644
index 0000000..6aa192b
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_query.cc
@@ -0,0 +1,210 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_model_query.h"
+
+#include <algorithm>
+#include <unordered_set>
+
+#include "base/memory/ptr_util.h"
+
+namespace offline_pages {
+
+using Requirement = OfflinePageModelQuery::Requirement;
+
+OfflinePageModelQueryBuilder::OfflinePageModelQueryBuilder()
+    : offline_ids_(std::make_pair(Requirement::UNSET, std::vector<int64_t>())) {
+}
+
+OfflinePageModelQueryBuilder::~OfflinePageModelQueryBuilder() = default;
+
+OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetOfflinePageIds(
+    Requirement requirement,
+    const std::vector<int64_t>& ids) {
+  offline_ids_ = std::make_pair(requirement, ids);
+  return *this;
+}
+
+OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetClientIds(
+    Requirement requirement,
+    const std::vector<ClientId>& ids) {
+  client_ids_ = std::make_pair(requirement, ids);
+  return *this;
+}
+
+OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetUrls(
+    Requirement requirement,
+    const std::vector<GURL>& urls) {
+  urls_ = std::make_pair(requirement, urls);
+  return *this;
+}
+
+OfflinePageModelQueryBuilder&
+OfflinePageModelQueryBuilder::RequireSupportedByDownload(
+    Requirement supported_by_download) {
+  supported_by_download_ = supported_by_download;
+  return *this;
+}
+
+OfflinePageModelQueryBuilder&
+OfflinePageModelQueryBuilder::RequireShownAsRecentlyVisitedSite(
+    Requirement recently_visited) {
+  shown_as_recently_visited_site_ = recently_visited;
+  return *this;
+}
+
+OfflinePageModelQueryBuilder&
+OfflinePageModelQueryBuilder::RequireRestrictedToOriginalTab(
+    Requirement original_tab) {
+  restricted_to_original_tab_ = original_tab;
+  return *this;
+}
+
+std::unique_ptr<OfflinePageModelQuery> OfflinePageModelQueryBuilder::Build(
+    ClientPolicyController* controller) {
+  DCHECK(controller);
+
+  auto query = base::MakeUnique<OfflinePageModelQuery>();
+
+  query->urls_ = std::make_pair(
+      urls_.first, std::set<GURL>(urls_.second.begin(), urls_.second.end()));
+  urls_ = std::make_pair(Requirement::UNSET, std::vector<GURL>());
+  query->offline_ids_ = std::make_pair(
+      offline_ids_.first, std::set<int64_t>(offline_ids_.second.begin(),
+                                            offline_ids_.second.end()));
+  offline_ids_ = std::make_pair(Requirement::UNSET, std::vector<int64_t>());
+  query->client_ids_ = std::make_pair(
+      client_ids_.first,
+      std::set<ClientId>(client_ids_.second.begin(), client_ids_.second.end()));
+  client_ids_ = std::make_pair(Requirement::UNSET, std::vector<ClientId>());
+
+  std::vector<std::string> allowed_namespaces;
+  bool uses_namespace_restrictions = false;
+
+  for (auto& name_space : controller->GetAllNamespaces()) {
+    // If any exclusion requirements exist, and the namespace matches one of
+    // those excluded by policy, skip adding it to |allowed_namespaces|.
+    if ((supported_by_download_ == Requirement::EXCLUDE_MATCHING &&
+         controller->IsSupportedByDownload(name_space)) ||
+        (shown_as_recently_visited_site_ == Requirement::EXCLUDE_MATCHING &&
+         controller->IsShownAsRecentlyVisitedSite(name_space)) ||
+        (restricted_to_original_tab_ == Requirement::EXCLUDE_MATCHING &&
+         controller->IsRestrictedToOriginalTab(name_space))) {
+      // Skip namespaces that meet exclusion requirements.
+      uses_namespace_restrictions = true;
+      continue;
+    }
+
+    if ((supported_by_download_ == Requirement::INCLUDE_MATCHING &&
+         !controller->IsSupportedByDownload(name_space)) ||
+        (shown_as_recently_visited_site_ == Requirement::INCLUDE_MATCHING &&
+         !controller->IsShownAsRecentlyVisitedSite(name_space)) ||
+        (restricted_to_original_tab_ == Requirement::INCLUDE_MATCHING &&
+         !controller->IsRestrictedToOriginalTab(name_space))) {
+      // Skip namespaces that don't meet inclusion requirement.
+      uses_namespace_restrictions = true;
+      continue;
+    }
+
+    // Add namespace otherwise.
+    allowed_namespaces.emplace_back(name_space);
+  }
+
+  supported_by_download_ = Requirement::UNSET;
+  shown_as_recently_visited_site_ = Requirement::UNSET;
+  restricted_to_original_tab_ = Requirement::UNSET;
+
+  if (uses_namespace_restrictions) {
+    query->restricted_to_namespaces_ = base::MakeUnique<std::set<std::string>>(
+        allowed_namespaces.begin(), allowed_namespaces.end());
+  }
+
+  return query;
+}
+
+OfflinePageModelQuery::OfflinePageModelQuery() = default;
+OfflinePageModelQuery::~OfflinePageModelQuery() = default;
+
+std::pair<bool, std::set<std::string>>
+OfflinePageModelQuery::GetRestrictedToNamespaces() const {
+  if (!restricted_to_namespaces_)
+    return std::make_pair(false, std::set<std::string>());
+
+  return std::make_pair(true, *restricted_to_namespaces_);
+}
+
+std::pair<Requirement, std::set<int64_t>>
+OfflinePageModelQuery::GetRestrictedToOfflineIds() const {
+  if (offline_ids_.first == Requirement::UNSET)
+    return std::make_pair(Requirement::UNSET, std::set<int64_t>());
+
+  return offline_ids_;
+}
+
+std::pair<Requirement, std::set<ClientId>>
+OfflinePageModelQuery::GetRestrictedToClientIds() const {
+  if (client_ids_.first == Requirement::UNSET)
+    return std::make_pair(Requirement::UNSET, std::set<ClientId>());
+
+  return client_ids_;
+}
+
+std::pair<Requirement, std::set<GURL>>
+OfflinePageModelQuery::GetRestrictedToUrls() const {
+  if (urls_.first == Requirement::UNSET)
+    return std::make_pair(Requirement::UNSET, std::set<GURL>());
+
+  return urls_;
+}
+
+bool OfflinePageModelQuery::Matches(const OfflinePageItem& item) const {
+  switch (offline_ids_.first) {
+    case Requirement::UNSET:
+      break;
+    case Requirement::INCLUDE_MATCHING:
+      if (offline_ids_.second.count(item.offline_id) == 0)
+        return false;
+      break;
+    case Requirement::EXCLUDE_MATCHING:
+      if (offline_ids_.second.count(item.offline_id) > 0)
+        return false;
+      break;
+  }
+
+  switch (urls_.first) {
+    case Requirement::UNSET:
+      break;
+    case Requirement::INCLUDE_MATCHING:
+      if (urls_.second.count(item.url) == 0)
+        return false;
+      break;
+    case Requirement::EXCLUDE_MATCHING:
+      if (urls_.second.count(item.url) > 0)
+        return false;
+      break;
+  }
+
+  const ClientId& client_id = item.client_id;
+  if (restricted_to_namespaces_ &&
+      restricted_to_namespaces_->count(client_id.name_space) == 0) {
+    return false;
+  }
+
+  switch (client_ids_.first) {
+    case Requirement::UNSET:
+      break;
+    case Requirement::INCLUDE_MATCHING:
+      if (client_ids_.second.count(client_id) == 0)
+        return false;
+      break;
+    case Requirement::EXCLUDE_MATCHING:
+      if (client_ids_.second.count(client_id) > 0)
+        return false;
+      break;
+  }
+
+  return true;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_model_query.h b/components/offline_pages/core/offline_page_model_query.h
new file mode 100644
index 0000000..526c370
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_query.h
@@ -0,0 +1,135 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
+
+#include <set>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_types.h"
+
+namespace offline_pages {
+
+// Can be used by OfflinePageModel instances to direct a query of the model.
+class OfflinePageModelQuery {
+ public:
+  // A query can be constrained by several fields.  This constraint can be
+  // either a positive or negative one (or no constraint): a page MUST have
+  // something, or a page MUST NOT have something to match a query.
+  enum class Requirement {
+    // No requirement.
+    UNSET,
+    // All returned pages will have one of the values specified in the query
+    // requirement.
+    INCLUDE_MATCHING,
+    // All returned pages will not have any of the values specified in the query
+    // requirement.
+    EXCLUDE_MATCHING,
+  };
+
+  OfflinePageModelQuery();
+  virtual ~OfflinePageModelQuery();
+
+  std::pair<bool, std::set<std::string>> GetRestrictedToNamespaces() const;
+  std::pair<Requirement, std::set<int64_t>> GetRestrictedToOfflineIds() const;
+  std::pair<Requirement, std::set<ClientId>> GetRestrictedToClientIds() const;
+  std::pair<Requirement, std::set<GURL>> GetRestrictedToUrls() const;
+
+  // This is the workhorse function that is used by the in-memory offline page
+  // model, given a page it will find out whether that page matches the query.
+  bool Matches(const OfflinePageItem& page) const;
+
+ private:
+  friend class OfflinePageModelQueryBuilder;
+
+  std::unique_ptr<std::set<std::string>> restricted_to_namespaces_;
+
+  std::pair<Requirement, std::set<int64_t>> offline_ids_;
+  std::pair<Requirement, std::set<ClientId>> client_ids_;
+  std::pair<Requirement, std::set<GURL>> urls_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQuery);
+};
+
+// Used to create an offline page model query.  QueryBuilders without
+// modifications create queries that allow all pages.
+// Can restrict results by policies provided by |ClientPolicyController|, or by
+// individual features of pages. Each restriction comes with a |Requirement|
+// that can be used to specify whether the input restriction should include or
+// exclude matching pages.
+class OfflinePageModelQueryBuilder {
+ public:
+  using Requirement = OfflinePageModelQuery::Requirement;
+
+  OfflinePageModelQueryBuilder();
+  ~OfflinePageModelQueryBuilder();
+
+  // Sets the offline page IDs that are valid for this request.  If called
+  // multiple times, overwrites previous offline page ID restrictions.
+  OfflinePageModelQueryBuilder& SetOfflinePageIds(
+      Requirement requirement,
+      const std::vector<int64_t>& ids);
+
+  // Sets the client IDs that are valid for this request.  If called multiple
+  // times, overwrites previous client ID restrictions.
+  OfflinePageModelQueryBuilder& SetClientIds(Requirement requirement,
+                                             const std::vector<ClientId>& ids);
+
+  // Sets the URLs that are valid for this request.  If called multiple times,
+  // overwrites previous URL restrictions.
+  OfflinePageModelQueryBuilder& SetUrls(Requirement requirement,
+                                        const std::vector<GURL>& urls);
+
+  // Only include pages whose namespaces satisfy
+  // ClientPolicyController::IsSupportedByDownload(|namespace|) ==
+  //     |supported_by_download|
+  // Multiple calls overwrite previous ones.
+  OfflinePageModelQueryBuilder& RequireSupportedByDownload(
+      Requirement supported_by_download);
+
+  // Only include pages whose namespaces satisfy
+  // ClientPolicyController::IsShownAsRecentlyVisitedSite(|namespace|) ==
+  //     |recently_visited|
+  // Multiple calls overwrite previous ones.
+  OfflinePageModelQueryBuilder& RequireShownAsRecentlyVisitedSite(
+      Requirement recently_visited);
+
+  // Only include pages whose namespaces satisfy
+  // ClientPolicyController::IsRestrictedToOriginalTab(|namespace|) ==
+  //     |original_tab|
+  // Multiple calls overwrite previous ones.
+  OfflinePageModelQueryBuilder& RequireRestrictedToOriginalTab(
+      Requirement original_tab);
+
+  // Builds the query using the namespace policies provided by |controller|
+  // This resets the internal state.  |controller| should not be |nullptr|.
+  std::unique_ptr<OfflinePageModelQuery> Build(
+      ClientPolicyController* controller);
+
+ private:
+  // Intersects the allowed namespaces in query_ with |namespaces|.  If
+  // |inverted| is true, intersects the allowed namespaces with all namespaces
+  // except those provided in |namespaces|.
+  void IntersectWithNamespaces(ClientPolicyController* controller,
+                               const std::vector<std::string>& namespaces,
+                               Requirement match_requirement);
+
+  std::pair<Requirement, std::vector<int64_t>> offline_ids_;
+  std::pair<Requirement, std::vector<ClientId>> client_ids_;
+  std::pair<Requirement, std::vector<GURL>> urls_;
+
+  Requirement supported_by_download_ = Requirement::UNSET;
+  Requirement shown_as_recently_visited_site_ = Requirement::UNSET;
+  Requirement restricted_to_original_tab_ = Requirement::UNSET;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQueryBuilder);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_MODEL_QUERY_H_
diff --git a/components/offline_pages/core/offline_page_model_query_unittest.cc b/components/offline_pages/core/offline_page_model_query_unittest.cc
new file mode 100644
index 0000000..fc05633
--- /dev/null
+++ b/components/offline_pages/core/offline_page_model_query_unittest.cc
@@ -0,0 +1,399 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+#include "components/offline_pages/client_namespace_constants.h"
+#include "components/offline_pages/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_model_query.h"
+#include "components/offline_pages/offline_page_client_policy.h"
+#include "components/offline_pages/offline_page_item.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+using Requirement = OfflinePageModelQueryBuilder::Requirement;
+
+namespace {
+
+const ClientId kClientId1 = {kDefaultNamespace, "id1"};
+const GURL kUrl1 = GURL("https://ktestitem1.com");
+const OfflinePageItem kTestItem1(kUrl1, 1, kClientId1, base::FilePath(), 1);
+
+const ClientId kClientId2 = {kDefaultNamespace, "id2"};
+const GURL kUrl2 = GURL("https://ktestitem2.com");
+const OfflinePageItem kTestItem2(kUrl2, 2, kClientId2, base::FilePath(), 2);
+
+const char kTestNamespace[] = "test_namespace";
+}  // namespace
+
+class OfflinePageModelQueryTest : public testing::Test {
+ public:
+  OfflinePageModelQueryTest();
+  ~OfflinePageModelQueryTest() override;
+
+ protected:
+  ClientPolicyController policy_;
+  OfflinePageModelQueryBuilder builder_;
+
+  const OfflinePageItem download_page() {
+    return OfflinePageItem(GURL("https://download.com"), 4,
+                           {kDownloadNamespace, "id1"}, base::FilePath(), 4);
+  }
+
+  const OfflinePageItem original_tab_page() {
+    return OfflinePageItem(GURL("https://download.com"), 5,
+                           {kLastNNamespace, "id1"}, base::FilePath(), 5);
+  }
+
+  const OfflinePageItem test_namespace_page() {
+    return OfflinePageItem(GURL("https://download.com"), 6,
+                           {kTestNamespace, "id1"}, base::FilePath(), 6);
+  }
+
+  const OfflinePageItem recent_page() {
+    return OfflinePageItem(GURL("https://download.com"), 7,
+                           {kLastNNamespace, "id1"}, base::FilePath(), 7);
+  }
+};
+
+OfflinePageModelQueryTest::OfflinePageModelQueryTest() {
+  policy_.AddPolicyForTest(
+      kTestNamespace,
+      OfflinePageClientPolicyBuilder(kTestNamespace,
+                                     LifetimePolicy::LifetimeType::TEMPORARY,
+                                     kUnlimitedPages, kUnlimitedPages)
+          .SetIsOnlyShownInOriginalTab(true));
+}
+
+OfflinePageModelQueryTest::~OfflinePageModelQueryTest() {}
+
+TEST_F(OfflinePageModelQueryTest, DefaultValues) {
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  EXPECT_NE(nullptr, query.get());
+  EXPECT_EQ(Requirement::UNSET, query->GetRestrictedToOfflineIds().first);
+  EXPECT_FALSE(query->GetRestrictedToNamespaces().first);
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, OfflinePageIdsSet_Exclude) {
+  std::vector<int64_t> ids = {1, 4, 9, 16};
+  builder_.SetOfflinePageIds(Requirement::EXCLUDE_MATCHING, ids);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
+      query->GetRestrictedToOfflineIds();
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, offline_id_restriction.first);
+
+  ASSERT_EQ(ids.size(), offline_id_restriction.second.size());
+  for (auto id : ids) {
+    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
+        << "Did not find " << id << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, OfflinePageIdsSet) {
+  std::vector<int64_t> ids = {1, 4, 9, 16};
+  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
+      query->GetRestrictedToOfflineIds();
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, offline_id_restriction.first);
+
+  ASSERT_EQ(ids.size(), offline_id_restriction.second.size());
+  for (auto id : ids) {
+    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
+        << "Did not find " << id << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, OfflinePageIdsReplace) {
+  std::vector<int64_t> ids = {1, 4, 9, 16};
+  std::vector<int64_t> ids2 = {1, 2, 3, 4};
+
+  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids);
+  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids2);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
+      query->GetRestrictedToOfflineIds();
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, offline_id_restriction.first);
+
+  ASSERT_EQ(ids2.size(), offline_id_restriction.second.size());
+  for (auto id : ids2) {
+    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
+        << "Did not find " << id << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, ClientIdsSet) {
+  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
+  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToClientIds();
+  const Requirement& requirement = restriction.first;
+  const std::set<ClientId>& ids_out = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(ids.size(), ids_out.size());
+  for (auto id : ids) {
+    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
+                                     << id.id << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem2));
+  EXPECT_FALSE(query->Matches(kTestItem1));
+}
+
+TEST_F(OfflinePageModelQueryTest, ClientIdsSet_Exclude) {
+  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
+  builder_.SetClientIds(Requirement::EXCLUDE_MATCHING, ids);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToClientIds();
+  const Requirement& requirement = restriction.first;
+  const std::set<ClientId>& ids_out = restriction.second;
+
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(ids.size(), ids_out.size());
+  for (auto id : ids) {
+    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
+                                     << id.id << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, ClientIdsReplace) {
+  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
+  std::vector<ClientId> ids2 = {kClientId1, {"invalid", "client id"}};
+
+  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids);
+  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids2);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToClientIds();
+  const Requirement& requirement = restriction.first;
+  const std::set<ClientId>& ids_out = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(ids2.size(), ids_out.size());
+  for (auto id : ids2) {
+    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
+                                     << id.id << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const std::set<GURL>& urls_out = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(urls.size(), urls_out.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
+                                       << "in query restrictions.";
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const std::set<GURL>& urls_out = restriction.second;
+
+  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(urls.size(), urls_out.size());
+  for (auto url : urls) {
+    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
+                                       << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, UrlsReplace) {
+  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
+  std::vector<GURL> urls2 = {kUrl2, GURL("https://abc.def")};
+
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
+  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls2);
+
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToUrls();
+  const Requirement& requirement = restriction.first;
+  const std::set<GURL>& urls_out = restriction.second;
+
+  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
+
+  ASSERT_EQ(urls2.size(), urls_out.size());
+  for (auto url : urls2) {
+    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
+                                       << "in query restrictions.";
+  }
+
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(kTestItem2));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireSupportedByDownload_Only) {
+  builder_.RequireSupportedByDownload(Requirement::INCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_TRUE(policy_.IsSupportedByDownload(name_space)) << "Namespace: "
+                                                           << name_space;
+  }
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(download_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireSupportedByDownload_Except) {
+  builder_.RequireSupportedByDownload(Requirement::EXCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_FALSE(policy_.IsSupportedByDownload(name_space)) << "Namespace: "
+                                                            << name_space;
+  }
+
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(download_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireShownAsRecentlyVisitedSite_Only) {
+  builder_.RequireShownAsRecentlyVisitedSite(Requirement::INCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_TRUE(policy_.IsShownAsRecentlyVisitedSite(name_space))
+        << "Namespace: " << name_space;
+  }
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(recent_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireShownAsRecentlyVisitedSite_Except) {
+  builder_.RequireShownAsRecentlyVisitedSite(Requirement::EXCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_FALSE(policy_.IsShownAsRecentlyVisitedSite(name_space))
+        << "Namespace: " << name_space;
+  }
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(recent_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireRestrictedToOriginalTab_Only) {
+  builder_.RequireRestrictedToOriginalTab(Requirement::INCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_TRUE(policy_.IsRestrictedToOriginalTab(name_space)) << "Namespace: "
+                                                               << name_space;
+  }
+  EXPECT_FALSE(query->Matches(kTestItem1));
+  EXPECT_TRUE(query->Matches(original_tab_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, RequireRestrictedToOriginalTab_Except) {
+  builder_.RequireRestrictedToOriginalTab(Requirement::EXCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  for (const std::string& name_space : namespaces_allowed) {
+    EXPECT_FALSE(policy_.IsRestrictedToOriginalTab(name_space)) << "Namespace: "
+                                                                << name_space;
+  }
+  EXPECT_TRUE(query->Matches(kTestItem1));
+  EXPECT_FALSE(query->Matches(original_tab_page()));
+}
+
+TEST_F(OfflinePageModelQueryTest, IntersectNamespaces) {
+  // This should exclude last N, but include |kTestNamespace|.
+  builder_.RequireRestrictedToOriginalTab(Requirement::INCLUDE_MATCHING)
+      .RequireShownAsRecentlyVisitedSite(Requirement::EXCLUDE_MATCHING);
+  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
+
+  auto restriction = query->GetRestrictedToNamespaces();
+  std::set<std::string> namespaces_allowed = restriction.second;
+  bool restricted_to_namespaces = restriction.first;
+  EXPECT_TRUE(restricted_to_namespaces);
+
+  EXPECT_TRUE(namespaces_allowed.count(kTestNamespace) == 1);
+  EXPECT_FALSE(query->Matches(recent_page()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_storage_manager.cc b/components/offline_pages/core/offline_page_storage_manager.cc
new file mode 100644
index 0000000..2b99f22
--- /dev/null
+++ b/components/offline_pages/core/offline_page_storage_manager.cc
@@ -0,0 +1,195 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_client_policy.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model.h"
+
+using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
+
+namespace offline_pages {
+
+constexpr double constants::kOfflinePageStorageLimit;
+constexpr double constants::kOfflinePageStorageClearThreshold;
+constexpr base::TimeDelta constants::kClearStorageInterval;
+constexpr base::TimeDelta constants::kRemovePageItemInterval;
+
+OfflinePageStorageManager::OfflinePageStorageManager(
+    OfflinePageModel* model,
+    ClientPolicyController* policy_controller,
+    ArchiveManager* archive_manager)
+    : model_(model),
+      policy_controller_(policy_controller),
+      archive_manager_(archive_manager),
+      clock_(new base::DefaultClock()),
+      weak_ptr_factory_(this) {}
+
+OfflinePageStorageManager::~OfflinePageStorageManager() {}
+
+void OfflinePageStorageManager::ClearPagesIfNeeded(
+    const ClearStorageCallback& callback) {
+  if (IsInProgress())
+    return;
+  clear_time_ = clock_->Now();
+  archive_manager_->GetStorageStats(base::Bind(
+      &OfflinePageStorageManager::OnGetStorageStatsDoneForClearingPages,
+      weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void OfflinePageStorageManager::SetClockForTesting(
+    std::unique_ptr<base::Clock> clock) {
+  clock_ = std::move(clock);
+}
+
+void OfflinePageStorageManager::OnGetStorageStatsDoneForClearingPages(
+    const ClearStorageCallback& callback,
+    const ArchiveManager::StorageStats& stats) {
+  DCHECK(IsInProgress());
+  ClearMode mode = ShouldClearPages(stats);
+  if (mode == ClearMode::NOT_NEEDED) {
+    last_clear_time_ = clear_time_;
+    callback.Run(0, ClearStorageResult::UNNECESSARY);
+    return;
+  }
+  model_->GetAllPages(
+      base::Bind(&OfflinePageStorageManager::OnGetAllPagesDoneForClearingPages,
+                 weak_ptr_factory_.GetWeakPtr(), callback, stats));
+}
+
+void OfflinePageStorageManager::OnGetAllPagesDoneForClearingPages(
+    const ClearStorageCallback& callback,
+    const ArchiveManager::StorageStats& stats,
+    const MultipleOfflinePageItemResult& pages) {
+  std::vector<int64_t> page_ids_to_clear;
+  GetPageIdsToClear(pages, stats, &page_ids_to_clear);
+  model_->DeletePagesByOfflineId(
+      page_ids_to_clear,
+      base::Bind(&OfflinePageStorageManager::OnExpiredPagesCleared,
+                 weak_ptr_factory_.GetWeakPtr(), callback,
+                 page_ids_to_clear.size()));
+}
+
+void OfflinePageStorageManager::OnExpiredPagesCleared(
+    const ClearStorageCallback& callback,
+    size_t pages_cleared,
+    DeletePageResult result) {
+  last_clear_time_ = clear_time_;
+  ClearStorageResult clear_result = ClearStorageResult::SUCCESS;
+  if (result != DeletePageResult::SUCCESS)
+    clear_result = ClearStorageResult::DELETE_FAILURE;
+  callback.Run(pages_cleared, clear_result);
+}
+
+void OfflinePageStorageManager::GetPageIdsToClear(
+    const MultipleOfflinePageItemResult& pages,
+    const ArchiveManager::StorageStats& stats,
+    std::vector<int64_t>* page_ids_to_clear) {
+  // TODO(romax): See how persistent should be considered here.
+  // Creating a map from namespace to a vector of page items.
+  // Sort each vector based on last accessed time and all pages after index
+  // min{size(), page_limit} should be deleted.
+  std::map<std::string, std::vector<OfflinePageItem>> pages_map;
+  std::vector<OfflinePageItem> kept_pages;
+  int64_t kept_pages_size = 0;
+
+  for (const auto& page : pages) {
+    if (!IsExpired(page))
+      pages_map[page.client_id.name_space].push_back(page);
+    else
+      page_ids_to_clear->push_back(page.offline_id);
+  }
+
+  for (auto& iter : pages_map) {
+    std::string name_space = iter.first;
+    std::vector<OfflinePageItem>& page_list = iter.second;
+
+    LifetimePolicy policy =
+        policy_controller_->GetPolicy(name_space).lifetime_policy;
+
+    std::sort(page_list.begin(), page_list.end(),
+              [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
+                return a.last_access_time > b.last_access_time;
+              });
+
+    size_t page_list_size = page_list.size();
+    size_t pos = 0;
+    while (pos < page_list_size &&
+           (policy.page_limit == kUnlimitedPages || pos < policy.page_limit) &&
+           !IsExpired(page_list.at(pos))) {
+      kept_pages_size += page_list.at(pos).file_size;
+      kept_pages.push_back(page_list.at(pos));
+      pos++;
+    }
+
+    for (; pos < page_list_size; pos++)
+      page_ids_to_clear->push_back(page_list.at(pos).offline_id);
+  }
+
+  // If we're still over the clear threshold, we're going to clear remaining
+  // pages from oldest last access time.
+  int64_t free_space = stats.free_disk_space;
+  int64_t total_size = stats.total_archives_size;
+  int64_t space_to_release =
+      kept_pages_size -
+      (total_size + free_space) * constants::kOfflinePageStorageClearThreshold;
+  if (space_to_release > 0) {
+    // Here we're sorting the |kept_pages| with oldest first.
+    std::sort(kept_pages.begin(), kept_pages.end(),
+              [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
+                return a.last_access_time < b.last_access_time;
+              });
+    size_t kept_pages_size = kept_pages.size();
+    size_t pos = 0;
+    while (pos < kept_pages_size && space_to_release > 0) {
+      space_to_release -= kept_pages.at(pos).file_size;
+      page_ids_to_clear->push_back(kept_pages.at(pos).offline_id);
+      pos++;
+    }
+  }
+}
+
+OfflinePageStorageManager::ClearMode
+OfflinePageStorageManager::ShouldClearPages(
+    const ArchiveManager::StorageStats& storage_stats) {
+  int64_t total_size = storage_stats.total_archives_size;
+  int64_t free_space = storage_stats.free_disk_space;
+  if (total_size == 0)
+    return ClearMode::NOT_NEEDED;
+
+  // If the size of all offline pages is more than limit, or it's larger than a
+  // specified percentage of all available storage space on the disk we'll clear
+  // all offline pages.
+  if (total_size >=
+      (total_size + free_space) * constants::kOfflinePageStorageLimit)
+    return ClearMode::DEFAULT;
+  // If it's been more than the pre-defined interval since the last time we
+  // clear the storage, we should clear pages.
+  if (last_clear_time_ == base::Time() ||
+      clear_time_ - last_clear_time_ >= constants::kClearStorageInterval) {
+    return ClearMode::DEFAULT;
+  }
+  // Otherwise there's no need to clear storage right now.
+  return ClearMode::NOT_NEEDED;
+}
+
+bool OfflinePageStorageManager::IsExpired(const OfflinePageItem& page) const {
+  const LifetimePolicy& policy =
+      policy_controller_->GetPolicy(page.client_id.name_space).lifetime_policy;
+  return policy.lifetime_type == LifetimeType::TEMPORARY &&
+         clear_time_ - page.last_access_time >= policy.expiration_period;
+}
+
+bool OfflinePageStorageManager::IsInProgress() const {
+  return clear_time_ > last_clear_time_;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_storage_manager.h b/components/offline_pages/core/offline_page_storage_manager.h
new file mode 100644
index 0000000..7b206c7
--- /dev/null
+++ b/components/offline_pages/core/offline_page_storage_manager.h
@@ -0,0 +1,156 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/offline_page_types.h"
+
+namespace base {
+class Clock;
+}  // namespace base
+
+namespace offline_pages {
+
+// Maximum % of total available storage that will be occupied by offline pages
+// before a storage clearup.
+struct constants {
+  static constexpr double kOfflinePageStorageLimit = 0.3;
+  // The target % of storage usage we try to reach below when expiring pages.
+  static constexpr double kOfflinePageStorageClearThreshold = 0.1;
+  // The time that the storage cleanup will be triggered again since the last
+  // one.
+  static constexpr base::TimeDelta kClearStorageInterval =
+      base::TimeDelta::FromMinutes(10);
+  // The time that the page record will be removed from the store since the page
+  // has been expired.
+  static constexpr base::TimeDelta kRemovePageItemInterval =
+      base::TimeDelta::FromDays(21);
+};
+
+class ClientPolicyController;
+class OfflinePageModel;
+
+// This class is used for storage management of offline pages. It provides
+// a ClearPagesIfNeeded method which is used to clear outdated offline pages
+// based on last_access_time and lifetime policy of its namespace.
+// It has its own throttle mechanism so calling the method would not be
+// guaranteed to clear the pages immediately.
+//
+// OfflinePageModel should own and control the lifecycle of this manager.
+// And this manager would use OfflinePageModel to get/remove pages.
+class OfflinePageStorageManager {
+ public:
+  enum class ClearStorageResult {
+    SUCCESS,                                // Cleared successfully.
+    UNNECESSARY,                            // No expired pages.
+    DEPRECATED_EXPIRE_FAILURE,              // Expiration failed. (DEPRECATED)
+    DELETE_FAILURE,                         // Deletion failed.
+    DEPRECATED_EXPIRE_AND_DELETE_FAILURES,  // Both expiration and deletion
+                                            // failed. (DEPRECATED)
+    // NOTE: always keep this entry at the end. Add new result types only
+    // immediately above this line. Make sure to update the corresponding
+    // histogram enum accordingly.
+    RESULT_COUNT,
+  };
+
+  // Callback used when calling ClearPagesIfNeeded.
+  // size_t: the number of cleared pages.
+  // ClearStorageResult: result of clearing pages in storage.
+  typedef base::Callback<void(size_t, ClearStorageResult)> ClearStorageCallback;
+
+  explicit OfflinePageStorageManager(OfflinePageModel* model,
+                                     ClientPolicyController* policy_controller,
+                                     ArchiveManager* archive_manager);
+
+  ~OfflinePageStorageManager();
+
+  // The manager would *try* to clear pages when called. It may not delete any
+  // pages (if clearing condition wasn't satisfied).
+  // It clears the storage (expire pages) when it's using more disk space than a
+  // certain limit, or the time elapsed from last time clearing is longer than a
+  // certain interval. Both values are defined above.
+  void ClearPagesIfNeeded(const ClearStorageCallback& callback);
+
+  // Sets the clock for testing.
+  void SetClockForTesting(std::unique_ptr<base::Clock> clock);
+
+ private:
+  // Enum indicating how to clear the pages.
+  enum class ClearMode {
+    // Using normal expiration logic to clear pages. Will reduce the storage
+    // usage down below the threshold.
+    DEFAULT,
+    // No need to clear any page (no pages in the model or no expired pages and
+    // we're not exceeding the storage limit.)
+    NOT_NEEDED,
+  };
+
+  // Callback called after getting storage stats from archive manager.
+  void OnGetStorageStatsDoneForClearingPages(
+      const ClearStorageCallback& callback,
+      const ArchiveManager::StorageStats& pages);
+
+  // Callback called after getting all pages from model.
+  void OnGetAllPagesDoneForClearingPages(
+      const ClearStorageCallback& callback,
+      const ArchiveManager::StorageStats& storage_stats,
+      const MultipleOfflinePageItemResult& pages);
+
+  // Callback called after clearing expired pages from model.
+  void OnExpiredPagesCleared(const ClearStorageCallback& callback,
+                             size_t pages_cleared,
+                             DeletePageResult result);
+
+  // Gets offline IDs of pages that should be cleared based on current |stats|
+  // and return the IDs in |page_ids_to_clear|.
+  void GetPageIdsToClear(const MultipleOfflinePageItemResult& pages,
+                         const ArchiveManager::StorageStats& stats,
+                         std::vector<int64_t>* page_ids_to_clear);
+
+  // Determines if manager should clear pages.
+  ClearMode ShouldClearPages(const ArchiveManager::StorageStats& storage_stats);
+
+  // Returns true if |page| is should be cleared based on |clear_time_|.
+  bool IsExpired(const OfflinePageItem& page) const;
+
+  // Returns true if we're currently doing a cleanup.
+  bool IsInProgress() const;
+
+  // Not owned.
+  OfflinePageModel* model_;
+
+  // Not owned.
+  ClientPolicyController* policy_controller_;
+
+  // Not owned.
+  ArchiveManager* archive_manager_;
+
+  // Starting time of the current storage cleanup. If this time is later than
+  // |last_clear_time_| it means we're doing a cleanup.
+  base::Time clear_time_;
+
+  // Timestamp of last storage cleanup.
+  base::Time last_clear_time_;
+
+  // Clock for getting time.
+  std::unique_ptr<base::Clock> clock_;
+
+  base::WeakPtrFactory<OfflinePageStorageManager> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageStorageManager);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_STORAGE_MANAGER_H_
diff --git a/components/offline_pages/core/offline_page_storage_manager_unittest.cc b/components/offline_pages/core/offline_page_storage_manager_unittest.cc
new file mode 100644
index 0000000..bc613a0
--- /dev/null
+++ b/components/offline_pages/core/offline_page_storage_manager_unittest.cc
@@ -0,0 +1,394 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_storage_manager.h"
+
+#include <stdint.h>
+#include <map>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/offline_pages/core/archive_manager.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_model_impl.h"
+#include "components/offline_pages/core/offline_page_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using LifetimePolicy = offline_pages::LifetimePolicy;
+using ClearStorageResult =
+    offline_pages::OfflinePageStorageManager::ClearStorageResult;
+using StorageStats = offline_pages::ArchiveManager::StorageStats;
+
+namespace offline_pages {
+
+namespace {
+const GURL kTestUrl("http://example.com");
+const base::FilePath::CharType kFilePath[] = FILE_PATH_LITERAL("/data");
+const int64_t kTestFileSize = 1 << 19;  // Make a page 512KB.
+const int64_t kFreeSpaceNormal = 100 * (1 << 20);
+
+enum TestOptions {
+  DEFAULT = 1 << 0,
+  DELETE_FAILURE = 1 << 1,
+};
+
+struct PageSettings {
+  std::string name_space;
+  int fresh_pages_count;
+  int expired_pages_count;
+};
+}  // namespace
+
+class OfflinePageTestModel : public OfflinePageModelImpl {
+ public:
+  OfflinePageTestModel(std::vector<PageSettings> page_settings,
+                       base::SimpleTestClock* clock,
+                       TestOptions options = TestOptions::DEFAULT)
+      : policy_controller_(new ClientPolicyController()),
+        clock_(clock),
+        options_(options),
+        next_offline_id_(0) {
+    for (const auto& setting : page_settings)
+      AddPages(setting);
+  }
+
+  ~OfflinePageTestModel() override;
+
+  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override {
+    MultipleOfflinePageItemResult pages;
+    for (const auto& id_page_pair : pages_)
+      pages.push_back(id_page_pair.second);
+    callback.Run(pages);
+  }
+
+  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
+                              const DeletePageCallback& callback) override;
+
+  void AddPages(const PageSettings& setting);
+
+  // The |removed_pages_| would not be cleared in during a test, so the number
+  // of removed pages will be accumulative in a single test case.
+  const std::vector<OfflinePageItem>& GetRemovedPages() {
+    return removed_pages_;
+  }
+
+  int64_t GetTotalSize() const;
+
+  base::SimpleTestClock* clock() { return clock_; }
+
+ private:
+  std::map<int64_t, OfflinePageItem> pages_;
+
+  std::vector<OfflinePageItem> removed_pages_;
+
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+
+  base::SimpleTestClock* clock_;
+
+  TestOptions options_;
+
+  int64_t next_offline_id_;
+};
+
+void OfflinePageTestModel::DeletePagesByOfflineId(
+    const std::vector<int64_t>& offline_ids,
+    const DeletePageCallback& callback) {
+  if (options_ & TestOptions::DELETE_FAILURE) {
+    callback.Run(DeletePageResult::STORE_FAILURE);
+    return;
+  }
+  for (const auto id : offline_ids) {
+    removed_pages_.push_back(pages_.at(id));
+    pages_.erase(id);
+  }
+  callback.Run(DeletePageResult::SUCCESS);
+}
+
+int64_t OfflinePageTestModel::GetTotalSize() const {
+  int64_t res = 0;
+  for (const auto& id_page_pair : pages_)
+    res += id_page_pair.second.file_size;
+  return res;
+}
+
+OfflinePageTestModel::~OfflinePageTestModel() {}
+
+void OfflinePageTestModel::AddPages(const PageSettings& setting) {
+  std::string name_space = setting.name_space;
+  int fresh_pages_count = setting.fresh_pages_count;
+  int expired_pages_count = setting.expired_pages_count;
+  base::Time now = clock()->Now();
+  // Fresh pages.
+  for (int i = 0; i < fresh_pages_count; i++) {
+    OfflinePageItem page =
+        OfflinePageItem(kTestUrl, next_offline_id_,
+                        ClientId(name_space, std::to_string(next_offline_id_)),
+                        base::FilePath(kFilePath), kTestFileSize);
+    page.last_access_time = now;
+    pages_[next_offline_id_] = page;
+    next_offline_id_++;
+  }
+  // Expired pages.
+  for (int i = 0; i < expired_pages_count; i++) {
+    OfflinePageItem page =
+        OfflinePageItem(kTestUrl, next_offline_id_,
+                        ClientId(name_space, std::to_string(next_offline_id_)),
+                        base::FilePath(kFilePath), kTestFileSize);
+    page.last_access_time = now -
+                            policy_controller_->GetPolicy(name_space)
+                                .lifetime_policy.expiration_period;
+    pages_[next_offline_id_] = page;
+    next_offline_id_++;
+  }
+}
+
+class TestArchiveManager : public ArchiveManager {
+ public:
+  explicit TestArchiveManager(StorageStats stats) : stats_(stats) {}
+
+  void GetStorageStats(const base::Callback<
+                       void(const ArchiveManager::StorageStats& storage_stats)>&
+                           callback) const override {
+    callback.Run(stats_);
+  }
+
+  void SetValues(ArchiveManager::StorageStats stats) { stats_ = stats; }
+
+ private:
+  StorageStats stats_;
+};
+
+class OfflinePageStorageManagerTest : public testing::Test {
+ public:
+  OfflinePageStorageManagerTest();
+  OfflinePageStorageManager* manager() { return manager_.get(); }
+  OfflinePageTestModel* model() { return model_.get(); }
+  ClientPolicyController* policy_controller() {
+    return policy_controller_.get();
+  }
+  TestArchiveManager* test_archive_manager() { return archive_manager_.get(); }
+  void OnPagesCleared(size_t pages_cleared_count, ClearStorageResult result);
+  void Initialize(const std::vector<PageSettings>& settings,
+                  StorageStats stats = {kFreeSpaceNormal, 0},
+                  TestOptions options = TestOptions::DEFAULT);
+  void TryClearPages();
+
+  // testing::Test
+  void TearDown() override;
+
+  base::SimpleTestClock* clock() { return clock_; }
+  int last_cleared_page_count() const {
+    return static_cast<int>(last_cleared_page_count_);
+  }
+  int total_cleared_times() const { return total_cleared_times_; }
+  ClearStorageResult last_clear_storage_result() const {
+    return last_clear_storage_result_;
+  }
+
+ private:
+  std::unique_ptr<OfflinePageStorageManager> manager_;
+  std::unique_ptr<OfflinePageTestModel> model_;
+  std::unique_ptr<ClientPolicyController> policy_controller_;
+  std::unique_ptr<TestArchiveManager> archive_manager_;
+
+  base::SimpleTestClock* clock_;
+
+  size_t last_cleared_page_count_;
+  int total_cleared_times_;
+  ClearStorageResult last_clear_storage_result_;
+};
+
+OfflinePageStorageManagerTest::OfflinePageStorageManagerTest()
+    : policy_controller_(new ClientPolicyController()),
+      last_cleared_page_count_(0),
+      total_cleared_times_(0),
+      last_clear_storage_result_(ClearStorageResult::SUCCESS) {}
+
+void OfflinePageStorageManagerTest::Initialize(
+    const std::vector<PageSettings>& page_settings,
+    StorageStats stats,
+    TestOptions options) {
+  std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
+  clock_ = clock.get();
+  clock_->SetNow(base::Time::Now());
+  model_.reset(new OfflinePageTestModel(page_settings, clock_, options));
+
+  if (stats.free_disk_space == 0)
+    stats.free_disk_space = kFreeSpaceNormal;
+  if (stats.total_archives_size == 0) {
+    stats.total_archives_size = model_->GetTotalSize();
+  }
+  archive_manager_.reset(new TestArchiveManager(stats));
+  manager_.reset(new OfflinePageStorageManager(
+      model_.get(), policy_controller(), archive_manager_.get()));
+  manager_->SetClockForTesting(std::move(clock));
+}
+
+void OfflinePageStorageManagerTest::TryClearPages() {
+  manager()->ClearPagesIfNeeded(base::Bind(
+      &OfflinePageStorageManagerTest::OnPagesCleared, base::Unretained(this)));
+}
+
+void OfflinePageStorageManagerTest::TearDown() {
+  manager_.reset();
+  model_.reset();
+}
+
+void OfflinePageStorageManagerTest::OnPagesCleared(size_t pages_cleared_count,
+                                                   ClearStorageResult result) {
+  last_cleared_page_count_ = pages_cleared_count;
+  total_cleared_times_++;
+  last_clear_storage_result_ = result;
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestClearPagesLessThanLimit) {
+  Initialize(std::vector<PageSettings>(
+      {{kBookmarkNamespace, 1, 1}, {kLastNNamespace, 1, 1}}));
+  clock()->Advance(base::TimeDelta::FromMinutes(30));
+  TryClearPages();
+  EXPECT_EQ(2, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(2, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreThanLimit) {
+  Initialize(std::vector<PageSettings>(
+      {{kBookmarkNamespace, 10, 15}, {kLastNNamespace, 5, 30}}));
+  clock()->Advance(base::TimeDelta::FromMinutes(30));
+  TryClearPages();
+  EXPECT_EQ(45, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(45, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreFreshPages) {
+  Initialize(std::vector<PageSettings>(
+                 {{kBookmarkNamespace, 30, 0}, {kLastNNamespace, 100, 1}}),
+             {1000 * (1 << 20), 0});
+  clock()->Advance(base::TimeDelta::FromMinutes(30));
+  TryClearPages();
+  EXPECT_EQ(1, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestDeleteAsyncPages) {
+  Initialize(std::vector<PageSettings>({{kAsyncNamespace, 20, 0}}));
+  clock()->Advance(base::TimeDelta::FromDays(367));
+  TryClearPages();
+  EXPECT_EQ(0, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestDeletionFailed) {
+  Initialize(std::vector<PageSettings>(
+                 {{kBookmarkNamespace, 10, 10}, {kLastNNamespace, 10, 10}}),
+             {kFreeSpaceNormal, 0}, TestOptions::DELETE_FAILURE);
+  TryClearPages();
+  EXPECT_EQ(20, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::DELETE_FAILURE, last_clear_storage_result());
+  EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
+  Initialize(std::vector<PageSettings>(
+      {{kBookmarkNamespace, 10, 10}, {kLastNNamespace, 10, 10}}));
+  clock()->Advance(base::TimeDelta::FromMinutes(30));
+  TryClearPages();
+  EXPECT_EQ(20, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
+
+  // Advance clock so we go over the gap, but no expired pages.
+  clock()->Advance(constants::kClearStorageInterval +
+                   base::TimeDelta::FromMinutes(1));
+  TryClearPages();
+  EXPECT_EQ(0, last_cleared_page_count());
+  EXPECT_EQ(2, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
+
+  // Advance clock so we are still in the gap, should be unnecessary.
+  clock()->Advance(constants::kClearStorageInterval -
+                   base::TimeDelta::FromMinutes(1));
+  TryClearPages();
+  EXPECT_EQ(0, last_cleared_page_count());
+  EXPECT_EQ(3, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
+  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
+  Initialize(std::vector<PageSettings>({{kBookmarkNamespace, 30, 0},
+                                        {kLastNNamespace, 100, 1},
+                                        {kAsyncNamespace, 40, 0}}),
+             {1000 * (1 << 20), 0});
+  clock()->Advance(base::TimeDelta::FromMinutes(30));
+  LifetimePolicy policy =
+      policy_controller()->GetPolicy(kLastNNamespace).lifetime_policy;
+
+  TryClearPages();
+  EXPECT_EQ(1, last_cleared_page_count());
+  EXPECT_EQ(1, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
+
+  // Advance the clock by expiration period of last_n namespace, should be
+  // expiring all pages left in the namespace.
+  clock()->Advance(policy.expiration_period);
+  TryClearPages();
+  EXPECT_EQ(100, last_cleared_page_count());
+  EXPECT_EQ(2, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
+
+  // Only 1 ms passes and no changes in pages, so no need to clear page.
+  clock()->Advance(base::TimeDelta::FromMilliseconds(1));
+  TryClearPages();
+  EXPECT_EQ(0, last_cleared_page_count());
+  EXPECT_EQ(3, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
+  EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
+
+  // Adding more fresh pages to make it go over limit.
+  clock()->Advance(base::TimeDelta::FromMinutes(5));
+  model()->AddPages({kBookmarkNamespace, 400, 0});
+  int64_t total_size_before = model()->GetTotalSize();
+  int64_t available_space = 300 * (1 << 20);  // 300 MB
+  test_archive_manager()->SetValues({available_space, total_size_before});
+  EXPECT_GE(total_size_before, constants::kOfflinePageStorageLimit *
+                                   (available_space + total_size_before));
+  TryClearPages();
+  EXPECT_LE(total_size_before * constants::kOfflinePageStorageClearThreshold,
+            model()->GetTotalSize());
+  EXPECT_EQ(4, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  int deleted_page_total = last_cleared_page_count() + 101;
+  EXPECT_EQ(deleted_page_total,
+            static_cast<int>(model()->GetRemovedPages().size()));
+
+  // After more days, all pages should be deleted.
+  clock()->Advance(base::TimeDelta::FromDays(30));
+  TryClearPages();
+  EXPECT_EQ(0, model()->GetTotalSize());
+  EXPECT_EQ(5, total_cleared_times());
+  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
+  // Number of removed pages should be all the pages initially created plus 400
+  // more pages added above for bookmark namespace.
+  EXPECT_EQ(171 + 400, static_cast<int>(model()->GetRemovedPages().size()));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_test_archiver.cc b/components/offline_pages/core/offline_page_test_archiver.cc
new file mode 100644
index 0000000..7673595
--- /dev/null
+++ b/components/offline_pages/core/offline_page_test_archiver.cc
@@ -0,0 +1,62 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_test_archiver.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+OfflinePageTestArchiver::OfflinePageTestArchiver(
+    Observer* observer,
+    const GURL& url,
+    ArchiverResult result,
+    const base::string16& result_title,
+    int64_t size_to_report,
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+    : observer_(observer),
+      url_(url),
+      result_(result),
+      size_to_report_(size_to_report),
+      create_archive_called_(false),
+      delayed_(false),
+      result_title_(result_title),
+      task_runner_(task_runner) {}
+
+OfflinePageTestArchiver::~OfflinePageTestArchiver() {
+  EXPECT_TRUE(create_archive_called_);
+}
+
+void OfflinePageTestArchiver::CreateArchive(
+    const base::FilePath& archives_dir,
+    int64_t archive_id,
+    const CreateArchiveCallback& callback) {
+  create_archive_called_ = true;
+  callback_ = callback;
+  archives_dir_ = archives_dir;
+  if (!delayed_)
+    CompleteCreateArchive();
+}
+
+void OfflinePageTestArchiver::CompleteCreateArchive() {
+  DCHECK(!callback_.is_null());
+  base::FilePath archive_path;
+  if (filename_.empty()) {
+    ASSERT_TRUE(base::CreateTemporaryFileInDir(archives_dir_, &archive_path));
+  } else {
+    archive_path = archives_dir_.Append(filename_);
+    // This step ensures the file is created and closed immediately.
+    base::File file(archive_path, base::File::FLAG_OPEN_ALWAYS);
+  }
+  observer_->SetLastPathCreatedByArchiver(archive_path);
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(callback_, this, result_, url_, archive_path,
+                            result_title_, size_to_report_));
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_test_archiver.h b/components/offline_pages/core/offline_page_test_archiver.h
new file mode 100644
index 0000000..e4ce08fe
--- /dev/null
+++ b/components/offline_pages/core/offline_page_test_archiver.h
@@ -0,0 +1,86 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string16.h"
+#include "components/offline_pages/core/offline_page_archiver.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+}  // namespace
+
+namespace offline_pages {
+
+// A test archiver class, which allows for testing offline pages without a need
+// for an actual web contents.
+class OfflinePageTestArchiver : public OfflinePageArchiver {
+ public:
+  // TODO(fgorski): Try refactoring the observer out and replace it with a
+  // callback, or completely remove the call to |SetLastPathCreatedByArchiver|.
+  class Observer {
+   public:
+    virtual ~Observer() {}
+    virtual void SetLastPathCreatedByArchiver(
+        const base::FilePath& file_path) = 0;
+  };
+
+  OfflinePageTestArchiver(
+      Observer* observer,
+      const GURL& url,
+      ArchiverResult result,
+      const base::string16& result_title,
+      int64_t size_to_report,
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+  ~OfflinePageTestArchiver() override;
+
+  // OfflinePageArchiver implementation:
+  void CreateArchive(const base::FilePath& archives_dir,
+                     int64_t archive_id,
+                     const CreateArchiveCallback& callback) override;
+
+  // Completes the creation of archive. Should be used with |set_delayed| set to
+  // true.
+  void CompleteCreateArchive();
+
+  // When set to true, |CompleteCreateArchive| should be called explicitly for
+  // the process to finish.
+  // TODO(fgorski): See if we can move this to the constructor.
+  void set_delayed(bool delayed) { delayed_ = delayed; }
+
+  // Allows to explicitly specify a file name for the tests.
+  // TODO(fgorski): See if we can move this to the constructor.
+  void set_filename(const base::FilePath& filename) { filename_ = filename; }
+
+  bool create_archive_called() const { return create_archive_called_; }
+
+ private:
+  // Not owned. Outlives OfflinePageTestArchiver.
+  Observer* observer_;
+  GURL url_;
+  base::FilePath archives_dir_;
+  base::FilePath filename_;
+  ArchiverResult result_;
+  int64_t size_to_report_;
+  bool create_archive_called_;
+  bool delayed_;
+  base::string16 result_title_;
+  CreateArchiveCallback callback_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(OfflinePageTestArchiver);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_ARCHIVER_H_
diff --git a/components/offline_pages/core/offline_page_test_store.cc b/components/offline_pages/core/offline_page_test_store.cc
new file mode 100644
index 0000000..556c12a
--- /dev/null
+++ b/components/offline_pages/core/offline_page_test_store.cc
@@ -0,0 +1,160 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/offline_page_test_store.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+OfflinePageTestStore::OfflinePageTestStore(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+    : task_runner_(task_runner),
+      scenario_(TestScenario::SUCCESSFUL),
+      store_state_(StoreState::NOT_LOADED) {}
+
+OfflinePageTestStore::OfflinePageTestStore(
+    const OfflinePageTestStore& other_store)
+    : task_runner_(other_store.task_runner_),
+      scenario_(other_store.scenario_),
+      offline_pages_(other_store.offline_pages_) {}
+
+OfflinePageTestStore::~OfflinePageTestStore() {}
+
+void OfflinePageTestStore::Initialize(const InitializeCallback& callback) {
+  if (scenario_ == TestScenario::LOAD_FAILED_RESET_FAILED ||
+      scenario_ == TestScenario::LOAD_FAILED_RESET_SUCCESS) {
+    store_state_ = StoreState::FAILED_LOADING;
+    offline_pages_.clear();
+  } else {
+    store_state_ = StoreState::LOADED;
+  }
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(callback, store_state_ == StoreState::LOADED));
+}
+
+void OfflinePageTestStore::GetOfflinePages(const LoadCallback& callback) {
+  task_runner_->PostTask(FROM_HERE, base::Bind(callback, GetAllPages()));
+}
+
+void OfflinePageTestStore::AddOfflinePage(const OfflinePageItem& offline_page,
+                                          const AddCallback& callback) {
+  // TODO(fgorski): Add and cover scenario with existing item.
+  ItemActionStatus result;
+  if (store_state_ == StoreState::LOADED &&
+      scenario_ != TestScenario::WRITE_FAILED) {
+    offline_pages_[offline_page.offline_id] = offline_page;
+    last_saved_page_ = offline_page;
+    result = ItemActionStatus::SUCCESS;
+  } else {
+    result = ItemActionStatus::STORE_ERROR;
+  }
+  if (!callback.is_null())
+    task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+void OfflinePageTestStore::UpdateOfflinePages(
+    const std::vector<OfflinePageItem>& pages,
+    const UpdateCallback& callback) {
+  // TODO(fgorski): Cover scenario where new items are being created while they
+  // shouldn't.
+  std::unique_ptr<OfflinePagesUpdateResult> result(
+      new OfflinePagesUpdateResult(StoreState::LOADED));
+  if (scenario_ == TestScenario::WRITE_FAILED) {
+    for (const auto& page : pages) {
+      result->item_statuses.push_back(
+          std::make_pair(page.offline_id, ItemActionStatus::STORE_ERROR));
+    }
+  } else {
+    for (const auto& page : pages) {
+      offline_pages_[page.offline_id] = page;
+      last_saved_page_ = page;
+      result->item_statuses.push_back(
+          std::make_pair(page.offline_id, ItemActionStatus::SUCCESS));
+    }
+    result->updated_items.insert(result->updated_items.begin(), pages.begin(),
+                                 pages.end());
+  }
+  if (!callback.is_null())
+    task_runner_->PostTask(FROM_HERE,
+                           base::Bind(callback, base::Passed(&result)));
+}
+
+void OfflinePageTestStore::RemoveOfflinePages(
+    const std::vector<int64_t>& offline_ids,
+    const UpdateCallback& callback) {
+  std::unique_ptr<OfflinePagesUpdateResult> result(
+      new OfflinePagesUpdateResult(StoreState::LOADED));
+
+  ASSERT_FALSE(offline_ids.empty());
+  if (scenario_ == TestScenario::REMOVE_FAILED) {
+    for (const auto& offline_id : offline_ids) {
+      result->item_statuses.push_back(
+          std::make_pair(offline_id, ItemActionStatus::STORE_ERROR));
+    }
+    // Anything different that LOADED is good here.
+    result->store_state = StoreState::FAILED_LOADING;
+  } else {
+    for (const auto& offline_id : offline_ids) {
+      auto iter = offline_pages_.find(offline_id);
+      ItemActionStatus status;
+      if (iter != offline_pages_.end()) {
+        result->updated_items.push_back(iter->second);
+        status = ItemActionStatus::SUCCESS;
+        offline_pages_.erase(iter);
+      } else {
+        status = ItemActionStatus::NOT_FOUND;
+      }
+      result->item_statuses.push_back(std::make_pair(offline_id, status));
+    }
+  }
+
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(callback, base::Passed(&result)));
+}
+
+void OfflinePageTestStore::Reset(const ResetCallback& callback) {
+  if (scenario_ == TestScenario::LOAD_FAILED_RESET_FAILED) {
+    store_state_ = StoreState::FAILED_RESET;
+  } else {
+    store_state_ = StoreState::NOT_LOADED;
+    // Scenario is flipped to successful here, as the reset succeeds.
+    if (scenario_ == TestScenario::LOAD_FAILED_RESET_SUCCESS)
+      scenario_ = TestScenario::SUCCESSFUL;
+  }
+
+  offline_pages_.clear();
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(callback, store_state_ == StoreState::NOT_LOADED));
+}
+
+StoreState OfflinePageTestStore::state() const {
+  return store_state_;
+}
+
+void OfflinePageTestStore::UpdateLastAccessTime(
+    int64_t offline_id,
+    const base::Time& last_access_time) {
+  auto iter = offline_pages_.find(offline_id);
+  if (iter == offline_pages_.end())
+    return;
+  iter->second.last_access_time = last_access_time;
+}
+
+std::vector<OfflinePageItem> OfflinePageTestStore::GetAllPages() const {
+  std::vector<OfflinePageItem> offline_pages;
+  for (const auto& id_page_pair : offline_pages_)
+    offline_pages.push_back(id_page_pair.second);
+  return offline_pages;
+}
+
+void OfflinePageTestStore::ClearAllPages() {
+  offline_pages_.clear();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/offline_page_test_store.h b/components/offline_pages/core/offline_page_test_store.h
new file mode 100644
index 0000000..7b7599d8
--- /dev/null
+++ b/components/offline_pages/core/offline_page_test_store.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "components/offline_pages/core/offline_page_item.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
+
+namespace offline_pages {
+
+// Offline page store to be used for testing purposes. It stores offline pages
+// in memory. All callbacks are posted immediately through a provided
+// |task_runner|.
+class OfflinePageTestStore : public OfflinePageMetadataStore {
+ public:
+  enum class TestScenario {
+    SUCCESSFUL,
+    WRITE_FAILED,
+    LOAD_FAILED_RESET_SUCCESS,
+    LOAD_FAILED_RESET_FAILED,
+    REMOVE_FAILED,
+  };
+
+  explicit OfflinePageTestStore(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+  explicit OfflinePageTestStore(const OfflinePageTestStore& other_store);
+  ~OfflinePageTestStore() override;
+
+  // OfflinePageMetadataStore overrides:
+  void Initialize(const InitializeCallback& callback) override;
+  void GetOfflinePages(const LoadCallback& callback) override;
+  void AddOfflinePage(const OfflinePageItem& offline_page,
+                      const AddCallback& callback) override;
+  void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
+                          const UpdateCallback& callback) override;
+  void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
+                          const UpdateCallback& callback) override;
+  void Reset(const ResetCallback& callback) override;
+  StoreState state() const override;
+
+  void UpdateLastAccessTime(int64_t offline_id,
+                            const base::Time& last_access_time);
+
+  // Returns all pages, regardless their states.
+  std::vector<OfflinePageItem> GetAllPages() const;
+
+  // Clear all pages in the store.
+  void ClearAllPages();
+
+  const OfflinePageItem& last_saved_page() const { return last_saved_page_; }
+
+  void set_test_scenario(TestScenario scenario) { scenario_ = scenario; };
+
+ private:
+  OfflinePageItem last_saved_page_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  TestScenario scenario_;
+  StoreState store_state_;
+
+  std::map<int64_t, OfflinePageItem> offline_pages_;
+
+  DISALLOW_ASSIGN(OfflinePageTestStore);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TEST_STORE_H_
diff --git a/components/offline_pages/core/offline_page_types.h b/components/offline_pages/core/offline_page_types.h
new file mode 100644
index 0000000..ae9e298a
--- /dev/null
+++ b/components/offline_pages/core/offline_page_types.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <vector>
+
+#include "base/callback.h"
+#include "components/offline_pages/core/offline_page_item.h"
+
+class GURL;
+
+// This file contains common callbacks used by OfflinePageModel and is a
+// temporary step to refactor and interface of the model out of the
+// implementation.
+namespace offline_pages {
+// Result of saving a page offline.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
+enum class SavePageResult {
+  SUCCESS,
+  CANCELLED,
+  DEVICE_FULL,
+  CONTENT_UNAVAILABLE,
+  ARCHIVE_CREATION_FAILED,
+  STORE_FAILURE,
+  ALREADY_EXISTS,
+  // Certain pages, i.e. file URL or NTP, will not be saved because these
+  // are already locally accessible.
+  SKIPPED,
+  SECURITY_CERTIFICATE_ERROR,
+  // NOTE: always keep this entry at the end. Add new result types only
+  // immediately above this line. Make sure to update the corresponding
+  // histogram enum accordingly.
+  RESULT_COUNT,
+};
+
+// Result of deleting an offline page.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
+enum class DeletePageResult {
+  SUCCESS,
+  CANCELLED,
+  STORE_FAILURE,
+  DEVICE_FAILURE,
+  // Deprecated. Deleting pages which are not in metadata store would be
+  // returing |SUCCESS|. Should not be used anymore.
+  NOT_FOUND,
+  // NOTE: always keep this entry at the end. Add new result types only
+  // immediately above this line. Make sure to update the corresponding
+  // histogram enum accordingly.
+  RESULT_COUNT,
+};
+
+typedef std::set<GURL> CheckPagesExistOfflineResult;
+typedef std::vector<int64_t> MultipleOfflineIdResult;
+typedef std::vector<OfflinePageItem> MultipleOfflinePageItemResult;
+
+typedef base::Callback<void(SavePageResult, int64_t)> SavePageCallback;
+typedef base::Callback<void(DeletePageResult)> DeletePageCallback;
+typedef base::Callback<void(const CheckPagesExistOfflineResult&)>
+    CheckPagesExistOfflineCallback;
+typedef base::Callback<void(bool)> HasPagesCallback;
+typedef base::Callback<void(const MultipleOfflineIdResult&)>
+    MultipleOfflineIdCallback;
+typedef base::Callback<void(const OfflinePageItem*)>
+    SingleOfflinePageItemCallback;
+typedef base::Callback<void(const MultipleOfflinePageItemResult&)>
+    MultipleOfflinePageItemCallback;
+typedef base::Callback<bool(const GURL&)> UrlPredicate;
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_TYPES_H_
diff --git a/components/offline_pages/core/offline_store_types.h b/components/offline_pages/core/offline_store_types.h
new file mode 100644
index 0000000..77d6a6a
--- /dev/null
+++ b/components/offline_pages/core/offline_store_types.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+// This file contains common types and callbacks used by storage of various
+// offline page related components.
+namespace offline_pages {
+
+// TODO(fgorski): This enum is meant to replace |LoadStatus|.
+// Current store state. When LOADED, the store is operational. When
+// loading or reset fails, it is reflected appropriately.
+enum class StoreState {
+  NOT_LOADED,      // Store is not loaded yet.
+  LOADED,          // Store is properly loaded and operational.
+  FAILED_LOADING,  // Store initialization failed.
+  FAILED_RESET,    // Resetting the store failed.
+};
+
+// Statuses referring to actions taken on items in the stores.
+// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages
+enum class ItemActionStatus {
+  SUCCESS,
+  ALREADY_EXISTS,
+  NOT_FOUND,
+  STORE_ERROR,
+};
+
+// List of item action statuses mapped to item ID.
+typedef std::vector<std::pair<int64_t, ItemActionStatus>> MultipleItemStatuses;
+
+// Collective result for store update.
+template <typename T>
+class StoreUpdateResult {
+ public:
+  explicit StoreUpdateResult(StoreState state) : store_state(state) {}
+  ~StoreUpdateResult() {}
+
+  // List of Offline ID to item action status mappings.
+  // It is meant to be consumed by the original caller of the operation.
+  MultipleItemStatuses item_statuses;
+
+  // List of successfully updated offline page items as seen after operation
+  // concludes. It is meant to be used when passing to the observers.
+  std::vector<T> updated_items;
+
+  // State of the store after the operation is done.
+  StoreState store_state;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_STORE_TYPES_H_
diff --git a/components/offline_pages/request_header/BUILD.gn b/components/offline_pages/core/request_header/BUILD.gn
similarity index 100%
rename from components/offline_pages/request_header/BUILD.gn
rename to components/offline_pages/core/request_header/BUILD.gn
diff --git a/components/offline_pages/core/request_header/offline_page_header.cc b/components/offline_pages/core/request_header/offline_page_header.cc
new file mode 100644
index 0000000..9c58e7d
--- /dev/null
+++ b/components/offline_pages/core/request_header/offline_page_header.cc
@@ -0,0 +1,131 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/request_header/offline_page_header.h"
+
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+
+namespace offline_pages {
+
+const char kOfflinePageHeader[] = "X-Chrome-offline";
+const char kOfflinePageHeaderReasonKey[] = "reason";
+const char kOfflinePageHeaderReasonValueDueToNetError[] = "error";
+const char kOfflinePageHeaderReasonValueFromDownload[] = "download";
+const char kOfflinePageHeaderReasonValueReload[] = "reload";
+const char kOfflinePageHeaderPersistKey[] = "persist";
+const char kOfflinePageHeaderIDKey[] = "id";
+
+namespace {
+
+bool ParseOfflineHeaderValue(const std::string& header_value,
+                             bool* need_to_persist,
+                             OfflinePageHeader::Reason* reason,
+                             std::string* id) {
+  // If the offline header is not present, treat it as not parsed successfully.
+  if (header_value.empty())
+    return false;
+
+  bool token_found = false;
+  base::StringTokenizer tokenizer(header_value, ", ");
+  while (tokenizer.GetNext()) {
+    token_found = true;
+    std::string pair = tokenizer.token();
+    std::size_t pos = pair.find('=');
+    if (pos == std::string::npos)
+      return false;
+    std::string key = base::ToLowerASCII(pair.substr(0, pos));
+    std::string value = base::ToLowerASCII(pair.substr(pos + 1));
+    if (key == kOfflinePageHeaderPersistKey) {
+      if (value == "1")
+        *need_to_persist = true;
+      else if (value == "0")
+        *need_to_persist = false;
+      else
+        return false;
+    } else if (key == kOfflinePageHeaderReasonKey) {
+      if (value == kOfflinePageHeaderReasonValueDueToNetError)
+        *reason = OfflinePageHeader::Reason::NET_ERROR;
+      else if (value == kOfflinePageHeaderReasonValueFromDownload)
+        *reason = OfflinePageHeader::Reason::DOWNLOAD;
+      else if (value == kOfflinePageHeaderReasonValueReload)
+        *reason = OfflinePageHeader::Reason::RELOAD;
+      else
+        return false;
+    } else if (key == kOfflinePageHeaderIDKey) {
+      *id = value;
+    } else {
+      return false;
+    }
+  }
+
+  return token_found;
+}
+
+std::string ReasonToString(OfflinePageHeader::Reason reason) {
+  switch (reason) {
+    case OfflinePageHeader::Reason::NET_ERROR:
+      return kOfflinePageHeaderReasonValueDueToNetError;
+    case OfflinePageHeader::Reason::DOWNLOAD:
+      return kOfflinePageHeaderReasonValueFromDownload;
+    case OfflinePageHeader::Reason::RELOAD:
+      return kOfflinePageHeaderReasonValueReload;
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
+}  // namespace
+
+OfflinePageHeader::OfflinePageHeader()
+    : did_fail_parsing_for_test(false),
+      need_to_persist(false),
+      reason(Reason::NONE) {}
+
+OfflinePageHeader::OfflinePageHeader(const std::string& header_value)
+    : did_fail_parsing_for_test(false),
+      need_to_persist(false),
+      reason(Reason::NONE) {
+  if (!ParseOfflineHeaderValue(header_value, &need_to_persist, &reason, &id)) {
+    did_fail_parsing_for_test = true;
+    Clear();
+  }
+}
+
+OfflinePageHeader::~OfflinePageHeader() {}
+
+std::string OfflinePageHeader::GetCompleteHeaderString() const {
+  if (reason == Reason::NONE)
+    return std::string();
+
+  std::string value(kOfflinePageHeader);
+  value += ": ";
+
+  value += kOfflinePageHeaderPersistKey;
+  value += "=";
+  value += need_to_persist ? "1" : "0";
+
+  value += " ";
+  value += kOfflinePageHeaderReasonKey;
+  value += "=";
+  value += ReasonToString(reason);
+
+  if (!id.empty()) {
+    value += " ";
+    value += kOfflinePageHeaderIDKey;
+    value += "=";
+    value += id;
+  }
+
+  return value;
+}
+
+void OfflinePageHeader::Clear() {
+  reason = Reason::NONE;
+  need_to_persist = false;
+  id.clear();
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/request_header/offline_page_header.h b/components/offline_pages/core/request_header/offline_page_header.h
new file mode 100644
index 0000000..07175c6f
--- /dev/null
+++ b/components/offline_pages/core/request_header/offline_page_header.h
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
+
+#include <string>
+
+namespace offline_pages {
+
+// Header that defines the custom behavior to load offline pages. Its value is a
+// comma/space separated name-value pair.
+extern const char kOfflinePageHeader[];
+
+// The name used in name-value pair of kOfflinePageHeader to tell if the offline
+// info in this header should be persisted across session restore.
+extern const char kOfflinePageHeaderPersistKey[];
+
+// The name used in name-value pair of kOfflinePageHeader to denote the reason
+// for loading offline page.
+extern const char kOfflinePageHeaderReasonKey[];
+// Possible values in name-value pair that denote the reason for loading offline
+// page.
+// The offline page should be loaded even when the network is connected. This is
+// because the live version failed to load due to certain net error.
+extern const char kOfflinePageHeaderReasonValueDueToNetError[];
+// The offline page should be loaded because the user clicks to open the
+// downloaded page explicitly.
+extern const char kOfflinePageHeaderReasonValueFromDownload[];
+// This only happens after the offline page is loaded due to above reason and
+// then the user reload current page. The network condition should be checked
+// this time to decide if a live version should be tried again.
+extern const char kOfflinePageHeaderReasonValueReload[];
+
+// The name used in name-value pair of kOfflinePageHeader to denote the offline
+// ID of the offline page to load.
+extern const char kOfflinePageHeaderIDKey[];
+
+// Used to parse the extra request header string that defines offline page
+// loading behaviors.
+struct OfflinePageHeader {
+ public:
+  enum class Reason { NONE, NET_ERROR, DOWNLOAD, RELOAD };
+
+  OfflinePageHeader();
+
+  // Constructed from a request header value string.
+  // The struct members will be cleared if the parsing failed.
+  explicit OfflinePageHeader(const std::string& header_value);
+
+  ~OfflinePageHeader();
+
+  // Returns the full header string, including both key and value, that could be
+  // passed to set extra request header.
+  std::string GetCompleteHeaderString() const;
+
+  void Clear();
+
+  // Set if failed to parse a request header value string. For testing only.
+  bool did_fail_parsing_for_test;
+
+  // Flag to indicate if the header should be persisted across session restore.
+  bool need_to_persist;
+
+  // Describes the reason to load offline page.
+  Reason reason;
+
+  // The offline ID of the page to load.
+  std::string id;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
diff --git a/components/offline_pages/core/request_header/offline_page_header_unittest.cc b/components/offline_pages/core/request_header/offline_page_header_unittest.cc
new file mode 100644
index 0000000..636c3bd
--- /dev/null
+++ b/components/offline_pages/core/request_header/offline_page_header_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/request_header/offline_page_header.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+class OfflinePageHeaderTest : public testing::Test {
+ public:
+  OfflinePageHeaderTest() {}
+  ~OfflinePageHeaderTest() override {}
+
+  bool ParseFromHeaderValue(const std::string& header_value,
+                            bool* need_to_persist,
+                            OfflinePageHeader::Reason* reason,
+                            std::string* id) {
+    OfflinePageHeader header(header_value);
+    *need_to_persist = header.need_to_persist;
+    *reason = header.reason;
+    *id = header.id;
+    return !header.did_fail_parsing_for_test;
+  }
+};
+
+TEST_F(OfflinePageHeaderTest, Parse) {
+  bool need_to_persist;
+  OfflinePageHeader::Reason reason;
+  std::string id;
+
+  EXPECT_FALSE(ParseFromHeaderValue("", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(ParseFromHeaderValue("   ", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(ParseFromHeaderValue(" , ", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(ParseFromHeaderValue("reason", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(ParseFromHeaderValue("a b c", &need_to_persist, &reason, &id));
+
+  EXPECT_FALSE(
+      ParseFromHeaderValue("persist=aa", &need_to_persist, &reason, &id));
+
+  EXPECT_TRUE(
+      ParseFromHeaderValue("persist=1", &need_to_persist, &reason, &id));
+  EXPECT_TRUE(need_to_persist);
+
+  EXPECT_TRUE(
+      ParseFromHeaderValue("persist=0", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(need_to_persist);
+  EXPECT_EQ(OfflinePageHeader::Reason::NONE, reason);
+  EXPECT_EQ("", id);
+
+  EXPECT_FALSE(
+      ParseFromHeaderValue("reason=foo", &need_to_persist, &reason, &id));
+
+  EXPECT_TRUE(
+      ParseFromHeaderValue("reason=error", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(need_to_persist);
+  EXPECT_EQ(OfflinePageHeader::Reason::NET_ERROR, reason);
+  EXPECT_EQ("", id);
+
+  EXPECT_TRUE(ParseFromHeaderValue("id=a1b2", &need_to_persist, &reason, &id));
+  EXPECT_FALSE(need_to_persist);
+  EXPECT_EQ(OfflinePageHeader::Reason::NONE, reason);
+  EXPECT_EQ("a1b2", id);
+
+  EXPECT_TRUE(ParseFromHeaderValue("persist=1 reason=download id=a1b2",
+                                   &need_to_persist, &reason, &id));
+  EXPECT_TRUE(need_to_persist);
+  EXPECT_EQ(OfflinePageHeader::Reason::DOWNLOAD, reason);
+  EXPECT_EQ("a1b2", id);
+}
+
+TEST_F(OfflinePageHeaderTest, ToString) {
+  OfflinePageHeader header;
+  header.need_to_persist = true;
+  header.reason = OfflinePageHeader::Reason::DOWNLOAD;
+  header.id = "a1b2";
+  EXPECT_EQ("X-Chrome-offline: persist=1 reason=download id=a1b2",
+            header.GetCompleteHeaderString());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/snapshot_controller.cc b/components/offline_pages/core/snapshot_controller.cc
new file mode 100644
index 0000000..aa0b427
--- /dev/null
+++ b/components/offline_pages/core/snapshot_controller.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/snapshot_controller.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace {
+// Default delay, in milliseconds, between the main document parsed event and
+// snapshot. Note: this snapshot might not occur if the OnLoad event and
+// OnLoad delay elapses first to trigger a final snapshot.
+const size_t kDefaultDelayAfterDocumentAvailableMs = 7000;
+
+// Default delay, in milliseconds, between the main document OnLoad event and
+// snapshot.
+const size_t kDelayAfterDocumentOnLoadCompletedMs = 1000;
+
+}  // namespace
+
+namespace offline_pages {
+
+SnapshotController::SnapshotController(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+    SnapshotController::Client* client)
+    : task_runner_(task_runner),
+      client_(client),
+      state_(State::READY),
+      delay_after_document_available_ms_(kDefaultDelayAfterDocumentAvailableMs),
+      delay_after_document_on_load_completed_ms_(
+          kDelayAfterDocumentOnLoadCompletedMs),
+      weak_ptr_factory_(this) {}
+
+SnapshotController::SnapshotController(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+    SnapshotController::Client* client,
+    size_t delay_after_document_available_ms,
+    size_t delay_after_document_on_load_completed_ms)
+    : task_runner_(task_runner),
+      client_(client),
+      state_(State::READY),
+      delay_after_document_available_ms_(delay_after_document_available_ms),
+      delay_after_document_on_load_completed_ms_(
+          delay_after_document_on_load_completed_ms),
+      weak_ptr_factory_(this) {}
+
+SnapshotController::~SnapshotController() {}
+
+void SnapshotController::Reset() {
+  // Cancel potentially delayed tasks that relate to the previous 'session'.
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  state_ = State::READY;
+}
+
+void SnapshotController::Stop() {
+  state_ = State::STOPPED;
+}
+
+void SnapshotController::PendingSnapshotCompleted() {
+  // Unless the controller is "stopped", enable the subsequent snapshots.
+  // Stopped state prevents any further snapshots form being started.
+  if (state_ == State::STOPPED)
+    return;
+  state_ = State::READY;
+}
+
+void SnapshotController::DocumentAvailableInMainFrame() {
+  // Post a delayed task to snapshot.
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&SnapshotController::MaybeStartSnapshot,
+                            weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(delay_after_document_available_ms_));
+}
+
+void SnapshotController::DocumentOnLoadCompletedInMainFrame() {
+  // Post a delayed task to snapshot and then stop this controller.
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&SnapshotController::MaybeStartSnapshotThenStop,
+                            weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(
+          delay_after_document_on_load_completed_ms_));
+}
+
+void SnapshotController::MaybeStartSnapshot() {
+  if (state_ != State::READY)
+    return;
+  state_ = State::SNAPSHOT_PENDING;
+  client_->StartSnapshot();
+}
+
+void SnapshotController::MaybeStartSnapshotThenStop() {
+  MaybeStartSnapshot();
+  Stop();
+}
+
+size_t SnapshotController::GetDelayAfterDocumentAvailableForTest() {
+  return delay_after_document_available_ms_;
+}
+
+size_t SnapshotController::GetDelayAfterDocumentOnLoadCompletedForTest() {
+  return delay_after_document_on_load_completed_ms_;
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/snapshot_controller.h b/components/offline_pages/core/snapshot_controller.h
new file mode 100644
index 0000000..6a7bc64
--- /dev/null
+++ b/components/offline_pages/core/snapshot_controller.h
@@ -0,0 +1,97 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+
+namespace offline_pages {
+
+// Takes various signals and produces StartSnapshot calls following a specific
+// policy. Can request snapshots multiple times per 'session'. Session can be
+// ended and another one started by calling Reset().
+// Main invariants:
+// - It never starts overlapping snapshots, Client reports when previous
+//   snapshot is done.
+// - The currently worked on (pending) snapshot is always allowed to complete,
+//   the new attempts to start a snapshot are ignored until it does.
+// - Some signals prevent more snapshots to be taken.
+//   OnLoad is currently such signal.
+// - Once Reset() is called on the SnapshotController, the delayed tasks are
+//   reset so no StartSnapshot calls is made 'cross-session'.
+class SnapshotController {
+ public:
+  enum class State {
+    READY,             // Listening to input, will start snapshot when needed.
+    SNAPSHOT_PENDING,  // Snapshot is in progress, don't start another.
+    STOPPED,           // Terminal state, no snapshots until reset.
+  };
+
+  // Client of the SnapshotController.
+  class Client {
+   public:
+    // Invoked at a good moment to start a snapshot. May be invoked multiple
+    // times, but not in overlapping manner - waits until
+    // PreviousSnapshotCompleted() before the next StatrSnapshot().
+    // Client should overwrite the result of previous snapshot with the new one,
+    // it is assumed that later snapshots are better then previous.
+    virtual void StartSnapshot() = 0;
+
+   protected:
+    virtual ~Client() {}
+  };
+
+  SnapshotController(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+      SnapshotController::Client* client);
+  SnapshotController(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+      SnapshotController::Client* client,
+      size_t delay_after_document_available_ms,
+      size_t delay_after_document_on_load_completed_ms);
+  virtual ~SnapshotController();
+
+  // Resets the 'session', returning controller to initial state.
+  void Reset();
+
+  // Stops current session, no more Client::StartSnapshot calls will be
+  // invoked from the SnapshotController until current session is Reset().
+  // Called by Client, for example when it encounters an error loading the page.
+  void Stop();
+
+  // The way for Client to report that previously started snapshot is
+  // now completed (so the next one can be started).
+  void PendingSnapshotCompleted();
+
+  // Invoked from WebContentObserver::DocumentAvailableInMainFrame
+  void DocumentAvailableInMainFrame();
+
+  // Invoked from WebContentObserver::DocumentOnLoadCompletedInMainFrame
+  void DocumentOnLoadCompletedInMainFrame();
+
+  size_t GetDelayAfterDocumentAvailableForTest();
+  size_t GetDelayAfterDocumentOnLoadCompletedForTest();
+
+ private:
+  void MaybeStartSnapshot();
+  void MaybeStartSnapshotThenStop();
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  // Client owns this class.
+  SnapshotController::Client* client_;
+  SnapshotController::State state_;
+  size_t delay_after_document_available_ms_;
+  size_t delay_after_document_on_load_completed_ms_;
+
+  base::WeakPtrFactory<SnapshotController> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SnapshotController);
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_SNAPSHOT_CONTROLLER_H_
diff --git a/components/offline_pages/core/snapshot_controller_unittest.cc b/components/offline_pages/core/snapshot_controller_unittest.cc
new file mode 100644
index 0000000..390a96e
--- /dev/null
+++ b/components/offline_pages/core/snapshot_controller_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/snapshot_controller.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace offline_pages {
+
+class SnapshotControllerTest : public testing::Test,
+                               public SnapshotController::Client {
+ public:
+  SnapshotControllerTest();
+  ~SnapshotControllerTest() override;
+
+  SnapshotController* controller() { return controller_.get(); }
+  int snapshot_count() { return snapshot_count_; }
+
+  // testing::Test
+  void SetUp() override;
+  void TearDown() override;
+
+  // SnapshotController::Client
+  void StartSnapshot() override;
+
+  // Utility methods.
+  // Runs until all of the tasks that are not delayed are gone from the task
+  // queue.
+  void PumpLoop();
+  // Fast-forwards virtual time by |delta|, causing tasks with a remaining
+  // delay less than or equal to |delta| to be executed.
+  void FastForwardBy(base::TimeDelta delta);
+
+ private:
+  std::unique_ptr<SnapshotController> controller_;
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+  bool snapshot_started_;
+  int snapshot_count_;
+};
+
+SnapshotControllerTest::SnapshotControllerTest()
+    : task_runner_(new base::TestMockTimeTaskRunner),
+      snapshot_started_(true),
+      snapshot_count_(0) {}
+
+SnapshotControllerTest::~SnapshotControllerTest() {}
+
+void SnapshotControllerTest::SetUp() {
+  controller_.reset(new SnapshotController(task_runner_, this));
+  snapshot_started_ = true;
+}
+
+void SnapshotControllerTest::TearDown() {
+  controller_.reset();
+}
+
+void SnapshotControllerTest::StartSnapshot() {
+  snapshot_count_++;
+}
+
+void SnapshotControllerTest::PumpLoop() {
+  task_runner_->RunUntilIdle();
+}
+
+void SnapshotControllerTest::FastForwardBy(base::TimeDelta delta) {
+  task_runner_->FastForwardBy(delta);
+}
+
+TEST_F(SnapshotControllerTest, OnLoad) {
+  // Onload should make snapshot after its delay.
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  PumpLoop();
+  EXPECT_EQ(0, snapshot_count());
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
+  EXPECT_EQ(1, snapshot_count());
+}
+
+TEST_F(SnapshotControllerTest, OnDocumentAvailable) {
+  EXPECT_GT(controller()->GetDelayAfterDocumentAvailableForTest(), 0UL);
+  // OnDOM should make snapshot after a delay.
+  controller()->DocumentAvailableInMainFrame();
+  PumpLoop();
+  EXPECT_EQ(0, snapshot_count());
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  EXPECT_EQ(1, snapshot_count());
+}
+
+TEST_F(SnapshotControllerTest, OnLoadSnapshotIsTheLastOne) {
+  // This test assumes DocumentAvailable delay is longer than OnLoadCompleted.
+  EXPECT_GT(controller()->GetDelayAfterDocumentAvailableForTest(),
+            controller()->GetDelayAfterDocumentOnLoadCompletedForTest());
+  // OnDOM should make snapshot after a delay.
+  controller()->DocumentAvailableInMainFrame();
+  PumpLoop();
+  EXPECT_EQ(0, snapshot_count());
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  // Advance time to OnLoadCompleted delay to trigger snapshot.
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
+  EXPECT_EQ(1, snapshot_count());
+  // Report that snapshot is completed.
+  controller()->PendingSnapshotCompleted();
+  // Even though previous snapshot is completed, new one should not start
+  // when this DocumentAvailable delay expires.
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  EXPECT_EQ(1, snapshot_count());
+}
+
+TEST_F(SnapshotControllerTest, OnLoadSnapshotAfterLongDelay) {
+  // OnDOM should make snapshot after a delay.
+  controller()->DocumentAvailableInMainFrame();
+  PumpLoop();
+  EXPECT_EQ(0, snapshot_count());
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  EXPECT_EQ(1, snapshot_count());
+  // Report that snapshot is completed.
+  controller()->PendingSnapshotCompleted();
+  // OnLoad should make 2nd snapshot after its delay.
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
+  EXPECT_EQ(2, snapshot_count());
+}
+
+TEST_F(SnapshotControllerTest, Stop) {
+  // OnDOM should make snapshot after a delay.
+  controller()->DocumentAvailableInMainFrame();
+  PumpLoop();
+  EXPECT_EQ(0, snapshot_count());
+  controller()->Stop();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  // Should not start snapshots
+  EXPECT_EQ(0, snapshot_count());
+  // Also should not start snapshot.
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  EXPECT_EQ(0, snapshot_count());
+}
+
+TEST_F(SnapshotControllerTest, ClientReset) {
+  controller()->DocumentAvailableInMainFrame();
+
+  controller()->Reset();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  // No snapshot since session was reset.
+  EXPECT_EQ(0, snapshot_count());
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
+  EXPECT_EQ(1, snapshot_count());
+
+  controller()->Reset();
+  controller()->DocumentAvailableInMainFrame();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  // No snapshot since session was reset.
+  EXPECT_EQ(2, snapshot_count());
+}
+
+// This simulated a Reset while there is ongoing snapshot, which is reported
+// as done later. That reporting should have no effect nor crash.
+TEST_F(SnapshotControllerTest, ClientResetWhileSnapshotting) {
+  controller()->DocumentOnLoadCompletedInMainFrame();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
+  EXPECT_EQ(1, snapshot_count());
+  // This normally happens when navigation starts.
+  controller()->Reset();
+  controller()->PendingSnapshotCompleted();
+  // Next snapshot should be initiated when new document is loaded.
+  controller()->DocumentAvailableInMainFrame();
+  FastForwardBy(base::TimeDelta::FromMilliseconds(
+      controller()->GetDelayAfterDocumentAvailableForTest()));
+  // No snapshot since session was reset.
+  EXPECT_EQ(2, snapshot_count());
+}
+
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/stub_offline_page_model.cc b/components/offline_pages/core/stub_offline_page_model.cc
new file mode 100644
index 0000000..dcc69c4
--- /dev/null
+++ b/components/offline_pages/core/stub_offline_page_model.cc
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/offline_pages/core/stub_offline_page_model.h"
+
+namespace offline_pages {
+
+StubOfflinePageModel::StubOfflinePageModel() {}
+StubOfflinePageModel::~StubOfflinePageModel() {}
+
+void StubOfflinePageModel::AddObserver(Observer* observer) {}
+void StubOfflinePageModel::RemoveObserver(Observer* observer) {}
+void StubOfflinePageModel::SavePage(
+    const SavePageParams& save_page_params,
+    std::unique_ptr<OfflinePageArchiver> archiver,
+    const SavePageCallback& callback) {}
+void StubOfflinePageModel::MarkPageAccessed(int64_t offline_id) {}
+void StubOfflinePageModel::DeletePagesByOfflineId(
+    const std::vector<int64_t>& offline_ids,
+    const DeletePageCallback& callback) {}
+void StubOfflinePageModel::DeletePagesByClientIds(
+    const std::vector<ClientId>& client_ids,
+    const DeletePageCallback& callback) {}
+void StubOfflinePageModel::GetPagesMatchingQuery(
+    std::unique_ptr<OfflinePageModelQuery> query,
+    const MultipleOfflinePageItemCallback& callback) {}
+void StubOfflinePageModel::GetPagesByClientIds(
+    const std::vector<ClientId>& client_ids,
+    const MultipleOfflinePageItemCallback& callback) {}
+void StubOfflinePageModel::DeleteCachedPagesByURLPredicate(
+    const UrlPredicate& predicate,
+    const DeletePageCallback& callback) {}
+void StubOfflinePageModel::CheckPagesExistOffline(
+    const std::set<GURL>& urls,
+    const CheckPagesExistOfflineCallback& callback) {}
+void StubOfflinePageModel::GetAllPages(
+    const MultipleOfflinePageItemCallback& callback) {}
+void StubOfflinePageModel::GetOfflineIdsForClientId(
+    const ClientId& client_id,
+    const MultipleOfflineIdCallback& callback) {}
+void StubOfflinePageModel::GetPageByOfflineId(
+    int64_t offline_id,
+    const SingleOfflinePageItemCallback& callback) {}
+void StubOfflinePageModel::GetPagesByURL(
+    const GURL& url,
+    URLSearchMode url_search_mode,
+    const MultipleOfflinePageItemCallback& callback) {}
+ClientPolicyController* StubOfflinePageModel::GetPolicyController() {
+  return &policy_controller_;
+}
+bool StubOfflinePageModel::is_loaded() const {
+  return true;
+}
+OfflineEventLogger* StubOfflinePageModel::GetLogger() {
+  return nullptr;
+}
+}  // namespace offline_pages
diff --git a/components/offline_pages/core/stub_offline_page_model.h b/components/offline_pages/core/stub_offline_page_model.h
new file mode 100644
index 0000000..6d3bc8e
--- /dev/null
+++ b/components/offline_pages/core/stub_offline_page_model.h
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_page_model.h"
+
+namespace offline_pages {
+
+// Stub implementation of OfflinePageModel interface for testing. Besides using
+// as a stub for tests, it may also be subclassed to mock specific methods
+// needed for a set of tests.
+class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
+ public:
+  StubOfflinePageModel();
+  ~StubOfflinePageModel() override;
+
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  void SavePage(const SavePageParams& save_page_params,
+                std::unique_ptr<OfflinePageArchiver> archiver,
+                const SavePageCallback& callback) override;
+  void MarkPageAccessed(int64_t offline_id) override;
+  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
+                              const DeletePageCallback& callback) override;
+  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
+                              const DeletePageCallback& callback) override;
+  void GetPagesMatchingQuery(
+      std::unique_ptr<OfflinePageModelQuery> query,
+      const MultipleOfflinePageItemCallback& callback) override;
+  void GetPagesByClientIds(
+      const std::vector<ClientId>& client_ids,
+      const MultipleOfflinePageItemCallback& callback) override;
+  void DeleteCachedPagesByURLPredicate(
+      const UrlPredicate& predicate,
+      const DeletePageCallback& callback) override;
+  void CheckPagesExistOffline(
+      const std::set<GURL>& urls,
+      const CheckPagesExistOfflineCallback& callback) override;
+  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
+  void GetOfflineIdsForClientId(
+      const ClientId& client_id,
+      const MultipleOfflineIdCallback& callback) override;
+  void GetPageByOfflineId(
+      int64_t offline_id,
+      const SingleOfflinePageItemCallback& callback) override;
+  void GetPagesByURL(
+      const GURL& url,
+      URLSearchMode url_search_mode,
+      const MultipleOfflinePageItemCallback& callback) override;
+  ClientPolicyController* GetPolicyController() override;
+  bool is_loaded() const override;
+  OfflineEventLogger* GetLogger() override;
+
+ private:
+  ClientPolicyController policy_controller_;
+  std::vector<int64_t> offline_ids_;
+};
+
+}  // namespace offline_pages
+
+#endif  // COMPONENTS_OFFLINE_PAGES_CORE_STUB_OFFLINE_PAGE_MODEL_H_
diff --git a/components/offline_pages/downloads/BUILD.gn b/components/offline_pages/downloads/BUILD.gn
deleted file mode 100644
index 41f709f..0000000
--- a/components/offline_pages/downloads/BUILD.gn
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-}
-
-static_library("offline_pages_ui_adapter") {
-  sources = [
-    "download_notifying_observer.cc",
-    "download_notifying_observer.h",
-    "download_ui_adapter.cc",
-    "download_ui_adapter.h",
-    "download_ui_item.cc",
-    "download_ui_item.h",
-    "offline_page_download_notifier.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/offline_pages:offline_pages",
-    "//components/offline_pages/background:background_offliner",
-    "//url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "download_notifying_observer_unittest.cc",
-    "download_ui_adapter_unittest.cc",
-  ]
-
-  deps = [
-    ":offline_pages_ui_adapter",
-    "//base",
-    "//base/test:test_support",
-    "//components/offline_pages:offline_pages",
-    "//components/offline_pages:test_support",
-    "//components/offline_pages/background:background_offliner",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/offline_pages/downloads/download_notifying_observer.cc b/components/offline_pages/downloads/download_notifying_observer.cc
deleted file mode 100644
index ea5745c..0000000
--- a/components/offline_pages/downloads/download_notifying_observer.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/downloads/download_notifying_observer.h"
-
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/download_ui_adapter.h"
-#include "components/offline_pages/downloads/offline_page_download_notifier.h"
-
-namespace offline_pages {
-namespace {
-int kUserDataKey;  // Only address is used.
-}  // namespace
-
-DownloadNotifyingObserver::DownloadNotifyingObserver(
-    std::unique_ptr<OfflinePageDownloadNotifier> notifier,
-    ClientPolicyController* policy_controller)
-    : notifier_(std::move(notifier)), policy_controller_(policy_controller) {}
-
-DownloadNotifyingObserver::~DownloadNotifyingObserver() {}
-
-// static
-DownloadNotifyingObserver* DownloadNotifyingObserver::GetFromRequestCoordinator(
-    RequestCoordinator* request_coordinator) {
-  DCHECK(request_coordinator);
-  return static_cast<DownloadNotifyingObserver*>(
-      request_coordinator->GetUserData(&kUserDataKey));
-}
-
-// static
-void DownloadNotifyingObserver::CreateAndStartObserving(
-    RequestCoordinator* request_coordinator,
-    std::unique_ptr<OfflinePageDownloadNotifier> notifier) {
-  DCHECK(request_coordinator);
-  DCHECK(notifier.get());
-  DownloadNotifyingObserver* observer = new DownloadNotifyingObserver(
-      std::move(notifier), request_coordinator->GetPolicyController());
-  request_coordinator->AddObserver(observer);
-  // |request_coordinator| takes ownership of observer here.
-  request_coordinator->SetUserData(&kUserDataKey, observer);
-}
-
-void DownloadNotifyingObserver::OnAdded(const SavePageRequest& request) {
-  DCHECK(notifier_.get());
-  if (!IsVisibleInUI(request.client_id()))
-    return;
-
-  // Calling Progress ensures notification is created in lieu of specific
-  // Add/Create call.
-  notifier_->NotifyDownloadProgress(DownloadUIItem(request));
-
-  // Now we need to update the notification if it is not active/offlining.
-  if (request.request_state() != SavePageRequest::RequestState::OFFLINING)
-    NotifyRequestStateChange(request);
-}
-
-void DownloadNotifyingObserver::OnChanged(const SavePageRequest& request) {
-  DCHECK(notifier_.get());
-  if (!IsVisibleInUI(request.client_id()))
-    return;
-  NotifyRequestStateChange(request);
-}
-
-void DownloadNotifyingObserver::OnCompleted(
-    const SavePageRequest& request,
-    RequestCoordinator::BackgroundSavePageResult status) {
-  DCHECK(notifier_.get());
-  if (!IsVisibleInUI(request.client_id()))
-    return;
-  if (status == RequestCoordinator::BackgroundSavePageResult::SUCCESS)
-    notifier_->NotifyDownloadSuccessful(DownloadUIItem(request));
-  else if (status == RequestCoordinator::BackgroundSavePageResult::REMOVED)
-    notifier_->NotifyDownloadCanceled(DownloadUIItem(request));
-  else
-    notifier_->NotifyDownloadFailed(DownloadUIItem(request));
-}
-
-bool DownloadNotifyingObserver::IsVisibleInUI(const ClientId& page) {
-  return policy_controller_->IsSupportedByDownload(page.name_space) &&
-         base::IsValidGUID(page.id);
-}
-
-// Calls the appropriate notifier method depending upon the state of the
-// request. For example, an AVAILABLE request is not active (aka, pending)
-// which the notifier understands as an Interrupted operation vs. one that
-// has Progress or is Paused.
-void DownloadNotifyingObserver::NotifyRequestStateChange(
-    const SavePageRequest& request) {
-  if (request.request_state() == SavePageRequest::RequestState::PAUSED)
-    notifier_->NotifyDownloadPaused(DownloadUIItem(request));
-  else if (request.request_state() == SavePageRequest::RequestState::AVAILABLE)
-    notifier_->NotifyDownloadInterrupted(DownloadUIItem(request));
-  else
-    notifier_->NotifyDownloadProgress(DownloadUIItem(request));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/downloads/download_notifying_observer.h b/components/offline_pages/downloads/download_notifying_observer.h
deleted file mode 100644
index f89a543..0000000
--- a/components/offline_pages/downloads/download_notifying_observer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
-
-#include <memory>
-
-#include "base/guid.h"
-#include "base/macros.h"
-#include "components/offline_pages/background/request_coordinator.h"
-#include "components/offline_pages/client_policy_controller.h"
-
-namespace offline_pages {
-
-struct ClientId;
-struct OfflinePageDownloadNotifier;
-class ClientPolicyController;
-class SavePageRequest;
-
-// Class observing the save page requests and issuing corresponding user
-// notifications as requests are added or updated.
-class DownloadNotifyingObserver : public RequestCoordinator::Observer,
-                                  public base::SupportsUserData::Data {
- public:
-  ~DownloadNotifyingObserver() override;
-
-  static DownloadNotifyingObserver* GetFromRequestCoordinator(
-      RequestCoordinator* request_coordinator);
-  static void CreateAndStartObserving(
-      RequestCoordinator* request_coordinator,
-      std::unique_ptr<OfflinePageDownloadNotifier> notifier);
-
-  // RequestCoordinator::Observer implementation:
-  void OnAdded(const SavePageRequest& request) override;
-  void OnChanged(const SavePageRequest& request) override;
-  void OnCompleted(
-      const SavePageRequest& request,
-      RequestCoordinator::BackgroundSavePageResult status) override;
-
- private:
-  friend class DownloadNotifyingObserverTest;
-
-  DownloadNotifyingObserver(
-      std::unique_ptr<OfflinePageDownloadNotifier> notifier,
-      ClientPolicyController* policy_controller);
-
-  bool IsVisibleInUI(const ClientId& id);
-
-  void NotifyRequestStateChange(const SavePageRequest& request);
-
-  // Used to issue notifications related to save page requests.
-  std::unique_ptr<OfflinePageDownloadNotifier> notifier_;
-  // Used to determine policy-related permissions. Not owned.
-  ClientPolicyController* policy_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(DownloadNotifyingObserver);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
diff --git a/components/offline_pages/downloads/download_notifying_observer_unittest.cc b/components/offline_pages/downloads/download_notifying_observer_unittest.cc
deleted file mode 100644
index 6943b12..0000000
--- a/components/offline_pages/downloads/download_notifying_observer_unittest.cc
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/downloads/download_notifying_observer.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/offline_page_download_notifier.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-static const int64_t kTestOfflineId = 42L;
-static const char kTestUrl[] = "http://foo.com/bar";
-static const char kTestGuid[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
-static const ClientId kTestClientId(kDownloadNamespace, kTestGuid);
-static const base::Time kTestCreationTime = base::Time::Now();
-static const bool kTestUserRequested = true;
-
-enum class LastNotificationType {
-  NONE,
-  DOWNLOAD_SUCCESSFUL,
-  DOWNLOAD_FAILED,
-  DOWNLOAD_PROGRESS,
-  DOWNLOAD_PAUSED,
-  DOWNLOAD_INTERRUPTED,
-  DOWNLOAD_CANCELED,
-};
-
-class TestNotifier : public OfflinePageDownloadNotifier {
- public:
-  TestNotifier();
-  ~TestNotifier() override;
-
-  // OfflinePageDownloadNotifier implementation:
-  void NotifyDownloadSuccessful(const DownloadUIItem& item) override;
-  void NotifyDownloadFailed(const DownloadUIItem& item) override;
-  void NotifyDownloadProgress(const DownloadUIItem& item) override;
-  void NotifyDownloadPaused(const DownloadUIItem& item) override;
-  void NotifyDownloadInterrupted(const DownloadUIItem& item) override;
-  void NotifyDownloadCanceled(const DownloadUIItem& item) override;
-
-  void Reset();
-
-  LastNotificationType last_notification_type() const {
-    return last_notification_type_;
-  }
-
-  DownloadUIItem* download_item() const { return download_item_.get(); }
-
- private:
-  LastNotificationType last_notification_type_;
-  std::unique_ptr<DownloadUIItem> download_item_;
-};
-
-TestNotifier::TestNotifier()
-    : last_notification_type_(LastNotificationType::NONE) {}
-
-TestNotifier::~TestNotifier() {}
-
-void TestNotifier::NotifyDownloadSuccessful(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_SUCCESSFUL;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::NotifyDownloadFailed(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_FAILED;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::NotifyDownloadProgress(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_PROGRESS;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::NotifyDownloadPaused(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_PAUSED;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::NotifyDownloadInterrupted(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_INTERRUPTED;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::NotifyDownloadCanceled(const DownloadUIItem& item) {
-  last_notification_type_ = LastNotificationType::DOWNLOAD_CANCELED;
-  download_item_.reset(new DownloadUIItem(item));
-}
-
-void TestNotifier::Reset() {
-  last_notification_type_ = LastNotificationType::NONE;
-  download_item_.reset(nullptr);
-}
-
-}  // namespace
-
-class DownloadNotifyingObserverTest : public testing::Test {
- public:
-  DownloadNotifyingObserverTest();
-  ~DownloadNotifyingObserverTest() override;
-
-  // testing::Test implementation:
-  void SetUp() override;
-
-  TestNotifier* notifier() const { return notifier_; }
-  DownloadNotifyingObserver* observer() { return observer_.get(); }
-
- private:
-  TestNotifier* notifier_;
-  std::unique_ptr<DownloadNotifyingObserver> observer_;
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-};
-
-DownloadNotifyingObserverTest::DownloadNotifyingObserverTest() {}
-
-DownloadNotifyingObserverTest::~DownloadNotifyingObserverTest() {}
-
-void DownloadNotifyingObserverTest::SetUp() {
-  std::unique_ptr<TestNotifier> notifier(new TestNotifier);
-  policy_controller_.reset(new ClientPolicyController());
-  notifier_ = notifier.get();
-  observer_.reset(new DownloadNotifyingObserver(std::move(notifier),
-                                                policy_controller_.get()));
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsAvailable) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
-  observer()->OnAdded(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsOffling) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::OFFLINING);
-  observer()->OnAdded(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsPaused) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::PAUSED);
-  observer()->OnAdded(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToPaused) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::PAUSED);
-  observer()->OnChanged(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToAvailable) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
-  observer()->OnChanged(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToOfflining) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  request.set_request_state(SavePageRequest::RequestState::OFFLINING);
-  observer()->OnChanged(request);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedSuccess) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  observer()->OnCompleted(request,
-                          RequestNotifier::BackgroundSavePageResult::SUCCESS);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_SUCCESSFUL,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedCanceled) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  observer()->OnCompleted(request,
-                          RequestNotifier::BackgroundSavePageResult::REMOVED);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_CANCELED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedFailure) {
-  SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
-                          kTestCreationTime, kTestUserRequested);
-  observer()->OnCompleted(
-      request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-
-  notifier()->Reset();
-  observer()->OnCompleted(
-      request, RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-
-  notifier()->Reset();
-  observer()->OnCompleted(
-      request, RequestNotifier::BackgroundSavePageResult::SAVE_FAILED);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-
-  notifier()->Reset();
-  observer()->OnCompleted(request,
-                          RequestNotifier::BackgroundSavePageResult::EXPIRED);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-
-  notifier()->Reset();
-  observer()->OnCompleted(
-      request, RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
-  EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
-            notifier()->last_notification_type());
-  EXPECT_EQ(kTestGuid, notifier()->download_item()->guid);
-  EXPECT_EQ(GURL(kTestUrl), notifier()->download_item()->url);
-  EXPECT_EQ(kTestCreationTime, notifier()->download_item()->start_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, NamespacesNotVisibleInUI) {
-  std::vector<std::string> name_spaces = {kBookmarkNamespace, kLastNNamespace,
-                                          kCCTNamespace, kDefaultNamespace};
-
-  for (auto name_space : name_spaces) {
-    ClientId invisible_client_id(name_space, kTestGuid);
-    SavePageRequest request(kTestOfflineId, GURL(kTestUrl), invisible_client_id,
-                            kTestCreationTime, kTestUserRequested);
-    observer()->OnAdded(request);
-    EXPECT_EQ(LastNotificationType::NONE, notifier()->last_notification_type());
-  }
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/downloads/download_ui_adapter.cc b/components/offline_pages/downloads/download_ui_adapter.cc
deleted file mode 100644
index df7be348..0000000
--- a/components/offline_pages/downloads/download_ui_adapter.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/downloads/download_ui_adapter.h"
-
-#include "base/bind.h"
-#include "base/guid.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_model.h"
-
-namespace offline_pages {
-
-namespace {
-const char kDownloadUIAdapterKey[] = "download-ui-adapter";
-}
-
-DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page)
-    : ui_item(base::MakeUnique<DownloadUIItem>(page)),
-      offline_id(page.offline_id) {}
-
-DownloadUIAdapter::ItemInfo::~ItemInfo() {}
-
-DownloadUIAdapter::DownloadUIAdapter(OfflinePageModel* model)
-    : model_(model),
-      state_(State::NOT_LOADED),
-      observers_count_(0),
-      weak_ptr_factory_(this) {
-}
-
-DownloadUIAdapter::~DownloadUIAdapter() { }
-
-// static
-DownloadUIAdapter* DownloadUIAdapter::FromOfflinePageModel(
-    OfflinePageModel* offline_page_model) {
-  DownloadUIAdapter* adapter = static_cast<DownloadUIAdapter*>(
-      offline_page_model->GetUserData(kDownloadUIAdapterKey));
-  if (!adapter) {
-    adapter = new DownloadUIAdapter(offline_page_model);
-    offline_page_model->SetUserData(kDownloadUIAdapterKey, adapter);
-  }
-  return adapter;
-}
-
-void DownloadUIAdapter::AddObserver(Observer* observer) {
-  DCHECK(observer);
-  if (observers_.HasObserver(observer))
-    return;
-  if (observers_count_ == 0)
-    LoadCache();
-  observers_.AddObserver(observer);
-  ++observers_count_;
-  // If the items are already loaded, post the notification right away.
-  // Don't just invoke it from here to avoid reentrancy in the client.
-  if (state_ == State::LOADED) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&DownloadUIAdapter::NotifyItemsLoaded,
-                              weak_ptr_factory_.GetWeakPtr(),
-                              base::Unretained(observer)));
-  }
-}
-
-void DownloadUIAdapter::RemoveObserver(Observer* observer) {
-  DCHECK(observer);
-  if (!observers_.HasObserver(observer))
-    return;
-  observers_.RemoveObserver(observer);
-  --observers_count_;
-  // Once the last observer is gone, clear cached data.
-  if (observers_count_ == 0)
-    ClearCache();
-}
-
-void DownloadUIAdapter::OfflinePageModelLoaded(OfflinePageModel* model) {
-  // This signal is not used here.
-}
-
-void DownloadUIAdapter::OfflinePageModelChanged(OfflinePageModel* model) {
-  DCHECK(model == model_);
-  model_->GetAllPages(
-      base::Bind(&DownloadUIAdapter::OnOfflinePagesChanged,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void DownloadUIAdapter::OfflinePageDeleted(
-  int64_t offline_id, const ClientId& client_id) {
-  if (!IsVisibleInUI(client_id))
-    return;
-  std::string guid = client_id.id;
-  DownloadUIItems::const_iterator it = items_.find(guid);
-  if (it == items_.end())
-    return;
-  items_.erase(it);
-  for (Observer& observer : observers_)
-    observer.ItemDeleted(guid);
-}
-
-std::vector<const DownloadUIItem*> DownloadUIAdapter::GetAllItems() const {
-  std::vector<const DownloadUIItem*> result;
-  for (const auto& item : items_)
-    result.push_back(item.second->ui_item.get());
-  return result;
-}
-
-const DownloadUIItem*
-    DownloadUIAdapter::GetItem(const std::string& guid) const {
-  DownloadUIItems::const_iterator it = items_.find(guid);
-  if (it == items_.end())
-    return nullptr;
-  return it->second->ui_item.get();
-}
-
-void DownloadUIAdapter::DeleteItem(const std::string& guid) {
-  // TODO(dimich): Also remove pending request from RequestQueue.
-  DownloadUIItems::const_iterator it = items_.find(guid);
-  if (it == items_.end())
-    return;
-
-  std::vector<int64_t> page_ids;
-  page_ids.push_back(it->second->offline_id);
-  model_->DeletePagesByOfflineId(
-      page_ids, base::Bind(&DownloadUIAdapter::OnDeletePagesDone,
-                           weak_ptr_factory_.GetWeakPtr()));
-}
-
-int64_t DownloadUIAdapter::GetOfflineIdByGuid(
-    const std::string& guid) const {
-  // TODO(dimich): when requests are also in the cache, filter them out.
-  // Requests do not yet have offline ID.
-  DownloadUIItems::const_iterator it = items_.find(guid);
-  if (it != items_.end())
-    return it->second->offline_id;
-  return 0;
-}
-
-// Note that several LoadCache calls may be issued before the async GetAllPages
-// comes back.
-void DownloadUIAdapter::LoadCache() {
-  // TODO(dimich): Add fetching from RequestQueue as well.
-  state_ = State::LOADING;
-  model_->GetAllPages(
-      base::Bind(&DownloadUIAdapter::OnOfflinePagesLoaded,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void DownloadUIAdapter::ClearCache() {
-  // Once loaded, this class starts to observe the model. Only remove observer
-  // if it was added.
-  if (state_ == State::LOADED)
-    model_->RemoveObserver(this);
-  items_.clear();
-  state_ = State::NOT_LOADED;
-}
-
-void DownloadUIAdapter::OnOfflinePagesLoaded(
-    const MultipleOfflinePageItemResult& pages) {
-  // If multiple observers register quickly, the cache might be already loaded
-  // by the previous LoadCache call. At the same time, if all observers already
-  // left, there is no reason to populate the cache.
-  if (state_ != State::LOADING)
-    return;
-  for (const auto& page : pages) {
-    if (IsVisibleInUI(page.client_id)) {
-      std::string guid = page.client_id.id;
-      DCHECK(items_.find(guid) == items_.end());
-      items_[guid] = base::MakeUnique<ItemInfo>(page);
-    }
-  }
-  model_->AddObserver(this);
-  state_ = State::LOADED;
-  for (Observer& observer : observers_)
-    observer.ItemsLoaded();
-}
-
-void DownloadUIAdapter::NotifyItemsLoaded(Observer* observer) {
-  if (observer && observers_.HasObserver(observer))
-    observer->ItemsLoaded();
-}
-
-// This method is only called by OPM when a single item added.
-// TODO(dimich): change OPM to have real OnPageAdded/OnPageUpdated and
-// simplify this code.
-void DownloadUIAdapter::OnOfflinePagesChanged(
-    const MultipleOfflinePageItemResult& pages) {
-  std::vector<std::string> added_guids;
-  for (const auto& page : pages) {
-    if (!IsVisibleInUI(page.client_id))  // Item should be filtered out.
-      continue;
-    const std::string& guid = page.client_id.id;
-    if (items_.find(guid) != items_.end())  // Item already exists.
-      continue;
-    items_[guid] = base::MakeUnique<ItemInfo>(page);
-    added_guids.push_back(guid);
-  }
-  for (auto& guid : added_guids) {
-    const DownloadUIItem& item = *(items_.find(guid)->second->ui_item.get());
-    for (Observer& observer : observers_)
-      observer.ItemAdded(item);
-  }
-}
-
-void DownloadUIAdapter::OnDeletePagesDone(DeletePageResult result) {
-  // TODO(dimich): Consider adding UMA to record user actions.
-}
-
-bool DownloadUIAdapter::IsVisibleInUI(const ClientId& client_id) {
-  const std::string& name_space = client_id.name_space;
-  return model_->GetPolicyController()->IsSupportedByDownload(name_space) &&
-         base::IsValidGUID(client_id.id);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/downloads/download_ui_adapter.h b/components/offline_pages/downloads/download_ui_adapter.h
deleted file mode 100644
index a8835fe..0000000
--- a/components/offline_pages/downloads/download_ui_adapter.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
-#define COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/observer_list.h"
-#include "base/supports_user_data.h"
-#include "components/offline_pages/downloads/download_ui_item.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_types.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-// C++ side of the UI Adapter. Mimics DownloadManager/Item/History (since we
-// share UI with Downloads).
-// An instance of this class is owned by OfflinePageModel and is shared between
-// UI components if needed. It manages the cache of DownloadUIItems, so after
-// initial load the UI components can synchronously pull the whoel list or any
-// item by its guid.
-class DownloadUIAdapter : public OfflinePageModel::Observer,
-                          public base::SupportsUserData::Data {
- public:
-  // Observer, normally implemented by UI or a Bridge.
-  class Observer {
-   public:
-    // Invoked when UI items are loaded. GetAllItems/GetItem can now be used.
-    // Must be listened for in order to start getting the items.
-    // If the items are already loaded by the time observer is added, this
-    // callback will be posted right away.
-    virtual void ItemsLoaded() = 0;
-
-    // Invoked when the UI Item was added, usually as a request to download.
-    virtual void ItemAdded(const DownloadUIItem& item) = 0;
-
-    // Invoked when the UI Item was updated. Only guid of the item is guaranteed
-    // to survive the update, all other fields can change.
-    virtual void ItemUpdated(const DownloadUIItem& item) = 0;
-
-    // Invoked when the UI Item was deleted. At this point, only guid remains.
-    virtual void ItemDeleted(const std::string& guid) = 0;
-
-   protected:
-    virtual ~Observer() = default;
-  };
-
-  explicit DownloadUIAdapter(OfflinePageModel* model);
-  ~DownloadUIAdapter() override;
-
-  static DownloadUIAdapter* FromOfflinePageModel(
-      OfflinePageModel* offline_page_model);
-
-  // Checks a client ID for proper namespace and ID format to be shown in the
-  // Downloads Home UI.
-  bool IsVisibleInUI(const ClientId& page);
-
-  // This adapter is potentially shared by UI elements, each of which adds
-  // itself as an observer.
-  // When the last observer si removed, cached list of items is destroyed and
-  // next time the initial loading will take longer.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
-  // Returns all UI items. The list contains references to items in the cache
-  // and has to be used synchronously.
-  std::vector<const DownloadUIItem*> GetAllItems() const;
-  // May return nullptr if item with specified guid does not exist.
-  const DownloadUIItem* GetItem(const std::string& guid) const;
-
-  // Commands from UI. Start async operations, result is observable
-  // via Observer or directly by the user (as in 'open').
-  void DeleteItem(const std::string& guid);
-  int64_t GetOfflineIdByGuid(const std::string& guid) const;
-
-  // OfflinePageModel::Observer
-  void OfflinePageModelLoaded(OfflinePageModel* model) override;
-  void OfflinePageModelChanged(OfflinePageModel* model) override;
-  void OfflinePageDeleted(int64_t offline_id,
-                          const ClientId& client_id) override;
-
- private:
-  enum class State {
-    NOT_LOADED,
-    LOADING,
-    LOADED
-  };
-
-  struct ItemInfo {
-    explicit ItemInfo(const OfflinePageItem& page);
-    ~ItemInfo();
-
-    std::unique_ptr<DownloadUIItem> ui_item;
-    // Additional cached data, not exposed to UI through DownloadUIItem.
-    int64_t offline_id;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(ItemInfo);
-  };
-
-  typedef std::map<std::string, std::unique_ptr<ItemInfo>> DownloadUIItems;
-
-  void LoadCache();
-  void ClearCache();
-
-  // Task callbacks.
-  void OnOfflinePagesLoaded(const MultipleOfflinePageItemResult& pages);
-  void NotifyItemsLoaded(Observer* observer);
-  void OnOfflinePagesChanged(const MultipleOfflinePageItemResult& pages);
-  void OnDeletePagesDone(DeletePageResult result);
-
-  // Always valid, this class is a member of the model.
-  OfflinePageModel* model_;
-
-  State state_;
-
-  // The cache of UI items. The key is DownloadUIItem.guid.
-  DownloadUIItems items_;
-
-  // The observers.
-  base::ObserverList<Observer> observers_;
-  int observers_count_;
-
-  base::WeakPtrFactory<DownloadUIAdapter> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(DownloadUIAdapter);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGE_DOWNLOADS_DOWNLOAD_UI_ADAPTER_H_
diff --git a/components/offline_pages/downloads/download_ui_adapter_unittest.cc b/components/offline_pages/downloads/download_ui_adapter_unittest.cc
deleted file mode 100644
index ef21b92..0000000
--- a/components/offline_pages/downloads/download_ui_adapter_unittest.cc
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/downloads/download_ui_adapter.h"
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/stub_offline_page_model.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-// Constants for a test OfflinePageItem.
-static const int kTestOfflineId1 = 1;
-static const int kTestOfflineId2 = 2;
-static const int kTestOfflineId3 = 3;
-static const char kTestUrl[] = "http://foo.com/bar.mhtml";
-static const char kTestGuid1[] =   "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
-static const char kTestGuid2[] =   "cccccccc-cccc-4ccc-0ccc-ccccccccccc2";
-static const char kTestBadGuid[] = "ccccccc-cccc-0ccc-0ccc-ccccccccccc0";
-static const ClientId kTestClientIdOtherNamespace(kLastNNamespace, kTestGuid1);
-static const ClientId kTestClientIdOtherGuid(kLastNNamespace, kTestBadGuid);
-static const ClientId kTestClientId1(kAsyncNamespace, kTestGuid1);
-static const ClientId kTestClientId2(kAsyncNamespace, kTestGuid2);
-static const base::FilePath kTestFilePath =
-    base::FilePath(FILE_PATH_LITERAL("foo/bar.mhtml"));
-static const int kFileSize = 1000;
-static const base::Time kTestCreationTime = base::Time::Now();
-static const base::string16 kTestTitle = base::ASCIIToUTF16("test title");
-}  // namespace
-
-// Mock OfflinePageModel for testing the SavePage calls.
-class MockOfflinePageModel : public StubOfflinePageModel {
- public:
-  MockOfflinePageModel(base::TestMockTimeTaskRunner* task_runner)
-      : observer_(nullptr),
-        task_runner_(task_runner),
-        policy_controller_(new ClientPolicyController()) {
-    adapter.reset(new DownloadUIAdapter(this));
-    // Add one page.
-    OfflinePageItem page(GURL(kTestUrl),
-                         kTestOfflineId1,
-                         kTestClientId1,
-                         kTestFilePath,
-                         kFileSize,
-                         kTestCreationTime);
-    page.title = kTestTitle;
-    pages[kTestOfflineId1] = page;
-  }
-
-  ~MockOfflinePageModel() override {}
-
-  // OfflinePageModel overrides.
-  void AddObserver(Observer* observer) override {
-    EXPECT_TRUE(observer != nullptr);
-    observer_ = observer;
-  }
-
-  void RemoveObserver(Observer* observer) override {
-    EXPECT_TRUE(observer != nullptr);
-    EXPECT_EQ(observer, observer_);
-    observer_ = nullptr;
-  }
-
-  // PostTask instead of just running callback to simpulate the real class.
-  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override {
-    task_runner_->PostTask(
-        FROM_HERE, base::Bind(&MockOfflinePageModel::GetAllPagesImpl,
-        base::Unretained(this), callback));
-  }
-
-  void GetAllPagesImpl(const MultipleOfflinePageItemCallback& callback) {
-    std::vector<OfflinePageItem> result;
-    for (const auto& page : pages)
-      result.push_back(page.second);
-    callback.Run(result);
-  }
-
-  void DeletePageAndNotifyAdapter(const std::string& guid) {
-    for(const auto& page : pages) {
-      if (page.second.client_id.id == guid) {
-        observer_->OfflinePageDeleted(page.second.offline_id,
-                                      page.second.client_id);
-        pages.erase(page.first);
-        return;
-      }
-    }
-  }
-
-  void AddPageAndNotifyAdapter(const OfflinePageItem& page) {
-    EXPECT_EQ(pages.end(), pages.find(page.offline_id));
-    pages[page.offline_id] = page;
-    observer_->OfflinePageModelChanged(this);
-  }
-
-  ClientPolicyController* GetPolicyController() override {
-    return policy_controller_.get();
-  }
-
-  // Normally, OfflinePageModel owns this adapter, so lets test it this way.
-  std::unique_ptr<DownloadUIAdapter> adapter;
-
-  std::map<int64_t, OfflinePageItem> pages;
-
- private:
-  OfflinePageModel::Observer* observer_;
-  base::TestMockTimeTaskRunner* task_runner_;
-  // Normally owned by OfflinePageModel.
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockOfflinePageModel);
-};
-
-class DownloadUIAdapterTest
-    : public testing::Test,
-      public DownloadUIAdapter::Observer {
- public:
-  DownloadUIAdapterTest();
-  ~DownloadUIAdapterTest() override;
-
-  // testing::Test
-  void SetUp() override;
-
-  // DownloadUIAdapter::Observer
-  void ItemsLoaded() override;
-  void ItemAdded(const DownloadUIItem& item) override;
-  void ItemUpdated(const DownloadUIItem& item) override;
-  void ItemDeleted(const std::string& guid) override;
-
-  // Runs until all of the tasks that are not delayed are gone from the task
-  // queue.
-  void PumpLoop();
-
-  bool items_loaded;
-  std::vector<std::string> added_guids, updated_guids, deleted_guids;
-  std::unique_ptr<MockOfflinePageModel> model;
-
- private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-};
-
-DownloadUIAdapterTest::DownloadUIAdapterTest()
-    : items_loaded(false),
-      task_runner_(new base::TestMockTimeTaskRunner) {
-}
-
-DownloadUIAdapterTest::~DownloadUIAdapterTest() {
-}
-
-void DownloadUIAdapterTest::SetUp() {
-  model.reset(new MockOfflinePageModel(task_runner_.get()));
-  model->adapter->AddObserver(this);
-}
-
-void DownloadUIAdapterTest::ItemsLoaded() {
-  items_loaded = true;
-}
-
-void DownloadUIAdapterTest::ItemAdded(const DownloadUIItem& item) {
-  added_guids.push_back(item.guid);
-}
-
-void DownloadUIAdapterTest::ItemUpdated(const DownloadUIItem& item) {
-  updated_guids.push_back(item.guid);
-}
-
-void DownloadUIAdapterTest::ItemDeleted(const std::string& guid) {
-  deleted_guids.push_back(guid);
-}
-
-void DownloadUIAdapterTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-TEST_F(DownloadUIAdapterTest, InitialLoad) {
-  EXPECT_NE(nullptr, model->adapter);
-  EXPECT_FALSE(items_loaded);
-  PumpLoop();
-  EXPECT_TRUE(items_loaded);
-  const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1);
-  EXPECT_NE(nullptr, item);
-}
-
-TEST_F(DownloadUIAdapterTest, InitialItemConversion) {
-  EXPECT_EQ(1UL, model->pages.size());
-  EXPECT_EQ(kTestGuid1, model->pages[kTestOfflineId1].client_id.id);
-  PumpLoop();
-  const DownloadUIItem* item = model->adapter->GetItem(kTestGuid1);
-  EXPECT_EQ(kTestGuid1, item->guid);
-  EXPECT_EQ(kTestUrl, item->url.spec());
-  EXPECT_EQ(kTestFilePath, item->target_path);
-  EXPECT_EQ(kTestCreationTime, item->start_time);
-  EXPECT_EQ(kFileSize, item->total_bytes);
-  EXPECT_EQ(kTestTitle, item->title);
-}
-
-TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) {
-  PumpLoop();
-  // Add page, notify adapter.
-  OfflinePageItem page(GURL(kTestUrl),
-                       kTestOfflineId2,
-                       kTestClientId2,
-                       base::FilePath(kTestFilePath),
-                       kFileSize,
-                       kTestCreationTime);
-  model->AddPageAndNotifyAdapter(page);
-  PumpLoop();
-  EXPECT_EQ(1UL, added_guids.size());
-  EXPECT_EQ(kTestGuid2, added_guids[0]);
-  // Remove pages, notify adapter.
-  model->DeletePageAndNotifyAdapter(kTestGuid1);
-  model->DeletePageAndNotifyAdapter(kTestGuid2);
-  PumpLoop();
-  EXPECT_EQ(2UL, deleted_guids.size());
-  EXPECT_EQ(kTestGuid1, deleted_guids[0]);
-  EXPECT_EQ(kTestGuid2, deleted_guids[1]);
-}
-
-TEST_F(DownloadUIAdapterTest, ItemWithWrongNamespace) {
-  PumpLoop();
-  OfflinePageItem page1(GURL(kTestUrl),
-                        kTestOfflineId2,
-                        kTestClientIdOtherNamespace,
-                        base::FilePath(kTestFilePath),
-                        kFileSize,
-                        kTestCreationTime);
-  model->AddPageAndNotifyAdapter(page1);
-  PumpLoop();
-  // Should not add the page with wrong namespace.
-  EXPECT_EQ(0UL, added_guids.size());
-
-  OfflinePageItem page2(GURL(kTestUrl),
-                        kTestOfflineId3,
-                        kTestClientIdOtherGuid,
-                        base::FilePath(kTestFilePath),
-                        kFileSize,
-                        kTestCreationTime);
-  model->AddPageAndNotifyAdapter(page2);
-  PumpLoop();
-  // Should not add the page with wrong guid.
-  EXPECT_EQ(0UL, added_guids.size());
-}
-
-TEST_F(DownloadUIAdapterTest, ItemUpdated) {
-  PumpLoop();
-  // Clear the initial page and replace it with updated one.
-  model->pages.clear();
-  // Add page with the same offline_id/guid, notify adapter.
-  // This should generate 'updated' notification.
-  OfflinePageItem page1(GURL(kTestUrl),
-                        kTestOfflineId1,
-                        kTestClientId1,
-                        base::FilePath(kTestFilePath),
-                        kFileSize,
-                        kTestCreationTime);
-  // Add a new page which did not exist before.
-  OfflinePageItem page2(GURL(kTestUrl),
-                        kTestOfflineId2,
-                        kTestClientId2,
-                        base::FilePath(kTestFilePath),
-                        kFileSize,
-                        kTestCreationTime);
-  model->AddPageAndNotifyAdapter(page1);
-  model->AddPageAndNotifyAdapter(page2);
-  PumpLoop();
-  EXPECT_EQ(1UL, added_guids.size());
-  EXPECT_EQ(kTestGuid2, added_guids[0]);
-  // TODO(dimich): we currently don't report updated items since OPM doesn't
-  // have support for that. Add as needed, this will have to be updated when
-  // support is added.
-  EXPECT_EQ(0UL, updated_guids.size());
-}
-
-TEST_F(DownloadUIAdapterTest, NoHangingLoad) {
-  EXPECT_NE(nullptr, model->adapter);
-  EXPECT_FALSE(items_loaded);
-  // Removal of last observer causes cache unload of not-yet-loaded cache.
-  model->adapter->RemoveObserver(this);
-  // This will complete async fetch of items, but...
-  PumpLoop();
-  // items should not be loaded when there is no observers!
-  EXPECT_FALSE(items_loaded);
-  // This should not crash.
-  model->adapter->AddObserver(this);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/downloads/download_ui_item.cc b/components/offline_pages/downloads/download_ui_item.cc
deleted file mode 100644
index 13af20b..0000000
--- a/components/offline_pages/downloads/download_ui_item.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/downloads/download_ui_item.h"
-
-#include "components/offline_pages/background/save_page_request.h"
-#include "components/offline_pages/offline_page_item.h"
-
-namespace offline_pages {
-
-DownloadUIItem::DownloadUIItem()
-    : total_bytes(0) {
-}
-
-DownloadUIItem::DownloadUIItem(const OfflinePageItem& page)
-    : guid(page.client_id.id),
-      url(page.url),
-      title(page.title),
-      target_path(page.file_path),
-      start_time(page.creation_time),
-      total_bytes(page.file_size) {}
-
-DownloadUIItem::DownloadUIItem(const SavePageRequest& request)
-    : guid(request.client_id().id),
-      url(request.url()),
-      start_time(request.creation_time()),
-      total_bytes(-1L) {}
-
-DownloadUIItem::DownloadUIItem(const DownloadUIItem& other)
-    : guid(other.guid),
-      url(other.url),
-      title(other.title),
-      target_path(other.target_path),
-      start_time(other.start_time),
-      total_bytes(other.total_bytes) {}
-
-DownloadUIItem::~DownloadUIItem() {
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/downloads/download_ui_item.h b/components/offline_pages/downloads/download_ui_item.h
deleted file mode 100644
index 801c1ef..0000000
--- a/components/offline_pages/downloads/download_ui_item.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/time/time.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-struct OfflinePageItem;
-class SavePageRequest;
-
-struct DownloadUIItem {
- public:
-  DownloadUIItem();
-  explicit DownloadUIItem(const OfflinePageItem& page);
-  explicit DownloadUIItem(const SavePageRequest& request);
-  DownloadUIItem(const DownloadUIItem& other);
-  ~DownloadUIItem();
-
-  // Unique id.
-  std::string guid;
-
-  // The URL of the captured page.
-  GURL url;
-
-  // The Title of the captured page, if any. It can be empty string either
-  // because the page is not yet fully loaded, or because it doesn't have any.
-  base::string16 title;
-
-  // The file path to the archive with a local copy of the page.
-  base::FilePath target_path;
-
-  // The time when the offline archive was created.
-  base::Time start_time;
-
-  // The size of the offline copy.
-  int64_t total_bytes;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_DOWNLOAD_UI_ITEM_H_
diff --git a/components/offline_pages/downloads/offline_page_download_notifier.h b/components/offline_pages/downloads/offline_page_download_notifier.h
deleted file mode 100644
index 7492185..0000000
--- a/components/offline_pages/downloads/offline_page_download_notifier.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-
-#include "components/offline_pages/downloads/download_ui_item.h"
-
-namespace offline_pages {
-
-struct OfflinePageDownloadNotifier {
- public:
-  virtual ~OfflinePageDownloadNotifier() = default;
-
-  // Reports that |item| has completed successfully.
-  virtual void NotifyDownloadSuccessful(const DownloadUIItem& item) = 0;
-
-  // Reports that |item| has completed unsuccessfully.
-  virtual void NotifyDownloadFailed(const DownloadUIItem& item) = 0;
-
-  // Reports that |item| is active and possibly making progress.
-  virtual void NotifyDownloadProgress(const DownloadUIItem& item) = 0;
-
-  // Reports that |item| has been paused (and so it is not active).
-  virtual void NotifyDownloadPaused(const DownloadUIItem& item) = 0;
-
-  // Reports that any progress on |item| has been interrupted. It is pending
-  // or available for another attempt when conditions allows.
-  virtual void NotifyDownloadInterrupted(const DownloadUIItem& item) = 0;
-
-  // Reports that |item| has been canceled.
-  virtual void NotifyDownloadCanceled(const DownloadUIItem& item) = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
diff --git a/components/offline_pages/offline_event_logger.cc b/components/offline_pages/offline_event_logger.cc
deleted file mode 100644
index 810c2a6..0000000
--- a/components/offline_pages/offline_event_logger.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "components/offline_pages/offline_event_logger.h"
-
-namespace offline_pages {
-
-OfflineEventLogger::OfflineEventLogger()
-    : activities_(kMaxLogCount), is_logging_(false) {}
-
-OfflineEventLogger::~OfflineEventLogger() {}
-
-void OfflineEventLogger::SetIsLogging(bool is_logging) {
-  is_logging_ = is_logging;
-}
-
-bool OfflineEventLogger::GetIsLogging() {
-  return is_logging_;
-}
-
-void OfflineEventLogger::Clear() {
-  activities_.clear();
-}
-
-void OfflineEventLogger::RecordActivity(const std::string& activity) {
-  if (!is_logging_) {
-    return;
-  }
-  if (activities_.size() == kMaxLogCount)
-    activities_.pop_back();
-
-  base::Time::Exploded current_time;
-  base::Time::Now().LocalExplode(&current_time);
-
-  std::string date_string = base::StringPrintf(
-      "%d %02d %02d %02d:%02d:%02d",
-      current_time.year,
-      current_time.month,
-      current_time.day_of_month,
-      current_time.hour,
-      current_time.minute,
-      current_time.second);
-
-  activities_.push_front(date_string + ": " + activity);
-}
-
-void OfflineEventLogger::GetLogs(std::vector<std::string>* records) {
-  DCHECK(records);
-  for (std::deque<std::string>::iterator it = activities_.begin();
-       it != activities_.end(); it++) {
-    if (!it->empty())
-      records->push_back(*it);
-  }
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_event_logger.h b/components/offline_pages/offline_event_logger.h
deleted file mode 100644
index 95ac36c..0000000
--- a/components/offline_pages/offline_event_logger.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
-
-#include <deque>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-
-namespace offline_pages {
-
-// Maximum number of recorded Logs to keep track of at any moment.
-static const size_t kMaxLogCount = 50;
-
-// Facilitates the logging of events. Subclasses should create methods that
-// call RecordActivity to write into the log. |SetIsLogging|, |GetLogs|, and
-// |Clear| are called from the chrome://offline-internals page.
-//
-// Logging should be done by calling the corresponding subclass methods when
-// a loggable event occurs (i.e. when status has changed for a save request
-// or when an offlined page has been accessed/saved).
-//
-// This log only keeps track of the last |kMaxLogCount| events.
-class OfflineEventLogger {
- public:
-  OfflineEventLogger();
-
-  ~OfflineEventLogger();
-
-  // Clears the recorded activities.
-  void Clear();
-
-  // Turns logging on/off.
-  void SetIsLogging(bool is_logging);
-
-  // Returns whether we are currently logging.
-  bool GetIsLogging();
-
-  // Dumps the current activity list into |records|.
-  void GetLogs(std::vector<std::string>* records);
-
- protected:
-  // Write the activity into the cycling log if we are currently logging.
-  void RecordActivity(const std::string& activity);
-
- private:
-  // Recorded offline page activities.
-  std::deque<std::string> activities_;
-
-  // Whether we are currently recording logs or not.
-  bool is_logging_;
-};
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_EVENT_LOGGER_H_
diff --git a/components/offline_pages/offline_page_archiver.h b/components/offline_pages/offline_page_archiver.h
deleted file mode 100644
index ca605be..0000000
--- a/components/offline_pages/offline_page_archiver.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
-
-#include <stdint.h>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/strings/string16.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-// Interface of a class responsible for creation of the archive for offline use.
-//
-// Archiver will be implemented by embedder and may have additional methods that
-// are not interesting from the perspective of OfflinePageModel. Example of such
-// extra information or capability is a way to enumerate available WebContents
-// to find the one that needs to be used to create archive (or to map it to the
-// URL passed in CreateArchive in some other way).
-//
-// Archiver will be responsible for naming the file that is being saved (it has
-// URL, title and the whole page content at its disposal). For that it should be
-// also configured with the path where the archives are stored.
-//
-// Archiver should be able to archive multiple pages in parallel, as these are
-// asynchronous calls carried out by some other component.
-//
-// If archiver gets two consecutive requests to archive the same page (may be
-// run in parallel) it can generate 2 different names for files and save the
-// same page separately, as if these were 2 completely unrelated pages. It is up
-// to the caller (e.g. OfflinePageModel) to make sure that situation like that
-// does not happen.
-//
-// If the page is not completely loaded, it is up to the implementation of the
-// archiver whether to respond with ERROR_CONTENT_UNAVAILBLE, wait longer to
-// actually snapshot a complete page, or snapshot whatever is available at that
-// point in time (what the user sees).
-//
-// TODO(fgorski): Add ability to delete archive.
-// TODO(fgorski): Add ability to check that archive exists.
-// TODO(fgorski): Add ability to refresh an existing archive in one step.
-// TODO(fgorski): Add ability to identify all of the archives in the directory,
-// to enable to model to reconcile the archives.
-class OfflinePageArchiver {
- public:
-  // Results of the archive creation.
-  enum class ArchiverResult {
-    SUCCESSFULLY_CREATED,           // Archive created successfully.
-    ERROR_DEVICE_FULL,              // Cannot save the archive - device is full.
-    ERROR_CANCELED,                 // Caller canceled the request.
-    ERROR_CONTENT_UNAVAILABLE,      // Content to archive is not available.
-    ERROR_ARCHIVE_CREATION_FAILED,  // Creation of archive failed.
-    ERROR_SECURITY_CERTIFICATE,     // Page was loaded on secure connection, but
-                                    // there was a security error.
-  };
-
-  typedef base::Callback<void(OfflinePageArchiver* /* archiver */,
-                              ArchiverResult /* result */,
-                              const GURL& /* url */,
-                              const base::FilePath& /* file_path */,
-                              const base::string16& /* title */,
-                              int64_t /* file_size */)>
-      CreateArchiveCallback;
-
-  virtual ~OfflinePageArchiver() {}
-
-  // Starts creating the archive in the |archives_dir| with |archive_id| added
-  // to the archive filename. Once archive is created |callback| will be called
-  // with the result and additional information.
-  virtual void CreateArchive(const base::FilePath& archives_dir,
-                             int64_t archive_id,
-                             const CreateArchiveCallback& callback) = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ARCHIVER_H_
diff --git a/components/offline_pages/offline_page_client_policy.h b/components/offline_pages/offline_page_client_policy.h
deleted file mode 100644
index b52d51d..0000000
--- a/components/offline_pages/offline_page_client_policy.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/time/time.h"
-
-namespace offline_pages {
-
-static const size_t kUnlimitedPages = 0;
-
-// The struct describing the lifetime policy of offline pages.
-// The following behaviors are controlled by policy:
-//    a. Persistency of the offline page.
-//    b. Expiration time of an offline page
-//    c. Limit of number of pages offline.
-struct LifetimePolicy {
-  // Type of the client, indicating where the archived page would be saved
-  // and whether it could be kept indefinitely.
-  enum class LifetimeType {
-    TEMPORARY,
-    PERSISTENT,
-  };
-
-  // Type of the page generated by the client.
-  LifetimeType lifetime_type;
-
-  // The time after which the page expires.
-  // A TimeDelta of 0 means no expiration.
-  base::TimeDelta expiration_period;
-
-  // The maximum number of pages allowed to be saved by the namespace.
-  // kUnlimitedPages (defined above) means no limit set.
-  size_t page_limit;
-
-  LifetimePolicy(LifetimeType init_lifetime_type, size_t init_page_limit)
-      : lifetime_type(init_lifetime_type),
-        expiration_period(base::TimeDelta::FromDays(0)),
-        page_limit(init_page_limit){};
-};
-
-// The struct describing feature set of the offline pages.
-struct FeaturePolicy {
-  // Whether pages are shown in download ui.
-  bool is_supported_by_download;
-  // Whether pages are shown in recent tabs ui.
-  bool is_supported_by_recent_tabs;
-  // Whether pages should only be viewed in the tab they were generated in.
-  bool only_shown_in_original_tab;
-  // Whether pages are removed on user-initiated cache reset. Defaults to true.
-  bool is_removed_on_cache_reset;
-
-  FeaturePolicy()
-      : is_supported_by_download(false),
-        is_supported_by_recent_tabs(false),
-        only_shown_in_original_tab(false),
-        is_removed_on_cache_reset(true){};
-};
-
-// The struct describing policies for various namespaces (Bookmark, Last-N etc.)
-// used by offline page model. The name_space is supposed to be key, so that
-// it's sufficient to compare name_space only when doing comparisons.
-struct OfflinePageClientPolicy {
-  // Namespace to which the policy applied.
-  std::string name_space;
-
-  // Policy to control the lifetime of a page generated by this namespace.
-  LifetimePolicy lifetime_policy;
-
-  // How many pages for the same online URL can be stored at any time.
-  // kUnlimitedPages means there's no limit.
-  size_t pages_allowed_per_url;
-
-  FeaturePolicy feature_policy;
-
-  OfflinePageClientPolicy(std::string namespace_val,
-                          LifetimePolicy lifetime_policy_val,
-                          size_t pages_allowed_per_url_val,
-                          FeaturePolicy feature_policy_val)
-      : name_space(namespace_val),
-        lifetime_policy(lifetime_policy_val),
-        pages_allowed_per_url(pages_allowed_per_url_val),
-        feature_policy(feature_policy_val){};
-
-  OfflinePageClientPolicy(std::string namespace_val,
-                          LifetimePolicy lifetime_policy_val,
-                          size_t pages_allowed_per_url_val)
-      : OfflinePageClientPolicy(namespace_val,
-                                lifetime_policy_val,
-                                pages_allowed_per_url_val,
-                                FeaturePolicy()){};
-};
-
-class OfflinePageClientPolicyBuilder {
- public:
-  OfflinePageClientPolicyBuilder(const std::string& name_space,
-                                 LifetimePolicy::LifetimeType lifetime_type,
-                                 size_t page_limit,
-                                 size_t pages_allowed_per_url)
-      : policy_(
-            OfflinePageClientPolicy(name_space,
-                                    LifetimePolicy(lifetime_type, page_limit),
-                                    pages_allowed_per_url)){};
-
-  ~OfflinePageClientPolicyBuilder() {}
-
-  // Calling build does not reset the object inside.
-  const OfflinePageClientPolicy Build() const { return policy_; }
-
-  OfflinePageClientPolicyBuilder& SetExpirePeriod(
-      const base::TimeDelta& expire_period) {
-    policy_.lifetime_policy.expiration_period = expire_period;
-    return *this;
-  }
-
-  OfflinePageClientPolicyBuilder& SetIsSupportedByDownload(
-      const bool is_downloaded) {
-    policy_.feature_policy.is_supported_by_download = is_downloaded;
-    return *this;
-  }
-
-  OfflinePageClientPolicyBuilder& SetIsSupportedByRecentTabs(
-      const bool is_recent_tabs) {
-    policy_.feature_policy.is_supported_by_recent_tabs = is_recent_tabs;
-    return *this;
-  }
-
-  OfflinePageClientPolicyBuilder& SetIsRemovedOnCacheReset(
-      const bool removed_on_cache_reset) {
-    policy_.feature_policy.is_removed_on_cache_reset = removed_on_cache_reset;
-    return *this;
-  }
-
-  OfflinePageClientPolicyBuilder& SetIsOnlyShownInOriginalTab(
-      const bool only_shown_in_original_tab) {
-    policy_.feature_policy.only_shown_in_original_tab =
-        only_shown_in_original_tab;
-    return *this;
-  }
-
- private:
-  OfflinePageClientPolicy policy_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageClientPolicyBuilder);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_CLIENT_POLICY_H_
diff --git a/components/offline_pages/offline_page_feature.cc b/components/offline_pages/offline_page_feature.cc
deleted file mode 100644
index 99c0555..0000000
--- a/components/offline_pages/offline_page_feature.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_feature.h"
-
-#include <string>
-
-#include "base/feature_list.h"
-
-namespace offline_pages {
-
-const base::Feature kOfflineBookmarksFeature {
-   "OfflineBookmarks", base::FEATURE_DISABLED_BY_DEFAULT
-};
-
-const base::Feature kOffliningRecentPagesFeature {
-   "OfflineRecentPages", base::FEATURE_DISABLED_BY_DEFAULT
-};
-
-const base::Feature kOfflinePagesCTFeature {
-   "OfflinePagesCT", base::FEATURE_ENABLED_BY_DEFAULT
-};
-
-const base::Feature kOfflinePagesSharingFeature{
-    "OfflinePagesSharing", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature{
-    "OfflinePagesSvelteConcurrentLoading", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kBackgroundLoaderForDownloadsFeature{
-    "BackgroundLoadingForDownloads", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kOfflinePagesAsyncDownloadFeature {
-   "OfflinePagesAsyncDownload", base::FEATURE_DISABLED_BY_DEFAULT
-};
-
-const base::Feature kNewBackgroundLoaderFeature {
-    "BackgroundLoader", base::FEATURE_DISABLED_BY_DEFAULT
-};
-
-bool IsOfflineBookmarksEnabled() {
-  return base::FeatureList::IsEnabled(kOfflineBookmarksFeature);
-}
-
-bool IsOffliningRecentPagesEnabled() {
-  return  base::FeatureList::IsEnabled(kOffliningRecentPagesFeature);
-}
-
-bool IsOfflinePagesSvelteConcurrentLoadingEnabled() {
-  return base::FeatureList::IsEnabled(
-      kOfflinePagesSvelteConcurrentLoadingFeature);
-}
-
-bool IsOfflinePagesCTEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesCTFeature);
-}
-
-bool IsOfflinePagesSharingEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesSharingFeature);
-}
-
-bool IsBackgroundLoaderForDownloadsEnabled() {
-  return base::FeatureList::IsEnabled(kBackgroundLoaderForDownloadsFeature);
-}
-
-bool IsOfflinePagesAsyncDownloadEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesAsyncDownloadFeature);
-}
-
-bool ShouldUseNewBackgroundLoader() {
-  return base::FeatureList::IsEnabled(kNewBackgroundLoaderFeature);
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_feature.h b/components/offline_pages/offline_page_feature.h
deleted file mode 100644
index 63c4134..0000000
--- a/components/offline_pages/offline_page_feature.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
-
-#include "base/feature_list.h"
-#include "build/build_config.h"
-
-namespace offline_pages {
-
-extern const base::Feature kOfflineBookmarksFeature;
-extern const base::Feature kOffliningRecentPagesFeature;
-extern const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature;
-extern const base::Feature kOfflinePagesCTFeature;
-extern const base::Feature kOfflinePagesSharingFeature;
-extern const base::Feature kBackgroundLoaderForDownloadsFeature;
-extern const base::Feature kOfflinePagesAsyncDownloadFeature;
-extern const base::Feature kNewBackgroundLoaderFeature;
-
-// Returns true if saving bookmarked pages for offline viewing is enabled.
-bool IsOfflineBookmarksEnabled();
-
-// Returns true if offlining of recent pages (aka 'Last N pages') is enabled.
-bool IsOffliningRecentPagesEnabled();
-
-// Returns true if offline CT features are enabled.  See crbug.com/620421.
-bool IsOfflinePagesCTEnabled();
-
-// Returns true if offline page sharing is enabled.
-bool IsOfflinePagesSharingEnabled();
-
-// Returns true if saving a foreground tab that is taking too long using the
-// background scheduler is enabled.
-bool IsBackgroundLoaderForDownloadsEnabled();
-
-// Returns true if concurrent background loading is enabled for svelte.
-bool IsOfflinePagesSvelteConcurrentLoadingEnabled();
-
-// Returns true if downloading a page asynchonously is enabled.
-bool IsOfflinePagesAsyncDownloadEnabled();
-
-// Returns true if we should use background loader rather than prerenderer
-// to offline pages.
-bool ShouldUseNewBackgroundLoader();
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
diff --git a/components/offline_pages/offline_page_item.cc b/components/offline_pages/offline_page_item.cc
deleted file mode 100644
index a0f0cc2..0000000
--- a/components/offline_pages/offline_page_item.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_item.h"
-
-namespace offline_pages {
-
-ClientId::ClientId() : name_space(""), id("") {}
-
-ClientId::ClientId(std::string name_space, std::string id)
-    : name_space(name_space), id(id) {}
-
-bool ClientId::operator==(const ClientId& client_id) const {
-  return name_space == client_id.name_space && id == client_id.id;
-}
-
-bool ClientId::operator<(const ClientId& client_id) const {
-  if (name_space == client_id.name_space)
-    return (id < client_id.id);
-
-  return name_space < client_id.name_space;
-}
-
-OfflinePageItem::OfflinePageItem()
-    : file_size(0), access_count(0), flags(NO_FLAG) {}
-
-OfflinePageItem::OfflinePageItem(const GURL& url,
-                                 int64_t offline_id,
-                                 const ClientId& client_id,
-                                 const base::FilePath& file_path,
-                                 int64_t file_size)
-    : url(url),
-      offline_id(offline_id),
-      client_id(client_id),
-      file_path(file_path),
-      file_size(file_size),
-      access_count(0),
-      flags(NO_FLAG) {}
-
-OfflinePageItem::OfflinePageItem(const GURL& url,
-                                 int64_t offline_id,
-                                 const ClientId& client_id,
-                                 const base::FilePath& file_path,
-                                 int64_t file_size,
-                                 const base::Time& creation_time)
-    : url(url),
-      offline_id(offline_id),
-      client_id(client_id),
-      file_path(file_path),
-      file_size(file_size),
-      creation_time(creation_time),
-      last_access_time(creation_time),
-      access_count(0),
-      flags(NO_FLAG) {}
-
-OfflinePageItem::OfflinePageItem(const OfflinePageItem& other) = default;
-
-OfflinePageItem::~OfflinePageItem() {
-}
-
-bool OfflinePageItem::operator==(const OfflinePageItem& other) const {
-  return url == other.url &&
-         offline_id == other.offline_id &&
-         client_id == other.client_id &&
-         file_path == other.file_path &&
-         creation_time == other.creation_time &&
-         last_access_time == other.last_access_time &&
-         access_count == other.access_count &&
-         title == other.title &&
-         flags == other.flags &&
-         original_url == other.original_url;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_item.h b/components/offline_pages/offline_page_item.h
deleted file mode 100644
index 34c586c..0000000
--- a/components/offline_pages/offline_page_item.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-struct ClientId {
-  // The namespace for the id (of course 'namespace' is a reserved word, so...)
-  std::string name_space;
-  // The id in the client's namespace.  Opaque to us.
-  std::string id;
-
-  ClientId();
-  ClientId(std::string name_space, std::string id);
-
-  bool operator==(const ClientId& client_id) const;
-
-  bool operator<(const ClientId& client_id) const;
-};
-
-// Metadata of the offline page.
-struct OfflinePageItem {
- public:
-  // Note that this should match with Flags enum in offline_pages.proto.
-  enum Flags {
-    NO_FLAG = 0,
-    MARKED_FOR_DELETION = 0x1,
-  };
-
-  OfflinePageItem();
-  OfflinePageItem(const GURL& url,
-                  int64_t offline_id,
-                  const ClientId& client_id,
-                  const base::FilePath& file_path,
-                  int64_t file_size);
-  OfflinePageItem(const GURL& url,
-                  int64_t offline_id,
-                  const ClientId& client_id,
-                  const base::FilePath& file_path,
-                  int64_t file_size,
-                  const base::Time& creation_time);
-  OfflinePageItem(const OfflinePageItem& other);
-  ~OfflinePageItem();
-
-  bool operator==(const OfflinePageItem& other) const;
-
-  // The URL of the page. This is the last committed URL. In the case that
-  // redirects occur, access |original_url| for the original URL.
-  GURL url;
-  // The primary key/ID for this page in offline pages internal database.
-  int64_t offline_id;
-
-  // The Client ID (external) related to the offline page. This is opaque
-  // to our system, but useful for users of offline pages who want to map
-  // their ids to our saved pages.
-  ClientId client_id;
-
-  // The file path to the archive with a local copy of the page.
-  base::FilePath file_path;
-  // The size of the offline copy.
-  int64_t file_size;
-  // The time when the offline archive was created.
-  base::Time creation_time;
-  // The time when the offline archive was last accessed.
-  base::Time last_access_time;
-  // Number of times that the offline archive has been accessed.
-  int access_count;
-  // The title of the page at the time it was saved.
-  base::string16 title;
-  // Flags about the state and behavior of the offline page.
-  Flags flags;
-  // The original URL of the page if not empty. Otherwise, this is set to empty
-  // and |url| should be accessed instead.
-  GURL original_url;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_ITEM_H_
diff --git a/components/offline_pages/offline_page_metadata_store.cc b/components/offline_pages/offline_page_metadata_store.cc
deleted file mode 100644
index 0b1f07d..0000000
--- a/components/offline_pages/offline_page_metadata_store.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_metadata_store.h"
-
-namespace offline_pages {
-
-template class StoreUpdateResult<OfflinePageItem>;
-
-OfflinePageMetadataStore::~OfflinePageMetadataStore() {
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_metadata_store.h b/components/offline_pages/offline_page_metadata_store.h
deleted file mode 100644
index 551ad65..0000000
--- a/components/offline_pages/offline_page_metadata_store.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/callback.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_store_types.h"
-
-namespace offline_pages {
-
-typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult;
-
-// OfflinePageMetadataStore keeps metadata for the offline pages.
-// Ability to create multiple instances of the store as well as behavior of
-// asynchronous operations when the object is being destroyed, before such
-// operation finishes will depend on implementation. It should be possible to
-// issue multiple asynchronous operations in parallel.
-class OfflinePageMetadataStore {
- public:
-  // This enum is used in an UMA histogram. Hence the entries here shouldn't
-  // be deleted or re-ordered and new ones should be added to the end.
-  enum LoadStatus {
-    LOAD_SUCCEEDED,
-    STORE_INIT_FAILED,
-    STORE_LOAD_FAILED,
-    DATA_PARSING_FAILED,
-
-    // NOTE: always keep this entry at the end.
-    LOAD_STATUS_COUNT
-  };
-
-  typedef base::Callback<void(bool /* success */)> InitializeCallback;
-  typedef base::Callback<void(bool /* success */)> ResetCallback;
-  typedef base::Callback<void(const std::vector<OfflinePageItem>&)>
-      LoadCallback;
-  typedef base::Callback<void(ItemActionStatus)> AddCallback;
-  typedef base::Callback<void(std::unique_ptr<OfflinePagesUpdateResult>)>
-      UpdateCallback;
-
-  virtual ~OfflinePageMetadataStore();
-
-  // Initializes the store. Should be called before any other methods.
-  virtual void Initialize(const InitializeCallback& callback) = 0;
-
-  // Get all of the offline pages from the store.
-  virtual void GetOfflinePages(const LoadCallback& callback) = 0;
-
-  // Asynchronously adds an offline page item metadata to the store.
-  virtual void AddOfflinePage(const OfflinePageItem& offline_page,
-                              const AddCallback& callback) = 0;
-
-  // Asynchronously updates a set of offline page items in the store.
-  virtual void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
-                                  const UpdateCallback& callback) = 0;
-
-  // Asynchronously removes offline page metadata from the store.
-  // Result of the update is passed in callback.
-  virtual void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
-                                  const UpdateCallback& callback) = 0;
-
-  // Resets the store.
-  virtual void Reset(const ResetCallback& callback) = 0;
-
-  // Gets the store state.
-  virtual StoreState state() const = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_H_
diff --git a/components/offline_pages/offline_page_metadata_store_impl_unittest.cc b/components/offline_pages/offline_page_metadata_store_impl_unittest.cc
deleted file mode 100644
index ae2c71e..0000000
--- a/components/offline_pages/offline_page_metadata_store_impl_unittest.cc
+++ /dev/null
@@ -1,985 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_metadata_store.h"
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_metadata_store_sql.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-
-#define OFFLINE_PAGES_TABLE_V1 "offlinepages_v1"
-
-const char kTestClientNamespace[] = "CLIENT_NAMESPACE";
-const char kTestURL[] = "https://example.com";
-const char kOriginalTestURL[] = "https://example.com/foo";
-const ClientId kTestClientId1(kTestClientNamespace, "1234");
-const ClientId kTestClientId2(kTestClientNamespace, "5678");
-const base::FilePath::CharType kFilePath[] =
-    FILE_PATH_LITERAL("/offline_pages/example_com.mhtml");
-int64_t kFileSize = 234567LL;
-int64_t kOfflineId = 12345LL;
-
-// Build a store with outdated schema to simulate the upgrading process.
-// TODO(romax): move it to sql_unittests.
-void BuildTestStoreWithSchemaFromM52(const base::FilePath& file) {
-  sql::Connection connection;
-  ASSERT_TRUE(
-      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
-  ASSERT_TRUE(connection.is_open());
-  ASSERT_TRUE(connection.BeginTransaction());
-  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
-                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
-                                 "creation_time INTEGER NOT NULL, "
-                                 "file_size INTEGER NOT NULL, "
-                                 "version INTEGER NOT NULL, "
-                                 "last_access_time INTEGER NOT NULL, "
-                                 "access_count INTEGER NOT NULL, "
-                                 "status INTEGER NOT NULL DEFAULT 0, "
-                                 "user_initiated INTEGER, "
-                                 "client_namespace VARCHAR NOT NULL, "
-                                 "client_id VARCHAR NOT NULL, "
-                                 "online_url VARCHAR NOT NULL, "
-                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
-                                 "file_path VARCHAR NOT NULL "
-                                 ")"));
-  ASSERT_TRUE(connection.CommitTransaction());
-  sql::Statement statement(connection.GetUniqueStatement(
-      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
-      "(offline_id, creation_time, file_size, version, "
-      "last_access_time, access_count, client_namespace, "
-      "client_id, online_url, file_path) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-  statement.BindInt64(0, kOfflineId);
-  statement.BindInt(1, 0);
-  statement.BindInt64(2, kFileSize);
-  statement.BindInt(3, 0);
-  statement.BindInt(4, 0);
-  statement.BindInt(5, 1);
-  statement.BindCString(6, kTestClientNamespace);
-  statement.BindString(7, kTestClientId2.id);
-  statement.BindCString(8, kTestURL);
-  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
-  ASSERT_TRUE(statement.Run());
-  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
-  ASSERT_FALSE(
-      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
-}
-
-void BuildTestStoreWithSchemaFromM53(const base::FilePath& file) {
-  sql::Connection connection;
-  ASSERT_TRUE(
-      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
-  ASSERT_TRUE(connection.is_open());
-  ASSERT_TRUE(connection.BeginTransaction());
-  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
-                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
-                                 "creation_time INTEGER NOT NULL, "
-                                 "file_size INTEGER NOT NULL, "
-                                 "version INTEGER NOT NULL, "
-                                 "last_access_time INTEGER NOT NULL, "
-                                 "access_count INTEGER NOT NULL, "
-                                 "status INTEGER NOT NULL DEFAULT 0, "
-                                 "user_initiated INTEGER, "
-                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
-                                 "client_namespace VARCHAR NOT NULL, "
-                                 "client_id VARCHAR NOT NULL, "
-                                 "online_url VARCHAR NOT NULL, "
-                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
-                                 "file_path VARCHAR NOT NULL "
-                                 ")"));
-  ASSERT_TRUE(connection.CommitTransaction());
-  sql::Statement statement(connection.GetUniqueStatement(
-      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
-      "(offline_id, creation_time, file_size, version, "
-      "last_access_time, access_count, client_namespace, "
-      "client_id, online_url, file_path, expiration_time) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-  statement.BindInt64(0, kOfflineId);
-  statement.BindInt(1, 0);
-  statement.BindInt64(2, kFileSize);
-  statement.BindInt(3, 0);
-  statement.BindInt(4, 0);
-  statement.BindInt(5, 1);
-  statement.BindCString(6, kTestClientNamespace);
-  statement.BindString(7, kTestClientId2.id);
-  statement.BindCString(8, kTestURL);
-  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
-  statement.BindInt64(10, base::Time::Now().ToInternalValue());
-  ASSERT_TRUE(statement.Run());
-  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
-  ASSERT_FALSE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "title"));
-}
-
-void BuildTestStoreWithSchemaFromM54(const base::FilePath& file) {
-  sql::Connection connection;
-  ASSERT_TRUE(
-      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
-  ASSERT_TRUE(connection.is_open());
-  ASSERT_TRUE(connection.BeginTransaction());
-  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
-                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
-                                 "creation_time INTEGER NOT NULL, "
-                                 "file_size INTEGER NOT NULL, "
-                                 "version INTEGER NOT NULL, "
-                                 "last_access_time INTEGER NOT NULL, "
-                                 "access_count INTEGER NOT NULL, "
-                                 "status INTEGER NOT NULL DEFAULT 0, "
-                                 "user_initiated INTEGER, "
-                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
-                                 "client_namespace VARCHAR NOT NULL, "
-                                 "client_id VARCHAR NOT NULL, "
-                                 "online_url VARCHAR NOT NULL, "
-                                 "offline_url VARCHAR NOT NULL DEFAULT '', "
-                                 "file_path VARCHAR NOT NULL, "
-                                 "title VARCHAR NOT NULL DEFAULT ''"
-                                 ")"));
-  ASSERT_TRUE(connection.CommitTransaction());
-  sql::Statement statement(connection.GetUniqueStatement(
-      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
-      "(offline_id, creation_time, file_size, version, "
-      "last_access_time, access_count, client_namespace, "
-      "client_id, online_url, file_path, expiration_time, title) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-  statement.BindInt64(0, kOfflineId);
-  statement.BindInt(1, 0);
-  statement.BindInt64(2, kFileSize);
-  statement.BindInt(3, 0);
-  statement.BindInt(4, 0);
-  statement.BindInt(5, 1);
-  statement.BindCString(6, kTestClientNamespace);
-  statement.BindString(7, kTestClientId2.id);
-  statement.BindCString(8, kTestURL);
-  statement.BindString(9, base::FilePath(kFilePath).MaybeAsASCII());
-  statement.BindInt64(10, base::Time::Now().ToInternalValue());
-  statement.BindString16(11, base::UTF8ToUTF16("Test title"));
-  ASSERT_TRUE(statement.Run());
-  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
-  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "version"));
-  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "status"));
-  ASSERT_TRUE(
-      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "user_initiated"));
-  ASSERT_TRUE(
-      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "offline_url"));
-}
-
-void BuildTestStoreWithSchemaFromM55(const base::FilePath& file) {
-  sql::Connection connection;
-  ASSERT_TRUE(
-      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
-  ASSERT_TRUE(connection.is_open());
-  ASSERT_TRUE(connection.BeginTransaction());
-  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
-                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
-                                 "creation_time INTEGER NOT NULL, "
-                                 "file_size INTEGER NOT NULL, "
-                                 "last_access_time INTEGER NOT NULL, "
-                                 "access_count INTEGER NOT NULL, "
-                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
-                                 "client_namespace VARCHAR NOT NULL, "
-                                 "client_id VARCHAR NOT NULL, "
-                                 "online_url VARCHAR NOT NULL, "
-                                 "file_path VARCHAR NOT NULL, "
-                                 "title VARCHAR NOT NULL DEFAULT ''"
-                                 ")"));
-  ASSERT_TRUE(connection.CommitTransaction());
-  sql::Statement statement(connection.GetUniqueStatement(
-      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
-      "(offline_id, creation_time, file_size, "
-      "last_access_time, access_count, client_namespace, "
-      "client_id, online_url, file_path, expiration_time, title) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-  statement.BindInt64(0, kOfflineId);
-  statement.BindInt(1, 0);
-  statement.BindInt64(2, kFileSize);
-  statement.BindInt(3, 0);
-  statement.BindInt(4, 1);
-  statement.BindCString(5, kTestClientNamespace);
-  statement.BindString(6, kTestClientId2.id);
-  statement.BindCString(7, kTestURL);
-  statement.BindString(8, base::FilePath(kFilePath).MaybeAsASCII());
-  statement.BindInt64(9, base::Time::Now().ToInternalValue());
-  statement.BindString16(10, base::UTF8ToUTF16("Test title"));
-  ASSERT_TRUE(statement.Run());
-  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
-  ASSERT_TRUE(connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "title"));
-  ASSERT_FALSE(
-      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "original_url"));
-}
-
-void BuildTestStoreWithSchemaFromM56(const base::FilePath& file) {
-  sql::Connection connection;
-  ASSERT_TRUE(
-      connection.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
-  ASSERT_TRUE(connection.is_open());
-  ASSERT_TRUE(connection.BeginTransaction());
-  ASSERT_TRUE(connection.Execute("CREATE TABLE " OFFLINE_PAGES_TABLE_V1
-                                 "(offline_id INTEGER PRIMARY KEY NOT NULL, "
-                                 "creation_time INTEGER NOT NULL, "
-                                 "file_size INTEGER NOT NULL, "
-                                 "last_access_time INTEGER NOT NULL, "
-                                 "access_count INTEGER NOT NULL, "
-                                 "expiration_time INTEGER NOT NULL DEFAULT 0, "
-                                 "client_namespace VARCHAR NOT NULL, "
-                                 "client_id VARCHAR NOT NULL, "
-                                 "online_url VARCHAR NOT NULL, "
-                                 "file_path VARCHAR NOT NULL, "
-                                 "title VARCHAR NOT NULL DEFAULT '', "
-                                 "original_url VARCHAR NOT NULL DEFAULT ''"
-                                 ")"));
-  ASSERT_TRUE(connection.CommitTransaction());
-  sql::Statement statement(connection.GetUniqueStatement(
-      "INSERT INTO " OFFLINE_PAGES_TABLE_V1
-      "(offline_id, creation_time, file_size, "
-      "last_access_time, access_count, client_namespace, "
-      "client_id, online_url, file_path, expiration_time, title, original_url) "
-      "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-  statement.BindInt64(0, kOfflineId);
-  statement.BindInt(1, 0);
-  statement.BindInt64(2, kFileSize);
-  statement.BindInt(3, 0);
-  statement.BindInt(4, 1);
-  statement.BindCString(5, kTestClientNamespace);
-  statement.BindString(6, kTestClientId2.id);
-  statement.BindCString(7, kTestURL);
-  statement.BindString(8, base::FilePath(kFilePath).MaybeAsASCII());
-  statement.BindInt64(9, base::Time::Now().ToInternalValue());
-  statement.BindString16(10, base::UTF8ToUTF16("Test title"));
-  statement.BindCString(11, kOriginalTestURL);
-  ASSERT_TRUE(statement.Run());
-  ASSERT_TRUE(connection.DoesTableExist(OFFLINE_PAGES_TABLE_V1));
-  ASSERT_TRUE(
-      connection.DoesColumnExist(OFFLINE_PAGES_TABLE_V1, "expiration_time"));
-}
-
-class OfflinePageMetadataStoreFactory {
- public:
-  OfflinePageMetadataStore* BuildStore(const base::FilePath& file_path) {
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-
-  OfflinePageMetadataStore* BuildStoreM52(const base::FilePath& file_path) {
-    BuildTestStoreWithSchemaFromM52(file_path);
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-
-  OfflinePageMetadataStore* BuildStoreM53(const base::FilePath& file_path) {
-    BuildTestStoreWithSchemaFromM53(file_path);
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-
-  OfflinePageMetadataStore* BuildStoreM54(const base::FilePath& file_path) {
-    BuildTestStoreWithSchemaFromM54(file_path);
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-
-  OfflinePageMetadataStore* BuildStoreM55(const base::FilePath& file_path) {
-    BuildTestStoreWithSchemaFromM55(file_path);
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-
-  OfflinePageMetadataStore* BuildStoreM56(const base::FilePath& file_path) {
-    BuildTestStoreWithSchemaFromM56(file_path);
-    OfflinePageMetadataStoreSQL* store = new OfflinePageMetadataStoreSQL(
-        base::ThreadTaskRunnerHandle::Get(), file_path);
-    return store;
-  }
-};
-
-enum CalledCallback { NONE, LOAD, ADD, UPDATE, REMOVE, RESET };
-enum Status { STATUS_NONE, STATUS_TRUE, STATUS_FALSE };
-
-class OfflinePageMetadataStoreTest : public testing::Test {
- public:
-  OfflinePageMetadataStoreTest();
-  ~OfflinePageMetadataStoreTest() override;
-
-  void TearDown() override {
-    // Wait for all the pieces of the store to delete itself properly.
-    PumpLoop();
-  }
-
-  std::unique_ptr<OfflinePageMetadataStore> BuildStore();
-  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM52();
-  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM53();
-  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM54();
-  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM55();
-  std::unique_ptr<OfflinePageMetadataStore> BuildStoreWithSchemaFromM56();
-
-  void PumpLoop();
-
-  void InitializeCallback(bool success);
-  void GetOfflinePagesCallback(
-      const std::vector<OfflinePageItem>& offline_pages);
-  void AddCallback(ItemActionStatus status);
-  void UpdateCallback(CalledCallback called_callback,
-                      std::unique_ptr<OfflinePagesUpdateResult> result);
-  void ResetCallback(bool success);
-
-  void ClearResults();
-
-  OfflinePageItem CheckThatStoreHasOneItem();
-  void CheckThatOfflinePageCanBeSaved(
-      std::unique_ptr<OfflinePageMetadataStore> store);
-
-  OfflinePagesUpdateResult* last_update_result() {
-    return last_update_result_.get();
-  }
-
- protected:
-  CalledCallback last_called_callback_;
-  Status last_status_;
-  std::unique_ptr<OfflinePagesUpdateResult> last_update_result_;
-  std::vector<OfflinePageItem> offline_pages_;
-  OfflinePageMetadataStoreFactory factory_;
-
-  base::ScopedTempDir temp_directory_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-};
-
-OfflinePageMetadataStoreTest::OfflinePageMetadataStoreTest()
-    : last_called_callback_(NONE),
-      last_status_(STATUS_NONE),
-      task_runner_(new base::TestSimpleTaskRunner),
-      task_runner_handle_(task_runner_) {
-  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
-}
-
-OfflinePageMetadataStoreTest::~OfflinePageMetadataStoreTest() {}
-
-void OfflinePageMetadataStoreTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void OfflinePageMetadataStoreTest::InitializeCallback(bool success) {
-  last_status_ = success ? STATUS_TRUE : STATUS_FALSE;
-}
-
-void OfflinePageMetadataStoreTest::GetOfflinePagesCallback(
-    const std::vector<OfflinePageItem>& offline_pages) {
-  last_called_callback_ = LOAD;
-  offline_pages_.swap(const_cast<std::vector<OfflinePageItem>&>(offline_pages));
-}
-
-void OfflinePageMetadataStoreTest::AddCallback(ItemActionStatus status) {
-  last_called_callback_ = ADD;
-  // TODO(fgorski): Add specific add status.
-  // last_item_status_ = status;
-  last_status_ =
-      status == ItemActionStatus::SUCCESS ? STATUS_TRUE : STATUS_FALSE;
-}
-
-void OfflinePageMetadataStoreTest::UpdateCallback(
-    CalledCallback called_callback,
-    std::unique_ptr<OfflinePagesUpdateResult> result) {
-  last_called_callback_ = called_callback;
-  last_status_ = result->updated_items.size() > 0 ? STATUS_TRUE : STATUS_FALSE;
-  last_update_result_ = std::move(result);
-}
-
-void OfflinePageMetadataStoreTest::ResetCallback(bool success) {
-  last_called_callback_ = RESET;
-  last_status_ = success ? STATUS_TRUE : STATUS_FALSE;
-}
-
-void OfflinePageMetadataStoreTest::ClearResults() {
-  last_called_callback_ = NONE;
-  last_status_ = STATUS_NONE;
-  offline_pages_.clear();
-  last_update_result_.reset(nullptr);
-}
-
-OfflinePageItem OfflinePageMetadataStoreTest::CheckThatStoreHasOneItem() {
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  EXPECT_EQ(1U, offline_pages_.size());
-
-  return offline_pages_[0];
-}
-
-void OfflinePageMetadataStoreTest::CheckThatOfflinePageCanBeSaved(
-    std::unique_ptr<OfflinePageMetadataStore> store) {
-  size_t store_size = offline_pages_.size();
-  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
-                               base::FilePath(kFilePath), kFileSize);
-  offline_page.title = base::UTF8ToUTF16("a title");
-  offline_page.original_url = GURL(kOriginalTestURL);
-
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ClearResults();
-
-  // Close the store first to ensure file lock is removed.
-  store.reset();
-  store = BuildStore();
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ASSERT_EQ(store_size + 1, offline_pages_.size());
-  if (store_size > 0 &&
-      offline_pages_[0].offline_id != offline_page.offline_id) {
-    std::swap(offline_pages_[0], offline_pages_[1]);
-  }
-  EXPECT_EQ(offline_page, offline_pages_[0]);
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStore() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStore(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM52() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStoreM52(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM53() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStoreM53(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM54() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStoreM54(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM55() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStoreM55(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageMetadataStoreTest::BuildStoreWithSchemaFromM56() {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      factory_.BuildStoreM56(temp_directory_.GetPath()));
-  PumpLoop();
-  store->Initialize(
-      base::Bind(&OfflinePageMetadataStoreTest::InitializeCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  return store;
-}
-
-// Loads empty store and makes sure that there are no offline pages stored in
-// it.
-TEST_F(OfflinePageMetadataStoreTest, LoadEmptyStore) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  EXPECT_EQ(0U, offline_pages_.size());
-}
-
-TEST_F(OfflinePageMetadataStoreTest, GetOfflinePagesFromInvalidStore) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-  OfflinePageMetadataStoreSQL* sql_store =
-      static_cast<OfflinePageMetadataStoreSQL*>(store.get());
-
-  sql_store->SetStateForTesting(StoreState::NOT_LOADED, false);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-  EXPECT_EQ(StoreState::NOT_LOADED, store->state());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::FAILED_LOADING, false);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-  EXPECT_EQ(StoreState::FAILED_LOADING, store->state());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::FAILED_RESET, false);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-  EXPECT_EQ(StoreState::FAILED_RESET, store->state());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::LOADED, true);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::NOT_LOADED, true);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::FAILED_LOADING, false);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-
-  ClearResults();
-  sql_store->SetStateForTesting(StoreState::FAILED_RESET, false);
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0UL, offline_pages_.size());
-}
-
-// Loads a store which has an outdated schema.
-// This test case would crash if it's not handling correctly when we're loading
-// old version stores.
-// TODO(romax): Move this to sql_unittest.
-TEST_F(OfflinePageMetadataStoreTest, LoadVersion52Store) {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      BuildStoreWithSchemaFromM52());
-
-  OfflinePageItem item = CheckThatStoreHasOneItem();
-  CheckThatOfflinePageCanBeSaved(std::move(store));
-}
-
-// Loads a store which has an outdated schema.
-// This test case would crash if it's not handling correctly when we're loading
-// old version stores.
-// TODO(romax): Move this to sql_unittest.
-TEST_F(OfflinePageMetadataStoreTest, LoadVersion53Store) {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      BuildStoreWithSchemaFromM53());
-
-  OfflinePageItem item = CheckThatStoreHasOneItem();
-  CheckThatOfflinePageCanBeSaved(std::move(store));
-}
-
-// Loads a string with schema from M54.
-// This test case would crash if it's not handling correctly when we're loading
-// old version stores.
-// TODO(romax): Move this to sql_unittest.
-TEST_F(OfflinePageMetadataStoreTest, LoadVersion54Store) {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      BuildStoreWithSchemaFromM54());
-
-  OfflinePageItem item = CheckThatStoreHasOneItem();
-  CheckThatOfflinePageCanBeSaved(std::move(store));
-}
-
-// Loads a string with schema from M55.
-// This test case would crash if it's not handling correctly when we're loading
-// old version stores.
-// TODO(romax): Move this to sql_unittest.
-TEST_F(OfflinePageMetadataStoreTest, LoadVersion55Store) {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      BuildStoreWithSchemaFromM55());
-
-  OfflinePageItem item = CheckThatStoreHasOneItem();
-  CheckThatOfflinePageCanBeSaved(std::move(store));
-}
-
-// Loads a string with schema from M56.
-// This test case would crash if it's not handling correctly when we're loading
-// old version stores.
-// TODO(romax): Move this to sql_unittest.
-TEST_F(OfflinePageMetadataStoreTest, LoadVersion56Store) {
-  std::unique_ptr<OfflinePageMetadataStore> store(
-      BuildStoreWithSchemaFromM56());
-
-  OfflinePageItem item = CheckThatStoreHasOneItem();
-  CheckThatOfflinePageCanBeSaved(std::move(store));
-}
-
-// Adds metadata of an offline page into a store and then opens the store
-// again to make sure that stored metadata survives store restarts.
-TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) {
-  CheckThatOfflinePageCanBeSaved(BuildStore());
-}
-
-TEST_F(OfflinePageMetadataStoreTest, AddSameOfflinePageTwice) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
-                               base::FilePath(kFilePath), kFileSize);
-  offline_page.title = base::UTF8ToUTF16("a title");
-
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ClearResults();
-
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_FALSE, last_status_);
-}
-
-// Tests removing offline page metadata from the store, for which it first adds
-// metadata of an offline page.
-TEST_F(OfflinePageMetadataStoreTest, RemoveOfflinePage) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  // Add an offline page.
-  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
-                               base::FilePath(kFilePath), kFileSize);
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  // Get all pages from the store.
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(1U, offline_pages_.size());
-
-  // Remove the offline page.
-  std::vector<int64_t> ids_to_remove;
-  ids_to_remove.push_back(offline_page.offline_id);
-  store->RemoveOfflinePages(
-      ids_to_remove, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
-                                base::Unretained(this), REMOVE));
-  PumpLoop();
-  EXPECT_EQ(REMOVE, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ASSERT_TRUE(last_update_result() != nullptr);
-  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_update_result()->item_statuses.begin()->second);
-  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
-  EXPECT_EQ(offline_page, *(last_update_result()->updated_items.begin()));
-
-  ClearResults();
-
-  // Get all pages from the store.
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(0U, offline_pages_.size());
-
-  ClearResults();
-
-  // Close and reload the store.
-  store.reset();
-  store = BuildStore();
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  EXPECT_EQ(0U, offline_pages_.size());
-}
-
-// Adds metadata of multiple offline pages into a store and removes some.
-TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  // Add an offline page.
-  OfflinePageItem offline_page_1(GURL(kTestURL), 12345LL, kTestClientId1,
-                                 base::FilePath(kFilePath), kFileSize);
-  store->AddOfflinePage(offline_page_1,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  // Add anther offline page.
-  base::FilePath file_path_2 =
-      base::FilePath(FILE_PATH_LITERAL("//other.page.com.mhtml"));
-  OfflinePageItem offline_page_2(GURL("https://other.page.com"), 5678LL,
-                                 kTestClientId2, file_path_2, 12345,
-                                 base::Time::Now());
-  offline_page_2.original_url = GURL("https://example.com/bar");
-  store->AddOfflinePage(offline_page_2,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  // Get all pages from the store.
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(2U, offline_pages_.size());
-
-  // Remove the offline page.
-  std::vector<int64_t> ids_to_remove;
-  ids_to_remove.push_back(offline_page_1.offline_id);
-  store->RemoveOfflinePages(
-      ids_to_remove, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
-                                base::Unretained(this), REMOVE));
-  PumpLoop();
-  EXPECT_EQ(REMOVE, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ASSERT_TRUE(last_update_result() != nullptr);
-  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_update_result()->item_statuses.begin()->second);
-  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
-  EXPECT_EQ(offline_page_1, *(last_update_result()->updated_items.begin()));
-
-  ClearResults();
-
-  // Close and reload the store.
-  store.reset();
-  store = BuildStore();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ASSERT_EQ(1U, offline_pages_.size());
-  EXPECT_EQ(offline_page_2.url, offline_pages_[0].url);
-  EXPECT_EQ(offline_page_2.offline_id, offline_pages_[0].offline_id);
-  EXPECT_EQ(offline_page_2.file_path, offline_pages_[0].file_path);
-  EXPECT_EQ(offline_page_2.file_size, offline_pages_[0].file_size);
-  EXPECT_EQ(offline_page_2.creation_time, offline_pages_[0].creation_time);
-  EXPECT_EQ(offline_page_2.last_access_time,
-            offline_pages_[0].last_access_time);
-  EXPECT_EQ(offline_page_2.access_count, offline_pages_[0].access_count);
-  EXPECT_EQ(offline_page_2.client_id, offline_pages_[0].client_id);
-}
-
-// Tests updating offline page metadata from the store.
-TEST_F(OfflinePageMetadataStoreTest, UpdateOfflinePage) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  // First, adds a fresh page.
-  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
-                               base::FilePath(kFilePath), kFileSize);
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  ASSERT_EQ(1U, offline_pages_.size());
-  EXPECT_EQ(offline_page, offline_pages_[0]);
-
-  // Then update some data.
-  offline_page.file_size = kFileSize + 1;
-  offline_page.access_count++;
-  offline_page.original_url = GURL("https://example.com/bar");
-  std::vector<OfflinePageItem> items_to_update;
-  items_to_update.push_back(offline_page);
-  store->UpdateOfflinePages(
-      items_to_update, base::Bind(&OfflinePageMetadataStoreTest::UpdateCallback,
-                                  base::Unretained(this), UPDATE));
-  PumpLoop();
-  EXPECT_EQ(UPDATE, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-  ASSERT_TRUE(last_update_result() != nullptr);
-  EXPECT_EQ(1UL, last_update_result()->item_statuses.size());
-  EXPECT_EQ(ItemActionStatus::SUCCESS,
-            last_update_result()->item_statuses.begin()->second);
-  EXPECT_EQ(1UL, last_update_result()->updated_items.size());
-  EXPECT_EQ(offline_page, *(last_update_result()->updated_items.begin()));
-
-  ClearResults();
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  ASSERT_EQ(1U, offline_pages_.size());
-  EXPECT_EQ(offline_page, offline_pages_[0]);
-}
-
-TEST_F(OfflinePageMetadataStoreTest, ClearAllOfflinePages) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  // Add 2 offline pages.
-  OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
-                               base::FilePath(kFilePath), kFileSize);
-  store->AddOfflinePage(offline_page,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  OfflinePageItem offline_page2(GURL("http://test.com"), 5678LL, kTestClientId2,
-                                base::FilePath(kFilePath), kFileSize);
-  store->AddOfflinePage(offline_page2,
-                        base::Bind(&OfflinePageMetadataStoreTest::AddCallback,
-                                   base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(ADD, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  // Get all pages from the store.
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  EXPECT_EQ(2U, offline_pages_.size());
-
-  // Clear all records from the store.
-  store->Reset(base::Bind(&OfflinePageMetadataStoreTest::ResetCallback,
-                          base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(RESET, last_called_callback_);
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-
-  ClearResults();
-
-  // Get all pages from the store.
-  store->GetOfflinePages(
-      base::Bind(&OfflinePageMetadataStoreTest::GetOfflinePagesCallback,
-                 base::Unretained(this)));
-  PumpLoop();
-
-  EXPECT_EQ(LOAD, last_called_callback_);
-  ASSERT_EQ(0U, offline_pages_.size());
-}
-
-TEST_F(OfflinePageMetadataStoreTest, ResetStore) {
-  std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
-
-  store->Reset(base::Bind(&OfflinePageMetadataStoreTest::ResetCallback,
-                          base::Unretained(this)));
-  PumpLoop();
-  EXPECT_EQ(STATUS_TRUE, last_status_);
-}
-
-}  // namespace
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_metadata_store_sql.cc b/components/offline_pages/offline_page_metadata_store_sql.cc
deleted file mode 100644
index 5c20891..0000000
--- a/components/offline_pages/offline_page_metadata_store_sql.cc
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_metadata_store_sql.h"
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
-#include "sql/transaction.h"
-
-namespace offline_pages {
-
-namespace {
-
-// This is a macro instead of a const so that
-// it can be used inline in other SQL statements below.
-#define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1"
-
-bool CreateOfflinePagesTable(sql::Connection* db) {
-  const char kSql[] = "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
-                      "(offline_id INTEGER PRIMARY KEY NOT NULL,"
-                      " creation_time INTEGER NOT NULL,"
-                      " file_size INTEGER NOT NULL,"
-                      " last_access_time INTEGER NOT NULL,"
-                      " access_count INTEGER NOT NULL,"
-                      " client_namespace VARCHAR NOT NULL,"
-                      " client_id VARCHAR NOT NULL,"
-                      " online_url VARCHAR NOT NULL,"
-                      " file_path VARCHAR NOT NULL,"
-                      " title VARCHAR NOT NULL DEFAULT '',"
-                      " original_url VARCHAR NOT NULL DEFAULT ''"
-                      ")";
-  return db->Execute(kSql);
-}
-
-bool UpgradeWithQuery(sql::Connection* db, const char* upgrade_sql) {
-  if (!db->Execute("ALTER TABLE " OFFLINE_PAGES_TABLE_NAME
-                   " RENAME TO temp_" OFFLINE_PAGES_TABLE_NAME)) {
-    return false;
-  }
-  if (!CreateOfflinePagesTable(db))
-    return false;
-  if (!db->Execute(upgrade_sql))
-    return false;
-  if (!db->Execute("DROP TABLE IF EXISTS temp_" OFFLINE_PAGES_TABLE_NAME))
-    return false;
-  return true;
-}
-
-bool UpgradeFrom52(sql::Connection* db) {
-  const char kSql[] =
-      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, "
-      "online_url, file_path) "
-      "SELECT "
-      "offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, "
-      "online_url, file_path "
-      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
-  return UpgradeWithQuery(db, kSql);
-}
-
-bool UpgradeFrom53(sql::Connection* db) {
-  const char kSql[] =
-      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path) "
-      "SELECT "
-      "offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path "
-      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
-  return UpgradeWithQuery(db, kSql);
-}
-
-bool UpgradeFrom54(sql::Connection* db) {
-  const char kSql[] =
-      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title) "
-      "SELECT "
-      "offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title "
-      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
-  return UpgradeWithQuery(db, kSql);
-}
-
-bool UpgradeFrom55(sql::Connection* db) {
-  const char kSql[] =
-      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title) "
-      "SELECT "
-      "offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title "
-      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
-  return UpgradeWithQuery(db, kSql);
-}
-
-bool UpgradeFrom56(sql::Connection* db) {
-  const char kSql[] =
-      "INSERT INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title, original_url) "
-      "SELECT "
-      "offline_id, creation_time, file_size, last_access_time, "
-      "access_count, client_namespace, client_id, online_url, "
-      "file_path, title, original_url "
-      "FROM temp_" OFFLINE_PAGES_TABLE_NAME;
-  return UpgradeWithQuery(db, kSql);
-}
-
-bool CreateSchema(sql::Connection* db) {
-  // If you create a transaction but don't Commit() it is automatically
-  // rolled back by its destructor when it falls out of scope.
-  sql::Transaction transaction(db);
-  if (!transaction.Begin())
-    return false;
-
-  if (!db->DoesTableExist(OFFLINE_PAGES_TABLE_NAME)) {
-    if (!CreateOfflinePagesTable(db))
-      return false;
-  }
-
-  // Upgrade section. Details are described in the header file.
-  if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time") &&
-      !db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
-    if (!UpgradeFrom52(db))
-      return false;
-  } else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "title")) {
-    if (!UpgradeFrom53(db))
-      return false;
-  } else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "offline_url")) {
-    if (!UpgradeFrom54(db))
-      return false;
-  } else if (!db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "original_url")) {
-    if (!UpgradeFrom55(db))
-      return false;
-  } else if (db->DoesColumnExist(OFFLINE_PAGES_TABLE_NAME, "expiration_time")) {
-    if (!UpgradeFrom56(db))
-      return false;
-  }
-
-  // TODO(fgorski): Add indices here.
-  return transaction.Commit();
-}
-
-bool DeleteByOfflineId(sql::Connection* db, int64_t offline_id) {
-  static const char kSql[] =
-      "DELETE FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id=?";
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, offline_id);
-  return statement.Run();
-}
-
-base::FilePath GetPathFromUTF8String(const std::string& path_string) {
-#if defined(OS_POSIX)
-  return base::FilePath(path_string);
-#elif defined(OS_WIN)
-  return base::FilePath(base::UTF8ToWide(path_string));
-#else
-#error Unknown OS
-#endif
-}
-
-std::string GetUTF8StringFromPath(const base::FilePath& path) {
-#if defined(OS_POSIX)
-  return path.value();
-#elif defined(OS_WIN)
-  return base::WideToUTF8(path.value());
-#else
-#error Unknown OS
-#endif
-}
-
-// Create an offline page item from a SQL result.  Expects complete rows with
-// all columns present.
-OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
-  int64_t id = statement->ColumnInt64(0);
-  base::Time creation_time =
-      base::Time::FromInternalValue(statement->ColumnInt64(1));
-  int64_t file_size = statement->ColumnInt64(2);
-  base::Time last_access_time =
-      base::Time::FromInternalValue(statement->ColumnInt64(3));
-  int access_count = statement->ColumnInt(4);
-  ClientId client_id(statement->ColumnString(5), statement->ColumnString(6));
-  GURL url(statement->ColumnString(7));
-  base::FilePath path(GetPathFromUTF8String(statement->ColumnString(8)));
-  base::string16 title = statement->ColumnString16(9);
-  GURL original_url(statement->ColumnString(10));
-
-  OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
-  item.last_access_time = last_access_time;
-  item.access_count = access_count;
-  item.title = title;
-  item.original_url = original_url;
-  return item;
-}
-
-ItemActionStatus Insert(sql::Connection* db, const OfflinePageItem& item) {
-  // Using 'INSERT OR FAIL' or 'INSERT OR ABORT' in the query below causes debug
-  // builds to DLOG.
-  const char kSql[] =
-      "INSERT OR IGNORE INTO " OFFLINE_PAGES_TABLE_NAME
-      " (offline_id, online_url, client_namespace, client_id, file_path, "
-      "file_size, creation_time, last_access_time, access_count, "
-      "title, original_url)"
-      " VALUES "
-      " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, item.offline_id);
-  statement.BindString(1, item.url.spec());
-  statement.BindString(2, item.client_id.name_space);
-  statement.BindString(3, item.client_id.id);
-  statement.BindString(4, GetUTF8StringFromPath(item.file_path));
-  statement.BindInt64(5, item.file_size);
-  statement.BindInt64(6, item.creation_time.ToInternalValue());
-  statement.BindInt64(7, item.last_access_time.ToInternalValue());
-  statement.BindInt(8, item.access_count);
-  statement.BindString16(9, item.title);
-  statement.BindString(10, item.original_url.spec());
-  if (!statement.Run())
-    return ItemActionStatus::STORE_ERROR;
-  if (db->GetLastChangeCount() == 0)
-    return ItemActionStatus::ALREADY_EXISTS;
-  return ItemActionStatus::SUCCESS;
-}
-
-bool Update(sql::Connection* db, const OfflinePageItem& item) {
-  const char kSql[] =
-      "UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
-      " SET online_url = ?, client_namespace = ?, client_id = ?, file_path = ?,"
-      " file_size = ?, creation_time = ?, last_access_time = ?,"
-      " access_count = ?, title = ?, original_url = ?"
-      " WHERE offline_id = ?";
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindString(0, item.url.spec());
-  statement.BindString(1, item.client_id.name_space);
-  statement.BindString(2, item.client_id.id);
-  statement.BindString(3, GetUTF8StringFromPath(item.file_path));
-  statement.BindInt64(4, item.file_size);
-  statement.BindInt64(5, item.creation_time.ToInternalValue());
-  statement.BindInt64(6, item.last_access_time.ToInternalValue());
-  statement.BindInt(7, item.access_count);
-  statement.BindString16(8, item.title);
-  statement.BindString(9, item.original_url.spec());
-  statement.BindInt64(10, item.offline_id);
-  return statement.Run() && db->GetLastChangeCount() > 0;
-}
-
-bool InitDatabase(sql::Connection* db, base::FilePath path) {
-  db->set_page_size(4096);
-  db->set_cache_size(500);
-  db->set_histogram_tag("OfflinePageMetadata");
-  db->set_exclusive_locking();
-
-  base::File::Error err;
-  if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) {
-    LOG(ERROR) << "Failed to create offline pages db directory: "
-               << base::File::ErrorToString(err);
-    return false;
-  }
-  if (!db->Open(path)) {
-    LOG(ERROR) << "Failed to open database";
-    return false;
-  }
-  db->Preload();
-
-  return CreateSchema(db);
-}
-
-void NotifyLoadResult(scoped_refptr<base::SingleThreadTaskRunner> runner,
-                      const OfflinePageMetadataStore::LoadCallback& callback,
-                      OfflinePageMetadataStore::LoadStatus status,
-                      const std::vector<OfflinePageItem>& result) {
-  // TODO(fgorski): Switch to SQL specific UMA metrics.
-  UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status,
-                            OfflinePageMetadataStore::LOAD_STATUS_COUNT);
-  if (status == OfflinePageMetadataStore::LOAD_SUCCEEDED) {
-    UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount",
-                         static_cast<int32_t>(result.size()));
-  } else {
-    DVLOG(1) << "Offline pages database loading failed: " << status;
-  }
-  runner->PostTask(FROM_HERE, base::Bind(callback, result));
-}
-
-void OpenConnectionSync(sql::Connection* db,
-                        scoped_refptr<base::SingleThreadTaskRunner> runner,
-                        const base::FilePath& path,
-                        const base::Callback<void(bool)>& callback) {
-  bool success = InitDatabase(db, path);
-  runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-
-bool GetPageByOfflineIdSync(sql::Connection* db,
-                            int64_t offline_id,
-                            OfflinePageItem* item) {
-  const char kSql[] =
-      "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id = ?";
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-  statement.BindInt64(0, offline_id);
-
-  if (statement.Step()) {
-    *item = MakeOfflinePageItem(&statement);
-    return true;
-  }
-
-  return false;
-}
-
-void GetOfflinePagesSync(
-    sql::Connection* db,
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const OfflinePageMetadataStore::LoadCallback& callback) {
-  const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME;
-
-  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
-
-  std::vector<OfflinePageItem> result;
-  while (statement.Step())
-    result.push_back(MakeOfflinePageItem(&statement));
-
-  if (statement.Succeeded()) {
-    NotifyLoadResult(runner, callback, OfflinePageMetadataStore::LOAD_SUCCEEDED,
-                     result);
-  } else {
-    result.clear();
-    NotifyLoadResult(runner, callback,
-                     OfflinePageMetadataStore::STORE_LOAD_FAILED, result);
-  }
-}
-
-void AddOfflinePageSync(sql::Connection* db,
-                        scoped_refptr<base::SingleThreadTaskRunner> runner,
-                        const OfflinePageItem& offline_page,
-                        const OfflinePageMetadataStore::AddCallback& callback) {
-  ItemActionStatus status = Insert(db, offline_page);
-  runner->PostTask(FROM_HERE, base::Bind(callback, status));
-}
-
-void PostStoreUpdateResultForIds(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    StoreState store_state,
-    const std::vector<int64_t>& offline_ids,
-    ItemActionStatus action_status,
-    const OfflinePageMetadataStore::UpdateCallback& callback) {
-  std::unique_ptr<OfflinePagesUpdateResult> result(
-      new OfflinePagesUpdateResult(store_state));
-  for (const auto& offline_id : offline_ids)
-    result->item_statuses.push_back(std::make_pair(offline_id, action_status));
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void PostStoreErrorForAllPages(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const std::vector<OfflinePageItem>& pages,
-    const OfflinePageMetadataStore::UpdateCallback& callback) {
-  std::vector<int64_t> offline_ids;
-  for (const auto& page : pages)
-    offline_ids.push_back(page.offline_id);
-  PostStoreUpdateResultForIds(runner, StoreState::LOADED, offline_ids,
-                              ItemActionStatus::STORE_ERROR, callback);
-}
-
-void PostStoreErrorForAllIds(
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const std::vector<int64_t>& offline_ids,
-    const OfflinePageMetadataStore::UpdateCallback& callback) {
-  PostStoreUpdateResultForIds(runner, StoreState::LOADED, offline_ids,
-                              ItemActionStatus::STORE_ERROR, callback);
-}
-
-void UpdateOfflinePagesSync(
-    sql::Connection* db,
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const std::vector<OfflinePageItem>& pages,
-    const OfflinePageMetadataStore::UpdateCallback& callback) {
-  std::unique_ptr<OfflinePagesUpdateResult> result(
-      new OfflinePagesUpdateResult(StoreState::LOADED));
-
-  sql::Transaction transaction(db);
-  if (!transaction.Begin()) {
-    PostStoreErrorForAllPages(runner, pages, callback);
-    return;
-  }
-
-  for (const auto& page : pages) {
-    if (Update(db, page)) {
-      result->updated_items.push_back(page);
-      result->item_statuses.push_back(
-          std::make_pair(page.offline_id, ItemActionStatus::SUCCESS));
-    } else {
-      result->item_statuses.push_back(
-          std::make_pair(page.offline_id, ItemActionStatus::NOT_FOUND));
-    }
-  }
-
-  if (!transaction.Commit()) {
-    PostStoreErrorForAllPages(runner, pages, callback);
-    return;
-  }
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void RemoveOfflinePagesSync(
-    const std::vector<int64_t>& offline_ids,
-    sql::Connection* db,
-    scoped_refptr<base::SingleThreadTaskRunner> runner,
-    const OfflinePageMetadataStore::UpdateCallback& callback) {
-  // TODO(fgorski): Perhaps add metrics here.
-  std::unique_ptr<OfflinePagesUpdateResult> result(
-      new OfflinePagesUpdateResult(StoreState::LOADED));
-
-  // If you create a transaction but don't Commit() it is automatically
-  // rolled back by its destructor when it falls out of scope.
-  sql::Transaction transaction(db);
-  if (!transaction.Begin()) {
-    PostStoreErrorForAllIds(runner, offline_ids, callback);
-    return;
-  }
-
-  for (int64_t offline_id : offline_ids) {
-    OfflinePageItem page;
-    ItemActionStatus status;
-    if (!GetPageByOfflineIdSync(db, offline_id, &page)) {
-      status = ItemActionStatus::NOT_FOUND;
-    } else if (!DeleteByOfflineId(db, offline_id)) {
-      status = ItemActionStatus::STORE_ERROR;
-    } else {
-      status = ItemActionStatus::SUCCESS;
-      result->updated_items.push_back(page);
-    }
-
-    result->item_statuses.push_back(std::make_pair(offline_id, status));
-  }
-
-  if (!transaction.Commit()) {
-    PostStoreErrorForAllIds(runner, offline_ids, callback);
-    return;
-  }
-
-  runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
-}
-
-void ResetSync(sql::Connection* db,
-               const base::FilePath& db_file_path,
-               scoped_refptr<base::SingleThreadTaskRunner> runner,
-               const base::Callback<void(bool)>& callback) {
-  // This method deletes the content of the whole store and reinitializes it.
-  bool success = true;
-  if (db) {
-    success = db->Raze();
-    db->Close();
-  }
-  success = base::DeleteFile(db_file_path, true /*recursive*/) && success;
-  runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-
-}  // anonymous namespace
-
-OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    const base::FilePath& path)
-    : background_task_runner_(std::move(background_task_runner)),
-      db_file_path_(path.AppendASCII("OfflinePages.db")),
-      state_(StoreState::NOT_LOADED),
-      weak_ptr_factory_(this) {
-}
-
-OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {
-  if (db_.get() &&
-      !background_task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
-    DLOG(WARNING) << "SQL database will not be deleted.";
-  }
-}
-
-void OfflinePageMetadataStoreSQL::Initialize(
-    const InitializeCallback& callback) {
-  DCHECK(!db_);
-  db_.reset(new sql::Connection());
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&OpenConnectionSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), db_file_path_,
-                 base::Bind(&OfflinePageMetadataStoreSQL::OnOpenConnectionDone,
-                            weak_ptr_factory_.GetWeakPtr(), callback)));
-}
-
-void OfflinePageMetadataStoreSQL::GetOfflinePages(
-    const LoadCallback& callback) {
-  if (!CheckDb()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, std::vector<OfflinePageItem>()));
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&GetOfflinePagesSync, db_.get(),
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void OfflinePageMetadataStoreSQL::AddOfflinePage(
-    const OfflinePageItem& offline_page,
-    const AddCallback& callback) {
-  if (!CheckDb()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, ItemActionStatus::STORE_ERROR));
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&AddOfflinePageSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), offline_page, callback));
-}
-
-void OfflinePageMetadataStoreSQL::UpdateOfflinePages(
-    const std::vector<OfflinePageItem>& pages,
-    const UpdateCallback& callback) {
-  if (!CheckDb()) {
-    PostStoreErrorForAllPages(base::ThreadTaskRunnerHandle::Get(), pages,
-                              callback);
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&UpdateOfflinePagesSync, db_.get(),
-                 base::ThreadTaskRunnerHandle::Get(), pages, callback));
-}
-
-void OfflinePageMetadataStoreSQL::RemoveOfflinePages(
-    const std::vector<int64_t>& offline_ids,
-    const UpdateCallback& callback) {
-  if (!CheckDb()) {
-    PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), offline_ids,
-                            callback);
-    return;
-  }
-
-  if (offline_ids.empty()) {
-    // Nothing to do, but post a callback instead of calling directly
-    // to preserve the async style behavior to prevent bugs.
-    PostStoreUpdateResultForIds(
-        base::ThreadTaskRunnerHandle::Get(), state(), offline_ids,
-        ItemActionStatus::NOT_FOUND /* will be ignored */, callback);
-    return;
-  }
-
-  background_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&RemoveOfflinePagesSync, offline_ids, db_.get(),
-                            base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) {
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&ResetSync, db_.get(), db_file_path_,
-                 base::ThreadTaskRunnerHandle::Get(),
-                 base::Bind(&OfflinePageMetadataStoreSQL::OnResetDone,
-                            weak_ptr_factory_.GetWeakPtr(), callback)));
-}
-
-StoreState OfflinePageMetadataStoreSQL::state() const {
-  return state_;
-}
-
-void OfflinePageMetadataStoreSQL::SetStateForTesting(StoreState state,
-                                                     bool reset_db) {
-  state_ = state;
-  if (reset_db)
-    db_.reset(nullptr);
-}
-
-void OfflinePageMetadataStoreSQL::OnOpenConnectionDone(
-    const InitializeCallback& callback,
-    bool success) {
-  DCHECK(db_.get());
-  state_ = success ? StoreState::LOADED : StoreState::FAILED_LOADING;
-  callback.Run(success);
-}
-
-void OfflinePageMetadataStoreSQL::OnResetDone(const ResetCallback& callback,
-                                              bool success) {
-  state_ = success ? StoreState::NOT_LOADED : StoreState::FAILED_RESET;
-  db_.reset();
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, success));
-}
-
-bool OfflinePageMetadataStoreSQL::CheckDb() const {
-  return db_ && state_ == StoreState::LOADED;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_metadata_store_sql.h b/components/offline_pages/offline_page_metadata_store_sql.h
deleted file mode 100644
index 09de6ed..0000000
--- a/components/offline_pages/offline_page_metadata_store_sql.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
-
-namespace base {
-class SequencedTaskRunner;
-}
-
-namespace sql {
-class Connection;
-}
-
-namespace offline_pages {
-
-// OfflinePageMetadataStoreSQL is an instance of OfflinePageMetadataStore
-// which is implemented using a SQLite database.
-//
-// This store has a history of schema updates in pretty much every release.
-// Original schema was delivered in M52. Since then, the following changes
-// happened:
-// * In M53 expiration_time was added,
-// * In M54 title was added,
-// * In M55 we dropped the following fields (never used): version, status,
-//   offline_url, user_initiated.
-// * In M56 original_url was added.
-// * In M57 expiration_time was dropped. Existing expired pages would be
-//   removed when metadata consistency check happens.
-//
-// Here is a procedure to update the schema for this store:
-// * Decide how to detect that the store is on a particular version, which
-//   typically means that a certain field exists or is missing. This happens in
-//   Upgrade section of |CreateSchema|
-// * Work out appropriate change and apply it to all existing upgrade paths. In
-//   the interest of performing a single update of the store, it upgrades from a
-//   detected version to the current one. This means that when making a change,
-//   more than a single query may have to be updated (in case of fields being
-//   removed or needed to be initialized to a specific, non-default value).
-//   Such approach is preferred to doing N updates for every changed version on
-//   a startup after browser update.
-// * New upgrade method should specify which version it is upgrading from, e.g.
-//   |UpgradeFrom54|.
-// * Upgrade should use |UpgradeWithQuery| and simply specify SQL command to
-//   move data from old table (prefixed by temp_) to the new one.
-class OfflinePageMetadataStoreSQL : public OfflinePageMetadataStore {
- public:
-  OfflinePageMetadataStoreSQL(
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      const base::FilePath& database_dir);
-  ~OfflinePageMetadataStoreSQL() override;
-
-  // Implementation methods.
-  void Initialize(const InitializeCallback& callback) override;
-  void GetOfflinePages(const LoadCallback& callback) override;
-  void AddOfflinePage(const OfflinePageItem& offline_page,
-                      const AddCallback& callback) override;
-  void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
-                          const UpdateCallback& callback) override;
-  void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
-                          const UpdateCallback& callback) override;
-  void Reset(const ResetCallback& callback) override;
-  StoreState state() const override;
-
-  // Helper function used to force incorrect state for testing purposes.
-  void SetStateForTesting(StoreState state, bool reset_db);
-
- private:
-  // Used to conclude opening/resetting DB connection.
-  void OnOpenConnectionDone(const InitializeCallback& callback, bool success);
-  void OnResetDone(const ResetCallback& callback, bool success);
-
-  // Checks whether a valid DB connection is present and store state is LOADED.
-  bool CheckDb() const;
-
-  // Background thread where all SQL access should be run.
-  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
-  // Path to the database on disk.
-  base::FilePath db_file_path_;
-
-  // Database connection.
-  std::unique_ptr<sql::Connection> db_;
-
-  // State of the store.
-  StoreState state_;
-
-  base::WeakPtrFactory<OfflinePageMetadataStoreSQL> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageMetadataStoreSQL);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_METADATA_STORE_SQL_H_
diff --git a/components/offline_pages/offline_page_model.cc b/components/offline_pages/offline_page_model.cc
deleted file mode 100644
index 39cd918..0000000
--- a/components/offline_pages/offline_page_model.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model.h"
-
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-const int64_t OfflinePageModel::kInvalidOfflineId;
-
-OfflinePageModel::SavePageParams::SavePageParams()
-    : proposed_offline_id(OfflinePageModel::kInvalidOfflineId) {
-}
-
-OfflinePageModel::SavePageParams::SavePageParams(const SavePageParams& other) {
-  url = other.url;
-  client_id = other.client_id;
-  proposed_offline_id = other.proposed_offline_id;
-  original_url = other.original_url;
-}
-
-// static
-bool OfflinePageModel::CanSaveURL(const GURL& url) {
-  return url.is_valid() && url.SchemeIsHTTPOrHTTPS();
-}
-
-OfflinePageModel::OfflinePageModel() {}
-
-OfflinePageModel::~OfflinePageModel() {}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_model.h b/components/offline_pages/offline_page_model.h
deleted file mode 100644
index 8c8142cf..0000000
--- a/components/offline_pages/offline_page_model.h
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/supports_user_data.h"
-#include "components/offline_pages/offline_event_logger.h"
-#include "components/offline_pages/offline_page_archiver.h"
-#include "components/offline_pages/offline_page_model_query.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_types.h"
-
-class GURL;
-
-namespace offline_pages {
-
-struct ClientId;
-
-// Service for saving pages offline, storing the offline copy and metadata, and
-// retrieving them upon request.
-//
-// Example usage:
-//   class ArchiverImpl : public OfflinePageArchiver {
-//     // This is a class that knows how to create archiver
-//     void CreateArchiver(...) override;
-//     ...
-//   }
-//
-//   // In code using the OfflinePagesModel to save a page:
-//   std::unique_ptr<ArchiverImpl> archiver(new ArchiverImpl());
-//   // Callback is of type SavePageCallback.
-//   model->SavePage(url, std::move(archiver), callback);
-//
-// TODO(fgorski): Things to describe:
-// * how to cancel requests and what to expect
-class OfflinePageModel : public base::SupportsUserData {
- public:
-  // Controls how to search on differnt URLs for pages.
-  enum class URLSearchMode {
-    // Match against the last committed URL only.
-    SEARCH_BY_FINAL_URL_ONLY,
-    // Match against all stored URLs, including the last committed URL and
-    // the original request URL.
-    SEARCH_BY_ALL_URLS
-  };
-
-  // Describes the parameters to control how to save a page.
-  struct SavePageParams {
-    SavePageParams();
-    SavePageParams(const SavePageParams& other);
-
-    // The last committed URL of the page to save.
-    GURL url;
-
-    // The identification used by the client.
-    ClientId client_id;
-
-    // Used for the offline_id for the saved file if non-zero. If it is
-    // kInvalidOfflineId, a new, random ID will be generated.
-    int64_t proposed_offline_id;
-
-    // The original URL of the page to save. Empty if no redirect occurs.
-    GURL original_url;
-  };
-
-  // Observer of the OfflinePageModel.
-  class Observer {
-   public:
-    // Invoked when the model has finished loading.
-    virtual void OfflinePageModelLoaded(OfflinePageModel* model) = 0;
-
-    // Invoked when the model is being updated, due to adding, removing or
-    // updating an offline page.
-    virtual void OfflinePageModelChanged(OfflinePageModel* model) = 0;
-
-    // Invoked when an offline copy related to |offline_id| was deleted.
-    virtual void OfflinePageDeleted(int64_t offline_id,
-                                    const ClientId& client_id) = 0;
-
-   protected:
-    virtual ~Observer() = default;
-  };
-
-  using CheckPagesExistOfflineResult =
-      offline_pages::CheckPagesExistOfflineResult;
-  using MultipleOfflinePageItemResult =
-      offline_pages::MultipleOfflinePageItemResult;
-  using DeletePageResult = offline_pages::DeletePageResult;
-  using SavePageResult = offline_pages::SavePageResult;
-
-  // Returns true if saving an offline page may be attempted for |url|.
-  static bool CanSaveURL(const GURL& url);
-
-  OfflinePageModel();
-  ~OfflinePageModel() override;
-
-  virtual void AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(Observer* observer) = 0;
-
-  static const int64_t kInvalidOfflineId = 0;
-
-  // Attempts to save a page offline per |save_page_params|. Requires that the
-  // model is loaded.  Generates a new offline id or uses the proposed offline
-  // id in |save_page_params| and returns it.
-  virtual void SavePage(const SavePageParams& save_page_params,
-                        std::unique_ptr<OfflinePageArchiver> archiver,
-                        const SavePageCallback& callback) = 0;
-
-  // Marks that the offline page related to the passed |offline_id| has been
-  // accessed. Its access info, including last access time and access count,
-  // will be updated. Requires that the model is loaded.
-  virtual void MarkPageAccessed(int64_t offline_id) = 0;
-
-  // Deletes pages based on |offline_ids|.
-  virtual void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
-                                      const DeletePageCallback& callback) = 0;
-
-  // Deletes all pages associated with any of |client_ids|.
-  virtual void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
-                                      const DeletePageCallback& callback) = 0;
-
-  virtual void GetPagesMatchingQuery(
-      std::unique_ptr<OfflinePageModelQuery> query,
-      const MultipleOfflinePageItemCallback& callback) = 0;
-
-  // Retrieves all pages associated with any of |client_ids|.
-  virtual void GetPagesByClientIds(
-      const std::vector<ClientId>& client_ids,
-      const MultipleOfflinePageItemCallback& callback) = 0;
-
-  // Deletes cached offline pages matching the URL predicate.
-  virtual void DeleteCachedPagesByURLPredicate(
-      const UrlPredicate& predicate,
-      const DeletePageCallback& callback) = 0;
-
-  // Returns via callback all GURLs in |urls| that are equal to the online URL
-  // of any offline page.
-  virtual void CheckPagesExistOffline(
-      const std::set<GURL>& urls,
-      const CheckPagesExistOfflineCallback& callback) = 0;
-
-  // Gets all offline pages.
-  virtual void GetAllPages(const MultipleOfflinePageItemCallback& callback) = 0;
-
-  // Gets all offline ids where the offline page has the matching client id.
-  virtual void GetOfflineIdsForClientId(
-      const ClientId& client_id,
-      const MultipleOfflineIdCallback& callback) = 0;
-
-  // Returns zero or one offline pages associated with a specified |offline_id|.
-  virtual void GetPageByOfflineId(
-      int64_t offline_id,
-      const SingleOfflinePageItemCallback& callback) = 0;
-
-  // Returns the offline pages that are related to |url|. |url_search_mode|
-  // controls how the url match is done. See URLSearchMode for more details.
-  virtual void GetPagesByURL(
-      const GURL& url,
-      URLSearchMode url_search_mode,
-      const MultipleOfflinePageItemCallback& callback) = 0;
-
-  // Returns the policy controller.
-  virtual ClientPolicyController* GetPolicyController() = 0;
-
-  // TODO(dougarnett): Remove this and its uses.
-  virtual bool is_loaded() const = 0;
-
-  // Returns the logger. Ownership is retained by the model.
-  virtual OfflineEventLogger* GetLogger() = 0;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_H_
diff --git a/components/offline_pages/offline_page_model_event_logger.cc b/components/offline_pages/offline_page_model_event_logger.cc
deleted file mode 100644
index 0ca9d95..0000000
--- a/components/offline_pages/offline_page_model_event_logger.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model_event_logger.h"
-
-namespace offline_pages {
-
-void OfflinePageModelEventLogger::RecordPageSaved(const std::string& name_space,
-                                                  const std::string& url,
-                                                  const std::string& id) {
-  RecordActivity(url + " is saved at " + name_space + " with id " + id);
-}
-
-void OfflinePageModelEventLogger::RecordPageDeleted(const std::string& id) {
-  RecordActivity("Page with ID " + id + " has been deleted");
-}
-
-void OfflinePageModelEventLogger::RecordPageExpired(const std::string& id) {
-  RecordActivity("Page with ID " + id + " has been expired");
-}
-
-void OfflinePageModelEventLogger::RecordStoreClearError() {
-  RecordActivity("Offline store clear failed");
-}
-
-void OfflinePageModelEventLogger::RecordStoreCleared() {
-  RecordActivity("Offline store cleared");
-}
-
-void OfflinePageModelEventLogger::RecordStoreReloadError() {
-  RecordActivity("There was an error reloading the offline store");
-}
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_model_event_logger.h b/components/offline_pages/offline_page_model_event_logger.h
deleted file mode 100644
index f472ca0b..0000000
--- a/components/offline_pages/offline_page_model_event_logger.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
-
-#include "components/offline_pages/offline_event_logger.h"
-
-namespace offline_pages {
-
-class OfflinePageModelEventLogger : public OfflineEventLogger {
- public:
-  // Records that a page has been saved for |name_space| with |url|
-  // and |offline_id|.
-  void RecordPageSaved(const std::string& name_space,
-                       const std::string& url,
-                       const std::string& offline_id);
-
-  // Records that a page with |offline_id| has been deleted.
-  void RecordPageDeleted(const std::string& offline_id);
-
-  // Records that a page with |offline_id| has been expired.
-  void RecordPageExpired(const std::string& offline_id);
-
-  // Records that the offline store has been cleared.
-  void RecordStoreCleared();
-
-  // Records that there was an error when clearing the offline store.
-  void RecordStoreClearError();
-
-  // Records that there was an error when reloading the offline store.
-  void RecordStoreReloadError();
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_EVENT_LOGGER_H_
diff --git a/components/offline_pages/offline_page_model_event_logger_unittest.cc b/components/offline_pages/offline_page_model_event_logger_unittest.cc
deleted file mode 100644
index 1cc6fa26..0000000
--- a/components/offline_pages/offline_page_model_event_logger_unittest.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model_event_logger.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-
-const char kNamespace[] = "last_n";
-const char kUrl[] = "http://www.wikipedia.org";
-const char kOfflineId[] = "foobar";
-const int kTimeLength = 21;
-const char kPageSaved[] =
-    "http://www.wikipedia.org is saved at last_n with id foobar";
-const char kPageDeleted[] = "Page with ID foobar has been deleted";
-const char kPageExpired[] = "Page with ID foobar has been expired";
-const char kRecordStoreClearError[] = "Offline store clear failed";
-const char kRecordStoreCleared[] = "Offline store cleared";
-const char kRecordStoreReloadError[] =
-    "There was an error reloading the offline store";
-
-}  // namespace
-
-TEST(OfflinePageModelEventLoggerTest, RecordsWhenLoggingIsOn) {
-  OfflinePageModelEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(true);
-  logger.RecordStoreCleared();
-  logger.RecordPageSaved(kNamespace, kUrl, kOfflineId);
-  logger.RecordPageDeleted(kOfflineId);
-  logger.RecordPageExpired(kOfflineId);
-  logger.RecordStoreClearError();
-  logger.RecordStoreReloadError();
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(6u, log.size());
-  EXPECT_EQ(std::string(kRecordStoreCleared), log[5].substr(kTimeLength));
-  EXPECT_EQ(std::string(kPageSaved), log[4].substr(kTimeLength));
-  EXPECT_EQ(std::string(kPageDeleted), log[3].substr(kTimeLength));
-  EXPECT_EQ(std::string(kPageExpired), log[2].substr(kTimeLength));
-  EXPECT_EQ(std::string(kRecordStoreClearError), log[1].substr(kTimeLength));
-  EXPECT_EQ(std::string(kRecordStoreReloadError), log[0].substr(kTimeLength));
-}
-
-TEST(OfflinePageModelEventLoggerTest, DoesNotRecordWhenLoggingIsOff) {
-  OfflinePageModelEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(false);
-  logger.RecordStoreCleared();
-  logger.RecordPageSaved(kNamespace, kUrl, kOfflineId);
-  logger.RecordPageDeleted(kOfflineId);
-  logger.RecordPageExpired(kOfflineId);
-  logger.RecordStoreClearError();
-  logger.RecordStoreReloadError();
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(0u, log.size());
-}
-
-TEST(OfflinePageModelEventLoggerTest, DoesNotExceedMaxSize) {
-  OfflinePageModelEventLogger logger;
-  std::vector<std::string> log;
-
-  logger.SetIsLogging(true);
-  for (size_t i = 0; i < kMaxLogCount + 1; ++i) {
-    logger.RecordStoreCleared();
-  }
-  logger.GetLogs(&log);
-
-  EXPECT_EQ(kMaxLogCount, log.size());
-}
-
-}  // namespace offline_internals
diff --git a/components/offline_pages/offline_page_model_impl.cc b/components/offline_pages/offline_page_model_impl.cc
deleted file mode 100644
index b026304..0000000
--- a/components/offline_pages/offline_page_model_impl.cc
+++ /dev/null
@@ -1,1090 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model_impl.h"
-
-#include <algorithm>
-#include <limits>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/clock.h"
-#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "url/gurl.h"
-
-using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult;
-using ClearStorageCallback =
-    offline_pages::OfflinePageStorageManager::ClearStorageCallback;
-using ClearStorageResult =
-    offline_pages::OfflinePageStorageManager::ClearStorageResult;
-
-namespace offline_pages {
-
-namespace {
-
-// The delay used to schedule the first clear storage request for storage
-// manager after the model is loaded.
-const base::TimeDelta kStorageManagerStartingDelay =
-    base::TimeDelta::FromSeconds(20);
-
-int64_t GenerateOfflineId() {
-  return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
-}
-
-// The maximum histogram size for the metrics that measure time between views of
-// a given page.
-const base::TimeDelta kMaxOpenedPageHistogramBucket =
-    base::TimeDelta::FromDays(90);
-
-SavePageResult ToSavePageResult(ArchiverResult archiver_result) {
-  SavePageResult result;
-  switch (archiver_result) {
-    case ArchiverResult::SUCCESSFULLY_CREATED:
-      result = SavePageResult::SUCCESS;
-      break;
-    case ArchiverResult::ERROR_DEVICE_FULL:
-      result = SavePageResult::DEVICE_FULL;
-      break;
-    case ArchiverResult::ERROR_CONTENT_UNAVAILABLE:
-      result = SavePageResult::CONTENT_UNAVAILABLE;
-      break;
-    case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED:
-      result = SavePageResult::ARCHIVE_CREATION_FAILED;
-      break;
-    case ArchiverResult::ERROR_CANCELED:
-      result = SavePageResult::CANCELLED;
-      break;
-    case ArchiverResult::ERROR_SECURITY_CERTIFICATE:
-      result = SavePageResult::SECURITY_CERTIFICATE_ERROR;
-      break;
-    default:
-      NOTREACHED();
-      result = SavePageResult::CONTENT_UNAVAILABLE;
-  }
-  return result;
-}
-
-std::string AddHistogramSuffix(const ClientId& client_id,
-                               const char* histogram_name) {
-  if (client_id.name_space.empty()) {
-    NOTREACHED();
-    return histogram_name;
-  }
-  std::string adjusted_histogram_name(histogram_name);
-  adjusted_histogram_name += ".";
-  adjusted_histogram_name += client_id.name_space;
-  return adjusted_histogram_name;
-}
-
-void ReportStorageHistogramsAfterSave(
-    const ArchiveManager::StorageStats& storage_stats) {
-  const int kMB = 1024 * 1024;
-  int free_disk_space_mb =
-      static_cast<int>(storage_stats.free_disk_space / kMB);
-  UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.SavePage.FreeSpaceMB",
-                              free_disk_space_mb, 1, 500000, 50);
-
-  int total_page_size_mb =
-      static_cast<int>(storage_stats.total_archives_size / kMB);
-  UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb);
-}
-
-void ReportStorageHistogramsAfterDelete(
-    const ArchiveManager::StorageStats& storage_stats) {
-  const int kMB = 1024 * 1024;
-  int free_disk_space_mb =
-      static_cast<int>(storage_stats.free_disk_space / kMB);
-  UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DeletePage.FreeSpaceMB",
-                              free_disk_space_mb, 1, 500000, 50);
-
-  int total_page_size_mb =
-      static_cast<int>(storage_stats.total_archives_size / kMB);
-  UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb);
-
-  if (storage_stats.free_disk_space > 0) {
-    int percentage_of_free = static_cast<int>(
-        1.0 * storage_stats.total_archives_size /
-        (storage_stats.total_archives_size + storage_stats.free_disk_space) *
-        100);
-    UMA_HISTOGRAM_PERCENTAGE(
-        "OfflinePages.DeletePage.TotalPageSizeAsPercentageOfFreeSpace",
-        percentage_of_free);
-  }
-}
-
-void ReportSavePageResultHistogramAfterSave(const ClientId& client_id,
-                                           SavePageResult result) {
-  // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
-      AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"),
-      1,
-      static_cast<int>(SavePageResult::RESULT_COUNT),
-      static_cast<int>(SavePageResult::RESULT_COUNT) + 1,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(static_cast<int>(result));
-}
-
-// Goes through the list of offline pages, compiling the following two metrics:
-//   - a count of the pages with the same URL
-//   - The difference between the |created_before| time and the creation time of
-//     the page with the closest creation time before |created_before|.
-// Returns true if there was a page that was saved before |created_before| with
-// a matching URL.
-bool GetMatchingURLCountAndMostRecentCreationTime(
-    const std::map<int64_t, OfflinePageItem>& offline_pages,
-    std::string name_space,
-    const GURL& url,
-    base::Time created_before,
-    int* matching_url_count,
-    base::TimeDelta* most_recent_creation_time) {
-  int count = 0;
-
-  // Create a time that is very old, so that any valid time will be newer than
-  // it.
-  base::Time latest_time;
-  bool matching_page = false;
-
-  for (auto& id_page_pair : offline_pages) {
-    if (id_page_pair.second.client_id.name_space == name_space &&
-        url == id_page_pair.second.url) {
-      count++;
-      base::Time page_creation_time = id_page_pair.second.creation_time;
-      if (page_creation_time < created_before &&
-          page_creation_time > latest_time) {
-        latest_time = page_creation_time;
-        matching_page = true;
-      }
-    }
-  }
-
-  if (matching_url_count != nullptr)
-    *matching_url_count = count;
-  if (most_recent_creation_time != nullptr && latest_time != base::Time())
-    *most_recent_creation_time = created_before - latest_time;
-
-  return matching_page;
-}
-
-void ReportPageHistogramAfterSave(
-    ClientPolicyController* policy_controller_,
-    const std::map<int64_t, OfflinePageItem>& offline_pages,
-    const OfflinePageItem& offline_page,
-    const base::Time& save_time) {
-  DCHECK(policy_controller_);
-  // The histogram below is an expansion of the UMA_HISTOGRAM_TIMES
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
-      AddHistogramSuffix(offline_page.client_id, "OfflinePages.SavePageTime"),
-      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(10),
-      50, base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->AddTime(save_time - offline_page.creation_time);
-
-  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  // Reported as Kb between 1Kb and 10Mb.
-  histogram = base::Histogram::FactoryGet(
-      AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"),
-      1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(offline_page.file_size / 1024);
-
-  if (policy_controller_->IsSupportedByDownload(
-          offline_page.client_id.name_space)) {
-    int matching_url_count;
-    base::TimeDelta time_since_most_recent_duplicate;
-    if (GetMatchingURLCountAndMostRecentCreationTime(
-            offline_pages, offline_page.client_id.name_space, offline_page.url,
-            offline_page.creation_time, &matching_url_count,
-            &time_since_most_recent_duplicate)) {
-      // Using CUSTOM_COUNTS instead of time-oriented histogram to record
-      // samples in seconds rather than milliseconds.
-      UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved",
-          time_since_most_recent_duplicate.InSeconds(),
-          base::TimeDelta::FromSeconds(1).InSeconds(),
-          base::TimeDelta::FromDays(7).InSeconds(), 50);
-    }
-    UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DownloadSavedPageDuplicateCount",
-                                matching_url_count, 1, 20, 10);
-  }
-}
-
-void ReportPageHistogramsAfterDelete(
-    const std::map<int64_t, OfflinePageItem>& offline_pages,
-    const std::vector<OfflinePageItem>& deleted_pages,
-    const base::Time& delete_time) {
-  const int max_minutes = base::TimeDelta::FromDays(365).InMinutes();
-  int64_t total_size = 0;
-
-  for (const auto& page : deleted_pages) {
-    total_size += page.file_size;
-    ClientId client_id = page.client_id;
-
-    if (client_id.name_space == kDownloadNamespace) {
-      int remaining_pages_with_url;
-      GetMatchingURLCountAndMostRecentCreationTime(
-          offline_pages, page.client_id.name_space, page.url, base::Time::Max(),
-          &remaining_pages_with_url, nullptr);
-      UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "OfflinePages.DownloadDeletedPageDuplicateCount",
-          remaining_pages_with_url, 1, 20, 10);
-    }
-
-    // The histograms below are an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
-    // macro adapted to allow for a dynamically suffixed histogram name.
-    // Note: The factory creates and owns the histogram.
-    base::HistogramBase* histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"),
-        1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
-    histogram->Add((delete_time - page.creation_time).InMinutes());
-
-    histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(
-            client_id, "OfflinePages.DeletePage.TimeSinceLastOpen"),
-        1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
-    histogram->Add((delete_time - page.last_access_time).InMinutes());
-
-    histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(
-            client_id, "OfflinePages.DeletePage.LastOpenToCreated"),
-        1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
-    histogram->Add((page.last_access_time - page.creation_time).InMinutes());
-
-    // Reported as Kb between 1Kb and 10Mb.
-    histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"),
-        1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
-    histogram->Add(page.file_size / 1024);
-
-    histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"),
-        1, 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
-    histogram->Add(page.access_count);
-  }
-
-  if (deleted_pages.size() > 1) {
-    UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count",
-                         static_cast<int32_t>(deleted_pages.size()));
-    UMA_HISTOGRAM_MEMORY_KB(
-        "OfflinePages.BatchDelete.TotalPageSize", total_size / 1024);
-  }
-}
-
-void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item,
-                                     const base::Time& access_time) {
-  // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::Histogram::FactoryGet(
-        AddHistogramSuffix(
-            offline_page_item.client_id,
-            offline_page_item.access_count == 0 ?
-                "OfflinePages.FirstOpenSinceCreated" :
-                "OfflinePages.OpenSinceLastOpen"),
-        1, kMaxOpenedPageHistogramBucket.InMinutes(), 50,
-        base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(
-      (access_time - offline_page_item.last_access_time).InMinutes());
-}
-
-}  // namespace
-
-// protected
-OfflinePageModelImpl::OfflinePageModelImpl()
-    : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {}
-
-OfflinePageModelImpl::OfflinePageModelImpl(
-    std::unique_ptr<OfflinePageMetadataStore> store,
-    const base::FilePath& archives_dir,
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
-    : store_(std::move(store)),
-      archives_dir_(archives_dir),
-      is_loaded_(false),
-      policy_controller_(new ClientPolicyController()),
-      archive_manager_(new ArchiveManager(archives_dir, task_runner)),
-      testing_clock_(nullptr),
-      weak_ptr_factory_(this) {
-  archive_manager_->EnsureArchivesDirCreated(
-      base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone,
-                 weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()));
-}
-
-OfflinePageModelImpl::~OfflinePageModelImpl() {}
-
-void OfflinePageModelImpl::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void OfflinePageModelImpl::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void OfflinePageModelImpl::SavePage(
-    const SavePageParams& save_page_params,
-    std::unique_ptr<OfflinePageArchiver> archiver,
-    const SavePageCallback& callback) {
-  DCHECK(is_loaded_);
-
-  // Skip saving the page that is not intended to be saved, like local file
-  // page.
-  if (!OfflinePageModel::CanSaveURL(save_page_params.url)) {
-    InformSavePageDone(callback, SavePageResult::SKIPPED,
-                       save_page_params.client_id, kInvalidOfflineId);
-    return;
-  }
-
-  // The web contents is not available if archiver is not created and passed.
-  if (!archiver.get()) {
-    InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE,
-                       save_page_params.client_id, kInvalidOfflineId);
-    return;
-  }
-
-  // If we already have an offline id, use it.  If not, generate one.
-  int64_t offline_id = save_page_params.proposed_offline_id;
-  if (offline_id == kInvalidOfflineId)
-    offline_id = GenerateOfflineId();
-
-  archiver->CreateArchive(
-      archives_dir_, offline_id,
-      base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone,
-                 weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id,
-                 GetCurrentTime(), callback));
-  pending_archivers_.push_back(std::move(archiver));
-}
-
-void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) {
-  RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone,
-                           weak_ptr_factory_.GetWeakPtr(), offline_id));
-}
-
-void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) {
-  DCHECK(is_loaded_);
-
-  auto iter = offline_pages_.find(offline_id);
-  if (iter == offline_pages_.end())
-    return;
-
-  // Make a copy of the cached item and update it. The cached item should only
-  // be updated upon the successful store operation.
-  OfflinePageItem offline_page_item = iter->second;
-
-  ReportPageHistogramsAfterAccess(offline_page_item, GetCurrentTime());
-
-  offline_page_item.last_access_time = GetCurrentTime();
-  offline_page_item.access_count++;
-
-  std::vector<OfflinePageItem> items = { offline_page_item };
-  store_->UpdateOfflinePages(
-      items, base::Bind(&OfflinePageModelImpl::OnMarkPageAccesseDone,
-                        weak_ptr_factory_.GetWeakPtr(), offline_page_item));
-}
-
-void OfflinePageModelImpl::DeletePagesByOfflineId(
-    const std::vector<int64_t>& offline_ids,
-    const DeletePageCallback& callback) {
-  RunWhenLoaded(base::Bind(&OfflinePageModelImpl::DoDeletePagesByOfflineId,
-                           weak_ptr_factory_.GetWeakPtr(), offline_ids,
-                           callback));
-}
-
-void OfflinePageModelImpl::DoDeletePagesByOfflineId(
-    const std::vector<int64_t>& offline_ids,
-    const DeletePageCallback& callback) {
-  DCHECK(is_loaded_);
-
-  std::vector<base::FilePath> paths_to_delete;
-  for (const auto& offline_id : offline_ids) {
-    auto iter = offline_pages_.find(offline_id);
-    if (iter != offline_pages_.end()) {
-      paths_to_delete.push_back(iter->second.file_path);
-    }
-  }
-
-  // If there're no pages to delete, return early.
-  if (paths_to_delete.empty()) {
-    InformDeletePageDone(callback, DeletePageResult::SUCCESS);
-    return;
-  }
-
-  archive_manager_->DeleteMultipleArchives(
-      paths_to_delete,
-      base::Bind(&OfflinePageModelImpl::OnDeleteArchiveFilesDone,
-                 weak_ptr_factory_.GetWeakPtr(), offline_ids, callback));
-}
-
-void OfflinePageModelImpl::DeletePagesByClientIds(
-    const std::vector<ClientId>& client_ids,
-    const DeletePageCallback& callback) {
-  OfflinePageModelQueryBuilder builder;
-  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
-                       client_ids);
-  auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages,
-                                 weak_ptr_factory_.GetWeakPtr(), callback);
-  RunWhenLoaded(base::Bind(
-      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-      weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(builder.Build(GetPolicyController())), delete_pages));
-}
-
-void OfflinePageModelImpl::DeletePages(
-    const DeletePageCallback& callback,
-    const MultipleOfflinePageItemResult& pages) {
-  DCHECK(is_loaded_);
-
-  std::vector<int64_t> offline_ids;
-  for (auto& page : pages)
-    offline_ids.emplace_back(page.offline_id);
-
-  DoDeletePagesByOfflineId(offline_ids, callback);
-}
-
-void OfflinePageModelImpl::GetPagesMatchingQuery(
-    std::unique_ptr<OfflinePageModelQuery> query,
-    const MultipleOfflinePageItemCallback& callback) {
-  RunWhenLoaded(base::Bind(
-      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-      weak_ptr_factory_.GetWeakPtr(), base::Passed(&query), callback));
-}
-
-void OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone(
-    std::unique_ptr<OfflinePageModelQuery> query,
-    const MultipleOfflinePageItemCallback& callback) {
-  DCHECK(query);
-  DCHECK(is_loaded_);
-
-  MultipleOfflinePageItemResult offline_pages_result;
-
-  for (const auto& id_page_pair : offline_pages_) {
-    if (query->Matches(id_page_pair.second))
-      offline_pages_result.emplace_back(id_page_pair.second);
-  }
-
-  callback.Run(offline_pages_result);
-}
-
-void OfflinePageModelImpl::GetPagesByClientIds(
-    const std::vector<ClientId>& client_ids,
-    const MultipleOfflinePageItemCallback& callback) {
-  OfflinePageModelQueryBuilder builder;
-  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
-                       client_ids);
-  RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 base::Passed(builder.Build(GetPolicyController())), callback));
-}
-
-void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate(
-    const UrlPredicate& predicate,
-    const DeletePageCallback& callback) {
-  RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate,
-                 weak_ptr_factory_.GetWeakPtr(), predicate, callback));
-}
-
-void OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate(
-    const UrlPredicate& predicate,
-    const DeletePageCallback& callback) {
-  DCHECK(is_loaded_);
-
-  std::vector<int64_t> offline_ids;
-  for (const auto& id_page_pair : offline_pages_) {
-    if (IsRemovedOnCacheReset(id_page_pair.second) &&
-        predicate.Run(id_page_pair.second.url)) {
-      offline_ids.push_back(id_page_pair.first);
-    }
-  }
-  DoDeletePagesByOfflineId(offline_ids, callback);
-}
-
-void OfflinePageModelImpl::CheckPagesExistOffline(
-    const std::set<GURL>& urls,
-    const CheckPagesExistOfflineCallback& callback) {
-  OfflinePageModelQueryBuilder builder;
-  builder
-      .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
-               std::vector<GURL>(urls.begin(), urls.end()))
-      .RequireRestrictedToOriginalTab(
-          OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING);
-  auto pages_to_urls = base::Bind(
-      [](const CheckPagesExistOfflineCallback& callback,
-         const MultipleOfflinePageItemResult& pages) {
-        CheckPagesExistOfflineResult result;
-        for (auto& page : pages)
-          result.insert(page.url);
-        callback.Run(result);
-      },
-      callback);
-  RunWhenLoaded(base::Bind(
-      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-      weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(builder.Build(GetPolicyController())), pages_to_urls));
-}
-
-void OfflinePageModelImpl::GetAllPages(
-    const MultipleOfflinePageItemCallback& callback) {
-  OfflinePageModelQueryBuilder builder;
-  RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 base::Passed(builder.Build(GetPolicyController())), callback));
-}
-
-void OfflinePageModelImpl::GetOfflineIdsForClientId(
-    const ClientId& client_id,
-    const MultipleOfflineIdCallback& callback) {
-  RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone,
-                 weak_ptr_factory_.GetWeakPtr(), client_id, callback));
-}
-
-void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone(
-    const ClientId& client_id,
-    const MultipleOfflineIdCallback& callback) const {
-  DCHECK(is_loaded_);
-  callback.Run(MaybeGetOfflineIdsForClientId(client_id));
-}
-
-const std::vector<int64_t> OfflinePageModelImpl::MaybeGetOfflineIdsForClientId(
-    const ClientId& client_id) const {
-  DCHECK(is_loaded_);
-  std::vector<int64_t> results;
-
-  // We want only all pages, including those marked for deletion.
-  // TODO(fgorski): actually use an index rather than linear scan.
-  for (const auto& id_page_pair : offline_pages_) {
-    if (id_page_pair.second.client_id == client_id)
-      results.push_back(id_page_pair.second.offline_id);
-  }
-  return results;
-}
-
-void OfflinePageModelImpl::GetPageByOfflineId(
-    int64_t offline_id,
-    const SingleOfflinePageItemCallback& callback) {
-  std::vector<int64_t> query_ids;
-  query_ids.emplace_back(offline_id);
-
-  OfflinePageModelQueryBuilder builder;
-  builder.SetOfflinePageIds(
-      OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, query_ids);
-
-  auto multiple_callback = base::Bind(
-      [](const SingleOfflinePageItemCallback& callback,
-         const MultipleOfflinePageItemResult& result) {
-        DCHECK_LE(result.size(), 1U);
-        if (result.empty()) {
-          callback.Run(nullptr);
-        } else {
-          callback.Run(&result[0]);
-        }
-      },
-      callback);
-
-  RunWhenLoaded(base::Bind(
-      &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
-      weak_ptr_factory_.GetWeakPtr(),
-      base::Passed(builder.Build(GetPolicyController())), multiple_callback));
-}
-
-void OfflinePageModelImpl::GetPagesByURL(
-    const GURL& url,
-    URLSearchMode url_search_mode,
-    const MultipleOfflinePageItemCallback& callback) {
-  RunWhenLoaded(
-      base::Bind(&OfflinePageModelImpl::GetPagesByURLWhenLoadDone,
-                 weak_ptr_factory_.GetWeakPtr(), url,
-                 url_search_mode, callback));
-}
-
-void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
-    const GURL& url,
-    URLSearchMode url_search_mode,
-    const MultipleOfflinePageItemCallback& callback) const {
-  DCHECK(is_loaded_);
-  std::vector<OfflinePageItem> result;
-
-  GURL::Replacements remove_params;
-  remove_params.ClearRef();
-
-  GURL url_without_fragment =
-      url.ReplaceComponents(remove_params);
-
-  for (const auto& id_page_pair : offline_pages_) {
-    // First, search by last committed URL with fragment stripped.
-    if (url_without_fragment ==
-            id_page_pair.second.url.ReplaceComponents(remove_params)) {
-      result.push_back(id_page_pair.second);
-      continue;
-    }
-    // Then, search by original request URL if |url_search_mode| wants it.
-    // Note that we want to do the exact match with fragment included. This is
-    // because original URL is used for redirect purpose and it is always safer
-    // to support the exact redirect.
-    if (url_search_mode == URLSearchMode::SEARCH_BY_ALL_URLS &&
-        url == id_page_pair.second.original_url) {
-      result.push_back(id_page_pair.second);
-    }
-  }
-
-  callback.Run(result);
-}
-
-void OfflinePageModelImpl::CheckMetadataConsistency() {
-  archive_manager_->GetAllArchives(
-      base::Bind(&OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-ClientPolicyController* OfflinePageModelImpl::GetPolicyController() {
-  return policy_controller_.get();
-}
-
-OfflinePageMetadataStore* OfflinePageModelImpl::GetStoreForTesting() {
-  return store_.get();
-}
-
-OfflinePageStorageManager* OfflinePageModelImpl::GetStorageManager() {
-  return storage_manager_.get();
-}
-
-bool OfflinePageModelImpl::is_loaded() const {
-  return is_loaded_;
-}
-
-OfflineEventLogger* OfflinePageModelImpl::GetLogger() {
-  return &offline_event_logger_;
-}
-
-void OfflinePageModelImpl::OnCreateArchiveDone(
-    const SavePageParams& save_page_params,
-    int64_t offline_id,
-    const base::Time& start_time,
-    const SavePageCallback& callback,
-    OfflinePageArchiver* archiver,
-    ArchiverResult archiver_result,
-    const GURL& url,
-    const base::FilePath& file_path,
-    const base::string16& title,
-    int64_t file_size) {
-  if (save_page_params.url != url) {
-    DVLOG(1) << "Saved URL does not match requested URL.";
-    // TODO(fgorski): We have created an archive for a wrong URL. It should be
-    // deleted from here, once archiver has the right functionality.
-    InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
-                       save_page_params.client_id, offline_id);
-    DeletePendingArchiver(archiver);
-    return;
-  }
-
-  if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
-    SavePageResult result = ToSavePageResult(archiver_result);
-    InformSavePageDone(
-        callback, result, save_page_params.client_id, offline_id);
-    DeletePendingArchiver(archiver);
-    return;
-  }
-  OfflinePageItem offline_page_item(url, offline_id, save_page_params.client_id,
-                                    file_path, file_size, start_time);
-  offline_page_item.title = title;
-  offline_page_item.original_url = save_page_params.original_url;
-  store_->AddOfflinePage(offline_page_item,
-                         base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
-                                    weak_ptr_factory_.GetWeakPtr(), archiver,
-                                    callback, offline_page_item));
-}
-
-void OfflinePageModelImpl::OnAddOfflinePageDone(
-    OfflinePageArchiver* archiver,
-    const SavePageCallback& callback,
-    const OfflinePageItem& offline_page,
-    ItemActionStatus status) {
-  SavePageResult result;
-  if (status == ItemActionStatus::SUCCESS) {
-    offline_pages_[offline_page.offline_id] = offline_page;
-    result = SavePageResult::SUCCESS;
-    ReportPageHistogramAfterSave(policy_controller_.get(), offline_pages_,
-                                 offline_page, GetCurrentTime());
-    offline_event_logger_.RecordPageSaved(
-        offline_page.client_id.name_space, offline_page.url.spec(),
-        std::to_string(offline_page.offline_id));
-  } else if (status == ItemActionStatus::ALREADY_EXISTS) {
-    result = SavePageResult::ALREADY_EXISTS;
-  } else {
-    result = SavePageResult::STORE_FAILURE;
-  }
-  InformSavePageDone(callback, result, offline_page.client_id,
-                     offline_page.offline_id);
-  if (result == SavePageResult::SUCCESS) {
-    DeleteExistingPagesWithSameURL(offline_page);
-  } else {
-    PostClearStorageIfNeededTask(false /* delayed */);
-  }
-
-  DeletePendingArchiver(archiver);
-  for (Observer& observer : observers_)
-    observer.OfflinePageModelChanged(this);
-}
-
-void OfflinePageModelImpl::OnMarkPageAccesseDone(
-    const OfflinePageItem& offline_page_item,
-    std::unique_ptr<OfflinePagesUpdateResult> result) {
-  // Update the item in the cache only upon success.
-  if (result->updated_items.size() > 0)
-    offline_pages_[offline_page_item.offline_id] = offline_page_item;
-
-  // No need to fire OfflinePageModelChanged event since updating access info
-  // should not have any impact to the UI.
-}
-
-void OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone(
-    const base::TimeTicks& start_time) {
-  UMA_HISTOGRAM_TIMES("OfflinePages.Model.ArchiveDirCreationTime",
-                      base::TimeTicks::Now() - start_time);
-
-  const int kResetAttemptsLeft = 1;
-  store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
-                                weak_ptr_factory_.GetWeakPtr(), start_time,
-                                kResetAttemptsLeft));
-}
-
-void OfflinePageModelImpl::OnStoreInitialized(const base::TimeTicks& start_time,
-                                              int reset_attempts_left,
-                                              bool success) {
-  if (success) {
-    DCHECK_EQ(store_->state(), StoreState::LOADED);
-    store_->GetOfflinePages(
-        base::Bind(&OfflinePageModelImpl::OnInitialGetOfflinePagesDone,
-                   weak_ptr_factory_.GetWeakPtr(), start_time));
-    return;
-  }
-
-  DCHECK_EQ(store_->state(), StoreState::FAILED_LOADING);
-  // If there are no more reset attempts left, stop here.
-  if (reset_attempts_left == 0) {
-    FinalizeModelLoad();
-    return;
-  }
-
-  // Otherwise reduce the remaining attempts counter and reset store.
-  store_->Reset(base::Bind(&OfflinePageModelImpl::OnStoreResetDone,
-                           weak_ptr_factory_.GetWeakPtr(), start_time,
-                           reset_attempts_left - 1));
-}
-
-void OfflinePageModelImpl::OnStoreResetDone(const base::TimeTicks& start_time,
-                                            int reset_attempts_left,
-                                            bool success) {
-  if (success) {
-    DCHECK_EQ(store_->state(), StoreState::NOT_LOADED);
-    store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
-                                  weak_ptr_factory_.GetWeakPtr(), start_time,
-                                  reset_attempts_left));
-    return;
-  }
-
-  DCHECK_EQ(store_->state(), StoreState::FAILED_RESET);
-  FinalizeModelLoad();
-}
-
-void OfflinePageModelImpl::OnInitialGetOfflinePagesDone(
-    const base::TimeTicks& start_time,
-    const std::vector<OfflinePageItem>& offline_pages) {
-  DCHECK(!is_loaded_);
-
-  UMA_HISTOGRAM_TIMES("OfflinePages.Model.ConstructionToLoadedEventTime",
-                      base::TimeTicks::Now() - start_time);
-
-  CacheLoadedData(offline_pages);
-  FinalizeModelLoad();
-
-  // Ensure necessary cleanup operations are started.
-  CheckMetadataConsistency();
-}
-
-void OfflinePageModelImpl::FinalizeModelLoad() {
-  is_loaded_ = true;
-
-  // All actions below are meant to be taken regardless of successful load of
-  // the store.
-
-  // Inform observers the load is done.
-  for (Observer& observer : observers_)
-    observer.OfflinePageModelLoaded(this);
-
-  // Run all the delayed tasks.
-  for (const auto& delayed_task : delayed_tasks_)
-    delayed_task.Run();
-  delayed_tasks_.clear();
-
-  // Clear storage.
-  PostClearStorageIfNeededTask(true /* delayed */);
-}
-
-void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback,
-                                              SavePageResult result,
-                                              const ClientId& client_id,
-                                              int64_t offline_id) {
-  ReportSavePageResultHistogramAfterSave(client_id, result);
-  archive_manager_->GetStorageStats(
-      base::Bind(&ReportStorageHistogramsAfterSave));
-  callback.Run(result, offline_id);
-}
-
-void OfflinePageModelImpl::DeleteExistingPagesWithSameURL(
-    const OfflinePageItem& offline_page) {
-  // Remove existing pages generated by the same policy and with same url.
-  size_t pages_allowed =
-      policy_controller_->GetPolicy(offline_page.client_id.name_space)
-          .pages_allowed_per_url;
-  if (pages_allowed == kUnlimitedPages)
-    return;
-  GetPagesByURL(
-      offline_page.url,
-      URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
-      base::Bind(&OfflinePageModelImpl::OnPagesFoundWithSameURL,
-                 weak_ptr_factory_.GetWeakPtr(), offline_page, pages_allowed));
-}
-
-void OfflinePageModelImpl::OnPagesFoundWithSameURL(
-    const OfflinePageItem& offline_page,
-    size_t pages_allowed,
-    const MultipleOfflinePageItemResult& items) {
-  std::vector<OfflinePageItem> pages_to_delete;
-  for (const auto& item : items) {
-    if (item.offline_id != offline_page.offline_id &&
-        item.client_id.name_space == offline_page.client_id.name_space) {
-      pages_to_delete.push_back(item);
-    }
-  }
-  // Only keep |pages_allowed|-1 of most fresh pages and delete others, by
-  // sorting pages with the oldest  ones first and resize the vector.
-  if (pages_to_delete.size() >= pages_allowed) {
-    sort(pages_to_delete.begin(), pages_to_delete.end(),
-         [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
-           return a.last_access_time < b.last_access_time;
-         });
-    pages_to_delete.resize(pages_to_delete.size() - pages_allowed + 1);
-  }
-  std::vector<int64_t> page_ids_to_delete;
-  for (const auto& item : pages_to_delete)
-    page_ids_to_delete.push_back(item.offline_id);
-  DeletePagesByOfflineId(
-      page_ids_to_delete,
-      base::Bind(&OfflinePageModelImpl::OnDeleteOldPagesWithSameURL,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void OfflinePageModelImpl::OnDeleteOldPagesWithSameURL(
-    DeletePageResult result) {
-  // TODO(romax) Add UMAs for failure cases.
-  PostClearStorageIfNeededTask(false /* delayed */);
-}
-
-void OfflinePageModelImpl::DeletePendingArchiver(
-    OfflinePageArchiver* archiver) {
-  pending_archivers_.erase(std::find(pending_archivers_.begin(),
-                                     pending_archivers_.end(), archiver));
-}
-
-void OfflinePageModelImpl::OnDeleteArchiveFilesDone(
-    const std::vector<int64_t>& offline_ids,
-    const DeletePageCallback& callback,
-    bool success) {
-  if (!success) {
-    InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE);
-    return;
-  }
-
-  store_->RemoveOfflinePages(
-      offline_ids, base::Bind(&OfflinePageModelImpl::OnRemoveOfflinePagesDone,
-                              weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void OfflinePageModelImpl::OnRemoveOfflinePagesDone(
-    const DeletePageCallback& callback,
-    std::unique_ptr<OfflinePagesUpdateResult> result) {
-  ReportPageHistogramsAfterDelete(offline_pages_, result->updated_items,
-                                  GetCurrentTime());
-
-  // This part of the loop is explicitly broken out, as it should be gone in
-  // fully asynchronous code.
-  for (const auto& page : result->updated_items) {
-    int64_t offline_id = page.offline_id;
-    offline_event_logger_.RecordPageDeleted(std::to_string(offline_id));
-    auto iter = offline_pages_.find(offline_id);
-    if (iter == offline_pages_.end())
-      continue;
-    offline_pages_.erase(iter);
-  }
-
-  for (const auto& page : result->updated_items) {
-    for (Observer& observer : observers_)
-      observer.OfflinePageDeleted(page.offline_id, page.client_id);
-  }
-
-  // TODO(fgorski): React the FAILED_INITIALIZATION, FAILED_RESET here.
-  // TODO(fgorski): We need a better callback interface for the Remove action on
-  // the this class. Currently removing an item that does not exist is
-  // considered a success, but not called out as such to the caller.
-  DeletePageResult delete_result;
-  if (result->store_state == StoreState::LOADED)
-    delete_result = DeletePageResult::SUCCESS;
-  else
-    delete_result = DeletePageResult::STORE_FAILURE;
-
-  InformDeletePageDone(callback, delete_result);
-}
-
-void OfflinePageModelImpl::InformDeletePageDone(
-    const DeletePageCallback& callback,
-    DeletePageResult result) {
-  UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult",
-                            static_cast<int>(result),
-                            static_cast<int>(DeletePageResult::RESULT_COUNT));
-  archive_manager_->GetStorageStats(
-      base::Bind(&ReportStorageHistogramsAfterDelete));
-  if (!callback.is_null())
-    callback.Run(result);
-}
-
-void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths(
-    const std::set<base::FilePath>& archive_paths) {
-  DeletePagesMissingArchiveFile(archive_paths);
-  DeleteOrphanedArchives(archive_paths);
-}
-
-void OfflinePageModelImpl::DeletePagesMissingArchiveFile(
-    const std::set<base::FilePath>& archive_paths) {
-  std::vector<int64_t> ids_of_pages_missing_archive_file;
-  for (const auto& id_page_pair : offline_pages_) {
-    if (archive_paths.count(id_page_pair.second.file_path) == 0UL)
-      ids_of_pages_missing_archive_file.push_back(id_page_pair.first);
-  }
-
-  if (ids_of_pages_missing_archive_file.empty())
-    return;
-
-  DeletePagesByOfflineId(
-      ids_of_pages_missing_archive_file,
-      base::Bind(&OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 ids_of_pages_missing_archive_file));
-}
-
-void OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone(
-    const std::vector<int64_t>& offline_ids,
-    DeletePageResult result) {
-  UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount",
-                       static_cast<int32_t>(offline_ids.size()));
-  UMA_HISTOGRAM_ENUMERATION(
-      "OfflinePages.Consistency.DeletePagesMissingArchiveFileResult",
-      static_cast<int>(result),
-      static_cast<int>(DeletePageResult::RESULT_COUNT));
-}
-
-void OfflinePageModelImpl::DeleteOrphanedArchives(
-    const std::set<base::FilePath>& archive_paths) {
-  // Archives are considered orphaned unless they are pointed to by some pages.
-  std::set<base::FilePath> orphaned_archive_set(archive_paths);
-  for (const auto& id_page_pair : offline_pages_)
-    orphaned_archive_set.erase(id_page_pair.second.file_path);
-
-  if (orphaned_archive_set.empty())
-    return;
-
-  std::vector<base::FilePath> orphaned_archives(orphaned_archive_set.begin(),
-                                                orphaned_archive_set.end());
-  archive_manager_->DeleteMultipleArchives(
-      orphaned_archives,
-      base::Bind(&OfflinePageModelImpl::OnDeleteOrphanedArchivesDone,
-                 weak_ptr_factory_.GetWeakPtr(), orphaned_archives));
-}
-
-void OfflinePageModelImpl::OnDeleteOrphanedArchivesDone(
-    const std::vector<base::FilePath>& archives,
-    bool success) {
-  UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.OrphanedArchivesCount",
-                       static_cast<int32_t>(archives.size()));
-  UMA_HISTOGRAM_BOOLEAN("OfflinePages.Consistency.DeleteOrphanedArchivesResult",
-                        success);
-}
-
-void OfflinePageModelImpl::CacheLoadedData(
-    const std::vector<OfflinePageItem>& offline_pages) {
-  offline_pages_.clear();
-  for (const auto& offline_page : offline_pages)
-    offline_pages_[offline_page.offline_id] = offline_page;
-}
-
-void OfflinePageModelImpl::ClearStorageIfNeeded(
-    const ClearStorageCallback& callback) {
-  // Create Storage Manager if necessary.
-  if (!storage_manager_) {
-    storage_manager_.reset(new OfflinePageStorageManager(
-        this, GetPolicyController(), archive_manager_.get()));
-  }
-  storage_manager_->ClearPagesIfNeeded(callback);
-}
-
-void OfflinePageModelImpl::OnStorageCleared(size_t deleted_page_count,
-                                            ClearStorageResult result) {
-  UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult",
-                            static_cast<int>(result),
-                            static_cast<int>(ClearStorageResult::RESULT_COUNT));
-  if (deleted_page_count > 0) {
-    UMA_HISTOGRAM_COUNTS("OfflinePages.ClearStorageBatchSize",
-                         static_cast<int32_t>(deleted_page_count));
-  }
-}
-
-void OfflinePageModelImpl::PostClearStorageIfNeededTask(bool delayed) {
-  base::TimeDelta delay =
-      delayed ? kStorageManagerStartingDelay : base::TimeDelta();
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded,
-                            weak_ptr_factory_.GetWeakPtr(),
-                            base::Bind(&OfflinePageModelImpl::OnStorageCleared,
-                                       weak_ptr_factory_.GetWeakPtr())),
-      delay);
-}
-
-bool OfflinePageModelImpl::IsRemovedOnCacheReset(
-    const OfflinePageItem& offline_page) const {
-  return policy_controller_->IsRemovedOnCacheReset(
-      offline_page.client_id.name_space);
-}
-
-void OfflinePageModelImpl::RunWhenLoaded(const base::Closure& task) {
-  if (!is_loaded_) {
-    delayed_tasks_.push_back(task);
-    return;
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task);
-}
-
-base::Time OfflinePageModelImpl::GetCurrentTime() const {
-  return testing_clock_ ? testing_clock_->Now() : base::Time::Now();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_model_impl.h b/components/offline_pages/offline_page_model_impl.h
deleted file mode 100644
index 29ae109..0000000
--- a/components/offline_pages/offline_page_model_impl.h
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/optional.h"
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/offline_page_archiver.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
-#include "components/offline_pages/offline_page_model.h"
-#include "components/offline_pages/offline_page_model_event_logger.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_types.h"
-#include "components/offline_pages/offline_store_types.h"
-
-class GURL;
-namespace base {
-class Clock;
-class SequencedTaskRunner;
-class TimeTicks;
-}  // namespace base
-
-namespace offline_pages {
-
-struct ClientId;
-struct OfflinePageItem;
-
-class ArchiveManager;
-class ClientPolicyController;
-class OfflinePageModelQuery;
-class OfflinePageStorageManager;
-
-// Implementation of service for saving pages offline, storing the offline
-// copy and metadata, and retrieving them upon request.
-class OfflinePageModelImpl : public OfflinePageModel, public KeyedService {
- public:
-  // All blocking calls/disk access will happen on the provided |task_runner|.
-  OfflinePageModelImpl(
-      std::unique_ptr<OfflinePageMetadataStore> store,
-      const base::FilePath& archives_dir,
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
-  ~OfflinePageModelImpl() override;
-
-  // Implemented methods:
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
-  void SavePage(const SavePageParams& save_page_params,
-                std::unique_ptr<OfflinePageArchiver> archiver,
-                const SavePageCallback& callback) override;
-  void MarkPageAccessed(int64_t offline_id) override;
-  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
-                              const DeletePageCallback& callback) override;
-  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
-                              const DeletePageCallback& callback) override;
-
-  void GetPagesMatchingQuery(
-      std::unique_ptr<OfflinePageModelQuery> query,
-      const MultipleOfflinePageItemCallback& callback) override;
-
-  void GetPagesByClientIds(
-      const std::vector<ClientId>& client_ids,
-      const MultipleOfflinePageItemCallback& callback) override;
-
-  void DeleteCachedPagesByURLPredicate(
-      const UrlPredicate& predicate,
-      const DeletePageCallback& callback) override;
-  void CheckPagesExistOffline(
-      const std::set<GURL>& urls,
-      const CheckPagesExistOfflineCallback& callback) override;
-  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
-  void GetOfflineIdsForClientId(
-      const ClientId& client_id,
-      const MultipleOfflineIdCallback& callback) override;
-  void GetPageByOfflineId(
-      int64_t offline_id,
-      const SingleOfflinePageItemCallback& callback) override;
-  void GetPagesByURL(
-      const GURL& url,
-      URLSearchMode url_search_mode,
-      const MultipleOfflinePageItemCallback& callback) override;
-  ClientPolicyController* GetPolicyController() override;
-
-  // Methods for testing only:
-  OfflinePageMetadataStore* GetStoreForTesting();
-  void set_testing_clock(base::Clock* clock) { testing_clock_ = clock; }
-
-  OfflinePageStorageManager* GetStorageManager();
-
-  bool is_loaded() const override;
-
-  OfflineEventLogger* GetLogger() override;
-
- protected:
-  // Adding a protected constructor for testing-only purposes in
-  // offline_page_storage_manager_unittest.cc
-  OfflinePageModelImpl();
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(OfflinePageModelImplTest, MarkPageForDeletion);
-
-  typedef ScopedVector<OfflinePageArchiver> PendingArchivers;
-
-  // Callback for ensuring archive directory is created.
-  void OnEnsureArchivesDirCreatedDone(const base::TimeTicks& start_time);
-
-  void GetPagesMatchingQueryWhenLoadDone(
-      std::unique_ptr<OfflinePageModelQuery> query,
-      const MultipleOfflinePageItemCallback& callback);
-  void GetOfflineIdsForClientIdWhenLoadDone(
-      const ClientId& client_id,
-      const MultipleOfflineIdCallback& callback) const;
-  const std::vector<int64_t> MaybeGetOfflineIdsForClientId(
-      const ClientId& client_id) const;
-  void GetPagesByURLWhenLoadDone(
-      const GURL& url,
-      URLSearchMode url_search_mode,
-      const MultipleOfflinePageItemCallback& callback) const;
-  void MarkPageAccessedWhenLoadDone(int64_t offline_id);
-
-  // Check the consistency between metadata store and archives on disk,
-  // would delete the metadata entries which don't have an associated
-  // archive and the archives which doesn't have a metadata in the store.
-  // The expired pages (from previous versions) would also be cleared
-  // during this process.
-  void CheckMetadataConsistency();
-
-  // Callback for loading pages from the offline page metadata store.
-  void OnStoreInitialized(const base::TimeTicks& start_time,
-                          int reset_attempts_left,
-                          bool success);
-  void OnStoreResetDone(const base::TimeTicks& start_time,
-                        int reset_attempts_left,
-                        bool success);
-  void OnInitialGetOfflinePagesDone(
-      const base::TimeTicks& start_time,
-      const std::vector<OfflinePageItem>& offline_pages);
-  void FinalizeModelLoad();
-
-  // Steps for saving a page offline.
-  void OnCreateArchiveDone(const SavePageParams& save_page_params,
-                           int64_t offline_id,
-                           const base::Time& start_time,
-                           const SavePageCallback& callback,
-                           OfflinePageArchiver* archiver,
-                           OfflinePageArchiver::ArchiverResult result,
-                           const GURL& url,
-                           const base::FilePath& file_path,
-                           const base::string16& title,
-                           int64_t file_size);
-  void OnAddOfflinePageDone(OfflinePageArchiver* archiver,
-                            const SavePageCallback& callback,
-                            const OfflinePageItem& offline_page,
-                            ItemActionStatus status);
-  void InformSavePageDone(const SavePageCallback& callback,
-                          SavePageResult result,
-                          const ClientId& client_id,
-                          int64_t offline_id);
-  void DeletePendingArchiver(OfflinePageArchiver* archiver);
-
-  // Steps for deleting files and data for an offline page.
-  void OnDeleteArchiveFilesDone(const std::vector<int64_t>& offline_ids,
-                                const DeletePageCallback& callback,
-                                bool success);
-  void OnRemoveOfflinePagesDone(
-      const DeletePageCallback& callback,
-      std::unique_ptr<OfflinePagesUpdateResult> result);
-  void InformDeletePageDone(const DeletePageCallback& callback,
-                            DeletePageResult result);
-
-  void OnMarkPageAccesseDone(const OfflinePageItem& offline_page_item,
-                             std::unique_ptr<OfflinePagesUpdateResult> result);
-
-  // Callbacks for checking metadata consistency.
-  void CheckMetadataConsistencyForArchivePaths(
-      const std::set<base::FilePath>& archive_paths);
-  // Callbacks which would be called after orphaned archives are deleted.
-  // Orphaned archives are the files on disk which are not pointed to by any of
-  // the page entries in the metadata store.
-  void DeletePagesMissingArchiveFile(
-      const std::set<base::FilePath>& archive_paths);
-  void OnDeletePagesMissingArchiveFileDone(
-      const std::vector<int64_t>& offline_ids,
-      DeletePageResult result);
-  void DeleteOrphanedArchives(const std::set<base::FilePath>& archive_paths);
-  void OnDeleteOrphanedArchivesDone(const std::vector<base::FilePath>& archives,
-                                    bool success);
-
-  // Callbacks for deleting pages with same URL when saving pages.
-  void DeleteExistingPagesWithSameURL(const OfflinePageItem& offline_page);
-  void OnPagesFoundWithSameURL(const OfflinePageItem& offline_page,
-                               size_t pages_allowed,
-                               const MultipleOfflinePageItemResult& items);
-  void OnDeleteOldPagesWithSameURL(DeletePageResult result);
-
-  void CacheLoadedData(const std::vector<OfflinePageItem>& offline_pages);
-
-  // Actually does the work of deleting, requires the model is loaded.
-  void DoDeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
-                                const DeletePageCallback& callback);
-
-  // Actually does the work of deleting, requires the model is loaded.
-  void DeletePages(const DeletePageCallback& callback,
-                   const MultipleOfflinePageItemResult& items);
-
-  void DoGetPagesByClientIds(const std::vector<ClientId>& client_ids,
-                             const MultipleOfflinePageItemCallback& callback);
-
-  // Similar to DoDeletePagesByOfflineId, does actual work of deleting, and
-  // requires that the model is loaded.
-  void DoDeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
-                                         const DeletePageCallback& callback);
-
-  // Clears expired pages if there are any or we're running out of storage.
-  void ClearStorageIfNeeded(
-      const OfflinePageStorageManager::ClearStorageCallback& callback);
-
-  // Callback completing storage clearing.
-  void OnStorageCleared(size_t cleared_page_count,
-                        OfflinePageStorageManager::ClearStorageResult result);
-
-  // Post task to clear storage.
-  void PostClearStorageIfNeededTask(bool delayed);
-
-  // Check if |offline_page| should be removed on cache reset by user.
-  bool IsRemovedOnCacheReset(const OfflinePageItem& offline_page) const;
-
-  void RunWhenLoaded(const base::Closure& job);
-
-  base::Time GetCurrentTime() const;
-
-  // Persistent store for offline page metadata.
-  std::unique_ptr<OfflinePageMetadataStore> store_;
-
-  // Location where all of the archive files will be stored.
-  base::FilePath archives_dir_;
-
-  // The observers.
-  base::ObserverList<Observer> observers_;
-
-  bool is_loaded_;
-
-  // In memory copy of the offline page metadata, keyed by offline IDs.
-  std::map<int64_t, OfflinePageItem> offline_pages_;
-
-  // Pending archivers owned by this model.
-  PendingArchivers pending_archivers_;
-
-  // Delayed tasks that should be invoked after the loading is done.
-  std::vector<base::Closure> delayed_tasks_;
-
-  // Controller of the client policies.
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-
-  // Manager for the storage consumed by archives and responsible for
-  // automatic page clearing.
-  std::unique_ptr<OfflinePageStorageManager> storage_manager_;
-
-  // Manager for the offline archive files and directory.
-  std::unique_ptr<ArchiveManager> archive_manager_;
-
-  // Logger to facilitate recording of events.
-  OfflinePageModelEventLogger offline_event_logger_;
-
-  // Clock for getting time in testing code. The setter is responsible to reset
-  // it once it is not longer needed.
-  base::Clock* testing_clock_;
-
-  base::WeakPtrFactory<OfflinePageModelImpl> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelImpl);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_IMPL_H_
diff --git a/components/offline_pages/offline_page_model_impl_unittest.cc b/components/offline_pages/offline_page_model_impl_unittest.cc
deleted file mode 100644
index 7dfa2e5d..0000000
--- a/components/offline_pages/offline_page_model_impl_unittest.cc
+++ /dev/null
@@ -1,1316 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model_impl.h"
-
-#include <stdint.h>
-#include <algorithm>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-#include "components/offline_pages/offline_page_feature.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_storage_manager.h"
-#include "components/offline_pages/offline_page_test_archiver.h"
-#include "components/offline_pages/offline_page_test_store.h"
-#include "components/offline_pages/offline_page_types.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-namespace {
-const char kTestClientNamespace[] = "default";
-const char kUserRequestedNamespace[] = "download";
-const GURL kTestUrl("http://example.com");
-const GURL kTestUrl2("http://other.page.com");
-const GURL kTestUrl3("http://test.xyz");
-const GURL kTestUrl4("http://page.net");
-const GURL kFileUrl("file:///foo");
-const GURL kTestUrlWithFragment("http://example.com#frag");
-const GURL kTestUrl2WithFragment("http://other.page.com#frag");
-const GURL kTestUrl2WithFragment2("http://other.page.com#frag2");
-const ClientId kTestClientId1(kTestClientNamespace, "1234");
-const ClientId kTestClientId2(kTestClientNamespace, "5678");
-const ClientId kTestClientId3(kTestClientNamespace, "42");
-const ClientId kTestUserRequestedClientId(kUserRequestedNamespace, "714");
-const int64_t kTestFileSize = 876543LL;
-const base::string16 kTestTitle = base::UTF8ToUTF16("a title");
-
-bool URLSpecContains(std::string contains_value, const GURL& url) {
-  std::string spec = url.spec();
-  return spec.find(contains_value) != std::string::npos;
-}
-
-}  // namespace
-
-class OfflinePageModelImplTest
-    : public testing::Test,
-      public OfflinePageModel::Observer,
-      public OfflinePageTestArchiver::Observer,
-      public base::SupportsWeakPtr<OfflinePageModelImplTest> {
- public:
-  OfflinePageModelImplTest();
-  ~OfflinePageModelImplTest() override;
-
-  void SetUp() override;
-  void TearDown() override;
-
-  // OfflinePageModel::Observer implementation.
-  void OfflinePageModelLoaded(OfflinePageModel* model) override;
-  void OfflinePageModelChanged(OfflinePageModel* model) override;
-  void OfflinePageDeleted(int64_t offline_id,
-                          const ClientId& client_id) override;
-
-  // OfflinePageTestArchiver::Observer implementation.
-  void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
-
-  // OfflinePageModel callbacks.
-  void OnSavePageDone(SavePageResult result, int64_t offline_id);
-  void OnDeletePageDone(DeletePageResult result);
-  void OnCheckPagesExistOfflineDone(const CheckPagesExistOfflineResult& result);
-  void OnGetOfflineIdsForClientIdDone(MultipleOfflineIdResult* storage,
-                                      const MultipleOfflineIdResult& result);
-  void OnGetSingleOfflinePageItemResult(
-      std::unique_ptr<OfflinePageItem>* storage,
-      const OfflinePageItem* result);
-  void OnGetMultipleOfflinePageItemsResult(
-      MultipleOfflinePageItemResult* storage,
-      const MultipleOfflinePageItemResult& result);
-  void OnPagesExpired(bool result);
-
-  // OfflinePageMetadataStore callbacks.
-  void OnStoreUpdateDone(bool /* success */);
-
-  std::unique_ptr<OfflinePageTestArchiver> BuildArchiver(
-      const GURL& url,
-      OfflinePageArchiver::ArchiverResult result);
-  std::unique_ptr<OfflinePageMetadataStore> BuildStore();
-  std::unique_ptr<OfflinePageModelImpl> BuildModel(
-      std::unique_ptr<OfflinePageMetadataStore> store);
-  void ResetModel();
-
-  // Utility methods.
-  // Runs until all of the tasks that are not delayed are gone from the task
-  // queue.
-  void PumpLoop();
-  // Fast-forwards virtual time by |delta|, causing tasks with a remaining
-  // delay less than or equal to |delta| to be executed.
-  void FastForwardBy(base::TimeDelta delta);
-  void ResetResults();
-
-  OfflinePageTestStore* GetStore();
-
-  MultipleOfflinePageItemResult GetAllPages();
-  MultipleOfflinePageItemResult GetPagesByClientIds(
-      const std::vector<ClientId>& client_ids);
-  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids);
-
-  // Saves the page without waiting for it to finish.
-  void SavePageWithArchiverAsync(
-      const GURL& url,
-      const ClientId& client_id,
-      const GURL& original_url,
-      std::unique_ptr<OfflinePageArchiver> archiver);
-
-  // All 3 methods below will wait for the save to finish.
-  void SavePageWithArchiver(
-      const GURL& url,
-      const ClientId& client_id,
-      std::unique_ptr<OfflinePageArchiver> archiver);
-  void SavePageWithArchiverResult(const GURL& url,
-                                  const ClientId& client_id,
-                                  OfflinePageArchiver::ArchiverResult result);
-  // Returns the offline ID of the saved page.
-  std::pair<SavePageResult, int64_t> SavePage(const GURL& url,
-                                              const ClientId& client_id);
-
-  void DeletePage(int64_t offline_id, const DeletePageCallback& callback) {
-    std::vector<int64_t> offline_ids;
-    offline_ids.push_back(offline_id);
-    model()->DeletePagesByOfflineId(offline_ids, callback);
-  }
-
-  bool HasPages(std::string name_space);
-
-  CheckPagesExistOfflineResult CheckPagesExistOffline(std::set<GURL>);
-
-  MultipleOfflineIdResult GetOfflineIdsForClientId(const ClientId& client_id);
-
-  std::unique_ptr<OfflinePageItem> GetPageByOfflineId(int64_t offline_id);
-
-  MultipleOfflinePageItemResult GetPagesByFinalURL(const GURL& url);
-  MultipleOfflinePageItemResult GetPagesByAllURLS(const GURL& url);
-
-  OfflinePageModelImpl* model() { return model_.get(); }
-
-  int64_t last_save_offline_id() const { return last_save_offline_id_; }
-
-  SavePageResult last_save_result() const { return last_save_result_; }
-
-  DeletePageResult last_delete_result() const { return last_delete_result_; }
-
-  int64_t last_deleted_offline_id() const { return last_deleted_offline_id_; }
-
-  ClientId last_deleted_client_id() const { return last_deleted_client_id_; }
-
-  const base::FilePath& last_archiver_path() { return last_archiver_path_; }
-
-  int last_cleared_pages_count() const { return last_cleared_pages_count_; }
-
-  DeletePageResult last_clear_page_result() const {
-    return last_clear_page_result_;
-  }
-
-  bool last_expire_page_result() const { return last_expire_page_result_; }
-
-  const base::HistogramTester& histograms() const { return histogram_tester_; }
-
- private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
-  base::ScopedTempDir temp_dir_;
-
-  std::unique_ptr<OfflinePageModelImpl> model_;
-  SavePageResult last_save_result_;
-  int64_t last_save_offline_id_;
-  DeletePageResult last_delete_result_;
-  base::FilePath last_archiver_path_;
-  int64_t last_deleted_offline_id_;
-  ClientId last_deleted_client_id_;
-  CheckPagesExistOfflineResult last_pages_exist_result_;
-  int last_cleared_pages_count_;
-  DeletePageResult last_clear_page_result_;
-  bool last_expire_page_result_;
-
-  base::HistogramTester histogram_tester_;
-};
-
-OfflinePageModelImplTest::OfflinePageModelImplTest()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      task_runner_handle_(task_runner_),
-      last_save_result_(SavePageResult::CANCELLED),
-      last_save_offline_id_(-1),
-      last_delete_result_(DeletePageResult::CANCELLED),
-      last_deleted_offline_id_(-1) {}
-
-OfflinePageModelImplTest::~OfflinePageModelImplTest() {}
-
-void OfflinePageModelImplTest::SetUp() {
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  model_ = BuildModel(BuildStore());
-  model_->AddObserver(this);
-  PumpLoop();
-}
-
-void OfflinePageModelImplTest::TearDown() {
-  model_->RemoveObserver(this);
-  model_.reset();
-  PumpLoop();
-}
-
-void OfflinePageModelImplTest::OfflinePageModelLoaded(OfflinePageModel* model) {
-  ASSERT_EQ(model_.get(), model);
-}
-
-void OfflinePageModelImplTest::OfflinePageModelChanged(
-    OfflinePageModel* model) {
-  ASSERT_EQ(model_.get(), model);
-}
-
-void OfflinePageModelImplTest::OfflinePageDeleted(int64_t offline_id,
-                                                  const ClientId& client_id) {
-  last_deleted_offline_id_ = offline_id;
-  last_deleted_client_id_ = client_id;
-}
-
-void OfflinePageModelImplTest::SetLastPathCreatedByArchiver(
-    const base::FilePath& file_path) {
-  last_archiver_path_ = file_path;
-}
-
-void OfflinePageModelImplTest::OnSavePageDone(SavePageResult result,
-                                              int64_t offline_id) {
-  last_save_result_ = result;
-  last_save_offline_id_ = offline_id;
-}
-
-void OfflinePageModelImplTest::OnDeletePageDone(DeletePageResult result) {
-  last_delete_result_ = result;
-}
-
-void OfflinePageModelImplTest::OnCheckPagesExistOfflineDone(
-    const CheckPagesExistOfflineResult& result) {
-  last_pages_exist_result_ = result;
-}
-
-void OfflinePageModelImplTest::OnStoreUpdateDone(bool /* success - ignored */) {
-}
-
-std::unique_ptr<OfflinePageTestArchiver>
-OfflinePageModelImplTest::BuildArchiver(
-    const GURL& url,
-    OfflinePageArchiver::ArchiverResult result) {
-  return std::unique_ptr<OfflinePageTestArchiver>(
-      new OfflinePageTestArchiver(this, url, result, kTestTitle, kTestFileSize,
-                                  base::ThreadTaskRunnerHandle::Get()));
-}
-
-std::unique_ptr<OfflinePageMetadataStore>
-OfflinePageModelImplTest::BuildStore() {
-  return std::unique_ptr<OfflinePageMetadataStore>(
-      new OfflinePageTestStore(base::ThreadTaskRunnerHandle::Get()));
-}
-
-std::unique_ptr<OfflinePageModelImpl> OfflinePageModelImplTest::BuildModel(
-    std::unique_ptr<OfflinePageMetadataStore> store) {
-  return std::unique_ptr<OfflinePageModelImpl>(
-      new OfflinePageModelImpl(std::move(store), temp_dir_.GetPath(),
-                               base::ThreadTaskRunnerHandle::Get()));
-}
-
-void OfflinePageModelImplTest::ResetModel() {
-  model_->RemoveObserver(this);
-  OfflinePageTestStore* old_store = GetStore();
-  std::unique_ptr<OfflinePageMetadataStore> new_store(
-      new OfflinePageTestStore(*old_store));
-  model_ = BuildModel(std::move(new_store));
-  model_->AddObserver(this);
-  PumpLoop();
-}
-
-void OfflinePageModelImplTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void OfflinePageModelImplTest::FastForwardBy(base::TimeDelta delta) {
-  task_runner_->FastForwardBy(delta);
-}
-
-void OfflinePageModelImplTest::ResetResults() {
-  last_save_result_ = SavePageResult::CANCELLED;
-  last_delete_result_ = DeletePageResult::CANCELLED;
-  last_archiver_path_.clear();
-  last_pages_exist_result_.clear();
-  last_cleared_pages_count_ = 0;
-}
-
-OfflinePageTestStore* OfflinePageModelImplTest::GetStore() {
-  return static_cast<OfflinePageTestStore*>(model()->GetStoreForTesting());
-}
-
-void OfflinePageModelImplTest::SavePageWithArchiverAsync(
-    const GURL& url,
-    const ClientId& client_id,
-    const GURL& original_url,
-    std::unique_ptr<OfflinePageArchiver> archiver) {
-  OfflinePageModel::SavePageParams save_page_params;
-  save_page_params.url = url;
-  save_page_params.client_id = client_id;
-  save_page_params.original_url = original_url;
-  model()->SavePage(
-      save_page_params,
-      std::move(archiver),
-      base::Bind(&OfflinePageModelImplTest::OnSavePageDone, AsWeakPtr()));
-}
-
-void OfflinePageModelImplTest::SavePageWithArchiver(
-    const GURL& url,
-    const ClientId& client_id,
-    std::unique_ptr<OfflinePageArchiver> archiver) {
-  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
-  PumpLoop();
-}
-
-void OfflinePageModelImplTest::SavePageWithArchiverResult(
-    const GURL& url,
-    const ClientId& client_id,
-    OfflinePageArchiver::ArchiverResult result) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(url, result));
-  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
-  PumpLoop();
-}
-
-std::pair<SavePageResult, int64_t>
-OfflinePageModelImplTest::SavePage(const GURL& url, const ClientId& client_id) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
-      url, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  SavePageWithArchiverAsync(url, client_id, GURL(), std::move(archiver));
-  PumpLoop();
-  return std::make_pair(last_save_result_, last_save_offline_id_);
-}
-
-MultipleOfflinePageItemResult OfflinePageModelImplTest::GetAllPages() {
-  MultipleOfflinePageItemResult result;
-  model()->GetAllPages(
-      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByClientIds(
-    const std::vector<ClientId>& client_ids) {
-  MultipleOfflinePageItemResult result;
-  model()->GetPagesByClientIds(
-      client_ids,
-      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-void OfflinePageModelImplTest::DeletePagesByClientIds(
-    const std::vector<ClientId>& client_ids) {
-  model()->DeletePagesByClientIds(
-      client_ids,
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-  PumpLoop();
-}
-
-CheckPagesExistOfflineResult OfflinePageModelImplTest::CheckPagesExistOffline(
-    std::set<GURL> pages) {
-  model()->CheckPagesExistOffline(
-      pages, base::Bind(&OfflinePageModelImplTest::OnCheckPagesExistOfflineDone,
-                        AsWeakPtr()));
-  PumpLoop();
-  return last_pages_exist_result_;
-}
-
-MultipleOfflineIdResult OfflinePageModelImplTest::GetOfflineIdsForClientId(
-    const ClientId& client_id) {
-  MultipleOfflineIdResult result;
-  model()->GetOfflineIdsForClientId(
-      client_id,
-      base::Bind(&OfflinePageModelImplTest::OnGetOfflineIdsForClientIdDone,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-void OfflinePageModelImplTest::OnGetOfflineIdsForClientIdDone(
-    MultipleOfflineIdResult* storage,
-    const MultipleOfflineIdResult& result) {
-  *storage = result;
-}
-
-std::unique_ptr<OfflinePageItem> OfflinePageModelImplTest::GetPageByOfflineId(
-    int64_t offline_id) {
-  std::unique_ptr<OfflinePageItem> result = nullptr;
-  model()->GetPageByOfflineId(
-      offline_id,
-      base::Bind(&OfflinePageModelImplTest::OnGetSingleOfflinePageItemResult,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-void OfflinePageModelImplTest::OnGetSingleOfflinePageItemResult(
-    std::unique_ptr<OfflinePageItem>* storage,
-    const OfflinePageItem* result) {
-  if (result == nullptr) {
-    storage->reset(nullptr);
-    return;
-  }
-
-  *storage = base::MakeUnique<OfflinePageItem>(*result);
-}
-
-void OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult(
-    MultipleOfflinePageItemResult* storage,
-    const MultipleOfflinePageItemResult& result) {
-  *storage = result;
-}
-
-void OfflinePageModelImplTest::OnPagesExpired(bool result) {
-  last_expire_page_result_ = result;
-}
-
-MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByFinalURL(
-    const GURL& url) {
-  MultipleOfflinePageItemResult result;
-  model()->GetPagesByURL(
-      url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
-      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-MultipleOfflinePageItemResult OfflinePageModelImplTest::GetPagesByAllURLS(
-    const GURL& url) {
-  MultipleOfflinePageItemResult result;
-  model()->GetPagesByURL(
-      url,
-      OfflinePageModel::URLSearchMode::SEARCH_BY_ALL_URLS,
-      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
-                 AsWeakPtr(), base::Unretained(&result)));
-  PumpLoop();
-  return result;
-}
-
-bool OfflinePageModelImplTest::HasPages(std::string name_space) {
-  MultipleOfflinePageItemResult all_pages = GetAllPages();
-  for (const auto& page : all_pages) {
-    if (page.client_id.name_space == name_space)
-      return true;
-  }
-
-  return false;
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageSuccessful) {
-  EXPECT_FALSE(HasPages(kTestClientNamespace));
-
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
-      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  SavePageWithArchiverAsync(
-      kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
-  PumpLoop();
-  EXPECT_TRUE(HasPages(kTestClientNamespace));
-
-  OfflinePageTestStore* store = GetStore();
-  EXPECT_EQ(kTestUrl, store->last_saved_page().url);
-  EXPECT_EQ(kTestClientId1.id, store->last_saved_page().client_id.id);
-  EXPECT_EQ(kTestClientId1.name_space,
-            store->last_saved_page().client_id.name_space);
-  // Save last_archiver_path since it will be referred to later.
-  base::FilePath archiver_path = last_archiver_path();
-  EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
-  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  ResetResults();
-
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  ASSERT_EQ(1UL, offline_pages.size());
-  EXPECT_EQ(kTestUrl, offline_pages[0].url);
-  EXPECT_EQ(kTestClientId1.id, offline_pages[0].client_id.id);
-  EXPECT_EQ(kTestClientId1.name_space, offline_pages[0].client_id.name_space);
-  EXPECT_EQ(archiver_path, offline_pages[0].file_path);
-  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
-  EXPECT_EQ(0, offline_pages[0].access_count);
-  EXPECT_EQ(0, offline_pages[0].flags);
-  EXPECT_EQ(kTestTitle, offline_pages[0].title);
-  EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverCancelled) {
-  SavePageWithArchiverResult(
-      kTestUrl, kTestClientId1,
-      OfflinePageArchiver::ArchiverResult::ERROR_CANCELED);
-  EXPECT_EQ(SavePageResult::CANCELLED, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverDeviceFull) {
-  SavePageWithArchiverResult(
-      kTestUrl, kTestClientId1,
-      OfflinePageArchiver::ArchiverResult::ERROR_DEVICE_FULL);
-  EXPECT_EQ(SavePageResult::DEVICE_FULL, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverContentUnavailable) {
-  SavePageWithArchiverResult(
-      kTestUrl, kTestClientId1,
-      OfflinePageArchiver::ArchiverResult::ERROR_CONTENT_UNAVAILABLE);
-  EXPECT_EQ(SavePageResult::CONTENT_UNAVAILABLE, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineCreationFailed) {
-  SavePageWithArchiverResult(
-      kTestUrl, kTestClientId1,
-      OfflinePageArchiver::ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED);
-  EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverReturnedWrongUrl) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(
-      BuildArchiver(GURL("http://other.random.url.com"),
-                    OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  SavePageWithArchiver(kTestUrl, kTestClientId1, std::move(archiver));
-  EXPECT_EQ(SavePageResult::ARCHIVE_CREATION_FAILED, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineCreationStoreWriteFailure) {
-  GetStore()->set_test_scenario(
-      OfflinePageTestStore::TestScenario::WRITE_FAILED);
-  SavePageWithArchiverResult(
-      kTestUrl, kTestClientId1,
-      OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED);
-  EXPECT_EQ(SavePageResult::STORE_FAILURE, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageLocalFileFailed) {
-  // Don't create archiver since it will not be needed for pages that are not
-  // going to be saved.
-  SavePageWithArchiver(
-      kFileUrl, kTestClientId1, std::unique_ptr<OfflinePageTestArchiver>());
-  EXPECT_EQ(SavePageResult::SKIPPED, last_save_result());
-}
-
-TEST_F(OfflinePageModelImplTest, SavePageOfflineArchiverTwoPages) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
-      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  // archiver_ptr will be valid until after first PumpLoop() call after
-  // CompleteCreateArchive() is called.
-  OfflinePageTestArchiver* archiver_ptr = archiver.get();
-  archiver_ptr->set_delayed(true);
-  SavePageWithArchiverAsync(
-      kTestUrl, kTestClientId1, GURL(), std::move(archiver));
-  EXPECT_TRUE(archiver_ptr->create_archive_called());
-
-  // Request to save another page.
-  SavePage(kTestUrl2, kTestClientId2);
-
-  OfflinePageTestStore* store = GetStore();
-
-  EXPECT_EQ(kTestUrl2, store->last_saved_page().url);
-  EXPECT_EQ(kTestClientId2, store->last_saved_page().client_id);
-  base::FilePath archiver_path2 = last_archiver_path();
-  EXPECT_EQ(archiver_path2, store->last_saved_page().file_path);
-  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-
-  ResetResults();
-
-  archiver_ptr->CompleteCreateArchive();
-  // After this pump loop archiver_ptr is invalid.
-  PumpLoop();
-
-  EXPECT_EQ(kTestUrl, store->last_saved_page().url);
-  EXPECT_EQ(kTestClientId1, store->last_saved_page().client_id);
-  base::FilePath archiver_path = last_archiver_path();
-  EXPECT_EQ(archiver_path, store->last_saved_page().file_path);
-  EXPECT_EQ(kTestFileSize, store->last_saved_page().file_size);
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-
-  ResetResults();
-
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  EXPECT_EQ(2UL, offline_pages.size());
-  // Offline IDs are random, so the order of the pages is also random
-  // So load in the right page for the validation below.
-  const OfflinePageItem* page1 = nullptr;
-  const OfflinePageItem* page2 = nullptr;
-  if (offline_pages[0].client_id == kTestClientId1) {
-    page1 = &offline_pages[0];
-    page2 = &offline_pages[1];
-  } else {
-    page1 = &offline_pages[1];
-    page2 = &offline_pages[0];
-  }
-
-  EXPECT_EQ(kTestUrl, page1->url);
-  EXPECT_EQ(kTestClientId1, page1->client_id);
-  EXPECT_EQ(archiver_path, page1->file_path);
-  EXPECT_EQ(kTestFileSize, page1->file_size);
-  EXPECT_EQ(0, page1->access_count);
-  EXPECT_EQ(0, page1->flags);
-  EXPECT_EQ(kTestUrl2, page2->url);
-  EXPECT_EQ(kTestClientId2, page2->client_id);
-  EXPECT_EQ(archiver_path2, page2->file_path);
-  EXPECT_EQ(kTestFileSize, page2->file_size);
-  EXPECT_EQ(0, page2->access_count);
-  EXPECT_EQ(0, page2->flags);
-}
-
-TEST_F(OfflinePageModelImplTest, MarkPageAccessed) {
-  SavePage(kTestUrl, kTestClientId1);
-
-  // This will increase access_count by one.
-  model()->MarkPageAccessed(last_save_offline_id());
-  PumpLoop();
-
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  EXPECT_EQ(1UL, offline_pages.size());
-  EXPECT_EQ(kTestUrl, offline_pages[0].url);
-  EXPECT_EQ(kTestClientId1, offline_pages[0].client_id);
-  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
-  EXPECT_EQ(1, offline_pages[0].access_count);
-}
-
-TEST_F(OfflinePageModelImplTest, GetAllPagesStoreEmpty) {
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  EXPECT_EQ(0UL, offline_pages.size());
-}
-
-TEST_F(OfflinePageModelImplTest, DeletePageSuccessful) {
-  OfflinePageTestStore* store = GetStore();
-
-  // Save one page.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(1u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Save another page.
-  SavePage(kTestUrl2, kTestClientId2);
-  int64_t offline2 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(2u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Delete one page.
-  DeletePage(offline1, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                  AsWeakPtr()));
-
-  PumpLoop();
-
-  EXPECT_EQ(last_deleted_offline_id(), offline1);
-  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  ASSERT_EQ(1u, store->GetAllPages().size());
-  EXPECT_EQ(kTestUrl2, store->GetAllPages()[0].url);
-
-  // Delete another page.
-  DeletePage(offline2, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                  AsWeakPtr()));
-
-  ResetResults();
-
-  PumpLoop();
-
-  EXPECT_EQ(last_deleted_offline_id(), offline2);
-  EXPECT_EQ(last_deleted_client_id(), kTestClientId2);
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  EXPECT_EQ(0u, store->GetAllPages().size());
-}
-
-TEST_F(OfflinePageModelImplTest, DeleteCachedPageByPredicateUserRequested) {
-  OfflinePageTestStore* store = GetStore();
-
-  // Save one page.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(1u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Save an user-requested page in same domain.
-  SavePage(kTestUrl, kTestUserRequestedClientId);
-  int64_t offline2 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(2u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Delete the second page.
-  model()->DeleteCachedPagesByURLPredicate(
-      base::Bind(&URLSpecContains, "example.com"),
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-
-  PumpLoop();
-
-  EXPECT_EQ(last_deleted_offline_id(), offline1);
-  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  ASSERT_EQ(1u, store->GetAllPages().size());
-  EXPECT_EQ(kTestUrl, store->GetAllPages()[0].url);
-  EXPECT_EQ(offline2, store->GetAllPages()[0].offline_id);
-}
-
-TEST_F(OfflinePageModelImplTest, DeleteCachedPageByPredicate) {
-  OfflinePageTestStore* store = GetStore();
-
-  // Save one page.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(1u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Save another page.
-  SavePage(kTestUrl2, kTestClientId2);
-  int64_t offline2 = last_save_offline_id();
-  EXPECT_EQ(SavePageResult::SUCCESS, last_save_result());
-  EXPECT_EQ(2u, store->GetAllPages().size());
-
-  ResetResults();
-
-  // Delete the second page.
-  model()->DeleteCachedPagesByURLPredicate(
-      base::Bind(&URLSpecContains, "page.com"),
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-
-  PumpLoop();
-
-  EXPECT_EQ(last_deleted_offline_id(), offline2);
-  EXPECT_EQ(last_deleted_client_id(), kTestClientId2);
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  ASSERT_EQ(1u, store->GetAllPages().size());
-  EXPECT_EQ(kTestUrl, store->GetAllPages()[0].url);
-
-  ResetResults();
-
-  // Delete the first page.
-  model()->DeleteCachedPagesByURLPredicate(
-      base::Bind(&URLSpecContains, "example.com"),
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-
-  PumpLoop();
-
-  EXPECT_EQ(last_deleted_offline_id(), offline1);
-  EXPECT_EQ(last_deleted_client_id(), kTestClientId1);
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  EXPECT_EQ(0u, store->GetAllPages().size());
-}
-
-TEST_F(OfflinePageModelImplTest, DeletePageNotFound) {
-  DeletePage(1234LL, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                AsWeakPtr()));
-  PumpLoop();
-
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-
-  ResetResults();
-
-  model()->DeleteCachedPagesByURLPredicate(
-      base::Bind(&URLSpecContains, "page.com"),
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-  PumpLoop();
-
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-}
-
-TEST_F(OfflinePageModelImplTest, DeletePageStoreFailureOnRemove) {
-  // Save a page.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline_id = last_save_offline_id();
-  ResetResults();
-
-  // Try to delete this page.
-  GetStore()->set_test_scenario(
-      OfflinePageTestStore::TestScenario::REMOVE_FAILED);
-  DeletePage(offline_id, base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                    AsWeakPtr()));
-  PumpLoop();
-  EXPECT_EQ(DeletePageResult::STORE_FAILURE, last_delete_result());
-}
-
-TEST_F(OfflinePageModelImplTest, DetectThatOfflineCopyIsMissing) {
-  // Save a page.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline_id = last_save_offline_id();
-
-  ResetResults();
-
-  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
-
-  // Delete the offline copy of the page.
-  base::DeleteFile(page->file_path, false);
-
-  // Resetting the model will cause a consistency check.
-  ResetModel();
-
-  PumpLoop();
-
-  // Check if the page has been expired.
-  EXPECT_EQ(0UL, GetAllPages().size());
-}
-
-TEST_F(OfflinePageModelImplTest, DetectThatOfflineCopyIsMissingAfterLoad) {
-  // Save a page.
-  SavePage(kTestUrl, kTestClientId1);
-  PumpLoop();
-  int64_t offline_id = last_save_offline_id();
-
-  ResetResults();
-
-  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
-  // Delete the offline copy of the page and check the metadata.
-  base::DeleteFile(page->file_path, false);
-  // Reseting the model should trigger the metadata consistency check as well.
-  ResetModel();
-  PumpLoop();
-
-  // Check if the page has been expired.
-  EXPECT_EQ(0UL, GetAllPages().size());
-}
-
-TEST_F(OfflinePageModelImplTest, DetectThatHeadlessPageIsDeleted) {
-  // Save a page.
-  SavePage(kTestUrl, kTestClientId1);
-  PumpLoop();
-  int64_t offline_id = last_save_offline_id();
-
-  ResetResults();
-  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline_id);
-  base::FilePath path = page->file_path;
-  EXPECT_TRUE(base::PathExists(path));
-  GetStore()->ClearAllPages();
-
-  EXPECT_TRUE(base::PathExists(path));
-  // Since we've manually changed the store, we have to reload the model to
-  // actually refresh the in-memory copy in model. Otherwise GetAllPages() would
-  // still have the page we saved above.
-  ResetModel();
-  PumpLoop();
-
-  EXPECT_EQ(0UL, GetAllPages().size());
-  EXPECT_FALSE(base::PathExists(path));
-}
-
-TEST_F(OfflinePageModelImplTest, DeleteMultiplePages) {
-  OfflinePageTestStore* store = GetStore();
-
-  // Save 3 pages.
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-
-  SavePage(kTestUrl2, kTestClientId2);
-  int64_t offline2 = last_save_offline_id();
-
-  SavePage(kTestUrl3, kTestClientId3);
-  PumpLoop();
-
-  EXPECT_EQ(3u, store->GetAllPages().size());
-
-  // Delete multiple pages.
-  std::vector<int64_t> ids_to_delete;
-  ids_to_delete.push_back(offline2);
-  ids_to_delete.push_back(offline1);
-  ids_to_delete.push_back(23434LL);  // Non-existent ID.
-  model()->DeletePagesByOfflineId(
-      ids_to_delete,
-      base::Bind(&OfflinePageModelImplTest::OnDeletePageDone, AsWeakPtr()));
-  PumpLoop();
-
-  // Success is expected if at least one page is deleted successfully.
-  EXPECT_EQ(DeletePageResult::SUCCESS, last_delete_result());
-  EXPECT_EQ(1u, store->GetAllPages().size());
-}
-
-TEST_F(OfflinePageModelImplTest, GetPageByOfflineId) {
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  SavePage(kTestUrl2, kTestClientId2);
-  int64_t offline2 = last_save_offline_id();
-
-  std::unique_ptr<OfflinePageItem> page = GetPageByOfflineId(offline1);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(kTestUrl, page->url);
-  EXPECT_EQ(kTestClientId1, page->client_id);
-  EXPECT_EQ(kTestFileSize, page->file_size);
-
-  page = GetPageByOfflineId(offline2);
-  ASSERT_TRUE(page);
-  EXPECT_EQ(kTestUrl2, page->url);
-  EXPECT_EQ(kTestClientId2, page->client_id);
-  EXPECT_EQ(kTestFileSize, page->file_size);
-
-  page = GetPageByOfflineId(-42);
-  EXPECT_FALSE(page);
-}
-
-TEST_F(OfflinePageModelImplTest, GetPagesByFinalURL) {
-  SavePage(kTestUrl, kTestClientId1);
-  SavePage(kTestUrl2, kTestClientId2);
-
-  MultipleOfflinePageItemResult pages = GetPagesByFinalURL(kTestUrl2);
-  EXPECT_EQ(1U, pages.size());
-  EXPECT_EQ(kTestUrl2, pages[0].url);
-  EXPECT_EQ(kTestClientId2, pages[0].client_id);
-
-  pages = GetPagesByFinalURL(kTestUrl);
-  EXPECT_EQ(1U, pages.size());
-  EXPECT_EQ(kTestUrl, pages[0].url);
-  EXPECT_EQ(kTestClientId1, pages[0].client_id);
-
-  pages = GetPagesByFinalURL(GURL("http://foo"));
-  EXPECT_EQ(0U, pages.size());
-}
-
-TEST_F(OfflinePageModelImplTest, GetPagesByFinalURLWithFragmentStripped) {
-  SavePage(kTestUrl, kTestClientId1);
-  SavePage(kTestUrl2WithFragment, kTestClientId2);
-
-  MultipleOfflinePageItemResult pages =
-      GetPagesByFinalURL(kTestUrlWithFragment);
-  EXPECT_EQ(1U, pages.size());
-  EXPECT_EQ(kTestUrl, pages[0].url);
-  EXPECT_EQ(kTestClientId1, pages[0].client_id);
-
-  pages = GetPagesByFinalURL(kTestUrl2);
-  EXPECT_EQ(1U, pages.size());
-  EXPECT_EQ(kTestUrl2WithFragment, pages[0].url);
-  EXPECT_EQ(kTestClientId2, pages[0].client_id);
-
-  pages = GetPagesByFinalURL(kTestUrl2WithFragment2);
-  EXPECT_EQ(1U, pages.size());
-  EXPECT_EQ(kTestUrl2WithFragment, pages[0].url);
-  EXPECT_EQ(kTestClientId2, pages[0].client_id);
-}
-
-TEST_F(OfflinePageModelImplTest, GetPagesByAllURLS) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
-      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  SavePageWithArchiverAsync(
-      kTestUrl, kTestClientId1, kTestUrl2, std::move(archiver));
-  PumpLoop();
-
-  SavePage(kTestUrl2, kTestClientId2);
-
-  MultipleOfflinePageItemResult pages = GetPagesByAllURLS(kTestUrl2);
-  ASSERT_EQ(2U, pages.size());
-  // Validates the items regardless their order.
-  int i = -1;
-  if (pages[0].url == kTestUrl2)
-    i = 0;
-  else if (pages[1].url == kTestUrl2)
-    i = 1;
-  ASSERT_NE(-1, i);
-  EXPECT_EQ(kTestUrl2, pages[i].url);
-  EXPECT_EQ(kTestClientId2, pages[i].client_id);
-  EXPECT_EQ(GURL(), pages[i].original_url);
-  EXPECT_EQ(kTestUrl, pages[1 - i].url);
-  EXPECT_EQ(kTestClientId1, pages[1 - i].client_id);
-  EXPECT_EQ(kTestUrl2, pages[1 - i].original_url);
-}
-
-TEST_F(OfflinePageModelImplTest, CheckPagesExistOffline) {
-  SavePage(kTestUrl, kTestClientId1);
-  SavePage(kTestUrl2, kTestClientId2);
-
-  // TODO(dewittj): Remove the "Last N" restriction in favor of a better query
-  // interface.  See https://crbug.com/622763 for information.
-  const ClientId last_n_client_id(kLastNNamespace, "1234");
-  SavePage(kTestUrl3, last_n_client_id);
-
-  std::set<GURL> input;
-  input.insert(kTestUrl);
-  input.insert(kTestUrl2);
-  input.insert(kTestUrl3);
-  input.insert(kTestUrl4);
-
-  CheckPagesExistOfflineResult existing_pages = CheckPagesExistOffline(input);
-  EXPECT_EQ(2U, existing_pages.size());
-  EXPECT_NE(existing_pages.end(), existing_pages.find(kTestUrl));
-  EXPECT_NE(existing_pages.end(), existing_pages.find(kTestUrl2));
-  EXPECT_EQ(existing_pages.end(), existing_pages.find(kTestUrl3));
-  EXPECT_EQ(existing_pages.end(), existing_pages.find(kTestUrl4));
-}
-
-TEST_F(OfflinePageModelImplTest, CanSaveURL) {
-  EXPECT_TRUE(OfflinePageModel::CanSaveURL(GURL("http://foo")));
-  EXPECT_TRUE(OfflinePageModel::CanSaveURL(GURL("https://foo")));
-  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("file:///foo")));
-  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("data:image/png;base64,ab")));
-  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("chrome://version")));
-  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("chrome-native://newtab/")));
-  EXPECT_FALSE(OfflinePageModel::CanSaveURL(GURL("/invalid/url.mhtml")));
-}
-
-TEST_F(OfflinePageModelImplTest, SaveRetrieveMultipleClientIds) {
-  EXPECT_FALSE(HasPages(kTestClientNamespace));
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  EXPECT_TRUE(HasPages(kTestClientNamespace));
-
-  SavePage(kTestUrl2, kTestClientId1);
-  int64_t offline2 = last_save_offline_id();
-
-  EXPECT_NE(offline1, offline2);
-
-  const std::vector<int64_t> ids = GetOfflineIdsForClientId(kTestClientId1);
-
-  EXPECT_EQ(2UL, ids.size());
-
-  std::set<int64_t> id_set;
-  for (size_t i = 0; i < ids.size(); i++) {
-    id_set.insert(ids[i]);
-  }
-
-  EXPECT_TRUE(id_set.find(offline1) != id_set.end());
-  EXPECT_TRUE(id_set.find(offline2) != id_set.end());
-}
-
-TEST_F(OfflinePageModelImplTest, SaveMultiplePagesWithSameURLBySameClientId) {
-  EXPECT_FALSE(HasPages(kTestClientNamespace));
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline1 = last_save_offline_id();
-  EXPECT_TRUE(HasPages(kTestClientNamespace));
-
-  SavePage(kTestUrl, kTestClientId1);
-  int64_t offline2 = last_save_offline_id();
-
-  EXPECT_NE(offline1, offline2);
-
-  const std::vector<int64_t> ids = GetOfflineIdsForClientId(kTestClientId1);
-
-  EXPECT_EQ(1UL, ids.size());
-
-  std::set<int64_t> id_set;
-  for (size_t i = 0; i < ids.size(); i++) {
-    id_set.insert(ids[i]);
-  }
-
-  EXPECT_TRUE(id_set.find(offline2) != id_set.end());
-}
-
-TEST_F(OfflinePageModelImplTest, DownloadMetrics) {
-  EXPECT_FALSE(HasPages(kUserRequestedNamespace));
-  SavePage(kTestUrl, kTestUserRequestedClientId);
-  histograms().ExpectUniqueSample(
-      "OfflinePages.DownloadSavedPageDuplicateCount", 1, 1);
-  FastForwardBy(base::TimeDelta::FromMinutes(1));
-  histograms().ExpectTotalCount(
-      "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", 0);
-  SavePage(kTestUrl, kTestUserRequestedClientId);
-  histograms().ExpectTotalCount("OfflinePages.DownloadSavedPageDuplicateCount",
-                                2);
-  histograms().ExpectBucketCount("OfflinePages.DownloadSavedPageDuplicateCount",
-                                 2, 1);
-  histograms().ExpectBucketCount("OfflinePages.DownloadSavedPageDuplicateCount",
-                                 1, 1);
-  histograms().ExpectTotalCount(
-      "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", 1);
-  histograms().ExpectTotalCount(
-      "OfflinePages.DownloadDeletedPageDuplicateCount", 0);
-
-  // void DeletePage(int64_t offline_id, const DeletePageCallback& callback) {
-  const std::vector<int64_t> ids =
-      GetOfflineIdsForClientId(kTestUserRequestedClientId);
-  ASSERT_EQ(2U, ids.size());
-
-  DeletePage(ids[0], base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                AsWeakPtr()));
-  PumpLoop();
-  histograms().ExpectUniqueSample(
-      "OfflinePages.DownloadDeletedPageDuplicateCount", 2, 1);
-  DeletePage(ids[1], base::Bind(&OfflinePageModelImplTest::OnDeletePageDone,
-                                AsWeakPtr()));
-  PumpLoop();
-  // No change when we delete the last page.
-  histograms().ExpectTotalCount(
-      "OfflinePages.DownloadDeletedPageDuplicateCount", 2);
-  histograms().ExpectBucketCount(
-      "OfflinePages.DownloadDeletedPageDuplicateCount", 1, 1);
-  histograms().ExpectBucketCount(
-      "OfflinePages.DownloadDeletedPageDuplicateCount", 2, 1);
-}
-
-TEST_F(OfflinePageModelImplTest, GetPagesByClientIds) {
-  // We will save 2 pages.
-  std::pair<SavePageResult, int64_t> saved_pages[3];
-  saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
-  saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
-
-  for (const auto& save_result : saved_pages) {
-    ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
-              std::get<0>(save_result));
-  }
-
-  std::vector<ClientId> client_ids = {kTestClientId2};
-  std::vector<OfflinePageItem> offline_pages = GetPagesByClientIds(client_ids);
-  EXPECT_EQ(1U, offline_pages.size());
-
-  const OfflinePageItem& item = offline_pages[0];
-  EXPECT_EQ(kTestClientId2.name_space, item.client_id.name_space);
-  EXPECT_EQ(kTestClientId2.id, item.client_id.id);
-  EXPECT_EQ(kTestUrl2, item.url);
-}
-
-TEST_F(OfflinePageModelImplTest, DeletePagesByClientIds) {
-  // We will save 3 pages.
-  std::pair<SavePageResult, int64_t> saved_pages[3];
-  saved_pages[0] = SavePage(kTestUrl, kTestClientId1);
-  saved_pages[1] = SavePage(kTestUrl2, kTestClientId2);
-  saved_pages[2] = SavePage(kTestUrl3, kTestClientId3);
-
-  for (const auto& save_result : saved_pages) {
-    ASSERT_EQ(OfflinePageModel::SavePageResult::SUCCESS,
-              std::get<0>(save_result));
-  }
-
-  std::vector<ClientId> client_ids = {kTestClientId1, kTestClientId2};
-  DeletePagesByClientIds(client_ids);
-  std::vector<OfflinePageItem> offline_pages = GetAllPages();
-  ASSERT_EQ(1U, offline_pages.size());
-
-  const OfflinePageItem& item = offline_pages[0];
-  EXPECT_EQ(kTestClientId3.name_space, item.client_id.name_space);
-  EXPECT_EQ(kTestClientId3.id, item.client_id.id);
-  EXPECT_EQ(kTestUrl3, item.url);
-}
-
-TEST_F(OfflinePageModelImplTest, CustomTabsNamespace) {
-  SavePage(kTestUrl, ClientId(kCCTNamespace, "123"));
-  std::string histogram_name = "OfflinePages.SavePageResult.";
-  histogram_name += kCCTNamespace;
-
-  histograms().ExpectUniqueSample(histogram_name,
-                                  static_cast<int>(SavePageResult::SUCCESS), 1);
-}
-
-TEST_F(OfflinePageModelImplTest, DownloadNamespace) {
-  SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
-  std::string histogram_name = "OfflinePages.SavePageResult.";
-  histogram_name += kDownloadNamespace;
-
-  histograms().ExpectUniqueSample(histogram_name,
-                                  static_cast<int>(SavePageResult::SUCCESS), 1);
-}
-
-TEST_F(OfflinePageModelImplTest, NewTabPageNamespace) {
-  SavePage(kTestUrl, ClientId(kNTPSuggestionsNamespace, "123"));
-  std::string histogram_name = "OfflinePages.SavePageResult.";
-  histogram_name += kNTPSuggestionsNamespace;
-
-  histograms().ExpectUniqueSample(histogram_name,
-                                  static_cast<int>(SavePageResult::SUCCESS), 1);
-}
-
-TEST_F(OfflinePageModelImplTest, StoreResetSuccessful) {
-  GetStore()->set_test_scenario(
-      OfflinePageTestStore::TestScenario::LOAD_FAILED_RESET_SUCCESS);
-  ResetModel();
-
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(StoreState::LOADED, GetStore()->state());
-  EXPECT_EQ(0UL, offline_pages.size());
-
-  std::pair<SavePageResult, int64_t> result =
-      SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
-
-  EXPECT_EQ(SavePageResult::SUCCESS, result.first);
-}
-
-TEST_F(OfflinePageModelImplTest, StoreResetFailed) {
-  GetStore()->set_test_scenario(
-      OfflinePageTestStore::TestScenario::LOAD_FAILED_RESET_FAILED);
-  ResetModel();
-
-  const std::vector<OfflinePageItem>& offline_pages = GetAllPages();
-
-  EXPECT_TRUE(model()->is_loaded());
-  EXPECT_EQ(StoreState::FAILED_RESET, GetStore()->state());
-  EXPECT_EQ(0UL, offline_pages.size());
-
-  ResetResults();
-  std::pair<SavePageResult, int64_t> result =
-      SavePage(kTestUrl, ClientId(kDownloadNamespace, "123"));
-
-  EXPECT_EQ(SavePageResult::STORE_FAILURE, result.first);
-}
-
-TEST_F(OfflinePageModelImplTest, GetPagesMatchingQuery) {
-  std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
-      kTestUrl, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED));
-  SavePageWithArchiverAsync(kTestUrl, kTestClientId1, kTestUrl2,
-                            std::move(archiver));
-  PumpLoop();
-
-  std::vector<ClientId> client_ids{kTestClientId1};
-  OfflinePageModelQueryBuilder builder;
-  builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
-                       client_ids);
-
-  MultipleOfflinePageItemResult offline_pages;
-  model()->GetPagesMatchingQuery(
-      builder.Build(model()->GetPolicyController()),
-      base::Bind(&OfflinePageModelImplTest::OnGetMultipleOfflinePageItemsResult,
-                 AsWeakPtr(), base::Unretained(&offline_pages)));
-  PumpLoop();
-
-  ASSERT_EQ(1UL, offline_pages.size());
-  EXPECT_EQ(kTestUrl, offline_pages[0].url);
-  EXPECT_EQ(kTestClientId1.id, offline_pages[0].client_id.id);
-  EXPECT_EQ(kTestClientId1.name_space, offline_pages[0].client_id.name_space);
-  EXPECT_EQ(last_archiver_path(), offline_pages[0].file_path);
-  EXPECT_EQ(kTestFileSize, offline_pages[0].file_size);
-  EXPECT_EQ(0, offline_pages[0].access_count);
-  EXPECT_EQ(0, offline_pages[0].flags);
-  EXPECT_EQ(kTestTitle, offline_pages[0].title);
-  EXPECT_EQ(kTestUrl2, offline_pages[0].original_url);
-}
-
-TEST(CommandLineFlagsTest, OfflineBookmarks) {
-  // Disabled by default.
-  EXPECT_FALSE(offline_pages::IsOfflineBookmarksEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(kOfflineBookmarksFeature);
-  EXPECT_TRUE(offline_pages::IsOfflineBookmarksEnabled());
-}
-
-TEST(CommandLineFlagsTest, OffliningRecentPages) {
-  // Enable offline bookmarks feature first.
-  // TODO(dimich): once offline pages are enabled by default, remove this.
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
-      new base::test::ScopedFeatureList);
-  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
-
-  // This feature is still disabled by default.
-  EXPECT_FALSE(offline_pages::IsOffliningRecentPagesEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  scoped_feature_list.reset(new base::test::ScopedFeatureList);
-  scoped_feature_list->InitFromCommandLine(
-      std::string(kOfflineBookmarksFeature.name) + "," +
-          kOffliningRecentPagesFeature.name,
-      "");
-  EXPECT_TRUE(offline_pages::IsOffliningRecentPagesEnabled());
-}
-
-TEST(CommandLineFlagsTest, OfflinePagesSharing) {
-  // Enable offline bookmarks feature first.
-  // TODO(dimich): once offline pages are enabled by default, remove this.
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list(
-      new base::test::ScopedFeatureList);
-  scoped_feature_list->InitAndEnableFeature(kOfflineBookmarksFeature);
-
-  // This feature is still disabled by default.
-  EXPECT_FALSE(offline_pages::IsOfflinePagesSharingEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  scoped_feature_list.reset(new base::test::ScopedFeatureList);
-  scoped_feature_list->InitFromCommandLine(
-      std::string(kOfflineBookmarksFeature.name) + "," +
-          kOfflinePagesSharingFeature.name,
-      "");
-  EXPECT_TRUE(offline_pages::IsOfflinePagesSharingEnabled());
-}
-
-TEST(CommandLineFlagsTest, OfflinePagesSvelteConcurrentLoading) {
-  // This feature is disabled by default.
-  EXPECT_FALSE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
-
-  // Check if feature is correctly enabled by command-line flag.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      kOfflinePagesSvelteConcurrentLoadingFeature);
-  EXPECT_TRUE(offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_model_query.cc b/components/offline_pages/offline_page_model_query.cc
deleted file mode 100644
index 5fe192dc..0000000
--- a/components/offline_pages/offline_page_model_query.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_model_query.h"
-
-#include <algorithm>
-#include <unordered_set>
-
-#include "base/memory/ptr_util.h"
-
-namespace offline_pages {
-
-using Requirement = OfflinePageModelQuery::Requirement;
-
-OfflinePageModelQueryBuilder::OfflinePageModelQueryBuilder()
-    : offline_ids_(std::make_pair(Requirement::UNSET, std::vector<int64_t>())) {
-}
-
-OfflinePageModelQueryBuilder::~OfflinePageModelQueryBuilder() = default;
-
-OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetOfflinePageIds(
-    Requirement requirement,
-    const std::vector<int64_t>& ids) {
-  offline_ids_ = std::make_pair(requirement, ids);
-  return *this;
-}
-
-OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetClientIds(
-    Requirement requirement,
-    const std::vector<ClientId>& ids) {
-  client_ids_ = std::make_pair(requirement, ids);
-  return *this;
-}
-
-OfflinePageModelQueryBuilder& OfflinePageModelQueryBuilder::SetUrls(
-    Requirement requirement,
-    const std::vector<GURL>& urls) {
-  urls_ = std::make_pair(requirement, urls);
-  return *this;
-}
-
-OfflinePageModelQueryBuilder&
-OfflinePageModelQueryBuilder::RequireSupportedByDownload(
-    Requirement supported_by_download) {
-  supported_by_download_ = supported_by_download;
-  return *this;
-}
-
-OfflinePageModelQueryBuilder&
-OfflinePageModelQueryBuilder::RequireShownAsRecentlyVisitedSite(
-    Requirement recently_visited) {
-  shown_as_recently_visited_site_ = recently_visited;
-  return *this;
-}
-
-OfflinePageModelQueryBuilder&
-OfflinePageModelQueryBuilder::RequireRestrictedToOriginalTab(
-    Requirement original_tab) {
-  restricted_to_original_tab_ = original_tab;
-  return *this;
-}
-
-std::unique_ptr<OfflinePageModelQuery> OfflinePageModelQueryBuilder::Build(
-    ClientPolicyController* controller) {
-  DCHECK(controller);
-
-  auto query = base::MakeUnique<OfflinePageModelQuery>();
-
-  query->urls_ = std::make_pair(
-      urls_.first, std::set<GURL>(urls_.second.begin(), urls_.second.end()));
-  urls_ = std::make_pair(Requirement::UNSET, std::vector<GURL>());
-  query->offline_ids_ = std::make_pair(
-      offline_ids_.first, std::set<int64_t>(offline_ids_.second.begin(),
-                                            offline_ids_.second.end()));
-  offline_ids_ = std::make_pair(Requirement::UNSET, std::vector<int64_t>());
-  query->client_ids_ = std::make_pair(
-      client_ids_.first,
-      std::set<ClientId>(client_ids_.second.begin(), client_ids_.second.end()));
-  client_ids_ = std::make_pair(Requirement::UNSET, std::vector<ClientId>());
-
-  std::vector<std::string> allowed_namespaces;
-  bool uses_namespace_restrictions = false;
-
-  for (auto& name_space : controller->GetAllNamespaces()) {
-    // If any exclusion requirements exist, and the namespace matches one of
-    // those excluded by policy, skip adding it to |allowed_namespaces|.
-    if ((supported_by_download_ == Requirement::EXCLUDE_MATCHING &&
-         controller->IsSupportedByDownload(name_space)) ||
-        (shown_as_recently_visited_site_ == Requirement::EXCLUDE_MATCHING &&
-         controller->IsShownAsRecentlyVisitedSite(name_space)) ||
-        (restricted_to_original_tab_ == Requirement::EXCLUDE_MATCHING &&
-         controller->IsRestrictedToOriginalTab(name_space))) {
-      // Skip namespaces that meet exclusion requirements.
-      uses_namespace_restrictions = true;
-      continue;
-    }
-
-    if ((supported_by_download_ == Requirement::INCLUDE_MATCHING &&
-         !controller->IsSupportedByDownload(name_space)) ||
-        (shown_as_recently_visited_site_ == Requirement::INCLUDE_MATCHING &&
-         !controller->IsShownAsRecentlyVisitedSite(name_space)) ||
-        (restricted_to_original_tab_ == Requirement::INCLUDE_MATCHING &&
-         !controller->IsRestrictedToOriginalTab(name_space))) {
-      // Skip namespaces that don't meet inclusion requirement.
-      uses_namespace_restrictions = true;
-      continue;
-    }
-
-    // Add namespace otherwise.
-    allowed_namespaces.emplace_back(name_space);
-  }
-
-  supported_by_download_ = Requirement::UNSET;
-  shown_as_recently_visited_site_ = Requirement::UNSET;
-  restricted_to_original_tab_ = Requirement::UNSET;
-
-  if (uses_namespace_restrictions) {
-    query->restricted_to_namespaces_ = base::MakeUnique<std::set<std::string>>(
-        allowed_namespaces.begin(), allowed_namespaces.end());
-  }
-
-  return query;
-}
-
-OfflinePageModelQuery::OfflinePageModelQuery() = default;
-OfflinePageModelQuery::~OfflinePageModelQuery() = default;
-
-std::pair<bool, std::set<std::string>>
-OfflinePageModelQuery::GetRestrictedToNamespaces() const {
-  if (!restricted_to_namespaces_)
-    return std::make_pair(false, std::set<std::string>());
-
-  return std::make_pair(true, *restricted_to_namespaces_);
-}
-
-std::pair<Requirement, std::set<int64_t>>
-OfflinePageModelQuery::GetRestrictedToOfflineIds() const {
-  if (offline_ids_.first == Requirement::UNSET)
-    return std::make_pair(Requirement::UNSET, std::set<int64_t>());
-
-  return offline_ids_;
-}
-
-std::pair<Requirement, std::set<ClientId>>
-OfflinePageModelQuery::GetRestrictedToClientIds() const {
-  if (client_ids_.first == Requirement::UNSET)
-    return std::make_pair(Requirement::UNSET, std::set<ClientId>());
-
-  return client_ids_;
-}
-
-std::pair<Requirement, std::set<GURL>>
-OfflinePageModelQuery::GetRestrictedToUrls() const {
-  if (urls_.first == Requirement::UNSET)
-    return std::make_pair(Requirement::UNSET, std::set<GURL>());
-
-  return urls_;
-}
-
-bool OfflinePageModelQuery::Matches(const OfflinePageItem& item) const {
-  switch (offline_ids_.first) {
-    case Requirement::UNSET:
-      break;
-    case Requirement::INCLUDE_MATCHING:
-      if (offline_ids_.second.count(item.offline_id) == 0)
-        return false;
-      break;
-    case Requirement::EXCLUDE_MATCHING:
-      if (offline_ids_.second.count(item.offline_id) > 0)
-        return false;
-      break;
-  }
-
-  switch (urls_.first) {
-    case Requirement::UNSET:
-      break;
-    case Requirement::INCLUDE_MATCHING:
-      if (urls_.second.count(item.url) == 0)
-        return false;
-      break;
-    case Requirement::EXCLUDE_MATCHING:
-      if (urls_.second.count(item.url) > 0)
-        return false;
-      break;
-  }
-
-  const ClientId& client_id = item.client_id;
-  if (restricted_to_namespaces_ &&
-      restricted_to_namespaces_->count(client_id.name_space) == 0) {
-    return false;
-  }
-
-  switch (client_ids_.first) {
-    case Requirement::UNSET:
-      break;
-    case Requirement::INCLUDE_MATCHING:
-      if (client_ids_.second.count(client_id) == 0)
-        return false;
-      break;
-    case Requirement::EXCLUDE_MATCHING:
-      if (client_ids_.second.count(client_id) > 0)
-        return false;
-      break;
-  }
-
-  return true;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_model_query.h b/components/offline_pages/offline_page_model_query.h
deleted file mode 100644
index a1c22c6..0000000
--- a/components/offline_pages/offline_page_model_query.h
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
-
-#include <set>
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_types.h"
-
-namespace offline_pages {
-
-// Can be used by OfflinePageModel instances to direct a query of the model.
-class OfflinePageModelQuery {
- public:
-  // A query can be constrained by several fields.  This constraint can be
-  // either a positive or negative one (or no constraint): a page MUST have
-  // something, or a page MUST NOT have something to match a query.
-  enum class Requirement {
-    // No requirement.
-    UNSET,
-    // All returned pages will have one of the values specified in the query
-    // requirement.
-    INCLUDE_MATCHING,
-    // All returned pages will not have any of the values specified in the query
-    // requirement.
-    EXCLUDE_MATCHING,
-  };
-
-  OfflinePageModelQuery();
-  virtual ~OfflinePageModelQuery();
-
-  std::pair<bool, std::set<std::string>> GetRestrictedToNamespaces() const;
-  std::pair<Requirement, std::set<int64_t>> GetRestrictedToOfflineIds() const;
-  std::pair<Requirement, std::set<ClientId>> GetRestrictedToClientIds() const;
-  std::pair<Requirement, std::set<GURL>> GetRestrictedToUrls() const;
-
-  // This is the workhorse function that is used by the in-memory offline page
-  // model, given a page it will find out whether that page matches the query.
-  bool Matches(const OfflinePageItem& page) const;
-
- private:
-  friend class OfflinePageModelQueryBuilder;
-
-  std::unique_ptr<std::set<std::string>> restricted_to_namespaces_;
-
-  std::pair<Requirement, std::set<int64_t>> offline_ids_;
-  std::pair<Requirement, std::set<ClientId>> client_ids_;
-  std::pair<Requirement, std::set<GURL>> urls_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQuery);
-};
-
-// Used to create an offline page model query.  QueryBuilders without
-// modifications create queries that allow all pages.
-// Can restrict results by policies provided by |ClientPolicyController|, or by
-// individual features of pages. Each restriction comes with a |Requirement|
-// that can be used to specify whether the input restriction should include or
-// exclude matching pages.
-class OfflinePageModelQueryBuilder {
- public:
-  using Requirement = OfflinePageModelQuery::Requirement;
-
-  OfflinePageModelQueryBuilder();
-  ~OfflinePageModelQueryBuilder();
-
-  // Sets the offline page IDs that are valid for this request.  If called
-  // multiple times, overwrites previous offline page ID restrictions.
-  OfflinePageModelQueryBuilder& SetOfflinePageIds(
-      Requirement requirement,
-      const std::vector<int64_t>& ids);
-
-  // Sets the client IDs that are valid for this request.  If called multiple
-  // times, overwrites previous client ID restrictions.
-  OfflinePageModelQueryBuilder& SetClientIds(Requirement requirement,
-                                             const std::vector<ClientId>& ids);
-
-  // Sets the URLs that are valid for this request.  If called multiple times,
-  // overwrites previous URL restrictions.
-  OfflinePageModelQueryBuilder& SetUrls(Requirement requirement,
-                                        const std::vector<GURL>& urls);
-
-  // Only include pages whose namespaces satisfy
-  // ClientPolicyController::IsSupportedByDownload(|namespace|) ==
-  //     |supported_by_download|
-  // Multiple calls overwrite previous ones.
-  OfflinePageModelQueryBuilder& RequireSupportedByDownload(
-      Requirement supported_by_download);
-
-  // Only include pages whose namespaces satisfy
-  // ClientPolicyController::IsShownAsRecentlyVisitedSite(|namespace|) ==
-  //     |recently_visited|
-  // Multiple calls overwrite previous ones.
-  OfflinePageModelQueryBuilder& RequireShownAsRecentlyVisitedSite(
-      Requirement recently_visited);
-
-  // Only include pages whose namespaces satisfy
-  // ClientPolicyController::IsRestrictedToOriginalTab(|namespace|) ==
-  //     |original_tab|
-  // Multiple calls overwrite previous ones.
-  OfflinePageModelQueryBuilder& RequireRestrictedToOriginalTab(
-      Requirement original_tab);
-
-  // Builds the query using the namespace policies provided by |controller|
-  // This resets the internal state.  |controller| should not be |nullptr|.
-  std::unique_ptr<OfflinePageModelQuery> Build(
-      ClientPolicyController* controller);
-
- private:
-  // Intersects the allowed namespaces in query_ with |namespaces|.  If
-  // |inverted| is true, intersects the allowed namespaces with all namespaces
-  // except those provided in |namespaces|.
-  void IntersectWithNamespaces(ClientPolicyController* controller,
-                               const std::vector<std::string>& namespaces,
-                               Requirement match_requirement);
-
-  std::pair<Requirement, std::vector<int64_t>> offline_ids_;
-  std::pair<Requirement, std::vector<ClientId>> client_ids_;
-  std::pair<Requirement, std::vector<GURL>> urls_;
-
-  Requirement supported_by_download_ = Requirement::UNSET;
-  Requirement shown_as_recently_visited_site_ = Requirement::UNSET;
-  Requirement restricted_to_original_tab_ = Requirement::UNSET;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageModelQueryBuilder);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_MODEL_QUERY_H_
diff --git a/components/offline_pages/offline_page_model_query_unittest.cc b/components/offline_pages/offline_page_model_query_unittest.cc
deleted file mode 100644
index 884cee1..0000000
--- a/components/offline_pages/offline_page_model_query_unittest.cc
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/time/time.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_query.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-using Requirement = OfflinePageModelQueryBuilder::Requirement;
-
-namespace {
-
-const ClientId kClientId1 = {kDefaultNamespace, "id1"};
-const GURL kUrl1 = GURL("https://ktestitem1.com");
-const OfflinePageItem kTestItem1(kUrl1, 1, kClientId1, base::FilePath(), 1);
-
-const ClientId kClientId2 = {kDefaultNamespace, "id2"};
-const GURL kUrl2 = GURL("https://ktestitem2.com");
-const OfflinePageItem kTestItem2(kUrl2, 2, kClientId2, base::FilePath(), 2);
-
-const char kTestNamespace[] = "test_namespace";
-}  // namespace
-
-class OfflinePageModelQueryTest : public testing::Test {
- public:
-  OfflinePageModelQueryTest();
-  ~OfflinePageModelQueryTest() override;
-
- protected:
-  ClientPolicyController policy_;
-  OfflinePageModelQueryBuilder builder_;
-
-  const OfflinePageItem download_page() {
-    return OfflinePageItem(GURL("https://download.com"), 4,
-                           {kDownloadNamespace, "id1"}, base::FilePath(), 4);
-  }
-
-  const OfflinePageItem original_tab_page() {
-    return OfflinePageItem(GURL("https://download.com"), 5,
-                           {kLastNNamespace, "id1"}, base::FilePath(), 5);
-  }
-
-  const OfflinePageItem test_namespace_page() {
-    return OfflinePageItem(GURL("https://download.com"), 6,
-                           {kTestNamespace, "id1"}, base::FilePath(), 6);
-  }
-
-  const OfflinePageItem recent_page() {
-    return OfflinePageItem(GURL("https://download.com"), 7,
-                           {kLastNNamespace, "id1"}, base::FilePath(), 7);
-  }
-};
-
-OfflinePageModelQueryTest::OfflinePageModelQueryTest() {
-  policy_.AddPolicyForTest(
-      kTestNamespace,
-      OfflinePageClientPolicyBuilder(kTestNamespace,
-                                     LifetimePolicy::LifetimeType::TEMPORARY,
-                                     kUnlimitedPages, kUnlimitedPages)
-          .SetIsOnlyShownInOriginalTab(true));
-}
-
-OfflinePageModelQueryTest::~OfflinePageModelQueryTest() {}
-
-TEST_F(OfflinePageModelQueryTest, DefaultValues) {
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  EXPECT_NE(nullptr, query.get());
-  EXPECT_EQ(Requirement::UNSET, query->GetRestrictedToOfflineIds().first);
-  EXPECT_FALSE(query->GetRestrictedToNamespaces().first);
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, OfflinePageIdsSet_Exclude) {
-  std::vector<int64_t> ids = {1, 4, 9, 16};
-  builder_.SetOfflinePageIds(Requirement::EXCLUDE_MATCHING, ids);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
-      query->GetRestrictedToOfflineIds();
-  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, offline_id_restriction.first);
-
-  ASSERT_EQ(ids.size(), offline_id_restriction.second.size());
-  for (auto id : ids) {
-    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
-        << "Did not find " << id << "in query restrictions.";
-  }
-
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, OfflinePageIdsSet) {
-  std::vector<int64_t> ids = {1, 4, 9, 16};
-  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
-      query->GetRestrictedToOfflineIds();
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, offline_id_restriction.first);
-
-  ASSERT_EQ(ids.size(), offline_id_restriction.second.size());
-  for (auto id : ids) {
-    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
-        << "Did not find " << id << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, OfflinePageIdsReplace) {
-  std::vector<int64_t> ids = {1, 4, 9, 16};
-  std::vector<int64_t> ids2 = {1, 2, 3, 4};
-
-  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids);
-  builder_.SetOfflinePageIds(Requirement::INCLUDE_MATCHING, ids2);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-  std::pair<Requirement, std::set<int64_t>> offline_id_restriction =
-      query->GetRestrictedToOfflineIds();
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, offline_id_restriction.first);
-
-  ASSERT_EQ(ids2.size(), offline_id_restriction.second.size());
-  for (auto id : ids2) {
-    EXPECT_EQ(1U, offline_id_restriction.second.count(id))
-        << "Did not find " << id << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, ClientIdsSet) {
-  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
-  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToClientIds();
-  const Requirement& requirement = restriction.first;
-  const std::set<ClientId>& ids_out = restriction.second;
-
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(ids.size(), ids_out.size());
-  for (auto id : ids) {
-    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
-                                     << id.id << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem2));
-  EXPECT_FALSE(query->Matches(kTestItem1));
-}
-
-TEST_F(OfflinePageModelQueryTest, ClientIdsSet_Exclude) {
-  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
-  builder_.SetClientIds(Requirement::EXCLUDE_MATCHING, ids);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToClientIds();
-  const Requirement& requirement = restriction.first;
-  const std::set<ClientId>& ids_out = restriction.second;
-
-  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(ids.size(), ids_out.size());
-  for (auto id : ids) {
-    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
-                                     << id.id << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, ClientIdsReplace) {
-  std::vector<ClientId> ids = {kClientId2, {"invalid", "client id"}};
-  std::vector<ClientId> ids2 = {kClientId1, {"invalid", "client id"}};
-
-  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids);
-  builder_.SetClientIds(Requirement::INCLUDE_MATCHING, ids2);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToClientIds();
-  const Requirement& requirement = restriction.first;
-  const std::set<ClientId>& ids_out = restriction.second;
-
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(ids2.size(), ids_out.size());
-  for (auto id : ids2) {
-    EXPECT_EQ(1U, ids_out.count(id)) << "Did not find " << id.name_space << "."
-                                     << id.id << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, UrlsSet) {
-  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToUrls();
-  const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
-
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(urls.size(), urls_out.size());
-  for (auto url : urls) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, UrlsSet_Exclude) {
-  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
-  builder_.SetUrls(Requirement::EXCLUDE_MATCHING, urls);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToUrls();
-  const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
-
-  EXPECT_EQ(Requirement::EXCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(urls.size(), urls_out.size());
-  for (auto url : urls) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
-  }
-
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, UrlsReplace) {
-  std::vector<GURL> urls = {kUrl1, GURL("https://abc.def")};
-  std::vector<GURL> urls2 = {kUrl2, GURL("https://abc.def")};
-
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls);
-  builder_.SetUrls(Requirement::INCLUDE_MATCHING, urls2);
-
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToUrls();
-  const Requirement& requirement = restriction.first;
-  const std::set<GURL>& urls_out = restriction.second;
-
-  EXPECT_EQ(Requirement::INCLUDE_MATCHING, requirement);
-
-  ASSERT_EQ(urls2.size(), urls_out.size());
-  for (auto url : urls2) {
-    EXPECT_EQ(1U, urls_out.count(url)) << "Did not find " << url
-                                       << "in query restrictions.";
-  }
-
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(kTestItem2));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireSupportedByDownload_Only) {
-  builder_.RequireSupportedByDownload(Requirement::INCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_TRUE(policy_.IsSupportedByDownload(name_space)) << "Namespace: "
-                                                           << name_space;
-  }
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(download_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireSupportedByDownload_Except) {
-  builder_.RequireSupportedByDownload(Requirement::EXCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_FALSE(policy_.IsSupportedByDownload(name_space)) << "Namespace: "
-                                                            << name_space;
-  }
-
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(download_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireShownAsRecentlyVisitedSite_Only) {
-  builder_.RequireShownAsRecentlyVisitedSite(Requirement::INCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_TRUE(policy_.IsShownAsRecentlyVisitedSite(name_space))
-        << "Namespace: " << name_space;
-  }
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(recent_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireShownAsRecentlyVisitedSite_Except) {
-  builder_.RequireShownAsRecentlyVisitedSite(Requirement::EXCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_FALSE(policy_.IsShownAsRecentlyVisitedSite(name_space))
-        << "Namespace: " << name_space;
-  }
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(recent_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireRestrictedToOriginalTab_Only) {
-  builder_.RequireRestrictedToOriginalTab(Requirement::INCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_TRUE(policy_.IsRestrictedToOriginalTab(name_space)) << "Namespace: "
-                                                               << name_space;
-  }
-  EXPECT_FALSE(query->Matches(kTestItem1));
-  EXPECT_TRUE(query->Matches(original_tab_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, RequireRestrictedToOriginalTab_Except) {
-  builder_.RequireRestrictedToOriginalTab(Requirement::EXCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  for (const std::string& name_space : namespaces_allowed) {
-    EXPECT_FALSE(policy_.IsRestrictedToOriginalTab(name_space)) << "Namespace: "
-                                                                << name_space;
-  }
-  EXPECT_TRUE(query->Matches(kTestItem1));
-  EXPECT_FALSE(query->Matches(original_tab_page()));
-}
-
-TEST_F(OfflinePageModelQueryTest, IntersectNamespaces) {
-  // This should exclude last N, but include |kTestNamespace|.
-  builder_.RequireRestrictedToOriginalTab(Requirement::INCLUDE_MATCHING)
-      .RequireShownAsRecentlyVisitedSite(Requirement::EXCLUDE_MATCHING);
-  std::unique_ptr<OfflinePageModelQuery> query = builder_.Build(&policy_);
-
-  auto restriction = query->GetRestrictedToNamespaces();
-  std::set<std::string> namespaces_allowed = restriction.second;
-  bool restricted_to_namespaces = restriction.first;
-  EXPECT_TRUE(restricted_to_namespaces);
-
-  EXPECT_TRUE(namespaces_allowed.count(kTestNamespace) == 1);
-  EXPECT_FALSE(query->Matches(recent_page()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_storage_manager.cc b/components/offline_pages/offline_page_storage_manager.cc
deleted file mode 100644
index b284719e..0000000
--- a/components/offline_pages/offline_page_storage_manager.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_storage_manager.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/time/clock.h"
-#include "base/time/default_clock.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_client_policy.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model.h"
-
-using LifetimeType = offline_pages::LifetimePolicy::LifetimeType;
-
-namespace offline_pages {
-
-constexpr double constants::kOfflinePageStorageLimit;
-constexpr double constants::kOfflinePageStorageClearThreshold;
-constexpr base::TimeDelta constants::kClearStorageInterval;
-constexpr base::TimeDelta constants::kRemovePageItemInterval;
-
-OfflinePageStorageManager::OfflinePageStorageManager(
-    OfflinePageModel* model,
-    ClientPolicyController* policy_controller,
-    ArchiveManager* archive_manager)
-    : model_(model),
-      policy_controller_(policy_controller),
-      archive_manager_(archive_manager),
-      clock_(new base::DefaultClock()),
-      weak_ptr_factory_(this) {}
-
-OfflinePageStorageManager::~OfflinePageStorageManager() {}
-
-void OfflinePageStorageManager::ClearPagesIfNeeded(
-    const ClearStorageCallback& callback) {
-  if (IsInProgress())
-    return;
-  clear_time_ = clock_->Now();
-  archive_manager_->GetStorageStats(base::Bind(
-      &OfflinePageStorageManager::OnGetStorageStatsDoneForClearingPages,
-      weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void OfflinePageStorageManager::SetClockForTesting(
-    std::unique_ptr<base::Clock> clock) {
-  clock_ = std::move(clock);
-}
-
-void OfflinePageStorageManager::OnGetStorageStatsDoneForClearingPages(
-    const ClearStorageCallback& callback,
-    const ArchiveManager::StorageStats& stats) {
-  DCHECK(IsInProgress());
-  ClearMode mode = ShouldClearPages(stats);
-  if (mode == ClearMode::NOT_NEEDED) {
-    last_clear_time_ = clear_time_;
-    callback.Run(0, ClearStorageResult::UNNECESSARY);
-    return;
-  }
-  model_->GetAllPages(
-      base::Bind(&OfflinePageStorageManager::OnGetAllPagesDoneForClearingPages,
-                 weak_ptr_factory_.GetWeakPtr(), callback, stats));
-}
-
-void OfflinePageStorageManager::OnGetAllPagesDoneForClearingPages(
-    const ClearStorageCallback& callback,
-    const ArchiveManager::StorageStats& stats,
-    const MultipleOfflinePageItemResult& pages) {
-  std::vector<int64_t> page_ids_to_clear;
-  GetPageIdsToClear(pages, stats, &page_ids_to_clear);
-  model_->DeletePagesByOfflineId(
-      page_ids_to_clear,
-      base::Bind(&OfflinePageStorageManager::OnExpiredPagesCleared,
-                 weak_ptr_factory_.GetWeakPtr(), callback,
-                 page_ids_to_clear.size()));
-}
-
-void OfflinePageStorageManager::OnExpiredPagesCleared(
-    const ClearStorageCallback& callback,
-    size_t pages_cleared,
-    DeletePageResult result) {
-  last_clear_time_ = clear_time_;
-  ClearStorageResult clear_result = ClearStorageResult::SUCCESS;
-  if (result != DeletePageResult::SUCCESS)
-    clear_result = ClearStorageResult::DELETE_FAILURE;
-  callback.Run(pages_cleared, clear_result);
-}
-
-void OfflinePageStorageManager::GetPageIdsToClear(
-    const MultipleOfflinePageItemResult& pages,
-    const ArchiveManager::StorageStats& stats,
-    std::vector<int64_t>* page_ids_to_clear) {
-  // TODO(romax): See how persistent should be considered here.
-  // Creating a map from namespace to a vector of page items.
-  // Sort each vector based on last accessed time and all pages after index
-  // min{size(), page_limit} should be deleted.
-  std::map<std::string, std::vector<OfflinePageItem>> pages_map;
-  std::vector<OfflinePageItem> kept_pages;
-  int64_t kept_pages_size = 0;
-
-  for (const auto& page : pages) {
-    if (!IsExpired(page))
-      pages_map[page.client_id.name_space].push_back(page);
-    else
-      page_ids_to_clear->push_back(page.offline_id);
-  }
-
-  for (auto& iter : pages_map) {
-    std::string name_space = iter.first;
-    std::vector<OfflinePageItem>& page_list = iter.second;
-
-    LifetimePolicy policy =
-        policy_controller_->GetPolicy(name_space).lifetime_policy;
-
-    std::sort(page_list.begin(), page_list.end(),
-              [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
-                return a.last_access_time > b.last_access_time;
-              });
-
-    size_t page_list_size = page_list.size();
-    size_t pos = 0;
-    while (pos < page_list_size &&
-           (policy.page_limit == kUnlimitedPages || pos < policy.page_limit) &&
-           !IsExpired(page_list.at(pos))) {
-      kept_pages_size += page_list.at(pos).file_size;
-      kept_pages.push_back(page_list.at(pos));
-      pos++;
-    }
-
-    for (; pos < page_list_size; pos++)
-      page_ids_to_clear->push_back(page_list.at(pos).offline_id);
-  }
-
-  // If we're still over the clear threshold, we're going to clear remaining
-  // pages from oldest last access time.
-  int64_t free_space = stats.free_disk_space;
-  int64_t total_size = stats.total_archives_size;
-  int64_t space_to_release =
-      kept_pages_size -
-      (total_size + free_space) * constants::kOfflinePageStorageClearThreshold;
-  if (space_to_release > 0) {
-    // Here we're sorting the |kept_pages| with oldest first.
-    std::sort(kept_pages.begin(), kept_pages.end(),
-              [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
-                return a.last_access_time < b.last_access_time;
-              });
-    size_t kept_pages_size = kept_pages.size();
-    size_t pos = 0;
-    while (pos < kept_pages_size && space_to_release > 0) {
-      space_to_release -= kept_pages.at(pos).file_size;
-      page_ids_to_clear->push_back(kept_pages.at(pos).offline_id);
-      pos++;
-    }
-  }
-}
-
-OfflinePageStorageManager::ClearMode
-OfflinePageStorageManager::ShouldClearPages(
-    const ArchiveManager::StorageStats& storage_stats) {
-  int64_t total_size = storage_stats.total_archives_size;
-  int64_t free_space = storage_stats.free_disk_space;
-  if (total_size == 0)
-    return ClearMode::NOT_NEEDED;
-
-  // If the size of all offline pages is more than limit, or it's larger than a
-  // specified percentage of all available storage space on the disk we'll clear
-  // all offline pages.
-  if (total_size >=
-      (total_size + free_space) * constants::kOfflinePageStorageLimit)
-    return ClearMode::DEFAULT;
-  // If it's been more than the pre-defined interval since the last time we
-  // clear the storage, we should clear pages.
-  if (last_clear_time_ == base::Time() ||
-      clear_time_ - last_clear_time_ >= constants::kClearStorageInterval) {
-    return ClearMode::DEFAULT;
-  }
-  // Otherwise there's no need to clear storage right now.
-  return ClearMode::NOT_NEEDED;
-}
-
-bool OfflinePageStorageManager::IsExpired(const OfflinePageItem& page) const {
-  const LifetimePolicy& policy =
-      policy_controller_->GetPolicy(page.client_id.name_space).lifetime_policy;
-  return policy.lifetime_type == LifetimeType::TEMPORARY &&
-         clear_time_ - page.last_access_time >= policy.expiration_period;
-}
-
-bool OfflinePageStorageManager::IsInProgress() const {
-  return clear_time_ > last_clear_time_;
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_storage_manager.h b/components/offline_pages/offline_page_storage_manager.h
deleted file mode 100644
index e93b850..0000000
--- a/components/offline_pages/offline_page_storage_manager.h
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/offline_page_types.h"
-
-namespace base {
-class Clock;
-}  // namespace base
-
-namespace offline_pages {
-
-// Maximum % of total available storage that will be occupied by offline pages
-// before a storage clearup.
-struct constants {
-  static constexpr double kOfflinePageStorageLimit = 0.3;
-  // The target % of storage usage we try to reach below when expiring pages.
-  static constexpr double kOfflinePageStorageClearThreshold = 0.1;
-  // The time that the storage cleanup will be triggered again since the last
-  // one.
-  static constexpr base::TimeDelta kClearStorageInterval =
-      base::TimeDelta::FromMinutes(10);
-  // The time that the page record will be removed from the store since the page
-  // has been expired.
-  static constexpr base::TimeDelta kRemovePageItemInterval =
-      base::TimeDelta::FromDays(21);
-};
-
-class ClientPolicyController;
-class OfflinePageModel;
-
-// This class is used for storage management of offline pages. It provides
-// a ClearPagesIfNeeded method which is used to clear outdated offline pages
-// based on last_access_time and lifetime policy of its namespace.
-// It has its own throttle mechanism so calling the method would not be
-// guaranteed to clear the pages immediately.
-//
-// OfflinePageModel should own and control the lifecycle of this manager.
-// And this manager would use OfflinePageModel to get/remove pages.
-class OfflinePageStorageManager {
- public:
-  enum class ClearStorageResult {
-    SUCCESS,                                // Cleared successfully.
-    UNNECESSARY,                            // No expired pages.
-    DEPRECATED_EXPIRE_FAILURE,              // Expiration failed. (DEPRECATED)
-    DELETE_FAILURE,                         // Deletion failed.
-    DEPRECATED_EXPIRE_AND_DELETE_FAILURES,  // Both expiration and deletion
-                                            // failed. (DEPRECATED)
-    // NOTE: always keep this entry at the end. Add new result types only
-    // immediately above this line. Make sure to update the corresponding
-    // histogram enum accordingly.
-    RESULT_COUNT,
-  };
-
-  // Callback used when calling ClearPagesIfNeeded.
-  // size_t: the number of cleared pages.
-  // ClearStorageResult: result of clearing pages in storage.
-  typedef base::Callback<void(size_t, ClearStorageResult)> ClearStorageCallback;
-
-  explicit OfflinePageStorageManager(OfflinePageModel* model,
-                                     ClientPolicyController* policy_controller,
-                                     ArchiveManager* archive_manager);
-
-  ~OfflinePageStorageManager();
-
-  // The manager would *try* to clear pages when called. It may not delete any
-  // pages (if clearing condition wasn't satisfied).
-  // It clears the storage (expire pages) when it's using more disk space than a
-  // certain limit, or the time elapsed from last time clearing is longer than a
-  // certain interval. Both values are defined above.
-  void ClearPagesIfNeeded(const ClearStorageCallback& callback);
-
-  // Sets the clock for testing.
-  void SetClockForTesting(std::unique_ptr<base::Clock> clock);
-
- private:
-  // Enum indicating how to clear the pages.
-  enum class ClearMode {
-    // Using normal expiration logic to clear pages. Will reduce the storage
-    // usage down below the threshold.
-    DEFAULT,
-    // No need to clear any page (no pages in the model or no expired pages and
-    // we're not exceeding the storage limit.)
-    NOT_NEEDED,
-  };
-
-  // Callback called after getting storage stats from archive manager.
-  void OnGetStorageStatsDoneForClearingPages(
-      const ClearStorageCallback& callback,
-      const ArchiveManager::StorageStats& pages);
-
-  // Callback called after getting all pages from model.
-  void OnGetAllPagesDoneForClearingPages(
-      const ClearStorageCallback& callback,
-      const ArchiveManager::StorageStats& storage_stats,
-      const MultipleOfflinePageItemResult& pages);
-
-  // Callback called after clearing expired pages from model.
-  void OnExpiredPagesCleared(const ClearStorageCallback& callback,
-                             size_t pages_cleared,
-                             DeletePageResult result);
-
-  // Gets offline IDs of pages that should be cleared based on current |stats|
-  // and return the IDs in |page_ids_to_clear|.
-  void GetPageIdsToClear(const MultipleOfflinePageItemResult& pages,
-                         const ArchiveManager::StorageStats& stats,
-                         std::vector<int64_t>* page_ids_to_clear);
-
-  // Determines if manager should clear pages.
-  ClearMode ShouldClearPages(const ArchiveManager::StorageStats& storage_stats);
-
-  // Returns true if |page| is should be cleared based on |clear_time_|.
-  bool IsExpired(const OfflinePageItem& page) const;
-
-  // Returns true if we're currently doing a cleanup.
-  bool IsInProgress() const;
-
-  // Not owned.
-  OfflinePageModel* model_;
-
-  // Not owned.
-  ClientPolicyController* policy_controller_;
-
-  // Not owned.
-  ArchiveManager* archive_manager_;
-
-  // Starting time of the current storage cleanup. If this time is later than
-  // |last_clear_time_| it means we're doing a cleanup.
-  base::Time clear_time_;
-
-  // Timestamp of last storage cleanup.
-  base::Time last_clear_time_;
-
-  // Clock for getting time.
-  std::unique_ptr<base::Clock> clock_;
-
-  base::WeakPtrFactory<OfflinePageStorageManager> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageStorageManager);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_STORAGE_MANAGER_H_
diff --git a/components/offline_pages/offline_page_storage_manager_unittest.cc b/components/offline_pages/offline_page_storage_manager_unittest.cc
deleted file mode 100644
index 6528eed..0000000
--- a/components/offline_pages/offline_page_storage_manager_unittest.cc
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_storage_manager.h"
-
-#include <stdint.h>
-#include <map>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/test/simple_test_clock.h"
-#include "base/time/time.h"
-#include "components/offline_pages/archive_manager.h"
-#include "components/offline_pages/client_namespace_constants.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_model_impl.h"
-#include "components/offline_pages/offline_page_types.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using LifetimePolicy = offline_pages::LifetimePolicy;
-using ClearStorageResult =
-    offline_pages::OfflinePageStorageManager::ClearStorageResult;
-using StorageStats = offline_pages::ArchiveManager::StorageStats;
-
-namespace offline_pages {
-
-namespace {
-const GURL kTestUrl("http://example.com");
-const base::FilePath::CharType kFilePath[] = FILE_PATH_LITERAL("/data");
-const int64_t kTestFileSize = 1 << 19;  // Make a page 512KB.
-const int64_t kFreeSpaceNormal = 100 * (1 << 20);
-
-enum TestOptions {
-  DEFAULT = 1 << 0,
-  DELETE_FAILURE = 1 << 1,
-};
-
-struct PageSettings {
-  std::string name_space;
-  int fresh_pages_count;
-  int expired_pages_count;
-};
-}  // namespace
-
-class OfflinePageTestModel : public OfflinePageModelImpl {
- public:
-  OfflinePageTestModel(std::vector<PageSettings> page_settings,
-                       base::SimpleTestClock* clock,
-                       TestOptions options = TestOptions::DEFAULT)
-      : policy_controller_(new ClientPolicyController()),
-        clock_(clock),
-        options_(options),
-        next_offline_id_(0) {
-    for (const auto& setting : page_settings)
-      AddPages(setting);
-  }
-
-  ~OfflinePageTestModel() override;
-
-  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override {
-    MultipleOfflinePageItemResult pages;
-    for (const auto& id_page_pair : pages_)
-      pages.push_back(id_page_pair.second);
-    callback.Run(pages);
-  }
-
-  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
-                              const DeletePageCallback& callback) override;
-
-  void AddPages(const PageSettings& setting);
-
-  // The |removed_pages_| would not be cleared in during a test, so the number
-  // of removed pages will be accumulative in a single test case.
-  const std::vector<OfflinePageItem>& GetRemovedPages() {
-    return removed_pages_;
-  }
-
-  int64_t GetTotalSize() const;
-
-  base::SimpleTestClock* clock() { return clock_; }
-
- private:
-  std::map<int64_t, OfflinePageItem> pages_;
-
-  std::vector<OfflinePageItem> removed_pages_;
-
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-
-  base::SimpleTestClock* clock_;
-
-  TestOptions options_;
-
-  int64_t next_offline_id_;
-};
-
-void OfflinePageTestModel::DeletePagesByOfflineId(
-    const std::vector<int64_t>& offline_ids,
-    const DeletePageCallback& callback) {
-  if (options_ & TestOptions::DELETE_FAILURE) {
-    callback.Run(DeletePageResult::STORE_FAILURE);
-    return;
-  }
-  for (const auto id : offline_ids) {
-    removed_pages_.push_back(pages_.at(id));
-    pages_.erase(id);
-  }
-  callback.Run(DeletePageResult::SUCCESS);
-}
-
-int64_t OfflinePageTestModel::GetTotalSize() const {
-  int64_t res = 0;
-  for (const auto& id_page_pair : pages_)
-    res += id_page_pair.second.file_size;
-  return res;
-}
-
-OfflinePageTestModel::~OfflinePageTestModel() {}
-
-void OfflinePageTestModel::AddPages(const PageSettings& setting) {
-  std::string name_space = setting.name_space;
-  int fresh_pages_count = setting.fresh_pages_count;
-  int expired_pages_count = setting.expired_pages_count;
-  base::Time now = clock()->Now();
-  // Fresh pages.
-  for (int i = 0; i < fresh_pages_count; i++) {
-    OfflinePageItem page =
-        OfflinePageItem(kTestUrl, next_offline_id_,
-                        ClientId(name_space, std::to_string(next_offline_id_)),
-                        base::FilePath(kFilePath), kTestFileSize);
-    page.last_access_time = now;
-    pages_[next_offline_id_] = page;
-    next_offline_id_++;
-  }
-  // Expired pages.
-  for (int i = 0; i < expired_pages_count; i++) {
-    OfflinePageItem page =
-        OfflinePageItem(kTestUrl, next_offline_id_,
-                        ClientId(name_space, std::to_string(next_offline_id_)),
-                        base::FilePath(kFilePath), kTestFileSize);
-    page.last_access_time = now -
-                            policy_controller_->GetPolicy(name_space)
-                                .lifetime_policy.expiration_period;
-    pages_[next_offline_id_] = page;
-    next_offline_id_++;
-  }
-}
-
-class TestArchiveManager : public ArchiveManager {
- public:
-  explicit TestArchiveManager(StorageStats stats) : stats_(stats) {}
-
-  void GetStorageStats(const base::Callback<
-                       void(const ArchiveManager::StorageStats& storage_stats)>&
-                           callback) const override {
-    callback.Run(stats_);
-  }
-
-  void SetValues(ArchiveManager::StorageStats stats) { stats_ = stats; }
-
- private:
-  StorageStats stats_;
-};
-
-class OfflinePageStorageManagerTest : public testing::Test {
- public:
-  OfflinePageStorageManagerTest();
-  OfflinePageStorageManager* manager() { return manager_.get(); }
-  OfflinePageTestModel* model() { return model_.get(); }
-  ClientPolicyController* policy_controller() {
-    return policy_controller_.get();
-  }
-  TestArchiveManager* test_archive_manager() { return archive_manager_.get(); }
-  void OnPagesCleared(size_t pages_cleared_count, ClearStorageResult result);
-  void Initialize(const std::vector<PageSettings>& settings,
-                  StorageStats stats = {kFreeSpaceNormal, 0},
-                  TestOptions options = TestOptions::DEFAULT);
-  void TryClearPages();
-
-  // testing::Test
-  void TearDown() override;
-
-  base::SimpleTestClock* clock() { return clock_; }
-  int last_cleared_page_count() const {
-    return static_cast<int>(last_cleared_page_count_);
-  }
-  int total_cleared_times() const { return total_cleared_times_; }
-  ClearStorageResult last_clear_storage_result() const {
-    return last_clear_storage_result_;
-  }
-
- private:
-  std::unique_ptr<OfflinePageStorageManager> manager_;
-  std::unique_ptr<OfflinePageTestModel> model_;
-  std::unique_ptr<ClientPolicyController> policy_controller_;
-  std::unique_ptr<TestArchiveManager> archive_manager_;
-
-  base::SimpleTestClock* clock_;
-
-  size_t last_cleared_page_count_;
-  int total_cleared_times_;
-  ClearStorageResult last_clear_storage_result_;
-};
-
-OfflinePageStorageManagerTest::OfflinePageStorageManagerTest()
-    : policy_controller_(new ClientPolicyController()),
-      last_cleared_page_count_(0),
-      total_cleared_times_(0),
-      last_clear_storage_result_(ClearStorageResult::SUCCESS) {}
-
-void OfflinePageStorageManagerTest::Initialize(
-    const std::vector<PageSettings>& page_settings,
-    StorageStats stats,
-    TestOptions options) {
-  std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
-  clock_ = clock.get();
-  clock_->SetNow(base::Time::Now());
-  model_.reset(new OfflinePageTestModel(page_settings, clock_, options));
-
-  if (stats.free_disk_space == 0)
-    stats.free_disk_space = kFreeSpaceNormal;
-  if (stats.total_archives_size == 0) {
-    stats.total_archives_size = model_->GetTotalSize();
-  }
-  archive_manager_.reset(new TestArchiveManager(stats));
-  manager_.reset(new OfflinePageStorageManager(
-      model_.get(), policy_controller(), archive_manager_.get()));
-  manager_->SetClockForTesting(std::move(clock));
-}
-
-void OfflinePageStorageManagerTest::TryClearPages() {
-  manager()->ClearPagesIfNeeded(base::Bind(
-      &OfflinePageStorageManagerTest::OnPagesCleared, base::Unretained(this)));
-}
-
-void OfflinePageStorageManagerTest::TearDown() {
-  manager_.reset();
-  model_.reset();
-}
-
-void OfflinePageStorageManagerTest::OnPagesCleared(size_t pages_cleared_count,
-                                                   ClearStorageResult result) {
-  last_cleared_page_count_ = pages_cleared_count;
-  total_cleared_times_++;
-  last_clear_storage_result_ = result;
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestClearPagesLessThanLimit) {
-  Initialize(std::vector<PageSettings>(
-      {{kBookmarkNamespace, 1, 1}, {kLastNNamespace, 1, 1}}));
-  clock()->Advance(base::TimeDelta::FromMinutes(30));
-  TryClearPages();
-  EXPECT_EQ(2, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(2, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreThanLimit) {
-  Initialize(std::vector<PageSettings>(
-      {{kBookmarkNamespace, 10, 15}, {kLastNNamespace, 5, 30}}));
-  clock()->Advance(base::TimeDelta::FromMinutes(30));
-  TryClearPages();
-  EXPECT_EQ(45, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(45, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestClearPagesMoreFreshPages) {
-  Initialize(std::vector<PageSettings>(
-                 {{kBookmarkNamespace, 30, 0}, {kLastNNamespace, 100, 1}}),
-             {1000 * (1 << 20), 0});
-  clock()->Advance(base::TimeDelta::FromMinutes(30));
-  TryClearPages();
-  EXPECT_EQ(1, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestDeleteAsyncPages) {
-  Initialize(std::vector<PageSettings>({{kAsyncNamespace, 20, 0}}));
-  clock()->Advance(base::TimeDelta::FromDays(367));
-  TryClearPages();
-  EXPECT_EQ(0, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestDeletionFailed) {
-  Initialize(std::vector<PageSettings>(
-                 {{kBookmarkNamespace, 10, 10}, {kLastNNamespace, 10, 10}}),
-             {kFreeSpaceNormal, 0}, TestOptions::DELETE_FAILURE);
-  TryClearPages();
-  EXPECT_EQ(20, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::DELETE_FAILURE, last_clear_storage_result());
-  EXPECT_EQ(0, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestStorageTimeInterval) {
-  Initialize(std::vector<PageSettings>(
-      {{kBookmarkNamespace, 10, 10}, {kLastNNamespace, 10, 10}}));
-  clock()->Advance(base::TimeDelta::FromMinutes(30));
-  TryClearPages();
-  EXPECT_EQ(20, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
-
-  // Advance clock so we go over the gap, but no expired pages.
-  clock()->Advance(constants::kClearStorageInterval +
-                   base::TimeDelta::FromMinutes(1));
-  TryClearPages();
-  EXPECT_EQ(0, last_cleared_page_count());
-  EXPECT_EQ(2, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
-
-  // Advance clock so we are still in the gap, should be unnecessary.
-  clock()->Advance(constants::kClearStorageInterval -
-                   base::TimeDelta::FromMinutes(1));
-  TryClearPages();
-  EXPECT_EQ(0, last_cleared_page_count());
-  EXPECT_EQ(3, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
-  EXPECT_EQ(20, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-TEST_F(OfflinePageStorageManagerTest, TestClearMultipleTimes) {
-  Initialize(std::vector<PageSettings>({{kBookmarkNamespace, 30, 0},
-                                        {kLastNNamespace, 100, 1},
-                                        {kAsyncNamespace, 40, 0}}),
-             {1000 * (1 << 20), 0});
-  clock()->Advance(base::TimeDelta::FromMinutes(30));
-  LifetimePolicy policy =
-      policy_controller()->GetPolicy(kLastNNamespace).lifetime_policy;
-
-  TryClearPages();
-  EXPECT_EQ(1, last_cleared_page_count());
-  EXPECT_EQ(1, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(1, static_cast<int>(model()->GetRemovedPages().size()));
-
-  // Advance the clock by expiration period of last_n namespace, should be
-  // expiring all pages left in the namespace.
-  clock()->Advance(policy.expiration_period);
-  TryClearPages();
-  EXPECT_EQ(100, last_cleared_page_count());
-  EXPECT_EQ(2, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
-
-  // Only 1 ms passes and no changes in pages, so no need to clear page.
-  clock()->Advance(base::TimeDelta::FromMilliseconds(1));
-  TryClearPages();
-  EXPECT_EQ(0, last_cleared_page_count());
-  EXPECT_EQ(3, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::UNNECESSARY, last_clear_storage_result());
-  EXPECT_EQ(101, static_cast<int>(model()->GetRemovedPages().size()));
-
-  // Adding more fresh pages to make it go over limit.
-  clock()->Advance(base::TimeDelta::FromMinutes(5));
-  model()->AddPages({kBookmarkNamespace, 400, 0});
-  int64_t total_size_before = model()->GetTotalSize();
-  int64_t available_space = 300 * (1 << 20);  // 300 MB
-  test_archive_manager()->SetValues({available_space, total_size_before});
-  EXPECT_GE(total_size_before, constants::kOfflinePageStorageLimit *
-                                   (available_space + total_size_before));
-  TryClearPages();
-  EXPECT_LE(total_size_before * constants::kOfflinePageStorageClearThreshold,
-            model()->GetTotalSize());
-  EXPECT_EQ(4, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  int deleted_page_total = last_cleared_page_count() + 101;
-  EXPECT_EQ(deleted_page_total,
-            static_cast<int>(model()->GetRemovedPages().size()));
-
-  // After more days, all pages should be deleted.
-  clock()->Advance(base::TimeDelta::FromDays(30));
-  TryClearPages();
-  EXPECT_EQ(0, model()->GetTotalSize());
-  EXPECT_EQ(5, total_cleared_times());
-  EXPECT_EQ(ClearStorageResult::SUCCESS, last_clear_storage_result());
-  // Number of removed pages should be all the pages initially created plus 400
-  // more pages added above for bookmark namespace.
-  EXPECT_EQ(171 + 400, static_cast<int>(model()->GetRemovedPages().size()));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_test_archiver.cc b/components/offline_pages/offline_page_test_archiver.cc
deleted file mode 100644
index 8ee7f37c..0000000
--- a/components/offline_pages/offline_page_test_archiver.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_test_archiver.h"
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace offline_pages {
-
-OfflinePageTestArchiver::OfflinePageTestArchiver(
-    Observer* observer,
-    const GURL& url,
-    ArchiverResult result,
-    const base::string16& result_title,
-    int64_t size_to_report,
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : observer_(observer),
-      url_(url),
-      result_(result),
-      size_to_report_(size_to_report),
-      create_archive_called_(false),
-      delayed_(false),
-      result_title_(result_title),
-      task_runner_(task_runner) {}
-
-OfflinePageTestArchiver::~OfflinePageTestArchiver() {
-  EXPECT_TRUE(create_archive_called_);
-}
-
-void OfflinePageTestArchiver::CreateArchive(
-    const base::FilePath& archives_dir,
-    int64_t archive_id,
-    const CreateArchiveCallback& callback) {
-  create_archive_called_ = true;
-  callback_ = callback;
-  archives_dir_ = archives_dir;
-  if (!delayed_)
-    CompleteCreateArchive();
-}
-
-void OfflinePageTestArchiver::CompleteCreateArchive() {
-  DCHECK(!callback_.is_null());
-  base::FilePath archive_path;
-  if (filename_.empty()) {
-    ASSERT_TRUE(base::CreateTemporaryFileInDir(archives_dir_, &archive_path));
-  } else {
-    archive_path = archives_dir_.Append(filename_);
-    // This step ensures the file is created and closed immediately.
-    base::File file(archive_path, base::File::FLAG_OPEN_ALWAYS);
-  }
-  observer_->SetLastPathCreatedByArchiver(archive_path);
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(callback_, this, result_, url_, archive_path,
-                            result_title_, size_to_report_));
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_test_archiver.h b/components/offline_pages/offline_page_test_archiver.h
deleted file mode 100644
index 4893f66..0000000
--- a/components/offline_pages/offline_page_test_archiver.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string16.h"
-#include "components/offline_pages/offline_page_archiver.h"
-
-class GURL;
-
-namespace base {
-class FilePath;
-}  // namespace
-
-namespace offline_pages {
-
-// A test archiver class, which allows for testing offline pages without a need
-// for an actual web contents.
-class OfflinePageTestArchiver : public OfflinePageArchiver {
- public:
-  // TODO(fgorski): Try refactoring the observer out and replace it with a
-  // callback, or completely remove the call to |SetLastPathCreatedByArchiver|.
-  class Observer {
-   public:
-    virtual ~Observer() {}
-    virtual void SetLastPathCreatedByArchiver(
-        const base::FilePath& file_path) = 0;
-  };
-
-  OfflinePageTestArchiver(
-      Observer* observer,
-      const GURL& url,
-      ArchiverResult result,
-      const base::string16& result_title,
-      int64_t size_to_report,
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
-  ~OfflinePageTestArchiver() override;
-
-  // OfflinePageArchiver implementation:
-  void CreateArchive(const base::FilePath& archives_dir,
-                     int64_t archive_id,
-                     const CreateArchiveCallback& callback) override;
-
-  // Completes the creation of archive. Should be used with |set_delayed| set to
-  // true.
-  void CompleteCreateArchive();
-
-  // When set to true, |CompleteCreateArchive| should be called explicitly for
-  // the process to finish.
-  // TODO(fgorski): See if we can move this to the constructor.
-  void set_delayed(bool delayed) { delayed_ = delayed; }
-
-  // Allows to explicitly specify a file name for the tests.
-  // TODO(fgorski): See if we can move this to the constructor.
-  void set_filename(const base::FilePath& filename) { filename_ = filename; }
-
-  bool create_archive_called() const { return create_archive_called_; }
-
- private:
-  // Not owned. Outlives OfflinePageTestArchiver.
-  Observer* observer_;
-  GURL url_;
-  base::FilePath archives_dir_;
-  base::FilePath filename_;
-  ArchiverResult result_;
-  int64_t size_to_report_;
-  bool create_archive_called_;
-  bool delayed_;
-  base::string16 result_title_;
-  CreateArchiveCallback callback_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(OfflinePageTestArchiver);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_ARCHIVER_H_
diff --git a/components/offline_pages/offline_page_test_store.cc b/components/offline_pages/offline_page_test_store.cc
deleted file mode 100644
index 59f4906..0000000
--- a/components/offline_pages/offline_page_test_store.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/offline_page_test_store.h"
-
-#include <map>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-OfflinePageTestStore::OfflinePageTestStore(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : task_runner_(task_runner),
-      scenario_(TestScenario::SUCCESSFUL),
-      store_state_(StoreState::NOT_LOADED) {}
-
-OfflinePageTestStore::OfflinePageTestStore(
-    const OfflinePageTestStore& other_store)
-    : task_runner_(other_store.task_runner_),
-      scenario_(other_store.scenario_),
-      offline_pages_(other_store.offline_pages_) {}
-
-OfflinePageTestStore::~OfflinePageTestStore() {}
-
-void OfflinePageTestStore::Initialize(const InitializeCallback& callback) {
-  if (scenario_ == TestScenario::LOAD_FAILED_RESET_FAILED ||
-      scenario_ == TestScenario::LOAD_FAILED_RESET_SUCCESS) {
-    store_state_ = StoreState::FAILED_LOADING;
-    offline_pages_.clear();
-  } else {
-    store_state_ = StoreState::LOADED;
-  }
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(callback, store_state_ == StoreState::LOADED));
-}
-
-void OfflinePageTestStore::GetOfflinePages(const LoadCallback& callback) {
-  task_runner_->PostTask(FROM_HERE, base::Bind(callback, GetAllPages()));
-}
-
-void OfflinePageTestStore::AddOfflinePage(const OfflinePageItem& offline_page,
-                                          const AddCallback& callback) {
-  // TODO(fgorski): Add and cover scenario with existing item.
-  ItemActionStatus result;
-  if (store_state_ == StoreState::LOADED &&
-      scenario_ != TestScenario::WRITE_FAILED) {
-    offline_pages_[offline_page.offline_id] = offline_page;
-    last_saved_page_ = offline_page;
-    result = ItemActionStatus::SUCCESS;
-  } else {
-    result = ItemActionStatus::STORE_ERROR;
-  }
-  if (!callback.is_null())
-    task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
-}
-
-void OfflinePageTestStore::UpdateOfflinePages(
-    const std::vector<OfflinePageItem>& pages,
-    const UpdateCallback& callback) {
-  // TODO(fgorski): Cover scenario where new items are being created while they
-  // shouldn't.
-  std::unique_ptr<OfflinePagesUpdateResult> result(
-      new OfflinePagesUpdateResult(StoreState::LOADED));
-  if (scenario_ == TestScenario::WRITE_FAILED) {
-    for (const auto& page : pages) {
-      result->item_statuses.push_back(
-          std::make_pair(page.offline_id, ItemActionStatus::STORE_ERROR));
-    }
-  } else {
-    for (const auto& page : pages) {
-      offline_pages_[page.offline_id] = page;
-      last_saved_page_ = page;
-      result->item_statuses.push_back(
-          std::make_pair(page.offline_id, ItemActionStatus::SUCCESS));
-    }
-    result->updated_items.insert(result->updated_items.begin(), pages.begin(),
-                                 pages.end());
-  }
-  if (!callback.is_null())
-    task_runner_->PostTask(FROM_HERE,
-                           base::Bind(callback, base::Passed(&result)));
-}
-
-void OfflinePageTestStore::RemoveOfflinePages(
-    const std::vector<int64_t>& offline_ids,
-    const UpdateCallback& callback) {
-  std::unique_ptr<OfflinePagesUpdateResult> result(
-      new OfflinePagesUpdateResult(StoreState::LOADED));
-
-  ASSERT_FALSE(offline_ids.empty());
-  if (scenario_ == TestScenario::REMOVE_FAILED) {
-    for (const auto& offline_id : offline_ids) {
-      result->item_statuses.push_back(
-          std::make_pair(offline_id, ItemActionStatus::STORE_ERROR));
-    }
-    // Anything different that LOADED is good here.
-    result->store_state = StoreState::FAILED_LOADING;
-  } else {
-    for (const auto& offline_id : offline_ids) {
-      auto iter = offline_pages_.find(offline_id);
-      ItemActionStatus status;
-      if (iter != offline_pages_.end()) {
-        result->updated_items.push_back(iter->second);
-        status = ItemActionStatus::SUCCESS;
-        offline_pages_.erase(iter);
-      } else {
-        status = ItemActionStatus::NOT_FOUND;
-      }
-      result->item_statuses.push_back(std::make_pair(offline_id, status));
-    }
-  }
-
-  task_runner_->PostTask(FROM_HERE,
-                         base::Bind(callback, base::Passed(&result)));
-}
-
-void OfflinePageTestStore::Reset(const ResetCallback& callback) {
-  if (scenario_ == TestScenario::LOAD_FAILED_RESET_FAILED) {
-    store_state_ = StoreState::FAILED_RESET;
-  } else {
-    store_state_ = StoreState::NOT_LOADED;
-    // Scenario is flipped to successful here, as the reset succeeds.
-    if (scenario_ == TestScenario::LOAD_FAILED_RESET_SUCCESS)
-      scenario_ = TestScenario::SUCCESSFUL;
-  }
-
-  offline_pages_.clear();
-  task_runner_->PostTask(
-      FROM_HERE, base::Bind(callback, store_state_ == StoreState::NOT_LOADED));
-}
-
-StoreState OfflinePageTestStore::state() const {
-  return store_state_;
-}
-
-void OfflinePageTestStore::UpdateLastAccessTime(
-    int64_t offline_id,
-    const base::Time& last_access_time) {
-  auto iter = offline_pages_.find(offline_id);
-  if (iter == offline_pages_.end())
-    return;
-  iter->second.last_access_time = last_access_time;
-}
-
-std::vector<OfflinePageItem> OfflinePageTestStore::GetAllPages() const {
-  std::vector<OfflinePageItem> offline_pages;
-  for (const auto& id_page_pair : offline_pages_)
-    offline_pages.push_back(id_page_pair.second);
-  return offline_pages;
-}
-
-void OfflinePageTestStore::ClearAllPages() {
-  offline_pages_.clear();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/offline_page_test_store.h b/components/offline_pages/offline_page_test_store.h
deleted file mode 100644
index 2304138..0000000
--- a/components/offline_pages/offline_page_test_store.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-#include "components/offline_pages/offline_page_item.h"
-#include "components/offline_pages/offline_page_metadata_store.h"
-
-namespace offline_pages {
-
-// Offline page store to be used for testing purposes. It stores offline pages
-// in memory. All callbacks are posted immediately through a provided
-// |task_runner|.
-class OfflinePageTestStore : public OfflinePageMetadataStore {
- public:
-  enum class TestScenario {
-    SUCCESSFUL,
-    WRITE_FAILED,
-    LOAD_FAILED_RESET_SUCCESS,
-    LOAD_FAILED_RESET_FAILED,
-    REMOVE_FAILED,
-  };
-
-  explicit OfflinePageTestStore(
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
-  explicit OfflinePageTestStore(const OfflinePageTestStore& other_store);
-  ~OfflinePageTestStore() override;
-
-  // OfflinePageMetadataStore overrides:
-  void Initialize(const InitializeCallback& callback) override;
-  void GetOfflinePages(const LoadCallback& callback) override;
-  void AddOfflinePage(const OfflinePageItem& offline_page,
-                      const AddCallback& callback) override;
-  void UpdateOfflinePages(const std::vector<OfflinePageItem>& pages,
-                          const UpdateCallback& callback) override;
-  void RemoveOfflinePages(const std::vector<int64_t>& offline_ids,
-                          const UpdateCallback& callback) override;
-  void Reset(const ResetCallback& callback) override;
-  StoreState state() const override;
-
-  void UpdateLastAccessTime(int64_t offline_id,
-                            const base::Time& last_access_time);
-
-  // Returns all pages, regardless their states.
-  std::vector<OfflinePageItem> GetAllPages() const;
-
-  // Clear all pages in the store.
-  void ClearAllPages();
-
-  const OfflinePageItem& last_saved_page() const { return last_saved_page_; }
-
-  void set_test_scenario(TestScenario scenario) { scenario_ = scenario; };
-
- private:
-  OfflinePageItem last_saved_page_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  TestScenario scenario_;
-  StoreState store_state_;
-
-  std::map<int64_t, OfflinePageItem> offline_pages_;
-
-  DISALLOW_ASSIGN(OfflinePageTestStore);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TEST_STORE_H_
diff --git a/components/offline_pages/offline_page_types.h b/components/offline_pages/offline_page_types.h
deleted file mode 100644
index 8dbfa3d..0000000
--- a/components/offline_pages/offline_page_types.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
-
-#include <stdint.h>
-
-#include <set>
-#include <vector>
-
-#include "base/callback.h"
-#include "components/offline_pages/offline_page_item.h"
-
-class GURL;
-
-// This file contains common callbacks used by OfflinePageModel and is a
-// temporary step to refactor and interface of the model out of the
-// implementation.
-namespace offline_pages {
-// Result of saving a page offline.
-// A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
-enum class SavePageResult {
-  SUCCESS,
-  CANCELLED,
-  DEVICE_FULL,
-  CONTENT_UNAVAILABLE,
-  ARCHIVE_CREATION_FAILED,
-  STORE_FAILURE,
-  ALREADY_EXISTS,
-  // Certain pages, i.e. file URL or NTP, will not be saved because these
-  // are already locally accessible.
-  SKIPPED,
-  SECURITY_CERTIFICATE_ERROR,
-  // NOTE: always keep this entry at the end. Add new result types only
-  // immediately above this line. Make sure to update the corresponding
-  // histogram enum accordingly.
-  RESULT_COUNT,
-};
-
-// Result of deleting an offline page.
-// A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offlinepages
-enum class DeletePageResult {
-  SUCCESS,
-  CANCELLED,
-  STORE_FAILURE,
-  DEVICE_FAILURE,
-  // Deprecated. Deleting pages which are not in metadata store would be
-  // returing |SUCCESS|. Should not be used anymore.
-  NOT_FOUND,
-  // NOTE: always keep this entry at the end. Add new result types only
-  // immediately above this line. Make sure to update the corresponding
-  // histogram enum accordingly.
-  RESULT_COUNT,
-};
-
-typedef std::set<GURL> CheckPagesExistOfflineResult;
-typedef std::vector<int64_t> MultipleOfflineIdResult;
-typedef std::vector<OfflinePageItem> MultipleOfflinePageItemResult;
-
-typedef base::Callback<void(SavePageResult, int64_t)> SavePageCallback;
-typedef base::Callback<void(DeletePageResult)> DeletePageCallback;
-typedef base::Callback<void(const CheckPagesExistOfflineResult&)>
-    CheckPagesExistOfflineCallback;
-typedef base::Callback<void(bool)> HasPagesCallback;
-typedef base::Callback<void(const MultipleOfflineIdResult&)>
-    MultipleOfflineIdCallback;
-typedef base::Callback<void(const OfflinePageItem*)>
-    SingleOfflinePageItemCallback;
-typedef base::Callback<void(const MultipleOfflinePageItemResult&)>
-    MultipleOfflinePageItemCallback;
-typedef base::Callback<bool(const GURL&)> UrlPredicate;
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_TYPES_H_
diff --git a/components/offline_pages/offline_store_types.h b/components/offline_pages/offline_store_types.h
deleted file mode 100644
index a4b3704..0000000
--- a/components/offline_pages/offline_store_types.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
-#define COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
-
-#include <stdint.h>
-
-#include <utility>
-#include <vector>
-
-// This file contains common types and callbacks used by storage of various
-// offline page related components.
-namespace offline_pages {
-
-// TODO(fgorski): This enum is meant to replace |LoadStatus|.
-// Current store state. When LOADED, the store is operational. When
-// loading or reset fails, it is reflected appropriately.
-enum class StoreState {
-  NOT_LOADED,      // Store is not loaded yet.
-  LOADED,          // Store is properly loaded and operational.
-  FAILED_LOADING,  // Store initialization failed.
-  FAILED_RESET,    // Resetting the store failed.
-};
-
-// Statuses referring to actions taken on items in the stores.
-// GENERATED_JAVA_ENUM_PACKAGE:org.chromium.components.offlinepages
-enum class ItemActionStatus {
-  SUCCESS,
-  ALREADY_EXISTS,
-  NOT_FOUND,
-  STORE_ERROR,
-};
-
-// List of item action statuses mapped to item ID.
-typedef std::vector<std::pair<int64_t, ItemActionStatus>> MultipleItemStatuses;
-
-// Collective result for store update.
-template <typename T>
-class StoreUpdateResult {
- public:
-  explicit StoreUpdateResult(StoreState state)
-      : store_state(state) {}
-  ~StoreUpdateResult() {}
-
-  // List of Offline ID to item action status mappings.
-  // It is meant to be consumed by the original caller of the operation.
-  MultipleItemStatuses item_statuses;
-
-  // List of successfully updated offline page items as seen after operation
-  // concludes. It is meant to be used when passing to the observers.
-  std::vector<T> updated_items;
-
-  // State of the store after the operation is done.
-  StoreState store_state;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_OFFLINE_STORE_TYPES_H_
diff --git a/components/offline_pages/request_header/offline_page_header.cc b/components/offline_pages/request_header/offline_page_header.cc
deleted file mode 100644
index db7b7c9..0000000
--- a/components/offline_pages/request_header/offline_page_header.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/request_header/offline_page_header.h"
-
-#include "base/strings/string_tokenizer.h"
-#include "base/strings/string_util.h"
-
-namespace offline_pages {
-
-const char kOfflinePageHeader[] = "X-Chrome-offline";
-const char kOfflinePageHeaderReasonKey[] = "reason";
-const char kOfflinePageHeaderReasonValueDueToNetError[] = "error";
-const char kOfflinePageHeaderReasonValueFromDownload[] = "download";
-const char kOfflinePageHeaderReasonValueReload[] = "reload";
-const char kOfflinePageHeaderPersistKey[] = "persist";
-const char kOfflinePageHeaderIDKey[] = "id";
-
-namespace {
-
-bool ParseOfflineHeaderValue(const std::string& header_value,
-                             bool* need_to_persist,
-                             OfflinePageHeader::Reason* reason,
-                             std::string* id) {
-  // If the offline header is not present, treat it as not parsed successfully.
-  if (header_value.empty())
-    return false;
-
-  bool token_found = false;
-  base::StringTokenizer tokenizer(header_value, ", ");
-  while (tokenizer.GetNext()) {
-    token_found = true;
-    std::string pair = tokenizer.token();
-    std::size_t pos = pair.find('=');
-    if (pos == std::string::npos)
-      return false;
-    std::string key = base::ToLowerASCII(pair.substr(0, pos));
-    std::string value = base::ToLowerASCII(pair.substr(pos + 1));
-    if (key == kOfflinePageHeaderPersistKey) {
-      if (value == "1")
-        *need_to_persist = true;
-      else if (value == "0")
-        *need_to_persist = false;
-      else
-        return false;
-    } else if (key == kOfflinePageHeaderReasonKey) {
-      if (value == kOfflinePageHeaderReasonValueDueToNetError)
-        *reason = OfflinePageHeader::Reason::NET_ERROR;
-      else if (value == kOfflinePageHeaderReasonValueFromDownload)
-        *reason = OfflinePageHeader::Reason::DOWNLOAD;
-      else if (value == kOfflinePageHeaderReasonValueReload)
-        *reason = OfflinePageHeader::Reason::RELOAD;
-      else
-        return false;
-    } else if (key == kOfflinePageHeaderIDKey) {
-      *id = value;
-    } else {
-      return false;
-    }
-  }
-
-  return token_found;
-}
-
-std::string ReasonToString(OfflinePageHeader::Reason reason) {
-  switch (reason) {
-    case OfflinePageHeader::Reason::NET_ERROR:
-      return kOfflinePageHeaderReasonValueDueToNetError;
-    case OfflinePageHeader::Reason::DOWNLOAD:
-      return kOfflinePageHeaderReasonValueFromDownload;
-    case OfflinePageHeader::Reason::RELOAD:
-      return kOfflinePageHeaderReasonValueReload;
-    default:
-      NOTREACHED();
-      return "";
-  }
-}
-
-}  // namespace
-
-OfflinePageHeader::OfflinePageHeader()
-    : did_fail_parsing_for_test(false),
-      need_to_persist(false),
-      reason(Reason::NONE) {
-}
-
-OfflinePageHeader::OfflinePageHeader(const std::string& header_value)
-    : did_fail_parsing_for_test(false),
-      need_to_persist(false),
-      reason(Reason::NONE) {
-  if (!ParseOfflineHeaderValue(header_value, &need_to_persist, &reason, &id)) {
-    did_fail_parsing_for_test = true;
-    Clear();
-  }
-}
-
-OfflinePageHeader::~OfflinePageHeader() {}
-
-std::string OfflinePageHeader::GetCompleteHeaderString() const {
-  if (reason == Reason::NONE)
-    return std::string();
-
-  std::string value(kOfflinePageHeader);
-  value += ": ";
-
-  value += kOfflinePageHeaderPersistKey;
-  value += "=";
-  value += need_to_persist ? "1" : "0";
-
-  value += " " ;
-  value += kOfflinePageHeaderReasonKey;
-  value += "=";
-  value += ReasonToString(reason);
-
-  if (!id.empty()) {
-    value += " " ;
-    value += kOfflinePageHeaderIDKey;
-    value += "=";
-    value += id;
-  }
-
-  return value;
-}
-
-void OfflinePageHeader::Clear() {
-  reason = Reason::NONE;
-  need_to_persist = false;
-  id.clear();
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/request_header/offline_page_header.h b/components/offline_pages/request_header/offline_page_header.h
deleted file mode 100644
index 370fe667..0000000
--- a/components/offline_pages/request_header/offline_page_header.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
-#define COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
-
-#include <string>
-
-namespace offline_pages {
-
-// Header that defines the custom behavior to load offline pages. Its value is a
-// comma/space separated name-value pair.
-extern const char kOfflinePageHeader[];
-
-// The name used in name-value pair of kOfflinePageHeader to tell if the offline
-// info in this header should be persisted across session restore.
-extern const char kOfflinePageHeaderPersistKey[];
-
-// The name used in name-value pair of kOfflinePageHeader to denote the reason
-// for loading offline page.
-extern const char kOfflinePageHeaderReasonKey[];
-// Possible values in name-value pair that denote the reason for loading offline
-// page.
-// The offline page should be loaded even when the network is connected. This is
-// because the live version failed to load due to certain net error.
-extern const char kOfflinePageHeaderReasonValueDueToNetError[];
-// The offline page should be loaded because the user clicks to open the
-// downloaded page explicitly.
-extern const char kOfflinePageHeaderReasonValueFromDownload[];
-// This only happens after the offline page is loaded due to above reason and
-// then the user reload current page. The network condition should be checked
-// this time to decide if a live version should be tried again.
-extern const char kOfflinePageHeaderReasonValueReload[];
-
-// The name used in name-value pair of kOfflinePageHeader to denote the offline
-// ID of the offline page to load.
-extern const char kOfflinePageHeaderIDKey[];
-
-// Used to parse the extra request header string that defines offline page
-// loading behaviors.
-struct OfflinePageHeader {
- public:
-  enum class Reason {
-    NONE,
-    NET_ERROR,
-    DOWNLOAD,
-    RELOAD
-  };
-
-  OfflinePageHeader();
-
-  // Constructed from a request header value string.
-  // The struct members will be cleared if the parsing failed.
-  explicit OfflinePageHeader(const std::string& header_value);
-
-  ~OfflinePageHeader();
-
-  // Returns the full header string, including both key and value, that could be
-  // passed to set extra request header.
-  std::string GetCompleteHeaderString() const;
-
-  void Clear();
-
-  // Set if failed to parse a request header value string. For testing only.
-  bool did_fail_parsing_for_test;
-
-  // Flag to indicate if the header should be persisted across session restore.
-  bool need_to_persist;
-
-  // Describes the reason to load offline page.
-  Reason reason;
-
-  // The offline ID of the page to load.
-  std::string id;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_REQUEST_HEADER_OFFLINE_PAGE_HEADER_H_
diff --git a/components/offline_pages/request_header/offline_page_header_unittest.cc b/components/offline_pages/request_header/offline_page_header_unittest.cc
deleted file mode 100644
index 744a66f6..0000000
--- a/components/offline_pages/request_header/offline_page_header_unittest.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/request_header/offline_page_header.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-class OfflinePageHeaderTest : public testing::Test {
- public:
-  OfflinePageHeaderTest() {}
-  ~OfflinePageHeaderTest() override {}
-
-  bool ParseFromHeaderValue(const std::string& header_value,
-                            bool* need_to_persist,
-                            OfflinePageHeader::Reason* reason,
-                            std::string* id) {
-    OfflinePageHeader header(header_value);
-    *need_to_persist = header.need_to_persist;
-    *reason = header.reason;
-    *id = header.id;
-    return !header.did_fail_parsing_for_test;
-  }
-};
-
-TEST_F(OfflinePageHeaderTest, Parse) {
-  bool need_to_persist;
-  OfflinePageHeader::Reason reason;
-  std::string id;
-
-  EXPECT_FALSE(ParseFromHeaderValue("", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(ParseFromHeaderValue("   ", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(ParseFromHeaderValue(" , ", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(ParseFromHeaderValue("reason", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(ParseFromHeaderValue("a b c", &need_to_persist, &reason, &id));
-
-  EXPECT_FALSE(
-      ParseFromHeaderValue("persist=aa", &need_to_persist, &reason, &id));
-
-  EXPECT_TRUE(
-      ParseFromHeaderValue("persist=1", &need_to_persist, &reason, &id));
-  EXPECT_TRUE(need_to_persist);
-
-  EXPECT_TRUE(
-      ParseFromHeaderValue("persist=0", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(need_to_persist);
-  EXPECT_EQ(OfflinePageHeader::Reason::NONE, reason);
-  EXPECT_EQ("", id);
-
-  EXPECT_FALSE(
-      ParseFromHeaderValue("reason=foo", &need_to_persist, &reason, &id));
-
-  EXPECT_TRUE(
-      ParseFromHeaderValue("reason=error", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(need_to_persist);
-  EXPECT_EQ(OfflinePageHeader::Reason::NET_ERROR, reason);
-  EXPECT_EQ("", id);
-
-  EXPECT_TRUE(ParseFromHeaderValue("id=a1b2", &need_to_persist, &reason, &id));
-  EXPECT_FALSE(need_to_persist);
-  EXPECT_EQ(OfflinePageHeader::Reason::NONE, reason);
-  EXPECT_EQ("a1b2", id);
-
-  EXPECT_TRUE(ParseFromHeaderValue("persist=1 reason=download id=a1b2",
-      &need_to_persist, &reason, &id));
-  EXPECT_TRUE(need_to_persist);
-  EXPECT_EQ(OfflinePageHeader::Reason::DOWNLOAD, reason);
-  EXPECT_EQ("a1b2", id);
-}
-
-TEST_F(OfflinePageHeaderTest, ToString) {
-  OfflinePageHeader header;
-  header.need_to_persist = true;
-  header.reason = OfflinePageHeader::Reason::DOWNLOAD;
-  header.id = "a1b2";
-  EXPECT_EQ("X-Chrome-offline: persist=1 reason=download id=a1b2",
-            header.GetCompleteHeaderString());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/snapshot_controller.cc b/components/offline_pages/snapshot_controller.cc
deleted file mode 100644
index 0ff98418..0000000
--- a/components/offline_pages/snapshot_controller.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/snapshot_controller.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/time/time.h"
-
-namespace {
-// Default delay, in milliseconds, between the main document parsed event and
-// snapshot. Note: this snapshot might not occur if the OnLoad event and
-// OnLoad delay elapses first to trigger a final snapshot.
-const size_t kDefaultDelayAfterDocumentAvailableMs = 7000;
-
-// Default delay, in milliseconds, between the main document OnLoad event and
-// snapshot.
-const size_t kDelayAfterDocumentOnLoadCompletedMs = 1000;
-
-}  // namespace
-
-namespace offline_pages {
-
-SnapshotController::SnapshotController(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    SnapshotController::Client* client)
-    : task_runner_(task_runner),
-      client_(client),
-      state_(State::READY),
-      delay_after_document_available_ms_(
-          kDefaultDelayAfterDocumentAvailableMs),
-      delay_after_document_on_load_completed_ms_(
-          kDelayAfterDocumentOnLoadCompletedMs),
-      weak_ptr_factory_(this) {}
-
-SnapshotController::SnapshotController(
-    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-    SnapshotController::Client* client,
-    size_t delay_after_document_available_ms,
-    size_t delay_after_document_on_load_completed_ms)
-    : task_runner_(task_runner),
-      client_(client),
-      state_(State::READY),
-      delay_after_document_available_ms_(
-          delay_after_document_available_ms),
-      delay_after_document_on_load_completed_ms_(
-          delay_after_document_on_load_completed_ms),
-      weak_ptr_factory_(this) {}
-
-SnapshotController::~SnapshotController() {}
-
-void SnapshotController::Reset() {
-  // Cancel potentially delayed tasks that relate to the previous 'session'.
-  weak_ptr_factory_.InvalidateWeakPtrs();
-  state_ = State::READY;
-}
-
-void SnapshotController::Stop() {
-  state_ = State::STOPPED;
-}
-
-void SnapshotController::PendingSnapshotCompleted() {
-  // Unless the controller is "stopped", enable the subsequent snapshots.
-  // Stopped state prevents any further snapshots form being started.
-  if (state_ == State::STOPPED)
-    return;
-  state_ = State::READY;
-}
-
-void SnapshotController::DocumentAvailableInMainFrame() {
-  // Post a delayed task to snapshot.
-  task_runner_->PostDelayedTask(
-      FROM_HERE, base::Bind(&SnapshotController::MaybeStartSnapshot,
-                            weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(
-          delay_after_document_available_ms_));
-}
-
-void SnapshotController::DocumentOnLoadCompletedInMainFrame() {
-  // Post a delayed task to snapshot and then stop this controller.
-  task_runner_->PostDelayedTask(
-      FROM_HERE, base::Bind(&SnapshotController::MaybeStartSnapshotThenStop,
-                            weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(
-          delay_after_document_on_load_completed_ms_));
-}
-
-void SnapshotController::MaybeStartSnapshot() {
-  if (state_ != State::READY)
-    return;
-  state_ = State::SNAPSHOT_PENDING;
-  client_->StartSnapshot();
-}
-
-void SnapshotController::MaybeStartSnapshotThenStop() {
-  MaybeStartSnapshot();
-  Stop();
-}
-
-size_t SnapshotController::GetDelayAfterDocumentAvailableForTest() {
-  return delay_after_document_available_ms_;
-}
-
-size_t SnapshotController::GetDelayAfterDocumentOnLoadCompletedForTest() {
-  return delay_after_document_on_load_completed_ms_;
-}
-
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/snapshot_controller.h b/components/offline_pages/snapshot_controller.h
deleted file mode 100644
index c3ec36e..0000000
--- a/components/offline_pages/snapshot_controller.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
-#define COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
-
-namespace offline_pages {
-
-// Takes various signals and produces StartSnapshot calls following a specific
-// policy. Can request snapshots multiple times per 'session'. Session can be
-// ended and another one started by calling Reset().
-// Main invariants:
-// - It never starts overlapping snapshots, Client reports when previous
-//   snapshot is done.
-// - The currently worked on (pending) snapshot is always allowed to complete,
-//   the new attempts to start a snapshot are ignored until it does.
-// - Some signals prevent more snapshots to be taken.
-//   OnLoad is currently such signal.
-// - Once Reset() is called on the SnapshotController, the delayed tasks are
-//   reset so no StartSnapshot calls is made 'cross-session'.
-class SnapshotController {
- public:
-  enum class State {
-    READY,             // Listening to input, will start snapshot when needed.
-    SNAPSHOT_PENDING,  // Snapshot is in progress, don't start another.
-    STOPPED,           // Terminal state, no snapshots until reset.
-  };
-
-  // Client of the SnapshotController.
-  class  Client {
-   public:
-    // Invoked at a good moment to start a snapshot. May be invoked multiple
-    // times, but not in overlapping manner - waits until
-    // PreviousSnapshotCompleted() before the next StatrSnapshot().
-    // Client should overwrite the result of previous snapshot with the new one,
-    // it is assumed that later snapshots are better then previous.
-    virtual void StartSnapshot() = 0;
-
-   protected:
-    virtual ~Client() {}
-  };
-
-  SnapshotController(
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      SnapshotController::Client* client);
-  SnapshotController(
-      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
-      SnapshotController::Client* client,
-      size_t delay_after_document_available_ms,
-      size_t delay_after_document_on_load_completed_ms);
-  virtual ~SnapshotController();
-
-  // Resets the 'session', returning controller to initial state.
-  void Reset();
-
-  // Stops current session, no more Client::StartSnapshot calls will be
-  // invoked from the SnapshotController until current session is Reset().
-  // Called by Client, for example when it encounters an error loading the page.
-  void Stop();
-
-  // The way for Client to report that previously started snapshot is
-  // now completed (so the next one can be started).
-  void PendingSnapshotCompleted();
-
-  // Invoked from WebContentObserver::DocumentAvailableInMainFrame
-  void DocumentAvailableInMainFrame();
-
-  // Invoked from WebContentObserver::DocumentOnLoadCompletedInMainFrame
-  void DocumentOnLoadCompletedInMainFrame();
-
-  size_t GetDelayAfterDocumentAvailableForTest();
-  size_t GetDelayAfterDocumentOnLoadCompletedForTest();
-
- private:
-  void MaybeStartSnapshot();
-  void MaybeStartSnapshotThenStop();
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  // Client owns this class.
-  SnapshotController::Client* client_;
-  SnapshotController::State state_;
-  size_t delay_after_document_available_ms_;
-  size_t delay_after_document_on_load_completed_ms_;
-
-  base::WeakPtrFactory<SnapshotController> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SnapshotController);
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_SNAPSHOT_CONTROLLER_H_
diff --git a/components/offline_pages/snapshot_controller_unittest.cc b/components/offline_pages/snapshot_controller_unittest.cc
deleted file mode 100644
index df561bc3..0000000
--- a/components/offline_pages/snapshot_controller_unittest.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/snapshot_controller.h"
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-class SnapshotControllerTest
-    : public testing::Test,
-      public SnapshotController::Client {
- public:
-  SnapshotControllerTest();
-  ~SnapshotControllerTest() override;
-
-  SnapshotController* controller() { return controller_.get(); }
-  int snapshot_count() { return snapshot_count_; }
-
-  // testing::Test
-  void SetUp() override;
-  void TearDown() override;
-
-  // SnapshotController::Client
-  void StartSnapshot() override;
-
-  // Utility methods.
-  // Runs until all of the tasks that are not delayed are gone from the task
-  // queue.
-  void PumpLoop();
-  // Fast-forwards virtual time by |delta|, causing tasks with a remaining
-  // delay less than or equal to |delta| to be executed.
-  void FastForwardBy(base::TimeDelta delta);
-
- private:
-  std::unique_ptr<SnapshotController> controller_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  bool snapshot_started_;
-  int snapshot_count_;
-};
-
-SnapshotControllerTest::SnapshotControllerTest()
-    : task_runner_(new base::TestMockTimeTaskRunner),
-      snapshot_started_(true),
-      snapshot_count_(0) {
-}
-
-SnapshotControllerTest::~SnapshotControllerTest() {
-}
-
-void SnapshotControllerTest::SetUp() {
-  controller_.reset(new SnapshotController(task_runner_, this));
-  snapshot_started_ = true;
-}
-
-void SnapshotControllerTest::TearDown() {
-  controller_.reset();
-}
-
-void SnapshotControllerTest::StartSnapshot() {
-  snapshot_count_++;
-}
-
-void SnapshotControllerTest::PumpLoop() {
-  task_runner_->RunUntilIdle();
-}
-
-void SnapshotControllerTest::FastForwardBy(base::TimeDelta delta) {
-  task_runner_->FastForwardBy(delta);
-}
-
-TEST_F(SnapshotControllerTest, OnLoad) {
-  // Onload should make snapshot after its delay.
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  PumpLoop();
-  EXPECT_EQ(0, snapshot_count());
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
-  EXPECT_EQ(1, snapshot_count());
-}
-
-TEST_F(SnapshotControllerTest, OnDocumentAvailable) {
-  EXPECT_GT(controller()->GetDelayAfterDocumentAvailableForTest(), 0UL);
-  // OnDOM should make snapshot after a delay.
-  controller()->DocumentAvailableInMainFrame();
-  PumpLoop();
-  EXPECT_EQ(0, snapshot_count());
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  EXPECT_EQ(1, snapshot_count());
-}
-
-TEST_F(SnapshotControllerTest, OnLoadSnapshotIsTheLastOne) {
-  // This test assumes DocumentAvailable delay is longer than OnLoadCompleted.
-  EXPECT_GT(controller()->GetDelayAfterDocumentAvailableForTest(),
-            controller()->GetDelayAfterDocumentOnLoadCompletedForTest());
-  // OnDOM should make snapshot after a delay.
-  controller()->DocumentAvailableInMainFrame();
-  PumpLoop();
-  EXPECT_EQ(0, snapshot_count());
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  // Advance time to OnLoadCompleted delay to trigger snapshot.
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
-  EXPECT_EQ(1, snapshot_count());
-  // Report that snapshot is completed.
-  controller()->PendingSnapshotCompleted();
-  // Even though previous snapshot is completed, new one should not start
-  // when this DocumentAvailable delay expires.
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  EXPECT_EQ(1, snapshot_count());
-}
-
-TEST_F(SnapshotControllerTest, OnLoadSnapshotAfterLongDelay) {
-  // OnDOM should make snapshot after a delay.
-  controller()->DocumentAvailableInMainFrame();
-  PumpLoop();
-  EXPECT_EQ(0, snapshot_count());
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  EXPECT_EQ(1, snapshot_count());
-  // Report that snapshot is completed.
-  controller()->PendingSnapshotCompleted();
-  // OnLoad should make 2nd snapshot after its delay.
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
-  EXPECT_EQ(2, snapshot_count());
-}
-
-TEST_F(SnapshotControllerTest, Stop) {
-  // OnDOM should make snapshot after a delay.
-  controller()->DocumentAvailableInMainFrame();
-  PumpLoop();
-  EXPECT_EQ(0, snapshot_count());
-  controller()->Stop();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  // Should not start snapshots
-  EXPECT_EQ(0, snapshot_count());
-  // Also should not start snapshot.
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  EXPECT_EQ(0, snapshot_count());
-}
-
-TEST_F(SnapshotControllerTest, ClientReset) {
-  controller()->DocumentAvailableInMainFrame();
-
-  controller()->Reset();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  // No snapshot since session was reset.
-  EXPECT_EQ(0, snapshot_count());
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
-  EXPECT_EQ(1, snapshot_count());
-
-  controller()->Reset();
-  controller()->DocumentAvailableInMainFrame();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  // No snapshot since session was reset.
-  EXPECT_EQ(2, snapshot_count());
-}
-
-// This simulated a Reset while there is ongoing snapshot, which is reported
-// as done later. That reporting should have no effect nor crash.
-TEST_F(SnapshotControllerTest, ClientResetWhileSnapshotting) {
-  controller()->DocumentOnLoadCompletedInMainFrame();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentOnLoadCompletedForTest()));
-  EXPECT_EQ(1, snapshot_count());
-  // This normally happens when navigation starts.
-  controller()->Reset();
-  controller()->PendingSnapshotCompleted();
-  // Next snapshot should be initiated when new document is loaded.
-  controller()->DocumentAvailableInMainFrame();
-  FastForwardBy(base::TimeDelta::FromMilliseconds(
-      controller()->GetDelayAfterDocumentAvailableForTest()));
-  // No snapshot since session was reset.
-  EXPECT_EQ(2, snapshot_count());
-}
-
-}  // namespace offline_pages
diff --git a/components/offline_pages/stub_offline_page_model.cc b/components/offline_pages/stub_offline_page_model.cc
deleted file mode 100644
index f45189e..0000000
--- a/components/offline_pages/stub_offline_page_model.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/offline_pages/stub_offline_page_model.h"
-
-namespace offline_pages {
-
-StubOfflinePageModel::StubOfflinePageModel() {}
-StubOfflinePageModel::~StubOfflinePageModel() {}
-
-void StubOfflinePageModel::AddObserver(Observer* observer) {}
-void StubOfflinePageModel::RemoveObserver(Observer* observer) {}
-void StubOfflinePageModel::SavePage(
-    const SavePageParams& save_page_params,
-    std::unique_ptr<OfflinePageArchiver> archiver,
-    const SavePageCallback& callback) {}
-void StubOfflinePageModel::MarkPageAccessed(int64_t offline_id) {}
-void StubOfflinePageModel::DeletePagesByOfflineId(
-    const std::vector<int64_t>& offline_ids,
-    const DeletePageCallback& callback) {}
-void StubOfflinePageModel::DeletePagesByClientIds(
-    const std::vector<ClientId>& client_ids,
-    const DeletePageCallback& callback) {}
-void StubOfflinePageModel::GetPagesMatchingQuery(
-    std::unique_ptr<OfflinePageModelQuery> query,
-    const MultipleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::GetPagesByClientIds(
-    const std::vector<ClientId>& client_ids,
-    const MultipleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::DeleteCachedPagesByURLPredicate(
-    const UrlPredicate& predicate,
-    const DeletePageCallback& callback) {}
-void StubOfflinePageModel::CheckPagesExistOffline(
-    const std::set<GURL>& urls,
-    const CheckPagesExistOfflineCallback& callback) {}
-void StubOfflinePageModel::GetAllPages(
-    const MultipleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::GetOfflineIdsForClientId(
-    const ClientId& client_id,
-    const MultipleOfflineIdCallback& callback) {}
-void StubOfflinePageModel::GetPageByOfflineId(
-    int64_t offline_id,
-    const SingleOfflinePageItemCallback& callback) {}
-void StubOfflinePageModel::GetPagesByURL(
-    const GURL& url,
-    URLSearchMode url_search_mode,
-    const MultipleOfflinePageItemCallback& callback) {}
-ClientPolicyController* StubOfflinePageModel::GetPolicyController() {
-  return &policy_controller_;
-}
-bool StubOfflinePageModel::is_loaded() const {
-  return true;
-}
-OfflineEventLogger* StubOfflinePageModel::GetLogger() {
-  return nullptr;
-}
-}  // namespace offline_pages
diff --git a/components/offline_pages/stub_offline_page_model.h b/components/offline_pages/stub_offline_page_model.h
deleted file mode 100644
index c40e81d..0000000
--- a/components/offline_pages/stub_offline_page_model.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
-#define COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/offline_pages/client_policy_controller.h"
-#include "components/offline_pages/offline_page_model.h"
-
-namespace offline_pages {
-
-// Stub implementation of OfflinePageModel interface for testing. Besides using
-// as a stub for tests, it may also be subclassed to mock specific methods
-// needed for a set of tests.
-class StubOfflinePageModel : public OfflinePageModel, public KeyedService {
- public:
-  StubOfflinePageModel();
-  ~StubOfflinePageModel() override;
-
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
-  void SavePage(const SavePageParams& save_page_params,
-                std::unique_ptr<OfflinePageArchiver> archiver,
-                const SavePageCallback& callback) override;
-  void MarkPageAccessed(int64_t offline_id) override;
-  void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
-                              const DeletePageCallback& callback) override;
-  void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
-                              const DeletePageCallback& callback) override;
-  void GetPagesMatchingQuery(
-      std::unique_ptr<OfflinePageModelQuery> query,
-      const MultipleOfflinePageItemCallback& callback) override;
-  void GetPagesByClientIds(
-      const std::vector<ClientId>& client_ids,
-      const MultipleOfflinePageItemCallback& callback) override;
-  void DeleteCachedPagesByURLPredicate(
-      const UrlPredicate& predicate,
-      const DeletePageCallback& callback) override;
-  void CheckPagesExistOffline(
-      const std::set<GURL>& urls,
-      const CheckPagesExistOfflineCallback& callback) override;
-  void GetAllPages(const MultipleOfflinePageItemCallback& callback) override;
-  void GetOfflineIdsForClientId(
-      const ClientId& client_id,
-      const MultipleOfflineIdCallback& callback) override;
-  void GetPageByOfflineId(
-      int64_t offline_id,
-      const SingleOfflinePageItemCallback& callback) override;
-  void GetPagesByURL(
-      const GURL& url,
-      URLSearchMode url_search_mode,
-      const MultipleOfflinePageItemCallback& callback) override;
-  ClientPolicyController* GetPolicyController() override;
-  bool is_loaded() const override;
-  OfflineEventLogger* GetLogger() override;
-
- private:
-  ClientPolicyController policy_controller_;
-  std::vector<int64_t> offline_ids_;
-};
-
-}  // namespace offline_pages
-
-#endif  // COMPONENTS_OFFLINE_PAGES_STUB_OFFLINE_PAGE_MODEL_H_
diff --git a/components/password_manager/core/browser/fake_form_fetcher.cc b/components/password_manager/core/browser/fake_form_fetcher.cc
index a25c93b..3f0ca14 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.cc
+++ b/components/password_manager/core/browser/fake_form_fetcher.cc
@@ -23,8 +23,8 @@
   return state_;
 }
 
-const std::vector<const InteractionsStats*>&
-FakeFormFetcher::GetInteractionsStats() const {
+const std::vector<InteractionsStats>& FakeFormFetcher::GetInteractionsStats()
+    const {
   return stats_;
 }
 
diff --git a/components/password_manager/core/browser/fake_form_fetcher.h b/components/password_manager/core/browser/fake_form_fetcher.h
index e8d6f13..93fa7e3 100644
--- a/components/password_manager/core/browser/fake_form_fetcher.h
+++ b/components/password_manager/core/browser/fake_form_fetcher.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 
 namespace autofill {
 struct PasswordForm;
@@ -37,10 +38,9 @@
   State GetState() const override;
 
   // Statistics for recent password bubble usage.
-  const std::vector<const InteractionsStats*>& GetInteractionsStats()
-      const override;
+  const std::vector<InteractionsStats>& GetInteractionsStats() const override;
 
-  void set_stats(const std::vector<const InteractionsStats*>& stats) {
+  void set_stats(const std::vector<InteractionsStats>& stats) {
     state_ = State::NOT_WAITING;
     stats_ = stats;
   }
@@ -64,7 +64,7 @@
  private:
   std::set<Consumer*> consumers_;
   State state_ = State::NOT_WAITING;
-  std::vector<const InteractionsStats*> stats_;
+  std::vector<InteractionsStats> stats_;
   std::vector<const autofill::PasswordForm*> federated_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeFormFetcher);
diff --git a/components/password_manager/core/browser/form_fetcher.h b/components/password_manager/core/browser/form_fetcher.h
index 145a565..e2e1642 100644
--- a/components/password_manager/core/browser/form_fetcher.h
+++ b/components/password_manager/core/browser/form_fetcher.h
@@ -60,7 +60,7 @@
   virtual State GetState() const = 0;
 
   // Statistics for recent password bubble usage.
-  virtual const std::vector<const InteractionsStats*>& GetInteractionsStats()
+  virtual const std::vector<InteractionsStats>& GetInteractionsStats()
       const = 0;
 
   // Federated matches obtained from the backend. Valid only if GetState()
diff --git a/components/password_manager/core/browser/form_fetcher_impl.cc b/components/password_manager/core/browser/form_fetcher_impl.cc
index f40d40b8..493e44e 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -75,9 +75,9 @@
   return state_;
 }
 
-const std::vector<const InteractionsStats*>&
-FormFetcherImpl::GetInteractionsStats() const {
-  return weak_interactions_stats_;
+const std::vector<InteractionsStats>& FormFetcherImpl::GetInteractionsStats()
+    const {
+  return interactions_stats_;
 }
 
 const std::vector<const autofill::PasswordForm*>&
@@ -123,11 +123,10 @@
 }
 
 void FormFetcherImpl::OnGetSiteStatistics(
-    std::vector<std::unique_ptr<InteractionsStats>> stats) {
+    std::vector<InteractionsStats> stats) {
   // On Windows the password request may be resolved after the statistics due to
   // importing from IE.
   interactions_stats_ = std::move(stats);
-  weak_interactions_stats_ = MakeWeakCopies(interactions_stats_);
 }
 
 void FormFetcherImpl::Fetch() {
diff --git a/components/password_manager/core/browser/form_fetcher_impl.h b/components/password_manager/core/browser/form_fetcher_impl.h
index 096bd618..b786f945 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.h
+++ b/components/password_manager/core/browser/form_fetcher_impl.h
@@ -32,8 +32,7 @@
   // FormFetcher:
   void AddConsumer(Consumer* consumer) override;
   State GetState() const override;
-  const std::vector<const InteractionsStats*>& GetInteractionsStats()
-      const override;
+  const std::vector<InteractionsStats>& GetInteractionsStats() const override;
   const std::vector<const autofill::PasswordForm*>& GetFederatedMatches()
       const override;
   void Fetch() override;
@@ -41,8 +40,7 @@
   // PasswordStoreConsumer:
   void OnGetPasswordStoreResults(
       std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
-  void OnGetSiteStatistics(
-      std::vector<std::unique_ptr<InteractionsStats>> stats) override;
+  void OnGetSiteStatistics(std::vector<InteractionsStats> stats) override;
 
  private:
   // PasswordStore results will be fetched for this description.
@@ -57,12 +55,11 @@
   std::vector<std::unique_ptr<autofill::PasswordForm>> federated_;
 
   // Statistics for the current domain.
-  std::vector<std::unique_ptr<InteractionsStats>> interactions_stats_;
+  std::vector<InteractionsStats> interactions_stats_;
 
   // Non-owning copies of the vectors above.
   std::vector<const autofill::PasswordForm*> weak_non_federated_;
   std::vector<const autofill::PasswordForm*> weak_federated_;
-  std::vector<const InteractionsStats*> weak_interactions_stats_;
 
   // Consumers of the fetcher, all are assumed to outlive |this|.
   std::set<Consumer*> consumers_;
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 301bc7e..269abc9 100644
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -151,8 +151,8 @@
   // A wrapper around form_fetcher_.Fetch(), adding the call expectations.
   void Fetch() {
 #if !defined(OS_IOS) && !defined(OS_ANDROID)
-    EXPECT_CALL(*mock_store_, GetSiteStatsMock(_))
-        .WillOnce(Return(std::vector<InteractionsStats*>()));
+    EXPECT_CALL(*mock_store_, GetSiteStatsImpl(_))
+        .WillOnce(Return(std::vector<InteractionsStats>()));
 #endif
     EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
     form_fetcher_->Fetch();
@@ -291,8 +291,7 @@
 TEST_F(FormFetcherImplTest, Stats) {
   Fetch();
   form_fetcher_->AddConsumer(&consumer_);
-  std::vector<std::unique_ptr<InteractionsStats>> stats;
-  stats.push_back(base::MakeUnique<InteractionsStats>());
+  std::vector<InteractionsStats> stats(1);
   form_fetcher_->OnGetSiteStatistics(std::move(stats));
   EXPECT_EQ(1u, form_fetcher_->GetInteractionsStats().size());
 }
@@ -344,21 +343,20 @@
   stats.origin_domain = form_digest_.origin.GetOrigin();
   stats.username_value = ASCIIToUTF16("some username");
   stats.dismissal_count = 5;
-  std::vector<InteractionsStats*> db_stats;
-  db_stats.push_back(new InteractionsStats(stats));
+  std::vector<InteractionsStats> db_stats = {stats};
   EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
-  EXPECT_CALL(*mock_store_, GetSiteStatsMock(stats.origin_domain))
+  EXPECT_CALL(*mock_store_, GetSiteStatsImpl(stats.origin_domain))
       .WillOnce(Return(db_stats));
   form_fetcher_->Fetch();
   base::RunLoop().RunUntilIdle();
 
   EXPECT_THAT(form_fetcher_->GetInteractionsStats(),
-              UnorderedElementsAre(Pointee(stats)));
+              UnorderedElementsAre(stats));
 }
 #else
 TEST_F(FormFetcherImplTest, DontFetchStatistics) {
   EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get()));
-  EXPECT_CALL(*mock_store_, GetSiteStatsMock(_)).Times(0);
+  EXPECT_CALL(*mock_store_, GetSiteStatsImpl(_)).Times(0);
   form_fetcher_->Fetch();
   base::RunLoop().RunUntilIdle();
 }
diff --git a/components/password_manager/core/browser/mock_password_store.cc b/components/password_manager/core/browser/mock_password_store.cc
index ef0c961..b1c2ea9 100644
--- a/components/password_manager/core/browser/mock_password_store.cc
+++ b/components/password_manager/core/browser/mock_password_store.cc
@@ -16,15 +16,4 @@
 MockPasswordStore::~MockPasswordStore() {
 }
 
-std::vector<std::unique_ptr<InteractionsStats>>
-MockPasswordStore::GetSiteStatsImpl(const GURL& origin_domain) {
-  std::vector<InteractionsStats*> stats = GetSiteStatsMock(origin_domain);
-  std::vector<std::unique_ptr<InteractionsStats>> result;
-  result.reserve(stats.size());
-  for (auto* stat : stats) {
-    result.push_back(base::WrapUnique(stat));
-  }
-  return result;
-}
-
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index 9975a2c..0e6c9d1 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -59,10 +59,8 @@
   MOCK_METHOD1(FillBlacklistLogins,
                bool(std::vector<std::unique_ptr<autofill::PasswordForm>>*));
   MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&));
-  // GMock doesn't allow to return noncopyable types.
-  std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
-      const GURL& origin_domain) override;
-  MOCK_METHOD1(GetSiteStatsMock, std::vector<InteractionsStats*>(const GURL&));
+  MOCK_METHOD1(GetSiteStatsImpl,
+               std::vector<InteractionsStats>(const GURL& origin_domain));
   MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
   MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
 
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index ba29bfe0..c698f12 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -122,7 +122,7 @@
 
     EXPECT_CALL(client_, GetPasswordStore())
         .WillRepeatedly(Return(store_.get()));
-    EXPECT_CALL(*store_, GetSiteStatsMock(_)).Times(AnyNumber());
+    EXPECT_CALL(*store_, GetSiteStatsImpl(_)).Times(AnyNumber());
     EXPECT_CALL(client_, GetDriver()).WillRepeatedly(Return(&driver_));
 
     manager_.reset(new PasswordManager(&client_));
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 32a1597..b5a8a136 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -53,7 +53,7 @@
 }
 
 void PasswordStore::GetLoginsRequest::NotifyWithSiteStatistics(
-    std::vector<std::unique_ptr<InteractionsStats>> stats) {
+    std::vector<InteractionsStats> stats) {
   origin_task_runner_->PostTask(
       FROM_HERE, base::Bind(&PasswordStoreConsumer::OnGetSiteStatistics,
                             consumer_weak_, base::Passed(&stats)));
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index fe0ec7a..28a7046 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -240,8 +240,7 @@
     void NotifyConsumerWithResults(
         std::vector<std::unique_ptr<autofill::PasswordForm>> results);
 
-    void NotifyWithSiteStatistics(
-        std::vector<std::unique_ptr<InteractionsStats>> stats);
+    void NotifyWithSiteStatistics(std::vector<InteractionsStats> stats);
 
     void set_ignore_logins_cutoff(base::Time cutoff) {
       ignore_logins_cutoff_ = cutoff;
@@ -326,7 +325,7 @@
   // Synchronous implementation for manipulating with statistics.
   virtual void AddSiteStatsImpl(const InteractionsStats& stats) = 0;
   virtual void RemoveSiteStatsImpl(const GURL& origin_domain) = 0;
-  virtual std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+  virtual std::vector<InteractionsStats> GetSiteStatsImpl(
       const GURL& origin_domain) = 0;
 
   // Log UMA stats for number of bulk deletions.
diff --git a/components/password_manager/core/browser/password_store_consumer.cc b/components/password_manager/core/browser/password_store_consumer.cc
index eef0bf9..15451215 100644
--- a/components/password_manager/core/browser/password_store_consumer.cc
+++ b/components/password_manager/core/browser/password_store_consumer.cc
@@ -15,6 +15,6 @@
 }
 
 void PasswordStoreConsumer::OnGetSiteStatistics(
-    std::vector<std::unique_ptr<InteractionsStats>> stats) {}
+    std::vector<InteractionsStats> stats) {}
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store_consumer.h b/components/password_manager/core/browser/password_store_consumer.h
index f137225b..89d7975 100644
--- a/components/password_manager/core/browser/password_store_consumer.h
+++ b/components/password_manager/core/browser/password_store_consumer.h
@@ -34,8 +34,7 @@
 
   // Called when the GetLogins() request is finished, with the associated site
   // statistics.
-  virtual void OnGetSiteStatistics(
-      std::vector<std::unique_ptr<InteractionsStats>> stats);
+  virtual void OnGetSiteStatistics(std::vector<InteractionsStats> stats);
 
   // The base::CancelableTaskTracker can be used for cancelling the
   // tasks associated with the consumer.
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index 12b98d5..5588faaf 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -205,11 +205,11 @@
     login_db_->stats_table().RemoveRow(origin_domain);
 }
 
-std::vector<std::unique_ptr<InteractionsStats>>
-PasswordStoreDefault::GetSiteStatsImpl(const GURL& origin_domain) {
+std::vector<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl(
+    const GURL& origin_domain) {
   DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
   return login_db_ ? login_db_->stats_table().GetRows(origin_domain)
-                   : std::vector<std::unique_ptr<InteractionsStats>>();
+                   : std::vector<InteractionsStats>();
 }
 
 void PasswordStoreDefault::ResetLoginDB() {
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 9381067b..f7e98b1 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -72,7 +72,7 @@
       std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
   void AddSiteStatsImpl(const InteractionsStats& stats) override;
   void RemoveSiteStatsImpl(const GURL& origin_domain) override;
-  std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+  std::vector<InteractionsStats> GetSiteStatsImpl(
       const GURL& origin_domain) override;
 
   inline bool DeleteAndRecreateDatabaseFile() {
diff --git a/components/password_manager/core/browser/statistics_table.cc b/components/password_manager/core/browser/statistics_table.cc
index 0b6cc2c..e03ba5b 100644
--- a/components/password_manager/core/browser/statistics_table.cc
+++ b/components/password_manager/core/browser/statistics_table.cc
@@ -37,13 +37,13 @@
 }
 
 const InteractionsStats* FindStatsByUsername(
-    const std::vector<const InteractionsStats*>& stats,
+    const std::vector<InteractionsStats>& stats,
     const base::string16& username) {
   auto it = std::find_if(stats.begin(), stats.end(),
-                         [&username](const InteractionsStats* element) {
-                           return username == element->username_value;
+                         [&username](const InteractionsStats& element) {
+                           return username == element.username_value;
                          });
-  return it == stats.end() ? nullptr : *it;
+  return it == stats.end() ? nullptr : &*it;
 }
 
 StatisticsTable::StatisticsTable() : db_(nullptr) {
@@ -106,22 +106,21 @@
   return s.Run();
 }
 
-std::vector<std::unique_ptr<InteractionsStats>> StatisticsTable::GetRows(
-    const GURL& domain) {
+std::vector<InteractionsStats> StatisticsTable::GetRows(const GURL& domain) {
   if (!domain.is_valid())
-    return std::vector<std::unique_ptr<InteractionsStats>>();
+    return std::vector<InteractionsStats>();
   const char query[] =
       "SELECT origin_domain, username_value, "
       "dismissal_count, update_time FROM stats WHERE origin_domain == ?";
   sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE, query));
   s.BindString(0, domain.spec());
-  std::vector<std::unique_ptr<InteractionsStats>> result;
+  std::vector<InteractionsStats> result;
   while (s.Step()) {
-    result.push_back(base::WrapUnique(new InteractionsStats));
-    result.back()->origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN));
-    result.back()->username_value = s.ColumnString16(COLUMN_USERNAME);
-    result.back()->dismissal_count = s.ColumnInt(COLUMN_DISMISSALS);
-    result.back()->update_time =
+    result.push_back(InteractionsStats());
+    result.back().origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN));
+    result.back().username_value = s.ColumnString16(COLUMN_USERNAME);
+    result.back().dismissal_count = s.ColumnInt(COLUMN_DISMISSALS);
+    result.back().update_time =
         base::Time::FromInternalValue(s.ColumnInt64(COLUMN_DATE));
   }
   return result;
diff --git a/components/password_manager/core/browser/statistics_table.h b/components/password_manager/core/browser/statistics_table.h
index eefa3b45..57d7070 100644
--- a/components/password_manager/core/browser/statistics_table.h
+++ b/components/password_manager/core/browser/statistics_table.h
@@ -41,7 +41,7 @@
 
 // Returns an element from |stats| with |username| or nullptr if not found.
 const InteractionsStats* FindStatsByUsername(
-    const std::vector<const InteractionsStats*>& stats,
+    const std::vector<InteractionsStats>& stats,
     const base::string16& username);
 
 // Represents the 'stats' table in the Login Database.
@@ -70,7 +70,7 @@
   bool RemoveRow(const GURL& domain);
 
   // Returns the statistics for |domain| if it exists.
-  std::vector<std::unique_ptr<InteractionsStats>> GetRows(const GURL& domain);
+  std::vector<InteractionsStats> GetRows(const GURL& domain);
 
   // Removes the statistics between the dates. If |origin_filter| is not null,
   // only statistics for matching origins are removed. Returns true if the SQL
diff --git a/components/password_manager/core/browser/statistics_table_unittest.cc b/components/password_manager/core/browser/statistics_table_unittest.cc
index d51aa286..e84e78b 100644
--- a/components/password_manager/core/browser/statistics_table_unittest.cc
+++ b/components/password_manager/core/browser/statistics_table_unittest.cc
@@ -62,7 +62,7 @@
 TEST_F(StatisticsTableTest, Sanity) {
   EXPECT_TRUE(db()->AddRow(test_data()));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(test_data())));
+              ElementsAre(test_data()));
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
 }
@@ -73,7 +73,7 @@
   ReloadDatabase();
 
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(test_data())));
+              ElementsAre(test_data()));
 }
 
 TEST_F(StatisticsTableTest, DoubleOperation) {
@@ -82,7 +82,7 @@
   EXPECT_TRUE(db()->AddRow(test_data()));
 
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(test_data())));
+              ElementsAre(test_data()));
 
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
@@ -97,7 +97,7 @@
   EXPECT_TRUE(db()->AddRow(stats1));
   EXPECT_TRUE(db()->AddRow(stats2));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              UnorderedElementsAre(Pointee(stats1), Pointee(stats2)));
+              UnorderedElementsAre(stats1, stats2));
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
 }
@@ -119,26 +119,19 @@
   EXPECT_TRUE(db()->AddRow(stats2));
   EXPECT_TRUE(db()->AddRow(stats3));
   EXPECT_TRUE(db()->AddRow(stats4));
-  EXPECT_THAT(db()->GetRows(stats1.origin_domain),
-              ElementsAre(Pointee(stats1)));
-  EXPECT_THAT(db()->GetRows(stats2.origin_domain),
-              ElementsAre(Pointee(stats2)));
-  EXPECT_THAT(db()->GetRows(stats3.origin_domain),
-              ElementsAre(Pointee(stats3)));
-  EXPECT_THAT(db()->GetRows(stats4.origin_domain),
-              ElementsAre(Pointee(stats4)));
+  EXPECT_THAT(db()->GetRows(stats1.origin_domain), ElementsAre(stats1));
+  EXPECT_THAT(db()->GetRows(stats2.origin_domain), ElementsAre(stats2));
+  EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
+  EXPECT_THAT(db()->GetRows(stats4.origin_domain), ElementsAre(stats4));
 
   // Remove the entry with the timestamp 1 with no origin filter.
   EXPECT_TRUE(
       db()->RemoveStatsByOriginAndTime(base::Callback<bool(const GURL&)>(),
                                        base::Time(), base::Time::FromTimeT(2)));
   EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty());
-  EXPECT_THAT(db()->GetRows(stats2.origin_domain),
-              ElementsAre(Pointee(stats2)));
-  EXPECT_THAT(db()->GetRows(stats3.origin_domain),
-              ElementsAre(Pointee(stats3)));
-  EXPECT_THAT(db()->GetRows(stats4.origin_domain),
-              ElementsAre(Pointee(stats4)));
+  EXPECT_THAT(db()->GetRows(stats2.origin_domain), ElementsAre(stats2));
+  EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
+  EXPECT_THAT(db()->GetRows(stats4.origin_domain), ElementsAre(stats4));
 
   // Remove the entries with the timestamp 2 that are NOT matching
   // |kTestDomain3|.
@@ -148,8 +141,7 @@
       base::Time::FromTimeT(2), base::Time()));
   EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty());
   EXPECT_THAT(db()->GetRows(stats2.origin_domain), IsEmpty());
-  EXPECT_THAT(db()->GetRows(stats3.origin_domain),
-              ElementsAre(Pointee(stats3)));
+  EXPECT_THAT(db()->GetRows(stats3.origin_domain), ElementsAre(stats3));
   EXPECT_THAT(db()->GetRows(stats4.origin_domain), IsEmpty());
 
   // Remove the entries with the timestamp 2 with no origin filter.
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index fc70489e..25589e2 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -166,9 +166,9 @@
 void TestPasswordStore::RemoveSiteStatsImpl(const GURL& origin_domain) {
 }
 
-std::vector<std::unique_ptr<InteractionsStats>>
-TestPasswordStore::GetSiteStatsImpl(const GURL& origin_domain) {
-  return std::vector<std::unique_ptr<InteractionsStats>>();
+std::vector<InteractionsStats> TestPasswordStore::GetSiteStatsImpl(
+    const GURL& origin_domain) {
+  return std::vector<InteractionsStats>();
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 891d88c..67bce279 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -72,7 +72,7 @@
       std::vector<std::unique_ptr<autofill::PasswordForm>>* forms) override;
   void AddSiteStatsImpl(const InteractionsStats& stats) override;
   void RemoveSiteStatsImpl(const GURL& origin_domain) override;
-  std::vector<std::unique_ptr<InteractionsStats>> GetSiteStatsImpl(
+  std::vector<InteractionsStats> GetSiteStatsImpl(
       const GURL& origin_domain) override;
 
  private:
diff --git a/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc b/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc
index 56f91cd..b027d80ac 100644
--- a/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc
+++ b/components/policy/core/browser/android/android_combined_policy_provider_unittest.cc
@@ -77,7 +77,7 @@
   const PolicyMap& map = bundle.Get(ns);
   const base::Value* value = map.GetValue("TestPolicy");
   ASSERT_NE(nullptr, value);
-  EXPECT_EQ(base::Value::TYPE_STRING, value->GetType());
+  EXPECT_EQ(base::Value::Type::STRING, value->GetType());
   std::string out_value;
   EXPECT_TRUE(value->GetAsString(&out_value));
   EXPECT_EQ("TestValue", out_value);
diff --git a/components/policy/core/browser/android/policy_converter.cc b/components/policy/core/browser/android/policy_converter.cc
index 35911c7..afdac494b 100644
--- a/components/policy/core/browser/android/policy_converter.cc
+++ b/components/policy/core/browser/android/policy_converter.cc
@@ -114,10 +114,10 @@
     return value;
 
   switch (schema.type()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return base::Value::CreateNullValue();
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       std::string string_value;
       if (value->GetAsString(&string_value)) {
         if (string_value.compare("true") == 0)
@@ -135,7 +135,7 @@
       return value;
     }
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       std::string string_value;
       if (value->GetAsString(&string_value)) {
         int int_value = 0;
@@ -145,7 +145,7 @@
       return value;
     }
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       std::string string_value;
       if (value->GetAsString(&string_value)) {
         double double_value = 0;
@@ -156,19 +156,19 @@
     }
 
     // String can't be converted from other types.
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       return value;
     }
 
     // Binary is not a valid schema type.
-    case base::Value::TYPE_BINARY: {
+    case base::Value::Type::BINARY: {
       NOTREACHED();
       return std::unique_ptr<base::Value>();
     }
 
     // Complex types have to be deserialized from JSON.
-    case base::Value::TYPE_DICTIONARY:
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::DICTIONARY:
+    case base::Value::Type::LIST: {
       std::string string_value;
       if (value->GetAsString(&string_value)) {
         std::unique_ptr<base::Value> decoded_value =
diff --git a/components/policy/core/browser/autofill_policy_handler.cc b/components/policy/core/browser/autofill_policy_handler.cc
index 89d4e91..eb19817 100644
--- a/components/policy/core/browser/autofill_policy_handler.cc
+++ b/components/policy/core/browser/autofill_policy_handler.cc
@@ -14,7 +14,7 @@
 
 AutofillPolicyHandler::AutofillPolicyHandler()
     : TypeCheckingPolicyHandler(key::kAutoFillEnabled,
-                                base::Value::TYPE_BOOLEAN) {}
+                                base::Value::Type::BOOLEAN) {}
 
 AutofillPolicyHandler::~AutofillPolicyHandler() {
 }
diff --git a/components/policy/core/browser/configuration_policy_handler.cc b/components/policy/core/browser/configuration_policy_handler.cc
index 74a6689..10edb53 100644
--- a/components/policy/core/browser/configuration_policy_handler.cc
+++ b/components/policy/core/browser/configuration_policy_handler.cc
@@ -85,7 +85,7 @@
     int min,
     int max,
     bool clamp)
-    : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_INTEGER),
+    : TypeCheckingPolicyHandler(policy_name, base::Value::Type::INTEGER),
       min_(min),
       max_(max),
       clamp_(clamp) {
@@ -145,7 +145,7 @@
     const char* policy_name,
     const char* pref_path,
     const GenerateMapCallback& callback)
-    : TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
+    : TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
       pref_path_(pref_path),
       map_getter_(callback) {}
 
@@ -188,7 +188,7 @@
       if (errors) {
         errors->AddError(policy_name(), entry - list_value->begin(),
                          IDS_POLICY_TYPE_ERROR,
-                         base::Value::GetTypeName(base::Value::TYPE_STRING));
+                         base::Value::GetTypeName(base::Value::Type::STRING));
       }
       continue;
     }
diff --git a/components/policy/core/browser/configuration_policy_pref_store_unittest.cc b/components/policy/core/browser/configuration_policy_pref_store_unittest.cc
index 8fae2550..7fde2991 100644
--- a/components/policy/core/browser/configuration_policy_pref_store_unittest.cc
+++ b/components/policy/core/browser/configuration_policy_pref_store_unittest.cc
@@ -47,7 +47,7 @@
   void SetUp() override {
     handler_list_.AddHandler(
         base::WrapUnique<ConfigurationPolicyHandler>(new SimplePolicyHandler(
-            kTestPolicy, kTestPref, base::Value::TYPE_LIST)));
+            kTestPolicy, kTestPref, base::Value::Type::LIST)));
   }
 };
 
@@ -75,7 +75,7 @@
   void SetUp() override {
     handler_list_.AddHandler(
         base::WrapUnique<ConfigurationPolicyHandler>(new SimplePolicyHandler(
-            kTestPolicy, kTestPref, base::Value::TYPE_STRING)));
+            kTestPolicy, kTestPref, base::Value::Type::STRING)));
   }
 };
 
@@ -102,7 +102,7 @@
   void SetUp() override {
     handler_list_.AddHandler(
         base::WrapUnique<ConfigurationPolicyHandler>(new SimplePolicyHandler(
-            kTestPolicy, kTestPref, base::Value::TYPE_BOOLEAN)));
+            kTestPolicy, kTestPref, base::Value::Type::BOOLEAN)));
   }
 };
 
@@ -142,7 +142,7 @@
   void SetUp() override {
     handler_list_.AddHandler(
         base::WrapUnique<ConfigurationPolicyHandler>(new SimplePolicyHandler(
-            kTestPolicy, kTestPref, base::Value::TYPE_INTEGER)));
+            kTestPolicy, kTestPref, base::Value::Type::INTEGER)));
   }
 };
 
@@ -170,7 +170,7 @@
     store_->AddObserver(&observer_);
     handler_list_.AddHandler(
         base::WrapUnique<ConfigurationPolicyHandler>(new SimplePolicyHandler(
-            kTestPolicy, kTestPref, base::Value::TYPE_STRING)));
+            kTestPolicy, kTestPref, base::Value::Type::STRING)));
   }
 
   void TearDown() override {
diff --git a/components/policy/core/browser/proxy_policy_handler.cc b/components/policy/core/browser/proxy_policy_handler.cc
index d81461eb..5017e49 100644
--- a/components/policy/core/browser/proxy_policy_handler.cc
+++ b/components/policy/core/browser/proxy_policy_handler.cc
@@ -227,8 +227,8 @@
   const base::Value* policy_value = NULL;
   std::string tmp;
   if (!settings->Get(policy_name, &policy_value) ||
-      policy_value->IsType(base::Value::TYPE_NULL) ||
-      (policy_value->IsType(base::Value::TYPE_STRING) &&
+      policy_value->IsType(base::Value::Type::NONE) ||
+      (policy_value->IsType(base::Value::Type::STRING) &&
        policy_value->GetAsString(&tmp) &&
        tmp.empty())) {
     return NULL;
@@ -257,7 +257,7 @@
     if (!mode->GetAsString(mode_value)) {
       errors->AddError(key::kProxySettings, key::kProxyMode,
                        IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_BOOLEAN));
+                       base::Value::GetTypeName(base::Value::Type::BOOLEAN));
       return false;
     }
 
@@ -286,7 +286,7 @@
     if (!server_mode->GetAsInteger(&server_mode_value)) {
       errors->AddError(key::kProxySettings, key::kProxyServerMode,
                        IDS_POLICY_TYPE_ERROR,
-                       base::Value::GetTypeName(base::Value::TYPE_INTEGER));
+                       base::Value::GetTypeName(base::Value::Type::INTEGER));
       return false;
     }
 
diff --git a/components/policy/core/browser/proxy_policy_handler_unittest.cc b/components/policy/core/browser/proxy_policy_handler_unittest.cc
index 10335de..c54fb4c 100644
--- a/components/policy/core/browser/proxy_policy_handler_unittest.cc
+++ b/components/policy/core/browser/proxy_policy_handler_unittest.cc
@@ -46,7 +46,7 @@
       const ProxyPrefs::ProxyMode& expected_proxy_mode) {
     const base::Value* value = NULL;
     ASSERT_TRUE(store_->GetValue(proxy_config::prefs::kProxy, &value));
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, value->GetType());
     ProxyConfigDictionary dict(
         static_cast<const base::DictionaryValue*>(value));
     std::string s;
diff --git a/components/policy/core/browser/url_blacklist_policy_handler.cc b/components/policy/core/browser/url_blacklist_policy_handler.cc
index cdbe8af..49de7d03 100644
--- a/components/policy/core/browser/url_blacklist_policy_handler.cc
+++ b/components/policy/core/browser/url_blacklist_policy_handler.cc
@@ -27,14 +27,14 @@
       policies.GetValue(key::kDisabledSchemes);
   const base::Value* url_blacklist = policies.GetValue(key::kURLBlacklist);
 
-  if (disabled_schemes && !disabled_schemes->IsType(base::Value::TYPE_LIST)) {
+  if (disabled_schemes && !disabled_schemes->IsType(base::Value::Type::LIST)) {
     errors->AddError(key::kDisabledSchemes, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_LIST));
+                     base::Value::GetTypeName(base::Value::Type::LIST));
   }
 
-  if (url_blacklist && !url_blacklist->IsType(base::Value::TYPE_LIST)) {
+  if (url_blacklist && !url_blacklist->IsType(base::Value::Type::LIST)) {
     errors->AddError(key::kURLBlacklist, IDS_POLICY_TYPE_ERROR,
-                     base::Value::GetTypeName(base::Value::TYPE_LIST));
+                     base::Value::GetTypeName(base::Value::Type::LIST));
   }
 
   return true;
@@ -69,7 +69,7 @@
 
   if (url_blacklist) {
     for (const auto& entry : *url_blacklist) {
-      if (entry->IsType(base::Value::TYPE_STRING))
+      if (entry->IsType(base::Value::Type::STRING))
         merged_url_blacklist->Append(entry->CreateDeepCopy());
     }
   }
diff --git a/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc b/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
index f5bf326d..c3112952 100644
--- a/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
@@ -7,8 +7,8 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/macros.h"
@@ -81,16 +81,8 @@
 
   std::unique_ptr<UserCloudPolicyValidator> CreateValidator(
       std::unique_ptr<em::PolicyFetchResponse> policy_response) {
-    std::vector<uint8_t> public_key_bytes;
-    EXPECT_TRUE(
-        PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(
-            &public_key_bytes));
-
-    // Convert from bytes to string format (which is what ValidateSignature()
-    // takes).
-    std::string public_key =
-        std::string(reinterpret_cast<const char*>(public_key_bytes.data()),
-                    public_key_bytes.size());
+    std::string public_key = PolicyBuilder::GetPublicTestKeyAsString();
+    EXPECT_FALSE(public_key.empty());
 
     UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create(
         std::move(policy_response), base::ThreadTaskRunnerHandle::Get());
diff --git a/components/policy/core/common/cloud/component_cloud_policy_service_unittest.cc b/components/policy/core/common/cloud/component_cloud_policy_service_unittest.cc
index 5f0b258b..ca417eb0 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_service_unittest.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_service_unittest.cc
@@ -9,7 +9,6 @@
 #include <map>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
@@ -126,10 +125,7 @@
     builder_.payload().set_download_url(kTestDownload);
     builder_.payload().set_secure_hash(crypto::SHA256HashString(kTestPolicy));
 
-    std::vector<uint8_t> public_key_bits;
-    builder_.GetSigningKey()->ExportPublicKey(&public_key_bits);
-    public_key_.assign(reinterpret_cast<const char*>(public_key_bits.data()),
-                       public_key_bits.size());
+    public_key_ = builder_.GetPublicSigningKeyAsString();
 
     expected_policy_.Set(
         "Name", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
@@ -603,12 +599,7 @@
   ASSERT_FALSE(fetcher_factory_.GetFetcherByID(0));
 
   // Update the signing key in the store.
-  std::vector<uint8_t> new_public_key_bits;
-  new_signing_key->ExportPublicKey(&new_public_key_bits);
-  const std::string new_public_key(
-      reinterpret_cast<const char*>(new_public_key_bits.data()),
-      new_public_key_bits.size());
-  store_.policy_signature_public_key_ = new_public_key;
+  store_.policy_signature_public_key_ = builder_.GetPublicSigningKeyAsString();
   store_.policy_->set_public_key_version(kNewPublicKeyVersion);
   store_.NotifyStoreLoaded();
   RunUntilIdle();
diff --git a/components/policy/core/common/cloud/component_cloud_policy_store_unittest.cc b/components/policy/core/common/cloud/component_cloud_policy_store_unittest.cc
index eaa8c0a..9798ea0 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_store_unittest.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_store_unittest.cc
@@ -9,7 +9,6 @@
 #include <map>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -85,10 +84,7 @@
     builder_.payload().set_download_url(kTestDownload);
     builder_.payload().set_secure_hash(TestPolicyHash());
 
-    std::vector<uint8_t> public_key_bits;
-    builder_.GetSigningKey()->ExportPublicKey(&public_key_bits);
-    public_key_.assign(reinterpret_cast<const char*>(public_key_bits.data()),
-                       public_key_bits.size());
+    public_key_ = builder_.GetPublicSigningKeyAsString();
 
     PolicyMap& policy = expected_bundle_.Get(kTestPolicyNS);
     policy.Set("Name", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
diff --git a/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc b/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
index 8444546d..6e0834e 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
@@ -4,6 +4,10 @@
 
 #include "components/policy/core/common/cloud/component_cloud_policy_updater.h"
 
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/files/scoped_temp_dir.h"
@@ -19,6 +23,7 @@
 #include "components/policy/core/common/external_data_fetcher.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/proto/chrome_extension_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -97,10 +102,7 @@
   builder_.payload().set_download_url(kTestDownload);
   builder_.payload().set_secure_hash(crypto::SHA256HashString(kTestPolicy));
 
-  std::vector<uint8_t> public_key_bits;
-  builder_.GetSigningKey()->ExportPublicKey(&public_key_bits);
-  public_key_.assign(reinterpret_cast<const char*>(public_key_bits.data()),
-                     public_key_bits.size());
+  public_key_ = builder_.GetPublicSigningKeyAsString();
 
   PolicyMap& policy = expected_bundle_.Get(kTestPolicyNS);
   policy.Set("Name", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
diff --git a/components/policy/core/common/cloud/policy_builder.cc b/components/policy/core/common/cloud/policy_builder.cc
index de72a4c..8237baee 100644
--- a/components/policy/core/common/cloud/policy_builder.cc
+++ b/components/policy/core/common/cloud/policy_builder.cc
@@ -4,8 +4,6 @@
 
 #include "components/policy/core/common/cloud/policy_builder.h"
 
-#include <vector>
-
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
@@ -135,6 +133,31 @@
     0xDD, 0x6F, 0x80, 0xC3,
 };
 
+std::vector<uint8_t> ExportPublicKey(const crypto::RSAPrivateKey& key) {
+  std::vector<uint8_t> public_key;
+  CHECK(key.ExportPublicKey(&public_key));
+  return public_key;
+}
+
+std::string ConvertPublicKeyToString(const std::vector<uint8_t>& public_key) {
+  return std::string(reinterpret_cast<const char*>(public_key.data()),
+                     public_key.size());
+}
+
+// Produces |key|'s signature over |data| and stores it in |signature|.
+void SignData(const std::string& data,
+              crypto::RSAPrivateKey* const key,
+              std::string* const signature) {
+  std::unique_ptr<crypto::SignatureCreator> signature_creator(
+      crypto::SignatureCreator::Create(key, crypto::SignatureCreator::SHA1));
+  signature_creator->Update(reinterpret_cast<const uint8_t*>(data.c_str()),
+                            data.size());
+  std::vector<uint8_t> signature_bytes;
+  CHECK(signature_creator->Final(&signature_bytes));
+  signature->assign(reinterpret_cast<const char*>(signature_bytes.data()),
+                    signature_bytes.size());
+}
+
 }  // namespace
 
 // Constants used as dummy data for filling the PolicyData protobuf.
@@ -148,9 +171,9 @@
 const char PolicyBuilder::kFakeUsername[] = "username@example.com";
 const char PolicyBuilder::kFakeServiceAccountIdentity[] = "robot4test@g.com";
 
-PolicyBuilder::PolicyBuilder()
-    : policy_data_(new em::PolicyData()) {
+PolicyBuilder::PolicyBuilder() {
   SetDefaultSigningKey();
+  CreatePolicyData();
   policy_data_->set_policy_type(kFakePolicyType);
   policy_data_->set_timestamp(kFakeTimestamp);
   policy_data_->set_request_token(kFakeToken);
@@ -164,11 +187,10 @@
 
 PolicyBuilder::~PolicyBuilder() {}
 
-std::unique_ptr<crypto::RSAPrivateKey> PolicyBuilder::GetSigningKey() {
+std::unique_ptr<crypto::RSAPrivateKey> PolicyBuilder::GetSigningKey() const {
   if (raw_signing_key_.empty())
     return std::unique_ptr<crypto::RSAPrivateKey>();
-  return std::unique_ptr<crypto::RSAPrivateKey>(
-      crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_signing_key_));
+  return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_signing_key_);
 }
 
 void PolicyBuilder::SetSigningKey(const crypto::RSAPrivateKey& key) {
@@ -176,15 +198,14 @@
 }
 
 void PolicyBuilder::SetDefaultSigningKey() {
-  std::vector<uint8_t> key(kSigningKey, kSigningKey + arraysize(kSigningKey));
-  raw_signing_key_.swap(key);
+  raw_signing_key_.assign(kSigningKey, kSigningKey + arraysize(kSigningKey));
 }
 
 void PolicyBuilder::UnsetSigningKey() {
   raw_signing_key_.clear();
 }
 
-std::unique_ptr<crypto::RSAPrivateKey> PolicyBuilder::GetNewSigningKey() {
+std::unique_ptr<crypto::RSAPrivateKey> PolicyBuilder::GetNewSigningKey() const {
   if (raw_new_signing_key_.empty())
     return std::unique_ptr<crypto::RSAPrivateKey>();
   return std::unique_ptr<crypto::RSAPrivateKey>(
@@ -192,35 +213,31 @@
 }
 
 void PolicyBuilder::SetDefaultNewSigningKey() {
-  std::vector<uint8_t> key(kNewSigningKey,
-                           kNewSigningKey + arraysize(kNewSigningKey));
-  raw_new_signing_key_.swap(key);
+  raw_new_signing_key_.assign(kNewSigningKey,
+                              kNewSigningKey + arraysize(kNewSigningKey));
   raw_new_signing_key_signature_ = GetTestOtherSigningKeySignature();
 }
 
-void PolicyBuilder::SetDefaultInitialSigningKey() {
-  std::vector<uint8_t> key(kSigningKey, kSigningKey + arraysize(kSigningKey));
-  raw_new_signing_key_.swap(key);
-  raw_new_signing_key_signature_ = GetTestSigningKeySignature();
-  UnsetSigningKey();
-}
-
 void PolicyBuilder::UnsetNewSigningKey() {
   raw_new_signing_key_.clear();
   raw_new_signing_key_signature_.clear();
 }
 
+void PolicyBuilder::SetDefaultInitialSigningKey() {
+  raw_new_signing_key_.assign(kSigningKey,
+                              kSigningKey + arraysize(kSigningKey));
+  raw_new_signing_key_signature_ = GetTestSigningKeySignature();
+  UnsetSigningKey();
+}
+
 void PolicyBuilder::Build() {
   // Generate signatures if applicable.
   std::unique_ptr<crypto::RSAPrivateKey> policy_signing_key =
       GetNewSigningKey();
   if (policy_signing_key) {
     // Add the new public key.
-    std::vector<uint8_t> raw_new_public_signing_key;
-    CHECK(policy_signing_key->ExportPublicKey(&raw_new_public_signing_key));
-    policy_.set_new_public_key(raw_new_public_signing_key.data(),
-                               raw_new_public_signing_key.size());
-
+    policy_.set_new_public_key(
+        ConvertPublicKeyToString(ExportPublicKey(*policy_signing_key)));
     policy_.set_new_public_key_verification_signature_deprecated(
         raw_new_signing_key_signature_);
 
@@ -235,42 +252,43 @@
     // No new signing key, so clear the old public key (this allows us to
     // reuse the same PolicyBuilder to build multiple policy blobs).
     policy_.clear_new_public_key();
+    policy_.clear_new_public_key_verification_signature_deprecated();
     policy_.clear_new_public_key_signature();
     policy_signing_key = GetSigningKey();
   }
 
-  // Policy isn't signed, so there shouldn't be a public key version.
-  if (!policy_signing_key)
-    policy_data_->clear_public_key_version();
+  if (policy_data_) {
+    // Policy isn't signed, so there shouldn't be a public key version.
+    if (!policy_signing_key)
+      policy_data_->clear_public_key_version();
 
-  // Serialize the policy data.
-  if (policy_data_.get())
+    // Serialize the policy data.
     CHECK(policy_data_->SerializeToString(policy_.mutable_policy_data()));
 
-  // PolicyData signature.
-  if (policy_signing_key) {
-    SignData(policy_.policy_data(), policy_signing_key.get(),
-             policy_.mutable_policy_data_signature());
+    // PolicyData signature.
+    if (policy_signing_key) {
+      SignData(policy_.policy_data(), policy_signing_key.get(),
+               policy_.mutable_policy_data_signature());
+    }
+  } else {
+    policy_.clear_policy_data();
+    policy_.clear_policy_data_signature();
   }
 }
 
-std::string PolicyBuilder::GetBlob() {
+std::string PolicyBuilder::GetBlob() const {
   return policy_.SerializeAsString();
 }
 
-std::unique_ptr<em::PolicyFetchResponse> PolicyBuilder::GetCopy() {
-  std::unique_ptr<em::PolicyFetchResponse> result(
-      new em::PolicyFetchResponse());
-  result->CopyFrom(policy_);
-  return result;
+std::unique_ptr<em::PolicyFetchResponse> PolicyBuilder::GetCopy() const {
+  return base::MakeUnique<em::PolicyFetchResponse>(policy_);
 }
 
 // static
 std::unique_ptr<crypto::RSAPrivateKey> PolicyBuilder::CreateTestSigningKey() {
   std::vector<uint8_t> raw_signing_key(kSigningKey,
                                        kSigningKey + arraysize(kSigningKey));
-  return std::unique_ptr<crypto::RSAPrivateKey>(
-      crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_signing_key));
+  return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_signing_key);
 }
 
 // static
@@ -278,8 +296,7 @@
 PolicyBuilder::CreateTestOtherSigningKey() {
   std::vector<uint8_t> raw_new_signing_key(
       kNewSigningKey, kNewSigningKey + arraysize(kNewSigningKey));
-  return std::unique_ptr<crypto::RSAPrivateKey>(
-      crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_new_signing_key));
+  return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_new_signing_key);
 }
 
 // static
@@ -294,17 +311,46 @@
                      sizeof(kNewSigningKeySignature));
 }
 
-void PolicyBuilder::SignData(const std::string& data,
-                             crypto::RSAPrivateKey* key,
-                             std::string* signature) {
-  std::unique_ptr<crypto::SignatureCreator> signature_creator(
-      crypto::SignatureCreator::Create(key, crypto::SignatureCreator::SHA1));
-  signature_creator->Update(reinterpret_cast<const uint8_t*>(data.c_str()),
-                            data.size());
-  std::vector<uint8_t> signature_bytes;
-  CHECK(signature_creator->Final(&signature_bytes));
-  signature->assign(reinterpret_cast<const char*>(signature_bytes.data()),
-                    signature_bytes.size());
+std::vector<uint8_t> PolicyBuilder::GetPublicSigningKey() const {
+  std::unique_ptr<crypto::RSAPrivateKey> key = GetSigningKey();
+  if (!key)
+    return std::vector<uint8_t>();
+  return ExportPublicKey(*key);
+}
+
+std::vector<uint8_t> PolicyBuilder::GetPublicNewSigningKey() const {
+  std::unique_ptr<crypto::RSAPrivateKey> key = GetNewSigningKey();
+  if (!key)
+    return std::vector<uint8_t>();
+  return ExportPublicKey(*key);
+}
+
+// static
+std::vector<uint8_t> PolicyBuilder::GetPublicTestKey() {
+  return ExportPublicKey(*CreateTestSigningKey());
+}
+
+// static
+std::vector<uint8_t> PolicyBuilder::GetPublicTestOtherKey() {
+  return ExportPublicKey(*CreateTestOtherSigningKey());
+}
+
+std::string PolicyBuilder::GetPublicSigningKeyAsString() const {
+  return ConvertPublicKeyToString(GetPublicSigningKey());
+}
+
+std::string PolicyBuilder::GetPublicNewSigningKeyAsString() const {
+  return ConvertPublicKeyToString(GetPublicNewSigningKey());
+}
+
+// static
+std::string PolicyBuilder::GetPublicTestKeyAsString() {
+  return ConvertPublicKeyToString(GetPublicTestKey());
+}
+
+// static
+std::string PolicyBuilder::GetPublicTestOtherKeyAsString() {
+  return ConvertPublicKeyToString(GetPublicTestOtherKey());
 }
 
 template<>
@@ -318,8 +364,8 @@
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
 template<>
-TypedPolicyBuilder<em::ExternalPolicyData>::TypedPolicyBuilder()
-    : payload_(new em::ExternalPolicyData()) {
+TypedPolicyBuilder<em::ExternalPolicyData>::TypedPolicyBuilder() {
+  CreatePayload();
   policy_data().set_policy_type(dm_protocol::kChromeExtensionPolicyType);
 }
 
diff --git a/components/policy/core/common/cloud/policy_builder.h b/components/policy/core/common/cloud/policy_builder.h
index 2a8af4a..3b5f09a 100644
--- a/components/policy/core/common/cloud/policy_builder.h
+++ b/components/policy/core/common/cloud/policy_builder.h
@@ -9,10 +9,12 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "build/build_config.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -40,48 +42,59 @@
   static const char kFakeUsername[];
   static const char kFakeServiceAccountIdentity[];
 
-  // Creates a policy builder. The builder will have all PolicyData fields
+  // Creates a policy builder. The builder will have all |policy_data_| fields
   // initialized to dummy values and use the test signing keys.
   PolicyBuilder();
   virtual ~PolicyBuilder();
 
-  // Use this member to access the PolicyData protobuf.
-  enterprise_management::PolicyData& policy_data() {
-    if (!policy_data_.get())
-      policy_data_.reset(new enterprise_management::PolicyData());
+  // Returns a reference to the policy data protobuf being built. Note that an
+  // initial policy data payload protobuf is created and filled with testing
+  // values in the constructor. Note also that the public_key_version field will
+  // be filled with the right values only after the Build() method call.
+  enterprise_management::PolicyData& policy_data() { return *policy_data_; }
+  const enterprise_management::PolicyData& policy_data() const {
     return *policy_data_;
   }
-  void clear_policy_data() {
-    policy_data_.reset();
+  void clear_policy_data() { policy_data_.reset(); }
+  void CreatePolicyData() {
+    policy_data_ = base::MakeUnique<enterprise_management::PolicyData>();
   }
 
-  enterprise_management::PolicyFetchResponse& policy() {
+  // Returns a reference to the policy protobuf being built. Note that the
+  // fields relating to the public key, serialized policy data and signature
+  // will be filled with the right values only after the Build() method call.
+  enterprise_management::PolicyFetchResponse& policy() { return policy_; }
+  const enterprise_management::PolicyFetchResponse& policy() const {
     return policy_;
   }
 
-  std::unique_ptr<crypto::RSAPrivateKey> GetSigningKey();
+  // Use these methods for obtaining and changing the current signing key.
+  // Note that, by default, a hard-coded testing signing key is used.
+  std::unique_ptr<crypto::RSAPrivateKey> GetSigningKey() const;
   void SetSigningKey(const crypto::RSAPrivateKey& key);
   void SetDefaultSigningKey();
   void UnsetSigningKey();
 
+  // Use these methods for obtaining and changing the new signing key.
+  // By default, there is no new signing key.
+  std::unique_ptr<crypto::RSAPrivateKey> GetNewSigningKey() const;
+  void SetDefaultNewSigningKey();
+  void UnsetNewSigningKey();
+
   // Sets the default initial signing key - the resulting policy will be signed
   // by the default signing key, and will have that key set as the
   // new_public_key field, as if it were an initial key provision.
   void SetDefaultInitialSigningKey();
 
-  std::unique_ptr<crypto::RSAPrivateKey> GetNewSigningKey();
-  void SetDefaultNewSigningKey();
-  void UnsetNewSigningKey();
-
   // Assembles the policy components. The resulting policy protobuf is available
   // through policy() after this call.
   virtual void Build();
 
   // Returns a copy of policy().
-  std::unique_ptr<enterprise_management::PolicyFetchResponse> GetCopy();
+  std::unique_ptr<enterprise_management::PolicyFetchResponse> GetCopy() const;
 
   // Returns a binary policy blob, i.e. an encoded PolicyFetchResponse.
-  std::string GetBlob();
+  std::string GetBlob() const;
 
   // These return hard-coded testing keys. Don't use in production!
   static std::unique_ptr<crypto::RSAPrivateKey> CreateTestSigningKey();
@@ -92,18 +105,30 @@
   static std::string GetTestSigningKeySignature();
   static std::string GetTestOtherSigningKeySignature();
 
-  std::vector<uint8_t> raw_signing_key() { return raw_signing_key_; }
-  std::vector<uint8_t> raw_new_signing_key() { return raw_new_signing_key_; }
+  std::vector<uint8_t> raw_signing_key() const { return raw_signing_key_; }
+  std::vector<uint8_t> raw_new_signing_key() const {
+    return raw_new_signing_key_;
+  }
+
+  // These methods return the public part of the corresponding signing keys,
+  // using the same binary format that is used for storing the public keys in
+  // the policy protobufs.
+  std::vector<uint8_t> GetPublicSigningKey() const;
+  std::vector<uint8_t> GetPublicNewSigningKey() const;
+  static std::vector<uint8_t> GetPublicTestKey();
+  static std::vector<uint8_t> GetPublicTestOtherKey();
+
+  // These methods return the public part of the corresponding signing keys as a
+  // string, using the same binary format that is used for storing the public
+  // keys in the policy protobufs.
+  std::string GetPublicSigningKeyAsString() const;
+  std::string GetPublicNewSigningKeyAsString() const;
+  static std::string GetPublicTestKeyAsString();
+  static std::string GetPublicTestOtherKeyAsString();
 
  private:
-  // Produces |key|'s signature over |data| and stores it in |signature|.
-  void SignData(const std::string& data,
-                crypto::RSAPrivateKey* key,
-                std::string* signature);
-
   enterprise_management::PolicyFetchResponse policy_;
   std::unique_ptr<enterprise_management::PolicyData> policy_data_;
-  std::string payload_data_;
 
   // The keys cannot be stored in NSS. Temporary keys are not guaranteed to
   // remain in the database. Persistent keys require a persistent database,
@@ -124,21 +149,17 @@
 class TypedPolicyBuilder : public PolicyBuilder {
  public:
   TypedPolicyBuilder();
-  ~TypedPolicyBuilder() override {}
 
-  // Returns a reference to the payload protobuf being built.
-  PayloadProto& payload() {
-    if (!payload_.get())
-      payload_.reset(new PayloadProto());
-    return *payload_;
-  }
-  void clear_payload() {
-    payload_.reset();
-  }
+  // Returns a reference to the payload protobuf being built. Note that an
+  // initial payload protobuf is created in the constructor.
+  PayloadProto& payload() { return *payload_; }
+  const PayloadProto& payload() const { return *payload_; }
+  void clear_payload() { payload_.reset(); }
+  void CreatePayload() { payload_ = base::MakeUnique<PayloadProto>(); }
 
   // PolicyBuilder:
   void Build() override {
-    if (payload_.get())
+    if (payload_)
       CHECK(payload_->SerializeToString(policy_data().mutable_policy_value()));
 
     PolicyBuilder::Build();
diff --git a/components/policy/core/common/generate_policy_source_unittest.cc b/components/policy/core/common/generate_policy_source_unittest.cc
index 87738a81..2e2a91f 100644
--- a/components/policy/core/common/generate_policy_source_unittest.cc
+++ b/components/policy/core/common/generate_policy_source_unittest.cc
@@ -31,9 +31,9 @@
     return true;
   if (a.type() != b.type())
     return false;
-  if (a.type() == base::Value::TYPE_LIST)
+  if (a.type() == base::Value::Type::LIST)
     return IsSameSchema(a.GetItems(), b.GetItems());
-  if (a.type() != base::Value::TYPE_DICTIONARY)
+  if (a.type() != base::Value::Type::DICTIONARY)
     return true;
   Schema::Iterator a_it = a.GetPropertiesIterator();
   Schema::Iterator b_it = b.GetPropertiesIterator();
@@ -58,7 +58,7 @@
 TEST(GeneratePolicySource, ChromeSchemaData) {
   Schema schema = Schema::Wrap(GetChromeSchemaData());
   ASSERT_TRUE(schema.valid());
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   Schema subschema = schema.GetAdditionalProperties();
   EXPECT_FALSE(subschema.valid());
@@ -68,26 +68,26 @@
 
   subschema = schema.GetProperty(key::kSearchSuggestEnabled);
   ASSERT_TRUE(subschema.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, subschema.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, subschema.type());
 
   subschema = schema.GetProperty(key::kDefaultCookiesSetting);
   ASSERT_TRUE(subschema.valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, subschema.type());
+  EXPECT_EQ(base::Value::Type::INTEGER, subschema.type());
 
   subschema = schema.GetProperty(key::kProxyMode);
   ASSERT_TRUE(subschema.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, subschema.type());
+  EXPECT_EQ(base::Value::Type::STRING, subschema.type());
 
   subschema = schema.GetProperty(key::kURLBlacklist);
   ASSERT_TRUE(subschema.valid());
-  EXPECT_EQ(base::Value::TYPE_LIST, subschema.type());
+  EXPECT_EQ(base::Value::Type::LIST, subschema.type());
   ASSERT_TRUE(subschema.GetItems().valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, subschema.GetItems().type());
+  EXPECT_EQ(base::Value::Type::STRING, subschema.GetItems().type());
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   subschema = schema.GetProperty(key::kExtensionSettings);
   ASSERT_TRUE(subschema.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, subschema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
   EXPECT_FALSE(subschema.GetAdditionalProperties().valid());
   EXPECT_FALSE(subschema.GetProperty("no such extension id exists").valid());
   EXPECT_TRUE(subschema.GetPatternProperties("*").empty());
@@ -102,22 +102,22 @@
   ASSERT_EQ(1u, schema_list.size());
   subschema = schema_list[0];
   ASSERT_TRUE(subschema.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, subschema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
   subschema = subschema.GetProperty("installation_mode");
   ASSERT_TRUE(subschema.valid());
-  ASSERT_EQ(base::Value::TYPE_STRING, subschema.type());
+  ASSERT_EQ(base::Value::Type::STRING, subschema.type());
 
   subschema = schema.GetProperty(key::kExtensionSettings).GetProperty("*");
   ASSERT_TRUE(subschema.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, subschema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
   subschema = subschema.GetProperty("installation_mode");
   ASSERT_TRUE(subschema.valid());
-  ASSERT_EQ(base::Value::TYPE_STRING, subschema.type());
+  ASSERT_EQ(base::Value::Type::STRING, subschema.type());
 #endif
 
   subschema = schema.GetProperty(key::kProxySettings);
   ASSERT_TRUE(subschema.valid());
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, subschema.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, subschema.type());
   EXPECT_FALSE(subschema.GetAdditionalProperties().valid());
   EXPECT_FALSE(subschema.GetProperty("no such proxy key exists").valid());
   ASSERT_TRUE(subschema.GetProperty(key::kProxyMode).valid());
@@ -149,7 +149,7 @@
     ASSERT_TRUE(*next != NULL);
     EXPECT_STREQ(*next, it.key());
     ASSERT_TRUE(it.schema().valid());
-    EXPECT_EQ(base::Value::TYPE_STRING, it.schema().type());
+    EXPECT_EQ(base::Value::Type::STRING, it.schema().type());
   }
   EXPECT_TRUE(*next == NULL);
 
diff --git a/components/policy/core/common/mac_util_unittest.cc b/components/policy/core/common/mac_util_unittest.cc
index c082dafa..b96e298 100644
--- a/components/policy/core/common/mac_util_unittest.cc
+++ b/components/policy/core/common/mac_util_unittest.cc
@@ -18,26 +18,26 @@
 TEST(PolicyMacUtilTest, PropertyToValue) {
   base::DictionaryValue root;
 
-  // base::Value::TYPE_NULL
+  // base::Value::Type::NONE
   root.Set("null", base::Value::CreateNullValue());
 
-  // base::Value::TYPE_BOOLEAN
+  // base::Value::Type::BOOLEAN
   root.SetBoolean("false", false);
   root.SetBoolean("true", true);
 
-  // base::Value::TYPE_INTEGER
+  // base::Value::Type::INTEGER
   root.SetInteger("int", 123);
   root.SetInteger("zero", 0);
 
-  // base::Value::TYPE_DOUBLE
+  // base::Value::Type::DOUBLE
   root.SetDouble("double", 123.456);
   root.SetDouble("zerod", 0.0);
 
-  // base::Value::TYPE_STRING
+  // base::Value::Type::STRING
   root.SetString("string", "the fox jumps over something");
   root.SetString("empty", "");
 
-  // base::Value::TYPE_LIST
+  // base::Value::Type::LIST
   base::ListValue list;
   root.Set("emptyl", list.DeepCopy());
   for (base::DictionaryValue::Iterator it(root); !it.IsAtEnd(); it.Advance())
@@ -46,7 +46,7 @@
   list.Append(root.DeepCopy());
   root.Set("list", list.DeepCopy());
 
-  // base::Value::TYPE_DICTIONARY
+  // base::Value::Type::DICTIONARY
   base::DictionaryValue dict;
   root.Set("emptyd", dict.DeepCopy());
   // Very meta.
diff --git a/components/policy/core/common/policy_loader_win_unittest.cc b/components/policy/core/common/policy_loader_win_unittest.cc
index 53a202d..1e06bd8 100644
--- a/components/policy/core/common/policy_loader_win_unittest.cc
+++ b/components/policy/core/common/policy_loader_win_unittest.cc
@@ -72,24 +72,24 @@
   RegKey key(hive, path.c_str(), KEY_ALL_ACCESS);
   EXPECT_TRUE(key.Valid());
   switch (value.GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return key.WriteValue(name.c_str(), L"") == ERROR_SUCCESS;
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value;
       if (!value.GetAsBoolean(&bool_value))
         return false;
       return key.WriteValue(name.c_str(), bool_value ? 1 : 0) == ERROR_SUCCESS;
     }
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value;
       if (!value.GetAsInteger(&int_value))
         return false;
       return key.WriteValue(name.c_str(), int_value) == ERROR_SUCCESS;
     }
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       if (!value.GetAsDouble(&double_value))
         return false;
@@ -98,14 +98,14 @@
       return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
     }
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       base::string16 str_value;
       if (!value.GetAsString(&str_value))
         return false;
       return key.WriteValue(name.c_str(), str_value.c_str()) == ERROR_SUCCESS;
     }
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* sub_dict = NULL;
       if (!value.GetAsDictionary(&sub_dict))
         return false;
@@ -119,7 +119,7 @@
       return true;
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list = NULL;
       if (!value.GetAsList(&list))
         return false;
@@ -135,7 +135,7 @@
       return true;
     }
 
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       return false;
   }
   NOTREACHED();
@@ -624,31 +624,31 @@
                                              const std::string& key,
                                              const base::Value* value) {
   switch (value->GetType()) {
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool boolean_value = false;
       ASSERT_TRUE(value->GetAsBoolean(&boolean_value));
       AppendDWORDToPRegFile(path, key, boolean_value);
       break;
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value = 0;
       ASSERT_TRUE(value->GetAsInteger(&int_value));
       AppendDWORDToPRegFile(path, key, int_value);
       break;
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value = 0;
       ASSERT_TRUE(value->GetAsDouble(&double_value));
       AppendStringToPRegFile(path, key, base::DoubleToString(double_value));
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string string_value;
       ASSERT_TRUE(value->GetAsString(&string_value));
       AppendStringToPRegFile(path, key, string_value);
       break;
     }
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       base::string16 subpath = path + kPathSep + UTF8ToUTF16(key);
       const base::DictionaryValue* dict = NULL;
       ASSERT_TRUE(value->GetAsDictionary(&dict));
@@ -658,7 +658,7 @@
       }
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       base::string16 subpath = path + kPathSep + UTF8ToUTF16(key);
       const base::ListValue* list = NULL;
       ASSERT_TRUE(value->GetAsList(&list));
@@ -669,8 +669,8 @@
       }
       break;
     }
-    case base::Value::TYPE_BINARY:
-    case base::Value::TYPE_NULL: {
+    case base::Value::Type::BINARY:
+    case base::Value::Type::NONE: {
       ADD_FAILURE();
       break;
     }
diff --git a/components/policy/core/common/policy_test_utils.cc b/components/policy/core/common/policy_test_utils.cc
index c0860ef1..7f4e5973 100644
--- a/components/policy/core/common/policy_test_utils.cc
+++ b/components/policy/core/common/policy_test_utils.cc
@@ -57,17 +57,17 @@
 #if defined(OS_IOS) || defined(OS_MACOSX)
 CFPropertyListRef ValueToProperty(const base::Value& value) {
   switch (value.GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return kCFNull;
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value;
       if (value.GetAsBoolean(&bool_value))
         return bool_value ? kCFBooleanTrue : kCFBooleanFalse;
       break;
     }
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value;
       if (value.GetAsInteger(&int_value)) {
         return CFNumberCreate(
@@ -76,7 +76,7 @@
       break;
     }
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       if (value.GetAsDouble(&double_value)) {
         return CFNumberCreate(
@@ -85,14 +85,14 @@
       break;
     }
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string string_value;
       if (value.GetAsString(&string_value))
         return base::SysUTF8ToCFStringRef(string_value);
       break;
     }
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dict_value;
       if (value.GetAsDictionary(&dict_value)) {
         // |dict| is owned by the caller.
@@ -117,7 +117,7 @@
       break;
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list;
       if (value.GetAsList(&list)) {
         CFMutableArrayRef array =
@@ -135,7 +135,7 @@
       break;
     }
 
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       // This type isn't converted (though it can be represented as CFData)
       // because there's no equivalent JSON type, and policy values can only
       // take valid JSON values.
diff --git a/components/policy/core/common/registry_dict.cc b/components/policy/core/common/registry_dict.cc
index 0db631e..ca7df29 100644
--- a/components/policy/core/common/registry_dict.cc
+++ b/components/policy/core/common/registry_dict.cc
@@ -75,10 +75,10 @@
   std::string string_value;
   int int_value = 0;
   switch (schema.type()) {
-    case base::Value::TYPE_NULL: {
+    case base::Value::Type::NONE: {
       return base::Value::CreateNullValue();
     }
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       // Accept booleans encoded as either string or integer.
       if (value.GetAsInteger(&int_value) ||
           (value.GetAsString(&string_value) &&
@@ -88,7 +88,7 @@
       }
       break;
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       // Integers may be string-encoded.
       if (value.GetAsString(&string_value) &&
           base::StringToInt(string_value, &int_value)) {
@@ -97,7 +97,7 @@
       }
       break;
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       // Doubles may be string-encoded or integer-encoded.
       double double_value = 0;
       if (value.GetAsDouble(&double_value) ||
@@ -108,7 +108,7 @@
       }
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       // Lists are encoded as subkeys with numbered value in the registry
       // (non-numerical keys are ignored).
       const base::DictionaryValue* dict = nullptr;
@@ -127,7 +127,7 @@
       }
       // Fall through in order to accept lists encoded as JSON strings.
     }
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       // Dictionaries may be encoded as JSON strings.
       if (value.GetAsString(&string_value)) {
         std::unique_ptr<base::Value> result =
@@ -137,8 +137,8 @@
       }
       break;
     }
-    case base::Value::TYPE_STRING:
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::STRING:
+    case base::Value::Type::BINARY:
       // No conversion possible.
       break;
   }
@@ -307,9 +307,9 @@
 std::unique_ptr<base::Value> RegistryDict::ConvertToJSON(
     const Schema& schema) const {
   base::Value::Type type =
-      schema.valid() ? schema.type() : base::Value::TYPE_DICTIONARY;
+      schema.valid() ? schema.type() : base::Value::Type::DICTIONARY;
   switch (type) {
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       std::unique_ptr<base::DictionaryValue> result(
           new base::DictionaryValue());
       for (RegistryDict::ValueMap::const_iterator entry(values_.begin());
@@ -332,7 +332,7 @@
       }
       return std::move(result);
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       std::unique_ptr<base::ListValue> result(new base::ListValue());
       Schema item_schema = schema.valid() ? schema.GetItems() : Schema();
       for (RegistryDict::KeyMap::const_iterator entry(keys_.begin());
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
index f925e02..330848c 100644
--- a/components/policy/core/common/schema.cc
+++ b/components/policy/core/common/schema.cc
@@ -71,13 +71,13 @@
     const char* schema_type;
     base::Value::Type value_type;
   } kSchemaToValueTypeMap[] = {
-    { schema::kArray,        base::Value::TYPE_LIST       },
-    { schema::kBoolean,      base::Value::TYPE_BOOLEAN    },
-    { schema::kInteger,      base::Value::TYPE_INTEGER    },
-    { schema::kNull,         base::Value::TYPE_NULL       },
-    { schema::kNumber,       base::Value::TYPE_DOUBLE     },
-    { schema::kObject,       base::Value::TYPE_DICTIONARY },
-    { schema::kString,       base::Value::TYPE_STRING     },
+    { schema::kArray,        base::Value::Type::LIST       },
+    { schema::kBoolean,      base::Value::Type::BOOLEAN    },
+    { schema::kInteger,      base::Value::Type::INTEGER    },
+    { schema::kNull,         base::Value::Type::NONE       },
+    { schema::kNumber,       base::Value::Type::DOUBLE     },
+    { schema::kObject,       base::Value::Type::DICTIONARY },
+    { schema::kString,       base::Value::Type::STRING     },
   };
   for (size_t i = 0; i < arraysize(kSchemaToValueTypeMap); ++i) {
     if (kSchemaToValueTypeMap[i].schema_type == type_string) {
@@ -366,7 +366,7 @@
   }
 
   std::string type_string;
-  base::Value::Type type = base::Value::TYPE_NULL;
+  base::Value::Type type = base::Value::Type::NONE;
   if (!schema.GetString(schema::kType, &type_string) ||
       !SchemaTypeToValueType(type_string, &type)) {
     // This schema is invalid.
@@ -375,11 +375,11 @@
 
   sizes->schema_nodes++;
 
-  if (type == base::Value::TYPE_LIST) {
+  if (type == base::Value::Type::LIST) {
     const base::DictionaryValue* items = NULL;
     if (schema.GetDictionary(schema::kItems, &items))
       DetermineStorageSizes(*items, sizes);
-  } else if (type == base::Value::TYPE_DICTIONARY) {
+  } else if (type == base::Value::Type::DICTIONARY) {
     sizes->properties_nodes++;
 
     const base::DictionaryValue* dict = NULL;
@@ -411,18 +411,18 @@
   } else if (schema.HasKey(schema::kEnum)) {
     const base::ListValue* possible_values = NULL;
     if (schema.GetList(schema::kEnum, &possible_values)) {
-      if (type == base::Value::TYPE_INTEGER) {
+      if (type == base::Value::Type::INTEGER) {
         sizes->int_enums += possible_values->GetSize();
-      } else if (type == base::Value::TYPE_STRING) {
+      } else if (type == base::Value::Type::STRING) {
         sizes->string_enums += possible_values->GetSize();
         sizes->strings += possible_values->GetSize();
       }
       sizes->restriction_nodes++;
     }
-  } else if (type == base::Value::TYPE_INTEGER) {
+  } else if (type == base::Value::Type::INTEGER) {
     if (schema.HasKey(schema::kMinimum) || schema.HasKey(schema::kMaximum))
       sizes->restriction_nodes++;
-  } else if (type == base::Value::TYPE_STRING) {
+  } else if (type == base::Value::Type::STRING) {
     if (schema.HasKey(schema::kPattern)) {
       sizes->strings++;
       sizes->string_enums++;
@@ -453,7 +453,7 @@
     return false;
   }
 
-  base::Value::Type type = base::Value::TYPE_NULL;
+  base::Value::Type type = base::Value::Type::NONE;
   if (!SchemaTypeToValueType(type_string, &type)) {
     *error = "Type not supported: " + type_string;
     return false;
@@ -465,10 +465,10 @@
   schema_node->type = type;
   schema_node->extra = kInvalid;
 
-  if (type == base::Value::TYPE_DICTIONARY) {
+  if (type == base::Value::Type::DICTIONARY) {
     if (!ParseDictionary(schema, schema_node, id_map, reference_list, error))
       return false;
-  } else if (type == base::Value::TYPE_LIST) {
+  } else if (type == base::Value::Type::LIST) {
     if (!ParseList(schema, schema_node, id_map, reference_list, error))
       return false;
   } else if (schema.HasKey(schema::kEnum)) {
@@ -479,7 +479,7 @@
       return false;
   } else if (schema.HasKey(schema::kMinimum) ||
              schema.HasKey(schema::kMaximum)) {
-    if (type != base::Value::TYPE_INTEGER) {
+    if (type != base::Value::Type::INTEGER) {
       *error = "Only integers can have minimum and maximum";
       return false;
     }
@@ -614,7 +614,7 @@
   }
   int offset_begin;
   int offset_end;
-  if (type == base::Value::TYPE_INTEGER) {
+  if (type == base::Value::Type::INTEGER) {
     offset_begin = static_cast<int>(int_enums_.size());
     int value;
     for (base::ListValue::const_iterator it = possible_values->begin();
@@ -626,7 +626,7 @@
       int_enums_.push_back(value);
     }
     offset_end = static_cast<int>(int_enums_.size());
-  } else if (type == base::Value::TYPE_STRING) {
+  } else if (type == base::Value::Type::STRING) {
     offset_begin = static_cast<int>(string_enums_.size());
     std::string value;
     for (base::ListValue::const_iterator it = possible_values->begin();
@@ -785,8 +785,8 @@
   if (!value.IsType(type())) {
     // Allow the integer to double promotion. Note that range restriction on
     // double is not supported now.
-    if (value.IsType(base::Value::TYPE_INTEGER) &&
-        type() == base::Value::TYPE_DOUBLE) {
+    if (value.IsType(base::Value::Type::INTEGER) &&
+        type() == base::Value::Type::DOUBLE) {
       return true;
     }
 
@@ -867,8 +867,8 @@
   if (!value->IsType(type())) {
     // Allow the integer to double promotion. Note that range restriction on
     // double is not supported now.
-    if (value->IsType(base::Value::TYPE_INTEGER) &&
-        type() == base::Value::TYPE_DOUBLE) {
+    if (value->IsType(base::Value::Type::INTEGER) &&
+        type() == base::Value::Type::DOUBLE) {
       return true;
     }
 
@@ -993,7 +993,7 @@
 
 Schema::Iterator Schema::GetPropertiesIterator() const {
   CHECK(valid());
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+  CHECK_EQ(base::Value::Type::DICTIONARY, type());
   return Iterator(storage_, storage_->properties(node_->extra));
 }
 
@@ -1007,7 +1007,7 @@
 
 Schema Schema::GetKnownProperty(const std::string& key) const {
   CHECK(valid());
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+  CHECK_EQ(base::Value::Type::DICTIONARY, type());
   const PropertiesNode* node = storage_->properties(node_->extra);
   const PropertyNode* begin = storage_->property(node->begin);
   const PropertyNode* end = storage_->property(node->end);
@@ -1019,7 +1019,7 @@
 
 Schema Schema::GetAdditionalProperties() const {
   CHECK(valid());
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+  CHECK_EQ(base::Value::Type::DICTIONARY, type());
   const PropertiesNode* node = storage_->properties(node_->extra);
   if (node->additional == kInvalid)
     return Schema();
@@ -1028,7 +1028,7 @@
 
 SchemaList Schema::GetPatternProperties(const std::string& key) const {
   CHECK(valid());
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+  CHECK_EQ(base::Value::Type::DICTIONARY, type());
   const PropertiesNode* node = storage_->properties(node_->extra);
   const PropertyNode* begin = storage_->property(node->end);
   const PropertyNode* end = storage_->property(node->pattern_end);
@@ -1071,7 +1071,7 @@
 
 Schema Schema::GetItems() const {
   CHECK(valid());
-  CHECK_EQ(base::Value::TYPE_LIST, type());
+  CHECK_EQ(base::Value::Type::LIST, type());
   if (node_->extra == kInvalid)
     return Schema();
   return Schema(storage_, storage_->schema(node_->extra));
diff --git a/components/policy/core/common/schema.h b/components/policy/core/common/schema.h
index ec761486..cd0f463 100644
--- a/components/policy/core/common/schema.h
+++ b/components/policy/core/common/schema.h
@@ -113,7 +113,7 @@
                  std::string* error,
                  bool* changed) const;
 
-  // Used to iterate over the known properties of TYPE_DICTIONARY schemas.
+  // Used to iterate over the known properties of Type::DICTIONARY schemas.
   class POLICY_EXPORT Iterator {
    public:
     Iterator(const scoped_refptr<const InternalStorage>& storage,
@@ -141,7 +141,7 @@
     const internal::PropertyNode* end_;
   };
 
-  // These methods should be called only if type() == TYPE_DICTIONARY,
+  // These methods should be called only if type() == Type::DICTIONARY,
   // otherwise invalid memory will be read. A CHECK is currently enforcing this.
 
   // Returns an iterator that goes over the named properties of this schema.
@@ -172,7 +172,7 @@
   SchemaList GetMatchingProperties(const std::string& key) const;
 
   // Returns the Schema for items of an array.
-  // This method should be called only if type() == TYPE_LIST,
+  // This method should be called only if type() == Type::LIST,
   // otherwise invalid memory will be read. A CHECK is currently enforcing this.
   Schema GetItems() const;
 
diff --git a/components/policy/core/common/schema_internal.h b/components/policy/core/common/schema_internal.h
index 586b3c3..503bbc5 100644
--- a/components/policy/core/common/schema_internal.h
+++ b/components/policy/core/common/schema_internal.h
@@ -20,15 +20,15 @@
   // The policy type.
   base::Value::Type type;
 
-  // If |type| is TYPE_DICTIONARY then |extra| is an offset into
+  // If |type| is Type::DICTIONARY then |extra| is an offset into
   // SchemaData::properties_nodes that indexes the PropertiesNode describing
   // the entries of this dictionary.
   //
-  // If |type| is TYPE_LIST then |extra| is an offset into
+  // If |type| is Type::LIST then |extra| is an offset into
   // SchemaData::schema_nodes that indexes the SchemaNode describing the items
   // of this list.
   //
-  // If |type| is TYPE_INTEGER or TYPE_STRING, and contains corresponding
+  // If |type| is Type::INTEGER or Type::STRING, and contains corresponding
   // restriction (enumeration of possible values, or range for integer), then
   // |extra| is an offset into SchemaData::restriction_nodes that indexes the
   // RestrictionNode describing the restriction on the value.
@@ -78,7 +78,7 @@
   int additional;
 };
 
-// Represents the restriction on TYPE_INTEGER or TYPE_STRING instance of
+// Represents the restriction on Type::INTEGER or Type::STRING instance of
 // base::Value.
 union POLICY_EXPORT RestrictionNode {
   // Offsets into SchemaData::int_enums or SchemaData::string_enums, the
diff --git a/components/policy/core/common/schema_unittest.cc b/components/policy/core/common/schema_unittest.cc
index ef4c25ee..932296d 100644
--- a/components/policy/core/common/schema_unittest.cc
+++ b/components/policy/core/common/schema_unittest.cc
@@ -272,11 +272,11 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   schema = schema.GetKnownProperty("sub");
   ASSERT_TRUE(schema.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   {
     Schema::Iterator it = schema.GetPropertiesIterator();
@@ -289,7 +289,7 @@
   }
 
   ASSERT_TRUE(schema.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
+  EXPECT_EQ(base::Value::Type::STRING, schema.type());
 
   // This test shouldn't leak nor use invalid memory.
 }
@@ -299,120 +299,120 @@
   Schema schema = Schema::Parse(kTestSchema, &error);
   ASSERT_TRUE(schema.valid()) << error;
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
   EXPECT_FALSE(schema.GetProperty("invalid").valid());
 
   Schema sub = schema.GetProperty("Boolean");
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, sub.type());
 
   sub = schema.GetProperty("Integer");
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type());
+  EXPECT_EQ(base::Value::Type::INTEGER, sub.type());
 
   sub = schema.GetProperty("Null");
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_NULL, sub.type());
+  EXPECT_EQ(base::Value::Type::NONE, sub.type());
 
   sub = schema.GetProperty("Number");
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type());
+  EXPECT_EQ(base::Value::Type::DOUBLE, sub.type());
 
   sub = schema.GetProperty("String");
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
 
   sub = schema.GetProperty("Array");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
   sub = sub.GetItems();
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
 
   sub = schema.GetProperty("ArrayOfObjects");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
   sub = sub.GetItems();
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, sub.type());
   Schema subsub = sub.GetProperty("one");
   ASSERT_TRUE(subsub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+  EXPECT_EQ(base::Value::Type::STRING, subsub.type());
   subsub = sub.GetProperty("two");
   ASSERT_TRUE(subsub.valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+  EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
   subsub = sub.GetProperty("invalid");
   EXPECT_FALSE(subsub.valid());
 
   sub = schema.GetProperty("ArrayOfArray");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
   sub = sub.GetItems();
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
   sub = sub.GetItems();
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
 
   sub = schema.GetProperty("Object");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
   subsub = sub.GetProperty("one");
   ASSERT_TRUE(subsub.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, subsub.type());
   subsub = sub.GetProperty("two");
   ASSERT_TRUE(subsub.valid());
-  EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+  EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
   subsub = sub.GetProperty("undeclared");
   ASSERT_TRUE(subsub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+  EXPECT_EQ(base::Value::Type::STRING, subsub.type());
 
   sub = schema.GetProperty("IntegerWithEnums");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
 
   sub = schema.GetProperty("IntegerWithEnumsGaps");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
 
   sub = schema.GetProperty("StringWithEnums");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_STRING, sub.type());
+  ASSERT_EQ(base::Value::Type::STRING, sub.type());
 
   sub = schema.GetProperty("IntegerWithRange");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_INTEGER, sub.type());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
 
   sub = schema.GetProperty("StringWithPattern");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_STRING, sub.type());
+  ASSERT_EQ(base::Value::Type::STRING, sub.type());
 
   sub = schema.GetProperty("ObjectWithPatternProperties");
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
 
   struct {
     const char* expected_key;
     base::Value::Type expected_type;
   } kExpectedProperties[] = {
-    { "Array",                       base::Value::TYPE_LIST },
-    { "ArrayOfArray",                base::Value::TYPE_LIST },
-    { "ArrayOfObjectOfArray",        base::Value::TYPE_LIST },
-    { "ArrayOfObjects",              base::Value::TYPE_LIST },
-    { "Boolean",                     base::Value::TYPE_BOOLEAN },
-    { "Integer",                     base::Value::TYPE_INTEGER },
-    { "IntegerWithEnums",            base::Value::TYPE_INTEGER },
-    { "IntegerWithEnumsGaps",        base::Value::TYPE_INTEGER },
-    { "IntegerWithRange",            base::Value::TYPE_INTEGER },
-    { "Null",                        base::Value::TYPE_NULL },
-    { "Number",                      base::Value::TYPE_DOUBLE },
-    { "Object",                      base::Value::TYPE_DICTIONARY },
-    { "ObjectOfArray",               base::Value::TYPE_DICTIONARY },
-    { "ObjectOfObject",              base::Value::TYPE_DICTIONARY },
-    { "ObjectWithPatternProperties", base::Value::TYPE_DICTIONARY },
-    { "String",                      base::Value::TYPE_STRING },
-    { "StringWithEnums",             base::Value::TYPE_STRING },
-    { "StringWithPattern",           base::Value::TYPE_STRING },
+    { "Array",                       base::Value::Type::LIST },
+    { "ArrayOfArray",                base::Value::Type::LIST },
+    { "ArrayOfObjectOfArray",        base::Value::Type::LIST },
+    { "ArrayOfObjects",              base::Value::Type::LIST },
+    { "Boolean",                     base::Value::Type::BOOLEAN },
+    { "Integer",                     base::Value::Type::INTEGER },
+    { "IntegerWithEnums",            base::Value::Type::INTEGER },
+    { "IntegerWithEnumsGaps",        base::Value::Type::INTEGER },
+    { "IntegerWithRange",            base::Value::Type::INTEGER },
+    { "Null",                        base::Value::Type::NONE },
+    { "Number",                      base::Value::Type::DOUBLE },
+    { "Object",                      base::Value::Type::DICTIONARY },
+    { "ObjectOfArray",               base::Value::Type::DICTIONARY },
+    { "ObjectOfObject",              base::Value::Type::DICTIONARY },
+    { "ObjectWithPatternProperties", base::Value::Type::DICTIONARY },
+    { "String",                      base::Value::Type::STRING },
+    { "StringWithEnums",             base::Value::Type::STRING },
+    { "StringWithPattern",           base::Value::Type::STRING },
   };
   Schema::Iterator it = schema.GetPropertiesIterator();
   for (size_t i = 0; i < arraysize(kExpectedProperties); ++i) {
@@ -430,7 +430,7 @@
 
   Schema schema = Schema::Parse("{ \"type\": \"object\" }", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   // This empty schema should never find named properties.
   EXPECT_FALSE(schema.GetKnownProperty("").valid());
@@ -445,7 +445,7 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   EXPECT_FALSE(schema.GetKnownProperty("").valid());
   EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
@@ -463,7 +463,7 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   EXPECT_FALSE(schema.GetKnownProperty("").valid());
   EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
@@ -472,11 +472,11 @@
     const char* expected_key;
     base::Value::Type expected_type;
   } kExpectedKeys[] = {
-    { "aa",     base::Value::TYPE_BOOLEAN },
-    { "ab",     base::Value::TYPE_DOUBLE },
-    { "aba",    base::Value::TYPE_INTEGER },
-    { "abab",   base::Value::TYPE_STRING },
-    { "bb",     base::Value::TYPE_NULL },
+    { "aa",     base::Value::Type::BOOLEAN },
+    { "ab",     base::Value::Type::DOUBLE },
+    { "aba",    base::Value::Type::INTEGER },
+    { "abab",   base::Value::Type::STRING },
+    { "bb",     base::Value::Type::NONE },
   };
   for (size_t i = 0; i < arraysize(kExpectedKeys); ++i) {
     Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
@@ -487,17 +487,17 @@
 
 TEST(SchemaTest, Wrap) {
   const internal::SchemaNode kSchemas[] = {
-    { base::Value::TYPE_DICTIONARY,   0 },    //  0: root node
-    { base::Value::TYPE_BOOLEAN,      -1 },   //  1
-    { base::Value::TYPE_INTEGER,      -1 },   //  2
-    { base::Value::TYPE_DOUBLE,       -1 },   //  3
-    { base::Value::TYPE_STRING,       -1 },   //  4
-    { base::Value::TYPE_LIST,         4 },    //  5: list of strings.
-    { base::Value::TYPE_LIST,         5 },    //  6: list of lists of strings.
-    { base::Value::TYPE_INTEGER,      0 },    //  7: integer enumerations.
-    { base::Value::TYPE_INTEGER,      1 },    //  8: ranged integers.
-    { base::Value::TYPE_STRING,       2 },    //  9: string enumerations.
-    { base::Value::TYPE_STRING,       3 },    // 10: string with pattern.
+    { base::Value::Type::DICTIONARY,   0 },    //  0: root node
+    { base::Value::Type::BOOLEAN,      -1 },   //  1
+    { base::Value::Type::INTEGER,      -1 },   //  2
+    { base::Value::Type::DOUBLE,       -1 },   //  3
+    { base::Value::Type::STRING,       -1 },   //  4
+    { base::Value::Type::LIST,         4 },    //  5: list of strings.
+    { base::Value::Type::LIST,         5 },    //  6: list of lists of strings.
+    { base::Value::Type::INTEGER,      0 },    //  7: integer enumerations.
+    { base::Value::Type::INTEGER,      1 },    //  8: ranged integers.
+    { base::Value::Type::STRING,       2 },    //  9: string enumerations.
+    { base::Value::Type::STRING,       3 },    // 10: string with pattern.
   };
 
   const internal::PropertyNode kPropertyNodes[] = {
@@ -546,21 +546,21 @@
 
   Schema schema = Schema::Wrap(&kData);
   ASSERT_TRUE(schema.valid());
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   struct {
     const char* key;
     base::Value::Type type;
   } kExpectedProperties[] = {
-    { "Boolean", base::Value::TYPE_BOOLEAN },
-    { "Integer", base::Value::TYPE_INTEGER },
-    { "Number", base::Value::TYPE_DOUBLE },
-    { "String", base::Value::TYPE_STRING },
-    { "List", base::Value::TYPE_LIST },
-    { "IntEnum", base::Value::TYPE_INTEGER },
-    { "RangedInt", base::Value::TYPE_INTEGER },
-    { "StrEnum", base::Value::TYPE_STRING },
-    { "StrPat", base::Value::TYPE_STRING },
+    { "Boolean", base::Value::Type::BOOLEAN },
+    { "Integer", base::Value::Type::INTEGER },
+    { "Number", base::Value::Type::DOUBLE },
+    { "String", base::Value::Type::STRING },
+    { "List", base::Value::Type::LIST },
+    { "IntEnum", base::Value::Type::INTEGER },
+    { "RangedInt", base::Value::Type::INTEGER },
+    { "StrEnum", base::Value::Type::STRING },
+    { "StrPat", base::Value::Type::STRING },
   };
 
   Schema::Iterator it = schema.GetPropertiesIterator();
@@ -571,10 +571,10 @@
     ASSERT_TRUE(sub.valid());
     EXPECT_EQ(kExpectedProperties[i].type, sub.type());
 
-    if (sub.type() == base::Value::TYPE_LIST) {
+    if (sub.type() == base::Value::Type::LIST) {
       Schema items = sub.GetItems();
       ASSERT_TRUE(items.valid());
-      EXPECT_EQ(base::Value::TYPE_STRING, items.type());
+      EXPECT_EQ(base::Value::Type::STRING, items.type());
     }
 
     it.Advance();
@@ -583,19 +583,19 @@
 
   Schema sub = schema.GetAdditionalProperties();
   ASSERT_TRUE(sub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
   Schema subsub = sub.GetItems();
   ASSERT_TRUE(subsub.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, subsub.type());
+  ASSERT_EQ(base::Value::Type::LIST, subsub.type());
   Schema subsubsub = subsub.GetItems();
   ASSERT_TRUE(subsubsub.valid());
-  ASSERT_EQ(base::Value::TYPE_STRING, subsubsub.type());
+  ASSERT_EQ(base::Value::Type::STRING, subsubsub.type());
 
   SchemaList schema_list = schema.GetPatternProperties("barr");
   ASSERT_EQ(1u, schema_list.size());
   sub = schema_list[0];
   ASSERT_TRUE(sub.valid());
-  EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
 
   EXPECT_TRUE(schema.GetPatternProperties("ba").empty());
   EXPECT_TRUE(schema.GetPatternProperties("bar+$").empty());
@@ -1001,29 +1001,29 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   Schema parent = schema.GetKnownProperty("bookmarks");
   ASSERT_TRUE(parent.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, parent.type());
+  ASSERT_EQ(base::Value::Type::LIST, parent.type());
 
   // Check the recursive type a number of times.
   for (int i = 0; i < 10; ++i) {
     Schema items = parent.GetItems();
     ASSERT_TRUE(items.valid());
-    ASSERT_EQ(base::Value::TYPE_DICTIONARY, items.type());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, items.type());
 
     Schema prop = items.GetKnownProperty("name");
     ASSERT_TRUE(prop.valid());
-    ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
+    ASSERT_EQ(base::Value::Type::STRING, prop.type());
 
     prop = items.GetKnownProperty("url");
     ASSERT_TRUE(prop.valid());
-    ASSERT_EQ(base::Value::TYPE_STRING, prop.type());
+    ASSERT_EQ(base::Value::Type::STRING, prop.type());
 
     prop = items.GetKnownProperty("children");
     ASSERT_TRUE(prop.valid());
-    ASSERT_EQ(base::Value::TYPE_LIST, prop.type());
+    ASSERT_EQ(base::Value::Type::LIST, prop.type());
 
     parent = prop;
   }
@@ -1051,12 +1051,12 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   for (char c = 'a'; c <= 'i'; ++c) {
     Schema sub = schema.GetKnownProperty(std::string(1, c));
     ASSERT_TRUE(sub.valid()) << c;
-    ASSERT_EQ(base::Value::TYPE_BOOLEAN, sub.type()) << c;
+    ASSERT_EQ(base::Value::Type::BOOLEAN, sub.type()) << c;
   }
 }
 
@@ -1080,23 +1080,23 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   Schema policy = schema.GetKnownProperty("policy");
   ASSERT_TRUE(policy.valid());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, policy.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, policy.type());
 
   Schema foo = policy.GetKnownProperty("foo");
   ASSERT_TRUE(foo.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
 
   Schema additional = policy.GetAdditionalProperties();
   ASSERT_TRUE(additional.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, additional.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, additional.type());
 
   Schema x = policy.GetProperty("x");
   ASSERT_TRUE(x.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, x.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, x.type());
 }
 
 TEST(SchemaTest, ItemsReference) {
@@ -1117,19 +1117,19 @@
       "  }"
       "}", &error);
   ASSERT_TRUE(schema.valid()) << error;
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
 
   Schema foo = schema.GetKnownProperty("foo");
   ASSERT_TRUE(foo.valid());
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, foo.type());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
 
   Schema list = schema.GetKnownProperty("list");
   ASSERT_TRUE(list.valid());
-  ASSERT_EQ(base::Value::TYPE_LIST, list.type());
+  ASSERT_EQ(base::Value::Type::LIST, list.type());
 
   Schema items = list.GetItems();
   ASSERT_TRUE(items.valid());
-  ASSERT_EQ(base::Value::TYPE_BOOLEAN, items.type());
+  ASSERT_EQ(base::Value::Type::BOOLEAN, items.type());
 }
 
 TEST(SchemaTest, EnumerationRestriction) {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index ecdaa153..73fecaa 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -7506,7 +7506,7 @@
           'id': 200,
           'caption': '''Enable remote attestation for the user''',
           'tags': [],
-          'desc': '''If true, the user can use the hardware on Chrome devices to remote attest its identity to the privacy CA via the Enterprise Platform Keys API chrome.enterprise.platformKeys.challengeUserKey().
+          'desc': '''If true, the user can use the hardware on Chrome devices to remote attest its identity to the privacy CA via the <ph name="ENTERPRISE_PLATFORM_KEYS_API">Enterprise Platform Keys API</ph> using <ph name="CHALLENGE_USER_KEY_FUNCTION">chrome.enterprise.platformKeys.challengeUserKey()</ph>.
 
           If it is set to false, or if it is not set, calls to the API will fail with an error code.''',
         },
@@ -7526,7 +7526,7 @@
           'id': 201,
           'caption': '''Extensions allowed to to use the remote attestation API''',
           'tags': [],
-          'desc': '''This policy specifies the allowed extensions to use Enterprise Platform Keys API chrome.enterprise.platformKeys.challengeUserKey() for remote attestation. Extensions must be added to this list to use the API.
+          'desc': '''This policy specifies the allowed extensions to use the <ph name="ENTERPRISE_PLATFORM_KEYS_API">Enterprise Platform Keys API</ph> function <ph name="CHALLENGE_USER_KEY_FUNCTION">chrome.enterprise.platformKeys.challengeUserKey()</ph> for remote attestation. Extensions must be added to this list to use the API.
 
           If an extension is not in the list, or the list is not set, the call to the API will fail with an error code.''',
         },
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 2bc7593d..4da6696 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -38,23 +38,23 @@
   # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type
   # that can also be used to represent lists of other JSON objects.
   TYPE_MAP = {
-    'dict':             ('TYPE_DICTIONARY',   'string',       'String',
+    'dict':             ('Type::DICTIONARY',  'string',       'String',
                         'string'),
     'external':         ('TYPE_EXTERNAL',     'string',       'String',
                         'invalid'),
-    'int':              ('TYPE_INTEGER',      'int64',        'Integer',
+    'int':              ('Type::INTEGER',     'int64',        'Integer',
                         'integer'),
-    'int-enum':         ('TYPE_INTEGER',      'int64',        'Integer',
+    'int-enum':         ('Type::INTEGER',     'int64',        'Integer',
                         'choice'),
-    'list':             ('TYPE_LIST',         'StringList',   'StringList',
+    'list':             ('Type::LIST',        'StringList',   'StringList',
                         'string'),
-    'main':             ('TYPE_BOOLEAN',      'bool',         'Boolean',
+    'main':             ('Type::BOOLEAN',     'bool',         'Boolean',
                         'bool'),
-    'string':           ('TYPE_STRING',       'string',       'String',
+    'string':           ('Type::STRING',      'string',       'String',
                         'string'),
-    'string-enum':      ('TYPE_STRING',       'string',       'String',
+    'string-enum':      ('Type::STRING',      'string',       'String',
                         'choice'),
-    'string-enum-list': ('TYPE_LIST',         'StringList',   'StringList',
+    'string-enum-list': ('Type::LIST',        'StringList',   'StringList',
                         'multi-select'),
   }
 
@@ -348,11 +348,11 @@
 
 # A mapping of the simple schema types to base::Value::Types.
 SIMPLE_SCHEMA_NAME_MAP = {
-  'boolean': 'TYPE_BOOLEAN',
-  'integer': 'TYPE_INTEGER',
-  'null'   : 'TYPE_NULL',
-  'number' : 'TYPE_DOUBLE',
-  'string' : 'TYPE_STRING',
+  'boolean': 'Type::BOOLEAN',
+  'integer': 'Type::INTEGER',
+  'null'   : 'Type::NONE',
+  'number' : 'Type::DOUBLE',
+  'string' : 'Type::STRING',
 }
 
 class SchemaNodesGenerator:
@@ -412,7 +412,7 @@
   def GetStringList(self):
     if self.stringlist_type == None:
       self.stringlist_type = self.AppendSchema(
-          'TYPE_LIST',
+          'Type::LIST',
           self.GetSimpleType('string'),
           'simple type: stringlist')
     return self.stringlist_type
@@ -431,12 +431,12 @@
     possible_values = schema['enum']
     if self.IsConsecutiveInterval(possible_values):
       index = self.AppendRestriction(max(possible_values), min(possible_values))
-      return self.AppendSchema('TYPE_INTEGER', index,
+      return self.AppendSchema('Type::INTEGER', index,
           'integer with enumeration restriction (use range instead): %s' % name)
     offset_begin = len(self.int_enums)
     self.int_enums += possible_values
     offset_end = len(self.int_enums)
-    return self.AppendSchema('TYPE_INTEGER',
+    return self.AppendSchema('Type::INTEGER',
         self.AppendRestriction(offset_begin, offset_end),
         'integer with enumeration restriction: %s' % name)
 
@@ -445,7 +445,7 @@
     offset_begin = len(self.string_enums)
     self.string_enums += schema['enum']
     offset_end = len(self.string_enums)
-    return self.AppendSchema('TYPE_STRING',
+    return self.AppendSchema('Type::STRING',
         self.AppendRestriction(offset_begin, offset_end),
         'string with enumeration restriction: %s' % name)
 
@@ -469,7 +469,7 @@
     re.compile(pattern)
     index = len(self.string_enums);
     self.string_enums.append(pattern);
-    return self.AppendSchema('TYPE_STRING',
+    return self.AppendSchema('Type::STRING',
         self.AppendRestriction(index, index),
         'string with pattern restriction: %s' % name);
 
@@ -488,7 +488,7 @@
     index = self.AppendRestriction(
         str(max_value) if max_value_set else 'INT_MAX',
         str(min_value) if min_value_set else 'INT_MIN')
-    return self.AppendSchema('TYPE_INTEGER',
+    return self.AppendSchema('Type::INTEGER',
         index,
         'integer with ranged restriction: %s' % name)
 
@@ -519,13 +519,13 @@
       # The 'type' may be missing if the schema has a '$ref' attribute.
       if schema['items'].get('type', '') == 'string':
         return self.GetStringList()
-      return self.AppendSchema('TYPE_LIST',
+      return self.AppendSchema('Type::LIST',
           self.GenerateAndCollectID(schema['items'], 'items of ' + name))
     elif schema['type'] == 'object':
       # Reserve an index first, so that dictionaries come before their
       # properties. This makes sure that the root node is the first in the
       # SchemaNodes array.
-      index = self.AppendSchema('TYPE_DICTIONARY', -1)
+      index = self.AppendSchema('Type::DICTIONARY', -1)
 
       if 'additionalProperties' in schema:
         additionalProperties = self.GenerateAndCollectID(
@@ -563,7 +563,7 @@
           additionalProperties, name))
 
       # Set the right data at |index| now.
-      self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name)
+      self.schema_nodes[index] = ('Type::DICTIONARY', extra, name)
       return index
     else:
       assert False
@@ -737,13 +737,13 @@
 
   for policy in policies:
     if policy.has_enterprise_default:
-      if policy.policy_type == 'TYPE_BOOLEAN':
+      if policy.policy_type == 'Type::BOOLEAN':
         creation_expression = 'new base::FundamentalValue(%s)' %\
                               ('true' if policy.enterprise_default else 'false')
-      elif policy.policy_type == 'TYPE_INTEGER':
+      elif policy.policy_type == 'Type::INTEGER':
         creation_expression = 'new base::FundamentalValue(%s)' %\
                               policy.enterprise_default
-      elif policy.policy_type == 'TYPE_STRING':
+      elif policy.policy_type == 'Type::STRING':
         creation_expression = 'new base::StringValue("%s")' %\
                               policy.enterprise_default
       else:
@@ -955,7 +955,7 @@
     _OutputComment(f, '\nValid values:')
     for item in policy.items:
       _OutputComment(f, '  %s: %s' % (str(item.value), item.caption))
-  if policy.policy_type == 'TYPE_DICTIONARY':
+  if policy.policy_type == 'Type::DICTIONARY':
     _OutputComment(f, '\nValue schema:\n%s' %
                    json.dumps(policy.schema, sort_keys=True, indent=4,
                               separators=(',', ': ')))
@@ -1073,15 +1073,15 @@
 
 
 def _CreateValue(type, arg):
-  if type == 'TYPE_BOOLEAN':
+  if type == 'Type::BOOLEAN':
     return 'new base::FundamentalValue(%s)' % arg
-  elif type == 'TYPE_INTEGER':
+  elif type == 'Type::INTEGER':
     return 'DecodeIntegerValue(%s)' % arg
-  elif type == 'TYPE_STRING':
+  elif type == 'Type::STRING':
     return 'new base::StringValue(%s)' % arg
-  elif type == 'TYPE_LIST':
+  elif type == 'Type::LIST':
     return 'DecodeStringList(%s)' % arg
-  elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL':
+  elif type == 'Type::DICTIONARY' or type == 'TYPE_EXTERNAL':
     return 'DecodeJson(%s)' % arg
   else:
     raise NotImplementedError('Unknown type %s' % type)
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 8213293..bcaef1d 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -338,7 +338,7 @@
     Cloud
   </message>
   <message name="IDS_POLICY_SOURCE_ACTIVE_DIRECTORY" desc="Indicates that the policy originates from Active Directory.">
-    Microsoft® Active Directory®
+    <ph name="MICROSOFT_ACTIVE_DIRECTORY">Microsoft® Active Directory®</ph>
   </message>
   <message name="IDS_POLICY_SOURCE_PLATFORM" desc="Indicates that the policy is obtained from the local OS.">
     Platform
diff --git a/components/prefs/json_pref_store.cc b/components/prefs/json_pref_store.cc
index 4076433d..026606a7 100644
--- a/components/prefs/json_pref_store.cc
+++ b/components/prefs/json_pref_store.cc
@@ -89,7 +89,7 @@
                            : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
     }
   }
-  if (!value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value->IsType(base::Value::Type::DICTIONARY))
     return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
   return PersistentPrefStore::PREF_READ_ERROR_NONE;
 }
diff --git a/components/prefs/overlay_user_pref_store_unittest.cc b/components/prefs/overlay_user_pref_store_unittest.cc
index ae32081..d722aea 100644
--- a/components/prefs/overlay_user_pref_store_unittest.cc
+++ b/components/prefs/overlay_user_pref_store_unittest.cc
@@ -137,19 +137,19 @@
   Value* modify = NULL;
   EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify));
   ASSERT_TRUE(modify);
-  ASSERT_TRUE(modify->IsType(Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(modify->IsType(Value::Type::DICTIONARY));
   static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42);
 
   Value* original_in_underlay = NULL;
   EXPECT_TRUE(underlay_->GetMutableValue(overlay_key, &original_in_underlay));
   ASSERT_TRUE(original_in_underlay);
-  ASSERT_TRUE(original_in_underlay->IsType(Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(original_in_underlay->IsType(Value::Type::DICTIONARY));
   EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
 
   Value* modified = NULL;
   EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modified));
   ASSERT_TRUE(modified);
-  ASSERT_TRUE(modified->IsType(Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(modified->IsType(Value::Type::DICTIONARY));
   EXPECT_TRUE(Value::Equals(modify, static_cast<DictionaryValue*>(modified)));
 }
 
diff --git a/components/prefs/pref_member.cc b/components/prefs/pref_member.cc
index 53c56d5f..8ed13ee5 100644
--- a/components/prefs/pref_member.cc
+++ b/components/prefs/pref_member.cc
@@ -131,7 +131,7 @@
 
 bool PrefMemberVectorStringUpdate(const base::Value& value,
                                   std::vector<std::string>* string_vector) {
-  if (!value.IsType(base::Value::TYPE_LIST))
+  if (!value.IsType(base::Value::Type::LIST))
     return false;
   const base::ListValue* list = static_cast<const base::ListValue*>(&value);
 
diff --git a/components/prefs/pref_registry.cc b/components/prefs/pref_registry.cc
index c42e77f..63b5e6a 100644
--- a/components/prefs/pref_registry.cc
+++ b/components/prefs/pref_registry.cc
@@ -54,8 +54,8 @@
                                       base::Value* default_value,
                                       uint32_t flags) {
   base::Value::Type orig_type = default_value->GetType();
-  DCHECK(orig_type != base::Value::TYPE_NULL &&
-         orig_type != base::Value::TYPE_BINARY) <<
+  DCHECK(orig_type != base::Value::Type::NONE &&
+         orig_type != base::Value::Type::BINARY) <<
          "invalid preference type: " << orig_type;
   DCHECK(!defaults_->GetValue(path, NULL)) <<
       "Trying to register a previously registered pref: " << path;
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc
index e909d58..d0274b1 100644
--- a/components/prefs/pref_service.cc
+++ b/components/prefs/pref_service.cc
@@ -288,7 +288,7 @@
     NOTREACHED() << "Trying to read an unregistered pref: " << path;
     return NULL;
   }
-  if (value->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (value->GetType() != base::Value::Type::DICTIONARY) {
     NOTREACHED();
     return NULL;
   }
@@ -345,7 +345,7 @@
     NOTREACHED() << "Trying to read an unregistered pref: " << path;
     return NULL;
   }
-  if (value->GetType() != base::Value::TYPE_LIST) {
+  if (value->GetType() != base::Value::Type::LIST) {
     NOTREACHED();
     return NULL;
   }
@@ -453,7 +453,8 @@
 
 base::Value* PrefService::GetMutableUserPref(const std::string& path,
                                              base::Value::Type type) {
-  CHECK(type == base::Value::TYPE_DICTIONARY || type == base::Value::TYPE_LIST);
+  CHECK(type == base::Value::Type::DICTIONARY ||
+        type == base::Value::Type::LIST);
   DCHECK(CalledOnValidThread());
 
   const Preference* pref = FindPreference(path);
@@ -471,9 +472,9 @@
   base::Value* value = NULL;
   if (!user_pref_store_->GetMutableValue(path, &value) ||
       !value->IsType(type)) {
-    if (type == base::Value::TYPE_DICTIONARY) {
+    if (type == base::Value::Type::DICTIONARY) {
       value = new base::DictionaryValue;
-    } else if (type == base::Value::TYPE_LIST) {
+    } else if (type == base::Value::Type::LIST) {
       value = new base::ListValue;
     } else {
       NOTREACHED();
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 82de7c4..d26e118 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -222,7 +222,7 @@
 
   // Int64 helper methods that actually store the given value as a string.
   // Note that if obtaining the named value via GetDictionary or GetList, the
-  // Value type will be TYPE_STRING.
+  // Value type will be Type::STRING.
   void SetInt64(const std::string& path, int64_t value);
   int64_t GetInt64(const std::string& path) const;
 
@@ -365,7 +365,7 @@
   // This will create a dictionary or list if one does not exist in the user
   // pref store. This method returns NULL only if you're requesting an
   // unregistered pref or a non-dict/non-list pref.
-  // |type| may only be Values::TYPE_DICTIONARY or Values::TYPE_LIST and
+  // |type| may only be Values::Type::DICTIONARY or Values::Type::LIST and
   // |path| must point to a registered preference of type |type|.
   // Ownership of the returned value remains at the user pref store.
   base::Value* GetMutableUserPref(const std::string& path,
diff --git a/components/prefs/pref_service_unittest.cc b/components/prefs/pref_service_unittest.cc
index 373c01b..5229b97e 100644
--- a/components/prefs/pref_service_unittest.cc
+++ b/components/prefs/pref_service_unittest.cc
@@ -148,7 +148,7 @@
   ASSERT_TRUE(pref);
   const base::Value* value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   int actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kTestValue, actual_int_value);
@@ -168,7 +168,7 @@
   // Check that GetValue() returns the default value.
   const base::Value* value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   int actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kDefaultValue, actual_int_value);
@@ -183,7 +183,7 @@
   // Check that GetValue() returns the user-set value.
   value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kUserValue, actual_int_value);
@@ -199,7 +199,7 @@
   // Check that GetValue() returns the user-set value.
   value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kUserValue, actual_int_value);
@@ -207,7 +207,7 @@
   // Check that GetRecommendedValue() returns the recommended value.
   value = pref->GetRecommendedValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -218,7 +218,7 @@
   // Check that GetValue() returns the recommended value.
   value = pref->GetValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -226,7 +226,7 @@
   // Check that GetRecommendedValue() returns the recommended value.
   value = pref->GetRecommendedValue();
   ASSERT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, value->GetType());
   actual_int_value = -1;
   EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
   EXPECT_EQ(kRecommendedValue, actual_int_value);
@@ -321,7 +321,7 @@
     SCOPED_TRACE("Currently testing pref with name: " +
                  std::string(entry.pref_name));
 
-    prefs->GetMutableUserPref(entry.pref_name, base::Value::TYPE_DICTIONARY);
+    prefs->GetMutableUserPref(entry.pref_name, base::Value::Type::DICTIONARY);
     EXPECT_TRUE(flag_checker->last_write_flags_set());
     EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
 
diff --git a/components/prefs/pref_value_store_unittest.cc b/components/prefs/pref_value_store_unittest.cc
index b1656a4..23fc686e 100644
--- a/components/prefs/pref_value_store_unittest.cc
+++ b/components/prefs/pref_value_store_unittest.cc
@@ -271,7 +271,7 @@
   // Test getting a managed value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   std::string actual_str_value;
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(managed_pref::kManagedValue, actual_str_value);
@@ -279,35 +279,35 @@
   // Test getting a supervised user value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref,
-                                          base::Value::TYPE_STRING, &value));
+                                          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,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value);
 
   // Test getting a command-line value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value);
 
   // Test getting a user-set value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(user_pref::kUserValue, actual_str_value);
 
   // Test getting a user set value overwriting a recommended value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kRecommendedValue,
             actual_str_value);
@@ -315,7 +315,7 @@
   // Test getting a default value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref,
-                                          base::Value::TYPE_STRING, &value));
+                                          base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(default_pref::kDefaultValue, actual_str_value);
 
@@ -324,7 +324,7 @@
   base::FundamentalValue tmp_dummy_value(true);
   value = &tmp_dummy_value;
   ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref,
-                                           base::Value::TYPE_STRING, &value));
+                                           base::Value::Type::STRING, &value));
   ASSERT_FALSE(value);
 }
 
@@ -339,7 +339,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kManagedPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   std::string actual_str_value;
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value);
@@ -348,7 +348,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kSupervisedUserPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value);
 
@@ -356,7 +356,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kExtensionPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value);
 
@@ -364,7 +364,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kCommandLinePref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value);
 
@@ -372,7 +372,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kUserPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kUserValue, actual_str_value);
 
@@ -380,7 +380,7 @@
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
       prefs::kRecommendedPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kRecommendedValue,
             actual_str_value);
@@ -390,7 +390,7 @@
   value = &tmp_dummy_value;
   ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
       prefs::kDefaultPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   ASSERT_FALSE(value);
 
   // Test getting a preference value that the |PrefValueStore|
@@ -398,7 +398,7 @@
   value = &tmp_dummy_value;
   ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
       prefs::kMissingPref,
-      base::Value::TYPE_STRING, &value));
+      base::Value::Type::STRING, &value));
   ASSERT_FALSE(value);
 }
 
diff --git a/components/prefs/scoped_user_pref_update.h b/components/prefs/scoped_user_pref_update.h
index bfcdb3f5..29859d65 100644
--- a/components/prefs/scoped_user_pref_update.h
+++ b/components/prefs/scoped_user_pref_update.h
@@ -101,9 +101,9 @@
 };
 
 typedef ScopedUserPrefUpdate<base::DictionaryValue,
-                             base::Value::TYPE_DICTIONARY>
+                             base::Value::Type::DICTIONARY>
     DictionaryPrefUpdate;
-typedef ScopedUserPrefUpdate<base::ListValue, base::Value::TYPE_LIST>
+typedef ScopedUserPrefUpdate<base::ListValue, base::Value::Type::LIST>
     ListPrefUpdate;
 
 #endif  // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn
index 64e0acd..cb1261b 100644
--- a/components/proximity_auth/BUILD.gn
+++ b/components/proximity_auth/BUILD.gn
@@ -46,8 +46,6 @@
     "proximity_monitor_impl.cc",
     "proximity_monitor_impl.h",
     "proximity_monitor_observer.h",
-    "remote_device.cc",
-    "remote_device.h",
     "remote_device_life_cycle.h",
     "remote_device_life_cycle_impl.cc",
     "remote_device_life_cycle_impl.h",
diff --git a/components/proximity_auth/ble/BUILD.gn b/components/proximity_auth/ble/BUILD.gn
index 3cd3dc4..83755066 100644
--- a/components/proximity_auth/ble/BUILD.gn
+++ b/components/proximity_auth/ble/BUILD.gn
@@ -30,6 +30,7 @@
 
   deps = [
     "//base",
+    "//components/cryptauth",
     "//components/prefs",
     "//components/proximity_auth/logging",
 
@@ -63,6 +64,7 @@
   deps = [
     ":ble",
     "//base/test:test_support",
+    "//components/cryptauth",
     "//components/prefs:test_support",
     "//components/proximity_auth:test_support",
     "//device/bluetooth:mocks",
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
index df14192..cd1cb50 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
@@ -53,7 +53,7 @@
 }  // namespace
 
 BluetoothLowEnergyConnection::BluetoothLowEnergyConnection(
-    const RemoteDevice& device,
+    const cryptauth::RemoteDevice& device,
     scoped_refptr<device::BluetoothAdapter> adapter,
     const BluetoothUUID remote_service_uuid,
     BluetoothThrottler* bluetooth_throttler,
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.h b/components/proximity_auth/ble/bluetooth_low_energy_connection.h
index dec8d41..eaa766c835 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection.h
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.h
@@ -87,12 +87,11 @@
   // initaalized and ready. The GATT connection may alreaady be established and
   // pass through |gatt_connection|. A subsequent call to Connect() must be
   // made.
-  BluetoothLowEnergyConnection(
-      const RemoteDevice& remote_device,
-      scoped_refptr<device::BluetoothAdapter> adapter,
-      const device::BluetoothUUID remote_service_uuid,
-      BluetoothThrottler* bluetooth_throttler,
-      int max_number_of_write_attempts);
+  BluetoothLowEnergyConnection(const cryptauth::RemoteDevice& remote_device,
+                               scoped_refptr<device::BluetoothAdapter> adapter,
+                               const device::BluetoothUUID remote_service_uuid,
+                               BluetoothThrottler* bluetooth_throttler,
+                               int max_number_of_write_attempts);
 
   ~BluetoothLowEnergyConnection() override;
 
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
index d4c6a7a9..1e7de73a 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
@@ -37,7 +37,7 @@
 class BluetoothThrottler;
 
 BluetoothLowEnergyConnectionFinder::BluetoothLowEnergyConnectionFinder(
-    const RemoteDevice remote_device,
+    const cryptauth::RemoteDevice remote_device,
     const std::string& remote_service_uuid,
     FinderStrategy finder_strategy,
     const BluetoothLowEnergyDeviceWhitelist* device_whitelist,
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h
index 806308b2..426348a 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h
@@ -13,10 +13,10 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/connection.h"
 #include "components/proximity_auth/connection_finder.h"
 #include "components/proximity_auth/connection_observer.h"
-#include "components/proximity_auth/remote_device.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_session.h"
@@ -53,7 +53,7 @@
   // TODO(sacomoto): Remove |device_whitelist| when ProximityAuthBleSystem is
   // not needed anymore.
   BluetoothLowEnergyConnectionFinder(
-      const RemoteDevice remote_device,
+      const cryptauth::RemoteDevice remote_device,
       const std::string& remote_service_uuid,
       const FinderStrategy finder_strategy,
       const BluetoothLowEnergyDeviceWhitelist* device_whitelist,
@@ -125,7 +125,7 @@
   // The remote BLE device being searched. It maybe empty, in this case the
   // remote device should advertise |remote_service_uuid_| and
   // |advertised_name_|.
-  RemoteDevice remote_device_;
+  cryptauth::RemoteDevice remote_device_;
 
   // The uuid of the service it looks for to establish a GattConnection.
   device::BluetoothUUID remote_service_uuid_;
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc
index 180cb37..845af2c 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder_unittest.cc
@@ -16,10 +16,10 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
 #include "components/proximity_auth/fake_connection.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_uuid.h"
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_unittest.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_unittest.cc
index 537dc75..87c814fe 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection_unittest.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_unittest.cc
@@ -16,11 +16,11 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/ble/bluetooth_low_energy_characteristics_finder.h"
 #include "components/proximity_auth/bluetooth_throttler.h"
 #include "components/proximity_auth/connection_finder.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
@@ -87,7 +87,7 @@
 class MockBluetoothLowEnergyConnection : public BluetoothLowEnergyConnection {
  public:
   MockBluetoothLowEnergyConnection(
-      const RemoteDevice& remote_device,
+      const cryptauth::RemoteDevice& remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
       BluetoothThrottler* bluetooth_throttler,
@@ -354,7 +354,7 @@
 
  protected:
   scoped_refptr<device::MockBluetoothAdapter> adapter_;
-  RemoteDevice remote_device_;
+  cryptauth::RemoteDevice remote_device_;
   device::BluetoothUUID service_uuid_;
   device::BluetoothUUID to_peripheral_char_uuid_;
   device::BluetoothUUID from_peripheral_char_uuid_;
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc
index da1d3609..e87a224f 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.cc
@@ -55,7 +55,7 @@
   for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd();
        it.Advance()) {
     std::string value_string;
-    DCHECK(it.value().IsType(base::Value::TYPE_STRING));
+    DCHECK(it.value().IsType(base::Value::Type::STRING));
     if (it.value().GetAsString(&value_string) && value_string == public_key)
       return it.key();
   }
@@ -69,7 +69,7 @@
   for (base::DictionaryValue::Iterator it(*device_whitelist); !it.IsAtEnd();
        it.Advance()) {
     std::string value_string;
-    DCHECK(it.value().IsType(base::Value::TYPE_STRING));
+    DCHECK(it.value().IsType(base::Value::Type::STRING));
     it.value().GetAsString(&value_string);
     public_keys.push_back(value_string);
   }
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.cc b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.cc
index 3fde272..6f96df20b 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.cc
@@ -40,7 +40,7 @@
 
 BluetoothLowEnergyWeaveClientConnection::
     BluetoothLowEnergyWeaveClientConnection(
-        const RemoteDevice& device,
+        const cryptauth::RemoteDevice& device,
         scoped_refptr<device::BluetoothAdapter> adapter,
         const BluetoothUUID remote_service_uuid,
         BluetoothThrottler* bluetooth_throttler,
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.h b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.h
index bda1ee7..681fe3b 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.h
+++ b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection.h
@@ -88,7 +88,7 @@
   // pass through |gatt_connection|. A subsequent call to Connect() must be
   // made.
   BluetoothLowEnergyWeaveClientConnection(
-      const RemoteDevice& remote_device,
+      const cryptauth::RemoteDevice& remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
       BluetoothThrottler* bluetooth_throttler,
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
index 509a225..26837fa 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
@@ -14,11 +14,11 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/bluetooth_throttler.h"
 #include "components/proximity_auth/connection_finder.h"
 #include "components/proximity_auth/connection_observer.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -301,7 +301,7 @@
     : public BluetoothLowEnergyWeaveClientConnection {
  public:
   TestBluetoothLowEnergyWeaveClientConnection(
-      const RemoteDevice& remote_device,
+      const cryptauth::RemoteDevice& remote_device,
       scoped_refptr<device::BluetoothAdapter> adapter,
       const device::BluetoothUUID remote_service_uuid,
       BluetoothThrottler* bluetooth_throttler,
@@ -579,7 +579,7 @@
 
  protected:
   scoped_refptr<device::MockBluetoothAdapter> adapter_;
-  RemoteDevice remote_device_;
+  cryptauth::RemoteDevice remote_device_;
   device::BluetoothUUID service_uuid_;
   device::BluetoothUUID tx_characteristic_uuid_;
   device::BluetoothUUID rx_characteristic_uuid_;
diff --git a/components/proximity_auth/bluetooth_connection.cc b/components/proximity_auth/bluetooth_connection.cc
index b40421e..511e7ef 100644
--- a/components/proximity_auth/bluetooth_connection.cc
+++ b/components/proximity_auth/bluetooth_connection.cc
@@ -11,8 +11,8 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/logging/logging.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_device.h"
@@ -23,10 +23,10 @@
 const int kReceiveBufferSizeBytes = 1024;
 }
 
-BluetoothConnection::BluetoothConnection(const RemoteDevice& remote_device,
-                                         const device::BluetoothUUID& uuid)
-    : Connection(remote_device), uuid_(uuid), weak_ptr_factory_(this) {
-}
+BluetoothConnection::BluetoothConnection(
+    const cryptauth::RemoteDevice& remote_device,
+    const device::BluetoothUUID& uuid)
+    : Connection(remote_device), uuid_(uuid), weak_ptr_factory_(this) {}
 
 BluetoothConnection::~BluetoothConnection() {
   if (status() != DISCONNECTED)
diff --git a/components/proximity_auth/bluetooth_connection.h b/components/proximity_auth/bluetooth_connection.h
index 29255793..ccebdbd 100644
--- a/components/proximity_auth/bluetooth_connection.h
+++ b/components/proximity_auth/bluetooth_connection.h
@@ -20,9 +20,11 @@
 class IOBuffer;
 }
 
-namespace proximity_auth {
-
+namespace cryptauth {
 struct RemoteDevice;
+}
+
+namespace proximity_auth {
 
 // Represents a Bluetooth connection with a remote device. The connection is a
 // persistent bidirectional channel for sending and receiving wire messages.
@@ -32,7 +34,7 @@
   // Constructs a Bluetooth connection to the service with |uuid| on the
   // |remote_device|. The |remote_device| must already be known to the system
   // Bluetooth daemon.
-  BluetoothConnection(const RemoteDevice& remote_device,
+  BluetoothConnection(const cryptauth::RemoteDevice& remote_device,
                       const device::BluetoothUUID& uuid);
   ~BluetoothConnection() override;
 
diff --git a/components/proximity_auth/bluetooth_connection_finder.cc b/components/proximity_auth/bluetooth_connection_finder.cc
index 7032241..39f240fe7 100644
--- a/components/proximity_auth/bluetooth_connection_finder.cc
+++ b/components/proximity_auth/bluetooth_connection_finder.cc
@@ -20,15 +20,14 @@
 namespace proximity_auth {
 
 BluetoothConnectionFinder::BluetoothConnectionFinder(
-    const RemoteDevice& remote_device,
+    const cryptauth::RemoteDevice& remote_device,
     const device::BluetoothUUID& uuid,
     const base::TimeDelta& polling_interval)
     : remote_device_(remote_device),
       uuid_(uuid),
       polling_interval_(polling_interval),
       has_delayed_poll_scheduled_(false),
-      weak_ptr_factory_(this) {
-}
+      weak_ptr_factory_(this) {}
 
 BluetoothConnectionFinder::~BluetoothConnectionFinder() {
   UnregisterAsObserver();
diff --git a/components/proximity_auth/bluetooth_connection_finder.h b/components/proximity_auth/bluetooth_connection_finder.h
index 9405c03..a2f5ee7 100644
--- a/components/proximity_auth/bluetooth_connection_finder.h
+++ b/components/proximity_auth/bluetooth_connection_finder.h
@@ -12,10 +12,10 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/bluetooth_util.h"
 #include "components/proximity_auth/connection_finder.h"
 #include "components/proximity_auth/connection_observer.h"
-#include "components/proximity_auth/remote_device.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_uuid.h"
 
@@ -27,7 +27,7 @@
                                   public ConnectionObserver,
                                   public device::BluetoothAdapter::Observer {
  public:
-  BluetoothConnectionFinder(const RemoteDevice& remote_device,
+  BluetoothConnectionFinder(const cryptauth::RemoteDevice& remote_device,
                             const device::BluetoothUUID& uuid,
                             const base::TimeDelta& polling_interval);
   ~BluetoothConnectionFinder() override;
@@ -86,7 +86,7 @@
   void InvokeCallbackAsync();
 
   // The remote device to connect to.
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   // The UUID of the service on the remote device.
   const device::BluetoothUUID uuid_;
diff --git a/components/proximity_auth/bluetooth_connection_finder_unittest.cc b/components/proximity_auth/bluetooth_connection_finder_unittest.cc
index 9f62f17..49b4579 100644
--- a/components/proximity_auth/bluetooth_connection_finder_unittest.cc
+++ b/components/proximity_auth/bluetooth_connection_finder_unittest.cc
@@ -14,8 +14,8 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_uuid.h"
diff --git a/components/proximity_auth/bluetooth_connection_unittest.cc b/components/proximity_auth/bluetooth_connection_unittest.cc
index 8b4fa4b7..1c2cd24 100644
--- a/components/proximity_auth/bluetooth_connection_unittest.cc
+++ b/components/proximity_auth/bluetooth_connection_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/message_loop/message_loop.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_uuid.h"
diff --git a/components/proximity_auth/bluetooth_throttler_impl_unittest.cc b/components/proximity_auth/bluetooth_throttler_impl_unittest.cc
index 427110f..cbf65a1 100644
--- a/components/proximity_auth/bluetooth_throttler_impl_unittest.cc
+++ b/components/proximity_auth/bluetooth_throttler_impl_unittest.cc
@@ -44,7 +44,7 @@
 
   void PerformConnectionStateTransition(Connection::Status old_status,
                                         Connection::Status new_status) {
-    FakeConnection connection((RemoteDevice()));
+    FakeConnection connection((cryptauth::RemoteDevice()));
     throttler_.OnConnection(&connection);
     static_cast<ConnectionObserver*>(&throttler_)
         ->OnConnectionStatusChanged(&connection, old_status, new_status);
diff --git a/components/proximity_auth/connection.cc b/components/proximity_auth/connection.cc
index 3c7ff25..b191d5f 100644
--- a/components/proximity_auth/connection.cc
+++ b/components/proximity_auth/connection.cc
@@ -12,11 +12,10 @@
 
 namespace proximity_auth {
 
-Connection::Connection(const RemoteDevice& remote_device)
+Connection::Connection(const cryptauth::RemoteDevice& remote_device)
     : remote_device_(remote_device),
       status_(DISCONNECTED),
-      is_sending_message_(false) {
-}
+      is_sending_message_(false) {}
 
 Connection::~Connection() {
 }
diff --git a/components/proximity_auth/connection.h b/components/proximity_auth/connection.h
index a222e42..f66e831 100644
--- a/components/proximity_auth/connection.h
+++ b/components/proximity_auth/connection.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
-#include "components/proximity_auth/remote_device.h"
+#include "components/cryptauth/remote_device.h"
 
 namespace proximity_auth {
 
@@ -28,7 +28,7 @@
   };
 
   // Constructs a connection to the given |remote_device|.
-  explicit Connection(const RemoteDevice& remote_device);
+  explicit Connection(const cryptauth::RemoteDevice& remote_device);
   virtual ~Connection();
 
   // Returns true iff the connection's status is CONNECTED.
@@ -45,7 +45,9 @@
   void AddObserver(ConnectionObserver* observer);
   void RemoveObserver(ConnectionObserver* observer);
 
-  const RemoteDevice& remote_device() const { return remote_device_; }
+  const cryptauth::RemoteDevice& remote_device() const {
+    return remote_device_;
+  }
 
   // Abstract methods that subclasses should implement:
 
@@ -92,7 +94,7 @@
 
  private:
   // The remote device corresponding to this connection.
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   // The current status of the connection.
   Status status_;
diff --git a/components/proximity_auth/connection_unittest.cc b/components/proximity_auth/connection_unittest.cc
index b8d8c79..c39857cb 100644
--- a/components/proximity_auth/connection_unittest.cc
+++ b/components/proximity_auth/connection_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/connection_observer.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,7 +24,7 @@
 
 class MockConnection : public Connection {
  public:
-  MockConnection() : Connection(RemoteDevice()) {}
+  MockConnection() : Connection(cryptauth::RemoteDevice()) {}
   ~MockConnection() {}
 
   MOCK_METHOD1(SetPaused, void(bool paused));
diff --git a/components/proximity_auth/device_to_device_authenticator_unittest.cc b/components/proximity_auth/device_to_device_authenticator_unittest.cc
index 829c1317b..5dbbe83 100644
--- a/components/proximity_auth/device_to_device_authenticator_unittest.cc
+++ b/components/proximity_auth/device_to_device_authenticator_unittest.cc
@@ -63,7 +63,7 @@
 // Connection implementation for testing.
 class FakeConnection : public Connection {
  public:
-  FakeConnection(const RemoteDevice& remote_device)
+  FakeConnection(const cryptauth::RemoteDevice& remote_device)
       : Connection(remote_device), connection_blocked_(false) {}
   ~FakeConnection() override {}
 
@@ -222,7 +222,7 @@
   MOCK_METHOD1(OnAuthenticationResultProxy, void(Authenticator::Result result));
 
   // Contains information about the remote device.
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   // Simulates the connection to the remote device.
   FakeConnection connection_;
diff --git a/components/proximity_auth/fake_connection.cc b/components/proximity_auth/fake_connection.cc
index 2c77bcac..c8e22fec 100644
--- a/components/proximity_auth/fake_connection.cc
+++ b/components/proximity_auth/fake_connection.cc
@@ -11,7 +11,7 @@
 
 namespace proximity_auth {
 
-FakeConnection::FakeConnection(const RemoteDevice& remote_device)
+FakeConnection::FakeConnection(const cryptauth::RemoteDevice& remote_device)
     : Connection(remote_device) {
   Connect();
 }
diff --git a/components/proximity_auth/fake_connection.h b/components/proximity_auth/fake_connection.h
index c525d4f..5bfe3fb 100644
--- a/components/proximity_auth/fake_connection.h
+++ b/components/proximity_auth/fake_connection.h
@@ -13,7 +13,7 @@
 // A fake implementation of Connection to use in tests.
 class FakeConnection : public Connection {
  public:
-  FakeConnection(const RemoteDevice& remote_device);
+  FakeConnection(const cryptauth::RemoteDevice& remote_device);
   ~FakeConnection() override;
 
   // Connection:
diff --git a/components/proximity_auth/messenger_impl.cc b/components/proximity_auth/messenger_impl.cc
index 05169bf..897f822 100644
--- a/components/proximity_auth/messenger_impl.cc
+++ b/components/proximity_auth/messenger_impl.cc
@@ -78,7 +78,8 @@
 
   // TODO(tengs): We need CryptAuth to report if the phone runs iOS or Android,
   // rather than relying on this heuristic.
-  if (connection_->remote_device().bluetooth_type == RemoteDevice::BLUETOOTH_LE)
+  if (connection_->remote_device().bluetooth_type ==
+      cryptauth::RemoteDevice::BLUETOOTH_LE)
     PollScreenStateForIOS();
 }
 
@@ -100,7 +101,7 @@
   return (secure_context_->GetProtocolVersion() ==
           SecureContext::PROTOCOL_VERSION_THREE_ONE) &&
          connection_->remote_device().bluetooth_type !=
-             RemoteDevice::BLUETOOTH_LE;
+             cryptauth::RemoteDevice::BLUETOOTH_LE;
 }
 
 void MessengerImpl::DispatchUnlockEvent() {
@@ -200,7 +201,7 @@
   // The decoded message should be a JSON string.
   std::unique_ptr<base::Value> message_value =
       base::JSONReader::Read(decoded_message);
-  if (!message_value || !message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!message_value || !message_value->IsType(base::Value::Type::DICTIONARY)) {
     PA_LOG(ERROR) << "Unable to parse message as JSON:\n" << decoded_message;
     return;
   }
diff --git a/components/proximity_auth/messenger_impl_unittest.cc b/components/proximity_auth/messenger_impl_unittest.cc
index d6f5d88..84a16315 100644
--- a/components/proximity_auth/messenger_impl_unittest.cc
+++ b/components/proximity_auth/messenger_impl_unittest.cc
@@ -9,12 +9,12 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/connection.h"
 #include "components/proximity_auth/fake_connection.h"
 #include "components/proximity_auth/fake_secure_context.h"
 #include "components/proximity_auth/messenger_observer.h"
 #include "components/proximity_auth/proximity_auth_test_util.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/remote_status_update.h"
 #include "components/proximity_auth/wire_message.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/proximity_auth/proximity_auth_pref_manager.cc b/components/proximity_auth/proximity_auth_pref_manager.cc
index 93e90d4..7d2dbd6 100644
--- a/components/proximity_auth/proximity_auth_pref_manager.cc
+++ b/components/proximity_auth/proximity_auth_pref_manager.cc
@@ -52,7 +52,7 @@
   for (base::DictionaryValue::Iterator it(*remote_ble_devices); !it.IsAtEnd();
        it.Advance()) {
     std::string value_string;
-    DCHECK(it.value().IsType(base::Value::TYPE_STRING));
+    DCHECK(it.value().IsType(base::Value::Type::STRING));
     if (it.value().GetAsString(&value_string) && value_string == public_key)
       return it.key();
   }
@@ -65,7 +65,7 @@
   for (base::DictionaryValue::Iterator it(*remote_ble_devices); !it.IsAtEnd();
        it.Advance()) {
     std::string value_string;
-    DCHECK(it.value().IsType(base::Value::TYPE_STRING));
+    DCHECK(it.value().IsType(base::Value::Type::STRING));
     it.value().GetAsString(&value_string);
     public_keys.push_back(value_string);
   }
diff --git a/components/proximity_auth/proximity_auth_system.cc b/components/proximity_auth/proximity_auth_system.cc
index 9be3637..b115066 100644
--- a/components/proximity_auth/proximity_auth_system.cc
+++ b/components/proximity_auth/proximity_auth_system.cc
@@ -56,7 +56,7 @@
 
 void ProximityAuthSystem::SetRemoteDevicesForUser(
     const AccountId& account_id,
-    const RemoteDeviceList& remote_devices) {
+    const cryptauth::RemoteDeviceList& remote_devices) {
   remote_devices_map_[account_id] = remote_devices;
   if (started_) {
     const AccountId& focused_account_id =
@@ -66,10 +66,10 @@
   }
 }
 
-RemoteDeviceList ProximityAuthSystem::GetRemoteDevicesForUser(
+cryptauth::RemoteDeviceList ProximityAuthSystem::GetRemoteDevicesForUser(
     const AccountId& account_id) const {
   if (remote_devices_map_.find(account_id) == remote_devices_map_.end())
-    return RemoteDeviceList();
+    return cryptauth::RemoteDeviceList();
   return remote_devices_map_.at(account_id);
 }
 
@@ -153,7 +153,7 @@
 
   // TODO(tengs): We currently assume each user has only one RemoteDevice, so we
   // can simply take the first item in the list.
-  RemoteDevice remote_device = remote_devices_map_[account_id][0];
+  cryptauth::RemoteDevice remote_device = remote_devices_map_[account_id][0];
   if (!suspended_) {
     PA_LOG(INFO) << "Creating RemoteDeviceLifeCycle for focused user: "
                  << account_id.Serialize();
diff --git a/components/proximity_auth/proximity_auth_system.h b/components/proximity_auth/proximity_auth_system.h
index f411a5e..b6c1d657 100644
--- a/components/proximity_auth/proximity_auth_system.h
+++ b/components/proximity_auth/proximity_auth_system.h
@@ -10,7 +10,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/proximity_auth/remote_device.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/remote_device_life_cycle.h"
 #include "components/proximity_auth/screenlock_bridge.h"
 #include "components/signin/core/account_id/account_id.h"
@@ -45,13 +45,15 @@
   // Registers a list of |remote_devices| for |account_id| that can be used for
   // sign-in/unlock. If devices were previously registered for the user, then
   // they will be replaced.
-  void SetRemoteDevicesForUser(const AccountId& account_id,
-                               const RemoteDeviceList& remote_devices);
+  void SetRemoteDevicesForUser(
+      const AccountId& account_id,
+      const cryptauth::RemoteDeviceList& remote_devices);
 
   // Returns the RemoteDevices registered for |account_id|. Returns an empty
   // list
   // if no devices are registered for |account_id|.
-  RemoteDeviceList GetRemoteDevicesForUser(const AccountId& account_id) const;
+  cryptauth::RemoteDeviceList GetRemoteDevicesForUser(
+      const AccountId& account_id) const;
 
   // Called when the user clicks the user pod and attempts to unlock/sign-in.
   void OnAuthAttempted(const AccountId& account_id);
@@ -79,7 +81,7 @@
   void ResumeAfterWakeUpTimeout();
 
   // Lists of remote devices, keyed by user account id.
-  std::map<AccountId, RemoteDeviceList> remote_devices_map_;
+  std::map<AccountId, cryptauth::RemoteDeviceList> remote_devices_map_;
 
   // Delegate for Chrome dependent functionality.
   ProximityAuthClient* proximity_auth_client_;
diff --git a/components/proximity_auth/proximity_auth_system_unittest.cc b/components/proximity_auth/proximity_auth_system_unittest.cc
index c9c676e1..0e5ed58 100644
--- a/components/proximity_auth/proximity_auth_system_unittest.cc
+++ b/components/proximity_auth/proximity_auth_system_unittest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/proximity_auth_system.h"
-#include "components/proximity_auth/remote_device.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace proximity_auth {
diff --git a/components/proximity_auth/proximity_auth_test_util.h b/components/proximity_auth/proximity_auth_test_util.h
index 5e3346c..40a2764f 100644
--- a/components/proximity_auth/proximity_auth_test_util.h
+++ b/components/proximity_auth/proximity_auth_test_util.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_PROXIMITY_AUTH_PROXIMITY_AUTH_TEST_UTIL_H
 #define COMPONENTS_PROXIMITY_AUTH_PROXIMITY_AUTH_TEST_UTIL_H
 
-#include "components/proximity_auth/remote_device.h"
+#include "components/cryptauth/remote_device.h"
 
 namespace proximity_auth {
 
@@ -18,20 +18,21 @@
 extern const char kTestRemoteDeviceSignInChallenge[];
 
 // Returns a BLE RemoteDevice used for tests.
-inline RemoteDevice CreateLERemoteDeviceForTest() {
-  return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
-                      kTestRemoteDevicePublicKey, RemoteDevice::BLUETOOTH_LE,
-                      kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
-                      kTestRemoteDeviceSignInChallenge);
+inline cryptauth::RemoteDevice CreateLERemoteDeviceForTest() {
+  return cryptauth::RemoteDevice(
+      kTestRemoteDeviceUserId, kTestRemoteDeviceName,
+      kTestRemoteDevicePublicKey, cryptauth::RemoteDevice::BLUETOOTH_LE,
+      kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
+      kTestRemoteDeviceSignInChallenge);
 }
 
 // Returns a classic Bluetooth RemoteDevice used for tests.
-inline RemoteDevice CreateClassicRemoteDeviceForTest() {
-  return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
-                      kTestRemoteDevicePublicKey,
-                      RemoteDevice::BLUETOOTH_CLASSIC,
-                      kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
-                      kTestRemoteDeviceSignInChallenge);
+inline cryptauth::RemoteDevice CreateClassicRemoteDeviceForTest() {
+  return cryptauth::RemoteDevice(
+      kTestRemoteDeviceUserId, kTestRemoteDeviceName,
+      kTestRemoteDevicePublicKey, cryptauth::RemoteDevice::BLUETOOTH_CLASSIC,
+      kTestRemoteDeviceBluetoothAddress, kTestRemoteDevicePSK,
+      kTestRemoteDeviceSignInChallenge);
 }
 
 }  // namespace proximity_auth
diff --git a/components/proximity_auth/proximity_monitor_impl.cc b/components/proximity_auth/proximity_monitor_impl.cc
index cf908b4..dce7cb0 100644
--- a/components/proximity_auth/proximity_monitor_impl.cc
+++ b/components/proximity_auth/proximity_monitor_impl.cc
@@ -33,7 +33,7 @@
 const double kRssiSampleWeight = 0.3;
 
 ProximityMonitorImpl::ProximityMonitorImpl(
-    const RemoteDevice& remote_device,
+    const cryptauth::RemoteDevice& remote_device,
     std::unique_ptr<base::TickClock> clock)
     : remote_device_(remote_device),
       strategy_(Strategy::NONE),
diff --git a/components/proximity_auth/proximity_monitor_impl.h b/components/proximity_auth/proximity_monitor_impl.h
index dc6143d..11cffd3 100644
--- a/components/proximity_auth/proximity_monitor_impl.h
+++ b/components/proximity_auth/proximity_monitor_impl.h
@@ -11,8 +11,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/proximity_monitor.h"
-#include "components/proximity_auth/remote_device.h"
 #include "device/bluetooth/bluetooth_device.h"
 
 namespace base {
@@ -32,7 +32,7 @@
 class ProximityMonitorImpl : public ProximityMonitor {
  public:
   // The |observer| is not owned, and must outlive |this| instance.
-  ProximityMonitorImpl(const RemoteDevice& remote_device,
+  ProximityMonitorImpl(const cryptauth::RemoteDevice& remote_device,
                        std::unique_ptr<base::TickClock> clock);
   ~ProximityMonitorImpl() override;
 
@@ -104,7 +104,7 @@
   void CheckForProximityStateChange();
 
   // The remote device being monitored.
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   // The observers attached to the ProximityMonitor.
   base::ObserverList<ProximityMonitorObserver> observers_;
diff --git a/components/proximity_auth/proximity_monitor_impl_unittest.cc b/components/proximity_auth/proximity_monitor_impl_unittest.cc
index 56b88b2d..9d96bd93 100644
--- a/components/proximity_auth/proximity_monitor_impl_unittest.cc
+++ b/components/proximity_auth/proximity_monitor_impl_unittest.cc
@@ -15,9 +15,9 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/logging/logging.h"
 #include "components/proximity_auth/proximity_monitor_observer.h"
-#include "components/proximity_auth/remote_device.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -40,8 +40,9 @@
 
 class TestProximityMonitorImpl : public ProximityMonitorImpl {
  public:
-  explicit TestProximityMonitorImpl(const RemoteDevice& remote_device,
-                                    std::unique_ptr<base::TickClock> clock)
+  explicit TestProximityMonitorImpl(
+      const cryptauth::RemoteDevice& remote_device,
+      std::unique_ptr<base::TickClock> clock)
       : ProximityMonitorImpl(remote_device, std::move(clock)) {}
   ~TestProximityMonitorImpl() override {}
 
@@ -85,14 +86,15 @@
                                  kBluetoothAddress,
                                  false /* paired */,
                                  true /* connected */),
-        monitor_(RemoteDevice(kRemoteDeviceUserId,
-                              kRemoteDeviceName,
-                              kRemoteDevicePublicKey,
-                              RemoteDevice::BLUETOOTH_CLASSIC,
-                              kBluetoothAddress,
-                              kPersistentSymmetricKey,
-                              std::string()),
-                 base::WrapUnique(clock_)),
+        monitor_(
+            cryptauth::RemoteDevice(kRemoteDeviceUserId,
+                                    kRemoteDeviceName,
+                                    kRemoteDevicePublicKey,
+                                    cryptauth::RemoteDevice::BLUETOOTH_CLASSIC,
+                                    kBluetoothAddress,
+                                    kPersistentSymmetricKey,
+                                    std::string()),
+            base::WrapUnique(clock_)),
         task_runner_(new base::TestSimpleTaskRunner()),
         thread_task_runner_handle_(task_runner_) {
     ON_CALL(*bluetooth_adapter_, GetDevice(kBluetoothAddress))
@@ -540,9 +542,9 @@
        RecordProximityMetricsOnAuthSuccess_UnknownValues) {
   // Note: A device without a recorded name will have its Bluetooth address as
   // its name.
-  RemoteDevice unnamed_remote_device(
+  cryptauth::RemoteDevice unnamed_remote_device(
       kRemoteDeviceUserId, kBluetoothAddress, kRemoteDevicePublicKey,
-      RemoteDevice::BLUETOOTH_CLASSIC, kBluetoothAddress,
+      cryptauth::RemoteDevice::BLUETOOTH_CLASSIC, kBluetoothAddress,
       kPersistentSymmetricKey, std::string());
 
   std::unique_ptr<base::TickClock> clock(new base::SimpleTestTickClock());
diff --git a/components/proximity_auth/remote_device.cc b/components/proximity_auth/remote_device.cc
deleted file mode 100644
index cecf023..0000000
--- a/components/proximity_auth/remote_device.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/proximity_auth/remote_device.h"
-
-namespace proximity_auth {
-
-RemoteDevice::RemoteDevice() : bluetooth_type(BLUETOOTH_CLASSIC) {}
-
-RemoteDevice::RemoteDevice(const std::string& user_id,
-                           const std::string& name,
-                           const std::string& public_key,
-                           BluetoothType bluetooth_type,
-                           const std::string& bluetooth_address,
-                           const std::string& persistent_symmetric_key,
-                           std::string sign_in_challenge)
-    : user_id(user_id),
-      name(name),
-      public_key(public_key),
-      bluetooth_type(bluetooth_type),
-      bluetooth_address(bluetooth_address),
-      persistent_symmetric_key(persistent_symmetric_key),
-      sign_in_challenge(sign_in_challenge) {}
-
-RemoteDevice::RemoteDevice(const RemoteDevice& other) = default;
-
-RemoteDevice::~RemoteDevice() {}
-
-}  // namespace
diff --git a/components/proximity_auth/remote_device.h b/components/proximity_auth/remote_device.h
index e10db03a..a484a2a 100644
--- a/components/proximity_auth/remote_device.h
+++ b/components/proximity_auth/remote_device.h
@@ -8,7 +8,7 @@
 #include <string>
 #include <vector>
 
-namespace proximity_auth {
+namespace cryptauth {
 
 struct RemoteDevice {
  public:
@@ -36,6 +36,6 @@
 
 typedef std::vector<RemoteDevice> RemoteDeviceList;
 
-}  // namespace proximity_auth
+}  // namespace cryptauth
 
 #endif  // COMPONENTS_PROXIMITY_AUTH_REMOTE_DEVICE_H
diff --git a/components/proximity_auth/remote_device_life_cycle.h b/components/proximity_auth/remote_device_life_cycle.h
index 6b8e87a..ed7f4c6 100644
--- a/components/proximity_auth/remote_device_life_cycle.h
+++ b/components/proximity_auth/remote_device_life_cycle.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_PROXIMITY_AUTH_REMOTE_DEVICE_LIFE_CYCLE_H
 
 #include "base/macros.h"
-#include "components/proximity_auth/remote_device.h"
+#include "components/cryptauth/remote_device.h"
 
 namespace proximity_auth {
 
@@ -55,7 +55,7 @@
   virtual void Start() = 0;
 
   // Returns the RemoteDevice instance that this life cycle manages.
-  virtual RemoteDevice GetRemoteDevice() const = 0;
+  virtual cryptauth::RemoteDevice GetRemoteDevice() const = 0;
 
   // Returns the current state of in the life cycle.
   virtual State GetState() const = 0;
diff --git a/components/proximity_auth/remote_device_life_cycle_impl.cc b/components/proximity_auth/remote_device_life_cycle_impl.cc
index 287a472..4293772 100644
--- a/components/proximity_auth/remote_device_life_cycle_impl.cc
+++ b/components/proximity_auth/remote_device_life_cycle_impl.cc
@@ -41,7 +41,7 @@
 }  // namespace
 
 RemoteDeviceLifeCycleImpl::RemoteDeviceLifeCycleImpl(
-    const RemoteDevice& remote_device,
+    const cryptauth::RemoteDevice& remote_device,
     ProximityAuthClient* proximity_auth_client)
     : remote_device_(remote_device),
       proximity_auth_client_(proximity_auth_client),
@@ -60,7 +60,7 @@
   FindConnection();
 }
 
-RemoteDevice RemoteDeviceLifeCycleImpl::GetRemoteDevice() const {
+cryptauth::RemoteDevice RemoteDeviceLifeCycleImpl::GetRemoteDevice() const {
   return remote_device_;
 }
 
@@ -82,7 +82,7 @@
 
 std::unique_ptr<ConnectionFinder>
 RemoteDeviceLifeCycleImpl::CreateConnectionFinder() {
-  if (remote_device_.bluetooth_type == RemoteDevice::BLUETOOTH_LE) {
+  if (remote_device_.bluetooth_type == cryptauth::RemoteDevice::BLUETOOTH_LE) {
     return base::MakeUnique<BluetoothLowEnergyConnectionFinder>(
         remote_device_, kBLESmartLockServiceUUID,
         BluetoothLowEnergyConnectionFinder::FinderStrategy::FIND_PAIRED_DEVICE,
diff --git a/components/proximity_auth/remote_device_life_cycle_impl.h b/components/proximity_auth/remote_device_life_cycle_impl.h
index fbc272a..c1649eb 100644
--- a/components/proximity_auth/remote_device_life_cycle_impl.h
+++ b/components/proximity_auth/remote_device_life_cycle_impl.h
@@ -11,9 +11,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/authenticator.h"
 #include "components/proximity_auth/messenger_observer.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/remote_device_life_cycle.h"
 
 namespace proximity_auth {
@@ -31,13 +31,13 @@
  public:
   // Creates the life cycle for controlling the given |remote_device|.
   // |proximity_auth_client| is not owned.
-  RemoteDeviceLifeCycleImpl(const RemoteDevice& remote_device,
+  RemoteDeviceLifeCycleImpl(const cryptauth::RemoteDevice& remote_device,
                             ProximityAuthClient* proximity_auth_client);
   ~RemoteDeviceLifeCycleImpl() override;
 
   // RemoteDeviceLifeCycle:
   void Start() override;
-  RemoteDevice GetRemoteDevice() const override;
+  cryptauth::RemoteDevice GetRemoteDevice() const override;
   RemoteDeviceLifeCycle::State GetState() const override;
   Messenger* GetMessenger() override;
   void AddObserver(Observer* observer) override;
@@ -74,7 +74,7 @@
   void OnDisconnected() override;
 
   // The remote device being controlled.
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   // Used to grab dependencies in chrome. Not owned.
   ProximityAuthClient* proximity_auth_client_;
diff --git a/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc b/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
index a20e8f5..913c9efc 100644
--- a/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
+++ b/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
@@ -61,7 +61,7 @@
 
 class FakeConnectionFinder : public ConnectionFinder {
  public:
-  FakeConnectionFinder(const RemoteDevice& remote_device)
+  FakeConnectionFinder(const cryptauth::RemoteDevice& remote_device)
       : remote_device_(remote_device), connection_(nullptr) {}
   ~FakeConnectionFinder() override {}
 
@@ -82,7 +82,7 @@
     connection_callback_ = connection_callback;
   }
 
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
 
   FakeConnection* connection_;
 
@@ -127,7 +127,8 @@
 // Subclass of RemoteDeviceLifeCycleImpl to make it testable.
 class TestableRemoteDeviceLifeCycleImpl : public RemoteDeviceLifeCycleImpl {
  public:
-  TestableRemoteDeviceLifeCycleImpl(const RemoteDevice& remote_device)
+  TestableRemoteDeviceLifeCycleImpl(
+      const cryptauth::RemoteDevice& remote_device)
       : RemoteDeviceLifeCycleImpl(remote_device, nullptr),
         remote_device_(remote_device) {}
 
@@ -152,7 +153,7 @@
     return std::move(scoped_authenticator);
   }
 
-  const RemoteDevice remote_device_;
+  const cryptauth::RemoteDevice remote_device_;
   FakeConnectionFinder* connection_finder_;
   FakeAuthenticator* authenticator_;
 
@@ -239,8 +240,9 @@
 };
 
 TEST_F(ProximityAuthRemoteDeviceLifeCycleImplTest, GetRemoteDevice) {
-  RemoteDevice expected_remote_device = CreateClassicRemoteDeviceForTest();
-  RemoteDevice remote_device = life_cycle_.GetRemoteDevice();
+  cryptauth::RemoteDevice expected_remote_device =
+      CreateClassicRemoteDeviceForTest();
+  cryptauth::RemoteDevice remote_device = life_cycle_.GetRemoteDevice();
   EXPECT_EQ(expected_remote_device.user_id, remote_device.user_id);
   EXPECT_EQ(expected_remote_device.name, remote_device.name);
   EXPECT_EQ(expected_remote_device.public_key, remote_device.public_key);
diff --git a/components/proximity_auth/remote_device_loader.cc b/components/proximity_auth/remote_device_loader.cc
index 4b8b0965..c856cff 100644
--- a/components/proximity_auth/remote_device_loader.cc
+++ b/components/proximity_auth/remote_device_loader.cc
@@ -69,9 +69,10 @@
 
   // TODO(tengs): We assume that devices without a |bluetooth_address| field are
   // BLE devices. Ideally, we should have a separate field for this information.
-  RemoteDevice::BluetoothType bluetooth_type =
-      unlock_key.bluetooth_address().empty() ? RemoteDevice::BLUETOOTH_LE
-                                             : RemoteDevice::BLUETOOTH_CLASSIC;
+  cryptauth::RemoteDevice::BluetoothType bluetooth_type =
+      unlock_key.bluetooth_address().empty()
+          ? cryptauth::RemoteDevice::BLUETOOTH_LE
+          : cryptauth::RemoteDevice::BLUETOOTH_CLASSIC;
 
   std::string bluetooth_address = unlock_key.bluetooth_address();
   if (bluetooth_address.empty() && pref_manager_) {
@@ -84,7 +85,7 @@
                  << " is " << bluetooth_address;
   }
 
-  remote_devices_.push_back(RemoteDevice(
+  remote_devices_.push_back(cryptauth::RemoteDevice(
       user_id_, unlock_key.friendly_device_name(), unlock_key.public_key(),
       bluetooth_type, bluetooth_address, psk, std::string()));
 
diff --git a/components/proximity_auth/remote_device_loader.h b/components/proximity_auth/remote_device_loader.h
index c3b10f8..81be589 100644
--- a/components/proximity_auth/remote_device_loader.h
+++ b/components/proximity_auth/remote_device_loader.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
-#include "components/proximity_auth/remote_device.h"
+#include "components/cryptauth/remote_device.h"
 
 namespace cryptauth {
 class SecureMessageDelegate;
@@ -43,7 +43,8 @@
   ~RemoteDeviceLoader();
 
   // Loads the RemoteDevice objects. |callback| will be invoked upon completion.
-  typedef base::Callback<void(const RemoteDeviceList&)> RemoteDeviceCallback;
+  typedef base::Callback<void(const cryptauth::RemoteDeviceList&)>
+      RemoteDeviceCallback;
   void Load(const RemoteDeviceCallback& callback);
 
  private:
@@ -71,7 +72,7 @@
   RemoteDeviceCallback callback_;
 
   // The collection of RemoteDevices to return.
-  RemoteDeviceList remote_devices_;
+  cryptauth::RemoteDeviceList remote_devices_;
 
   base::WeakPtrFactory<RemoteDeviceLoader> weak_ptr_factory_;
 
diff --git a/components/proximity_auth/remote_device_loader_unittest.cc b/components/proximity_auth/remote_device_loader_unittest.cc
index a758331..9945ac3 100644
--- a/components/proximity_auth/remote_device_loader_unittest.cc
+++ b/components/proximity_auth/remote_device_loader_unittest.cc
@@ -61,7 +61,8 @@
 
   ~ProximityAuthRemoteDeviceLoaderTest() {}
 
-  void OnRemoteDevicesLoaded(const std::vector<RemoteDevice>& remote_devices) {
+  void OnRemoteDevicesLoaded(
+      const std::vector<cryptauth::RemoteDevice>& remote_devices) {
     remote_devices_ = remote_devices;
     LoadCompleted();
   }
@@ -78,7 +79,7 @@
   std::string user_private_key_;
 
   // Stores the result of the RemoteDeviceLoader.
-  std::vector<RemoteDevice> remote_devices_;
+  std::vector<cryptauth::RemoteDevice> remote_devices_;
 
   // Stores the bluetooth address for BLE devices.
   std::unique_ptr<MockProximityAuthPrefManager> pref_manager_;
@@ -92,7 +93,7 @@
                             std::move(secure_message_delegate_),
                             pref_manager_.get());
 
-  std::vector<RemoteDevice> result;
+  std::vector<cryptauth::RemoteDevice> result;
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
       base::Bind(&ProximityAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
@@ -108,7 +109,7 @@
                             std::move(secure_message_delegate_),
                             pref_manager_.get());
 
-  std::vector<RemoteDevice> result;
+  std::vector<cryptauth::RemoteDevice> result;
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
       base::Bind(&ProximityAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
@@ -120,7 +121,8 @@
   EXPECT_EQ(unlock_keys[0].public_key(), remote_devices_[0].public_key);
   EXPECT_EQ(unlock_keys[0].bluetooth_address(),
             remote_devices_[0].bluetooth_address);
-  EXPECT_EQ(RemoteDevice::BLUETOOTH_CLASSIC, remote_devices_[0].bluetooth_type);
+  EXPECT_EQ(cryptauth::RemoteDevice::BLUETOOTH_CLASSIC,
+            remote_devices_[0].bluetooth_type);
 }
 
 TEST_F(ProximityAuthRemoteDeviceLoaderTest, LoadOneBLERemoteDevice) {
@@ -135,7 +137,7 @@
   EXPECT_CALL(*pref_manager_, GetDeviceAddress(testing::_))
       .WillOnce(testing::Return(ble_address));
 
-  std::vector<RemoteDevice> result;
+  std::vector<cryptauth::RemoteDevice> result;
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
       base::Bind(&ProximityAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
@@ -146,7 +148,8 @@
   EXPECT_EQ(unlock_keys[0].friendly_device_name(), remote_devices_[0].name);
   EXPECT_EQ(unlock_keys[0].public_key(), remote_devices_[0].public_key);
   EXPECT_EQ(ble_address, remote_devices_[0].bluetooth_address);
-  EXPECT_EQ(RemoteDevice::BLUETOOTH_LE, remote_devices_[0].bluetooth_type);
+  EXPECT_EQ(cryptauth::RemoteDevice::BLUETOOTH_LE,
+            remote_devices_[0].bluetooth_type);
 }
 
 TEST_F(ProximityAuthRemoteDeviceLoaderTest, LoadThreeRemoteDevice) {
@@ -158,7 +161,7 @@
                             std::move(secure_message_delegate_),
                             pref_manager_.get());
 
-  std::vector<RemoteDevice> result;
+  std::vector<cryptauth::RemoteDevice> result;
   EXPECT_CALL(*this, LoadCompleted());
   loader.Load(
       base::Bind(&ProximityAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
diff --git a/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc b/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc
index 539ebd90..fb7478634 100644
--- a/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc
+++ b/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc
@@ -11,10 +11,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/bluetooth_connection_finder.h"
 #include "components/proximity_auth/bluetooth_throttler.h"
 #include "components/proximity_auth/fake_connection.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/wire_message.h"
 #include "device/bluetooth/bluetooth_uuid.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,13 +52,14 @@
  public:
   FakeBluetoothConnectionFinder()
       : BluetoothConnectionFinder(
-            RemoteDevice(),
+            cryptauth::RemoteDevice(),
             device::BluetoothUUID(kUuid),
             base::TimeDelta::FromSeconds(kPollingIntervalSeconds)) {}
   ~FakeBluetoothConnectionFinder() override {}
 
   void Find(const ConnectionCallback& connection_callback) override {
-    connection_callback.Run(base::MakeUnique<FakeConnection>(RemoteDevice()));
+    connection_callback.Run(
+        base::MakeUnique<FakeConnection>(cryptauth::RemoteDevice()));
   }
 
  private:
diff --git a/components/proximity_auth/unlock_manager.cc b/components/proximity_auth/unlock_manager.cc
index 20999086..fd745b52 100644
--- a/components/proximity_auth/unlock_manager.cc
+++ b/components/proximity_auth/unlock_manager.cc
@@ -12,12 +12,12 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/cryptauth/remote_device.h"
 #include "components/proximity_auth/logging/logging.h"
 #include "components/proximity_auth/messenger.h"
 #include "components/proximity_auth/metrics.h"
 #include "components/proximity_auth/proximity_auth_client.h"
 #include "components/proximity_auth/proximity_monitor_impl.h"
-#include "components/proximity_auth/remote_device.h"
 #include "components/proximity_auth/secure_context.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
@@ -320,7 +320,7 @@
 }
 
 std::unique_ptr<ProximityMonitor> UnlockManager::CreateProximityMonitor(
-    const RemoteDevice& remote_device) {
+    const cryptauth::RemoteDevice& remote_device) {
   return base::MakeUnique<ProximityMonitorImpl>(
       remote_device, base::WrapUnique(new base::DefaultTickClock()));
 }
@@ -331,7 +331,7 @@
     return;
   }
 
-  RemoteDevice remote_device = life_cycle_->GetRemoteDevice();
+  cryptauth::RemoteDevice remote_device = life_cycle_->GetRemoteDevice();
   proximity_auth_client_->GetChallengeForUserAndDevice(
       remote_device.user_id, remote_device.public_key,
       GetMessenger()->GetSecureContext()->GetChannelBindingData(),
diff --git a/components/proximity_auth/unlock_manager.h b/components/proximity_auth/unlock_manager.h
index 2a6602a0..c5960ed 100644
--- a/components/proximity_auth/unlock_manager.h
+++ b/components/proximity_auth/unlock_manager.h
@@ -63,7 +63,7 @@
   // Creates a ProximityMonitor instance for the given |remote_device|.
   // Exposed for testing.
   virtual std::unique_ptr<ProximityMonitor> CreateProximityMonitor(
-      const RemoteDevice& remote_device);
+      const cryptauth::RemoteDevice& remote_device);
 
  private:
   // The possible lock screen states for the remote device.
diff --git a/components/proximity_auth/unlock_manager_unittest.cc b/components/proximity_auth/unlock_manager_unittest.cc
index 13c04e1..cf6645f9 100644
--- a/components/proximity_auth/unlock_manager_unittest.cc
+++ b/components/proximity_auth/unlock_manager_unittest.cc
@@ -62,7 +62,7 @@
   ~MockRemoteDeviceLifeCycle() override {}
 
   MOCK_METHOD0(Start, void());
-  MOCK_CONST_METHOD0(GetRemoteDevice, RemoteDevice());
+  MOCK_CONST_METHOD0(GetRemoteDevice, cryptauth::RemoteDevice());
   MOCK_CONST_METHOD0(GetState, State());
   MOCK_METHOD0(GetMessenger, Messenger*());
   MOCK_METHOD1(AddObserver, void(Observer*));
@@ -159,7 +159,7 @@
 
  private:
   std::unique_ptr<ProximityMonitor> CreateProximityMonitor(
-      const RemoteDevice& remote_device) override {
+      const cryptauth::RemoteDevice& remote_device) override {
     EXPECT_EQ(kTestRemoteDevicePublicKey, remote_device.public_key);
     std::unique_ptr<MockProximityMonitor> proximity_monitor(
         new NiceMock<MockProximityMonitor>());
@@ -253,7 +253,7 @@
   }
 
  protected:
-  RemoteDevice remote_device_;
+  cryptauth::RemoteDevice remote_device_;
 
   // Mock used for verifying interactions with the Bluetooth subsystem.
   scoped_refptr<device::MockBluetoothAdapter> bluetooth_adapter_;
diff --git a/components/proximity_auth/webui/proximity_auth_webui_handler.cc b/components/proximity_auth/webui/proximity_auth_webui_handler.cc
index 8c69114..836cf0a 100644
--- a/components/proximity_auth/webui/proximity_auth_webui_handler.cc
+++ b/components/proximity_auth/webui/proximity_auth_webui_handler.cc
@@ -471,7 +471,7 @@
 }
 
 void ProximityAuthWebUIHandler::OnRemoteDevicesLoaded(
-    const std::vector<RemoteDevice>& remote_devices) {
+    const std::vector<cryptauth::RemoteDevice>& remote_devices) {
   if (remote_devices[0].persistent_symmetric_key.empty()) {
     PA_LOG(ERROR) << "Failed to derive PSK.";
     return;
@@ -571,7 +571,7 @@
   PA_LOG(INFO) << "Cleaning up connection to " << selected_remote_device_.name
                << " [" << selected_remote_device_.bluetooth_address << "]";
   life_cycle_.reset();
-  selected_remote_device_ = RemoteDevice();
+  selected_remote_device_ = cryptauth::RemoteDevice();
   last_remote_status_update_.reset();
   web_ui()->CallJavascriptFunctionUnsafe(
       "LocalStateInterface.onUnlockKeysChanged", *GetUnlockKeysList());
diff --git a/components/proximity_auth/webui/proximity_auth_webui_handler.h b/components/proximity_auth/webui/proximity_auth_webui_handler.h
index 0f97a51..3a05795 100644
--- a/components/proximity_auth/webui/proximity_auth_webui_handler.h
+++ b/components/proximity_auth/webui/proximity_auth_webui_handler.h
@@ -99,7 +99,8 @@
       const std::vector<cryptauth::ExternalDeviceInfo>& reachable_phones);
 
   // Called when the RemoteDevice is loaded so we can create a connection.
-  void OnRemoteDevicesLoaded(const std::vector<RemoteDevice>& remote_devices);
+  void OnRemoteDevicesLoaded(
+      const std::vector<cryptauth::RemoteDevice>& remote_devices);
 
   // Converts an ExternalDeviceInfo proto to a JSON dictionary used in
   // JavaScript.
@@ -148,7 +149,7 @@
   // Member variables for connecting to and authenticating the remote device.
   // TODO(tengs): Support multiple simultaenous connections.
   std::unique_ptr<RemoteDeviceLoader> remote_device_loader_;
-  RemoteDevice selected_remote_device_;
+  cryptauth::RemoteDevice selected_remote_device_;
   std::unique_ptr<RemoteDeviceLifeCycle> life_cycle_;
   std::unique_ptr<RemoteStatusUpdate> last_remote_status_update_;
 
diff --git a/components/proximity_auth/wire_message.cc b/components/proximity_auth/wire_message.cc
index ef27a31..6da69d1a 100644
--- a/components/proximity_auth/wire_message.cc
+++ b/components/proximity_auth/wire_message.cc
@@ -88,7 +88,7 @@
 
   std::unique_ptr<base::Value> body_value =
       base::JSONReader::Read(serialized_message.substr(kHeaderLength));
-  if (!body_value || !body_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!body_value || !body_value->IsType(base::Value::Type::DICTIONARY)) {
     PA_LOG(WARNING) << "Error: Unable to parse message as JSON.";
     return std::unique_ptr<WireMessage>();
   }
diff --git a/components/rappor/BUILD.gn b/components/rappor/BUILD.gn
index be595ad..66b8744 100644
--- a/components/rappor/BUILD.gn
+++ b/components/rappor/BUILD.gn
@@ -14,25 +14,23 @@
     "rappor_metric.cc",
     "rappor_metric.h",
     "rappor_parameters.cc",
-    "rappor_parameters.h",
     "rappor_pref_names.cc",
     "rappor_pref_names.h",
     "rappor_prefs.cc",
     "rappor_prefs.h",
-    "rappor_service.cc",
-    "rappor_service.h",
+    "rappor_service_impl.cc",
+    "rappor_service_impl.h",
     "rappor_utils.cc",
-    "rappor_utils.h",
     "reports.cc",
     "reports.h",
     "sample.cc",
-    "sample.h",
     "sampler.cc",
     "sampler.h",
   ]
 
   public_deps = [
     "//components/rappor/proto",
+    "//components/rappor/public",
   ]
 
   deps = [
diff --git a/components/rappor/byte_vector_utils.h b/components/rappor/byte_vector_utils.h
index 71ce88f..339b3da 100644
--- a/components/rappor/byte_vector_utils.h
+++ b/components/rappor/byte_vector_utils.h
@@ -12,7 +12,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 #include "crypto/hmac.h"
 
 namespace rappor {
diff --git a/components/rappor/log_uploader.h b/components/rappor/log_uploader.h
index a6dc8bad..55e53202 100644
--- a/components/rappor/log_uploader.h
+++ b/components/rappor/log_uploader.h
@@ -24,9 +24,9 @@
 
 namespace rappor {
 
-// Uploads logs from RapporService.  Logs are passed in via QueueLog(), stored
-// internally, and uploaded one at a time.  A queued log will be uploaded at a
-// fixed interval after the successful upload of the previous logs.  If an
+// Uploads logs from RapporServiceImpl.  Logs are passed in via QueueLog(),
+// stored internally, and uploaded one at a time.  A queued log will be uploaded
+// at a fixed interval after the successful upload of the previous logs.  If an
 // upload fails, the uploader will keep retrying the upload with an exponential
 // backoff interval.
 class LogUploader : public net::URLFetcherDelegate,
diff --git a/components/rappor/public/BUILD.gn b/components/rappor/public/BUILD.gn
new file mode 100644
index 0000000..7e766c61
--- /dev/null
+++ b/components/rappor/public/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("public") {
+  sources = [
+    "rappor_parameters.h",
+    "rappor_service.h",
+    "rappor_utils.h",
+    "sample.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
diff --git a/components/rappor/public/rappor_parameters.h b/components/rappor/public/rappor_parameters.h
new file mode 100644
index 0000000..0497615
--- /dev/null
+++ b/components/rappor/public/rappor_parameters.h
@@ -0,0 +1,183 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
+
+#include <string>
+
+namespace rappor {
+
+// Levels of noise added to a sample.
+enum NoiseLevel {
+  NO_NOISE = 0,
+  NORMAL_NOISE,
+  SPARSE_NOISE,
+  NUM_NOISE_LEVELS,
+};
+
+// The type of data stored in a metric.
+// Any use of the LOW_FREQUENCY types must be approved by Chrome Privacy and
+// the rappor-dev team.
+enum RapporType {
+  // Generic metrics from UMA opt-in users.
+  UMA_RAPPOR_TYPE = 0,
+  // Generic metrics for SafeBrowsing users. Deprecated, replaced by
+  // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE.
+  SAFEBROWSING_RAPPOR_TYPE,
+  // Deprecated: Use UMA_RAPPOR_TYPE for new metrics
+  ETLD_PLUS_ONE_RAPPOR_TYPE,
+  // Type for low-frequency metrics from UMA opt-in users.
+  LOW_FREQUENCY_UMA_RAPPOR_TYPE,
+  // Type for low-frequency metrics from SafeBrowsing users.
+  LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE,
+  // Type for low-frequency metrics from UMA opt-in users. Do not use for new
+  // metrics.
+  LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
+  NUM_RAPPOR_TYPES,
+  COARSE_RAPPOR_TYPE = SAFEBROWSING_RAPPOR_TYPE,
+};
+
+enum Probability {
+  PROBABILITY_100,  // 100%
+  PROBABILITY_75,   // 75%
+  PROBABILITY_50,   // 50%
+  PROBABILITY_25,   // 25%
+  PROBABILITY_0,    // 0%
+};
+
+// A metric is reported when its reporting group is in the set of groups
+// passed in to RapporServiceImpl::Start()
+enum RecordingGroup {
+  // Metrics for UMA users.
+  UMA_RAPPOR_GROUP = 1 << 0,
+  // Metrics related to SafeBrowsing, for SafeBrowsing users.
+  SAFEBROWSING_RAPPOR_GROUP = 1 << 1,
+};
+
+// An object describing noise probabilities for a noise level
+struct NoiseParameters {
+  // The probability that a bit will be redacted with fake data. This
+  // corresponds to the F privacy parameter.
+  Probability fake_prob;
+  // The probability that a fake bit will be a one.
+  Probability fake_one_prob;
+  // The probability that a one bit in the redacted data reports as one. This
+  // corresponds to the Q privacy parameter
+  Probability one_coin_prob;
+  // The probability that a zero bit in the redacted data reports as one. This
+  // corresponds to the P privacy parameter.
+  Probability zero_coin_prob;
+};
+
+// An object describing a rappor metric and the parameters used to generate it.
+//
+// For a full description of the rappor metrics, see
+// http://www.chromium.org/developers/design-documents/rappor
+struct RapporParameters {
+  // Get a string representing the parameters, for DCHECK_EQ.
+  std::string ToString() const;
+
+  // The maximum number of cohorts we divide clients into.
+  static const int kMaxCohorts;
+
+  // The number of cohorts to divide the reports for this metric into.
+  // This should divide kMaxCohorts evenly so that each cohort has an equal
+  // probability of being assigned users.
+  int num_cohorts;
+
+  // The number of bytes stored in the Bloom filter.
+  size_t bloom_filter_size_bytes;
+  // The number of hash functions used in the Bloom filter.
+  int bloom_filter_hash_function_count;
+
+  // The level of noise to use.
+  NoiseLevel noise_level;
+
+  // The reporting level this metric is reported at.
+  RecordingGroup recording_group;
+};
+
+namespace internal {
+
+const NoiseParameters kNoiseParametersForLevel[NUM_NOISE_LEVELS] = {
+    // NO_NOISE
+    {
+        rappor::PROBABILITY_0 /* Fake data probability */,
+        rappor::PROBABILITY_0 /* Fake one probability */,
+        rappor::PROBABILITY_100 /* One coin probability */,
+        rappor::PROBABILITY_0 /* Zero coin probability */,
+    },
+    // NORMAL_NOISE
+    {
+        rappor::PROBABILITY_50 /* Fake data probability */,
+        rappor::PROBABILITY_50 /* Fake one probability */,
+        rappor::PROBABILITY_75 /* One coin probability */,
+        rappor::PROBABILITY_25 /* Zero coin probability */,
+    },
+    // SPARSE_NOISE
+    {
+        rappor::PROBABILITY_25 /* Fake data probability */,
+        rappor::PROBABILITY_50 /* Fake one probability */,
+        rappor::PROBABILITY_75 /* One coin probability */,
+        rappor::PROBABILITY_25 /* Zero coin probability */,
+    },
+};
+
+const RapporParameters kRapporParametersForType[NUM_RAPPOR_TYPES] = {
+    // UMA_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        4 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::NORMAL_NOISE /* Noise level */,
+        UMA_RAPPOR_GROUP /* Recording group */
+    },
+    // SAFEBROWSING_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        1 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::NORMAL_NOISE /* Noise level */,
+        SAFEBROWSING_RAPPOR_GROUP /* Recording group */
+    },
+    // ETLD_PLUS_ONE_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        16 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::NORMAL_NOISE /* Noise level */,
+        UMA_RAPPOR_GROUP /* Recording group */
+    },
+    // LOW_FREQUENCY_UMA_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        4 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::SPARSE_NOISE /* Noise level */,
+        UMA_RAPPOR_GROUP /* Recording group */
+    },
+    // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        1 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::SPARSE_NOISE /* Noise level */,
+        SAFEBROWSING_RAPPOR_GROUP /* Recording group */
+    },
+    // LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE
+    {
+        128 /* Num cohorts */,
+        16 /* Bloom filter size bytes */,
+        2 /* Bloom filter hash count */,
+        rappor::SPARSE_NOISE /* Noise level */,
+        UMA_RAPPOR_GROUP /* Recording group */
+    },
+};
+
+}  // namespace internal
+
+}  // namespace rappor
+
+#endif  // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_PARAMETERS_H_
diff --git a/components/rappor/public/rappor_service.h b/components/rappor/public/rappor_service.h
new file mode 100644
index 0000000..73b7eb5
--- /dev/null
+++ b/components/rappor/public/rappor_service.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/sample.h"
+
+namespace rappor {
+
+// This class provides a public interface for recording samples for rappor
+// metrics, which other components can be depended on.
+class RapporService : public base::SupportsWeakPtr<RapporService> {
+ public:
+  // Constructs a Sample object for the caller to record fields in.
+  virtual std::unique_ptr<Sample> CreateSample(RapporType) = 0;
+
+  // Records a Sample of rappor metric specified by |metric_name|.
+  //
+  // example:
+  // std::unique_ptr<Sample> sample =
+  // rappor_service->CreateSample(MY_METRIC_TYPE);
+  // sample->SetStringField("Field1", "some string");
+  // sample->SetFlagsValue("Field2", SOME|FLAGS);
+  // rappor_service->RecordSample("MyMetric", std::move(sample));
+  virtual void RecordSample(const std::string& metric_name,
+                            std::unique_ptr<Sample> sample) = 0;
+
+  // Records a sample of the rappor metric specified by |metric_name|.
+  // Creates and initializes the metric, if it doesn't yet exist.
+  virtual void RecordSampleString(const std::string& metric_name,
+                                  RapporType type,
+                                  const std::string& sample) = 0;
+};
+
+}  // namespace rappor
+
+#endif  // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_SERVICE_H_
diff --git a/components/rappor/public/rappor_utils.h b/components/rappor/public/rappor_utils.h
new file mode 100644
index 0000000..77d71429
--- /dev/null
+++ b/components/rappor/public/rappor_utils.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
+#define COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
+
+#include <string>
+
+#include "components/rappor/public/rappor_service.h"
+
+class GURL;
+
+namespace rappor {
+
+// Records a string to a Rappor metric.
+// If |rappor_service| is NULL, this call does nothing.
+void SampleString(RapporService* rappor_service,
+                  const std::string& metric,
+                  RapporType type,
+                  const std::string& sample);
+
+// Extract the domain and registry for a sample from a GURL.
+// For file:// urls this will just return "file://" and for other special
+// schemes like chrome-extension will return the scheme and host.
+std::string GetDomainAndRegistrySampleFromGURL(const GURL& gurl);
+
+// Records the domain and registry of a url to a Rappor metric.
+// If |rappor_service| is NULL, this call does nothing.
+void SampleDomainAndRegistryFromGURL(RapporService* rappor_service,
+                                     const std::string& metric,
+                                     const GURL& gurl);
+
+// Returns NULL if there is no default service.
+RapporService* GetDefaultService();
+
+void SetDefaultServiceAccessor(RapporService* (*getDefaultService)());
+
+}  // namespace rappor
+
+#endif  // COMPONENTS_RAPPOR_PUBLIC_RAPPOR_UTILS_H_
diff --git a/components/rappor/public/sample.h b/components/rappor/public/sample.h
new file mode 100644
index 0000000..afdfa14
--- /dev/null
+++ b/components/rappor/public/sample.h
@@ -0,0 +1,92 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
+#define COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "components/rappor/public/rappor_parameters.h"
+
+namespace rappor {
+
+class RapporReports;
+class RapporServiceImpl;
+class TestSamplerFactory;
+
+// Sample is a container for information about a single instance of some event
+// we are sending Rappor data about.  It may contain multiple different fields,
+// which describe different details of the event, and they will be sent in the
+// same Rappor report, enabling analysis of correlations between those fields.
+class Sample {
+ public:
+  virtual ~Sample();
+
+  // Sets a string value field in this sample.
+  virtual void SetStringField(const std::string& field_name,
+                              const std::string& value);
+
+  // TODO(holte): Move all callers to the version with NoiseLevel.
+  virtual void SetFlagsField(const std::string& field_name,
+                             uint64_t flags,
+                             size_t num_flags);
+
+  // Sets a group of boolean flags as a field in this sample, with the
+  // specified noise level.
+  // |flags| should be a set of boolean flags stored in the lowest |num_flags|
+  // bits of |flags|.
+  virtual void SetFlagsField(const std::string& field_name,
+                             uint64_t flags,
+                             size_t num_flags,
+                             NoiseLevel noise_level);
+
+  // Sets an integer value field in this sample, at the given noise level.
+  virtual void SetUInt64Field(const std::string& field_name,
+                              uint64_t value,
+                              NoiseLevel noise_level);
+
+  // Generate randomized reports and store them in |reports|.
+  virtual void ExportMetrics(const std::string& secret,
+                             const std::string& metric_name,
+                             RapporReports* reports) const;
+
+  const RapporParameters& parameters() { return parameters_; }
+
+ private:
+  friend class TestSamplerFactory;
+  friend class RapporServiceImpl;
+  friend class TestSample;
+
+  // Constructs a sample.  Instead of calling this directly, call
+  // RapporService::MakeSampleObj to create a sample.
+  Sample(int32_t cohort_seed, const RapporParameters& parameters);
+
+  const RapporParameters parameters_;
+
+  // Offset used for bloom filter hash functions.
+  uint32_t bloom_offset_;
+
+  struct FieldInfo {
+    // Size of the field, in bytes.
+    size_t size;
+    // The non-randomized report value for the field.
+    uint64_t value;
+    // The noise level to use when creating a report for the field.
+    NoiseLevel noise_level;
+  };
+
+  // Information about all recorded fields.
+  std::map<std::string, FieldInfo> field_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(Sample);
+};
+
+}  // namespace rappor
+
+#endif  // COMPONENTS_RAPPOR_PUBLIC_SAMPLE_H_
diff --git a/components/rappor/rappor_metric.cc b/components/rappor/rappor_metric.cc
index c2a3cd1..ecc6ae3 100644
--- a/components/rappor/rappor_metric.cc
+++ b/components/rappor/rappor_metric.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "base/rand_util.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 #include "components/rappor/reports.h"
 
 namespace rappor {
diff --git a/components/rappor/rappor_metric.h b/components/rappor/rappor_metric.h
index 41d69ec0..6e17b1d 100644
--- a/components/rappor/rappor_metric.h
+++ b/components/rappor/rappor_metric.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "components/rappor/bloom_filter.h"
 #include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 
 namespace rappor {
 
@@ -20,7 +20,7 @@
 // and generates randomized reports about the collected data.
 //
 // This class should not be used directly by metrics clients.  Record metrics
-// using RapporService::RecordSample instead.
+// using RapporServiceImpl::RecordSample or RapporService::RecordSample instead.
 //
 // For a full description of the rappor metrics, see
 // http://www.chromium.org/developers/design-documents/rappor
diff --git a/components/rappor/rappor_parameters.cc b/components/rappor/rappor_parameters.cc
index ddad048..7ab8246e 100644
--- a/components/rappor/rappor_parameters.cc
+++ b/components/rappor/rappor_parameters.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 "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
diff --git a/components/rappor/rappor_parameters.h b/components/rappor/rappor_parameters.h
deleted file mode 100644
index 1696269f..0000000
--- a/components/rappor/rappor_parameters.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_RAPPOR_RAPPOR_PARAMETERS_H_
-#define COMPONENTS_RAPPOR_RAPPOR_PARAMETERS_H_
-
-#include <string>
-
-namespace rappor {
-
-// Levels of noise added to a sample.
-enum NoiseLevel {
-  NO_NOISE = 0,
-  NORMAL_NOISE,
-  SPARSE_NOISE,
-  NUM_NOISE_LEVELS,
-};
-
-// The type of data stored in a metric.
-// Any use of the LOW_FREQUENCY types must be approved by Chrome Privacy and
-// the rappor-dev team.
-enum RapporType {
-  // Generic metrics from UMA opt-in users.
-  UMA_RAPPOR_TYPE = 0,
-  // Generic metrics for SafeBrowsing users. Deprecated, replaced by
-  // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE.
-  SAFEBROWSING_RAPPOR_TYPE,
-  // Deprecated: Use UMA_RAPPOR_TYPE for new metrics
-  ETLD_PLUS_ONE_RAPPOR_TYPE,
-  // Type for low-frequency metrics from UMA opt-in users.
-  LOW_FREQUENCY_UMA_RAPPOR_TYPE,
-  // Type for low-frequency metrics from SafeBrowsing users.
-  LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE,
-  // Type for low-frequency metrics from UMA opt-in users. Do not use for new
-  // metrics.
-  LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
-  NUM_RAPPOR_TYPES,
-  COARSE_RAPPOR_TYPE = SAFEBROWSING_RAPPOR_TYPE,
-};
-
-enum Probability {
-  PROBABILITY_100,   // 100%
-  PROBABILITY_75,    // 75%
-  PROBABILITY_50,    // 50%
-  PROBABILITY_25,    // 25%
-  PROBABILITY_0,     // 0%
-};
-
-
-// A metric is reported when its reporting group is in the set of groups
-// passed in to RapporService::Start()
-enum RecordingGroup {
-  // Metrics for UMA users.
-  UMA_RAPPOR_GROUP = 1 << 0,
-  // Metrics related to SafeBrowsing, for SafeBrowsing users.
-  SAFEBROWSING_RAPPOR_GROUP = 1 << 1,
-};
-
-
-// An object describing noise probabilities for a noise level
-struct NoiseParameters {
-  // The probability that a bit will be redacted with fake data. This
-  // corresponds to the F privacy parameter.
-  Probability fake_prob;
-  // The probability that a fake bit will be a one.
-  Probability fake_one_prob;
-  // The probability that a one bit in the redacted data reports as one. This
-  // corresponds to the Q privacy parameter
-  Probability one_coin_prob;
-  // The probability that a zero bit in the redacted data reports as one. This
-  // corresponds to the P privacy parameter.
-  Probability zero_coin_prob;
-};
-
-// An object describing a rappor metric and the parameters used to generate it.
-//
-// For a full description of the rappor metrics, see
-// http://www.chromium.org/developers/design-documents/rappor
-struct RapporParameters {
-  // Get a string representing the parameters, for DCHECK_EQ.
-  std::string ToString() const;
-
-  // The maximum number of cohorts we divide clients into.
-  static const int kMaxCohorts;
-
-  // The number of cohorts to divide the reports for this metric into.
-  // This should divide kMaxCohorts evenly so that each cohort has an equal
-  // probability of being assigned users.
-  int num_cohorts;
-
-  // The number of bytes stored in the Bloom filter.
-  size_t bloom_filter_size_bytes;
-  // The number of hash functions used in the Bloom filter.
-  int bloom_filter_hash_function_count;
-
-  // The level of noise to use.
-  NoiseLevel noise_level;
-
-  // The reporting level this metric is reported at.
-  RecordingGroup recording_group;
-};
-
-namespace internal {
-
-const NoiseParameters kNoiseParametersForLevel[NUM_NOISE_LEVELS] = {
-    // NO_NOISE
-    {
-     rappor::PROBABILITY_0 /* Fake data probability */,
-     rappor::PROBABILITY_0 /* Fake one probability */,
-     rappor::PROBABILITY_100 /* One coin probability */,
-     rappor::PROBABILITY_0 /* Zero coin probability */,
-    },
-    // NORMAL_NOISE
-    {
-     rappor::PROBABILITY_50 /* Fake data probability */,
-     rappor::PROBABILITY_50 /* Fake one probability */,
-     rappor::PROBABILITY_75 /* One coin probability */,
-     rappor::PROBABILITY_25 /* Zero coin probability */,
-    },
-    // SPARSE_NOISE
-    {
-     rappor::PROBABILITY_25 /* Fake data probability */,
-     rappor::PROBABILITY_50 /* Fake one probability */,
-     rappor::PROBABILITY_75 /* One coin probability */,
-     rappor::PROBABILITY_25 /* Zero coin probability */,
-    },
-};
-
-const RapporParameters kRapporParametersForType[NUM_RAPPOR_TYPES] = {
-    // UMA_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     4 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::NORMAL_NOISE /* Noise level */,
-     UMA_RAPPOR_GROUP /* Recording group */},
-    // SAFEBROWSING_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     1 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::NORMAL_NOISE /* Noise level */,
-     SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
-    // ETLD_PLUS_ONE_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     16 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::NORMAL_NOISE /* Noise level */,
-     UMA_RAPPOR_GROUP /* Recording group */},
-    // LOW_FREQUENCY_UMA_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     4 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::SPARSE_NOISE /* Noise level */,
-     UMA_RAPPOR_GROUP /* Recording group */},
-    // LOW_FREQUENCY_SAFEBROWSING_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     1 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::SPARSE_NOISE /* Noise level */,
-     SAFEBROWSING_RAPPOR_GROUP /* Recording group */},
-    // LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE
-    {128 /* Num cohorts */,
-     16 /* Bloom filter size bytes */,
-     2 /* Bloom filter hash count */,
-     rappor::SPARSE_NOISE /* Noise level */,
-     UMA_RAPPOR_GROUP /* Recording group */},
-};
-
-}  // namespace internal
-
-}  // namespace rappor
-
-#endif  // COMPONENTS_RAPPOR_RAPPOR_PARAMETERS_H_
diff --git a/components/rappor/rappor_prefs.cc b/components/rappor/rappor_prefs.cc
index 20894fd..e16fb50 100644
--- a/components/rappor/rappor_prefs.cc
+++ b/components/rappor/rappor_prefs.cc
@@ -11,7 +11,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 #include "components/rappor/rappor_pref_names.h"
 
 namespace rappor {
diff --git a/components/rappor/rappor_recorder_impl.cc b/components/rappor/rappor_recorder_impl.cc
index 101c067..32032390 100644
--- a/components/rappor/rappor_recorder_impl.cc
+++ b/components/rappor/rappor_recorder_impl.cc
@@ -4,19 +4,19 @@
 
 #include "components/rappor/rappor_recorder_impl.h"
 
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace rappor {
 
-RapporRecorderImpl::RapporRecorderImpl(RapporService* rappor_service)
+RapporRecorderImpl::RapporRecorderImpl(RapporServiceImpl* rappor_service)
     : rappor_service_(rappor_service) {}
 
 RapporRecorderImpl::~RapporRecorderImpl() = default;
 
 // static
-void RapporRecorderImpl::Create(RapporService* rappor_service,
+void RapporRecorderImpl::Create(RapporServiceImpl* rappor_service,
                                 mojom::RapporRecorderRequest request) {
   mojo::MakeStrongBinding(base::MakeUnique<RapporRecorderImpl>(rappor_service),
                           std::move(request));
diff --git a/components/rappor/rappor_recorder_impl.h b/components/rappor/rappor_recorder_impl.h
index d558a274..103b4cd 100644
--- a/components/rappor/rappor_recorder_impl.h
+++ b/components/rappor/rappor_recorder_impl.h
@@ -12,16 +12,16 @@
 
 namespace rappor {
 
-class RapporService;
+class RapporServiceImpl;
 
 // Records aggregate, privacy-preserving samples from the renderers.
 // See https://www.chromium.org/developers/design-documents/rappor
 class RapporRecorderImpl : public mojom::RapporRecorder {
  public:
-  explicit RapporRecorderImpl(RapporService* rappor_service);
+  explicit RapporRecorderImpl(RapporServiceImpl* rappor_service);
   ~RapporRecorderImpl() override;
 
-  static void Create(RapporService* rappor_service,
+  static void Create(RapporServiceImpl* rappor_service,
                      mojom::RapporRecorderRequest request);
 
  private:
@@ -30,7 +30,7 @@
                     const std::string& sample) override;
   void RecordRapporURL(const std::string& metric, const GURL& sample) override;
 
-  RapporService* rappor_service_;
+  RapporServiceImpl* rappor_service_;
   base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(RapporRecorderImpl);
diff --git a/components/rappor/rappor_service.cc b/components/rappor/rappor_service.cc
deleted file mode 100644
index 18d57eea..0000000
--- a/components/rappor/rappor_service.cc
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/rappor/rappor_service.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/time/time.h"
-#include "components/rappor/log_uploader.h"
-#include "components/rappor/proto/rappor_metric.pb.h"
-#include "components/rappor/rappor_metric.h"
-#include "components/rappor/rappor_pref_names.h"
-#include "components/rappor/rappor_prefs.h"
-#include "components/variations/variations_associated_data.h"
-
-namespace rappor {
-
-namespace {
-
-// Seconds before the initial log is generated.
-const int kInitialLogIntervalSeconds = 15;
-// Interval between ongoing logs.
-const int kLogIntervalSeconds = 30 * 60;
-
-const char kMimeType[] = "application/vnd.chrome.rappor";
-
-const char kRapporDailyEventHistogram[] = "Rappor.DailyEvent.IntervalType";
-
-// Constants for the RAPPOR rollout field trial.
-const char kRapporRolloutFieldTrialName[] = "RapporRollout";
-
-// Constant for the finch parameter name for the server URL
-const char kRapporRolloutServerUrlParam[] = "ServerUrl";
-
-// The rappor server's URL.
-const char kDefaultServerUrl[] = "https://clients4.google.com/rappor";
-
-GURL GetServerUrl() {
-  std::string server_url = variations::GetVariationParamValue(
-      kRapporRolloutFieldTrialName,
-      kRapporRolloutServerUrlParam);
-  if (!server_url.empty())
-    return GURL(server_url);
-  else
-    return GURL(kDefaultServerUrl);
-}
-
-}  // namespace
-
-RapporService::RapporService(
-    PrefService* pref_service,
-    const base::Callback<bool(void)> is_incognito_callback)
-    : pref_service_(pref_service),
-      is_incognito_callback_(is_incognito_callback),
-      cohort_(-1),
-      daily_event_(pref_service,
-                   prefs::kRapporLastDailySample,
-                   kRapporDailyEventHistogram),
-      recording_groups_(0) {
-}
-
-RapporService::~RapporService() {
-}
-
-void RapporService::AddDailyObserver(
-    std::unique_ptr<metrics::DailyEvent::Observer> observer) {
-  daily_event_.AddObserver(std::move(observer));
-}
-
-void RapporService::Initialize(net::URLRequestContextGetter* request_context) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!IsInitialized());
-  const GURL server_url = GetServerUrl();
-  if (!server_url.is_valid()) {
-    DVLOG(1) << server_url.spec() << " is invalid. "
-             << "RapporService not started.";
-    return;
-  }
-  DVLOG(1) << "RapporService reporting to " << server_url.spec();
-  InitializeInternal(
-      base::MakeUnique<LogUploader>(server_url, kMimeType, request_context),
-      internal::LoadCohort(pref_service_), internal::LoadSecret(pref_service_));
-}
-
-void RapporService::Update(int recording_groups, bool may_upload) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsInitialized());
-  if (recording_groups_ != recording_groups) {
-    if (recording_groups == 0) {
-      DVLOG(1) << "Rappor service stopped because all groups were disabled.";
-      recording_groups_ = 0;
-      CancelNextLogRotation();
-    } else if (recording_groups_ == 0) {
-      DVLOG(1) << "RapporService started for groups: "
-               << recording_groups;
-      recording_groups_ = recording_groups;
-      ScheduleNextLogRotation(
-          base::TimeDelta::FromSeconds(kInitialLogIntervalSeconds));
-    } else {
-      DVLOG(1) << "RapporService recording_groups changed:" << recording_groups;
-      recording_groups_ = recording_groups;
-    }
-  }
-
-  DVLOG(1) << "RapporService recording_groups=" << recording_groups_
-           << " may_upload=" << may_upload;
-  if (may_upload) {
-    uploader_->Start();
-  } else {
-    uploader_->Stop();
-  }
-}
-
-// static
-void RapporService::RegisterPrefs(PrefRegistrySimple* registry) {
-  internal::RegisterPrefs(registry);
-}
-
-void RapporService::InitializeInternal(
-    std::unique_ptr<LogUploaderInterface> uploader,
-    int32_t cohort,
-    const std::string& secret) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!IsInitialized());
-  DCHECK(secret_.empty());
-  uploader_.swap(uploader);
-  cohort_ = cohort;
-  secret_ = secret;
-}
-
-void RapporService::CancelNextLogRotation() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  metrics_map_.clear();
-  log_rotation_timer_.Stop();
-}
-
-void RapporService::ScheduleNextLogRotation(base::TimeDelta interval) {
-  log_rotation_timer_.Start(FROM_HERE,
-                            interval,
-                            this,
-                            &RapporService::OnLogInterval);
-}
-
-void RapporService::OnLogInterval() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(uploader_);
-  DVLOG(2) << "RapporService::OnLogInterval";
-  daily_event_.CheckInterval();
-  RapporReports reports;
-  if (ExportMetrics(&reports)) {
-    std::string log_text;
-    bool success = reports.SerializeToString(&log_text);
-    DCHECK(success);
-    DVLOG(1) << "RapporService sending a report of "
-             << reports.report_size() << " value(s).";
-    uploader_->QueueLog(log_text);
-  }
-  ScheduleNextLogRotation(base::TimeDelta::FromSeconds(kLogIntervalSeconds));
-}
-
-bool RapporService::ExportMetrics(RapporReports* reports) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_GE(cohort_, 0);
-  reports->set_cohort(cohort_);
-
-  for (const auto& kv : metrics_map_) {
-    const RapporMetric* metric = kv.second.get();
-    RapporReports::Report* report = reports->add_report();
-    report->set_name_hash(base::HashMetricName(kv.first));
-    ByteVector bytes = metric->GetReport(secret_);
-    report->set_bits(std::string(bytes.begin(), bytes.end()));
-    DVLOG(2) << "Exporting metric " << kv.first;
-  }
-  metrics_map_.clear();
-
-  sampler_.ExportMetrics(secret_, reports);
-
-  DVLOG(2) << "Generated a report with " << reports->report_size()
-           << " metrics.";
-  return reports->report_size() > 0;
-}
-
-bool RapporService::IsInitialized() const {
-  return cohort_ >= 0;
-}
-
-bool RapporService::RecordingAllowed(const RapporParameters& parameters) {
-  // Skip recording in incognito mode.
-  if (is_incognito_callback_.Run()) {
-    DVLOG(2) << "Metric not logged due to incognito mode.";
-    return false;
-  }
-  // Skip this metric if its recording_group is not enabled.
-  if (!(recording_groups_ & parameters.recording_group)) {
-    DVLOG(2) << "Metric not logged due to recording_group "
-             << recording_groups_ << " < " << parameters.recording_group;
-    return false;
-  }
-  return true;
-}
-
-void RapporService::RecordSample(const std::string& metric_name,
-                                 RapporType type,
-                                 const std::string& sample) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  // Ignore the sample if the service hasn't started yet.
-  if (!IsInitialized())
-    return;
-  DCHECK_LT(type, NUM_RAPPOR_TYPES);
-  const RapporParameters& parameters = internal::kRapporParametersForType[type];
-  DVLOG(2) << "Recording sample \"" << sample
-           << "\" for metric \"" << metric_name
-           << "\" of type: " << type;
-  RecordSampleInternal(metric_name, parameters, sample);
-}
-
-void RapporService::RecordSampleInternal(const std::string& metric_name,
-                                         const RapporParameters& parameters,
-                                         const std::string& sample) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsInitialized());
-  if (!RecordingAllowed(parameters))
-    return;
-  RapporMetric* metric = LookUpMetric(metric_name, parameters);
-  metric->AddSample(sample);
-}
-
-RapporMetric* RapporService::LookUpMetric(const std::string& metric_name,
-                                          const RapporParameters& parameters) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsInitialized());
-  auto it = metrics_map_.find(metric_name);
-  if (it != metrics_map_.end()) {
-    RapporMetric* metric = it->second.get();
-    DCHECK_EQ(parameters.ToString(), metric->parameters().ToString());
-    return metric;
-  }
-
-  RapporMetric* new_metric = new RapporMetric(metric_name, parameters, cohort_);
-  metrics_map_[metric_name] = base::WrapUnique(new_metric);
-  return new_metric;
-}
-
-std::unique_ptr<Sample> RapporService::CreateSample(RapporType type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsInitialized());
-  return base::WrapUnique(
-      new Sample(cohort_, internal::kRapporParametersForType[type]));
-}
-
-void RapporService::RecordSampleObj(const std::string& metric_name,
-                                    std::unique_ptr<Sample> sample) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (!RecordingAllowed(sample->parameters()))
-    return;
-  DVLOG(1) << "Recording sample of metric \"" << metric_name << "\"";
-  sampler_.AddSample(metric_name, std::move(sample));
-}
-
-}  // namespace rappor
diff --git a/components/rappor/rappor_service.h b/components/rappor/rappor_service.h
deleted file mode 100644
index 3bafcad7..0000000
--- a/components/rappor/rappor_service.h
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
-#define COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/timer/timer.h"
-#include "components/metrics/daily_event.h"
-#include "components/rappor/rappor_parameters.h"
-#include "components/rappor/sample.h"
-#include "components/rappor/sampler.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace rappor {
-
-class LogUploaderInterface;
-class RapporMetric;
-class RapporReports;
-
-// This class provides an interface for recording samples for rappor metrics,
-// and periodically generates and uploads reports based on the collected data.
-class RapporService : public base::SupportsWeakPtr<RapporService> {
- public:
-  // Constructs a RapporService.
-  // Calling code is responsible for ensuring that the lifetime of
-  // |pref_service| is longer than the lifetime of RapporService.
-  // |is_incognito_callback| will be called to test if incognito mode is active.
-  RapporService(PrefService* pref_service,
-                const base::Callback<bool(void)> is_incognito_callback);
-  virtual ~RapporService();
-
-  // Add an observer for collecting daily metrics.
-  void AddDailyObserver(
-      std::unique_ptr<metrics::DailyEvent::Observer> observer);
-
-  // Initializes the rappor service, including loading the cohort and secret
-  // preferences from disk.
-  void Initialize(net::URLRequestContextGetter* context);
-
-  // Updates the settings for metric recording and uploading.
-  // The RapporService must be initialized before this method is called.
-  // |recording_groups| should be set of flags, e.g.
-  //    UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
-  // If it contains any enabled groups, periodic reports will be
-  // generated and queued for upload.
-  // If |may_upload| is true, reports will be uploaded from the queue.
-  void Update(int recording_groups, bool may_upload);
-
-  // Constructs a Sample object for the caller to record fields in.
-  virtual std::unique_ptr<Sample> CreateSample(RapporType);
-
-  // Records a Sample of rappor metric specified by |metric_name|.
-  //
-  // TODO(holte): Rename RecordSample to RecordString and then rename this
-  // to RecordSample.
-  //
-  // example:
-  // std::unique_ptr<Sample> sample =
-  // rappor_service->CreateSample(MY_METRIC_TYPE);
-  // sample->SetStringField("Field1", "some string");
-  // sample->SetFlagsValue("Field2", SOME|FLAGS);
-  // rappor_service->RecordSample("MyMetric", std::move(sample));
-  //
-  // This will result in a report setting two metrics "MyMetric.Field1" and
-  // "MyMetric.Field2", and they will both be generated from the same sample,
-  // to allow for correlations to be computed.
-  virtual void RecordSampleObj(const std::string& metric_name,
-                               std::unique_ptr<Sample> sample);
-
-  // Records a sample of the rappor metric specified by |metric_name|.
-  // Creates and initializes the metric, if it doesn't yet exist.
-  virtual void RecordSample(const std::string& metric_name,
-                            RapporType type,
-                            const std::string& sample);
-
-  // Registers the names of all of the preferences used by RapporService in the
-  // provided PrefRegistry. This should be called before calling Start().
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
- protected:
-  // Initializes the state of the RapporService.
-  void InitializeInternal(std::unique_ptr<LogUploaderInterface> uploader,
-                          int32_t cohort,
-                          const std::string& secret);
-
-  // Cancels the next call to OnLogInterval.
-  virtual void CancelNextLogRotation();
-
-  // Schedules the next call to OnLogInterval.
-  virtual void ScheduleNextLogRotation(base::TimeDelta interval);
-
-  // Logs all of the collected metrics to the reports proto message and clears
-  // the internal map. Exposed for tests. Returns true if any metrics were
-  // recorded.
-  bool ExportMetrics(RapporReports* reports);
-
- private:
-  // Records a sample of the rappor metric specified by |parameters|.
-  // Creates and initializes the metric, if it doesn't yet exist.
-  // Exposed for tests.
-  void RecordSampleInternal(const std::string& metric_name,
-                            const RapporParameters& parameters,
-                            const std::string& sample);
-
-  // Checks if the service has been started successfully.
-  bool IsInitialized() const;
-
-  // Called whenever the logging interval elapses to generate a new log of
-  // reports and pass it to the uploader.
-  void OnLogInterval();
-
-  // Check if recording of the metric is allowed, given it's parameters.
-  // This will check that we are not in incognito mode, and that the
-  // appropriate recording group is enabled.
-  bool RecordingAllowed(const RapporParameters& parameters);
-
-  // Finds a metric in the metrics_map_, creating it if it doesn't already
-  // exist.
-  RapporMetric* LookUpMetric(const std::string& metric_name,
-                             const RapporParameters& parameters);
-
-  // A weak pointer to the PrefService used to read and write preferences.
-  PrefService* pref_service_;
-
-  // A callback for testing if incognito mode is active;
-  const base::Callback<bool(void)> is_incognito_callback_;
-
-  // Client-side secret used to generate fake bits.
-  std::string secret_;
-
-  // The cohort this client is assigned to. -1 is uninitialized.
-  int32_t cohort_;
-
-  // Timer which schedules calls to OnLogInterval().
-  base::OneShotTimer log_rotation_timer_;
-
-  // A daily event for collecting metrics once a day.
-  metrics::DailyEvent daily_event_;
-
-  // A private LogUploader instance for sending reports to the server.
-  std::unique_ptr<LogUploaderInterface> uploader_;
-
-  // The set of recording groups that metrics are being recorded, e.g.
-  //     UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
-  int recording_groups_;
-
-  // We keep all registered metrics in a map, from name to metric.
-  std::map<std::string, std::unique_ptr<RapporMetric>> metrics_map_;
-
-  internal::Sampler sampler_;
-
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(RapporService);
-};
-
-}  // namespace rappor
-
-#endif  // COMPONENTS_RAPPOR_RAPPOR_SERVICE_H_
diff --git a/components/rappor/rappor_service_impl.cc b/components/rappor/rappor_service_impl.cc
new file mode 100644
index 0000000..19bfc67
--- /dev/null
+++ b/components/rappor/rappor_service_impl.cc
@@ -0,0 +1,260 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/rappor/rappor_service_impl.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/time/time.h"
+#include "components/rappor/log_uploader.h"
+#include "components/rappor/proto/rappor_metric.pb.h"
+#include "components/rappor/rappor_metric.h"
+#include "components/rappor/rappor_pref_names.h"
+#include "components/rappor/rappor_prefs.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace rappor {
+
+namespace {
+
+// Seconds before the initial log is generated.
+const int kInitialLogIntervalSeconds = 15;
+// Interval between ongoing logs.
+const int kLogIntervalSeconds = 30 * 60;
+
+const char kMimeType[] = "application/vnd.chrome.rappor";
+
+const char kRapporDailyEventHistogram[] = "Rappor.DailyEvent.IntervalType";
+
+// Constants for the RAPPOR rollout field trial.
+const char kRapporRolloutFieldTrialName[] = "RapporRollout";
+
+// Constant for the finch parameter name for the server URL
+const char kRapporRolloutServerUrlParam[] = "ServerUrl";
+
+// The rappor server's URL.
+const char kDefaultServerUrl[] = "https://clients4.google.com/rappor";
+
+GURL GetServerUrl() {
+  std::string server_url = variations::GetVariationParamValue(
+      kRapporRolloutFieldTrialName, kRapporRolloutServerUrlParam);
+  if (!server_url.empty())
+    return GURL(server_url);
+  else
+    return GURL(kDefaultServerUrl);
+}
+
+}  // namespace
+
+RapporServiceImpl::RapporServiceImpl(
+    PrefService* pref_service,
+    const base::Callback<bool(void)> is_incognito_callback)
+    : pref_service_(pref_service),
+      is_incognito_callback_(is_incognito_callback),
+      cohort_(-1),
+      daily_event_(pref_service,
+                   prefs::kRapporLastDailySample,
+                   kRapporDailyEventHistogram),
+      recording_groups_(0) {}
+
+RapporServiceImpl::~RapporServiceImpl() {}
+
+void RapporServiceImpl::AddDailyObserver(
+    std::unique_ptr<metrics::DailyEvent::Observer> observer) {
+  daily_event_.AddObserver(std::move(observer));
+}
+
+void RapporServiceImpl::Initialize(
+    net::URLRequestContextGetter* request_context) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!IsInitialized());
+  const GURL server_url = GetServerUrl();
+  if (!server_url.is_valid()) {
+    DVLOG(1) << server_url.spec() << " is invalid. "
+             << "RapporServiceImpl not started.";
+    return;
+  }
+  DVLOG(1) << "RapporServiceImpl reporting to " << server_url.spec();
+  InitializeInternal(
+      base::MakeUnique<LogUploader>(server_url, kMimeType, request_context),
+      internal::LoadCohort(pref_service_), internal::LoadSecret(pref_service_));
+}
+
+void RapporServiceImpl::Update(int recording_groups, bool may_upload) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(IsInitialized());
+  if (recording_groups_ != recording_groups) {
+    if (recording_groups == 0) {
+      DVLOG(1) << "Rappor service stopped because all groups were disabled.";
+      recording_groups_ = 0;
+      CancelNextLogRotation();
+    } else if (recording_groups_ == 0) {
+      DVLOG(1) << "RapporServiceImpl started for groups: " << recording_groups;
+      recording_groups_ = recording_groups;
+      ScheduleNextLogRotation(
+          base::TimeDelta::FromSeconds(kInitialLogIntervalSeconds));
+    } else {
+      DVLOG(1) << "RapporServiceImpl recording_groups changed:"
+               << recording_groups;
+      recording_groups_ = recording_groups;
+    }
+  }
+
+  DVLOG(1) << "RapporServiceImpl recording_groups=" << recording_groups_
+           << " may_upload=" << may_upload;
+  if (may_upload) {
+    uploader_->Start();
+  } else {
+    uploader_->Stop();
+  }
+}
+
+// static
+void RapporServiceImpl::RegisterPrefs(PrefRegistrySimple* registry) {
+  internal::RegisterPrefs(registry);
+}
+
+void RapporServiceImpl::InitializeInternal(
+    std::unique_ptr<LogUploaderInterface> uploader,
+    int32_t cohort,
+    const std::string& secret) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!IsInitialized());
+  DCHECK(secret_.empty());
+  uploader_.swap(uploader);
+  cohort_ = cohort;
+  secret_ = secret;
+}
+
+void RapporServiceImpl::CancelNextLogRotation() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  metrics_map_.clear();
+  log_rotation_timer_.Stop();
+}
+
+void RapporServiceImpl::ScheduleNextLogRotation(base::TimeDelta interval) {
+  log_rotation_timer_.Start(FROM_HERE, interval, this,
+                            &RapporServiceImpl::OnLogInterval);
+}
+
+void RapporServiceImpl::OnLogInterval() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(uploader_);
+  DVLOG(2) << "RapporServiceImpl::OnLogInterval";
+  daily_event_.CheckInterval();
+  RapporReports reports;
+  if (ExportMetrics(&reports)) {
+    std::string log_text;
+    bool success = reports.SerializeToString(&log_text);
+    DCHECK(success);
+    DVLOG(1) << "RapporServiceImpl sending a report of "
+             << reports.report_size() << " value(s).";
+    uploader_->QueueLog(log_text);
+  }
+  ScheduleNextLogRotation(base::TimeDelta::FromSeconds(kLogIntervalSeconds));
+}
+
+bool RapporServiceImpl::ExportMetrics(RapporReports* reports) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_GE(cohort_, 0);
+  reports->set_cohort(cohort_);
+
+  for (const auto& kv : metrics_map_) {
+    const RapporMetric* metric = kv.second.get();
+    RapporReports::Report* report = reports->add_report();
+    report->set_name_hash(base::HashMetricName(kv.first));
+    ByteVector bytes = metric->GetReport(secret_);
+    report->set_bits(std::string(bytes.begin(), bytes.end()));
+    DVLOG(2) << "Exporting metric " << kv.first;
+  }
+  metrics_map_.clear();
+
+  sampler_.ExportMetrics(secret_, reports);
+
+  DVLOG(2) << "Generated a report with " << reports->report_size()
+           << " metrics.";
+  return reports->report_size() > 0;
+}
+
+bool RapporServiceImpl::IsInitialized() const {
+  return cohort_ >= 0;
+}
+
+bool RapporServiceImpl::RecordingAllowed(const RapporParameters& parameters) {
+  // Skip recording in incognito mode.
+  if (is_incognito_callback_.Run()) {
+    DVLOG(2) << "Metric not logged due to incognito mode.";
+    return false;
+  }
+  // Skip this metric if its recording_group is not enabled.
+  if (!(recording_groups_ & parameters.recording_group)) {
+    DVLOG(2) << "Metric not logged due to recording_group " << recording_groups_
+             << " < " << parameters.recording_group;
+    return false;
+  }
+  return true;
+}
+
+void RapporServiceImpl::RecordSampleString(const std::string& metric_name,
+                                           RapporType type,
+                                           const std::string& sample) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Ignore the sample if the service hasn't started yet.
+  if (!IsInitialized())
+    return;
+  DCHECK_LT(type, NUM_RAPPOR_TYPES);
+  const RapporParameters& parameters = internal::kRapporParametersForType[type];
+  DVLOG(2) << "Recording sample \"" << sample << "\" for metric \""
+           << metric_name << "\" of type: " << type;
+  RecordSampleInternal(metric_name, parameters, sample);
+}
+
+void RapporServiceImpl::RecordSampleInternal(const std::string& metric_name,
+                                             const RapporParameters& parameters,
+                                             const std::string& sample) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(IsInitialized());
+  if (!RecordingAllowed(parameters))
+    return;
+  RapporMetric* metric = LookUpMetric(metric_name, parameters);
+  metric->AddSample(sample);
+}
+
+RapporMetric* RapporServiceImpl::LookUpMetric(
+    const std::string& metric_name,
+    const RapporParameters& parameters) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(IsInitialized());
+  auto it = metrics_map_.find(metric_name);
+  if (it != metrics_map_.end()) {
+    RapporMetric* metric = it->second.get();
+    DCHECK_EQ(parameters.ToString(), metric->parameters().ToString());
+    return metric;
+  }
+
+  RapporMetric* new_metric = new RapporMetric(metric_name, parameters, cohort_);
+  metrics_map_[metric_name] = base::WrapUnique(new_metric);
+  return new_metric;
+}
+
+std::unique_ptr<Sample> RapporServiceImpl::CreateSample(RapporType type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(IsInitialized());
+  return base::WrapUnique(
+      new Sample(cohort_, internal::kRapporParametersForType[type]));
+}
+
+void RapporServiceImpl::RecordSample(const std::string& metric_name,
+                                     std::unique_ptr<Sample> sample) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!RecordingAllowed(sample->parameters()))
+    return;
+  DVLOG(1) << "Recording sample of metric \"" << metric_name << "\"";
+  sampler_.AddSample(metric_name, std::move(sample));
+}
+
+}  // namespace rappor
diff --git a/components/rappor/rappor_service_impl.h b/components/rappor/rappor_service_impl.h
new file mode 100644
index 0000000..625ff9d
--- /dev/null
+++ b/components/rappor/rappor_service_impl.h
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
+#define COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
+#include "components/metrics/daily_event.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/rappor_service.h"
+#include "components/rappor/public/sample.h"
+#include "components/rappor/sampler.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace rappor {
+
+class LogUploaderInterface;
+class RapporMetric;
+class RapporReports;
+
+// This class provides an interface for recording samples for rappor metrics,
+// and periodically generates and uploads reports based on the collected data.
+class RapporServiceImpl : public RapporService {
+ public:
+  // Constructs a RapporServiceImpl.
+  // Calling code is responsible for ensuring that the lifetime of
+  // |pref_service| is longer than the lifetime of RapporServiceImpl.
+  // |is_incognito_callback| will be called to test if incognito mode is active.
+  RapporServiceImpl(PrefService* pref_service,
+                    const base::Callback<bool(void)> is_incognito_callback);
+  virtual ~RapporServiceImpl();
+
+  // Add an observer for collecting daily metrics.
+  void AddDailyObserver(
+      std::unique_ptr<metrics::DailyEvent::Observer> observer);
+
+  // Initializes the rappor service, including loading the cohort and secret
+  // preferences from disk.
+  void Initialize(net::URLRequestContextGetter* context);
+
+  // Updates the settings for metric recording and uploading.
+  // The RapporServiceImpl must be initialized before this method is called.
+  // |recording_groups| should be set of flags, e.g.
+  //    UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
+  // If it contains any enabled groups, periodic reports will be
+  // generated and queued for upload.
+  // If |may_upload| is true, reports will be uploaded from the queue.
+  void Update(int recording_groups, bool may_upload);
+
+  // Constructs a Sample object for the caller to record fields in.
+  std::unique_ptr<Sample> CreateSample(RapporType) override;
+
+  // Records a Sample of rappor metric specified by |metric_name|.
+  //
+  // example:
+  // std::unique_ptr<Sample> sample =
+  // rappor_service->CreateSample(MY_METRIC_TYPE);
+  // sample->SetStringField("Field1", "some string");
+  // sample->SetFlagsValue("Field2", SOME|FLAGS);
+  // rappor_service->RecordSample("MyMetric", std::move(sample));
+  //
+  // This will result in a report setting two metrics "MyMetric.Field1" and
+  // "MyMetric.Field2", and they will both be generated from the same sample,
+  // to allow for correlations to be computed.
+  void RecordSample(const std::string& metric_name,
+                    std::unique_ptr<Sample> sample) override;
+
+  // Records a sample of the rappor metric specified by |metric_name|.
+  // Creates and initializes the metric, if it doesn't yet exist.
+  void RecordSampleString(const std::string& metric_name,
+                          RapporType type,
+                          const std::string& sample) override;
+
+  // Registers the names of all of the preferences used by RapporServiceImpl in
+  // the provided PrefRegistry. This should be called before calling Start().
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ protected:
+  // Initializes the state of the RapporServiceImpl.
+  void InitializeInternal(std::unique_ptr<LogUploaderInterface> uploader,
+                          int32_t cohort,
+                          const std::string& secret);
+
+  // Cancels the next call to OnLogInterval.
+  virtual void CancelNextLogRotation();
+
+  // Schedules the next call to OnLogInterval.
+  virtual void ScheduleNextLogRotation(base::TimeDelta interval);
+
+  // Logs all of the collected metrics to the reports proto message and clears
+  // the internal map. Exposed for tests. Returns true if any metrics were
+  // recorded.
+  bool ExportMetrics(RapporReports* reports);
+
+ private:
+  // Records a sample of the rappor metric specified by |parameters|.
+  // Creates and initializes the metric, if it doesn't yet exist.
+  // Exposed for tests.
+  void RecordSampleInternal(const std::string& metric_name,
+                            const RapporParameters& parameters,
+                            const std::string& sample);
+
+  // Checks if the service has been started successfully.
+  bool IsInitialized() const;
+
+  // Called whenever the logging interval elapses to generate a new log of
+  // reports and pass it to the uploader.
+  void OnLogInterval();
+
+  // Check if recording of the metric is allowed, given it's parameters.
+  // This will check that we are not in incognito mode, and that the
+  // appropriate recording group is enabled.
+  bool RecordingAllowed(const RapporParameters& parameters);
+
+  // Finds a metric in the metrics_map_, creating it if it doesn't already
+  // exist.
+  RapporMetric* LookUpMetric(const std::string& metric_name,
+                             const RapporParameters& parameters);
+
+  // A weak pointer to the PrefService used to read and write preferences.
+  PrefService* pref_service_;
+
+  // A callback for testing if incognito mode is active;
+  const base::Callback<bool(void)> is_incognito_callback_;
+
+  // Client-side secret used to generate fake bits.
+  std::string secret_;
+
+  // The cohort this client is assigned to. -1 is uninitialized.
+  int32_t cohort_;
+
+  // Timer which schedules calls to OnLogInterval().
+  base::OneShotTimer log_rotation_timer_;
+
+  // A daily event for collecting metrics once a day.
+  metrics::DailyEvent daily_event_;
+
+  // A private LogUploader instance for sending reports to the server.
+  std::unique_ptr<LogUploaderInterface> uploader_;
+
+  // The set of recording groups that metrics are being recorded, e.g.
+  //     UMA_RECORDING_GROUP | SAFEBROWSING_RECORDING_GROUP
+  int recording_groups_;
+
+  // We keep all registered metrics in a map, from name to metric.
+  std::map<std::string, std::unique_ptr<RapporMetric>> metrics_map_;
+
+  internal::Sampler sampler_;
+
+  base::ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(RapporServiceImpl);
+};
+
+}  // namespace rappor
+
+#endif  // COMPONENTS_RAPPOR_RAPPOR_SERVICE_IMPL_H_
diff --git a/components/rappor/rappor_service_unittest.cc b/components/rappor/rappor_service_unittest.cc
index 2c1e8b6c..825458a 100644
--- a/components/rappor/rappor_service_unittest.cc
+++ b/components/rappor/rappor_service_unittest.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 "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -15,7 +15,7 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/rappor/byte_vector_utils.h"
 #include "components/rappor/proto/rappor_metric.pb.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 #include "components/rappor/rappor_pref_names.h"
 #include "components/rappor/test_log_uploader.h"
 #include "components/rappor/test_rappor_service.h"
@@ -23,9 +23,9 @@
 
 namespace rappor {
 
-TEST(RapporServiceTest, Update) {
+TEST(RapporServiceImplTest, Update) {
   // Test rappor service initially has uploading and reporting enabled.
-  TestRapporService rappor_service;
+  TestRapporServiceImpl rappor_service;
   EXPECT_LT(base::TimeDelta(), rappor_service.next_rotation());
   EXPECT_TRUE(rappor_service.test_uploader()->is_running());
 
@@ -47,12 +47,14 @@
 }
 
 // Check that samples can be recorded and exported.
-TEST(RapporServiceTest, RecordAndExportMetrics) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, RecordAndExportMetrics) {
+  TestRapporServiceImpl rappor_service;
 
   // Multiple samples for the same metric should only generate one report.
-  rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "foo");
-  rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "bar");
+  rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+                                    "foo");
+  rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+                                    "bar");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -65,12 +67,12 @@
 }
 
 // Check that the reporting groups are respected.
-TEST(RapporServiceTest, UmaRecordingGroup) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, UmaRecordingGroup) {
+  TestRapporServiceImpl rappor_service;
   rappor_service.Update(SAFEBROWSING_RAPPOR_GROUP, false);
 
   // Wrong recording group.
-  rappor_service.RecordSample("UmaMetric", UMA_RAPPOR_TYPE, "foo");
+  rappor_service.RecordSampleString("UmaMetric", UMA_RAPPOR_TYPE, "foo");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -78,12 +80,13 @@
 }
 
 // Check that the reporting groups are respected.
-TEST(RapporServiceTest, SafeBrowsingRecordingGroup) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, SafeBrowsingRecordingGroup) {
+  TestRapporServiceImpl rappor_service;
   rappor_service.Update(UMA_RAPPOR_GROUP, false);
 
   // Wrong recording group.
-  rappor_service.RecordSample("SbMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
+  rappor_service.RecordSampleString("SbMetric", SAFEBROWSING_RAPPOR_TYPE,
+                                    "foo");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -91,12 +94,14 @@
 }
 
 // Check that GetRecordedSampleForMetric works as expected.
-TEST(RapporServiceTest, GetRecordedSampleForMetric) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, GetRecordedSampleForMetric) {
+  TestRapporServiceImpl rappor_service;
 
   // Multiple samples for the same metric; only the latest is remembered.
-  rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "foo");
-  rappor_service.RecordSample("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE, "bar");
+  rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+                                    "foo");
+  rappor_service.RecordSampleString("MyMetric", ETLD_PLUS_ONE_RAPPOR_TYPE,
+                                    "bar");
 
   std::string sample;
   RapporType type;
@@ -109,11 +114,12 @@
 }
 
 // Check that the incognito is respected.
-TEST(RapporServiceTest, Incognito) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, Incognito) {
+  TestRapporServiceImpl rappor_service;
   rappor_service.set_is_incognito(true);
 
-  rappor_service.RecordSample("MyMetric", SAFEBROWSING_RAPPOR_TYPE, "foo");
+  rappor_service.RecordSampleString("MyMetric", SAFEBROWSING_RAPPOR_TYPE,
+                                    "foo");
 
   RapporReports reports;
   rappor_service.GetReports(&reports);
@@ -121,13 +127,13 @@
 }
 
 // Check that Sample objects record correctly.
-TEST(RapporServiceTest, RecordSample) {
-  TestRapporService rappor_service;
+TEST(RapporServiceImplTest, RecordSample) {
+  TestRapporServiceImpl rappor_service;
   std::unique_ptr<Sample> sample =
       rappor_service.CreateSample(SAFEBROWSING_RAPPOR_TYPE);
   sample->SetStringField("Url", "example.com");
   sample->SetFlagsField("Flags1", 0xbcd, 12);
-  rappor_service.RecordSampleObj("ObjMetric", std::move(sample));
+  rappor_service.RecordSample("ObjMetric", std::move(sample));
   uint64_t url_hash = base::HashMetricName("ObjMetric.Url");
   uint64_t flags_hash = base::HashMetricName("ObjMetric.Flags1");
   RapporReports reports;
diff --git a/components/rappor/rappor_utils.cc b/components/rappor/rappor_utils.cc
index 639ba259..dfc232a8 100644
--- a/components/rappor/rappor_utils.cc
+++ b/components/rappor/rappor_utils.cc
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
 
-#include "components/rappor/rappor_service.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
 #include "url/gurl.h"
@@ -17,7 +16,7 @@
                   const std::string& sample) {
   if (!rappor_service)
     return;
-  rappor_service->RecordSample(metric, type, sample);
+  rappor_service->RecordSampleString(metric, type, sample);
 }
 
 std::string GetDomainAndRegistrySampleFromGURL(const GURL& gurl) {
@@ -39,10 +38,8 @@
                                      const GURL& gurl) {
   if (!rappor_service)
     return;
-  rappor_service->RecordSample(
-      metric,
-      rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
-      GetDomainAndRegistrySampleFromGURL(gurl));
+  rappor_service->RecordSampleString(metric, rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
+                                     GetDomainAndRegistrySampleFromGURL(gurl));
 }
 
 RapporService* (*g_GetDefaultService)() = nullptr;
diff --git a/components/rappor/rappor_utils.h b/components/rappor/rappor_utils.h
deleted file mode 100644
index f8600d6..0000000
--- a/components/rappor/rappor_utils.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_RAPPOR_RAPPOR_UTILS_H_
-#define COMPONENTS_RAPPOR_RAPPOR_UTILS_H_
-
-#include <string>
-
-#include "components/rappor/rappor_service.h"
-
-class GURL;
-
-namespace rappor {
-
-class RapporService;
-
-// Records a string to a Rappor metric.
-// If |rappor_service| is NULL, this call does nothing.
-void SampleString(RapporService* rappor_service,
-                  const std::string& metric,
-                  RapporType type,
-                  const std::string& sample);
-
-// Extract the domain and registry for a sample from a GURL.
-// For file:// urls this will just return "file://" and for other special
-// schemes like chrome-extension will return the scheme and host.
-std::string GetDomainAndRegistrySampleFromGURL(const GURL& gurl);
-
-// Records the domain and registry of a url to a Rappor metric.
-// If |rappor_service| is NULL, this call does nothing.
-void SampleDomainAndRegistryFromGURL(RapporService* rappor_service,
-                                     const std::string& metric,
-                                     const GURL& gurl);
-
-// Returns NULL if there is no default service.
-RapporService* GetDefaultService();
-
-void SetDefaultServiceAccessor(RapporService* (*getDefaultService)());
-
-}  // namespace rappor
-
-#endif  // COMPONENTS_RAPPOR_RAPPOR_UTILS_H_
diff --git a/components/rappor/rappor_utils_unittest.cc b/components/rappor/rappor_utils_unittest.cc
index 4b1fba5..d502df8 100644
--- a/components/rappor/rappor_utils_unittest.cc
+++ b/components/rappor/rappor_utils_unittest.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 "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
diff --git a/components/rappor/reports.cc b/components/rappor/reports.cc
index 2f0f1320..b035b5b5 100644
--- a/components/rappor/reports.cc
+++ b/components/rappor/reports.cc
@@ -8,7 +8,7 @@
 #include "base/rand_util.h"
 #include "base/strings/string_piece.h"
 #include "components/rappor/byte_vector_utils.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 
 namespace rappor {
 
diff --git a/components/rappor/sample.cc b/components/rappor/sample.cc
index b54d9c4..bdc933a 100644
--- a/components/rappor/sample.cc
+++ b/components/rappor/sample.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 "components/rappor/sample.h"
+#include "components/rappor/public/sample.h"
 
 #include <map>
 #include <string>
diff --git a/components/rappor/sample.h b/components/rappor/sample.h
deleted file mode 100644
index 3f143f3f..0000000
--- a/components/rappor/sample.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_RAPPOR_SAMPLE_H_
-#define COMPONENTS_RAPPOR_SAMPLE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <string>
-
-#include "base/macros.h"
-#include "components/rappor/rappor_parameters.h"
-
-namespace rappor {
-
-class RapporReports;
-class RapporService;
-class TestSamplerFactory;
-
-// Sample is a container for information about a single instance of some event
-// we are sending Rappor data about.  It may contain multiple different fields,
-// which describe different details of the event, and they will be sent in the
-// same Rappor report, enabling analysis of correlations between those fields.
-class Sample {
- public:
-  virtual ~Sample();
-
-  // Sets a string value field in this sample.
-  virtual void SetStringField(const std::string& field_name,
-                              const std::string& value);
-
-  // TODO(holte): Move all callers to the version with NoiseLevel.
-  virtual void SetFlagsField(const std::string& field_name,
-                             uint64_t flags,
-                             size_t num_flags);
-
-  // Sets a group of boolean flags as a field in this sample, with the
-  // specified noise level.
-  // |flags| should be a set of boolean flags stored in the lowest |num_flags|
-  // bits of |flags|.
-  virtual void SetFlagsField(const std::string& field_name,
-                             uint64_t flags,
-                             size_t num_flags,
-                             NoiseLevel noise_level);
-
-  // Sets an integer value field in this sample, at the given noise level.
-  virtual void SetUInt64Field(const std::string& field_name,
-                              uint64_t value,
-                              NoiseLevel noise_level);
-
-  // Generate randomized reports and store them in |reports|.
-  virtual void ExportMetrics(const std::string& secret,
-                             const std::string& metric_name,
-                             RapporReports* reports) const;
-
-  const RapporParameters& parameters() { return parameters_; }
-
- private:
-  friend class TestSamplerFactory;
-  friend class RapporService;
-  friend class TestSample;
-
-  // Constructs a sample.  Instead of calling this directly, call
-  // RapporService::MakeSampleObj to create a sample.
-  Sample(int32_t cohort_seed, const RapporParameters& parameters);
-
-  const RapporParameters parameters_;
-
-  // Offset used for bloom filter hash functions.
-  uint32_t bloom_offset_;
-
-  struct FieldInfo {
-    // Size of the field, in bytes.
-    size_t size;
-    // The non-randomized report value for the field.
-    uint64_t value;
-    // The noise level to use when creating a report for the field.
-    NoiseLevel noise_level;
-  };
-
-  // Information about all recorded fields.
-  std::map<std::string, FieldInfo> field_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(Sample);
-};
-
-}  // namespace rappor
-
-#endif  // COMPONENTS_RAPPOR_SAMPLE_H_
diff --git a/components/rappor/sampler.h b/components/rappor/sampler.h
index 8059de4..58e0ed1 100644
--- a/components/rappor/sampler.h
+++ b/components/rappor/sampler.h
@@ -11,8 +11,8 @@
 
 #include "base/containers/scoped_ptr_hash_map.h"
 #include "base/macros.h"
-#include "components/rappor/rappor_parameters.h"
-#include "components/rappor/sample.h"
+#include "components/rappor/public/rappor_parameters.h"
+#include "components/rappor/public/sample.h"
 
 namespace rappor {
 
diff --git a/components/rappor/test_rappor_service.cc b/components/rappor/test_rappor_service.cc
index edd51c3f..8689873 100644
--- a/components/rappor/test_rappor_service.cc
+++ b/components/rappor/test_rappor_service.cc
@@ -10,7 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "components/rappor/byte_vector_utils.h"
 #include "components/rappor/proto/rappor_metric.pb.h"
-#include "components/rappor/rappor_parameters.h"
+#include "components/rappor/public/rappor_parameters.h"
 #include "components/rappor/rappor_prefs.h"
 #include "components/rappor/test_log_uploader.h"
 
@@ -52,8 +52,9 @@
 
 TestSample::Shadow::~Shadow() {}
 
-TestRapporService::TestRapporService()
-    : RapporService(&test_prefs_, base::Bind(&MockIsIncognito, &is_incognito_)),
+TestRapporServiceImpl::TestRapporServiceImpl()
+    : RapporServiceImpl(&test_prefs_,
+                        base::Bind(&MockIsIncognito, &is_incognito_)),
       next_rotation_(base::TimeDelta()),
       is_incognito_(false) {
   RegisterPrefs(test_prefs_.registry());
@@ -63,48 +64,48 @@
   Update(UMA_RAPPOR_GROUP | SAFEBROWSING_RAPPOR_GROUP, true);
 }
 
-TestRapporService::~TestRapporService() {}
+TestRapporServiceImpl::~TestRapporServiceImpl() {}
 
-std::unique_ptr<Sample> TestRapporService::CreateSample(RapporType type) {
+std::unique_ptr<Sample> TestRapporServiceImpl::CreateSample(RapporType type) {
   std::unique_ptr<TestSample> test_sample(new TestSample(type));
   return std::move(test_sample);
 }
 
 // Intercepts the sample being recorded and saves it in a test structure.
-void TestRapporService::RecordSampleObj(const std::string& metric_name,
-                                        std::unique_ptr<Sample> sample) {
+void TestRapporServiceImpl::RecordSample(const std::string& metric_name,
+                                         std::unique_ptr<Sample> sample) {
   TestSample* test_sample = static_cast<TestSample*>(sample.get());
   // Erase the previous sample if we logged one.
   shadows_.erase(metric_name);
   shadows_.insert(std::pair<std::string, TestSample::Shadow>(
       metric_name, test_sample->GetShadow()));
   // Original version is still called.
-  RapporService::RecordSampleObj(metric_name, std::move(sample));
+  RapporServiceImpl::RecordSample(metric_name, std::move(sample));
 }
 
-void TestRapporService::RecordSample(const std::string& metric_name,
-                                     RapporType type,
-                                     const std::string& sample) {
+void TestRapporServiceImpl::RecordSampleString(const std::string& metric_name,
+                                               RapporType type,
+                                               const std::string& sample) {
   // Save the recorded sample to the local structure.
   RapporSample rappor_sample;
   rappor_sample.type = type;
   rappor_sample.value = sample;
   samples_[metric_name] = rappor_sample;
   // Original version is still called.
-  RapporService::RecordSample(metric_name, type, sample);
+  RapporServiceImpl::RecordSampleString(metric_name, type, sample);
 }
 
-int TestRapporService::GetReportsCount() {
+int TestRapporServiceImpl::GetReportsCount() {
   RapporReports reports;
   ExportMetrics(&reports);
   return reports.report_size();
 }
 
-void TestRapporService::GetReports(RapporReports* reports) {
+void TestRapporServiceImpl::GetReports(RapporReports* reports) {
   ExportMetrics(reports);
 }
 
-TestSample::Shadow* TestRapporService::GetRecordedSampleForMetric(
+TestSample::Shadow* TestRapporServiceImpl::GetRecordedSampleForMetric(
     const std::string& metric_name) {
   ShadowMap::iterator it = shadows_.find(metric_name);
   if (it == shadows_.end())
@@ -112,7 +113,7 @@
   return &it->second;
 }
 
-bool TestRapporService::GetRecordedSampleForMetric(
+bool TestRapporServiceImpl::GetRecordedSampleForMetric(
     const std::string& metric_name,
     std::string* sample,
     RapporType* type) {
@@ -125,12 +126,12 @@
 }
 
 // Cancel the next call to OnLogInterval.
-void TestRapporService::CancelNextLogRotation() {
+void TestRapporServiceImpl::CancelNextLogRotation() {
   next_rotation_ = base::TimeDelta();
 }
 
 // Schedule the next call to OnLogInterval.
-void TestRapporService::ScheduleNextLogRotation(base::TimeDelta interval) {
+void TestRapporServiceImpl::ScheduleNextLogRotation(base::TimeDelta interval) {
   next_rotation_ = interval;
 }
 
diff --git a/components/rappor/test_rappor_service.h b/components/rappor/test_rappor_service.h
index 01a850c..a323c7829 100644
--- a/components/rappor/test_rappor_service.h
+++ b/components/rappor/test_rappor_service.h
@@ -13,7 +13,7 @@
 
 #include "base/macros.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/rappor/test_log_uploader.h"
 
 namespace rappor {
@@ -51,19 +51,19 @@
 // This class provides a simple instance that can be instantiated by tests
 // and examined to check that metrics were recorded.  It assumes the most
 // permissive settings so that any metric can be recorded.
-class TestRapporService : public RapporService {
+class TestRapporServiceImpl : public RapporServiceImpl {
  public:
-  TestRapporService();
+  TestRapporServiceImpl();
 
-  ~TestRapporService() override;
+  ~TestRapporServiceImpl() override;
 
-  // RapporService:
+  // RapporServiceImpl:
   std::unique_ptr<Sample> CreateSample(RapporType type) override;
-  void RecordSampleObj(const std::string& metric_name,
-                       std::unique_ptr<Sample> sample) override;
   void RecordSample(const std::string& metric_name,
-                    RapporType type,
-                    const std::string& sample) override;
+                    std::unique_ptr<Sample> sample) override;
+  void RecordSampleString(const std::string& metric_name,
+                          RapporType type,
+                          const std::string& sample) override;
 
   // Gets the number of reports that would be uploaded by this service.
   // This also clears the internal map of metrics as a biproduct, so if
@@ -129,7 +129,7 @@
   // Sets this to true to mock incognito state.
   bool is_incognito_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestRapporService);
+  DISALLOW_COPY_AND_ASSIGN(TestRapporServiceImpl);
 };
 
 }  // namespace rappor
diff --git a/components/reading_list/ios/reading_list_entry.cc b/components/reading_list/ios/reading_list_entry.cc
index 283d78a..79a40d16 100644
--- a/components/reading_list/ios/reading_list_entry.cc
+++ b/components/reading_list/ios/reading_list_entry.cc
@@ -49,6 +49,7 @@
                        UNSEEN,
                        0,
                        0,
+                       0,
                        WAITING,
                        base::FilePath(),
                        0,
@@ -59,6 +60,7 @@
     const std::string& title,
     State state,
     int64_t creation_time,
+    int64_t first_read_time,
     int64_t update_time,
     ReadingListEntry::DistillationState distilled_state,
     const base::FilePath& distilled_path,
@@ -71,6 +73,7 @@
       distilled_state_(distilled_state),
       failed_download_counter_(failed_download_counter),
       creation_time_us_(creation_time),
+      first_read_time_us_(first_read_time),
       update_time_us_(update_time) {
   if (backoff) {
     backoff_ = std::move(backoff);
@@ -96,6 +99,7 @@
       backoff_(std::move(entry.backoff_)),
       failed_download_counter_(std::move(entry.failed_download_counter_)),
       creation_time_us_(std::move(entry.creation_time_us_)),
+      first_read_time_us_(std::move(entry.first_read_time_us_)),
       update_time_us_(std::move(entry.update_time_us_)) {}
 
 ReadingListEntry::~ReadingListEntry() {}
@@ -133,6 +137,7 @@
   state_ = std::move(other.state_);
   failed_download_counter_ = std::move(other.failed_download_counter_);
   creation_time_us_ = std::move(other.creation_time_us_);
+  first_read_time_us_ = std::move(other.first_read_time_us_);
   update_time_us_ = std::move(other.update_time_us_);
   return *this;
 }
@@ -147,6 +152,10 @@
 
 void ReadingListEntry::SetRead(bool read) {
   state_ = read ? READ : UNREAD;
+  if (FirstReadTime() == 0 && read) {
+    first_read_time_us_ =
+        (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+  }
   MarkEntryUpdated();
 }
 
@@ -189,6 +198,10 @@
   return creation_time_us_;
 }
 
+int64_t ReadingListEntry::FirstReadTime() const {
+  return first_read_time_us_;
+}
+
 void ReadingListEntry::MarkEntryUpdated() {
   update_time_us_ =
       (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
@@ -214,6 +227,11 @@
     creation_time_us = pb_entry.creation_time_us();
   }
 
+  int64_t first_read_time_us = 0;
+  if (pb_entry.has_first_read_time_us()) {
+    first_read_time_us = pb_entry.first_read_time_us();
+  }
+
   int64_t update_time_us = 0;
   if (pb_entry.has_update_time_us()) {
     update_time_us = pb_entry.update_time_us();
@@ -278,8 +296,9 @@
   }
 
   return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
-      url, title, state, creation_time_us, update_time_us, distillation_state,
-      distilled_path, failed_download_counter, std::move(backoff)));
+      url, title, state, creation_time_us, first_read_time_us, update_time_us,
+      distillation_state, distilled_path, failed_download_counter,
+      std::move(backoff)));
 }
 
 // static
@@ -302,6 +321,11 @@
     creation_time_us = pb_entry.creation_time_us();
   }
 
+  int64_t first_read_time_us = 0;
+  if (pb_entry.has_first_read_time_us()) {
+    creation_time_us = pb_entry.first_read_time_us();
+  }
+
   int64_t update_time_us = 0;
   if (pb_entry.has_update_time_us()) {
     update_time_us = pb_entry.update_time_us();
@@ -322,9 +346,9 @@
     }
   }
 
-  return base::WrapUnique<ReadingListEntry>(
-      new ReadingListEntry(url, title, state, creation_time_us, update_time_us,
-                           WAITING, base::FilePath(), 0, nullptr));
+  return base::WrapUnique<ReadingListEntry>(new ReadingListEntry(
+      url, title, state, creation_time_us, first_read_time_us, update_time_us,
+      WAITING, base::FilePath(), 0, nullptr));
 }
 
 void ReadingListEntry::MergeLocalStateFrom(ReadingListEntry& other) {
@@ -345,6 +369,7 @@
   pb_entry->set_title(Title());
   pb_entry->set_url(URL().spec());
   pb_entry->set_creation_time_us(CreationTime());
+  pb_entry->set_first_read_time_us(FirstReadTime());
   pb_entry->set_update_time_us(UpdateTime());
 
   switch (state_) {
@@ -407,6 +432,7 @@
   pb_entry->set_title(Title());
   pb_entry->set_url(URL().spec());
   pb_entry->set_creation_time_us(CreationTime());
+  pb_entry->set_first_read_time_us(FirstReadTime());
   pb_entry->set_update_time_us(UpdateTime());
 
   switch (state_) {
diff --git a/components/reading_list/ios/reading_list_entry.h b/components/reading_list/ios/reading_list_entry.h
index 7664803..49d9c7f 100644
--- a/components/reading_list/ios/reading_list_entry.h
+++ b/components/reading_list/ios/reading_list_entry.h
@@ -72,6 +72,10 @@
   // Jan 1st 1970.
   int64_t CreationTime() const;
 
+  // The time when the entry was read for the first time. The value is in
+  // microseconds since Jan 1st 1970.
+  int64_t FirstReadTime() const;
+
   // Set the update time to now.
   void MarkEntryUpdated();
 
@@ -120,6 +124,7 @@
                    const std::string& title,
                    State state,
                    int64_t creation_time,
+                   int64_t first_read_time,
                    int64_t update_time,
                    ReadingListEntry::DistillationState distilled_state,
                    const base::FilePath& distilled_path,
@@ -138,6 +143,7 @@
   // sorting the entries from the database. They are kept in int64_t to avoid
   // conversion on each save/read event.
   int64_t creation_time_us_;
+  int64_t first_read_time_us_;
   int64_t update_time_us_;
 
   DISALLOW_COPY_AND_ASSIGN(ReadingListEntry);
diff --git a/components/safe_browsing_db/safe_browsing_api_handler_util.cc b/components/safe_browsing_db/safe_browsing_api_handler_util.cc
index e4474ef..4e894f0 100644
--- a/components/safe_browsing_db/safe_browsing_api_handler_util.cc
+++ b/components/safe_browsing_db/safe_browsing_api_handler_util.cc
@@ -157,7 +157,7 @@
   // Pick out the "matches" list.
   std::unique_ptr<base::Value> value = base::JSONReader::Read(metadata_str);
   const base::ListValue* matches = nullptr;
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY) ||
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY) ||
       !(static_cast<base::DictionaryValue*>(value.get()))
            ->GetList(kJsonKeyMatches, &matches) ||
       !matches) {
diff --git a/components/safe_browsing_db/safe_browsing_prefs.cc b/components/safe_browsing_db/safe_browsing_prefs.cc
index c892aba..c5a110a 100644
--- a/components/safe_browsing_db/safe_browsing_prefs.cc
+++ b/components/safe_browsing_db/safe_browsing_prefs.cc
@@ -14,6 +14,37 @@
 // Switch value which force the ScoutGroupSelected pref to false.
 const char kForceScoutGroupValueFalse[] = "false";
 
+// Name of the Scout Transition UMA metric.
+const char kScoutTransitionMetricName[] = "SafeBrowsing.Pref.Scout.Transition";
+
+// Reasons that a state transition for Scout was performed.
+// These values are written to logs.  New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum ScoutTransitionReason {
+  // Flag forced Scout Group to true
+  FORCE_SCOUT_FLAG_TRUE = 0,
+  // Flag forced Scout Group to false
+  FORCE_SCOUT_FLAG_FALSE = 1,
+  // User in OnlyShowScout group, enters Scout Group
+  ONLY_SHOW_SCOUT_OPT_IN = 2,
+  // User in CanShowScout group, enters Scout Group immediately
+  CAN_SHOW_SCOUT_OPT_IN_SCOUT_GROUP_ON = 3,
+  // User in CanShowScout group, waiting for interstitial to enter Scout Group
+  CAN_SHOW_SCOUT_OPT_IN_WAIT_FOR_INTERSTITIAL = 4,
+  // User in CanShowScout group saw the first interstitial and entered the Scout
+  // Group
+  CAN_SHOW_SCOUT_OPT_IN_SAW_FIRST_INTERSTITIAL = 5,
+  // User in Control group
+  CONTROL = 6,
+  // Rollback: SBER2 on on implies SBER1 can turn on
+  ROLLBACK_SBER2_IMPLIES_SBER1 = 7,
+  // Rollback: SBER2 off so SBER1 must be turned off
+  ROLLBACK_NO_SBER2_SET_SBER1_FALSE = 8,
+  // Rollback: SBER2 absent so SBER1 must be cleared
+  ROLLBACK_NO_SBER2_CLEAR_SBER1 = 9,
+  // New reasons must be added BEFORE MAX_REASONS
+  MAX_REASONS
+};
 }  // namespace
 
 namespace prefs {
@@ -83,8 +114,12 @@
             kSwitchForceScoutGroup);
     if (switch_value == kForceScoutGroupValueTrue) {
       prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
+      UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                FORCE_SCOUT_FLAG_TRUE, MAX_REASONS);
     } else if (switch_value == kForceScoutGroupValueFalse) {
       prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, false);
+      UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                FORCE_SCOUT_FLAG_FALSE, MAX_REASONS);
     }
 
     // If the switch is used, don't process the experiment state.
@@ -95,6 +130,8 @@
   if (base::FeatureList::IsEnabled(kOnlyShowScoutOptIn)) {
     // OnlyShowScoutOptIn immediately turns on ScoutGroupSelected pref.
     prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
+    UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                              ONLY_SHOW_SCOUT_OPT_IN, MAX_REASONS);
   } else if (base::FeatureList::IsEnabled(kCanShowScoutOptIn)) {
     // CanShowScoutOptIn will only turn on ScoutGroupSelected pref if the legacy
     // SBER pref is false. Otherwise the legacy SBER pref will stay on and
@@ -102,16 +139,26 @@
     // the Scout pref will become the active one.
     if (!prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
       prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
+      UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                CAN_SHOW_SCOUT_OPT_IN_SCOUT_GROUP_ON,
+                                MAX_REASONS);
+    } else {
+      UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                CAN_SHOW_SCOUT_OPT_IN_WAIT_FOR_INTERSTITIAL,
+                                MAX_REASONS);
     }
   } else {
     // Both experiment features are off, so this is the Control group. We must
     // handle the possibility that the user was previously in an experiment
     // group (above) that was reverted. We want to restore the user to a
     // reasonable state based on the ScoutGroup and ScoutReporting preferences.
+    UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName, CONTROL, MAX_REASONS);
     if (prefs->GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled)) {
       // User opted-in to Scout which is broader than legacy Extended Reporting.
       // Opt them in to Extended Reporting.
       prefs->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, true);
+      UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                ROLLBACK_SBER2_IMPLIES_SBER1, MAX_REASONS);
     } else if (prefs->GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
       // User was in the Scout Group (ie: seeing the Scout opt-in text) but did
       // NOT opt-in to Scout. Assume this was a conscious choice and remove
@@ -124,9 +171,14 @@
         // Scout Reporting pref was explicitly set to false, so set the SBER
         // pref to false.
         prefs->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, false);
+        UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                  ROLLBACK_NO_SBER2_SET_SBER1_FALSE,
+                                  MAX_REASONS);
       } else {
         // Scout Reporting pref is unset, so clear the SBER pref.
         prefs->ClearPref(prefs::kSafeBrowsingExtendedReportingEnabled);
+        UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                                  ROLLBACK_NO_SBER2_CLEAR_SBER1, MAX_REASONS);
       }
     }
 
@@ -182,9 +234,13 @@
 
 void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
   // Move the user into the Scout Group if the CanShowScoutOptIn experiment is
-  // enabled.
-  if (base::FeatureList::IsEnabled(kCanShowScoutOptIn)) {
+  // enabled and they're not in the group already.
+  if (base::FeatureList::IsEnabled(kCanShowScoutOptIn) &&
+      !prefs->GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
     prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
+    UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
+                              CAN_SHOW_SCOUT_OPT_IN_SAW_FIRST_INTERSTITIAL,
+                              MAX_REASONS);
   }
 }
 
diff --git a/components/safe_json/json_sanitizer.cc b/components/safe_json/json_sanitizer.cc
index 3ad634d..c88dd883 100644
--- a/components/safe_json/json_sanitizer.cc
+++ b/components/safe_json/json_sanitizer.cc
@@ -61,7 +61,8 @@
   // A valid JSON document may only have a dictionary or list as its top-level
   // type, but the JSON parser also accepts other types, so we filter them out.
   base::Value::Type type = value->GetType();
-  if (type != base::Value::TYPE_DICTIONARY && type != base::Value::TYPE_LIST) {
+  if (type != base::Value::Type::DICTIONARY &&
+      type != base::Value::Type::LIST) {
     error_callback_.Run("Invalid top-level type");
     return;
   }
diff --git a/components/safe_json/safe_json_parser_impl.cc b/components/safe_json/safe_json_parser_impl.cc
index 52bd0a3d..eadb80c 100644
--- a/components/safe_json/safe_json_parser_impl.cc
+++ b/components/safe_json/safe_json_parser_impl.cc
@@ -56,8 +56,9 @@
   mojo_json_parser_.reset();
 
   caller_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&SafeJsonParserImpl::ReportResults,
-                            base::Unretained(this), nullptr, "Json error"));
+      FROM_HERE,
+      base::Bind(&SafeJsonParserImpl::ReportResults, base::Unretained(this),
+                 nullptr, "Connection error with the json parser process."));
 }
 
 void SafeJsonParserImpl::OnParseDone(const base::ListValue& wrapper,
diff --git a/components/safe_json/testing_json_parser_unittest.cc b/components/safe_json/testing_json_parser_unittest.cc
index 1e944a9..63f638fa 100644
--- a/components/safe_json/testing_json_parser_unittest.cc
+++ b/components/safe_json/testing_json_parser_unittest.cc
@@ -40,7 +40,7 @@
     test->did_success_ = true;
     quit_closure.Run();
 
-    ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+    ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
     base::DictionaryValue* dict;
     ASSERT_TRUE(value->GetAsDictionary(&dict));
     int key_value = 0;
diff --git a/components/search_engines/default_search_policy_handler.cc b/components/search_engines/default_search_policy_handler.cc
index 7c742da5..876827d 100644
--- a/components/search_engines/default_search_policy_handler.cc
+++ b/components/search_engines/default_search_policy_handler.cc
@@ -62,95 +62,96 @@
 const PolicyToPreferenceMapEntry kDefaultSearchPolicyMap[] = {
   { key::kDefaultSearchProviderEnabled,
     prefs::kDefaultSearchProviderEnabled,
-    base::Value::TYPE_BOOLEAN },
+    base::Value::Type::BOOLEAN },
   { key::kDefaultSearchProviderName,
     prefs::kDefaultSearchProviderName,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderKeyword,
     prefs::kDefaultSearchProviderKeyword,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderSearchURL,
     prefs::kDefaultSearchProviderSearchURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderSuggestURL,
     prefs::kDefaultSearchProviderSuggestURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderInstantURL,
     prefs::kDefaultSearchProviderInstantURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderIconURL,
     prefs::kDefaultSearchProviderIconURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderEncodings,
     prefs::kDefaultSearchProviderEncodings,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kDefaultSearchProviderAlternateURLs,
     prefs::kDefaultSearchProviderAlternateURLs,
-    base::Value::TYPE_LIST },
+    base::Value::Type::LIST },
   { key::kDefaultSearchProviderSearchTermsReplacementKey,
     prefs::kDefaultSearchProviderSearchTermsReplacementKey,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderImageURL,
     prefs::kDefaultSearchProviderImageURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderNewTabURL,
     prefs::kDefaultSearchProviderNewTabURL,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderSearchURLPostParams,
     prefs::kDefaultSearchProviderSearchURLPostParams,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderSuggestURLPostParams,
     prefs::kDefaultSearchProviderSuggestURLPostParams,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderInstantURLPostParams,
     prefs::kDefaultSearchProviderInstantURLPostParams,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
   { key::kDefaultSearchProviderImageURLPostParams,
     prefs::kDefaultSearchProviderImageURLPostParams,
-    base::Value::TYPE_STRING },
+    base::Value::Type::STRING },
 };
 
 // List of policy types to preference names, for policies affecting the default
 // search provider.
 const PolicyToPreferenceMapEntry kDefaultSearchPolicyDataMap[] = {
     {key::kDefaultSearchProviderName, DefaultSearchManager::kShortName,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderKeyword, DefaultSearchManager::kKeyword,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderSearchURL, DefaultSearchManager::kURL,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderSuggestURL,
-     DefaultSearchManager::kSuggestionsURL, base::Value::TYPE_STRING},
+     DefaultSearchManager::kSuggestionsURL, base::Value::Type::STRING},
     {key::kDefaultSearchProviderInstantURL, DefaultSearchManager::kInstantURL,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderIconURL, DefaultSearchManager::kFaviconURL,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderEncodings,
-     DefaultSearchManager::kInputEncodings, base::Value::TYPE_LIST},
+     DefaultSearchManager::kInputEncodings, base::Value::Type::LIST},
     {key::kDefaultSearchProviderAlternateURLs,
-     DefaultSearchManager::kAlternateURLs, base::Value::TYPE_LIST},
+     DefaultSearchManager::kAlternateURLs, base::Value::Type::LIST},
     {key::kDefaultSearchProviderSearchTermsReplacementKey,
      DefaultSearchManager::kSearchTermsReplacementKey,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderImageURL, DefaultSearchManager::kImageURL,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderNewTabURL, DefaultSearchManager::kNewTabURL,
-     base::Value::TYPE_STRING},
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderSearchURLPostParams,
-     DefaultSearchManager::kSearchURLPostParams, base::Value::TYPE_STRING},
+     DefaultSearchManager::kSearchURLPostParams, base::Value::Type::STRING},
     {key::kDefaultSearchProviderSuggestURLPostParams,
-     DefaultSearchManager::kSuggestionsURLPostParams, base::Value::TYPE_STRING},
+     DefaultSearchManager::kSuggestionsURLPostParams,
+     base::Value::Type::STRING},
     {key::kDefaultSearchProviderInstantURLPostParams,
-     DefaultSearchManager::kInstantURLPostParams, base::Value::TYPE_STRING},
+     DefaultSearchManager::kInstantURLPostParams, base::Value::Type::STRING},
     {key::kDefaultSearchProviderImageURLPostParams,
-     DefaultSearchManager::kImageURLPostParams, base::Value::TYPE_STRING},
+     DefaultSearchManager::kImageURLPostParams, base::Value::Type::STRING},
 };
 
 // DefaultSearchEncodingsPolicyHandler implementation --------------------------
 
 DefaultSearchEncodingsPolicyHandler::DefaultSearchEncodingsPolicyHandler()
     : TypeCheckingPolicyHandler(key::kDefaultSearchProviderEncodings,
-                                base::Value::TYPE_LIST) {}
+                                base::Value::Type::LIST) {}
 
 DefaultSearchEncodingsPolicyHandler::~DefaultSearchEncodingsPolicyHandler() {
 }
@@ -247,13 +248,13 @@
   for (size_t i = 0; i < arraysize(kDefaultSearchPolicyDataMap); ++i) {
     const char* policy_name = kDefaultSearchPolicyDataMap[i].policy_name;
     switch (kDefaultSearchPolicyDataMap[i].value_type) {
-      case base::Value::TYPE_STRING:
+      case base::Value::Type::STRING:
         SetStringInPref(policies,
                         policy_name,
                         kDefaultSearchPolicyDataMap[i].preference_path,
                         dict.get());
         break;
-      case base::Value::TYPE_LIST:
+      case base::Value::Type::LIST:
         SetListInPref(policies,
                       policy_name,
                       kDefaultSearchPolicyDataMap[i].preference_path,
diff --git a/components/search_engines/template_url_data_util.cc b/components/search_engines/template_url_data_util.cc
index be06e2c..ab76c92 100644
--- a/components/search_engines/template_url_data_util.cc
+++ b/components/search_engines/template_url_data_util.cc
@@ -77,7 +77,7 @@
     result->date_created = base::Time::FromInternalValue(date_created);
 
   int64_t last_modified = 0;
-  if (base::StringToInt64(date_created_str, &last_modified))
+  if (base::StringToInt64(last_modified_str, &last_modified))
     result->last_modified = base::Time::FromInternalValue(last_modified);
 
   int64_t last_visited = 0;
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 27376de..2b5983b 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -24,7 +24,7 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/search_host_to_urls_map.h"
 #include "components/search_engines/search_terms_data.h"
@@ -230,7 +230,7 @@
     const scoped_refptr<KeywordWebDataService>& web_data_service,
     std::unique_ptr<TemplateURLServiceClient> client,
     GoogleURLTracker* google_url_tracker,
-    rappor::RapporService* rappor_service,
+    rappor::RapporServiceImpl* rappor_service,
     const base::Closure& dsp_change_callback)
     : prefs_(prefs),
       search_terms_data_(std::move(search_terms_data)),
@@ -870,9 +870,8 @@
         SEARCH_ENGINE_MAX);
 
     if (rappor_service_) {
-      rappor_service_->RecordSample(
-          "Search.DefaultSearchProvider",
-          rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
+      rappor_service_->RecordSampleString(
+          "Search.DefaultSearchProvider", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
           net::registry_controlled_domains::GetDomainAndRegistry(
               default_search_provider_->url_ref().GetHost(search_terms_data()),
               net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h
index b387f7b..68f9eedb 100644
--- a/components/search_engines/template_url_service.h
+++ b/components/search_engines/template_url_service.h
@@ -41,7 +41,7 @@
 struct TemplateURLData;
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace syncer {
@@ -109,7 +109,7 @@
       const scoped_refptr<KeywordWebDataService>& web_data_service,
       std::unique_ptr<TemplateURLServiceClient> client,
       GoogleURLTracker* google_url_tracker,
-      rappor::RapporService* rappor_service,
+      rappor::RapporServiceImpl* rappor_service,
       const base::Closure& dsp_change_callback);
   // The following is for testing.
   TemplateURLService(const Initializer* initializers, const int count);
@@ -728,7 +728,7 @@
   GoogleURLTracker* google_url_tracker_;
 
   // ---------- Metrics related members ---------------------------------------
-  rappor::RapporService* rappor_service_;
+  rappor::RapporServiceImpl* rappor_service_;
 
   // This closure is run when the default search provider is set to Google.
   base::Closure dsp_change_callback_;
diff --git a/components/security_interstitials/core/metrics_helper.cc b/components/security_interstitials/core/metrics_helper.cc
index 17073b3c..6ec4ce0 100644
--- a/components/security_interstitials/core/metrics_helper.cc
+++ b/components/security_interstitials/core/metrics_helper.cc
@@ -11,8 +11,8 @@
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/rappor/rappor_service.h"
-#include "components/rappor/rappor_utils.h"
+#include "components/rappor/public/rappor_utils.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 
 using base::RecordAction;
@@ -117,10 +117,10 @@
 MetricsHelper::ReportDetails::~ReportDetails() {}
 
 MetricsHelper::MetricsHelper(
-  const GURL& request_url,
-  const ReportDetails settings,
-  history::HistoryService* history_service,
-  const base::WeakPtr<rappor::RapporService>& rappor_service)
+    const GURL& request_url,
+    const ReportDetails settings,
+    history::HistoryService* history_service,
+    const base::WeakPtr<rappor::RapporService>& rappor_service)
     : request_url_(request_url),
       settings_(settings),
       rappor_service_(rappor_service),
@@ -194,8 +194,8 @@
     sample->SetFlagsField("flags", flags,
                           InterstitialFlagBits::HIGHEST_USED_BIT + 1);
   }
-  rappor_service_->RecordSampleObj("interstitial." + rappor_prefix,
-                                   std::move(sample));
+  rappor_service_->RecordSample("interstitial." + rappor_prefix,
+                                std::move(sample));
 }
 
 void MetricsHelper::RecordUserInteraction(Interaction interaction) {
diff --git a/components/security_interstitials/core/metrics_helper.h b/components/security_interstitials/core/metrics_helper.h
index 66a3d6dd..1d37420d 100644
--- a/components/security_interstitials/core/metrics_helper.h
+++ b/components/security_interstitials/core/metrics_helper.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/time/time.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "url/gurl.h"
 
 namespace history {
diff --git a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
index 10ea6c9..d437a8b44 100644
--- a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
+++ b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
@@ -15,6 +15,7 @@
 
 class AccountTrackerService;
 class ProfileOAuth2TokenServiceIOSProvider;
+class SigninClient;
 
 class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate {
  public:
diff --git a/components/spellcheck/browser/spelling_service_client.cc b/components/spellcheck/browser/spelling_service_client.cc
index 44c51cc..985732a 100644
--- a/components/spellcheck/browser/spelling_service_client.cc
+++ b/components/spellcheck/browser/spelling_service_client.cc
@@ -196,7 +196,7 @@
       static_cast<base::DictionaryValue*>(
           base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS)
               .release()));
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   // Check for errors from spelling service.
diff --git a/components/startup_metric_utils/common/startup_metric.mojom b/components/startup_metric_utils/common/startup_metric.mojom
index 80161078..f970df0 100644
--- a/components/startup_metric_utils/common/startup_metric.mojom
+++ b/components/startup_metric_utils/common/startup_metric.mojom
@@ -4,7 +4,7 @@
 
 module startup_metric_utils.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 
 interface StartupMetricHost {
   RecordRendererMainEntryTime(
diff --git a/components/storage_monitor/storage_monitor_linux_unittest.cc b/components/storage_monitor/storage_monitor_linux_unittest.cc
index 92311f2c..769d46e 100644
--- a/components/storage_monitor/storage_monitor_linux_unittest.cc
+++ b/components/storage_monitor/storage_monitor_linux_unittest.cc
@@ -315,8 +315,7 @@
 };
 
 // Simple test case where we attach and detach a media device.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_BasicAttachDetach) {
+TEST_F(StorageMonitorLinuxTest, BasicAttachDetach) {
   base::FilePath test_path = CreateMountPointWithDCIMDir(kMountPointA);
   ASSERT_FALSE(test_path.empty());
   MtabTestData test_data[] = {
@@ -340,8 +339,7 @@
 }
 
 // Only removable devices are recognized.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_Removable) {
+TEST_F(StorageMonitorLinuxTest, Removable) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   ASSERT_FALSE(test_path_a.empty());
   MtabTestData test_data1[] = {
@@ -389,8 +387,7 @@
 }
 
 // More complicated test case with multiple devices on multiple mount points.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_SwapMountPoints) {
+TEST_F(StorageMonitorLinuxTest, SwapMountPoints) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
   ASSERT_FALSE(test_path_a.empty());
@@ -427,8 +424,7 @@
 }
 
 // More complicated test case with multiple devices on multiple mount points.
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_MultiDevicesMultiMountPoints) {
+TEST_F(StorageMonitorLinuxTest, MultiDevicesMultiMountPoints) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
   ASSERT_FALSE(test_path_a.empty());
@@ -495,9 +491,7 @@
   EXPECT_EQ(5, observer().detach_calls());
 }
 
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest,
-       DISABLED_MultipleMountPointsWithNonDCIMDevices) {
+TEST_F(StorageMonitorLinuxTest, MultipleMountPointsWithNonDCIMDevices) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   base::FilePath test_path_b = CreateMountPointWithDCIMDir(kMountPointB);
   ASSERT_FALSE(test_path_a.empty());
@@ -583,8 +577,7 @@
   EXPECT_EQ(4, observer().detach_calls());
 }
 
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_DeviceLookUp) {
+TEST_F(StorageMonitorLinuxTest, DeviceLookUp) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
   base::FilePath test_path_c = CreateMountPointWithoutDCIMDir(kMountPointC);
@@ -658,8 +651,7 @@
   EXPECT_EQ(1, observer().detach_calls());
 }
 
-// http://crbug.com/526252 flaky
-TEST_F(StorageMonitorLinuxTest, DISABLED_DevicePartitionSize) {
+TEST_F(StorageMonitorLinuxTest, DevicePartitionSize) {
   base::FilePath test_path_a = CreateMountPointWithDCIMDir(kMountPointA);
   base::FilePath test_path_b = CreateMountPointWithoutDCIMDir(kMountPointB);
   ASSERT_FALSE(test_path_a.empty());
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index cad00ffe..b3b524c 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -126,8 +126,6 @@
     "driver/glue/sync_backend_host_core.h",
     "driver/glue/sync_backend_host_impl.cc",
     "driver/glue/sync_backend_host_impl.h",
-    "driver/glue/sync_backend_registrar.cc",
-    "driver/glue/sync_backend_registrar.h",
     "driver/model_association_manager.cc",
     "driver/model_association_manager.h",
     "driver/model_associator.h",
@@ -237,6 +235,8 @@
     "engine/polling_constants.h",
     "engine/shutdown_reason.h",
     "engine/sync_auth_provider.h",
+    "engine/sync_backend_registrar.cc",
+    "engine/sync_backend_registrar.h",
     "engine/sync_encryption_handler.cc",
     "engine/sync_encryption_handler.h",
     "engine/sync_engine.cc",
@@ -856,7 +856,6 @@
     "driver/frontend_data_type_controller_unittest.cc",
     "driver/generic_change_processor_unittest.cc",
     "driver/glue/sync_backend_host_impl_unittest.cc",
-    "driver/glue/sync_backend_registrar_unittest.cc",
     "driver/model_association_manager_unittest.cc",
     "driver/model_type_controller_unittest.cc",
     "driver/shared_change_processor_unittest.cc",
@@ -873,6 +872,7 @@
     "engine/cycle/sync_cycle_snapshot_unittest.cc",
     "engine/model_safe_worker_unittest.cc",
     "engine/net/http_bridge_unittest.cc",
+    "engine/sync_backend_registrar_unittest.cc",
     "engine/ui_model_worker_unittest.cc",
     "engine_impl/apply_control_data_updates_unittest.cc",
     "engine_impl/attachments/attachment_downloader_impl_unittest.cc",
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index a449da4..afd0f877 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -44,6 +44,7 @@
 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {}
 
 DataTypeManagerImpl::DataTypeManagerImpl(
+    ModelTypeSet initial_types,
     const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
     const DataTypeController::TypeMap* controllers,
     const DataTypeEncryptionHandler* encryption_handler,
diff --git a/components/sync/driver/data_type_manager_impl.h b/components/sync/driver/data_type_manager_impl.h
index 73c0456..dd6f3ce4 100644
--- a/components/sync/driver/data_type_manager_impl.h
+++ b/components/sync/driver/data_type_manager_impl.h
@@ -36,6 +36,7 @@
                             public ModelAssociationManagerDelegate {
  public:
   DataTypeManagerImpl(
+      ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
       const DataTypeEncryptionHandler* encryption_handler,
diff --git a/components/sync/driver/data_type_manager_impl_unittest.cc b/components/sync/driver/data_type_manager_impl_unittest.cc
index 5e5ade9..039443f 100644
--- a/components/sync/driver/data_type_manager_impl_unittest.cc
+++ b/components/sync/driver/data_type_manager_impl_unittest.cc
@@ -217,12 +217,14 @@
 class TestDataTypeManager : public DataTypeManagerImpl {
  public:
   TestDataTypeManager(
+      ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       ModelTypeConfigurer* configurer,
       const DataTypeController::TypeMap* controllers,
       const DataTypeEncryptionHandler* encryption_handler,
       DataTypeManagerObserver* observer)
-      : DataTypeManagerImpl(debug_info_listener,
+      : DataTypeManagerImpl(initial_types,
+                            debug_info_listener,
                             controllers,
                             encryption_handler,
                             configurer,
@@ -263,8 +265,8 @@
  protected:
   void SetUp() override {
     dtm_ = base::MakeUnique<TestDataTypeManager>(
-        WeakHandle<DataTypeDebugInfoListener>(), &configurer_, &controllers_,
-        &encryption_handler_, &observer_);
+        ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(), &configurer_,
+        &controllers_, &encryption_handler_, &observer_);
   }
 
   void SetConfigureStartExpectation() { observer_.ExpectStart(); }
diff --git a/components/sync/driver/generic_change_processor_unittest.cc b/components/sync/driver/generic_change_processor_unittest.cc
index dc3d1bd..c29dd7f 100644
--- a/components/sync/driver/generic_change_processor_unittest.cc
+++ b/components/sync/driver/generic_change_processor_unittest.cc
@@ -70,10 +70,11 @@
       SyncService* sync_service,
       const RegisterDataTypesMethod& register_platform_types_method) override {}
   DataTypeManager* CreateDataTypeManager(
+      ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
       const DataTypeEncryptionHandler* encryption_handler,
-      SyncEngine* backend,
+      ModelTypeConfigurer* configurer,
       DataTypeManagerObserver* observer) override {
     return nullptr;
   };
diff --git a/components/sync/driver/glue/sync_backend_host_core.cc b/components/sync/driver/glue/sync_backend_host_core.cc
index 63f206a5..3fcbdbc 100644
--- a/components/sync/driver/glue/sync_backend_host_core.cc
+++ b/components/sync/driver/glue/sync_backend_host_core.cc
@@ -18,7 +18,6 @@
 #include "components/invalidation/public/object_id_invalidation_map.h"
 #include "components/sync/base/invalidation_adapter.h"
 #include "components/sync/device_info/local_device_info_provider_impl.h"
-#include "components/sync/driver/glue/sync_backend_registrar.h"
 #include "components/sync/engine/cycle/commit_counters.h"
 #include "components/sync/engine/cycle/status_counters.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
@@ -26,6 +25,7 @@
 #include "components/sync/engine/engine_components_factory.h"
 #include "components/sync/engine/events/protocol_event.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
+#include "components/sync/engine/sync_backend_registrar.h"
 #include "components/sync/engine/sync_manager.h"
 #include "components/sync/engine/sync_manager_factory.h"
 
@@ -486,7 +486,8 @@
 
   host_.Call(FROM_HERE,
              &SyncBackendHostImpl::HandleInitializationSuccessOnFrontendLoop,
-             js_backend_, debug_info_listener_,
+             registrar_->GetLastConfiguredTypes(), js_backend_,
+             debug_info_listener_,
              base::Passed(sync_manager_->GetModelTypeConnectorProxy()),
              sync_manager_->cache_guid());
 
diff --git a/components/sync/driver/glue/sync_backend_host_impl.cc b/components/sync/driver/glue/sync_backend_host_impl.cc
index 77be61b..20d2462 100644
--- a/components/sync/driver/glue/sync_backend_host_impl.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl.cc
@@ -20,7 +20,6 @@
 #include "components/sync/base/invalidation_helper.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/driver/glue/sync_backend_host_core.h"
-#include "components/sync/driver/glue/sync_backend_registrar.h"
 #include "components/sync/driver/sync_client.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/engine/activation_context.h"
@@ -28,6 +27,7 @@
 #include "components/sync/engine/engine_components_factory_impl.h"
 #include "components/sync/engine/events/protocol_event.h"
 #include "components/sync/engine/net/http_bridge.h"
+#include "components/sync/engine/sync_backend_registrar.h"
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/engine/sync_manager_factory.h"
 #include "components/sync/engine/sync_string_conversions.h"
@@ -539,6 +539,7 @@
 }
 
 void SyncBackendHostImpl::HandleInitializationSuccessOnFrontendLoop(
+    ModelTypeSet initial_types,
     const WeakHandle<JsBackend> js_backend,
     const WeakHandle<DataTypeDebugInfoListener> debug_info_listener,
     std::unique_ptr<ModelTypeConnector> model_type_connector,
@@ -562,12 +563,13 @@
   // experimental types to enable. This should be done before we inform
   // the host to ensure they're visible in the customize screen.
   AddExperimentalTypes();
-  host_->OnEngineInitialized(js_backend, debug_info_listener, cache_guid, true);
+  host_->OnEngineInitialized(initial_types, js_backend, debug_info_listener,
+                             cache_guid, true);
 }
 
 void SyncBackendHostImpl::HandleInitializationFailureOnFrontendLoop() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  host_->OnEngineInitialized(WeakHandle<JsBackend>(),
+  host_->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
                              WeakHandle<DataTypeDebugInfoListener>(), "",
                              false);
 }
diff --git a/components/sync/driver/glue/sync_backend_host_impl.h b/components/sync/driver/glue/sync_backend_host_impl.h
index 0f57a602..e6735981 100644
--- a/components/sync/driver/glue/sync_backend_host_impl.h
+++ b/components/sync/driver/glue/sync_backend_host_impl.h
@@ -160,6 +160,7 @@
   // |model_type_connector| is our ModelTypeConnector, which is owned because in
   // production it is a proxy object to the real ModelTypeConnector.
   virtual void HandleInitializationSuccessOnFrontendLoop(
+      ModelTypeSet initial_types,
       const WeakHandle<JsBackend> js_backend,
       const WeakHandle<DataTypeDebugInfoListener> debug_info_listener,
       std::unique_ptr<ModelTypeConnector> model_type_connector,
diff --git a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
index f615bb0..5276ddb3 100644
--- a/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_backend_host_impl_unittest.cc
@@ -75,8 +75,9 @@
  public:
   virtual ~MockSyncEngineHost() {}
 
-  MOCK_METHOD4(OnEngineInitialized,
-               void(const WeakHandle<JsBackend>&,
+  MOCK_METHOD5(OnEngineInitialized,
+               void(ModelTypeSet initial_types,
+                    const WeakHandle<JsBackend>&,
                     const WeakHandle<DataTypeDebugInfoListener>&,
                     const std::string&,
                     bool));
@@ -205,7 +206,7 @@
 
   // Synchronously initializes the backend.
   void InitializeBackend(bool expect_success) {
-    EXPECT_CALL(mock_host_, OnEngineInitialized(_, _, _, expect_success))
+    EXPECT_CALL(mock_host_, OnEngineInitialized(_, _, _, _, expect_success))
         .WillOnce(InvokeWithoutArgs(QuitMessageLoop));
     SyncEngine::HttpPostProviderFactoryGetter
         http_post_provider_factory_getter =
diff --git a/components/sync/driver/glue/sync_backend_registrar.cc b/components/sync/driver/glue/sync_backend_registrar.cc
deleted file mode 100644
index 874d31c..0000000
--- a/components/sync/driver/glue/sync_backend_registrar.cc
+++ /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.
-
-#include "components/sync/driver/glue/sync_backend_registrar.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "components/sync/model/change_processor.h"
-#include "components/sync/syncable/user_share.h"
-
-namespace syncer {
-
-SyncBackendRegistrar::SyncBackendRegistrar(
-    const std::string& name,
-    ModelSafeWorkerFactory worker_factory)
-    : name_(name) {
-  DCHECK(!worker_factory.is_null());
-  MaybeAddWorker(worker_factory, GROUP_DB);
-  MaybeAddWorker(worker_factory, GROUP_FILE);
-  MaybeAddWorker(worker_factory, GROUP_UI);
-  MaybeAddWorker(worker_factory, GROUP_PASSIVE);
-  MaybeAddWorker(worker_factory, GROUP_HISTORY);
-  MaybeAddWorker(worker_factory, GROUP_PASSWORD);
-}
-
-void SyncBackendRegistrar::RegisterNonBlockingType(ModelType type) {
-  DCHECK(ui_thread_checker_.CalledOnValidThread());
-  base::AutoLock lock(lock_);
-  // There may have been a previously successful sync of a type when passive,
-  // which is now NonBlocking. We're not sure what order these two sets of types
-  // are being registered in, so guard against SetInitialTypes(...) having been
-  // already called by undoing everything to these types.
-  if (routing_info_.find(type) != routing_info_.end() &&
-      routing_info_[type] != GROUP_NON_BLOCKING) {
-    routing_info_.erase(type);
-    last_configured_types_.Remove(type);
-  }
-  non_blocking_types_.Put(type);
-}
-
-void SyncBackendRegistrar::SetInitialTypes(ModelTypeSet initial_types) {
-  base::AutoLock lock(lock_);
-
-  // This function should be called only once, shortly after construction. The
-  // routing info at that point is expected to be empty.
-  DCHECK(routing_info_.empty());
-
-  // Set our initial state to reflect the current status of the sync directory.
-  // This will ensure that our calculations in ConfigureDataTypes() will always
-  // return correct results.
-  for (ModelTypeSet::Iterator it = initial_types.First(); it.Good(); it.Inc()) {
-    // If this type is also registered as NonBlocking, assume that it shouldn't
-    // be registered as passive. The NonBlocking path will eventually take care
-    // of adding to routing_info_ later on.
-    if (!non_blocking_types_.Has(it.Get())) {
-      routing_info_[it.Get()] = GROUP_PASSIVE;
-    }
-  }
-
-  if (!workers_.count(GROUP_HISTORY)) {
-    LOG_IF(WARNING, initial_types.Has(TYPED_URLS))
-        << "History store disabled, cannot sync Omnibox History";
-    routing_info_.erase(TYPED_URLS);
-  }
-
-  if (!workers_.count(GROUP_PASSWORD)) {
-    LOG_IF(WARNING, initial_types.Has(PASSWORDS))
-        << "Password store not initialized, cannot sync passwords";
-    routing_info_.erase(PASSWORDS);
-  }
-
-  // Although this can re-set NonBlocking types, this should be idempotent.
-  last_configured_types_ = GetRoutingInfoTypes(routing_info_);
-}
-
-void SyncBackendRegistrar::AddRestoredNonBlockingType(ModelType type) {
-  DCHECK(ui_thread_checker_.CalledOnValidThread());
-  base::AutoLock lock(lock_);
-  DCHECK(non_blocking_types_.Has(type));
-  DCHECK(routing_info_.find(type) == routing_info_.end());
-  routing_info_[type] = GROUP_NON_BLOCKING;
-  last_configured_types_.Put(type);
-}
-
-bool SyncBackendRegistrar::IsNigoriEnabled() const {
-  DCHECK(ui_thread_checker_.CalledOnValidThread());
-  base::AutoLock lock(lock_);
-  return routing_info_.find(NIGORI) != routing_info_.end();
-}
-
-ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes(
-    ModelTypeSet types_to_add,
-    ModelTypeSet types_to_remove) {
-  DCHECK(Intersection(types_to_add, types_to_remove).Empty());
-  ModelTypeSet filtered_types_to_add = types_to_add;
-  if (workers_.count(GROUP_HISTORY) == 0) {
-    LOG(WARNING) << "No history worker -- removing TYPED_URLS";
-    filtered_types_to_add.Remove(TYPED_URLS);
-  }
-  if (workers_.count(GROUP_PASSWORD) == 0) {
-    LOG(WARNING) << "No password worker -- removing PASSWORDS";
-    filtered_types_to_add.Remove(PASSWORDS);
-  }
-
-  base::AutoLock lock(lock_);
-  ModelTypeSet newly_added_types;
-  for (ModelTypeSet::Iterator it = filtered_types_to_add.First(); it.Good();
-       it.Inc()) {
-    // Add a newly specified data type corresponding initial group into the
-    // routing_info, if it does not already exist.
-    if (routing_info_.count(it.Get()) == 0) {
-      routing_info_[it.Get()] = GetInitialGroupForType(it.Get());
-      newly_added_types.Put(it.Get());
-    }
-  }
-  for (ModelTypeSet::Iterator it = types_to_remove.First(); it.Good();
-       it.Inc()) {
-    routing_info_.erase(it.Get());
-  }
-
-  // TODO(akalin): Use SVLOG/SLOG if we add any more logging.
-  DVLOG(1) << name_ << ": Adding types " << ModelTypeSetToString(types_to_add)
-           << " (with newly-added types "
-           << ModelTypeSetToString(newly_added_types) << ") and removing types "
-           << ModelTypeSetToString(types_to_remove)
-           << " to get new routing info "
-           << ModelSafeRoutingInfoToString(routing_info_);
-  last_configured_types_ = GetRoutingInfoTypes(routing_info_);
-
-  return newly_added_types;
-}
-
-ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
-  return last_configured_types_;
-}
-
-void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
-  DCHECK(ui_thread_checker_.CalledOnValidThread());
-  base::AutoLock lock(lock_);
-  for (const auto& kv : workers_) {
-    kv.second->RequestStop();
-  }
-}
-
-void SyncBackendRegistrar::ActivateDataType(ModelType type,
-                                            ModelSafeGroup group,
-                                            ChangeProcessor* change_processor,
-                                            UserShare* user_share) {
-  DVLOG(1) << "Activate: " << ModelTypeToString(type);
-
-  base::AutoLock lock(lock_);
-  // Ensure that the given data type is in the PASSIVE group.
-  ModelSafeRoutingInfo::iterator i = routing_info_.find(type);
-  DCHECK(i != routing_info_.end());
-  DCHECK_EQ(i->second, GROUP_PASSIVE);
-  routing_info_[type] = group;
-
-  // Add the data type's change processor to the list of change
-  // processors so it can receive updates.
-  DCHECK_EQ(processors_.count(type), 0U);
-  processors_[type] = change_processor;
-
-  // Start the change processor.
-  change_processor->Start(user_share);
-  DCHECK(GetProcessorUnsafe(type));
-}
-
-void SyncBackendRegistrar::DeactivateDataType(ModelType type) {
-  DVLOG(1) << "Deactivate: " << ModelTypeToString(type);
-
-  DCHECK(ui_thread_checker_.CalledOnValidThread() || IsControlType(type));
-  base::AutoLock lock(lock_);
-
-  routing_info_.erase(type);
-  ignore_result(processors_.erase(type));
-  DCHECK(!GetProcessorUnsafe(type));
-}
-
-bool SyncBackendRegistrar::IsTypeActivatedForTest(ModelType type) const {
-  return GetProcessor(type) != nullptr;
-}
-
-void SyncBackendRegistrar::OnChangesApplied(
-    ModelType model_type,
-    int64_t model_version,
-    const BaseTransaction* trans,
-    const ImmutableChangeRecordList& changes) {
-  ChangeProcessor* processor = GetProcessor(model_type);
-  if (!processor)
-    return;
-
-  processor->ApplyChangesFromSyncModel(trans, model_version, changes);
-}
-
-void SyncBackendRegistrar::OnChangesComplete(ModelType model_type) {
-  ChangeProcessor* processor = GetProcessor(model_type);
-  if (!processor)
-    return;
-
-  // This call just notifies the processor that it can commit; it
-  // already buffered any changes it plans to makes so needs no
-  // further information.
-  processor->CommitChangesFromSyncModel();
-}
-
-void SyncBackendRegistrar::GetWorkers(
-    std::vector<scoped_refptr<ModelSafeWorker>>* out) {
-  base::AutoLock lock(lock_);
-  out->clear();
-  for (const auto& kv : workers_) {
-    out->push_back(kv.second.get());
-  }
-}
-
-void SyncBackendRegistrar::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
-  base::AutoLock lock(lock_);
-  ModelSafeRoutingInfo copy(routing_info_);
-  out->swap(copy);
-}
-
-ChangeProcessor* SyncBackendRegistrar::GetProcessor(ModelType type) const {
-  base::AutoLock lock(lock_);
-  ChangeProcessor* processor = GetProcessorUnsafe(type);
-  if (!processor)
-    return nullptr;
-
-  // We can only check if |processor| exists, as otherwise the type is
-  // mapped to GROUP_PASSIVE.
-  CHECK(IsCurrentThreadSafeForModel(type));
-  return processor;
-}
-
-ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
-    ModelType type) const {
-  lock_.AssertAcquired();
-  auto it = processors_.find(type);
-
-  // Until model association happens for a datatype, it will not
-  // appear in the processors list.  During this time, it is OK to
-  // drop changes on the floor (since model association has not
-  // happened yet).  When the data type is activated, model
-  // association takes place then the change processor is added to the
-  // |processors_| list.
-  if (it == processors_.end())
-    return nullptr;
-
-  return it->second;
-}
-
-bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
-    ModelType model_type) const {
-  lock_.AssertAcquired();
-  ModelSafeGroup group = GetGroupForModelType(model_type, routing_info_);
-  DCHECK_NE(GROUP_NON_BLOCKING, group);
-
-  if (group == GROUP_PASSIVE) {
-    return IsControlType(model_type);
-  }
-
-  auto it = workers_.find(group);
-  DCHECK(it != workers_.end());
-  return it->second->IsOnModelThread();
-}
-
-SyncBackendRegistrar::~SyncBackendRegistrar() {
-  // All data types should have been deactivated by now.
-  DCHECK(processors_.empty());
-}
-
-void SyncBackendRegistrar::MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
-                                          ModelSafeGroup group) {
-  scoped_refptr<ModelSafeWorker> worker = worker_factory.Run(group);
-  if (worker) {
-    DCHECK(workers_.find(group) == workers_.end());
-    workers_[group] = worker;
-  }
-}
-
-ModelSafeGroup SyncBackendRegistrar::GetInitialGroupForType(
-    ModelType type) const {
-  return non_blocking_types_.Has(type) ? GROUP_NON_BLOCKING : GROUP_PASSIVE;
-}
-
-}  // namespace syncer
diff --git a/components/sync/driver/glue/sync_backend_registrar.h b/components/sync/driver/glue/sync_backend_registrar.h
deleted file mode 100644
index 2f8f3556..0000000
--- a/components/sync/driver/glue/sync_backend_registrar.h
+++ /dev/null
@@ -1,173 +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 COMPONENTS_SYNC_DRIVER_GLUE_SYNC_BACKEND_REGISTRAR_H_
-#define COMPONENTS_SYNC_DRIVER_GLUE_SYNC_BACKEND_REGISTRAR_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/engine/model_safe_worker.h"
-#include "components/sync/engine/sync_manager.h"
-
-namespace syncer {
-
-class ChangeProcessor;
-struct UserShare;
-
-// A class that keep track of the workers, change processors, and
-// routing info for the enabled sync types, and also routes change
-// events to the right processors.
-class SyncBackendRegistrar : public SyncManager::ChangeDelegate {
- public:
-  using ModelSafeWorkerFactory =
-      base::Callback<scoped_refptr<ModelSafeWorker>(ModelSafeGroup)>;
-
-  // |name| is used for debugging. Must be created on the UI thread.
-  SyncBackendRegistrar(const std::string& name,
-                       ModelSafeWorkerFactory worker_factory);
-
-  // A SyncBackendRegistrar is owned by a SyncBackendHostImpl. It is destroyed
-  // by SyncBackendHostImpl::Shutdown() which performs the following operations
-  // on the UI thread:
-  //
-  //   1) Call SyncBackendRegistrar::RequestWorkerStopOnUIThread().
-  //   2) Post a SyncBackendHostCore::DoShutdown() task to the sync thread. This
-  //      task destroys SyncManager which holds a SyncBackendRegistrar pointer.
-  //   3) Take ownership of the sync thread.
-  //   4) Post a task to delete the SyncBackendRegistrar on the sync thread.
-  //      When this task runs, there are no remaining pointers to the
-  //      SyncBackendRegistrar.
-  ~SyncBackendRegistrar() override;
-
-  // Adds |type| to set of non-blocking types. These types are assigned to
-  // GROUP_NON_BLOCKING model safe group and will be treated differently in
-  // ModelTypeRegistry. Unlike directory types, non-blocking types always stay
-  // assigned to GROUP_NON_BLOCKING group.
-  void RegisterNonBlockingType(ModelType type);
-
-  // Informs the SyncBackendRegistrar of the currently enabled set of types.
-  // These types will be placed in the passive group.  This function should be
-  // called exactly once during startup.
-  void SetInitialTypes(ModelTypeSet initial_types);
-
-  // Informs SyncBackendRegistrar about non-blocking type loaded from local
-  // storage. Initial sync was already performed for this type, therefore its
-  // data shouldn't be downloaded as part of configuration.
-  void AddRestoredNonBlockingType(ModelType type);
-
-  // Returns whether or not we are currently syncing encryption keys.
-  // Must be called on the UI thread.
-  bool IsNigoriEnabled() const;
-
-  // Removes all types in |types_to_remove| from the routing info and
-  // adds all the types in |types_to_add| to the routing info that are
-  // not already there (initially put in the passive group).
-  // |types_to_remove| and |types_to_add| must be disjoint.  Returns
-  // the set of newly-added types.  Must be called on the UI thread.
-  ModelTypeSet ConfigureDataTypes(ModelTypeSet types_to_add,
-                                  ModelTypeSet types_to_remove);
-
-  // Returns the set of enabled types as of the last configuration. Note that
-  // this might be different from the current types in the routing info due
-  // to DeactiveDataType being called separately from ConfigureDataTypes.
-  ModelTypeSet GetLastConfiguredTypes() const;
-
-  // Must be called from the UI thread. (See destructor comment.)
-  void RequestWorkerStopOnUIThread();
-
-  // Activates the given data type (which should belong to the given
-  // group) and starts the given change processor.  Must be called
-  // from |group|'s native thread.
-  void ActivateDataType(ModelType type,
-                        ModelSafeGroup group,
-                        ChangeProcessor* change_processor,
-                        UserShare* user_share);
-
-  // Deactivates the given type if necessary.  Must be called from the
-  // UI thread and not |type|'s native thread.  Yes, this is
-  // surprising: see http://crbug.com/92804.
-  void DeactivateDataType(ModelType type);
-
-  // Returns true only between calls to ActivateDataType(type, ...)
-  // and DeactivateDataType(type).  Used only by tests.
-  bool IsTypeActivatedForTest(ModelType type) const;
-
-  // SyncManager::ChangeDelegate implementation.  May be called from
-  // any thread.
-  void OnChangesApplied(ModelType model_type,
-                        int64_t model_version,
-                        const BaseTransaction* trans,
-                        const ImmutableChangeRecordList& changes) override;
-  void OnChangesComplete(ModelType model_type) override;
-
-  void GetWorkers(std::vector<scoped_refptr<ModelSafeWorker>>* out);
-  void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out);
-
- private:
-  // Add a worker for |group| to the worker map if one is successfully created
-  // by |worker_factory|.
-  void MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
-                      ModelSafeGroup group);
-
-  // Returns the change processor for the given model, or null if none
-  // exists.  Must be called from |group|'s native thread.
-  ChangeProcessor* GetProcessor(ModelType type) const;
-
-  // Must be called with |lock_| held.  Simply returns the change
-  // processor for the given type, if it exists.  May be called from
-  // any thread.
-  ChangeProcessor* GetProcessorUnsafe(ModelType type) const;
-
-  // Return true if |model_type| lives on the current thread.  Must be
-  // called with |lock_| held.  May be called on any thread.
-  bool IsCurrentThreadSafeForModel(ModelType model_type) const;
-
-  // Returns model safe group that should be assigned to type when it is first
-  // configured (before activation). Returns GROUP_PASSIVE for directory types
-  // and GROUP_NON_BLOCKING for non-blocking types.
-  ModelSafeGroup GetInitialGroupForType(ModelType type) const;
-
-  // Name used for debugging.
-  const std::string name_;
-
-  // Checker for the UI thread (where this object is constructed).
-  base::ThreadChecker ui_thread_checker_;
-
-  // Protects all variables below.
-  mutable base::Lock lock_;
-
-  // Workers created by this SyncBackendRegistrar.
-  std::map<ModelSafeGroup, scoped_refptr<ModelSafeWorker>> workers_;
-
-  // The change processors that handle the different data types.
-  std::map<ModelType, ChangeProcessor*> processors_;
-
-  // Maps ModelType to ModelSafeGroup.
-  ModelSafeRoutingInfo routing_info_;
-
-  // The types that were enabled as of the last configuration. Updated on each
-  // call to ConfigureDataTypes as well as SetInitialTypes.
-  ModelTypeSet last_configured_types_;
-
-  // Set of types with non-blocking implementation (as opposed to directory
-  // based).
-  ModelTypeSet non_blocking_types_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyncBackendRegistrar);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_DRIVER_GLUE_SYNC_BACKEND_REGISTRAR_H_
diff --git a/components/sync/driver/glue/sync_backend_registrar_unittest.cc b/components/sync/driver/glue/sync_backend_registrar_unittest.cc
deleted file mode 100644
index 4af902de..0000000
--- a/components/sync/driver/glue/sync_backend_registrar_unittest.cc
+++ /dev/null
@@ -1,274 +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 "components/sync/driver/glue/sync_backend_registrar.h"
-
-#include <memory>
-
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread.h"
-#include "components/sync/engine/browser_thread_model_worker.h"
-#include "components/sync/engine/passive_model_worker.h"
-#include "components/sync/model/change_processor_mock.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-
-namespace {
-
-using ::testing::_;
-using ::testing::InSequence;
-using ::testing::Return;
-using ::testing::StrictMock;
-
-class SyncBackendRegistrarTest : public testing::Test {
- public:
-  SyncBackendRegistrarTest()
-      : db_thread_("DBThreadForTest"),
-        file_thread_("FileThreadForTest"),
-        sync_thread_("SyncThreadForTest") {}
-
-  void SetUp() override {
-    db_thread_.StartAndWaitForTesting();
-    file_thread_.StartAndWaitForTesting();
-    sync_thread_.StartAndWaitForTesting();
-    test_user_share_.SetUp();
-    registrar_ = base::MakeUnique<SyncBackendRegistrar>(
-        "test", base::Bind(&SyncBackendRegistrarTest::CreateModelWorkerForGroup,
-                           base::Unretained(this)));
-  }
-
-  void TearDown() override {
-    registrar_->RequestWorkerStopOnUIThread();
-    test_user_share_.TearDown();
-    sync_thread_.task_runner()->DeleteSoon(FROM_HERE, registrar_.release());
-    sync_thread_.FlushForTesting();
-  }
-
-  void TriggerChanges(ModelType type) {
-    registrar_->OnChangesApplied(type, 0, nullptr, ImmutableChangeRecordList());
-    registrar_->OnChangesComplete(type);
-  }
-
-  void ExpectRoutingInfo(const ModelSafeRoutingInfo& expected_routing_info) {
-    ModelSafeRoutingInfo actual_routing_info;
-    registrar_->GetModelSafeRoutingInfo(&actual_routing_info);
-    EXPECT_EQ(expected_routing_info, actual_routing_info);
-  }
-
-  void ExpectHasProcessorsForTypes(ModelTypeSet types) {
-    for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
-      ModelType model_type = ModelTypeFromInt(i);
-      EXPECT_EQ(types.Has(model_type),
-                registrar_->IsTypeActivatedForTest(model_type));
-    }
-  }
-
-  size_t GetWorkersSize() {
-    std::vector<scoped_refptr<ModelSafeWorker>> workers;
-    registrar_->GetWorkers(&workers);
-    return workers.size();
-  }
-
-  // Part of the ActivateDeactivateNonUIDataType test below.
-  void TestNonUIDataTypeActivationAsync(ChangeProcessor* processor,
-                                        base::WaitableEvent* done) {
-    registrar_->ActivateDataType(AUTOFILL, GROUP_DB, processor, user_share());
-    ExpectRoutingInfo({{AUTOFILL, GROUP_DB}});
-    ExpectHasProcessorsForTypes(ModelTypeSet(AUTOFILL));
-    TriggerChanges(AUTOFILL);
-    done->Signal();
-  }
-
-  SyncBackendRegistrar* registrar() { return registrar_.get(); }
-  UserShare* user_share() { return test_user_share_.user_share(); }
-  scoped_refptr<base::SingleThreadTaskRunner> db_task_runner() {
-    return db_thread_.task_runner();
-  }
-
- private:
-  scoped_refptr<ModelSafeWorker> CreateModelWorkerForGroup(
-      ModelSafeGroup group) {
-    switch (group) {
-      case GROUP_UI:
-        return new BrowserThreadModelWorker(message_loop_.task_runner(), group);
-      case GROUP_DB:
-        return new BrowserThreadModelWorker(db_thread_.task_runner(), group);
-      case GROUP_FILE:
-        return new BrowserThreadModelWorker(file_thread_.task_runner(), group);
-      case GROUP_PASSIVE:
-        return new PassiveModelWorker();
-      default:
-        return nullptr;
-    }
-  }
-
-  base::MessageLoop message_loop_;
-  base::Thread db_thread_;
-  base::Thread file_thread_;
-  base::Thread sync_thread_;
-
-  TestUserShare test_user_share_;
-  std::unique_ptr<SyncBackendRegistrar> registrar_;
-};
-
-TEST_F(SyncBackendRegistrarTest, ConstructorEmpty) {
-  registrar()->SetInitialTypes(ModelTypeSet());
-  EXPECT_FALSE(registrar()->IsNigoriEnabled());
-  EXPECT_EQ(4u, GetWorkersSize());
-  ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-}
-
-TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) {
-  registrar()->RegisterNonBlockingType(BOOKMARKS);
-  registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS));
-  EXPECT_TRUE(registrar()->IsNigoriEnabled());
-  EXPECT_EQ(4u, GetWorkersSize());
-  EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes());
-  // Bookmarks dropped because it is nonblocking.
-  // Passwords dropped because of no password store.
-  ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-}
-
-TEST_F(SyncBackendRegistrarTest, ConstructorNonEmptyReversedInitialization) {
-  // The blocking types get to set initial types before NonBlocking types here.
-  registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS));
-  registrar()->RegisterNonBlockingType(BOOKMARKS);
-  EXPECT_TRUE(registrar()->IsNigoriEnabled());
-  EXPECT_EQ(4u, GetWorkersSize());
-  EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes());
-  // Bookmarks dropped because it is nonblocking.
-  // Passwords dropped because of no password store.
-  ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-}
-
-TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) {
-  registrar()->RegisterNonBlockingType(BOOKMARKS);
-  registrar()->SetInitialTypes(ModelTypeSet());
-
-  // Add.
-  const ModelTypeSet types1(BOOKMARKS, NIGORI, AUTOFILL);
-  EXPECT_EQ(types1, registrar()->ConfigureDataTypes(types1, ModelTypeSet()));
-  ExpectRoutingInfo({{BOOKMARKS, GROUP_NON_BLOCKING},
-                     {NIGORI, GROUP_PASSIVE},
-                     {AUTOFILL, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-  EXPECT_EQ(types1, registrar()->GetLastConfiguredTypes());
-
-  // Add and remove.
-  const ModelTypeSet types2(PREFERENCES, THEMES);
-  EXPECT_EQ(types2, registrar()->ConfigureDataTypes(types2, types1));
-
-  ExpectRoutingInfo({{PREFERENCES, GROUP_PASSIVE}, {THEMES, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-  EXPECT_EQ(types2, registrar()->GetLastConfiguredTypes());
-
-  // Remove.
-  EXPECT_TRUE(registrar()->ConfigureDataTypes(ModelTypeSet(), types2).Empty());
-  ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-  EXPECT_EQ(ModelTypeSet(), registrar()->GetLastConfiguredTypes());
-}
-
-TEST_F(SyncBackendRegistrarTest, ActivateDeactivateUIDataType) {
-  InSequence in_sequence;
-  registrar()->SetInitialTypes(ModelTypeSet());
-
-  // Should do nothing.
-  TriggerChanges(BOOKMARKS);
-
-  StrictMock<ChangeProcessorMock> change_processor_mock;
-  EXPECT_CALL(change_processor_mock, StartImpl());
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
-  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _));
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
-  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false));
-
-  const ModelTypeSet types(BOOKMARKS);
-  EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet()));
-  registrar()->ActivateDataType(BOOKMARKS, GROUP_UI, &change_processor_mock,
-                                user_share());
-  ExpectRoutingInfo({{BOOKMARKS, GROUP_UI}});
-  ExpectHasProcessorsForTypes(types);
-
-  TriggerChanges(BOOKMARKS);
-
-  registrar()->DeactivateDataType(BOOKMARKS);
-  ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-
-  // Should do nothing.
-  TriggerChanges(BOOKMARKS);
-}
-
-TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) {
-  InSequence in_sequence;
-  registrar()->SetInitialTypes(ModelTypeSet());
-
-  // Should do nothing.
-  TriggerChanges(AUTOFILL);
-
-  StrictMock<ChangeProcessorMock> change_processor_mock;
-  EXPECT_CALL(change_processor_mock, StartImpl());
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
-  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _));
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
-  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
-  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false));
-
-  const ModelTypeSet types(AUTOFILL);
-  EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet()));
-
-  base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                           base::WaitableEvent::InitialState::NOT_SIGNALED);
-  db_task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&SyncBackendRegistrarTest::TestNonUIDataTypeActivationAsync,
-                 base::Unretained(this), &change_processor_mock, &done));
-  done.Wait();
-
-  registrar()->DeactivateDataType(AUTOFILL);
-  ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
-
-  // Should do nothing.
-  TriggerChanges(AUTOFILL);
-}
-
-// Tests that registration and configuration of non-blocking data types is
-// handled correctly in SyncBackendRegistrar.
-TEST_F(SyncBackendRegistrarTest, ConfigureNonBlockingDataType) {
-  registrar()->RegisterNonBlockingType(AUTOFILL);
-  registrar()->RegisterNonBlockingType(BOOKMARKS);
-
-  ExpectRoutingInfo(ModelSafeRoutingInfo());
-  // Simulate that initial sync was already done for AUTOFILL.
-  registrar()->AddRestoredNonBlockingType(AUTOFILL);
-  // It should be added to routing info and set of configured types.
-  EXPECT_EQ(ModelTypeSet(AUTOFILL), registrar()->GetLastConfiguredTypes());
-  ExpectRoutingInfo({{AUTOFILL, GROUP_NON_BLOCKING}});
-
-  // Configure two non-blocking types. Initial sync wasn't done for BOOKMARKS so
-  // it should be included in types to be downloaded.
-  ModelTypeSet types_to_add(AUTOFILL, BOOKMARKS);
-  ModelTypeSet newly_added_types =
-      registrar()->ConfigureDataTypes(types_to_add, ModelTypeSet());
-  EXPECT_EQ(ModelTypeSet(BOOKMARKS), newly_added_types);
-  EXPECT_EQ(types_to_add, registrar()->GetLastConfiguredTypes());
-  ExpectRoutingInfo(
-      {{AUTOFILL, GROUP_NON_BLOCKING}, {BOOKMARKS, GROUP_NON_BLOCKING}});
-}
-
-}  // namespace
-
-}  // namespace syncer
diff --git a/components/sync/driver/shared_change_processor_unittest.cc b/components/sync/driver/shared_change_processor_unittest.cc
index 72f06592..b6494049 100644
--- a/components/sync/driver/shared_change_processor_unittest.cc
+++ b/components/sync/driver/shared_change_processor_unittest.cc
@@ -44,10 +44,11 @@
       SyncService* sync_service,
       const RegisterDataTypesMethod& register_platform_types_method) override {}
   DataTypeManager* CreateDataTypeManager(
+      ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
       const DataTypeEncryptionHandler* encryption_handler,
-      SyncEngine* engine,
+      ModelTypeConfigurer* configurer,
       DataTypeManagerObserver* observer) override {
     return nullptr;
   }
diff --git a/components/sync/driver/sync_api_component_factory.h b/components/sync/driver/sync_api_component_factory.h
index cb869fd..122fc11 100644
--- a/components/sync/driver/sync_api_component_factory.h
+++ b/components/sync/driver/sync_api_component_factory.h
@@ -80,10 +80,11 @@
 
   // Creates a DataTypeManager; the return pointer is owned by the caller.
   virtual DataTypeManager* CreateDataTypeManager(
+      ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
       const DataTypeEncryptionHandler* encryption_handler,
-      SyncEngine* engine,
+      ModelTypeConfigurer* configurer,
       DataTypeManagerObserver* observer) = 0;
 
   // Creating this in the factory helps us mock it out in testing.
diff --git a/components/sync/driver/sync_api_component_factory_mock.h b/components/sync/driver/sync_api_component_factory_mock.h
index 586543f..65643da 100644
--- a/components/sync/driver/sync_api_component_factory_mock.h
+++ b/components/sync/driver/sync_api_component_factory_mock.h
@@ -29,11 +29,12 @@
 
   MOCK_METHOD2(RegisterDataTypes,
                void(SyncService* sync_service, const RegisterDataTypesMethod&));
-  MOCK_METHOD5(CreateDataTypeManager,
-               DataTypeManager*(const WeakHandle<DataTypeDebugInfoListener>&,
+  MOCK_METHOD6(CreateDataTypeManager,
+               DataTypeManager*(ModelTypeSet initial_types,
+                                const WeakHandle<DataTypeDebugInfoListener>&,
                                 const DataTypeController::TypeMap*,
                                 const DataTypeEncryptionHandler*,
-                                SyncEngine*,
+                                ModelTypeConfigurer*,
                                 DataTypeManagerObserver* observer));
   MOCK_METHOD4(CreateSyncEngine,
                SyncEngine*(const std::string& name,
diff --git a/components/sync/driver/sync_policy_handler.cc b/components/sync/driver/sync_policy_handler.cc
index ed949646..84b8df8d 100644
--- a/components/sync/driver/sync_policy_handler.cc
+++ b/components/sync/driver/sync_policy_handler.cc
@@ -14,7 +14,7 @@
 
 SyncPolicyHandler::SyncPolicyHandler()
     : policy::TypeCheckingPolicyHandler(policy::key::kSyncDisabled,
-                                        base::Value::TYPE_BOOLEAN) {}
+                                        base::Value::Type::BOOLEAN) {}
 
 SyncPolicyHandler::~SyncPolicyHandler() {}
 
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc
index 969ea891..141bed1 100644
--- a/components/sync/engine/fake_sync_engine.cc
+++ b/components/sync/engine/fake_sync_engine.cc
@@ -29,7 +29,7 @@
     const base::Closure& report_unrecoverable_error_function,
     const HttpPostProviderFactoryGetter& http_post_provider_factory_getter,
     std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state) {
-  host->OnEngineInitialized(WeakHandle<JsBackend>(),
+  host->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(),
                             WeakHandle<DataTypeDebugInfoListener>(),
                             kTestCacheGuid, !fail_initial_download_);
 }
diff --git a/components/sync/engine/sync_backend_registrar.cc b/components/sync/engine/sync_backend_registrar.cc
new file mode 100644
index 0000000..ad300d46
--- /dev/null
+++ b/components/sync/engine/sync_backend_registrar.cc
@@ -0,0 +1,289 @@
+// 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 "components/sync/engine/sync_backend_registrar.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/sync/model/change_processor.h"
+#include "components/sync/syncable/user_share.h"
+
+namespace syncer {
+
+SyncBackendRegistrar::SyncBackendRegistrar(
+    const std::string& name,
+    ModelSafeWorkerFactory worker_factory)
+    : name_(name) {
+  DCHECK(!worker_factory.is_null());
+  MaybeAddWorker(worker_factory, GROUP_DB);
+  MaybeAddWorker(worker_factory, GROUP_FILE);
+  MaybeAddWorker(worker_factory, GROUP_UI);
+  MaybeAddWorker(worker_factory, GROUP_PASSIVE);
+  MaybeAddWorker(worker_factory, GROUP_HISTORY);
+  MaybeAddWorker(worker_factory, GROUP_PASSWORD);
+}
+
+void SyncBackendRegistrar::RegisterNonBlockingType(ModelType type) {
+  DCHECK(ui_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(lock_);
+  // There may have been a previously successful sync of a type when passive,
+  // which is now NonBlocking. We're not sure what order these two sets of types
+  // are being registered in, so guard against SetInitialTypes(...) having been
+  // already called by undoing everything to these types.
+  if (routing_info_.find(type) != routing_info_.end() &&
+      routing_info_[type] != GROUP_NON_BLOCKING) {
+    routing_info_.erase(type);
+    last_configured_types_.Remove(type);
+  }
+  non_blocking_types_.Put(type);
+}
+
+void SyncBackendRegistrar::SetInitialTypes(ModelTypeSet initial_types) {
+  base::AutoLock lock(lock_);
+
+  // This function should be called only once, shortly after construction. The
+  // routing info at that point is expected to be empty.
+  DCHECK(routing_info_.empty());
+
+  // Set our initial state to reflect the current status of the sync directory.
+  // This will ensure that our calculations in ConfigureDataTypes() will always
+  // return correct results.
+  for (ModelTypeSet::Iterator it = initial_types.First(); it.Good(); it.Inc()) {
+    // If this type is also registered as NonBlocking, assume that it shouldn't
+    // be registered as passive. The NonBlocking path will eventually take care
+    // of adding to routing_info_ later on.
+    if (!non_blocking_types_.Has(it.Get())) {
+      routing_info_[it.Get()] = GROUP_PASSIVE;
+    }
+  }
+
+  if (!workers_.count(GROUP_HISTORY)) {
+    LOG_IF(WARNING, initial_types.Has(TYPED_URLS))
+        << "History store disabled, cannot sync Omnibox History";
+    routing_info_.erase(TYPED_URLS);
+  }
+
+  if (!workers_.count(GROUP_PASSWORD)) {
+    LOG_IF(WARNING, initial_types.Has(PASSWORDS))
+        << "Password store not initialized, cannot sync passwords";
+    routing_info_.erase(PASSWORDS);
+  }
+
+  // Although this can re-set NonBlocking types, this should be idempotent.
+  last_configured_types_ = GetRoutingInfoTypes(routing_info_);
+}
+
+void SyncBackendRegistrar::AddRestoredNonBlockingType(ModelType type) {
+  DCHECK(ui_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(lock_);
+  DCHECK(non_blocking_types_.Has(type));
+  DCHECK(routing_info_.find(type) == routing_info_.end());
+  routing_info_[type] = GROUP_NON_BLOCKING;
+  last_configured_types_.Put(type);
+}
+
+bool SyncBackendRegistrar::IsNigoriEnabled() const {
+  DCHECK(ui_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(lock_);
+  return routing_info_.find(NIGORI) != routing_info_.end();
+}
+
+ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes(
+    ModelTypeSet types_to_add,
+    ModelTypeSet types_to_remove) {
+  DCHECK(Intersection(types_to_add, types_to_remove).Empty());
+  ModelTypeSet filtered_types_to_add = types_to_add;
+  if (workers_.count(GROUP_HISTORY) == 0) {
+    LOG(WARNING) << "No history worker -- removing TYPED_URLS";
+    filtered_types_to_add.Remove(TYPED_URLS);
+  }
+  if (workers_.count(GROUP_PASSWORD) == 0) {
+    LOG(WARNING) << "No password worker -- removing PASSWORDS";
+    filtered_types_to_add.Remove(PASSWORDS);
+  }
+
+  base::AutoLock lock(lock_);
+  ModelTypeSet newly_added_types;
+  for (ModelTypeSet::Iterator it = filtered_types_to_add.First(); it.Good();
+       it.Inc()) {
+    // Add a newly specified data type corresponding initial group into the
+    // routing_info, if it does not already exist.
+    if (routing_info_.count(it.Get()) == 0) {
+      routing_info_[it.Get()] = GetInitialGroupForType(it.Get());
+      newly_added_types.Put(it.Get());
+    }
+  }
+  for (ModelTypeSet::Iterator it = types_to_remove.First(); it.Good();
+       it.Inc()) {
+    routing_info_.erase(it.Get());
+  }
+
+  // TODO(akalin): Use SVLOG/SLOG if we add any more logging.
+  DVLOG(1) << name_ << ": Adding types " << ModelTypeSetToString(types_to_add)
+           << " (with newly-added types "
+           << ModelTypeSetToString(newly_added_types) << ") and removing types "
+           << ModelTypeSetToString(types_to_remove)
+           << " to get new routing info "
+           << ModelSafeRoutingInfoToString(routing_info_);
+  last_configured_types_ = GetRoutingInfoTypes(routing_info_);
+
+  return newly_added_types;
+}
+
+ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
+  return last_configured_types_;
+}
+
+void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
+  DCHECK(ui_thread_checker_.CalledOnValidThread());
+  base::AutoLock lock(lock_);
+  for (const auto& kv : workers_) {
+    kv.second->RequestStop();
+  }
+}
+
+void SyncBackendRegistrar::ActivateDataType(ModelType type,
+                                            ModelSafeGroup group,
+                                            ChangeProcessor* change_processor,
+                                            UserShare* user_share) {
+  DVLOG(1) << "Activate: " << ModelTypeToString(type);
+
+  base::AutoLock lock(lock_);
+  // Ensure that the given data type is in the PASSIVE group.
+  ModelSafeRoutingInfo::iterator i = routing_info_.find(type);
+  DCHECK(i != routing_info_.end());
+  DCHECK_EQ(i->second, GROUP_PASSIVE);
+  routing_info_[type] = group;
+
+  // Add the data type's change processor to the list of change
+  // processors so it can receive updates.
+  DCHECK_EQ(processors_.count(type), 0U);
+  processors_[type] = change_processor;
+
+  // Start the change processor.
+  change_processor->Start(user_share);
+  DCHECK(GetProcessorUnsafe(type));
+}
+
+void SyncBackendRegistrar::DeactivateDataType(ModelType type) {
+  DVLOG(1) << "Deactivate: " << ModelTypeToString(type);
+
+  DCHECK(ui_thread_checker_.CalledOnValidThread() || IsControlType(type));
+  base::AutoLock lock(lock_);
+
+  routing_info_.erase(type);
+  ignore_result(processors_.erase(type));
+  DCHECK(!GetProcessorUnsafe(type));
+}
+
+bool SyncBackendRegistrar::IsTypeActivatedForTest(ModelType type) const {
+  return GetProcessor(type) != nullptr;
+}
+
+void SyncBackendRegistrar::OnChangesApplied(
+    ModelType model_type,
+    int64_t model_version,
+    const BaseTransaction* trans,
+    const ImmutableChangeRecordList& changes) {
+  ChangeProcessor* processor = GetProcessor(model_type);
+  if (!processor)
+    return;
+
+  processor->ApplyChangesFromSyncModel(trans, model_version, changes);
+}
+
+void SyncBackendRegistrar::OnChangesComplete(ModelType model_type) {
+  ChangeProcessor* processor = GetProcessor(model_type);
+  if (!processor)
+    return;
+
+  // This call just notifies the processor that it can commit; it
+  // already buffered any changes it plans to makes so needs no
+  // further information.
+  processor->CommitChangesFromSyncModel();
+}
+
+void SyncBackendRegistrar::GetWorkers(
+    std::vector<scoped_refptr<ModelSafeWorker>>* out) {
+  base::AutoLock lock(lock_);
+  out->clear();
+  for (const auto& kv : workers_) {
+    out->push_back(kv.second.get());
+  }
+}
+
+void SyncBackendRegistrar::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
+  base::AutoLock lock(lock_);
+  ModelSafeRoutingInfo copy(routing_info_);
+  out->swap(copy);
+}
+
+ChangeProcessor* SyncBackendRegistrar::GetProcessor(ModelType type) const {
+  base::AutoLock lock(lock_);
+  ChangeProcessor* processor = GetProcessorUnsafe(type);
+  if (!processor)
+    return nullptr;
+
+  // We can only check if |processor| exists, as otherwise the type is
+  // mapped to GROUP_PASSIVE.
+  CHECK(IsCurrentThreadSafeForModel(type));
+  return processor;
+}
+
+ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
+    ModelType type) const {
+  lock_.AssertAcquired();
+  auto it = processors_.find(type);
+
+  // Until model association happens for a datatype, it will not
+  // appear in the processors list.  During this time, it is OK to
+  // drop changes on the floor (since model association has not
+  // happened yet).  When the data type is activated, model
+  // association takes place then the change processor is added to the
+  // |processors_| list.
+  if (it == processors_.end())
+    return nullptr;
+
+  return it->second;
+}
+
+bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
+    ModelType model_type) const {
+  lock_.AssertAcquired();
+  ModelSafeGroup group = GetGroupForModelType(model_type, routing_info_);
+  DCHECK_NE(GROUP_NON_BLOCKING, group);
+
+  if (group == GROUP_PASSIVE) {
+    return IsControlType(model_type);
+  }
+
+  auto it = workers_.find(group);
+  DCHECK(it != workers_.end());
+  return it->second->IsOnModelThread();
+}
+
+SyncBackendRegistrar::~SyncBackendRegistrar() {
+  // All data types should have been deactivated by now.
+  DCHECK(processors_.empty());
+}
+
+void SyncBackendRegistrar::MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
+                                          ModelSafeGroup group) {
+  scoped_refptr<ModelSafeWorker> worker = worker_factory.Run(group);
+  if (worker) {
+    DCHECK(workers_.find(group) == workers_.end());
+    workers_[group] = worker;
+  }
+}
+
+ModelSafeGroup SyncBackendRegistrar::GetInitialGroupForType(
+    ModelType type) const {
+  return non_blocking_types_.Has(type) ? GROUP_NON_BLOCKING : GROUP_PASSIVE;
+}
+
+}  // namespace syncer
diff --git a/components/sync/engine/sync_backend_registrar.h b/components/sync/engine/sync_backend_registrar.h
new file mode 100644
index 0000000..23bbb677
--- /dev/null
+++ b/components/sync/engine/sync_backend_registrar.h
@@ -0,0 +1,173 @@
+// 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 COMPONENTS_SYNC_ENGINE_SYNC_BACKEND_REGISTRAR_H_
+#define COMPONENTS_SYNC_ENGINE_SYNC_BACKEND_REGISTRAR_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/engine/model_safe_worker.h"
+#include "components/sync/engine/sync_manager.h"
+
+namespace syncer {
+
+class ChangeProcessor;
+struct UserShare;
+
+// A class that keep track of the workers, change processors, and
+// routing info for the enabled sync types, and also routes change
+// events to the right processors.
+class SyncBackendRegistrar : public SyncManager::ChangeDelegate {
+ public:
+  using ModelSafeWorkerFactory =
+      base::Callback<scoped_refptr<ModelSafeWorker>(ModelSafeGroup)>;
+
+  // |name| is used for debugging. Must be created on the UI thread.
+  SyncBackendRegistrar(const std::string& name,
+                       ModelSafeWorkerFactory worker_factory);
+
+  // A SyncBackendRegistrar is owned by a SyncBackendHostImpl. It is destroyed
+  // by SyncBackendHostImpl::Shutdown() which performs the following operations
+  // on the UI thread:
+  //
+  //   1) Call SyncBackendRegistrar::RequestWorkerStopOnUIThread().
+  //   2) Post a SyncBackendHostCore::DoShutdown() task to the sync thread. This
+  //      task destroys SyncManager which holds a SyncBackendRegistrar pointer.
+  //   3) Take ownership of the sync thread.
+  //   4) Post a task to delete the SyncBackendRegistrar on the sync thread.
+  //      When this task runs, there are no remaining pointers to the
+  //      SyncBackendRegistrar.
+  ~SyncBackendRegistrar() override;
+
+  // Adds |type| to set of non-blocking types. These types are assigned to
+  // GROUP_NON_BLOCKING model safe group and will be treated differently in
+  // ModelTypeRegistry. Unlike directory types, non-blocking types always stay
+  // assigned to GROUP_NON_BLOCKING group.
+  void RegisterNonBlockingType(ModelType type);
+
+  // Informs the SyncBackendRegistrar of the currently enabled set of types.
+  // These types will be placed in the passive group.  This function should be
+  // called exactly once during startup.
+  void SetInitialTypes(ModelTypeSet initial_types);
+
+  // Informs SyncBackendRegistrar about non-blocking type loaded from local
+  // storage. Initial sync was already performed for this type, therefore its
+  // data shouldn't be downloaded as part of configuration.
+  void AddRestoredNonBlockingType(ModelType type);
+
+  // Returns whether or not we are currently syncing encryption keys.
+  // Must be called on the UI thread.
+  bool IsNigoriEnabled() const;
+
+  // Removes all types in |types_to_remove| from the routing info and
+  // adds all the types in |types_to_add| to the routing info that are
+  // not already there (initially put in the passive group).
+  // |types_to_remove| and |types_to_add| must be disjoint.  Returns
+  // the set of newly-added types.  Must be called on the UI thread.
+  ModelTypeSet ConfigureDataTypes(ModelTypeSet types_to_add,
+                                  ModelTypeSet types_to_remove);
+
+  // Returns the set of enabled types as of the last configuration. Note that
+  // this might be different from the current types in the routing info due
+  // to DeactiveDataType being called separately from ConfigureDataTypes.
+  ModelTypeSet GetLastConfiguredTypes() const;
+
+  // Must be called from the UI thread. (See destructor comment.)
+  void RequestWorkerStopOnUIThread();
+
+  // Activates the given data type (which should belong to the given
+  // group) and starts the given change processor.  Must be called
+  // from |group|'s native thread.
+  void ActivateDataType(ModelType type,
+                        ModelSafeGroup group,
+                        ChangeProcessor* change_processor,
+                        UserShare* user_share);
+
+  // Deactivates the given type if necessary.  Must be called from the
+  // UI thread and not |type|'s native thread.  Yes, this is
+  // surprising: see http://crbug.com/92804.
+  void DeactivateDataType(ModelType type);
+
+  // Returns true only between calls to ActivateDataType(type, ...)
+  // and DeactivateDataType(type).  Used only by tests.
+  bool IsTypeActivatedForTest(ModelType type) const;
+
+  // SyncManager::ChangeDelegate implementation.  May be called from
+  // any thread.
+  void OnChangesApplied(ModelType model_type,
+                        int64_t model_version,
+                        const BaseTransaction* trans,
+                        const ImmutableChangeRecordList& changes) override;
+  void OnChangesComplete(ModelType model_type) override;
+
+  void GetWorkers(std::vector<scoped_refptr<ModelSafeWorker>>* out);
+  void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out);
+
+ private:
+  // Add a worker for |group| to the worker map if one is successfully created
+  // by |worker_factory|.
+  void MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
+                      ModelSafeGroup group);
+
+  // Returns the change processor for the given model, or null if none
+  // exists.  Must be called from |group|'s native thread.
+  ChangeProcessor* GetProcessor(ModelType type) const;
+
+  // Must be called with |lock_| held.  Simply returns the change
+  // processor for the given type, if it exists.  May be called from
+  // any thread.
+  ChangeProcessor* GetProcessorUnsafe(ModelType type) const;
+
+  // Return true if |model_type| lives on the current thread.  Must be
+  // called with |lock_| held.  May be called on any thread.
+  bool IsCurrentThreadSafeForModel(ModelType model_type) const;
+
+  // Returns model safe group that should be assigned to type when it is first
+  // configured (before activation). Returns GROUP_PASSIVE for directory types
+  // and GROUP_NON_BLOCKING for non-blocking types.
+  ModelSafeGroup GetInitialGroupForType(ModelType type) const;
+
+  // Name used for debugging.
+  const std::string name_;
+
+  // Checker for the UI thread (where this object is constructed).
+  base::ThreadChecker ui_thread_checker_;
+
+  // Protects all variables below.
+  mutable base::Lock lock_;
+
+  // Workers created by this SyncBackendRegistrar.
+  std::map<ModelSafeGroup, scoped_refptr<ModelSafeWorker>> workers_;
+
+  // The change processors that handle the different data types.
+  std::map<ModelType, ChangeProcessor*> processors_;
+
+  // Maps ModelType to ModelSafeGroup.
+  ModelSafeRoutingInfo routing_info_;
+
+  // The types that were enabled as of the last configuration. Updated on each
+  // call to ConfigureDataTypes as well as SetInitialTypes.
+  ModelTypeSet last_configured_types_;
+
+  // Set of types with non-blocking implementation (as opposed to directory
+  // based).
+  ModelTypeSet non_blocking_types_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncBackendRegistrar);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_ENGINE_SYNC_BACKEND_REGISTRAR_H_
diff --git a/components/sync/engine/sync_backend_registrar_unittest.cc b/components/sync/engine/sync_backend_registrar_unittest.cc
new file mode 100644
index 0000000..678aff2
--- /dev/null
+++ b/components/sync/engine/sync_backend_registrar_unittest.cc
@@ -0,0 +1,274 @@
+// 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 "components/sync/engine/sync_backend_registrar.h"
+
+#include <memory>
+
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "components/sync/engine/browser_thread_model_worker.h"
+#include "components/sync/engine/passive_model_worker.h"
+#include "components/sync/model/change_processor_mock.h"
+#include "components/sync/syncable/test_user_share.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+class SyncBackendRegistrarTest : public testing::Test {
+ public:
+  SyncBackendRegistrarTest()
+      : db_thread_("DBThreadForTest"),
+        file_thread_("FileThreadForTest"),
+        sync_thread_("SyncThreadForTest") {}
+
+  void SetUp() override {
+    db_thread_.StartAndWaitForTesting();
+    file_thread_.StartAndWaitForTesting();
+    sync_thread_.StartAndWaitForTesting();
+    test_user_share_.SetUp();
+    registrar_ = base::MakeUnique<SyncBackendRegistrar>(
+        "test", base::Bind(&SyncBackendRegistrarTest::CreateModelWorkerForGroup,
+                           base::Unretained(this)));
+  }
+
+  void TearDown() override {
+    registrar_->RequestWorkerStopOnUIThread();
+    test_user_share_.TearDown();
+    sync_thread_.task_runner()->DeleteSoon(FROM_HERE, registrar_.release());
+    sync_thread_.FlushForTesting();
+  }
+
+  void TriggerChanges(ModelType type) {
+    registrar_->OnChangesApplied(type, 0, nullptr, ImmutableChangeRecordList());
+    registrar_->OnChangesComplete(type);
+  }
+
+  void ExpectRoutingInfo(const ModelSafeRoutingInfo& expected_routing_info) {
+    ModelSafeRoutingInfo actual_routing_info;
+    registrar_->GetModelSafeRoutingInfo(&actual_routing_info);
+    EXPECT_EQ(expected_routing_info, actual_routing_info);
+  }
+
+  void ExpectHasProcessorsForTypes(ModelTypeSet types) {
+    for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
+      ModelType model_type = ModelTypeFromInt(i);
+      EXPECT_EQ(types.Has(model_type),
+                registrar_->IsTypeActivatedForTest(model_type));
+    }
+  }
+
+  size_t GetWorkersSize() {
+    std::vector<scoped_refptr<ModelSafeWorker>> workers;
+    registrar_->GetWorkers(&workers);
+    return workers.size();
+  }
+
+  // Part of the ActivateDeactivateNonUIDataType test below.
+  void TestNonUIDataTypeActivationAsync(ChangeProcessor* processor,
+                                        base::WaitableEvent* done) {
+    registrar_->ActivateDataType(AUTOFILL, GROUP_DB, processor, user_share());
+    ExpectRoutingInfo({{AUTOFILL, GROUP_DB}});
+    ExpectHasProcessorsForTypes(ModelTypeSet(AUTOFILL));
+    TriggerChanges(AUTOFILL);
+    done->Signal();
+  }
+
+  SyncBackendRegistrar* registrar() { return registrar_.get(); }
+  UserShare* user_share() { return test_user_share_.user_share(); }
+  scoped_refptr<base::SingleThreadTaskRunner> db_task_runner() {
+    return db_thread_.task_runner();
+  }
+
+ private:
+  scoped_refptr<ModelSafeWorker> CreateModelWorkerForGroup(
+      ModelSafeGroup group) {
+    switch (group) {
+      case GROUP_UI:
+        return new BrowserThreadModelWorker(message_loop_.task_runner(), group);
+      case GROUP_DB:
+        return new BrowserThreadModelWorker(db_thread_.task_runner(), group);
+      case GROUP_FILE:
+        return new BrowserThreadModelWorker(file_thread_.task_runner(), group);
+      case GROUP_PASSIVE:
+        return new PassiveModelWorker();
+      default:
+        return nullptr;
+    }
+  }
+
+  base::MessageLoop message_loop_;
+  base::Thread db_thread_;
+  base::Thread file_thread_;
+  base::Thread sync_thread_;
+
+  TestUserShare test_user_share_;
+  std::unique_ptr<SyncBackendRegistrar> registrar_;
+};
+
+TEST_F(SyncBackendRegistrarTest, ConstructorEmpty) {
+  registrar()->SetInitialTypes(ModelTypeSet());
+  EXPECT_FALSE(registrar()->IsNigoriEnabled());
+  EXPECT_EQ(4u, GetWorkersSize());
+  ExpectRoutingInfo(ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+}
+
+TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) {
+  registrar()->RegisterNonBlockingType(BOOKMARKS);
+  registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS));
+  EXPECT_TRUE(registrar()->IsNigoriEnabled());
+  EXPECT_EQ(4u, GetWorkersSize());
+  EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes());
+  // Bookmarks dropped because it is nonblocking.
+  // Passwords dropped because of no password store.
+  ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+}
+
+TEST_F(SyncBackendRegistrarTest, ConstructorNonEmptyReversedInitialization) {
+  // The blocking types get to set initial types before NonBlocking types here.
+  registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS));
+  registrar()->RegisterNonBlockingType(BOOKMARKS);
+  EXPECT_TRUE(registrar()->IsNigoriEnabled());
+  EXPECT_EQ(4u, GetWorkersSize());
+  EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes());
+  // Bookmarks dropped because it is nonblocking.
+  // Passwords dropped because of no password store.
+  ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+}
+
+TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) {
+  registrar()->RegisterNonBlockingType(BOOKMARKS);
+  registrar()->SetInitialTypes(ModelTypeSet());
+
+  // Add.
+  const ModelTypeSet types1(BOOKMARKS, NIGORI, AUTOFILL);
+  EXPECT_EQ(types1, registrar()->ConfigureDataTypes(types1, ModelTypeSet()));
+  ExpectRoutingInfo({{BOOKMARKS, GROUP_NON_BLOCKING},
+                     {NIGORI, GROUP_PASSIVE},
+                     {AUTOFILL, GROUP_PASSIVE}});
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+  EXPECT_EQ(types1, registrar()->GetLastConfiguredTypes());
+
+  // Add and remove.
+  const ModelTypeSet types2(PREFERENCES, THEMES);
+  EXPECT_EQ(types2, registrar()->ConfigureDataTypes(types2, types1));
+
+  ExpectRoutingInfo({{PREFERENCES, GROUP_PASSIVE}, {THEMES, GROUP_PASSIVE}});
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+  EXPECT_EQ(types2, registrar()->GetLastConfiguredTypes());
+
+  // Remove.
+  EXPECT_TRUE(registrar()->ConfigureDataTypes(ModelTypeSet(), types2).Empty());
+  ExpectRoutingInfo(ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+  EXPECT_EQ(ModelTypeSet(), registrar()->GetLastConfiguredTypes());
+}
+
+TEST_F(SyncBackendRegistrarTest, ActivateDeactivateUIDataType) {
+  InSequence in_sequence;
+  registrar()->SetInitialTypes(ModelTypeSet());
+
+  // Should do nothing.
+  TriggerChanges(BOOKMARKS);
+
+  StrictMock<ChangeProcessorMock> change_processor_mock;
+  EXPECT_CALL(change_processor_mock, StartImpl());
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
+  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _));
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
+  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false));
+
+  const ModelTypeSet types(BOOKMARKS);
+  EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet()));
+  registrar()->ActivateDataType(BOOKMARKS, GROUP_UI, &change_processor_mock,
+                                user_share());
+  ExpectRoutingInfo({{BOOKMARKS, GROUP_UI}});
+  ExpectHasProcessorsForTypes(types);
+
+  TriggerChanges(BOOKMARKS);
+
+  registrar()->DeactivateDataType(BOOKMARKS);
+  ExpectRoutingInfo(ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+
+  // Should do nothing.
+  TriggerChanges(BOOKMARKS);
+}
+
+TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) {
+  InSequence in_sequence;
+  registrar()->SetInitialTypes(ModelTypeSet());
+
+  // Should do nothing.
+  TriggerChanges(AUTOFILL);
+
+  StrictMock<ChangeProcessorMock> change_processor_mock;
+  EXPECT_CALL(change_processor_mock, StartImpl());
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
+  EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _));
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true));
+  EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel());
+  EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false));
+
+  const ModelTypeSet types(AUTOFILL);
+  EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet()));
+
+  base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                           base::WaitableEvent::InitialState::NOT_SIGNALED);
+  db_task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&SyncBackendRegistrarTest::TestNonUIDataTypeActivationAsync,
+                 base::Unretained(this), &change_processor_mock, &done));
+  done.Wait();
+
+  registrar()->DeactivateDataType(AUTOFILL);
+  ExpectRoutingInfo(ModelSafeRoutingInfo());
+  ExpectHasProcessorsForTypes(ModelTypeSet());
+
+  // Should do nothing.
+  TriggerChanges(AUTOFILL);
+}
+
+// Tests that registration and configuration of non-blocking data types is
+// handled correctly in SyncBackendRegistrar.
+TEST_F(SyncBackendRegistrarTest, ConfigureNonBlockingDataType) {
+  registrar()->RegisterNonBlockingType(AUTOFILL);
+  registrar()->RegisterNonBlockingType(BOOKMARKS);
+
+  ExpectRoutingInfo(ModelSafeRoutingInfo());
+  // Simulate that initial sync was already done for AUTOFILL.
+  registrar()->AddRestoredNonBlockingType(AUTOFILL);
+  // It should be added to routing info and set of configured types.
+  EXPECT_EQ(ModelTypeSet(AUTOFILL), registrar()->GetLastConfiguredTypes());
+  ExpectRoutingInfo({{AUTOFILL, GROUP_NON_BLOCKING}});
+
+  // Configure two non-blocking types. Initial sync wasn't done for BOOKMARKS so
+  // it should be included in types to be downloaded.
+  ModelTypeSet types_to_add(AUTOFILL, BOOKMARKS);
+  ModelTypeSet newly_added_types =
+      registrar()->ConfigureDataTypes(types_to_add, ModelTypeSet());
+  EXPECT_EQ(ModelTypeSet(BOOKMARKS), newly_added_types);
+  EXPECT_EQ(types_to_add, registrar()->GetLastConfiguredTypes());
+  ExpectRoutingInfo(
+      {{AUTOFILL, GROUP_NON_BLOCKING}, {BOOKMARKS, GROUP_NON_BLOCKING}});
+}
+
+}  // namespace
+
+}  // namespace syncer
diff --git a/components/sync/engine/sync_engine_host.h b/components/sync/engine/sync_engine_host.h
index c04289b..ad18493 100644
--- a/components/sync/engine/sync_engine_host.h
+++ b/components/sync/engine/sync_engine_host.h
@@ -41,6 +41,7 @@
   // |js_backend| is what about:sync interacts with. It is initialized only if
   // |success| is true.
   virtual void OnEngineInitialized(
+      ModelTypeSet initial_types,
       const WeakHandle<JsBackend>& js_backend,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const std::string& cache_guid,
diff --git a/components/sync/engine_impl/commit.cc b/components/sync/engine_impl/commit.cc
index 27444c3..877ba4c 100644
--- a/components/sync/engine_impl/commit.cc
+++ b/components/sync/engine_impl/commit.cc
@@ -74,10 +74,12 @@
 
   // Set extensions activity if bookmark commits are present.
   ExtensionsActivity::Records extensions_activity_buffer;
-  ContributionMap::const_iterator it = contributions.find(BOOKMARKS);
-  if (it != contributions.end() && it->second->GetNumEntries() != 0) {
-    commit_util::AddExtensionsActivityToMessage(
-        extensions_activity, &extensions_activity_buffer, commit_message);
+  if (extensions_activity != nullptr) {
+    ContributionMap::const_iterator it = contributions.find(BOOKMARKS);
+    if (it != contributions.end() && it->second->GetNumEntries() != 0) {
+      commit_util::AddExtensionsActivityToMessage(
+          extensions_activity, &extensions_activity_buffer, commit_message);
+    }
   }
 
   // Set the client config params.
@@ -179,9 +181,10 @@
   }
 
   // Handle bookmarks' special extensions activity stats.
-  if (cycle->status_controller()
-          .model_neutral_state()
-          .num_successful_bookmark_commits == 0) {
+  if (extensions_activity != nullptr &&
+      cycle->status_controller()
+              .model_neutral_state()
+              .num_successful_bookmark_commits == 0) {
     extensions_activity->PutRecords(extensions_activity_buffer_);
   }
 
diff --git a/components/sync/engine_impl/commit.h b/components/sync/engine_impl/commit.h
index e9a65823..585038f 100644
--- a/components/sync/engine_impl/commit.h
+++ b/components/sync/engine_impl/commit.h
@@ -46,6 +46,7 @@
   // This destructor will DCHECK if CleanUp() has not been called.
   ~Commit();
 
+  // |extensions_activity| may be null.
   static Commit* Init(ModelTypeSet requested_types,
                       ModelTypeSet enabled_types,
                       size_t max_entries,
@@ -56,6 +57,7 @@
                       CommitProcessor* commit_processor,
                       ExtensionsActivity* extensions_activity);
 
+  // |extensions_activity| may be null.
   SyncerError PostAndProcessResponse(NudgeTracker* nudge_tracker,
                                      SyncCycle* cycle,
                                      StatusController* status,
diff --git a/components/sync/model_impl/shared_model_type_processor.cc b/components/sync/model_impl/shared_model_type_processor.cc
index b55963b..6b5816f 100644
--- a/components/sync/model_impl/shared_model_type_processor.cc
+++ b/components/sync/model_impl/shared_model_type_processor.cc
@@ -288,6 +288,7 @@
 
     if (entity->CanClearMetadata()) {
       change_list->ClearMetadata(entity->storage_key());
+      storage_key_to_tag_hash_.erase(entity->storage_key());
       entities_.erase(entity->metadata().client_tag_hash());
     } else {
       change_list->UpdateMetadata(entity->storage_key(), entity->metadata());
@@ -334,6 +335,7 @@
 
     if (entity->CanClearMetadata()) {
       metadata_changes->ClearMetadata(entity->storage_key());
+      storage_key_to_tag_hash_.erase(entity->storage_key());
       entities_.erase(entity->metadata().client_tag_hash());
     } else {
       metadata_changes->UpdateMetadata(entity->storage_key(),
diff --git a/components/sync/model_impl/shared_model_type_processor.h b/components/sync/model_impl/shared_model_type_processor.h
index 4b0a6dd..65530d4 100644
--- a/components/sync/model_impl/shared_model_type_processor.h
+++ b/components/sync/model_impl/shared_model_type_processor.h
@@ -166,7 +166,7 @@
   // entities may not always contain model type data/specifics.
   EntityMap entities_;
 
-  // The bridge wants to communicate entirly via storage keys that is free to
+  // The bridge wants to communicate entirely via storage keys that is free to
   // define and can understand more easily. All of the sync machinery wants to
   // use client tag hash. This mapping allows us to convert from storage key to
   // client tag hash. The other direction can use |entities_|.
diff --git a/components/sync/model_impl/shared_model_type_processor_unittest.cc b/components/sync/model_impl/shared_model_type_processor_unittest.cc
index cefa9a1..7551170 100644
--- a/components/sync/model_impl/shared_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/shared_model_type_processor_unittest.cc
@@ -859,6 +859,14 @@
   worker()->AckOnePendingCommit();
   EXPECT_EQ(0U, db().metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
+
+  // Create item again.
+  WriteItemAndAck(kKey1, kValue1);
+  const EntityMetadata metadata_v3 = db().GetMetadata(kKey1);
+  EXPECT_FALSE(metadata_v3.is_deleted());
+  EXPECT_EQ(1, metadata_v3.sequence_number());
+  EXPECT_EQ(1, metadata_v3.acked_sequence_number());
+  EXPECT_EQ(3, metadata_v3.server_version());
 }
 
 // Tests creating and deleting an item locally before receiving a commit
@@ -927,6 +935,14 @@
   EXPECT_EQ(0U, db().metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
+
+  // Create item again.
+  WriteItemAndAck(kKey1, kValue1);
+  const EntityMetadata metadata = db().GetMetadata(kKey1);
+  EXPECT_FALSE(metadata.is_deleted());
+  EXPECT_EQ(1, metadata.sequence_number());
+  EXPECT_EQ(1, metadata.acked_sequence_number());
+  EXPECT_EQ(3, metadata.server_version());
 }
 
 // Deletes an item we've never seen before.
diff --git a/components/sync/syncable/model_type.cc b/components/sync/syncable/model_type.cc
index 08fa26d..d256055f 100644
--- a/components/sync/syncable/model_type.cc
+++ b/components/sync/syncable/model_type.cc
@@ -615,11 +615,11 @@
 }
 
 ModelType ModelTypeFromValue(const base::Value& value) {
-  if (value.IsType(base::Value::TYPE_STRING)) {
+  if (value.IsType(base::Value::Type::STRING)) {
     std::string result;
     CHECK(value.GetAsString(&result));
     return ModelTypeFromString(result);
-  } else if (value.IsType(base::Value::TYPE_INTEGER)) {
+  } else if (value.IsType(base::Value::Type::INTEGER)) {
     int result;
     CHECK(value.GetAsInteger(&result));
     return ModelTypeFromInt(result);
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index a203da3..7f893cf 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -101,7 +101,7 @@
       // Update the local preference based on what we got from the
       // sync server. Note: this only updates the user value store, which is
       // ignored if the preference is policy controlled.
-      if (new_value->IsType(base::Value::TYPE_NULL)) {
+      if (new_value->IsType(base::Value::Type::NONE)) {
         LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
         pref_service_->ClearPref(pref_name.c_str());
       } else if (!new_value->IsType(user_pref_value->GetType())) {
@@ -124,7 +124,7 @@
         sync_changes->push_back(syncer::SyncChange(
             FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
       }
-    } else if (!sync_value->IsType(base::Value::TYPE_NULL)) {
+    } else if (!sync_value->IsType(base::Value::Type::NONE)) {
       // Only a server value exists. Just set the local user value.
       pref_service_->Set(pref_name.c_str(), *sync_value);
     } else {
@@ -251,7 +251,7 @@
     const std::string& name,
     const base::Value& value,
     syncer::SyncData* sync_data) const {
-  if (value.IsType(base::Value::TYPE_NULL)) {
+  if (value.IsType(base::Value::Type::NONE)) {
     LOG(ERROR) << "Attempting to sync a null pref value for " << name;
     return false;
   }
@@ -277,13 +277,13 @@
 
 base::Value* PrefModelAssociator::MergeListValues(const base::Value& from_value,
                                                   const base::Value& to_value) {
-  if (from_value.GetType() == base::Value::TYPE_NULL)
+  if (from_value.GetType() == base::Value::Type::NONE)
     return to_value.DeepCopy();
-  if (to_value.GetType() == base::Value::TYPE_NULL)
+  if (to_value.GetType() == base::Value::Type::NONE)
     return from_value.DeepCopy();
 
-  DCHECK(from_value.GetType() == base::Value::TYPE_LIST);
-  DCHECK(to_value.GetType() == base::Value::TYPE_LIST);
+  DCHECK(from_value.GetType() == base::Value::Type::LIST);
+  DCHECK(to_value.GetType() == base::Value::Type::LIST);
   const base::ListValue& from_list_value =
       static_cast<const base::ListValue&>(from_value);
   const base::ListValue& to_list_value =
@@ -299,13 +299,13 @@
 base::Value* PrefModelAssociator::MergeDictionaryValues(
     const base::Value& from_value,
     const base::Value& to_value) {
-  if (from_value.GetType() == base::Value::TYPE_NULL)
+  if (from_value.GetType() == base::Value::Type::NONE)
     return to_value.DeepCopy();
-  if (to_value.GetType() == base::Value::TYPE_NULL)
+  if (to_value.GetType() == base::Value::Type::NONE)
     return from_value.DeepCopy();
 
-  DCHECK_EQ(from_value.GetType(), base::Value::TYPE_DICTIONARY);
-  DCHECK_EQ(to_value.GetType(), base::Value::TYPE_DICTIONARY);
+  DCHECK_EQ(from_value.GetType(), base::Value::Type::DICTIONARY);
+  DCHECK_EQ(to_value.GetType(), base::Value::Type::DICTIONARY);
   const base::DictionaryValue& from_dict_value =
       static_cast<const base::DictionaryValue&>(from_value);
   const base::DictionaryValue& to_dict_value =
@@ -317,8 +317,8 @@
     const base::Value* from_key_value = &it.value();
     base::Value* to_key_value;
     if (result->GetWithoutPathExpansion(it.key(), &to_key_value)) {
-      if (from_key_value->GetType() == base::Value::TYPE_DICTIONARY &&
-          to_key_value->GetType() == base::Value::TYPE_DICTIONARY) {
+      if (from_key_value->GetType() == base::Value::Type::DICTIONARY &&
+          to_key_value->GetType() == base::Value::Type::DICTIONARY) {
         base::Value* merged_value =
             MergeDictionaryValues(*from_key_value, *to_key_value);
         result->SetWithoutPathExpansion(it.key(), merged_value);
diff --git a/components/sync_preferences/pref_model_associator_unittest.cc b/components/sync_preferences/pref_model_associator_unittest.cc
index dbfd06e..22577e0b 100644
--- a/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/components/sync_preferences/pref_model_associator_unittest.cc
@@ -81,9 +81,9 @@
         pref_service_->FindPreference(pref_name.c_str());
     ASSERT_TRUE(pref);
     base::Value::Type type = pref->GetType();
-    if (type == base::Value::TYPE_DICTIONARY)
+    if (type == base::Value::Type::DICTIONARY)
       empty_value.reset(new base::DictionaryValue);
-    else if (type == base::Value::TYPE_LIST)
+    else if (type == base::Value::Type::LIST)
       empty_value.reset(new base::ListValue);
     else
       FAIL();
diff --git a/components/sync_sessions/sessions_sync_manager.cc b/components/sync_sessions/sessions_sync_manager.cc
index 04e01b7..9c55aa8 100644
--- a/components/sync_sessions/sessions_sync_manager.cc
+++ b/components/sync_sessions/sessions_sync_manager.cc
@@ -7,9 +7,12 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/format_macros.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "components/sync/base/hash_util.h"
 #include "components/sync/device_info/local_device_info_provider.h"
@@ -21,6 +24,7 @@
 #include "components/sync_sessions/synced_tab_delegate.h"
 #include "components/sync_sessions/synced_window_delegate.h"
 #include "components/sync_sessions/synced_window_delegates_getter.h"
+#include "components/sync_sessions/tab_node_pool.h"
 #include "components/variations/variations_associated_data.h"
 
 using sessions::SerializedNavigationEntry;
@@ -62,17 +66,31 @@
   return s1->modified_time > s2->modified_time;
 }
 
+std::string TabNodeIdToTag(const std::string& machine_tag, int tab_node_id) {
+  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
+}
+
 std::string TagFromSpecifics(const sync_pb::SessionSpecifics& specifics) {
   if (specifics.has_header()) {
     return specifics.session_tag();
   } else if (specifics.has_tab()) {
-    return TabNodePool::TabIdToTag(specifics.session_tag(),
-                                   specifics.tab_node_id());
+    return TabNodeIdToTag(specifics.session_tag(), specifics.tab_node_id());
   } else {
     return std::string();
   }
 }
 
+sync_pb::SessionSpecifics SessionTabToSpecifics(
+    const sessions::SessionTab& session_tab,
+    const std::string& local_tag,
+    int tab_node_id) {
+  sync_pb::SessionSpecifics specifics;
+  specifics.mutable_tab()->CopyFrom(session_tab.ToSyncData());
+  specifics.set_session_tag(local_tag);
+  specifics.set_tab_node_id(tab_node_id);
+  return specifics;
+}
+
 }  // namespace
 
 // |local_device| is owned by ProfileSyncService, its lifetime exceeds
@@ -117,7 +135,6 @@
     std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
   syncer::SyncMergeResult merge_result(type);
   DCHECK(session_tracker_.Empty());
-  DCHECK_EQ(0U, local_tab_pool_.Capacity());
 
   error_handler_ = std::move(error_handler);
   sync_processor_ = std::move(sync_processor);
@@ -153,13 +170,12 @@
     InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID());
   }
 
-  session_tracker_.SetLocalSessionTag(current_machine_tag_);
+  session_tracker_.SetLocalSessionTag(current_machine_tag());
 
   syncer::SyncChangeList new_changes;
 
   // First, we iterate over sync data to update our session_tracker_.
-  syncer::SyncDataList restored_tabs;
-  if (!InitFromSyncModel(initial_sync_data, &restored_tabs, &new_changes)) {
+  if (!InitFromSyncModel(initial_sync_data, &new_changes)) {
     // The sync db didn't have a header node for us. Create one.
     sync_pb::EntitySpecifics specifics;
     sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session();
@@ -176,12 +192,12 @@
 #if defined(OS_ANDROID)
   std::string sync_machine_tag(
       BuildMachineTag(local_device_->GetLocalSyncCacheGUID()));
-  if (current_machine_tag_.compare(sync_machine_tag) != 0)
+  if (current_machine_tag().compare(sync_machine_tag) != 0)
     DeleteForeignSessionInternal(sync_machine_tag, &new_changes);
 #endif
 
   // Check if anything has changed on the local client side.
-  AssociateWindows(RELOAD_TABS, restored_tabs, &new_changes);
+  AssociateWindows(RELOAD_TABS, &new_changes);
   local_tab_pool_out_of_sync_ = false;
 
   merge_result.set_error(
@@ -193,7 +209,6 @@
 
 void SessionsSyncManager::AssociateWindows(
     ReloadTabsOption option,
-    const syncer::SyncDataList& restored_tabs,
     syncer::SyncChangeList* change_output) {
   const std::string local_tag = current_machine_tag();
   sync_pb::SessionSpecifics specifics;
@@ -262,6 +277,12 @@
         if (!synced_tab)
           continue;
 
+        // Placeholder tabs are those without WebContents, either because they
+        // were never loaded into memory or they were evicted from memory
+        // (typically only on Android devices). They only have a tab id, window
+        // id, and a saved synced id (corresponding to the tab node id). Note
+        // that only placeholders have this sync id, as it's necessary to
+        // properly reassociate the tab with the entity that was backing it.
         if (synced_tab->IsPlaceholderTab()) {
           // For tabs without WebContents update the |tab_id| and |window_id|,
           // as it could have changed after a session restore.
@@ -271,7 +292,7 @@
           if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID &&
               tab_id > TabNodePool::kInvalidTabID) {
             AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id,
-                                            restored_tabs, change_output);
+                                            change_output);
             found_tabs = true;
             window_s.add_tab(tab_id);
           }
@@ -304,8 +325,15 @@
       }
     }
   }
-  local_tab_pool_.DeleteUnassociatedTabNodes(change_output);
-  session_tracker_.CleanupSession(local_tag);
+  std::set<int> deleted_tab_node_ids;
+  session_tracker_.CleanupLocalTabs(&deleted_tab_node_ids);
+  for (int tab_node_id : deleted_tab_node_ids) {
+    std::string tab_node_tag =
+        TabNodeIdToTag(current_machine_tag(), tab_node_id);
+    change_output->push_back(syncer::SyncChange(
+        FROM_HERE, syncer::SyncChange::ACTION_DELETE,
+        syncer::SyncData::CreateLocalDelete(tab_node_tag, syncer::SESSIONS)));
+  }
 
   // Always update the header.  Sync takes care of dropping this update
   // if the entity specifics are identical (i.e windows, client name did
@@ -318,73 +346,62 @@
       syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
 }
 
-void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab,
+void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate,
                                        syncer::SyncChangeList* change_output) {
-  DCHECK(!tab->IsPlaceholderTab());
-  SessionID::id_type tab_id = tab->GetSessionId();
+  DCHECK(!tab_delegate->IsPlaceholderTab());
 
-  if (tab->IsBeingDestroyed()) {
-    // This tab is closing.
-    TabLinksMap::iterator tab_iter = local_tab_map_.find(tab_id);
-    if (tab_iter == local_tab_map_.end()) {
-      // We aren't tracking this tab (for example, sync setting page).
-      return;
-    }
-    local_tab_pool_.FreeTabNode(tab_iter->second->tab_node_id(), change_output);
-    local_tab_map_.erase(tab_iter);
+  if (tab_delegate->IsBeingDestroyed()) {
+    // Do nothing. By not proactively adding the tab to the session, it will be
+    // removed if necessary during subsequent cleanup.
     return;
   }
 
-  if (!tab->ShouldSync(sessions_client_))
+  if (!tab_delegate->ShouldSync(sessions_client_))
     return;
 
-  TabLinksMap::iterator local_tab_map_iter = local_tab_map_.find(tab_id);
-  TabLink* tab_link = nullptr;
+  SessionID::id_type tab_id = tab_delegate->GetSessionId();
+  DVLOG(1) << "Syncing tab " << tab_id << " from window "
+           << tab_delegate->GetWindowId();
 
-  if (local_tab_map_iter == local_tab_map_.end()) {
-    int tab_node_id = tab->GetSyncId();
-    // If there is an old sync node for the tab, reuse it.  If this is a new
-    // tab, get a sync node for it.
-    if (!local_tab_pool_.IsUnassociatedTabNode(tab_node_id)) {
-      tab_node_id = local_tab_pool_.GetFreeTabNode(change_output);
-      tab->SetSyncId(tab_node_id);
-    }
-    local_tab_pool_.AssociateTabNode(tab_node_id, tab_id);
-    tab_link = new TabLink(tab_node_id, tab);
-    local_tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link);
-  } else {
-    // This tab is already associated with a sync node, reuse it.
-    // Note: on some platforms the tab object may have changed, so we ensure
-    // the tab link is up to date.
-    tab_link = local_tab_map_iter->second.get();
-    local_tab_map_iter->second->set_tab(tab);
-  }
-  DCHECK(tab_link);
-  DCHECK_NE(tab_link->tab_node_id(), TabNodePool::kInvalidTabNodeID);
-  DVLOG(1) << "Reloading tab " << tab_id << " from window "
-           << tab->GetWindowId();
+  int tab_node_id = TabNodePool::kInvalidTabNodeID;
+  bool existing_tab_node =
+      session_tracker_.GetTabNodeForLocalTab(tab_id, &tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
+  sessions::SessionTab* session_tab =
+      session_tracker_.GetTab(current_machine_tag(), tab_id);
 
-  // Write to sync model.
-  sync_pb::EntitySpecifics specifics;
-  LocalTabDelegateToSpecifics(*tab, specifics.mutable_session());
-  syncer::SyncData data = syncer::SyncData::CreateLocalData(
-      TabNodePool::TabIdToTag(current_machine_tag_, tab_link->tab_node_id()),
-      current_session_name_, specifics);
-  change_output->push_back(
-      syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
+  // Get the previously synced url.
+  int old_index = session_tab->normalized_navigation_index();
+  GURL old_url;
+  if (session_tab->navigations.size() > static_cast<size_t>(old_index))
+    old_url = session_tab->navigations[old_index].virtual_url();
 
-  int current_index = tab->GetCurrentEntryIndex();
-  const GURL new_url = tab->GetVirtualURLAtIndex(current_index);
-  if (new_url != tab_link->url()) {
-    tab_link->set_url(new_url);
-    favicon_cache_.OnFaviconVisited(new_url,
-                                    tab->GetFaviconURLAtIndex(current_index));
-    page_revisit_broadcaster_.OnPageVisit(
-        new_url, tab->GetTransitionAtIndex(current_index));
-  }
-
+  // Update the tracker's session representation.
+  SetSessionTabFromDelegate(*tab_delegate, base::Time::Now(), session_tab);
+  SetVariationIds(session_tab);
   session_tracker_.GetSession(current_machine_tag())->modified_time =
       base::Time::Now();
+
+  // Write to the sync model itself.
+  sync_pb::EntitySpecifics specifics;
+  specifics.mutable_session()->CopyFrom(
+      SessionTabToSpecifics(*session_tab, current_machine_tag(), tab_node_id));
+  syncer::SyncData data = syncer::SyncData::CreateLocalData(
+      TabNodeIdToTag(current_machine_tag(), tab_node_id), current_session_name_,
+      specifics);
+  change_output->push_back(syncer::SyncChange(
+      FROM_HERE, existing_tab_node ? syncer::SyncChange::ACTION_UPDATE
+                                   : syncer::SyncChange::ACTION_ADD,
+      data));
+
+  int current_index = tab_delegate->GetCurrentEntryIndex();
+  const GURL new_url = tab_delegate->GetVirtualURLAtIndex(current_index);
+  if (new_url != old_url) {
+    favicon_cache_.OnFaviconVisited(
+        new_url, tab_delegate->GetFaviconURLAtIndex(current_index));
+    page_revisit_broadcaster_.OnPageVisit(
+        new_url, tab_delegate->GetTransitionAtIndex(current_index));
+  }
 }
 
 bool SessionsSyncManager::RebuildAssociations() {
@@ -444,21 +461,14 @@
   // "interesting" by going to a valid URL, in which case it needs to be added
   // to the window's tab information. Similarly, if a tab became
   // "uninteresting", we remove it from the window's tab information.
-  AssociateWindows(DONT_RELOAD_TABS, syncer::SyncDataList(), &changes);
+  AssociateWindows(DONT_RELOAD_TABS, &changes);
   sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
 }
 
 void SessionsSyncManager::OnFaviconsChanged(const std::set<GURL>& page_urls,
                                             const GURL& /* icon_url */) {
-  // TODO(zea): consider a separate container for tabs with outstanding favicon
-  // loads so we don't have to iterate through all tabs comparing urls.
-  for (const GURL& page_url : page_urls) {
-    for (TabLinksMap::iterator tab_iter = local_tab_map_.begin();
-         tab_iter != local_tab_map_.end(); ++tab_iter) {
-      if (tab_iter->second->url() == page_url)
-        favicon_cache_.OnPageFaviconUpdated(page_url);
-    }
-  }
+  for (const GURL& page_url : page_urls)
+    favicon_cache_.OnPageFaviconUpdated(page_url);
 }
 
 void SessionsSyncManager::StopSyncing(syncer::ModelType type) {
@@ -471,8 +481,6 @@
   sync_processor_.reset(nullptr);
   error_handler_.reset();
   session_tracker_.Clear();
-  local_tab_map_.clear();
-  local_tab_pool_.Clear();
   current_machine_tag_.clear();
   current_session_name_.clear();
   local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID;
@@ -495,22 +503,17 @@
       current_machine_tag(), current_session_name_, header_entity);
   list.push_back(data);
 
-  for (auto win_iter = session->windows.begin();
-       win_iter != session->windows.end(); ++win_iter) {
-    for (auto tabs_iter = win_iter->second->tabs.begin();
-         tabs_iter != win_iter->second->tabs.end(); ++tabs_iter) {
+  for (auto& win_iter : session->windows) {
+    for (auto& tab : win_iter.second->tabs) {
+      // TODO(zea): replace with with the correct tab node id once there's a
+      // sync specific wrapper for SessionTab. This method is only used in
+      // tests though, so it's fine for now. crbug.com/662597
+      int tab_node_id = 0;
       sync_pb::EntitySpecifics entity;
-      sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-      specifics->mutable_tab()->MergeFrom((*tabs_iter)->ToSyncData());
-      specifics->set_session_tag(current_machine_tag_);
-
-      TabLinksMap::const_iterator tab_map_iter =
-          local_tab_map_.find((*tabs_iter)->tab_id.id());
-      DCHECK(tab_map_iter != local_tab_map_.end());
-      specifics->set_tab_node_id(tab_map_iter->second->tab_node_id());
+      entity.mutable_session()->CopyFrom(
+          SessionTabToSpecifics(*tab, current_machine_tag(), tab_node_id));
       syncer::SyncData data = syncer::SyncData::CreateLocalData(
-          TabNodePool::TabIdToTag(current_machine_tag_,
-                                  specifics->tab_node_id()),
+          TabNodeIdToTag(current_machine_tag(), tab_node_id),
           current_session_name_, entity);
       list.push_back(data);
     }
@@ -519,7 +522,7 @@
 }
 
 bool SessionsSyncManager::GetLocalSession(const SyncedSession** local_session) {
-  if (current_machine_tag_.empty())
+  if (current_machine_tag().empty())
     return false;
   *local_session = session_tracker_.GetSession(current_machine_tag());
   return true;
@@ -579,7 +582,7 @@
           LOG(WARNING) << "Dropping modification to local session.";
           return syncer::SyncError();
         }
-        UpdateTrackerWithForeignSession(
+        UpdateTrackerWithSpecifics(
             session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime());
         break;
       default:
@@ -601,7 +604,7 @@
     return syncer::SyncChange(
         FROM_HERE, SyncChange::ACTION_DELETE,
         SyncData::CreateLocalDelete(
-            TabNodePool::TabIdToTag(current_machine_tag(), tab.tab_node_id()),
+            TabNodeIdToTag(current_machine_tag(), tab.tab_node_id()),
             syncer::SESSIONS));
   }
 }
@@ -617,7 +620,6 @@
 
 bool SessionsSyncManager::InitFromSyncModel(
     const syncer::SyncDataList& sync_data,
-    syncer::SyncDataList* restored_tabs,
     syncer::SyncChangeList* new_changes) {
   bool found_current_header = false;
   int bad_foreign_hash_count = 0;
@@ -635,7 +637,7 @@
         new_changes->push_back(tombstone);
     } else if (specifics.session_tag() != current_machine_tag()) {
       if (TagHashFromSpecifics(specifics) == remote.GetClientTagHash()) {
-        UpdateTrackerWithForeignSession(specifics, remote.GetModifiedTime());
+        UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime());
       } else {
         // In the past, like years ago, we believe that some session data was
         // created with bad tag hashes. This causes any change this client makes
@@ -654,6 +656,10 @@
         found_current_header = true;
         if (specifics.header().has_client_name())
           current_session_name_ = specifics.header().client_name();
+
+        // TODO(zea): crbug.com/639009 update the tracker with the specifics
+        // from the header node as well. This will be necessary to preserve
+        // the set of open tabs when a custom tab is opened.
       } else {
         if (specifics.has_header() || !specifics.has_tab()) {
           LOG(WARNING) << "Found more than one session header node with local "
@@ -662,10 +668,13 @@
           if (tombstone.IsValid())
             new_changes->push_back(tombstone);
         } else {
-          // This is a valid old tab node, add it to the pool so it can be
-          // reused for reassociation.
-          local_tab_pool_.AddTabNode(specifics.tab_node_id());
-          restored_tabs->push_back(*it);
+          // This is a valid old tab node, add it to the tracker and associate
+          // it.
+          DVLOG(1) << "Associating local tab " << specifics.tab().tab_id()
+                   << " with node " << specifics.tab_node_id();
+          session_tracker_.ReassociateLocalTab(specifics.tab_node_id(),
+                                               specifics.tab().tab_id());
+          UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime());
         }
       }
     }
@@ -677,7 +686,7 @@
   session_tracker_.LookupAllForeignSessions(&sessions,
                                             SyncedSessionTracker::RAW);
   for (const auto* session : sessions) {
-    session_tracker_.CleanupSession(session->session_tag);
+    session_tracker_.CleanupForeignSession(session->session_tag);
   }
 
   UMA_HISTOGRAM_COUNTS_100("Sync.SessionsBadForeignHashOnMergeCount",
@@ -686,70 +695,83 @@
   return found_current_header;
 }
 
-void SessionsSyncManager::UpdateTrackerWithForeignSession(
+void SessionsSyncManager::UpdateTrackerWithSpecifics(
     const sync_pb::SessionSpecifics& specifics,
     const base::Time& modification_time) {
-  std::string foreign_session_tag = specifics.session_tag();
-  DCHECK_NE(foreign_session_tag, current_machine_tag());
-
-  SyncedSession* foreign_session =
-      session_tracker_.GetSession(foreign_session_tag);
+  std::string session_tag = specifics.session_tag();
+  SyncedSession* session = session_tracker_.GetSession(session_tag);
   if (specifics.has_header()) {
-    // Read in the header data for this foreign session. Header data is
+    // Read in the header data for this session. Header data is
     // essentially a collection of windows, each of which has an ordered id list
     // for their tabs.
 
     if (!IsValidSessionHeader(specifics.header())) {
-      LOG(WARNING) << "Ignoring foreign session node with invalid header "
-                   << "and tag " << foreign_session_tag << ".";
+      LOG(WARNING) << "Ignoring session node with invalid header "
+                   << "and tag " << session_tag << ".";
       return;
     }
 
     // Load (or create) the SyncedSession object for this client.
     const sync_pb::SessionHeader& header = specifics.header();
-    PopulateSessionHeaderFromSpecifics(header, modification_time,
-                                       foreign_session);
+    PopulateSessionHeaderFromSpecifics(header, modification_time, session);
 
     // Reset the tab/window tracking for this session (must do this before
     // we start calling PutWindowInSession and PutTabInWindow so that all
     // unused tabs/windows get cleared by the CleanupSession(...) call).
-    session_tracker_.ResetSessionTracking(foreign_session_tag);
+    session_tracker_.ResetSessionTracking(session_tag);
 
     // Process all the windows and their tab information.
     int num_windows = header.window_size();
-    DVLOG(1) << "Associating " << foreign_session_tag << " with " << num_windows
+    DVLOG(1) << "Populating " << session_tag << " with " << num_windows
              << " windows.";
 
     for (int i = 0; i < num_windows; ++i) {
       const sync_pb::SessionWindow& window_s = header.window(i);
       SessionID::id_type window_id = window_s.window_id();
-      session_tracker_.PutWindowInSession(foreign_session_tag, window_id);
-      BuildSyncedSessionFromSpecifics(
-          foreign_session_tag, window_s, modification_time,
-          foreign_session->windows[window_id].get());
+      session_tracker_.PutWindowInSession(session_tag, window_id);
+      BuildSyncedSessionFromSpecifics(session_tag, window_s, modification_time,
+                                      session->windows[window_id].get());
     }
     // Delete any closed windows and unused tabs as necessary.
-    session_tracker_.CleanupSession(foreign_session_tag);
+    session_tracker_.CleanupForeignSession(session_tag);
   } else if (specifics.has_tab()) {
     const sync_pb::SessionTab& tab_s = specifics.tab();
     SessionID::id_type tab_id = tab_s.tab_id();
+    DVLOG(1) << "Populating " << session_tag << "'s tab id " << tab_id
+             << " from node " << specifics.tab_node_id();
 
-    const sessions::SessionTab* existing_tab;
-    if (session_tracker_.LookupSessionTab(foreign_session_tag, tab_id,
-                                          &existing_tab) &&
-        existing_tab->timestamp > modification_time) {
-      // Force the tracker to remember this tab node id, even if it isn't
-      // currently being used.
-      session_tracker_.GetTab(foreign_session_tag, tab_id,
-                              specifics.tab_node_id());
-      DVLOG(1) << "Ignoring " << foreign_session_tag << "'s session tab "
-               << tab_id << " with earlier modification time";
+    // Ensure the tracker is aware of the tab node id. Deleting foreign sessions
+    // requires deleting all relevant tab nodes, and it's easier to track the
+    // tab node ids themselves separately from the tab ids.
+    //
+    // Note that TabIDs are not stable across restarts of a client. Consider
+    // this example with two tabs:
+    //
+    // http://a.com  TabID1 --> NodeIDA
+    // http://b.com  TabID2 --> NodeIDB
+    //
+    // After restart, tab ids are reallocated. e.g, one possibility:
+    // http://a.com TabID2 --> NodeIDA
+    // http://b.com TabID1 --> NodeIDB
+    //
+    // If that happened on a remote client, here we will see an update to
+    // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
+    // with tab_node_id changing from NodeIDB to NodeIDA.
+    //
+    // We can also wind up here if we created this tab as an out-of-order
+    // update to the header node for this session before actually associating
+    // the tab itself, so the tab node id wasn't available at the time and
+    // is currently kInvalidTabNodeID.
+    //
+    // In both cases, we can safely throw it into the set of node ids.
+    session_tracker_.OnTabNodeSeen(session_tag, specifics.tab_node_id());
+    sessions::SessionTab* tab = session_tracker_.GetTab(session_tag, tab_id);
+    if (tab->timestamp > modification_time) {
+      DVLOG(1) << "Ignoring " << session_tag << "'s session tab " << tab_id
+               << " with earlier modification time";
       return;
     }
 
-    sessions::SessionTab* tab = session_tracker_.GetTab(
-        foreign_session_tag, tab_id, specifics.tab_node_id());
-
     // Update SessionTab based on protobuf.
     tab->SetFromSyncData(tab_s, modification_time);
 
@@ -758,11 +780,11 @@
     RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time);
 
     // Update the last modified time.
-    if (foreign_session->modified_time < modification_time)
-      foreign_session->modified_time = modification_time;
+    if (session->modified_time < modification_time)
+      session->modified_time = modification_time;
   } else {
-    LOG(WARNING) << "Ignoring foreign session node with missing header/tab "
-                 << "fields and tag " << foreign_session_tag << ".";
+    LOG(WARNING) << "Ignoring session node with missing header/tab "
+                 << "fields and tag " << session_tag << ".";
   }
 }
 
@@ -780,8 +802,6 @@
     DVLOG(1) << "Creating session sync guid: " << current_machine_tag_;
     sync_prefs_->SetSyncSessionsGUID(current_machine_tag_);
   }
-
-  local_tab_pool_.SetMachineTag(current_machine_tag_);
 }
 
 // static
@@ -891,7 +911,7 @@
   }
 
   std::set<int> tab_node_ids_to_delete;
-  session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete);
+  session_tracker_.LookupForeignTabNodeIds(tag, &tab_node_ids_to_delete);
   if (DisassociateForeignSession(tag)) {
     // Only tell sync to delete the header if there was one.
     change_output->push_back(
@@ -900,10 +920,10 @@
   }
   for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin();
        it != tab_node_ids_to_delete.end(); ++it) {
-    change_output->push_back(syncer::SyncChange(
-        FROM_HERE, SyncChange::ACTION_DELETE,
-        SyncData::CreateLocalDelete(TabNodePool::TabIdToTag(tag, *it),
-                                    syncer::SESSIONS)));
+    change_output->push_back(
+        syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE,
+                           SyncData::CreateLocalDelete(TabNodeIdToTag(tag, *it),
+                                                       syncer::SESSIONS)));
   }
   if (!sessions_updated_callback_.is_null())
     sessions_updated_callback_.Run();
@@ -913,7 +933,7 @@
     const std::string& foreign_session_tag) {
   DCHECK_NE(foreign_session_tag, current_machine_tag());
   DVLOG(1) << "Disassociating session " << foreign_session_tag;
-  return session_tracker_.DeleteSession(foreign_session_tag);
+  return session_tracker_.DeleteForeignSession(foreign_session_tag);
 }
 
 bool SessionsSyncManager::GetForeignSession(
@@ -959,66 +979,32 @@
   return success;
 }
 
-void SessionsSyncManager::LocalTabDelegateToSpecifics(
-    const SyncedTabDelegate& tab_delegate,
-    sync_pb::SessionSpecifics* specifics) {
-  sessions::SessionTab* session_tab = nullptr;
-  session_tab = session_tracker_.GetTab(current_machine_tag(),
-                                        tab_delegate.GetSessionId(),
-                                        tab_delegate.GetSyncId());
-  SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab);
-  SetVariationIds(session_tab);
-  sync_pb::SessionTab tab_s = session_tab->ToSyncData();
-  specifics->set_session_tag(current_machine_tag_);
-  specifics->set_tab_node_id(tab_delegate.GetSyncId());
-  specifics->mutable_tab()->CopyFrom(tab_s);
-}
-
 void SessionsSyncManager::AssociateRestoredPlaceholderTab(
     const SyncedTabDelegate& tab_delegate,
     SessionID::id_type new_tab_id,
     SessionID::id_type new_window_id,
-    const syncer::SyncDataList& restored_tabs,
     syncer::SyncChangeList* change_output) {
   DCHECK_NE(tab_delegate.GetSyncId(), TabNodePool::kInvalidTabNodeID);
-  // Rewrite the tab using |restored_tabs| to retrieve the specifics.
-  if (restored_tabs.empty()) {
-    DLOG(WARNING) << "Can't Update tab ID.";
-    return;
-  }
 
-  for (syncer::SyncDataList::const_iterator it = restored_tabs.begin();
-       it != restored_tabs.end(); ++it) {
-    if (it->GetSpecifics().session().tab_node_id() !=
-        tab_delegate.GetSyncId()) {
-      continue;
-    }
+  // Update tracker with the new association (and inform it of the tab node
+  // in the process).
+  session_tracker_.ReassociateLocalTab(tab_delegate.GetSyncId(), new_tab_id);
 
-    sync_pb::EntitySpecifics entity;
-    sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-    specifics->CopyFrom(it->GetSpecifics().session());
-    DCHECK(specifics->has_tab());
+  // Update the window id on the SessionTab itself.
+  sessions::SessionTab* local_tab =
+      session_tracker_.GetTab(current_machine_tag(), new_tab_id);
+  local_tab->window_id.set_id(new_window_id);
 
-    // Update tab node pool with the new association.
-    local_tab_pool_.ReassociateTabNode(tab_delegate.GetSyncId(), new_tab_id);
-    TabLink* tab_link = new TabLink(tab_delegate.GetSyncId(), &tab_delegate);
-    local_tab_map_[new_tab_id] = make_linked_ptr<TabLink>(tab_link);
-
-    if (specifics->tab().tab_id() == new_tab_id &&
-        specifics->tab().window_id() == new_window_id)
-      return;
-
-    // Either the tab_id or window_id changed (e.g due to session restore), so
-    // update the sync node.
-    specifics->mutable_tab()->set_tab_id(new_tab_id);
-    specifics->mutable_tab()->set_window_id(new_window_id);
-    syncer::SyncData data = syncer::SyncData::CreateLocalData(
-        TabNodePool::TabIdToTag(current_machine_tag_, specifics->tab_node_id()),
-        current_session_name_, entity);
-    change_output->push_back(
-        syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
-    return;
-  }
+  // Rewrite the specifics based on the reassociated SessionTab to preserve
+  // the new tab and window ids.
+  sync_pb::EntitySpecifics entity;
+  entity.mutable_session()->CopyFrom(SessionTabToSpecifics(
+      *local_tab, current_machine_tag(), tab_delegate.GetSyncId()));
+  syncer::SyncData data = syncer::SyncData::CreateLocalData(
+      TabNodeIdToTag(current_machine_tag(), tab_delegate.GetSyncId()),
+      current_session_name_, entity);
+  change_output->push_back(
+      syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
 }
 
 // static
diff --git a/components/sync_sessions/sessions_sync_manager.h b/components/sync_sessions/sessions_sync_manager.h
index 5f2b5981..1f6af39 100644
--- a/components/sync_sessions/sessions_sync_manager.h
+++ b/components/sync_sessions/sessions_sync_manager.h
@@ -30,7 +30,6 @@
 #include "components/sync_sessions/revisit/page_revisit_broadcaster.h"
 #include "components/sync_sessions/synced_session.h"
 #include "components/sync_sessions/synced_session_tracker.h"
-#include "components/sync_sessions/tab_node_pool.h"
 
 namespace syncer {
 class LocalDeviceInfoProvider;
@@ -118,37 +117,6 @@
   void DoGarbageCollection();
 
  private:
-  // Keep all the links to local tab data in one place. A tab_node_id and tab
-  // must be passed at creation. The tab_node_id is not mutable, although
-  // all other fields are.
-  class TabLink {
-   public:
-    TabLink(int tab_node_id, const SyncedTabDelegate* tab)
-        : tab_node_id_(tab_node_id), tab_(tab) {}
-
-    void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; }
-    void set_url(const GURL& url) { url_ = url; }
-
-    int tab_node_id() const { return tab_node_id_; }
-    const SyncedTabDelegate* tab() const { return tab_; }
-    const GURL& url() const { return url_; }
-
-   private:
-    // The id for the sync node this tab is stored in.
-    const int tab_node_id_;
-
-    // The tab object itself.
-    const SyncedTabDelegate* tab_;
-
-    // The currently visible url of the tab (used for syncing favicons).
-    GURL url_;
-
-    DISALLOW_COPY_AND_ASSIGN(TabLink);
-  };
-
-  // Container for accessing local tab data by tab id.
-  typedef std::map<SessionID::id_type, linked_ptr<TabLink>> TabLinksMap;
-
   friend class extensions::ExtensionSessionsTest;
   friend class SessionsSyncManagerTest;
   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, PopulateSessionHeader);
@@ -182,20 +150,16 @@
 
   void InitializeCurrentMachineTag(const std::string& cache_guid);
 
-  // Load and add window or tab data for a foreign session to our internal
+  // Load and add window or tab data from synced specifics to our internal
   // tracking.
-  void UpdateTrackerWithForeignSession(
-      const sync_pb::SessionSpecifics& specifics,
-      const base::Time& modification_time);
+  void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
+                                  const base::Time& modification_time);
 
   // Returns true if |sync_data| contained a header node for the current
-  // machine, false otherwise. |restored_tabs| is a filtered tab-only
-  // subset of |sync_data| returned by this function for convenience.
-  // |new_changes| is a link to the SyncChange pipeline that exists in the
-  // caller's context. This function will append necessary changes for
-  // processing later.
+  // machine, false otherwise. |new_changes| is a link to the SyncChange
+  // pipeline that exists in the caller's context. This function will append
+  // necessary changes for processing later.
   bool InitFromSyncModel(const syncer::SyncDataList& sync_data,
-                         syncer::SyncDataList* restored_tabs,
                          syncer::SyncChangeList* new_changes);
 
   // Helper to construct a deletion SyncChange for a *tab node*.
@@ -244,11 +208,6 @@
   // RELOAD_TABS will additionally cause a resync of all tabs (same as calling
   // AssociateTabs with a vector of all tabs).
   //
-  // |restored_tabs| is a filtered tab-only subset of initial sync data, if
-  // available (during MergeDataAndStartSyncing). It can be used to obtain
-  // baseline SessionSpecifics for tabs we can't fully associate any other
-  // way because they don't yet have a WebContents.
-  //
   // Returns: false if the local session's sync nodes were deleted and
   // reassociation is necessary, true otherwise.
   //
@@ -257,7 +216,6 @@
   // changes for processing later.
   enum ReloadTabsOption { RELOAD_TABS, DONT_RELOAD_TABS };
   void AssociateWindows(ReloadTabsOption option,
-                        const syncer::SyncDataList& restored_tabs,
                         syncer::SyncChangeList* change_output);
 
   // Loads and reassociates the local tabs referenced in |tabs|.
@@ -286,17 +244,10 @@
   // as they may have changed after a session was restored.  This method
   // compares new_tab_id and new_window_id against the previously persisted tab
   // ID and window ID (from our TabNodePool) and updates them if either differs.
-  // |restored_tabs| is a filtered tab-only subset of initial sync data, if
-  // available (during MergeDataAndStartSyncing). It can be used to obtain
-  // baseline SessionSpecifics for tabs we can't fully associate any other
-  // way because they don't yet have a WebContents.
-  // TODO(tim): Bug 98892. We should be able to test this for this on android
-  // even though we didn't have tests for old API-based sessions sync.
   void AssociateRestoredPlaceholderTab(
       const SyncedTabDelegate& tab_delegate,
       SessionID::id_type new_tab_id,
       SessionID::id_type new_window_id,
-      const syncer::SyncDataList& restored_tabs,
       syncer::SyncChangeList* change_output);
 
   // Stops and re-starts syncing to rebuild association mappings. Returns true
@@ -323,15 +274,9 @@
   // The client of this sync sessions datatype.
   SyncSessionsClient* const sessions_client_;
 
-  // Mapping of current open (local) tabs to their sync identifiers.
-  TabLinksMap local_tab_map_;
-
   SyncedSessionTracker session_tracker_;
   FaviconCache favicon_cache_;
 
-  // Pool of used/available sync nodes associated with local tabs.
-  TabNodePool local_tab_pool_;
-
   // Tracks whether our local representation of which sync nodes map to what
   // tabs (belonging to the current local session) is inconsistent.  This can
   // happen if a foreign client deems our session as "stale" and decides to
diff --git a/components/sync_sessions/synced_session_tracker.cc b/components/sync_sessions/synced_session_tracker.cc
index a74b877c..e797295b 100644
--- a/components/sync_sessions/synced_session_tracker.cc
+++ b/components/sync_sessions/synced_session_tracker.cc
@@ -53,6 +53,8 @@
 
 void SyncedSessionTracker::SetLocalSessionTag(
     const std::string& local_session_tag) {
+  DCHECK(local_session_tag_.empty());
+  DCHECK(!local_session_tag.empty());
   local_session_tag_ = local_session_tag;
 }
 
@@ -107,8 +109,9 @@
   return true;
 }
 
-void SyncedSessionTracker::LookupTabNodeIds(const std::string& session_tag,
-                                            std::set<int>* tab_node_ids) {
+void SyncedSessionTracker::LookupForeignTabNodeIds(
+    const std::string& session_tag,
+    std::set<int>* tab_node_ids) const {
   tab_node_ids->clear();
   auto session_iter = synced_session_map_.find(session_tag);
   if (session_iter != synced_session_map_.end()) {
@@ -144,7 +147,9 @@
   return synced_session_map_[session_tag].get();
 }
 
-bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
+bool SyncedSessionTracker::DeleteForeignSession(
+    const std::string& session_tag) {
+  DCHECK_NE(local_session_tag_, session_tag);
   unmapped_windows_.erase(session_tag);
   unmapped_tabs_.erase(session_tag);
 
@@ -188,13 +193,14 @@
 
 void SyncedSessionTracker::DeleteForeignTab(const std::string& session_tag,
                                             int tab_node_id) {
+  DCHECK_NE(local_session_tag_, session_tag);
   auto session_iter = synced_session_map_.find(session_tag);
   if (session_iter != synced_session_map_.end()) {
     session_iter->second->tab_node_ids.erase(tab_node_id);
   }
 }
 
-void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
+void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
   for (const auto& window_pair : unmapped_windows_[session_tag])
     synced_window_map_[session_tag].erase(window_pair.first);
   unmapped_windows_[session_tag].clear();
@@ -236,7 +242,7 @@
                                           SessionID::id_type tab_id,
                                           size_t tab_index) {
   // We're called here for two reasons. 1) We've received an update to the
-  // SessionWindow information of a SessionHeader node for a foreign session,
+  // SessionWindow information of a SessionHeader node for a session,
   // and 2) The SessionHeader node for our local session changed. In both cases
   // we need to update our tracking state to reflect the change.
   //
@@ -246,7 +252,7 @@
   // We know that we will eventually process (via GetTab) every single tab node
   // in the system, so we permit ourselves to use kInvalidTabNodeID here and
   // rely on the later update to build the mapping (or a restart).
-  GetTabImpl(session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
+  GetTab(session_tag, tab_id);
 
   // The tab should be unmapped.
   std::unique_ptr<sessions::SessionTab> tab;
@@ -269,46 +275,18 @@
   window_tabs[tab_index] = std::move(tab);
 }
 
-sessions::SessionTab* SyncedSessionTracker::GetTab(
-    const std::string& session_tag,
-    SessionID::id_type tab_id,
-    int tab_node_id) {
-  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
-  return GetTabImpl(session_tag, tab_id, tab_node_id);
+void SyncedSessionTracker::OnTabNodeSeen(const std::string& session_tag,
+                                         int tab_node_id) {
+  GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
 }
 
-sessions::SessionTab* SyncedSessionTracker::GetTabImpl(
+sessions::SessionTab* SyncedSessionTracker::GetTab(
     const std::string& session_tag,
-    SessionID::id_type tab_id,
-    int tab_node_id) {
+    SessionID::id_type tab_id) {
   sessions::SessionTab* tab_ptr = nullptr;
   auto iter = synced_tab_map_[session_tag].find(tab_id);
   if (iter != synced_tab_map_[session_tag].end()) {
     tab_ptr = iter->second;
-    if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
-        tab_id != TabNodePool::kInvalidTabID) {
-      // TabIDs are not stable across restarts of a client. Consider this
-      // example with two tabs:
-      //
-      // http://a.com  TabID1 --> NodeIDA
-      // http://b.com  TabID2 --> NodeIDB
-      //
-      // After restart, tab ids are reallocated. e.g, one possibility:
-      // http://a.com TabID2 --> NodeIDA
-      // http://b.com TabID1 --> NodeIDB
-      //
-      // If that happend on a remote client, here we will see an update to
-      // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
-      // with tab_node_id changing from NodeIDB to NodeIDA.
-      //
-      // We can also wind up here if we created this tab as an out-of-order
-      // update to the header node for this session before actually associating
-      // the tab itself, so the tab node id wasn't available at the time and
-      // is currently kInvalidTabNodeID.
-      //
-      // In both cases, we can safely throw it into the set of node ids.
-      GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
-    }
 
     if (VLOG_IS_ON(1)) {
       std::string title;
@@ -328,7 +306,6 @@
     tab->tab_id.set_id(tab_id);
     synced_tab_map_[session_tag][tab_id] = tab_ptr;
     unmapped_tabs_[session_tag][tab_id] = std::move(tab);
-    GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
     DVLOG(1) << "Getting "
              << (session_tag == local_session_tag_ ? "local session"
                                                    : session_tag)
@@ -339,6 +316,90 @@
   return tab_ptr;
 }
 
+void SyncedSessionTracker::CleanupForeignSession(
+    const std::string& session_tag) {
+  DCHECK_NE(local_session_tag_, session_tag);
+  CleanupSessionImpl(session_tag);
+}
+
+void SyncedSessionTracker::CleanupLocalTabs(std::set<int>* deleted_node_ids) {
+  DCHECK(!local_session_tag_.empty());
+  for (const auto& tab_pair : unmapped_tabs_[local_session_tag_])
+    local_tab_pool_.FreeTab(tab_pair.first);
+  CleanupSessionImpl(local_session_tag_);
+  local_tab_pool_.CleanupTabNodes(deleted_node_ids);
+  for (int tab_node_id : *deleted_node_ids) {
+    GetSession(local_session_tag_)->tab_node_ids.erase(tab_node_id);
+  }
+}
+
+bool SyncedSessionTracker::GetTabNodeForLocalTab(int tab_id, int* tab_node_id) {
+  DCHECK(!local_session_tag_.empty());
+  // Ensure a placeholder SessionTab is in place, if not already.
+  // Although we don't need a SessionTab to fulfill this request, this forces
+  // the
+  // creation of one if it doesn't already exist. This helps to make sure we're
+  // tracking this |tab_id| if |local_tab_pool_| is, and everyone's data
+  // structures
+  // are kept in sync and as consistent as possible.
+  GetTab(local_session_tag_, tab_id);  // Ignore result.
+
+  bool reused_existing_tab =
+      local_tab_pool_.GetTabNodeForTab(tab_id, tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, *tab_node_id);
+  GetSession(local_session_tag_)->tab_node_ids.insert(*tab_node_id);
+  return reused_existing_tab;
+}
+
+void SyncedSessionTracker::ReassociateLocalTab(int tab_node_id,
+                                               SessionID::id_type new_tab_id) {
+  DCHECK(!local_session_tag_.empty());
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabID, new_tab_id);
+
+  SessionID::id_type old_tab_id =
+      local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id);
+  local_tab_pool_.ReassociateTabNode(tab_node_id, new_tab_id);
+
+  sessions::SessionTab* tab_ptr = nullptr;
+
+  auto old_tab_iter = synced_tab_map_[local_session_tag_].find(old_tab_id);
+  if (old_tab_iter != synced_tab_map_[local_session_tag_].end()) {
+    tab_ptr = old_tab_iter->second;
+    // Remove the tab from the synced tab map under the old id.
+    synced_tab_map_[local_session_tag_].erase(old_tab_iter);
+  } else {
+    // It's possible a placeholder is already in place for the new tab. If so,
+    // reuse it, otherwise create a new one (which will default to unmapped).
+    tab_ptr = GetTab(local_session_tag_, new_tab_id);
+  }
+
+  // If the old tab is unmapped, update the tab id under which it is indexed.
+  auto unmapped_tabs_iter = unmapped_tabs_[local_session_tag_].find(old_tab_id);
+  if (old_tab_id != TabNodePool::kInvalidTabID &&
+      unmapped_tabs_iter != unmapped_tabs_[local_session_tag_].end()) {
+    std::unique_ptr<sessions::SessionTab> tab =
+        std::move(unmapped_tabs_iter->second);
+    DCHECK_EQ(tab_ptr, tab.get());
+    unmapped_tabs_[local_session_tag_].erase(unmapped_tabs_iter);
+    unmapped_tabs_[local_session_tag_][new_tab_id] = std::move(tab);
+  }
+
+  // Update the tab id.
+  if (old_tab_id != TabNodePool::kInvalidTabID) {
+    DVLOG(1) << "Remapped tab " << old_tab_id << " with node " << tab_node_id
+             << " to tab " << new_tab_id;
+  } else {
+    DVLOG(1) << "Mapped new tab node " << tab_node_id << " to tab "
+             << new_tab_id;
+  }
+  tab_ptr->tab_id.set_id(new_tab_id);
+
+  // Add the tab back into the tab map with the new id.
+  synced_tab_map_[local_session_tag_][new_tab_id] = tab_ptr;
+  GetSession(local_session_tag_)->tab_node_ids.insert(tab_node_id);
+}
+
 void SyncedSessionTracker::Clear() {
   // Cleanup unmapped tabs and windows.
   unmapped_windows_.clear();
@@ -352,6 +413,7 @@
   synced_window_map_.clear();
   synced_tab_map_.clear();
 
+  local_tab_pool_.Clear();
   local_session_tag_.clear();
 }
 
diff --git a/components/sync_sessions/synced_session_tracker.h b/components/sync_sessions/synced_session_tracker.h
index 35123d7..e27ba21 100644
--- a/components/sync_sessions/synced_session_tracker.h
+++ b/components/sync_sessions/synced_session_tracker.h
@@ -40,8 +40,7 @@
   explicit SyncedSessionTracker(SyncSessionsClient* sessions_client);
   ~SyncedSessionTracker();
 
-  // We track and distinguish the local session from foreign sessions.
-  void SetLocalSessionTag(const std::string& local_session_tag);
+  // **** Synced session/tab query methods. ****
 
   // Fill a preallocated vector with all foreign sessions we're tracking (skips
   // the local session object). SyncedSession ownership remains within the
@@ -51,6 +50,11 @@
   bool LookupAllForeignSessions(std::vector<const SyncedSession*>* sessions,
                                 SessionLookup lookup) const;
 
+  // Fills |tab_node_ids| with the tab node ids (see GetTab) for all the tabs*
+  // associated with the session having tag |session_tag|.
+  void LookupForeignTabNodeIds(const std::string& session_tag,
+                               std::set<int>* tab_node_ids) const;
+
   // Attempts to look up the session windows associatd with the session given
   // by |session_tag|. Ownership of SessionWindows stays within the
   // SyncedSessionTracker.
@@ -76,15 +80,13 @@
   // this won't create-if-not-present.
   bool LookupLocalSession(const SyncedSession** output) const;
 
+  // **** Methods for manipulating synced sessions and tabs. ****
+
   // Returns a pointer to the SyncedSession object associated with
   // |session_tag|. If none exists, creates one. Ownership of the
   // SyncedSession remains within the SyncedSessionTracker.
   SyncedSession* GetSession(const std::string& session_tag);
 
-  // Deletes the session associated with |session_tag| if it exists.
-  // Returns true if the session existed and was deleted, false otherwise.
-  bool DeleteSession(const std::string& session_tag);
-
   // Resets the tracking information for the session specified by |session_tag|.
   // This involves clearing all the windows and tabs from the session, while
   // keeping pointers saved in the synced_window_map_ and synced_tab_map_. Once
@@ -94,19 +96,6 @@
   // tabs not owned.
   void ResetSessionTracking(const std::string& session_tag);
 
-  // Tracks the deletion of a foreign tab by removing the given |tab_node_id|
-  // from the parent session. Doesn't actually remove any tab objects because
-  // the header may have or may not have already been updated to no longer
-  // parent this tab. Regardless, when the header is updated then cleanup will
-  // remove the actual tab data. However, this method always needs to be called
-  // upon foreign tab deletion, otherwise LookupTabNodeIds(...) may return
-  // already deleted tab node ids.
-  void DeleteForeignTab(const std::string& session_tag, int tab_node_id);
-
-  // Deletes those windows and tabs associated with |session_tag| that are no
-  // longer owned. See ResetSessionTracking(...).
-  void CleanupSession(const std::string& session_tag);
-
   // Adds the window with id |window_id| to the session specified by
   // |session_tag|. If none existed for that session, creates one. Similarly, if
   // the session did not exist yet, creates it. Ownership of the SessionWindow
@@ -125,18 +114,64 @@
                       SessionID::id_type tab_id,
                       size_t tab_index);
 
-  // Returns a pointer to the SessionTab object associated with |tab_id| for
-  // the session specified with |session_tag|. If none exists, creates one.
-  // Ownership of the SessionTab remains within the SyncedSessionTracker.
-  // |tab_node_id| must be a valid node id for the node backing this tab.
-  sessions::SessionTab* GetTab(const std::string& session_tag,
-                               SessionID::id_type tab_id,
-                               int tab_node_id);
+  // Adds |tab_node_id| to the session specified by |session_tag|, creating that
+  // session if necessary. This is necessary to ensure that each session has an
+  // up to date list of tab nodes linked to it for session deletion purposes.
+  // Note that this won't update the local tab pool, even if the local session
+  // tag is passed. The tab pool is only updated with new tab nodes when they're
+  // associated with a tab id (see ReassociateLocalTabNode or GetTabNodeForTab).
+  void OnTabNodeSeen(const std::string& session_tag, int tab_node_id);
 
-  // Fills |tab_node_ids| with the tab node ids (see GetTab) for all the tabs*
-  // associated with the session having tag |session_tag|.
-  void LookupTabNodeIds(const std::string& session_tag,
-                        std::set<int>* tab_node_ids);
+  // Returns a pointer to the SessionTab object associated with
+  // |tab_id| for the session specified with |session_tag|.
+  // Note: Ownership of the SessionTab remains within the SyncedSessionTracker.
+  // TODO(zea): Replace SessionTab with a Sync specific wrapper.
+  // crbug.com/662597
+  sessions::SessionTab* GetTab(const std::string& session_tag,
+                               SessionID::id_type tab_id);
+
+  // **** Methods specific to foreign sessions. ****
+
+  // Tracks the deletion of a foreign tab by removing the given |tab_node_id|
+  // from the parent session. Doesn't actually remove any tab objects because
+  // the header may have or may not have already been updated to no longer
+  // parent this tab. Regardless, when the header is updated then cleanup will
+  // remove the actual tab data. However, this method always needs to be called
+  // upon foreign tab deletion, otherwise LookupTabNodeIds(...) may return
+  // already deleted tab node ids.
+  void DeleteForeignTab(const std::string& session_tag, int tab_node_id);
+
+  // Deletes the session associated with |session_tag| if it exists.
+  // Returns true if the session existed and was deleted, false otherwise.
+  bool DeleteForeignSession(const std::string& session_tag);
+
+  // Deletes those windows and tabs associated with |session_tag| that are no
+  // longer owned. See ResetSessionTracking(...)..
+  void CleanupForeignSession(const std::string& session_tag);
+
+  // **** Methods specific to the local session. ****
+
+  // Set the local session tag. Must be called before any other local session
+  // methods are invoked.
+  void SetLocalSessionTag(const std::string& local_session_tag);
+
+  // Similar to CleanupForeignSession, but also marks any unmapped tabs as free
+  // in the tab node pool and fills |deleted_node_ids| with the set of locally
+  // free tab nodes to be deleted.
+  void CleanupLocalTabs(std::set<int>* deleted_node_ids);
+
+  // Fills |tab_node_id| with a tab node for |tab_id|. Returns true if an
+  // existing tab node was found, false if there was none and one had to be
+  // created.
+  bool GetTabNodeForLocalTab(int tab_id, int* tab_node_id);
+
+  // Reassociates the tab denoted by |tab_node_id| with a new tab id, preserving
+  // any previous SessionTab object the node was associated with. This is useful
+  // on restart when sync needs to reassociate tabs from a previous session with
+  // newly restored tabs (and can be used in conjunction with PutTabInWindow).
+  void ReassociateLocalTab(int tab_node_id, SessionID::id_type new_tab_id);
+
+  // **** Methods for querying/manipulating overall state ****.
 
   // Free the memory for all dynamically allocated objects and clear the
   // tracking structures.
@@ -160,20 +195,21 @@
   }
 
  private:
-  // Implementation for GetTab(...) above, permits invalid tab_node_id.
-  sessions::SessionTab* GetTabImpl(const std::string& session_tag,
-                                   SessionID::id_type tab_id,
-                                   int tab_node_id);
+  friend class SessionsSyncManagerTest;
+  friend class SyncedSessionTrackerTest;
+
+  // Implementation of CleanupForeignSession/CleanupLocalTabs.
+  void CleanupSessionImpl(const std::string& session_tag);
 
   // The client of the sync sessions datatype.
   SyncSessionsClient* const sessions_client_;
 
-  // The mapping of tab/window ids to their SessionTab/SessionWindow objects.
+  // The mapping of tab/window to their SessionTab/SessionWindow objects.
   // The SessionTab/SessionWindow objects referred to may be owned either by the
   // session in the |synced_session_map_| or be temporarily unmapped and live in
   // the |unmapped_tabs_|/|unmapped_windows_| collections.
   //
-  // Map: session tag -> (tab/window id -> SessionTab*/SessionWindow*)
+  // Map: session tag -> (tab/window -> SessionTab*/SessionWindow*)
   std::map<std::string, std::map<SessionID::id_type, sessions::SessionTab*>>
       synced_tab_map_;
   std::map<std::string, std::map<SessionID::id_type, sessions::SessionWindow*>>
@@ -203,6 +239,9 @@
   // sessions.
   std::string local_session_tag_;
 
+  // Pool of used/available sync nodes associated with local tabs.
+  TabNodePool local_tab_pool_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncedSessionTracker);
 };
 
diff --git a/components/sync_sessions/synced_session_tracker_unittest.cc b/components/sync_sessions/synced_session_tracker_unittest.cc
index 68c1f471..282baf7 100644
--- a/components/sync_sessions/synced_session_tracker_unittest.cc
+++ b/components/sync_sessions/synced_session_tracker_unittest.cc
@@ -17,6 +17,10 @@
 
 const char kValidUrl[] = "http://www.example.com";
 const char kInvalidUrl[] = "invalid.url";
+const char kTag[] = "tag";
+const char kTag2[] = "tag2";
+const char kTag3[] = "tag3";
+const char kTitle[] = "title";
 
 }  // namespace
 
@@ -26,6 +30,7 @@
   ~SyncedSessionTrackerTest() override {}
 
   SyncedSessionTracker* GetTracker() { return &tracker_; }
+  TabNodePool* GetTabNodePool() { return &tracker_.local_tab_pool_; }
 
  private:
   FakeSyncSessionsClient sessions_client_;
@@ -33,34 +38,34 @@
 };
 
 TEST_F(SyncedSessionTrackerTest, GetSession) {
-  SyncedSession* session1 = GetTracker()->GetSession("tag");
-  SyncedSession* session2 = GetTracker()->GetSession("tag2");
-  ASSERT_EQ(session1, GetTracker()->GetSession("tag"));
+  SyncedSession* session1 = GetTracker()->GetSession(kTag);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  ASSERT_EQ(session1, GetTracker()->GetSession(kTag));
   ASSERT_NE(session1, session2);
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) {
-  sessions::SessionTab* tab = GetTracker()->GetTab("tag", 0, 0);
-  ASSERT_EQ(tab, GetTracker()->GetTab("tag", 0, 0));
+  sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 0);
+  ASSERT_EQ(tab, GetTracker()->GetTab(kTag, 0));
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, PutWindowInSession) {
-  GetTracker()->PutWindowInSession("tag", 0);
-  SyncedSession* session = GetTracker()->GetSession("tag");
+  GetTracker()->PutWindowInSession(kTag, 0);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
   ASSERT_EQ(1U, session->windows.size());
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, PutTabInWindow) {
-  GetTracker()->PutWindowInSession("tag", 10);
-  GetTracker()->PutTabInWindow("tag", 10, 15,
+  GetTracker()->PutWindowInSession(kTag, 10);
+  GetTracker()->PutTabInWindow(kTag, 10, 15,
                                0);  // win id 10, tab id 15, tab ind 0.
-  SyncedSession* session = GetTracker()->GetSession("tag");
+  SyncedSession* session = GetTracker()->GetSession(kTag);
   ASSERT_EQ(1U, session->windows.size());
   ASSERT_EQ(1U, session->windows[10]->tabs.size());
-  ASSERT_EQ(GetTracker()->GetTab("tag", 15, 1),
+  ASSERT_EQ(GetTracker()->GetTab(kTag, 15),
             session->windows[10]->tabs[0].get());
   // Should clean up memory on its own.
 }
@@ -69,28 +74,28 @@
   std::vector<const SyncedSession*> sessions;
   ASSERT_FALSE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::PRESENTABLE));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutTabInWindow("tag1", 0, 15, 0);
-  sessions::SessionTab* tab = GetTracker()->GetTab("tag1", 15, 1);
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 15, 0);
+  sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 15);
   ASSERT_TRUE(tab);
   tab->navigations.push_back(
       sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl,
-                                                                      "title"));
-  GetTracker()->GetSession("tag2");
-  GetTracker()->GetSession("tag3");
-  GetTracker()->PutWindowInSession("tag3", 0);
-  GetTracker()->PutTabInWindow("tag3", 0, 15, 0);
-  tab = GetTracker()->GetTab("tag3", 15, 1);
+                                                                      kTitle));
+  GetTracker()->GetSession(kTag2);
+  GetTracker()->GetSession(kTag3);
+  GetTracker()->PutWindowInSession(kTag3, 0);
+  GetTracker()->PutTabInWindow(kTag3, 0, 15, 0);
+  tab = GetTracker()->GetTab(kTag3, 15);
   ASSERT_TRUE(tab);
   tab->navigations.push_back(
       sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
-          kInvalidUrl, "title"));
+          kInvalidUrl, kTitle));
   ASSERT_TRUE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::PRESENTABLE));
   // Only the session with a valid window and tab gets returned.
   ASSERT_EQ(1U, sessions.size());
-  ASSERT_EQ("tag1", sessions[0]->session_tag);
+  ASSERT_EQ(kTag, sessions[0]->session_tag);
 
   ASSERT_TRUE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::RAW));
@@ -99,15 +104,15 @@
 
 TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) {
   std::vector<const sessions::SessionWindow*> windows;
-  ASSERT_FALSE(GetTracker()->LookupSessionWindows("tag1", &windows));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutWindowInSession("tag1", 2);
-  GetTracker()->GetSession("tag2");
-  GetTracker()->PutWindowInSession("tag2", 0);
-  GetTracker()->PutWindowInSession("tag2", 2);
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows("tag1", &windows));
-  ASSERT_EQ(2U, windows.size());  // Only windows from tag1 session.
+  ASSERT_FALSE(GetTracker()->LookupSessionWindows(kTag, &windows));
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutWindowInSession(kTag, 2);
+  GetTracker()->GetSession(kTag2);
+  GetTracker()->PutWindowInSession(kTag2, 0);
+  GetTracker()->PutWindowInSession(kTag2, 2);
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
+  ASSERT_EQ(2U, windows.size());  // Only windows from kTag session.
   ASSERT_NE((sessions::SessionWindow*)nullptr, windows[0]);
   ASSERT_NE((sessions::SessionWindow*)nullptr, windows[1]);
   ASSERT_NE(windows[1], windows[0]);
@@ -115,40 +120,37 @@
 
 TEST_F(SyncedSessionTrackerTest, LookupSessionTab) {
   const sessions::SessionTab* tab;
-  ASSERT_FALSE(GetTracker()->LookupSessionTab("tag1", 5, &tab));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutTabInWindow("tag1", 0, 5, 0);
-  ASSERT_TRUE(GetTracker()->LookupSessionTab("tag1", 5, &tab));
+  ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 5, &tab));
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 5, 0);
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 5, &tab));
   ASSERT_NE((sessions::SessionTab*)nullptr, tab);
 }
 
 TEST_F(SyncedSessionTrackerTest, Complex) {
-  const std::string tag1 = "tag";
-  const std::string tag2 = "tag2";
-  const std::string tag3 = "tag3";
   std::vector<sessions::SessionTab *> tabs1, tabs2;
   sessions::SessionTab* temp_tab;
   ASSERT_TRUE(GetTracker()->Empty());
   ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag1));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 0, 0));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 1, 1));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 2, 2));
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
-  temp_tab = GetTracker()->GetTab(tag1, 0, 0);  // Already created.
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 0));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 1));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 2));
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
+  temp_tab = GetTracker()->GetTab(kTag, 0);  // Already created.
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
   ASSERT_EQ(tabs1[0], temp_tab);
-  tabs2.push_back(GetTracker()->GetTab(tag2, 0, 0));
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
-  ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
-  ASSERT_FALSE(GetTracker()->DeleteSession(tag3));
+  tabs2.push_back(GetTracker()->GetTab(kTag2, 0));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
+  ASSERT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
 
-  SyncedSession* session = GetTracker()->GetSession(tag1);
-  SyncedSession* session2 = GetTracker()->GetSession(tag2);
-  SyncedSession* session3 = GetTracker()->GetSession(tag3);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  SyncedSession* session3 = GetTracker()->GetSession(kTag3);
   session3->device_type = SyncedSession::TYPE_OTHER;
   ASSERT_EQ(3U, GetTracker()->num_synced_sessions());
 
@@ -157,25 +159,25 @@
   ASSERT_TRUE(session3);
   ASSERT_NE(session, session2);
   ASSERT_NE(session2, session3);
-  ASSERT_TRUE(GetTracker()->DeleteSession(tag3));
+  ASSERT_TRUE(GetTracker()->DeleteForeignSession(kTag3));
   ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
 
-  GetTracker()->PutWindowInSession(tag1, 0);           // Create a window.
-  GetTracker()->PutTabInWindow(tag1, 0, 2, 0);         // No longer unmapped.
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));  // Has not changed.
+  GetTracker()->PutWindowInSession(kTag, 0);           // Create a window.
+  GetTracker()->PutTabInWindow(kTag, 0, 2, 0);         // No longer unmapped.
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));  // Has not changed.
 
   const sessions::SessionTab* tab_ptr;
-  ASSERT_TRUE(GetTracker()->LookupSessionTab(tag1, 0, &tab_ptr));
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 0, &tab_ptr));
   ASSERT_EQ(tab_ptr, tabs1[0]);
-  ASSERT_TRUE(GetTracker()->LookupSessionTab(tag1, 2, &tab_ptr));
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 2, &tab_ptr));
   ASSERT_EQ(tab_ptr, tabs1[2]);
-  ASSERT_FALSE(GetTracker()->LookupSessionTab(tag1, 3, &tab_ptr));
+  ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 3, &tab_ptr));
   ASSERT_FALSE(tab_ptr);
 
   std::vector<const sessions::SessionWindow*> windows;
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows(tag1, &windows));
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
   ASSERT_EQ(1U, windows.size());
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows(tag2, &windows));
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag2, &windows));
   ASSERT_EQ(0U, windows.size());
 
   // The sessions don't have valid tabs, lookup should not succeed.
@@ -187,8 +189,8 @@
   ASSERT_EQ(2U, sessions.size());
 
   GetTracker()->Clear();
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag2));
   ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
 }
 
@@ -203,107 +205,101 @@
       // More attempts than tabs means we'll sometimes get the same tabs,
       // sometimes have to allocate new tabs.
       int rand_tab_num = base::RandInt(0, kMaxTabs);
-      sessions::SessionTab* tab =
-          GetTracker()->GetTab(tag, rand_tab_num, rand_tab_num + 1);
+      sessions::SessionTab* tab = GetTracker()->GetTab(tag, rand_tab_num + 1);
       ASSERT_TRUE(tab);
     }
   }
 }
 
-TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) {
+TEST_F(SyncedSessionTrackerTest, LookupForeignTabNodeIds) {
   std::set<int> result;
-  std::string tag1 = "session1";
-  std::string tag2 = "session2";
-  std::string tag3 = "session3";
 
-  GetTracker()->GetTab(tag1, 1, 1);
-  GetTracker()->GetTab(tag1, 2, 2);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->OnTabNodeSeen(kTag, 1);
+  GetTracker()->OnTabNodeSeen(kTag, 2);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(1));
   EXPECT_FALSE(result.end() == result.find(2));
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_TRUE(result.empty());
 
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 3, 0);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 3, 0);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
 
-  GetTracker()->GetTab(tag1, 3, 3);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->OnTabNodeSeen(kTag, 3);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(3));
 
-  GetTracker()->GetTab(tag2, 1, 21);
-  GetTracker()->GetTab(tag2, 2, 22);
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->OnTabNodeSeen(kTag2, 21);
+  GetTracker()->OnTabNodeSeen(kTag2, 22);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(1));
   EXPECT_FALSE(result.end() == result.find(2));
 
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
-  GetTracker()->PutWindowInSession(tag3, 1);
-  GetTracker()->PutTabInWindow(tag3, 1, 5, 0);
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  GetTracker()->PutWindowInSession(kTag3, 1);
+  GetTracker()->PutTabInWindow(kTag3, 1, 5, 0);
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag3));
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
 
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag1));
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag));
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_TRUE(result.empty());
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
 
-  GetTracker()->GetTab(tag2, 1, 21);
-  GetTracker()->GetTab(tag2, 2, 23);
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->OnTabNodeSeen(kTag2, 21);
+  GetTracker()->OnTabNodeSeen(kTag2, 23);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
   EXPECT_FALSE(result.end() == result.find(23));
 
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag2));
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag2));
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_TRUE(result.empty());
 }
 
 TEST_F(SyncedSessionTrackerTest, SessionTracking) {
   ASSERT_TRUE(GetTracker()->Empty());
-  std::string tag1 = "tag1";
-  std::string tag2 = "tag2";
 
   // Create some session information that is stale.
-  SyncedSession* session1 = GetTracker()->GetSession(tag1);
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 0, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 1, 1);
-  GetTracker()->GetTab(tag1, 2, 3U)->window_id.set_id(0);  // Will be unmapped.
-  GetTracker()->GetTab(tag1, 3, 4U)->window_id.set_id(0);  // Will be unmapped.
-  GetTracker()->PutWindowInSession(tag1, 1);
-  GetTracker()->PutTabInWindow(tag1, 1, 4, 0);
-  GetTracker()->PutTabInWindow(tag1, 1, 5, 1);
+  SyncedSession* session1 = GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 0, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 1, 1);
+  GetTracker()->GetTab(kTag, 2)->window_id.set_id(0);  // Will be unmapped.
+  GetTracker()->GetTab(kTag, 3)->window_id.set_id(0);  // Will be unmapped.
+  GetTracker()->PutWindowInSession(kTag, 1);
+  GetTracker()->PutTabInWindow(kTag, 1, 4, 0);
+  GetTracker()->PutTabInWindow(kTag, 1, 5, 1);
   ASSERT_EQ(2U, session1->windows.size());
   ASSERT_EQ(2U, session1->windows[0]->tabs.size());
   ASSERT_EQ(2U, session1->windows[1]->tabs.size());
-  ASSERT_EQ(6U, GetTracker()->num_synced_tabs(tag1));
+  ASSERT_EQ(6U, GetTracker()->num_synced_tabs(kTag));
 
   // Create a session that should not be affected.
-  SyncedSession* session2 = GetTracker()->GetSession(tag2);
-  GetTracker()->PutWindowInSession(tag2, 2);
-  GetTracker()->PutTabInWindow(tag2, 2, 1, 0);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  GetTracker()->PutWindowInSession(kTag2, 2);
+  GetTracker()->PutTabInWindow(kTag2, 2, 1, 0);
   ASSERT_EQ(1U, session2->windows.size());
   ASSERT_EQ(1U, session2->windows[2]->tabs.size());
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
 
   // Reset tracking and get the current windows/tabs.
   // We simulate moving a tab from one window to another, then closing the
@@ -311,18 +307,18 @@
   // on the remaining window.
 
   // New tab, arrived before meta node so unmapped.
-  GetTracker()->GetTab(tag1, 6, 7U);
-  GetTracker()->ResetSessionTracking(tag1);
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 0, 0);
+  GetTracker()->GetTab(kTag, 6);
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 0, 0);
   // Tab 1 is closed.
-  GetTracker()->PutTabInWindow(tag1, 0, 2, 1);  // No longer unmapped.
+  GetTracker()->PutTabInWindow(kTag, 0, 2, 1);  // No longer unmapped.
   // Tab 3 was unmapped and does not get used.
-  GetTracker()->PutTabInWindow(tag1, 0, 4, 2);  // Moved from window 1.
+  GetTracker()->PutTabInWindow(kTag, 0, 4, 2);  // Moved from window 1.
   // Window 1 was closed, along with tab 5.
-  GetTracker()->PutTabInWindow(tag1, 0, 6, 3);  // No longer unmapped.
+  GetTracker()->PutTabInWindow(kTag, 0, 6, 3);  // No longer unmapped.
   // Session 2 should not be affected.
-  GetTracker()->CleanupSession(tag1);
+  GetTracker()->CleanupForeignSession(kTag);
 
   // Verify that only those parts of the session not owned have been removed.
   ASSERT_EQ(1U, session1->windows.size());
@@ -330,39 +326,171 @@
   ASSERT_EQ(1U, session2->windows.size());
   ASSERT_EQ(1U, session2->windows[2]->tabs.size());
   ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
-  ASSERT_EQ(4U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(4U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
 
   // All memory should be properly deallocated by destructor for the
   // SyncedSessionTracker.
 }
 
 TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) {
-  std::string session_tag = "session_tag";
-  int tab_id_1 = 1;
-  int tab_id_2 = 2;
-  int tab_node_id_3 = 3;
-  int tab_node_id_4 = 4;
+  int tab_node_id_1 = 1;
+  int tab_node_id_2 = 2;
   std::set<int> result;
 
-  GetTracker()->GetTab(session_tag, tab_id_1, tab_node_id_3);
-  GetTracker()->GetTab(session_tag, tab_id_1, tab_node_id_4);
-  GetTracker()->GetTab(session_tag, tab_id_2, tab_node_id_3);
-  GetTracker()->GetTab(session_tag, tab_id_2, tab_node_id_4);
+  GetTracker()->OnTabNodeSeen(kTag, tab_node_id_1);
+  GetTracker()->OnTabNodeSeen(kTag, tab_node_id_2);
 
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
-  EXPECT_TRUE(result.find(tab_node_id_3) != result.end());
-  EXPECT_TRUE(result.find(tab_node_id_4) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_1) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_2) != result.end());
 
-  GetTracker()->DeleteForeignTab(session_tag, tab_node_id_3);
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->DeleteForeignTab(kTag, tab_node_id_1);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(1U, result.size());
-  EXPECT_TRUE(result.find(tab_node_id_4) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_2) != result.end());
 
-  GetTracker()->DeleteForeignTab(session_tag, tab_node_id_4);
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->DeleteForeignTab(kTag, tab_node_id_2);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_TRUE(result.empty());
 }
 
+TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
+  std::set<int> free_node_ids;
+  int tab_node_id = TabNodePool::kInvalidTabNodeID;
+  const int kWindow1 = 1;
+  const int kTabNode1 = 1;
+  const int kTabNode2 = 2;
+  const int kTabNode3 = 3;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+  const int kTab3 = 35;
+  const int kTabIndex = 0;
+
+  GetTracker()->SetLocalSessionTag(kTag);
+
+  // Start with two restored tab nodes.
+  GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
+  GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+  EXPECT_FALSE(GetTabNodePool()->Full());
+  EXPECT_EQ(2U, GetTabNodePool()->Capacity());
+
+  // Associate with no tabs. The tab pool should now be full.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+  EXPECT_TRUE(GetTabNodePool()->Full());
+
+  // Associate with only 1 tab open. A tab node should be reused.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1, kTabIndex);
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab1, &tab_node_id));
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // TabNodePool should have one free tab node and one used.
+  EXPECT_EQ(2U, GetTabNodePool()->Capacity());
+  EXPECT_FALSE(GetTabNodePool()->Empty());
+  EXPECT_FALSE(GetTabNodePool()->Full());
+
+  // Simulate a tab opening, which should use the last free tab node.
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab2, &tab_node_id));
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Simulate another tab opening, which should create a new associated tab
+  // node.
+  EXPECT_FALSE(GetTracker()->GetTabNodeForLocalTab(kTab3, &tab_node_id));
+  EXPECT_EQ(kTabNode3, tab_node_id);
+  EXPECT_EQ(3U, GetTabNodePool()->Capacity());
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Fetching the same tab should return the same tab node id.
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab3, &tab_node_id));
+  EXPECT_EQ(kTabNode3, tab_node_id);
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Associate with no tabs. All tabs should be freed again, and the pool
+  // should now be full.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+  EXPECT_TRUE(GetTabNodePool()->Full());
+  EXPECT_FALSE(GetTabNodePool()->Empty());
+}
+
+TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
+  std::set<int> free_node_ids;
+  const int kWindow1 = 1;
+  const int kTabNode = 0;
+  const int kTabIndex = 0;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+
+  // First create the tab normally.
+  GetTracker()->SetLocalSessionTag(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
+
+  // Map it to a window with the same tab id as it was created with.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  ASSERT_EQ(1U, session->windows.size());
+  ASSERT_EQ(1U, session->windows[kWindow1]->tabs.size());
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
+            session->windows[kWindow1]->tabs[0].get());
+
+  // Then reassociate with a new tab id.
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
+
+  // Reset tracking, and put the new tab id into the window.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // Now that it's been mapped, it should be accessible both via the
+  // GetSession as well as the GetTab.
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
+            session->windows[kWindow1]->tabs[0].get());
+  ASSERT_EQ(session->tab_node_ids.size(),
+            session->tab_node_ids.count(kTabNode));
+  ASSERT_EQ(1U, GetTabNodePool()->Capacity());
+}
+
+TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
+  std::set<int> free_node_ids;
+  const int kWindow1 = 1;
+  const int kTabNode = 0;
+  const int kTabIndex = 0;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+
+  // First create the old tab in an unmapped state.
+  GetTracker()->SetLocalSessionTag(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
+
+  // Map it to a window, but reassociated with a new tab id.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // Now that it's been mapped, it should be accessible both via the
+  // GetSession as well as GetTab.
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
+            session->windows[kWindow1]->tabs[0].get());
+  ASSERT_EQ(session->tab_node_ids.size(),
+            session->tab_node_ids.count(kTabNode));
+  ASSERT_EQ(1U, GetTabNodePool()->Capacity());
+}
+
 }  // namespace sync_sessions
diff --git a/components/sync_sessions/tab_node_pool.cc b/components/sync_sessions/tab_node_pool.cc
index 4067b92..f187d23d 100644
--- a/components/sync_sessions/tab_node_pool.cc
+++ b/components/sync_sessions/tab_node_pool.cc
@@ -4,12 +4,10 @@
 
 #include "components/sync_sessions/tab_node_pool.h"
 
-#include "base/format_macros.h"
+#include <algorithm>
+
 #include "base/logging.h"
-#include "base/strings/stringprintf.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_data.h"
 #include "components/sync/protocol/session_specifics.pb.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -26,114 +24,91 @@
 
 TabNodePool::~TabNodePool() {}
 
-// Static
-std::string TabNodePool::TabIdToTag(const std::string& machine_tag,
-                                    int tab_node_id) {
-  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
-}
-
 void TabNodePool::AddTabNode(int tab_node_id) {
   DCHECK_GT(tab_node_id, kInvalidTabNodeID);
   DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
-  unassociated_nodes_.insert(tab_node_id);
-  if (max_used_tab_node_id_ < tab_node_id)
-    max_used_tab_node_id_ = tab_node_id;
+  DVLOG(1) << "Adding tab node " << tab_node_id << " to pool.";
+  max_used_tab_node_id_ = std::max(max_used_tab_node_id_, tab_node_id);
+  free_nodes_pool_.insert(tab_node_id);
 }
 
 void TabNodePool::AssociateTabNode(int tab_node_id, SessionID::id_type tab_id) {
   DCHECK_GT(tab_node_id, kInvalidTabNodeID);
-  // Remove sync node if it is in unassociated nodes pool.
-  std::set<int>::iterator u_it = unassociated_nodes_.find(tab_node_id);
-  if (u_it != unassociated_nodes_.end()) {
-    unassociated_nodes_.erase(u_it);
-  } else {
-    // This is a new node association, the sync node should be free.
-    // Remove node from free node pool and then associate it with the tab.
-    std::set<int>::iterator it = free_nodes_pool_.find(tab_node_id);
-    DCHECK(it != free_nodes_pool_.end());
-    free_nodes_pool_.erase(it);
-  }
+  DCHECK_GT(tab_id, kInvalidTabID);
+
+  // This is a new node association, the sync node should be free.
+  // Remove node from free node pool and then associate it with the tab.
+  std::set<int>::iterator it = free_nodes_pool_.find(tab_node_id);
+  DCHECK(it != free_nodes_pool_.end());
+  free_nodes_pool_.erase(it);
+
   DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
+  DVLOG(1) << "Associating tab node " << tab_node_id << " with tab " << tab_id;
   nodeid_tabid_map_[tab_node_id] = tab_id;
+  tabid_nodeid_map_[tab_id] = tab_node_id;
 }
 
-int TabNodePool::GetFreeTabNode(syncer::SyncChangeList* append_changes) {
-  DCHECK_GT(machine_tag_.length(), 0U);
-  DCHECK(append_changes);
+bool TabNodePool::GetTabNodeForTab(SessionID::id_type tab_id,
+                                   int* tab_node_id) {
+  if (tabid_nodeid_map_.find(tab_id) != tabid_nodeid_map_.end()) {
+    *tab_node_id = tabid_nodeid_map_[tab_id];
+    return true;
+  }
+
   if (free_nodes_pool_.empty()) {
     // Tab pool has no free nodes, allocate new one.
-    int tab_node_id = ++max_used_tab_node_id_;
-    std::string tab_node_tag = TabIdToTag(machine_tag_, tab_node_id);
+    *tab_node_id = ++max_used_tab_node_id_;
+    AddTabNode(*tab_node_id);
 
-    // We fill the new node with just enough data so that in case of a crash/bug
-    // we can identify the node as our own on re-association and reuse it.
-    sync_pb::EntitySpecifics entity;
-    sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-    specifics->set_session_tag(machine_tag_);
-    specifics->set_tab_node_id(tab_node_id);
-    append_changes->push_back(syncer::SyncChange(
-        FROM_HERE, syncer::SyncChange::ACTION_ADD,
-        syncer::SyncData::CreateLocalData(tab_node_tag, tab_node_tag, entity)));
-
-    // Grow the pool by 1 since we created a new node.
-    DVLOG(1) << "Adding sync node " << tab_node_id << " to tab node id pool";
-    free_nodes_pool_.insert(tab_node_id);
-    return tab_node_id;
+    AssociateTabNode(*tab_node_id, tab_id);
+    return false;
   } else {
     // Return the next free node.
-    return *free_nodes_pool_.begin();
+    *tab_node_id = *free_nodes_pool_.begin();
+    AssociateTabNode(*tab_node_id, tab_id);
+    return true;
   }
 }
 
-void TabNodePool::FreeTabNode(int tab_node_id,
-                              syncer::SyncChangeList* append_changes) {
-  DCHECK(append_changes);
-  TabNodeIDToTabIDMap::iterator it = nodeid_tabid_map_.find(tab_node_id);
-  DCHECK(it != nodeid_tabid_map_.end());
-  nodeid_tabid_map_.erase(it);
-  FreeTabNodeInternal(tab_node_id, append_changes);
-}
+void TabNodePool::FreeTab(int tab_id) {
+  DCHECK_GT(tab_id, kInvalidTabID);
+  TabIDToTabNodeIDMap::iterator it = tabid_nodeid_map_.find(tab_id);
+  if (it == tabid_nodeid_map_.end()) {
+    return;  // Already freed.
+  }
 
-void TabNodePool::FreeTabNodeInternal(int tab_node_id,
-                                      syncer::SyncChangeList* append_changes) {
-  DCHECK(free_nodes_pool_.find(tab_node_id) == free_nodes_pool_.end());
-  DCHECK(append_changes);
+  int tab_node_id = it->second;
+  DVLOG(1) << "Freeing tab " << tab_id << " at node " << tab_node_id;
+  nodeid_tabid_map_.erase(nodeid_tabid_map_.find(tab_node_id));
+  tabid_nodeid_map_.erase(it);
   free_nodes_pool_.insert(tab_node_id);
-
-  // If number of free nodes exceed kFreeNodesHighWatermark,
-  // delete sync nodes till number reaches kFreeNodesLowWatermark.
-  // Note: This logic is to mitigate temporary disassociation issues with old
-  // clients: http://crbug.com/259918. Newer versions do not need this.
-  if (free_nodes_pool_.size() > kFreeNodesHighWatermark) {
-    for (std::set<int>::iterator free_it = free_nodes_pool_.begin();
-         free_it != free_nodes_pool_.end();) {
-      const std::string tab_node_tag = TabIdToTag(machine_tag_, *free_it);
-      append_changes->push_back(syncer::SyncChange(
-          FROM_HERE, syncer::SyncChange::ACTION_DELETE,
-          syncer::SyncData::CreateLocalDelete(tab_node_tag, syncer::SESSIONS)));
-      free_nodes_pool_.erase(free_it++);
-      if (free_nodes_pool_.size() <= kFreeNodesLowWatermark) {
-        return;
-      }
-    }
-  }
-}
-
-bool TabNodePool::IsUnassociatedTabNode(int tab_node_id) {
-  return unassociated_nodes_.find(tab_node_id) != unassociated_nodes_.end();
 }
 
 void TabNodePool::ReassociateTabNode(int tab_node_id,
                                      SessionID::id_type tab_id) {
-  // Remove from list of unassociated sync_nodes if present.
-  std::set<int>::iterator it = unassociated_nodes_.find(tab_node_id);
-  if (it != unassociated_nodes_.end()) {
-    unassociated_nodes_.erase(it);
-  } else {
-    // tab_node_id must be an already associated node.
-    DCHECK(nodeid_tabid_map_.find(tab_node_id) != nodeid_tabid_map_.end());
+  DCHECK_GT(tab_node_id, kInvalidTabNodeID);
+  DCHECK_GT(tab_id, kInvalidTabID);
+
+  auto tabid_it = tabid_nodeid_map_.find(tab_id);
+  if (tabid_it != tabid_nodeid_map_.end()) {
+    if (tabid_it->second == tab_node_id) {
+      return;  // Already associated properly.
+    } else {
+      // Another node is already associated with this tab. Free it.
+      FreeTab(tab_id);
+    }
   }
-  nodeid_tabid_map_[tab_node_id] = tab_id;
+
+  auto nodeid_it = nodeid_tabid_map_.find(tab_node_id);
+  if (nodeid_it != nodeid_tabid_map_.end()) {
+    // This node was already associated with another tab. Free it.
+    FreeTab(nodeid_it->second);
+  } else {
+    // This is a new tab node. Add it before association.
+    AddTabNode(tab_node_id);
+  }
+
+  AssociateTabNode(tab_node_id, tab_id);
 }
 
 SessionID::id_type TabNodePool::GetTabIdFromTabNodeId(int tab_node_id) const {
@@ -144,27 +119,33 @@
   return kInvalidTabID;
 }
 
-void TabNodePool::DeleteUnassociatedTabNodes(
-    syncer::SyncChangeList* append_changes) {
-  for (std::set<int>::iterator it = unassociated_nodes_.begin();
-       it != unassociated_nodes_.end();) {
-    FreeTabNodeInternal(*it, append_changes);
-    unassociated_nodes_.erase(it++);
+void TabNodePool::CleanupTabNodes(std::set<int>* deleted_node_ids) {
+  // If number of free nodes exceed kFreeNodesHighWatermark,
+  // delete sync nodes till number reaches kFreeNodesLowWatermark.
+  // Note: This logic is to mitigate temporary disassociation issues with old
+  // clients: http://crbug.com/259918. Newer versions do not need this.
+  if (free_nodes_pool_.size() > kFreeNodesHighWatermark) {
+    for (std::set<int>::iterator free_it = free_nodes_pool_.begin();
+         free_it != free_nodes_pool_.end();) {
+      deleted_node_ids->insert(*free_it);
+      free_nodes_pool_.erase(free_it++);
+      if (free_nodes_pool_.size() <= kFreeNodesLowWatermark) {
+        return;
+      }
+    }
   }
-  DCHECK(unassociated_nodes_.empty());
 }
 
 // Clear tab pool.
 void TabNodePool::Clear() {
-  unassociated_nodes_.clear();
   free_nodes_pool_.clear();
   nodeid_tabid_map_.clear();
+  tabid_nodeid_map_.clear();
   max_used_tab_node_id_ = kInvalidTabNodeID;
 }
 
 size_t TabNodePool::Capacity() const {
-  return nodeid_tabid_map_.size() + unassociated_nodes_.size() +
-         free_nodes_pool_.size();
+  return nodeid_tabid_map_.size() + free_nodes_pool_.size();
 }
 
 bool TabNodePool::Empty() const {
@@ -175,8 +156,4 @@
   return nodeid_tabid_map_.empty();
 }
 
-void TabNodePool::SetMachineTag(const std::string& machine_tag) {
-  machine_tag_ = machine_tag;
-}
-
 }  // namespace sync_sessions
diff --git a/components/sync_sessions/tab_node_pool.h b/components/sync_sessions/tab_node_pool.h
index caadc4e..833daf7 100644
--- a/components/sync_sessions/tab_node_pool.h
+++ b/components/sync_sessions/tab_node_pool.h
@@ -13,11 +13,6 @@
 
 #include "base/macros.h"
 #include "components/sessions/core/session_id.h"
-#include "components/sync/model/sync_change_processor.h"
-
-namespace syncer {
-class SyncChangeProcessor;
-}  // namespace syncer
 
 namespace sync_sessions {
 
@@ -27,17 +22,11 @@
 // - a tab_id: created by session service, unique to this client
 // - a tab_node_id: the id for a particular sync tab node. This is used
 //   to generate the sync tab node tag through:
-//       tab_tag = StringPrintf("%s_%ui", local_session_tag, tab_node_id);
+//       tab_tag = StringPrintf("%s %d", local_session_tag, tab_node_id);
 //
-// A sync node can be in one of the three states:
+// A sync node can be in one of the two states:
 // 1. Associated   : Sync node is used and associated with a tab.
-// 2. Unassociated : Sync node is used but currently unassociated with any tab.
-//                   This is true for old nodes that remain from a session
-//                   restart. Nodes are only unassociated temporarily while the
-//                   model associator figures out which tabs belong to which
-//                   nodes. Eventually any remaining unassociated nodes are
-//                   freed.
-// 3. Free         : Sync node is unused.
+// 2. Free         : Sync node is unused.
 
 class TabNodePool {
  public:
@@ -54,65 +43,37 @@
 
   static const int kInvalidTabNodeID;
 
-  // Build a sync tag from tab_node_id.
-  static std::string TabIdToTag(const std::string& machine_tag,
-                                int tab_node_id);
-
-  // Returns the tab_node_id for the next free tab node. If none are available,
-  // creates a new tab node and adds it to free nodes pool. The free node can
-  // then be used to associate with a tab by calling AssociateTabNode.
-  // Note: The node is considered free until it has been associated. Repeated
-  // calls to GetFreeTabNode will return the same id until node has been
-  // associated.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller context. If the need
-  // to create nodes arises in the implementation, associated SyncChanges will
-  // be appended to this list for later application by the caller via the
-  // SyncChangeProcessor.
-  int GetFreeTabNode(syncer::SyncChangeList* change_output);
-
-  // Removes association for |tab_node_id| and returns it to the free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context. If the need
-  // to delete sync nodes arises in the implementation, associated SyncChanges
-  // will be appended to this list for later application by the caller via the
-  // SyncChangeProcessor.
-  void FreeTabNode(int tab_node_id, syncer::SyncChangeList* change_output);
-
-  // Associates |tab_node_id| with |tab_id|. |tab_node_id| should either be
-  // unassociated or free. If |tab_node_id| is free, |tab_node_id| is removed
-  // from the free node pool In order to associate a non free sync node,
-  // use ReassociateTabNode.
-  void AssociateTabNode(int tab_node_id, SessionID::id_type tab_id);
-
-  // Adds |tab_node_id| as an unassociated sync node.
-  // Note: this should only be called when we discover tab sync nodes from
-  // previous sessions, not for freeing tab nodes we created through
-  // GetFreeTabNode (use FreeTabNode below for that).
-  void AddTabNode(int tab_node_id);
+  // Fills |tab_node_id| with a tab node associated with |tab_id|.
+  // If tab_id is already associated with a tab_node_id, reuses the existing
+  // association. Otherwise attempts to get the next free tab node and
+  // associate it with |tab_id|. If none are available, will create a new tab
+  // node.
+  // Returns true if a pre-existing tab node could be reused, false if a new one
+  // had to be created.
+  bool GetTabNodeForTab(SessionID::id_type tab_id, int* tab_node_id);
 
   // Returns the tab_id for |tab_node_id| if it is associated else returns
   // kInvalidTabID.
   SessionID::id_type GetTabIdFromTabNodeId(int tab_node_id) const;
 
-  // Reassociates |tab_node_id| with |tab_id|. |tab_node_id| must be either
-  // associated with a tab or in the set of unassociated nodes.
+  // Reassociates |tab_node_id| with |tab_id|. If |tab_node_id| is not already
+  // known, it is added to the tab node pool before being associated.
   void ReassociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
-  // Returns true if |tab_node_id| is an unassociated tab node.
-  bool IsUnassociatedTabNode(int tab_node_id);
+  // Removes association for |tab_id| and returns its tab node to the free node
+  // pool.
+  void FreeTab(int tab_id);
 
-  // Returns any unassociated nodes to the free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context.
-  // See FreeTabNode for more detail.
-  void DeleteUnassociatedTabNodes(syncer::SyncChangeList* change_output);
+  // Fills |deleted_node_ids| with any free nodes to be deleted as proscribed
+  // by the free node low/high watermarks, in order to ensure the free node pool
+  // does not grow too large.
+  void CleanupTabNodes(std::set<int>* deleted_node_ids);
 
   // Clear tab pool.
   void Clear();
 
   // Return the number of tab nodes this client currently has allocated
-  // (including both free, unassociated and associated nodes)
+  // (including both free and associated nodes).
   size_t Capacity() const;
 
   // Return empty status (all tab nodes are in use).
@@ -121,41 +82,36 @@
   // Return full status (no tab nodes are in use).
   bool Full();
 
-  void SetMachineTag(const std::string& machine_tag);
-
  private:
   friend class SyncTabNodePoolTest;
   typedef std::map<int, SessionID::id_type> TabNodeIDToTabIDMap;
+  typedef std::map<SessionID::id_type, int> TabIDToTabNodeIDMap;
 
-  // Adds |tab_node_id| to free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context.
-  // See FreeTabNode for more detail.
-  void FreeTabNodeInternal(int tab_node_id,
-                           syncer::SyncChangeList* change_output);
+  // Adds |tab_node_id| to the tab node pool.
+  // Note: this should only be called when we discover tab sync nodes from
+  // previous sessions, not for freeing tab nodes we created through
+  // GetTabNodeForTab (use FreeTab for that).
+  void AddTabNode(int tab_node_id);
+
+  // Associates |tab_node_id| with |tab_id|. |tab_node_id| must be free. In
+  // order to associated a non-free tab node, ReassociateTabNode must be
+  // used.
+  void AssociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
   // Stores mapping of node ids associated with tab_ids, these are the used
   // nodes of tab node pool.
   // The nodes in the map can be returned to free tab node pool by calling
-  // FreeTabNode(tab_node_id).
+  // FreeTab(..).
   TabNodeIDToTabIDMap nodeid_tabid_map_;
+  TabIDToTabNodeIDMap tabid_nodeid_map_;
 
   // The node ids for the set of free sync nodes.
   std::set<int> free_nodes_pool_;
 
-  // The node ids that are added to pool using AddTabNode and are currently
-  // not associated with any tab. They can be reassociated using
-  // ReassociateTabNode.
-  std::set<int> unassociated_nodes_;
-
   // The maximum used tab_node id for a sync node. A new sync node will always
   // be created with max_used_tab_node_id_ + 1.
   int max_used_tab_node_id_;
 
-  // The machine tag associated with this tab pool. Used in the title of new
-  // sync nodes.
-  std::string machine_tag_;
-
   DISALLOW_COPY_AND_ASSIGN(TabNodePool);
 };
 
diff --git a/components/sync_sessions/tab_node_pool_unittest.cc b/components/sync_sessions/tab_node_pool_unittest.cc
index cd125a2..591c8b54 100644
--- a/components/sync_sessions/tab_node_pool_unittest.cc
+++ b/components/sync_sessions/tab_node_pool_unittest.cc
@@ -6,16 +6,13 @@
 
 #include <vector>
 
-#include "components/sync/model/sync_change.h"
-#include "components/sync/protocol/session_specifics.pb.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace sync_sessions {
 
 class SyncTabNodePoolTest : public testing::Test {
  protected:
-  SyncTabNodePoolTest() { pool_.SetMachineTag("tag"); }
+  SyncTabNodePoolTest() {}
 
   int GetMaxUsedTabNodeId() const { return pool_.max_used_tab_node_id_; }
 
@@ -32,107 +29,127 @@
 
 namespace {
 
+const int kTabNodeId1 = 10;
+const int kTabNodeId2 = 5;
+const int kTabNodeId3 = 1000;
+const int kTabId1 = 1;
+const int kTabId2 = 2;
+const int kTabId3 = 3;
+
 TEST_F(SyncTabNodePoolTest, TabNodeIdIncreases) {
-  syncer::SyncChangeList changes;
+  std::set<int> deleted_node_ids;
+
   // max_used_tab_node_ always increases.
-  pool_.AddTabNode(10);
-  EXPECT_EQ(10, GetMaxUsedTabNodeId());
-  pool_.AddTabNode(5);
-  EXPECT_EQ(10, GetMaxUsedTabNodeId());
-  pool_.AddTabNode(1000);
-  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
-  pool_.ReassociateTabNode(1000, 1);
-  pool_.ReassociateTabNode(5, 2);
-  pool_.ReassociateTabNode(10, 3);
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(kTabNodeId1, GetMaxUsedTabNodeId());
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  EXPECT_EQ(kTabNodeId1, GetMaxUsedTabNodeId());
+  pool_.ReassociateTabNode(kTabNodeId3, kTabId3);
+  EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   // Freeing a tab node does not change max_used_tab_node_id_.
-  pool_.FreeTabNode(1000, &changes);
-  EXPECT_TRUE(changes.empty());
-  pool_.FreeTabNode(5, &changes);
-  EXPECT_TRUE(changes.empty());
-  pool_.FreeTabNode(10, &changes);
-  EXPECT_TRUE(changes.empty());
+  pool_.FreeTab(kTabId3);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  pool_.FreeTab(kTabId2);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  pool_.FreeTab(kTabId1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
   for (int i = 0; i < 3; ++i) {
-    pool_.AssociateTabNode(pool_.GetFreeTabNode(&changes), i + 1);
-    EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+    int tab_node_id = -1;
+    EXPECT_TRUE(pool_.GetTabNodeForTab(i + 1, &tab_node_id));
+    EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   }
-  EXPECT_TRUE(changes.empty());
-  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   EXPECT_TRUE(pool_.Empty());
 }
 
-TEST_F(SyncTabNodePoolTest, OldTabNodesAddAndRemove) {
-  syncer::SyncChangeList changes;
-  // VerifyOldTabNodes are added.
-  pool_.AddTabNode(1);
-  pool_.AddTabNode(2);
-  EXPECT_EQ(2u, pool_.Capacity());
+TEST_F(SyncTabNodePoolTest, Reassociation) {
+  // Reassociate tab node 1 with tab id 1.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(1U, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(1));
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(2));
-  pool_.ReassociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  pool_.AssociateTabNode(2, 3);
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(1));
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(2));
-  pool_.FreeTabNode(2, &changes);
-  EXPECT_TRUE(changes.empty());
-  // 2 should be returned to free node pool_.
-  EXPECT_EQ(2u, pool_.Capacity());
-  // Should be able to free 1.
-  pool_.FreeTabNode(1, &changes);
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Introduce a new tab node associated with the same tab. The old tab node
+  // should get added to the free pool
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
   EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(1, 1);
-  EXPECT_EQ(2, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(2, 1);
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the same tab node/tab should have no effect.
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
+  EXPECT_FALSE(pool_.Empty());
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the new tab node with a new tab should just update the
+  // association tables.
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  EXPECT_EQ(2U, pool_.Capacity());
+  EXPECT_FALSE(pool_.Empty());
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId2, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the first tab node should make the pool empty.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
   EXPECT_FALSE(pool_.Full());
-  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId2, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
 }
 
-TEST_F(SyncTabNodePoolTest, OldTabNodesReassociation) {
-  // VerifyOldTabNodes are reassociated correctly.
-  pool_.AddTabNode(4);
-  pool_.AddTabNode(5);
-  pool_.AddTabNode(6);
+TEST_F(SyncTabNodePoolTest, ReassociateThenFree) {
+  std::set<int> deleted_node_ids;
+
+  // Verify old tab nodes are reassociated correctly.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  pool_.ReassociateTabNode(kTabNodeId3, kTabId3);
   EXPECT_EQ(3u, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(4));
-  pool_.ReassociateTabNode(4, 5);
-  pool_.AssociateTabNode(5, 6);
-  pool_.AssociateTabNode(6, 7);
-  // Free 5 and 6.
-  syncer::SyncChangeList changes;
-  pool_.FreeTabNode(5, &changes);
-  pool_.FreeTabNode(6, &changes);
-  EXPECT_TRUE(changes.empty());
-  // 5 and 6 nodes should not be unassociated.
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(5));
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(6));
-  // Free node pool should have 5 and 6.
+  // Free tabs 2 and 3.
+  pool_.FreeTab(kTabId2);
+  pool_.FreeTab(kTabId3);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  // Free node pool should have 2 and 3.
   EXPECT_FALSE(pool_.Empty());
   EXPECT_EQ(3u, pool_.Capacity());
 
   // Free all nodes
-  pool_.FreeTabNode(4, &changes);
-  EXPECT_TRUE(changes.empty());
+  pool_.FreeTab(kTabId1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
   EXPECT_TRUE(pool_.Full());
   std::set<int> free_sync_ids;
   for (int i = 0; i < 3; ++i) {
-    free_sync_ids.insert(pool_.GetFreeTabNode(&changes));
-    // GetFreeTabNode will return the same value till the node is
-    // reassociated.
-    pool_.AssociateTabNode(pool_.GetFreeTabNode(&changes), i + 1);
+    int tab_node_id = -1;
+    EXPECT_TRUE(pool_.GetTabNodeForTab(i, &tab_node_id));
+    free_sync_ids.insert(tab_node_id);
   }
 
   EXPECT_TRUE(pool_.Empty());
   EXPECT_EQ(3u, free_sync_ids.size());
-  EXPECT_EQ(1u, free_sync_ids.count(4));
-  EXPECT_EQ(1u, free_sync_ids.count(5));
-  EXPECT_EQ(1u, free_sync_ids.count(6));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId1));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId2));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId3));
 }
 
 TEST_F(SyncTabNodePoolTest, Init) {
@@ -141,106 +158,49 @@
 }
 
 TEST_F(SyncTabNodePoolTest, AddGet) {
-  syncer::SyncChangeList changes;
   int free_nodes[] = {5, 10};
   AddFreeTabNodes(2, free_nodes);
 
   EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_EQ(5, pool_.GetFreeTabNode(&changes));
-  pool_.AssociateTabNode(5, 1);
+  int tab_node_id = -1;
+  EXPECT_TRUE(pool_.GetTabNodeForTab(1, &tab_node_id));
+  EXPECT_EQ(5, tab_node_id);
   EXPECT_FALSE(pool_.Empty());
   EXPECT_FALSE(pool_.Full());
   EXPECT_EQ(2U, pool_.Capacity());
   // 5 is now used, should return 10.
-  EXPECT_EQ(10, pool_.GetFreeTabNode(&changes));
+  EXPECT_TRUE(pool_.GetTabNodeForTab(2, &tab_node_id));
+  EXPECT_EQ(10, tab_node_id);
 }
 
-TEST_F(SyncTabNodePoolTest, All) {
-  syncer::SyncChangeList changes;
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0U, pool_.Capacity());
-
-  // GetFreeTabNode returns the lowest numbered free node.
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_EQ(1U, changes.size());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(1U, pool_.Capacity());
-
-  // Associate 5, next free node should be 10.
-  pool_.AssociateTabNode(0, 1);
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_EQ(2U, changes.size());
-  changes.clear();
-  pool_.AssociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_FALSE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  // Release them in reverse order.
-  pool_.FreeTabNode(1, &changes);
-  pool_.FreeTabNode(0, &changes);
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  pool_.AssociateTabNode(0, 1);
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_FALSE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  // Release them again.
-  pool_.FreeTabNode(1, &changes);
-  pool_.FreeTabNode(0, &changes);
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  pool_.Clear();
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0U, pool_.Capacity());
-}
-
-TEST_F(SyncTabNodePoolTest, GetFreeTabNodeCreate) {
-  syncer::SyncChangeList changes;
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes[0].IsValid());
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
-  EXPECT_TRUE(changes[0].sync_data().IsValid());
-  sync_pb::EntitySpecifics entity = changes[0].sync_data().GetSpecifics();
-  sync_pb::SessionSpecifics specifics(entity.session());
-  EXPECT_EQ(0, specifics.tab_node_id());
+TEST_F(SyncTabNodePoolTest, GetTabNodeForTabCreate) {
+  int tab_node_id = -1;
+  EXPECT_FALSE(pool_.GetTabNodeForTab(1, &tab_node_id));
+  EXPECT_EQ(0, tab_node_id);
 }
 
 TEST_F(SyncTabNodePoolTest, TabPoolFreeNodeLimits) {
+  std::set<int> deleted_node_ids;
+
   // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
   // freeing the last node reduces the free node pool size to
   // kFreeNodesLowWatermark.
-  syncer::SyncChangeList changes;
   SessionID session_id;
   std::vector<int> used_sync_ids;
   for (size_t i = 1; i <= TabNodePool::kFreeNodesHighWatermark + 1; ++i) {
     session_id.set_id(i);
-    int sync_id = pool_.GetFreeTabNode(&changes);
-    pool_.AssociateTabNode(sync_id, i);
+    int sync_id = -1;
+    EXPECT_FALSE(pool_.GetTabNodeForTab(i, &sync_id));
     used_sync_ids.push_back(sync_id);
   }
 
   // Free all except one node.
-  int last_sync_id = used_sync_ids.back();
   used_sync_ids.pop_back();
 
-  for (size_t i = 0; i < used_sync_ids.size(); ++i) {
-    pool_.FreeTabNode(used_sync_ids[i], &changes);
+  for (size_t i = 1; i <= used_sync_ids.size(); ++i) {
+    pool_.FreeTab(i);
+    pool_.CleanupTabNodes(&deleted_node_ids);
+    EXPECT_TRUE(deleted_node_ids.empty());
   }
 
   // Except one node all nodes should be in FreeNode pool.
@@ -251,7 +211,11 @@
 
   // Freeing the last sync node should drop the free nodes to
   // kFreeNodesLowWatermark.
-  pool_.FreeTabNode(last_sync_id, &changes);
+  pool_.FreeTab(TabNodePool::kFreeNodesHighWatermark + 1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_EQ(TabNodePool::kFreeNodesHighWatermark + 1 -
+                TabNodePool::kFreeNodesLowWatermark,
+            deleted_node_ids.size());
   EXPECT_FALSE(pool_.Empty());
   EXPECT_TRUE(pool_.Full());
   EXPECT_EQ(TabNodePool::kFreeNodesLowWatermark, pool_.Capacity());
diff --git a/components/task_scheduler_util/BUILD.gn b/components/task_scheduler_util/BUILD.gn
index 9f3dcd5e..b070cc8d 100644
--- a/components/task_scheduler_util/BUILD.gn
+++ b/components/task_scheduler_util/BUILD.gn
@@ -10,6 +10,7 @@
 
   deps = [
     "//base",
-    "//components/variations",
+    "//components/task_scheduler_util/initialization",
+    "//components/task_scheduler_util/variations",
   ]
 }
diff --git a/components/task_scheduler_util/initialization/BUILD.gn b/components/task_scheduler_util/initialization/BUILD.gn
new file mode 100644
index 0000000..213326b
--- /dev/null
+++ b/components/task_scheduler_util/initialization/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("initialization") {
+  sources = [
+    "browser_util.cc",
+    "browser_util.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "browser_util_unittest.cc",
+  ]
+  deps = [
+    ":initialization",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/task_scheduler_util/initialization/DEPS b/components/task_scheduler_util/initialization/DEPS
new file mode 100644
index 0000000..d979d4e
--- /dev/null
+++ b/components/task_scheduler_util/initialization/DEPS
@@ -0,0 +1,3 @@
+# This component may be included into content and should follow the same rules
+# listed in content/DEPS
+include_rules = []
diff --git a/components/task_scheduler_util/initialization/browser_util.cc b/components/task_scheduler_util/initialization/browser_util.cc
new file mode 100644
index 0000000..a468281
--- /dev/null
+++ b/components/task_scheduler_util/initialization/browser_util.cc
@@ -0,0 +1,141 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/task_scheduler_util/initialization/browser_util.h"
+
+#include <map>
+#include <string>
+
+#include "base/task_scheduler/initialization_util.h"
+#include "base/task_scheduler/switches.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "build/build_config.h"
+
+namespace task_scheduler_util {
+namespace initialization {
+
+namespace {
+
+using StandbyThreadPolicy =
+    base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+using ThreadPriority = base::ThreadPriority;
+
+struct SchedulerWorkerPoolCustomizableConfiguration {
+  SchedulerWorkerPoolCustomizableConfiguration(
+      const char* name_in,
+      ThreadPriority priority_hint_in,
+      const SingleWorkerPoolConfiguration& single_worker_pool_config_in)
+      : name(name_in),
+        priority_hint(priority_hint_in),
+        single_worker_pool_config(single_worker_pool_config_in) {}
+
+  const char* name;
+  ThreadPriority priority_hint;
+  const SingleWorkerPoolConfiguration& single_worker_pool_config;
+};
+
+}  // namespace
+
+std::vector<base::SchedulerWorkerPoolParams>
+BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(
+    const BrowserWorkerPoolsConfiguration& config) {
+  const SchedulerWorkerPoolCustomizableConfiguration worker_pool_config[] = {
+      SchedulerWorkerPoolCustomizableConfiguration("Background",
+                                                   ThreadPriority::BACKGROUND,
+                                                   config.background),
+      SchedulerWorkerPoolCustomizableConfiguration("BackgroundFileIO",
+                                                   ThreadPriority::BACKGROUND,
+                                                   config.background_file_io),
+      SchedulerWorkerPoolCustomizableConfiguration("Foreground",
+                                                   ThreadPriority::NORMAL,
+                                                   config.foreground),
+      SchedulerWorkerPoolCustomizableConfiguration("ForegroundFileIO",
+                                                   ThreadPriority::NORMAL,
+                                                   config.foreground_file_io),
+
+  };
+  static_assert(arraysize(worker_pool_config) == WORKER_POOL_COUNT,
+                "Mismatched Worker Pool Types and Predefined Parameters");
+  constexpr size_t kNumWorkerPoolsDefined = sizeof(config) /
+                                            sizeof(config.background);
+  static_assert(arraysize(worker_pool_config) == kNumWorkerPoolsDefined,
+                "Mismatch in predefined parameters and worker pools.");
+  std::vector<base::SchedulerWorkerPoolParams> params_vector;
+  for (const auto& config : worker_pool_config) {
+    params_vector.emplace_back(
+        config.name, config.priority_hint,
+        config.single_worker_pool_config.standby_thread_policy,
+        config.single_worker_pool_config.threads,
+        config.single_worker_pool_config.detach_period);
+  }
+  DCHECK_EQ(WORKER_POOL_COUNT, params_vector.size());
+  return params_vector;
+}
+
+// Returns the worker pool index for |traits| defaulting to FOREGROUND or
+// FOREGROUND_FILE_IO on any priorities other than background.
+size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits) {
+  const bool is_background =
+      traits.priority() == base::TaskPriority::BACKGROUND;
+  if (traits.with_file_io())
+    return is_background ? BACKGROUND_FILE_IO : FOREGROUND_FILE_IO;
+
+  return is_background ? BACKGROUND : FOREGROUND;
+}
+
+std::vector<base::SchedulerWorkerPoolParams>
+GetDefaultBrowserSchedulerWorkerPoolParams() {
+  constexpr size_t kNumWorkerPoolsDefined =
+      sizeof(BrowserWorkerPoolsConfiguration) /
+      sizeof(SingleWorkerPoolConfiguration);
+  static_assert(kNumWorkerPoolsDefined == 4,
+                "Expected 4 worker pools in BrowserWorkerPoolsConfiguration");
+  BrowserWorkerPoolsConfiguration config;
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  config.background.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.background.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0);
+  config.background.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.background_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.background_file_io.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0);
+  config.background_file_io.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.foreground.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.foreground.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0);
+  config.foreground.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.foreground_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.foreground_file_io.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0);
+  config.foreground_file_io.detach_period = base::TimeDelta::FromSeconds(30);
+#else
+  config.background.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.background.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0);
+  config.background.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.background_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.background_file_io.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0);
+  config.background_file_io.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.foreground.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.foreground.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0);
+  config.foreground.detach_period = base::TimeDelta::FromSeconds(30);
+
+  config.foreground_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.foreground_file_io.threads =
+      base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0);
+  config.foreground_file_io.detach_period = base::TimeDelta::FromSeconds(30);
+#endif
+  return BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config);
+}
+
+}  // namespace initialization
+}  // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/initialization/browser_util.h b/components/task_scheduler_util/initialization/browser_util.h
new file mode 100644
index 0000000..e223468
--- /dev/null
+++ b/components/task_scheduler_util/initialization/browser_util.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_BROWSER_UTIL_H_
+#define COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_BROWSER_UTIL_H_
+
+#include <vector>
+
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/time/time.h"
+
+namespace base {
+class TaskTraits;
+}
+
+namespace task_scheduler_util {
+namespace initialization {
+
+enum WorkerPoolType : size_t {
+  BACKGROUND = 0,
+  BACKGROUND_FILE_IO,
+  FOREGROUND,
+  FOREGROUND_FILE_IO,
+  WORKER_POOL_COUNT  // Always last.
+};
+
+struct SingleWorkerPoolConfiguration {
+  base::SchedulerWorkerPoolParams::StandbyThreadPolicy standby_thread_policy;
+  int threads = 0;
+  base::TimeDelta detach_period;
+};
+
+struct BrowserWorkerPoolsConfiguration {
+  SingleWorkerPoolConfiguration background;
+  SingleWorkerPoolConfiguration background_file_io;
+  SingleWorkerPoolConfiguration foreground;
+  SingleWorkerPoolConfiguration foreground_file_io;
+};
+
+// Converts a BrowserWorkerPoolsConfiguration to a vector of
+// base::SchedulerWorkerPoolParams for consumption by task scheduler
+// initialization.
+std::vector<base::SchedulerWorkerPoolParams>
+BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(
+    const BrowserWorkerPoolsConfiguration& config);
+
+// Maps |traits| to the index of a browser worker pool vector provided by
+// BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams() or
+// GetDefaultBrowserSchedulerWorkerPoolParams().
+size_t BrowserWorkerPoolIndexForTraits(const base::TaskTraits& traits);
+
+// Returns the default browser scheduler worker pool params.
+std::vector<base::SchedulerWorkerPoolParams>
+GetDefaultBrowserSchedulerWorkerPoolParams();
+
+}  // namespace initialization
+}  // namespace task_scheduler_util
+
+#endif  // COMPONENTS_TASK_SCHEDULER_UTIL_INITIALIZATION_BROWSER_UTIL_H_
diff --git a/components/task_scheduler_util/initialization/browser_util_unittest.cc b/components/task_scheduler_util/initialization/browser_util_unittest.cc
new file mode 100644
index 0000000..7dad6ffe1
--- /dev/null
+++ b/components/task_scheduler_util/initialization/browser_util_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/task_scheduler_util/initialization/browser_util.h"
+
+#include <vector>
+
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace task_scheduler_util {
+namespace initialization {
+
+TEST(TaskSchedulerUtilBrowserUtilTest, CheckOrdering) {
+  using StandbyThreadPolicy =
+      base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+  BrowserWorkerPoolsConfiguration config;
+  config.background.standby_thread_policy = StandbyThreadPolicy::LAZY;
+  config.background.threads = 1;
+  config.background.detach_period = base::TimeDelta::FromSeconds(2);
+
+  config.background_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.background_file_io.threads = 3;
+  config.background_file_io.detach_period = base::TimeDelta::FromSeconds(4);
+
+  config.foreground.standby_thread_policy = StandbyThreadPolicy::LAZY;
+  config.foreground.threads = 5;
+  config.foreground.detach_period = base::TimeDelta::FromSeconds(6);
+
+  config.foreground_file_io.standby_thread_policy = StandbyThreadPolicy::ONE;
+  config.foreground_file_io.threads = 7;
+  config.foreground_file_io.detach_period = base::TimeDelta::FromSeconds(8);
+
+  const std::vector<base::SchedulerWorkerPoolParams> params_vector =
+      BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config);
+
+  ASSERT_EQ(4U, params_vector.size());
+
+  EXPECT_EQ(StandbyThreadPolicy::LAZY,
+            params_vector[0].standby_thread_policy());
+  EXPECT_EQ(1U, params_vector[0].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(2),
+            params_vector[0].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy());
+  EXPECT_EQ(3U, params_vector[1].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(4),
+            params_vector[1].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::LAZY,
+            params_vector[2].standby_thread_policy());
+  EXPECT_EQ(5U, params_vector[2].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(6),
+            params_vector[2].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy());
+  EXPECT_EQ(7U, params_vector[3].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(8),
+            params_vector[3].suggested_reclaim_time());
+}
+
+}  // namespace initialization
+}  // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/initialization_util.cc b/components/task_scheduler_util/initialization_util.cc
index 6c8584f4..a09fdd12 100644
--- a/components/task_scheduler_util/initialization_util.cc
+++ b/components/task_scheduler_util/initialization_util.cc
@@ -4,197 +4,29 @@
 
 #include "components/task_scheduler_util/initialization_util.h"
 
-#include <map>
-#include <string>
 #include <vector>
 
 #include "base/bind.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/task_scheduler/initialization_util.h"
 #include "base/task_scheduler/scheduler_worker_pool_params.h"
-#include "base/task_scheduler/switches.h"
 #include "base/task_scheduler/task_scheduler.h"
-#include "base/task_scheduler/task_traits.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "components/variations/variations_associated_data.h"
+#include "components/task_scheduler_util/initialization/browser_util.h"
+#include "components/task_scheduler_util/variations/browser_variations_util.h"
 
 namespace task_scheduler_util {
 
-namespace {
-
-using StandbyThreadPolicy =
-    base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
-
-enum WorkerPoolType : size_t {
-  BACKGROUND_WORKER_POOL = 0,
-  BACKGROUND_FILE_IO_WORKER_POOL,
-  FOREGROUND_WORKER_POOL,
-  FOREGROUND_FILE_IO_WORKER_POOL,
-  WORKER_POOL_COUNT  // Always last.
-};
-
-struct WorkerPoolVariationValues {
-  StandbyThreadPolicy standby_thread_policy;
-  int threads = 0;
-  base::TimeDelta detach_period;
-};
-
-// Converts |pool_descriptor| to a WorkerPoolVariationValues. Returns a default
-// WorkerPoolVariationValues on failure.
-//
-// |pool_descriptor| is a semi-colon separated value string with the following
-// items:
-// 0. Minimum Thread Count (int)
-// 1. Maximum Thread Count (int)
-// 2. Thread Count Multiplier (double)
-// 3. Thread Count Offset (int)
-// 4. Detach Time in Milliseconds (milliseconds)
-// 5. Standby Thread Policy (string)
-// Additional values may appear as necessary and will be ignored.
-WorkerPoolVariationValues StringToWorkerPoolVariationValues(
-    const base::StringPiece pool_descriptor) {
-  const std::vector<std::string> tokens =
-      SplitString(pool_descriptor, ";",
-                  base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  int min;
-  int max;
-  double cores_multiplier;
-  int offset;
-  int detach_milliseconds;
-  // Checking for a size greater than the expected amount allows us to be
-  // forward compatible if we add more variation values.
-  if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) &&
-      base::StringToInt(tokens[1], &max) &&
-      base::StringToDouble(tokens[2], &cores_multiplier) &&
-      base::StringToInt(tokens[3], &offset) &&
-      base::StringToInt(tokens[4], &detach_milliseconds)) {
-    WorkerPoolVariationValues values;
-    values.threads = base::RecommendedMaxNumberOfThreadsInPool(
-        min, max, cores_multiplier, offset);
-    values.detach_period =
-        base::TimeDelta::FromMilliseconds(detach_milliseconds);
-    values.standby_thread_policy =
-        (tokens.size() >= 6 && tokens[5] == "lazy")
-            ? StandbyThreadPolicy::LAZY
-            : StandbyThreadPolicy::ONE;
-    return values;
-  }
-  DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor;
-  return WorkerPoolVariationValues();
-}
-
-// Returns the worker pool index for |traits| defaulting to
-// FOREGROUND_WORKER_POOL or FOREGROUND_FILE_IO_WORKER_POOL on unknown
-// priorities.
-size_t WorkerPoolIndexForTraits(const base::TaskTraits& traits) {
-  const bool is_background =
-      traits.priority() == base::TaskPriority::BACKGROUND;
-  if (traits.with_file_io()) {
-    return is_background ? BACKGROUND_FILE_IO_WORKER_POOL
-                         : FOREGROUND_FILE_IO_WORKER_POOL;
-  }
-  return is_background ? BACKGROUND_WORKER_POOL : FOREGROUND_WORKER_POOL;
-}
-
-std::map<std::string, std::string> GetDefaultBrowserVariationParams() {
-  std::map<std::string, std::string> variation_params;
-#if defined(OS_ANDROID) || defined(OS_IOS)
-  variation_params["Background"] = "2;8;0.1;0;30000";
-  variation_params["BackgroundFileIO"] = "2;8;0.1;0;30000";
-  variation_params["Foreground"] = "3;8;0.3;0;30000";
-  variation_params["ForegroundFileIO"] = "3;8;0.3;0;30000";
-#else
-  variation_params["Background"] = "3;8;0.1;0;30000";
-  variation_params["BackgroundFileIO"] = "3;8;0.1;0;30000";
-  variation_params["Foreground"] = "8;32;0.3;0;30000";
-  variation_params["ForegroundFileIO"] = "8;32;0.3;0;30000";
-#endif  // defined(OS_ANDROID) || defined(OS_IOS)
-  return variation_params;
-}
-
-// Converts a browser-based |variation_params| to
-// std::vector<base::SchedulerWorkerPoolParams>. Returns an empty vector on
-// failure.
-std::vector<base::SchedulerWorkerPoolParams>
-VariationsParamsToBrowserSchedulerWorkerPoolParams(
-    const std::map<std::string, std::string>& variation_params) {
-  using ThreadPriority = base::ThreadPriority;
-  struct SchedulerWorkerPoolPredefinedParams {
-    const char* name;
-    ThreadPriority priority_hint;
-  };
-  static const SchedulerWorkerPoolPredefinedParams kAllPredefinedParams[] = {
-      {"Background", ThreadPriority::BACKGROUND},
-      {"BackgroundFileIO", ThreadPriority::BACKGROUND},
-      {"Foreground", ThreadPriority::NORMAL},
-      {"ForegroundFileIO", ThreadPriority::NORMAL},
-  };
-  static_assert(arraysize(kAllPredefinedParams) == WORKER_POOL_COUNT,
-                "Mismatched Worker Pool Types and Predefined Parameters");
-  std::vector<base::SchedulerWorkerPoolParams> params_vector;
-  for (const auto& predefined_params : kAllPredefinedParams) {
-    const auto pair = variation_params.find(predefined_params.name);
-    if (pair == variation_params.end()) {
-      DLOG(ERROR) << "Missing Worker Pool Configuration: "
-                  << predefined_params.name;
-      return std::vector<base::SchedulerWorkerPoolParams>();
-    }
-
-    const WorkerPoolVariationValues variation_values =
-        StringToWorkerPoolVariationValues(pair->second);
-
-    if (variation_values.threads <= 0 ||
-        variation_values.detach_period.is_zero()) {
-      DLOG(ERROR) << "Invalid Worker Pool Configuration: " <<
-          predefined_params.name << " [" << pair->second << "]";
-      return std::vector<base::SchedulerWorkerPoolParams>();
-    }
-
-    params_vector.emplace_back(predefined_params.name,
-                               predefined_params.priority_hint,
-                               variation_values.standby_thread_policy,
-                               variation_values.threads,
-                               variation_values.detach_period);
-  }
-  DCHECK_EQ(WORKER_POOL_COUNT, params_vector.size());
-  return params_vector;
-}
-
-}  // namespace
-
 void InitializeDefaultBrowserTaskScheduler() {
-  static constexpr char kFieldTrialName[] = "BrowserScheduler";
-  std::map<std::string, std::string> variation_params;
-  if (!variations::GetVariationParams(kFieldTrialName, &variation_params))
-    variation_params = GetDefaultBrowserVariationParams();
-
-  auto params_vector =
-      VariationsParamsToBrowserSchedulerWorkerPoolParams(variation_params);
+  std::vector<base::SchedulerWorkerPoolParams> params_vector =
+      variations::GetBrowserSchedulerWorkerPoolParamsFromVariations();
   if (params_vector.empty()) {
-    variation_params = GetDefaultBrowserVariationParams();
     params_vector =
-        VariationsParamsToBrowserSchedulerWorkerPoolParams(variation_params);
-    DCHECK(!params_vector.empty());
+        initialization::GetDefaultBrowserSchedulerWorkerPoolParams();
   }
-  base::TaskScheduler::CreateAndSetDefaultTaskScheduler(
-      params_vector, base::Bind(WorkerPoolIndexForTraits));
 
-  // TODO(gab): Remove this when http://crbug.com/622400 concludes.
-  const auto sequenced_worker_pool_param =
-      variation_params.find("RedirectSequencedWorkerPools");
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableBrowserTaskScheduler) &&
-      sequenced_worker_pool_param != variation_params.end() &&
-      sequenced_worker_pool_param->second == "true") {
-    base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess();
-  } else {
-    base::SequencedWorkerPool::EnableForProcess();
-  }
+  base::TaskScheduler::CreateAndSetDefaultTaskScheduler(
+      params_vector,
+      base::Bind(&initialization::BrowserWorkerPoolIndexForTraits));
+  task_scheduler_util::variations::
+      MaybePerformBrowserTaskSchedulerRedirection();
 }
 
 }  // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/variations/BUILD.gn b/components/task_scheduler_util/variations/BUILD.gn
new file mode 100644
index 0000000..64aab01
--- /dev/null
+++ b/components/task_scheduler_util/variations/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("variations") {
+  sources = [
+    "browser_variations_util.cc",
+    "browser_variations_util.h",
+  ]
+
+  deps = [
+    "//base",
+    "//base:base_static",
+    "//components/task_scheduler_util/initialization",
+    "//components/variations",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "browser_variations_util_unittest.cc",
+  ]
+  deps = [
+    ":variations",
+    "//base",
+    "//components/variations",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/task_scheduler_util/DEPS b/components/task_scheduler_util/variations/DEPS
similarity index 100%
rename from components/task_scheduler_util/DEPS
rename to components/task_scheduler_util/variations/DEPS
diff --git a/components/task_scheduler_util/variations/browser_variations_util.cc b/components/task_scheduler_util/variations/browser_variations_util.cc
new file mode 100644
index 0000000..a3616b1
--- /dev/null
+++ b/components/task_scheduler_util/variations/browser_variations_util.cc
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/task_scheduler_util/variations/browser_variations_util.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/task_scheduler/initialization_util.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/task_scheduler/switches.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/task_scheduler_util/initialization/browser_util.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace task_scheduler_util {
+namespace variations {
+
+namespace {
+
+constexpr char kFieldTrialName[] = "BrowserScheduler";
+
+// Converts |pool_descriptor| to a SingleWorkerPoolConfiguration. Returns a
+// default SingleWorkerPoolConfiguration on failure.
+//
+// |pool_descriptor| is a semi-colon separated value string with the following
+// items:
+// 0. Minimum Thread Count (int)
+// 1. Maximum Thread Count (int)
+// 2. Thread Count Multiplier (double)
+// 3. Thread Count Offset (int)
+// 4. Detach Time in Milliseconds (milliseconds)
+// 5. Standby Thread Policy (string)
+// Additional values may appear as necessary and will be ignored.
+initialization::SingleWorkerPoolConfiguration
+StringToSingleWorkerPoolConfiguration(const base::StringPiece pool_descriptor) {
+  using StandbyThreadPolicy =
+      base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+  const std::vector<base::StringPiece> tokens = SplitStringPiece(
+      pool_descriptor, ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  // Normally, we wouldn't initialize the values below because we don't read
+  // from them before we write to them. However, some compilers (like MSVC)
+  // complain about uninitialized varaibles due to the as_string() call below.
+  int min = 0;
+  int max = 0;
+  double cores_multiplier = 0.0;
+  int offset = 0;
+  int detach_milliseconds = 0;
+  // Checking for a size greater than the expected amount allows us to be
+  // forward compatible if we add more variation values.
+  if (tokens.size() >= 5 && base::StringToInt(tokens[0], &min) &&
+      base::StringToInt(tokens[1], &max) &&
+      base::StringToDouble(tokens[2].as_string(), &cores_multiplier) &&
+      base::StringToInt(tokens[3], &offset) &&
+      base::StringToInt(tokens[4], &detach_milliseconds)) {
+    initialization::SingleWorkerPoolConfiguration config;
+    config.threads = base::RecommendedMaxNumberOfThreadsInPool(
+        min, max, cores_multiplier, offset);
+    config.detach_period =
+        base::TimeDelta::FromMilliseconds(detach_milliseconds);
+    config.standby_thread_policy = (tokens.size() >= 6 && tokens[5] == "lazy")
+                                       ? StandbyThreadPolicy::LAZY
+                                       : StandbyThreadPolicy::ONE;
+    return config;
+  }
+  DLOG(ERROR) << "Invalid Worker Pool Descriptor: " << pool_descriptor;
+  return initialization::SingleWorkerPoolConfiguration();
+}
+
+// Converts a browser-based |variation_params| to
+// std::vector<base::SchedulerWorkerPoolParams>. Returns an empty vector on
+// failure.
+std::vector<base::SchedulerWorkerPoolParams>
+VariationsParamsToSchedulerWorkerPoolParamsVector(
+    const std::map<std::string, std::string>& variation_params) {
+  static const char* const kWorkerPoolNames[] = {
+      "Background", "BackgroundFileIO", "Foreground", "ForegroundFileIO"};
+  static_assert(
+      arraysize(kWorkerPoolNames) == initialization::WORKER_POOL_COUNT,
+      "Mismatched Worker Pool Types and Worker Pool Names");
+  initialization::BrowserWorkerPoolsConfiguration config;
+  initialization::SingleWorkerPoolConfiguration* const all_pools[]{
+      &config.background, &config.background_file_io, &config.foreground,
+      &config.foreground_file_io,
+  };
+  static_assert(arraysize(kWorkerPoolNames) == arraysize(all_pools),
+                "Mismatched Worker Pool Names and All Pools Array");
+  for (size_t i = 0; i < arraysize(kWorkerPoolNames); ++i) {
+    const auto* const worker_pool_name = kWorkerPoolNames[i];
+    const auto pair = variation_params.find(worker_pool_name);
+    if (pair == variation_params.end()) {
+      DLOG(ERROR) << "Missing Worker Pool Configuration: " << worker_pool_name;
+      return std::vector<base::SchedulerWorkerPoolParams>();
+    }
+
+    auto* const pool_config = all_pools[i];
+    *pool_config = StringToSingleWorkerPoolConfiguration(pair->second);
+    if (pool_config->threads <= 0 ||
+        pool_config->detach_period <= base::TimeDelta()) {
+      DLOG(ERROR) << "Invalid Worker Pool Configuration: " << worker_pool_name
+                  << " [" << pair->second << "]";
+      return std::vector<base::SchedulerWorkerPoolParams>();
+    }
+  }
+  return BrowserWorkerPoolConfigurationToSchedulerWorkerPoolParams(config);
+}
+
+}  // namespace
+
+std::vector<base::SchedulerWorkerPoolParams>
+GetBrowserSchedulerWorkerPoolParamsFromVariations() {
+  std::map<std::string, std::string> variation_params;
+  if (!::variations::GetVariationParams(kFieldTrialName, &variation_params))
+    return std::vector<base::SchedulerWorkerPoolParams>();
+
+  return VariationsParamsToSchedulerWorkerPoolParamsVector(variation_params);
+}
+
+void MaybePerformBrowserTaskSchedulerRedirection() {
+  std::map<std::string, std::string> variation_params;
+  ::variations::GetVariationParams(kFieldTrialName, &variation_params);
+
+  // TODO(gab): Remove this when http://crbug.com/622400 concludes.
+  const auto sequenced_worker_pool_param =
+      variation_params.find("RedirectSequencedWorkerPools");
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableBrowserTaskScheduler) &&
+      sequenced_worker_pool_param != variation_params.end() &&
+      sequenced_worker_pool_param->second == "true") {
+    base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess();
+  } else {
+    base::SequencedWorkerPool::EnableForProcess();
+  }
+}
+
+}  // variations
+}  // namespace task_scheduler_util
diff --git a/components/task_scheduler_util/variations/browser_variations_util.h b/components/task_scheduler_util/variations/browser_variations_util.h
new file mode 100644
index 0000000..114d5d3
--- /dev/null
+++ b/components/task_scheduler_util/variations/browser_variations_util.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TASK_SCHEDULER_UTIL_VARIATIONS_BROWSER_VARIATIONS_UTIL_H_
+#define COMPONENTS_TASK_SCHEDULER_UTIL_VARIATIONS_BROWSER_VARIATIONS_UTIL_H_
+
+#include <vector>
+
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+
+namespace task_scheduler_util {
+namespace variations {
+
+// Gets the browser SchedulerWorkerPoolParams vector based off of variations.
+// Returns an empty vector on failure.
+std::vector<base::SchedulerWorkerPoolParams>
+GetBrowserSchedulerWorkerPoolParamsFromVariations();
+
+// Redirects zero-to-many PostTask APIs to the browser task scheduler based off
+// of input from the variations parameters.
+void MaybePerformBrowserTaskSchedulerRedirection();
+
+}  // variations
+}  // namespace task_scheduler_util
+
+#endif  // COMPONENTS_TASK_SCHEDULER_UTIL_VARIATIONS_BROWSER_VARIATIONS_UTIL_H_
diff --git a/components/task_scheduler_util/variations/browser_variations_util_unittest.cc b/components/task_scheduler_util/variations/browser_variations_util_unittest.cc
new file mode 100644
index 0000000..9350c6d0
--- /dev/null
+++ b/components/task_scheduler_util/variations/browser_variations_util_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/task_scheduler_util/variations/browser_variations_util.h"
+
+#include <map>
+#include <vector>
+
+#include "base/metrics/field_trial.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace task_scheduler_util {
+namespace variations {
+
+namespace {
+
+using StandbyThreadPolicy =
+    base::SchedulerWorkerPoolParams::StandbyThreadPolicy;
+
+constexpr char kFieldTrialName[] = "BrowserScheduler";
+constexpr char kFieldTrialTestGroup[] = "Test";
+
+class TaskSchedulerUtilBrowserVariationsUtilTest : public ::testing::Test {
+ public:
+  TaskSchedulerUtilBrowserVariationsUtilTest() : field_trial_list_(nullptr) {}
+
+  ~TaskSchedulerUtilBrowserVariationsUtilTest() override {
+    // Ensure that the maps are cleared between tests, since they are stored as
+    // process singletons.
+    ::variations::testing::ClearAllVariationIDs();
+    ::variations::testing::ClearAllVariationParams();
+  }
+
+ private:
+  base::FieldTrialList field_trial_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerUtilBrowserVariationsUtilTest);
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerUtilBrowserVariationsUtilTest, OrderingParams5) {
+  std::map<std::string, std::string> params;
+  params["Background"] = "1;1;1;0;42";
+  params["BackgroundFileIO"] = "2;2;1;0;52";
+  params["Foreground"] = "4;4;1;0;62";
+  params["ForegroundFileIO"] = "8;8;1;0;72";
+  ASSERT_TRUE(::variations::AssociateVariationParams(kFieldTrialName,
+                                                     kFieldTrialTestGroup,
+                                                     params));
+
+  base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+  auto params_vector = GetBrowserSchedulerWorkerPoolParamsFromVariations();
+  ASSERT_EQ(4U, params_vector.size());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[0].standby_thread_policy());
+  EXPECT_EQ(1U, params_vector[0].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(42),
+            params_vector[0].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy());
+  EXPECT_EQ(2U, params_vector[1].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
+            params_vector[1].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[2].standby_thread_policy());
+  EXPECT_EQ(4U, params_vector[2].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(62),
+            params_vector[2].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy());
+  EXPECT_EQ(8U, params_vector[3].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
+            params_vector[3].suggested_reclaim_time());
+}
+
+TEST_F(TaskSchedulerUtilBrowserVariationsUtilTest, OrderingParams6) {
+  std::map<std::string, std::string> params;
+  params["Background"] = "1;1;1;0;42;lazy";
+  params["BackgroundFileIO"] = "2;2;1;0;52;one";
+  params["Foreground"] = "4;4;1;0;62;lazy";
+  params["ForegroundFileIO"] = "8;8;1;0;72;one";
+  ASSERT_TRUE(::variations::AssociateVariationParams(kFieldTrialName,
+                                                     kFieldTrialTestGroup,
+                                                     params));
+
+  base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+  auto params_vector = GetBrowserSchedulerWorkerPoolParamsFromVariations();
+  ASSERT_EQ(4U, params_vector.size());
+
+  EXPECT_EQ(StandbyThreadPolicy::LAZY,
+            params_vector[0].standby_thread_policy());
+  EXPECT_EQ(1U, params_vector[0].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(42),
+            params_vector[0].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[1].standby_thread_policy());
+  EXPECT_EQ(2U, params_vector[1].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
+            params_vector[1].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::LAZY,
+            params_vector[2].standby_thread_policy());
+  EXPECT_EQ(4U, params_vector[2].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(62),
+            params_vector[2].suggested_reclaim_time());
+
+  EXPECT_EQ(StandbyThreadPolicy::ONE, params_vector[3].standby_thread_policy());
+  EXPECT_EQ(8U, params_vector[3].max_threads());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
+            params_vector[3].suggested_reclaim_time());
+}
+
+TEST_F(TaskSchedulerUtilBrowserVariationsUtilTest, NoData) {
+  auto params_vector = GetBrowserSchedulerWorkerPoolParamsFromVariations();
+  EXPECT_TRUE(params_vector.empty());
+}
+
+TEST_F(TaskSchedulerUtilBrowserVariationsUtilTest, IncompleteParameters) {
+  std::map<std::string, std::string> params;
+  params["Background"] = "1;1;1;0";
+  params["BackgroundFileIO"] = "2;2;1;0";
+  params["Foreground"] = "4;4;1;0";
+  params["ForegroundFileIO"] = "8;8;1;0";
+  base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+  ASSERT_TRUE(::variations::AssociateVariationParams(kFieldTrialName,
+                                                     kFieldTrialTestGroup,
+                                                     params));
+  auto params_vector = GetBrowserSchedulerWorkerPoolParamsFromVariations();
+  EXPECT_TRUE(params_vector.empty());
+}
+
+TEST_F(TaskSchedulerUtilBrowserVariationsUtilTest, InvalidParameters) {
+  std::map<std::string, std::string> params;
+  params["Background"] = "a;b;c;d;e";
+  params["BackgroundFileIO"] = "a;b;c;d;e";
+  params["Foreground"] = "a;b;c;d;e";
+  params["ForegroundFileIO"] = "a;b;c;d;e";
+  base::FieldTrialList::CreateFieldTrial(kFieldTrialName, kFieldTrialTestGroup);
+  ASSERT_TRUE(::variations::AssociateVariationParams(kFieldTrialName,
+                                                     kFieldTrialTestGroup,
+                                                     params));
+  auto params_vector = GetBrowserSchedulerWorkerPoolParamsFromVariations();
+  EXPECT_TRUE(params_vector.empty());
+}
+
+}  // namespace variations
+}  // namespace task_scheduler_util
diff --git a/components/tracing/browser/trace_config_file.cc b/components/tracing/browser/trace_config_file.cc
index 62220d6..97f8a168 100644
--- a/components/tracing/browser/trace_config_file.cc
+++ b/components/tracing/browser/trace_config_file.cc
@@ -98,7 +98,7 @@
 
 bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) {
   std::unique_ptr<base::Value> value(base::JSONReader::Read(content));
-  if (!value || !value->IsType(base::Value::TYPE_DICTIONARY))
+  if (!value || !value->IsType(base::Value::Type::DICTIONARY))
     return false;
 
   std::unique_ptr<base::DictionaryValue> dict(
diff --git a/components/translate/content/common/translate.mojom b/components/translate/content/common/translate.mojom
index e0916071..9df5ce0 100644
--- a/components/translate/content/common/translate.mojom
+++ b/components/translate/content/common/translate.mojom
@@ -4,7 +4,7 @@
 
 module translate.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 import "url/mojo/url.mojom";
 
 enum TranslateError {
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc
index fec2f66b..cee6773 100644
--- a/components/translate/core/browser/translate_language_list.cc
+++ b/components/translate/core/browser/translate_language_list.cc
@@ -308,7 +308,8 @@
   std::unique_ptr<base::Value> json_value =
       base::JSONReader::Read(language_list, base::JSON_ALLOW_TRAILING_COMMAS);
 
-  if (json_value == NULL || !json_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (json_value == NULL ||
+      !json_value->IsType(base::Value::Type::DICTIONARY)) {
     NotifyEvent(__LINE__, "Language list is invalid");
     NOTREACHED();
     return false;
diff --git a/components/ui_devtools/devtools_server.cc b/components/ui_devtools/devtools_server.cc
index a7428e4..5ed5794 100644
--- a/components/ui_devtools/devtools_server.cc
+++ b/components/ui_devtools/devtools_server.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "components/ui_devtools/switches.h"
 #include "net/base/net_errors.h"
@@ -47,18 +48,19 @@
 UiDevToolsServer* UiDevToolsServer::devtools_server_ = nullptr;
 
 UiDevToolsServer::UiDevToolsServer(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : task_runner_(task_runner) {
+    scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
+    : io_thread_task_runner_(io_thread_task_runner) {
   DCHECK(!devtools_server_);
+  main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
   devtools_server_ = this;
-  if (task_runner_)
+  if (io_thread_task_runner_)
     return;
-  // If task_runner not passed in, create an I/O thread the server can run on
+  // If io_thread_task_runner not passed in, create an I/O thread
   thread_.reset(new base::Thread("UiDevToolsServerThread"));
   base::Thread::Options options;
   options.message_loop_type = base::MessageLoop::TYPE_IO;
   CHECK(thread_->StartWithOptions(options));
-  task_runner_ = thread_->task_runner();
+  io_thread_task_runner_ = thread_->task_runner();
 }
 
 UiDevToolsServer::~UiDevToolsServer() {
@@ -67,11 +69,11 @@
 
 // static
 std::unique_ptr<UiDevToolsServer> UiDevToolsServer::Create(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) {
   std::unique_ptr<UiDevToolsServer> server;
   if (IsUiDevToolsEnabled() && !devtools_server_) {
     // TODO(mhashmi): Change port if more than one inspectable clients
-    server.reset(new UiDevToolsServer(task_runner));
+    server.reset(new UiDevToolsServer(io_thread_task_runner));
     server->Start("127.0.0.1", GetUiDevToolsPort());
   }
   return server;
@@ -101,14 +103,14 @@
 
 void UiDevToolsServer::SendOverWebSocket(int connection_id,
                                          const String& message) {
-  task_runner_->PostTask(
+  io_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&net::HttpServer::SendOverWebSocket,
                  base::Unretained(server_.get()), connection_id, message));
 }
 
 void UiDevToolsServer::Start(const std::string& address_string, uint16_t port) {
-  task_runner_->PostTask(
+  io_thread_task_runner_->PostTask(
       FROM_HERE, base::Bind(&UiDevToolsServer::StartServer,
                             base::Unretained(this), address_string, port));
 }
@@ -150,7 +152,7 @@
     return;
   client->set_connection_id(connection_id);
   connections_[connection_id] = client;
-  task_runner_->PostTask(
+  io_thread_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&net::HttpServer::AcceptWebSocket,
                  base::Unretained(server_.get()), connection_id, info));
@@ -162,8 +164,9 @@
   DCHECK(it != connections_.end());
   UiDevToolsClient* client = it->second;
   DCHECK(client);
-  task_runner_->PostTask(FROM_HERE, base::Bind(&UiDevToolsClient::Dispatch,
-                                               base::Unretained(client), data));
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&UiDevToolsClient::Dispatch, base::Unretained(client), data));
 }
 
 void UiDevToolsServer::OnClose(int connection_id) {
@@ -172,7 +175,9 @@
     return;
   UiDevToolsClient* client = it->second;
   DCHECK(client);
-  client->Disconnect();
+  main_thread_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&UiDevToolsClient::Disconnect, base::Unretained(client)));
   connections_.erase(it);
 }
 
diff --git a/components/ui_devtools/devtools_server.h b/components/ui_devtools/devtools_server.h
index 73a8ae4..3f6ca85 100644
--- a/components/ui_devtools/devtools_server.h
+++ b/components/ui_devtools/devtools_server.h
@@ -28,7 +28,7 @@
   // Returns an empty unique_ptr if ui devtools flag isn't enabled or if a
   // server instance has already been created.
   static std::unique_ptr<UiDevToolsServer> Create(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner);
 
   // Returns a list of attached UiDevToolsClient name + URL
   using NameUrlPair = std::pair<std::string, std::string>;
@@ -39,7 +39,7 @@
 
  private:
   explicit UiDevToolsServer(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner);
 
   void Start(const std::string& address_string, uint16_t port);
   void StartServer(const std::string& address_string, uint16_t port);
@@ -60,7 +60,8 @@
 
   std::unique_ptr<base::Thread> thread_;
   std::unique_ptr<net::HttpServer> server_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
 
   // The server (owned by ash for now)
   static UiDevToolsServer* devtools_server_;
diff --git a/components/ui_devtools/protocol.json b/components/ui_devtools/protocol.json
index 7e5279b..e69dd13 100644
--- a/components/ui_devtools/protocol.json
+++ b/components/ui_devtools/protocol.json
@@ -20,7 +20,29 @@
                             "name": "root"
                         }
                     ]
+                },
+                {
+                    "name": "highlightNode",
+                    "parameters": [
+                        { 
+                            "name": "highlightConfig", 
+                            "$ref": "HighlightConfig",  
+                            "description": "A descriptor for the highlight appearance."  
+                        },
+                        {
+                            "description": "Identifier of the node to highlight.",
+                            "optional": true,
+                            "name": "nodeId",
+                            "$ref": "NodeId"
+                        }
+                    ],
+                    "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified."
+                },
+                {
+                    "name": "hideHighlight",
+                    "description": "Hides DOM node highlight."
                 }
+
             ],
             "events": [
                 {
@@ -126,7 +148,54 @@
                         }
                     ],
                     "type": "object"
-                }
+                },
+                {
+                    "id": "RGBA",
+                    "type": "object",
+                    "properties": [
+                        { 
+                            "name": "r",
+                            "type": "integer",
+                            "description": "The red component, in the [0-255] range."
+                        },
+                        { 
+                            "name": "g", 
+                            "type": "integer", 
+                            "description": "The green component, in the [0-255] range." 
+                        },
+                        {
+                            "name": "b", 
+                            "type": "integer", 
+                            "description": "The blue component, in the [0-255] range." 
+                        },
+                        { 
+                            "name": "a", 
+                            "type": "number", 
+                            "optional": true, 
+                            "description": "The alpha component, in the [0-1] range (default: 1)." 
+                        }
+                    ],
+                    "description": "A structure holding an RGBA color."
+                },
+                {
+                    "id": "HighlightConfig",
+                    "type": "object",
+                    "properties": [
+                        { 
+                            "name": "contentColor", 
+                            "$ref": "RGBA", 
+                            "optional": true, 
+                            "description": "The content box highlight fill color (default: transparent)." 
+                        },
+                        { 
+                            "name": "borderColor", 
+                            "$ref": "RGBA", 
+                            "optional": true, 
+                            "description": "The border highlight fill color (default: transparent)." 
+                        }
+                    ],
+                    "description": "Configuration data for the highlighting of page elements."
+                } 
             ]
         },
         {
@@ -155,6 +224,30 @@
                             "name": "inlineStyle",
                             "optional": true
                         }
+
+                    ]
+                },
+                {
+                    "description": "Applies specified style edits one after another in the given order.",
+                    "name": "setStyleTexts",
+                    "parameters": [
+                        {
+                            "items": {
+                                "$ref": "StyleDeclarationEdit"
+                            },
+                            "type": "array",
+                            "name": "edits"
+                        }
+                    ],
+                    "returns": [
+                        {
+                            "items": {
+                                "$ref": "CSSStyle"
+                            },
+                            "type": "array",
+                            "name": "styles",
+                            "description": "The resulting styles after modification."
+                        }
                     ]
                 }
             ],
@@ -177,6 +270,50 @@
                      "type": "string"
                 },
                 {
+                    "description": "Text range within a resource. All numbers are zero-based.",
+                    "type": "object",
+                    "id": "SourceRange",
+                    "properties": [
+                        {
+                            "type": "integer",
+                            "name": "startLine",
+                            "description": "Start line of range."
+                        },
+                        {
+                            "type": "integer",
+                            "name": "startColumn",
+                            "description": "Start column of range (inclusive)."
+                        },
+                        {
+                            "type": "integer",
+                            "name": "endLine",
+                            "description": "End line of range"
+                        },
+                        {
+                            "type": "integer",
+                            "name": "endColumn",
+                            "description": "End column of range (exclusive)."
+                        }
+                    ]
+                },
+                {
+                    "description": "A descriptor of operation to mutate style declaration text.",
+                    "type": "object",
+                    "id": "StyleDeclarationEdit",
+                    "properties": [
+                        {
+                            "description": "The css style sheet identifier (same as NodeId for UI DevTools).",
+                            "name": "styleSheetId",
+                            "$ref": "StyleSheetId"
+                        },
+                        {
+                            "type": "string",
+                            "name": "text",
+                            "description": "New style text."
+                        }
+                    ]
+                },
+                {
                     "id": "CSSProperty",
                     "properties": [
                         {
@@ -188,6 +325,12 @@
                             "description": "The property value.",
                             "name": "value",
                             "type": "string"
+                        },
+                        { 
+                            "name": "range", 
+                            "$ref": "SourceRange", 
+                            "optional": true, 
+                            "description": "The entire property range in the enclosing style declaration (if available)."  
                         }
                     ],
                     "type": "object"
@@ -217,6 +360,12 @@
                             },
                             "name": "shorthandEntries",
                             "type": "array"
+                        },
+                        {
+                            "description": "Style declaration range in the enclosing stylesheet (if available).",
+                            "optional": true,
+                            "name": "range",
+                            "$ref": "SourceRange"
                         }
                     ],
                     "type": "object"
diff --git a/components/update_client/component_patcher.cc b/components/update_client/component_patcher.cc
index cb18042..afafd874 100644
--- a/components/update_client/component_patcher.cc
+++ b/components/update_client/component_patcher.cc
@@ -34,7 +34,7 @@
   JSONFileValueDeserializer deserializer(commands);
   std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, NULL);
 
-  return (root.get() && root->IsType(base::Value::TYPE_LIST))
+  return (root.get() && root->IsType(base::Value::Type::LIST))
              ? static_cast<base::ListValue*>(root.release())
              : NULL;
 }
diff --git a/components/update_client/component_unpacker.cc b/components/update_client/component_unpacker.cc
index 24e9b482..6dd555cc 100644
--- a/components/update_client/component_unpacker.cc
+++ b/components/update_client/component_unpacker.cc
@@ -49,7 +49,7 @@
   std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
   if (!root.get())
     return std::unique_ptr<base::DictionaryValue>();
-  if (!root->IsType(base::Value::TYPE_DICTIONARY))
+  if (!root->IsType(base::Value::Type::DICTIONARY))
     return std::unique_ptr<base::DictionaryValue>();
   return std::unique_ptr<base::DictionaryValue>(
       static_cast<base::DictionaryValue*>(root.release()));
diff --git a/components/user_prefs/tracked/pref_hash_calculator_unittest.cc b/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
index 1b3ecd7..4d29146 100644
--- a/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
+++ b/components/user_prefs/tracked/pref_hash_calculator_unittest.cc
@@ -111,15 +111,15 @@
   list_value->AppendInteger(100);
   list_value->AppendDouble(1.0);
 
-  ASSERT_EQ(base::Value::TYPE_NULL, null_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_BOOLEAN, bool_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_INTEGER, int_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_DOUBLE, double_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_STRING, string_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, dict_value->GetType());
-  ASSERT_EQ(base::Value::TYPE_LIST, list_value->GetType());
+  ASSERT_EQ(base::Value::Type::NONE, null_value->GetType());
+  ASSERT_EQ(base::Value::Type::BOOLEAN, bool_value->GetType());
+  ASSERT_EQ(base::Value::Type::INTEGER, int_value->GetType());
+  ASSERT_EQ(base::Value::Type::DOUBLE, double_value->GetType());
+  ASSERT_EQ(base::Value::Type::STRING, string_value->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, dict_value->GetType());
+  ASSERT_EQ(base::Value::Type::LIST, list_value->GetType());
 
-  // Test every value type independently. Intentionally omits TYPE_BINARY which
+  // Test every value type independently. Intentionally omits Type::BINARY which
   // isn't even allowed in JSONWriter's input.
   static const char kExpectedNullValue[] =
       "82A9F3BBC7F9FF84C76B033C854E79EEB162783FA7B3E99FF9372FA8E12C44F7";
diff --git a/components/variations/android/BUILD.gn b/components/variations/android/BUILD.gn
index 641b584..6ad4936 100644
--- a/components/variations/android/BUILD.gn
+++ b/components/variations/android/BUILD.gn
@@ -7,7 +7,7 @@
 android_library("variations_java") {
   deps = [
     "//base:base_java",
-    "//third_party/android_tools:android_support_v4_java",
+    "//third_party/android_tools:android_support_core_utils_java",
   ]
   java_files = [
     "java/src/org/chromium/components/variations/VariationsAssociatedData.java",
diff --git a/components/web_restrictions/browser/web_restrictions_resource_throttle.cc b/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
index 6f83f08fae..1f78a4a 100644
--- a/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
+++ b/components/web_restrictions/browser/web_restrictions_resource_throttle.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "components/web_restrictions/browser/web_restrictions_client.h"
-#include "content/public/browser/resource_controller.h"
 #include "net/base/net_errors.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request.h"
@@ -53,9 +52,9 @@
 
 void WebRestrictionsResourceThrottle::OnCheckResult(const bool should_proceed) {
   if (should_proceed) {
-    controller()->Resume();
+    Resume();
   } else {
-    controller()->CancelWithError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
+    CancelWithError(net::ERR_BLOCKED_BY_ADMINISTRATOR);
   }
 }
 
diff --git a/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc b/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
index d3b54b8..f92951c 100644
--- a/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
+++ b/components/web_restrictions/browser/web_restrictions_resource_throttle_unittest.cc
@@ -7,7 +7,7 @@
 #include "components/web_restrictions/browser/mock_web_restrictions_client.h"
 #include "components/web_restrictions/browser/web_restrictions_client.h"
 #include "components/web_restrictions/browser/web_restrictions_resource_throttle.h"
-#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/resource_throttle.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/net_errors.h"
 #include "net/url_request/redirect_info.h"
@@ -17,9 +17,10 @@
 
 namespace {
 
-class TestResourceController : public content::ResourceController {
+class TestResourceThrottleDelegate
+    : public content::ResourceThrottle::Delegate {
  public:
-  TestResourceController(const base::Closure& quit_closure)
+  TestResourceThrottleDelegate(const base::Closure& quit_closure)
       : resume_called_(false),
         cancel_with_error_called_(false),
         error_code_(0),
@@ -56,8 +57,8 @@
  protected:
   WebRestrictionsResourceThrottleTest()
       : throttle_(&provider_, GURL("http://example.com"), true),
-        controller_(run_loop_.QuitClosure()) {
-    throttle_.set_controller_for_testing(&controller_);
+        delegate_(run_loop_.QuitClosure()) {
+    throttle_.set_delegate_for_testing(&delegate_);
   }
 
   void SetAuthority(std::string authority) {
@@ -78,7 +79,7 @@
   WebRestrictionsClient provider_;
   WebRestrictionsResourceThrottle throttle_;
   base::RunLoop run_loop_;
-  TestResourceController controller_;
+  TestResourceThrottleDelegate delegate_;
 };
 
 TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_NoAuthority) {
@@ -98,8 +99,8 @@
   throttle_.WillStartRequest(&defer);
   EXPECT_TRUE(defer);
   run_loop_.Run();
-  EXPECT_TRUE(controller_.ResumeCalled());
-  EXPECT_FALSE(controller_.CancelWithErrorCalled());
+  EXPECT_TRUE(delegate_.ResumeCalled());
+  EXPECT_FALSE(delegate_.CancelWithErrorCalled());
 }
 
 TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_DeferredForbid) {
@@ -108,14 +109,14 @@
   throttle_.WillStartRequest(&defer);
   EXPECT_TRUE(defer);
   run_loop_.Run();
-  EXPECT_FALSE(controller_.ResumeCalled());
-  EXPECT_TRUE(controller_.CancelWithErrorCalled());
-  EXPECT_EQ(net::ERR_BLOCKED_BY_ADMINISTRATOR, controller_.GetErrorCode());
+  EXPECT_FALSE(delegate_.ResumeCalled());
+  EXPECT_TRUE(delegate_.CancelWithErrorCalled());
+  EXPECT_EQ(net::ERR_BLOCKED_BY_ADMINISTRATOR, delegate_.GetErrorCode());
 }
 
 TEST_F(WebRestrictionsResourceThrottleTest, WillStartRequest_Subresource) {
   // Only the main frame should be deferred.
-  // Initialization of the controller is asynchronous, and this will only work
+  // Initialization of the delegate is asynchronous, and this will only work
   // correctly if the provider is initialized. Run a main frame through this
   // first to ensure that everything is initialized.
   StartProvider();
@@ -123,8 +124,8 @@
   WebRestrictionsResourceThrottle throttle(
       &provider_, GURL("http://example.com/sub"), false);
   base::RunLoop test_run_loop;
-  TestResourceController test_controller(test_run_loop.QuitClosure());
-  throttle.set_controller_for_testing(&test_controller);
+  TestResourceThrottleDelegate test_delegate(test_run_loop.QuitClosure());
+  throttle.set_delegate_for_testing(&test_delegate);
   bool defer;
   throttle.WillStartRequest(&defer);
   ASSERT_FALSE(defer);
@@ -148,8 +149,8 @@
   net::RedirectInfo redirect;
   redirect.new_url = GURL("http://example.com/2");
   base::RunLoop test_run_loop;
-  TestResourceController test_controller(test_run_loop.QuitClosure());
-  throttle_.set_controller_for_testing(&test_controller);
+  TestResourceThrottleDelegate test_delegate(test_run_loop.QuitClosure());
+  throttle_.set_delegate_for_testing(&test_delegate);
   bool defer;
   throttle_.WillRedirectRequest(redirect, &defer);
   ASSERT_TRUE(defer);
diff --git a/components/webcrypto/algorithms/test_helpers.cc b/components/webcrypto/algorithms/test_helpers.cc
index 8751735..8138f0d 100644
--- a/components/webcrypto/algorithms/test_helpers.cc
+++ b/components/webcrypto/algorithms/test_helpers.cc
@@ -391,7 +391,7 @@
                                 json.size());
   std::unique_ptr<base::Value> value = base::JSONReader::Read(json_string);
   EXPECT_TRUE(value.get());
-  EXPECT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  EXPECT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
 
   return std::unique_ptr<base::DictionaryValue>(
       static_cast<base::DictionaryValue*>(value.release()));
diff --git a/content/BUILD.gn b/content/BUILD.gn
index e0124da..53f6492 100644
--- a/content/BUILD.gn
+++ b/content/BUILD.gn
@@ -94,11 +94,10 @@
     ]
     set_sources_assignment_filter(sources_assignment_filter)
 
-    defines = [ "USE_SECCOMP_BPF=1" ]
-
     deps = [
       "//base",
       "//media:media_features",
+      "//sandbox:sandbox_features",
       "//sandbox/linux:sandbox",
     ]
   }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 94e8681b..3741c6b7 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -46,6 +46,7 @@
     "//components/link_header_util",
     "//components/mime_util",
     "//components/payments:payment_app",
+    "//components/rappor",
     "//components/tracing",
     "//components/tracing:startup_tracing",
     "//components/url_formatter",
@@ -85,6 +86,7 @@
     "//gpu",
     "//gpu/command_buffer/client:gles2_implementation",
     "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/ipc/host",
     "//ipc",
     "//ipc:mojom",
     "//media",
@@ -270,6 +272,10 @@
     "appcache/appcache_internals_ui.h",
     "appcache/appcache_manifest_parser.cc",
     "appcache/appcache_manifest_parser.h",
+    "appcache/appcache_navigation_handle.cc",
+    "appcache/appcache_navigation_handle.h",
+    "appcache/appcache_navigation_handle_core.cc",
+    "appcache/appcache_navigation_handle_core.h",
     "appcache/appcache_policy.h",
     "appcache/appcache_quota_client.cc",
     "appcache/appcache_quota_client.h",
@@ -786,6 +792,7 @@
     "loader/redirect_to_file_resource_handler.h",
     "loader/resource_buffer.cc",
     "loader/resource_buffer.h",
+    "loader/resource_controller.h",
     "loader/resource_dispatcher_host_impl.cc",
     "loader/resource_dispatcher_host_impl.h",
     "loader/resource_handler.cc",
@@ -1293,27 +1300,11 @@
     "shared_worker/worker_storage_partition.h",
     "site_instance_impl.cc",
     "site_instance_impl.h",
-    "speech/audio_buffer.cc",
-    "speech/audio_buffer.h",
-    "speech/audio_encoder.cc",
-    "speech/audio_encoder.h",
-    "speech/chunked_byte_buffer.cc",
-    "speech/chunked_byte_buffer.h",
-    "speech/endpointer/endpointer.cc",
-    "speech/endpointer/endpointer.h",
-    "speech/endpointer/energy_endpointer.cc",
-    "speech/endpointer/energy_endpointer.h",
-    "speech/endpointer/energy_endpointer_params.cc",
-    "speech/endpointer/energy_endpointer_params.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
-    "speech/speech_recognition_engine.cc",
-    "speech/speech_recognition_engine.h",
     "speech/speech_recognition_manager_impl.cc",
     "speech/speech_recognition_manager_impl.h",
     "speech/speech_recognizer.h",
-    "speech/speech_recognizer_impl.cc",
-    "speech/speech_recognizer_impl.h",
     "speech/speech_recognizer_impl_android.cc",
     "speech/speech_recognizer_impl_android.h",
     "ssl/ssl_client_auth_handler.cc",
@@ -1495,7 +1486,7 @@
       "//jingle:jingle_glue",
       "//third_party/libjingle/webrtc:libjingle_webrtc",
       "//third_party/webrtc/base:rtc_base",
-      "//third_party/webrtc/media:rtc_media",
+      "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/desktop_capture:primitives",
     ]
     if (!is_ios) {
@@ -1764,8 +1755,22 @@
       "media/session/audio_focus_manager.h",
       "tracing/tracing_ui.cc",
       "tracing/tracing_ui.h",
-
-      # Android skips most, but not all, of the speech code.
+    ]
+    deps -= [ "//device/battery" ]
+    deps += [
+      "//content/public/android:jni",
+      "//media",
+      "//media/capture/content/android",
+      "//media/capture/video/android",
+      "//mojo/android:libsystem_java",
+      "//ui/android",
+    ]
+    defines += [ "APPCACHE_USE_SIMPLE_CACHE" ]
+    libs += [ "jnigraphics" ]
+  } else {
+    # Not Android.
+    sources += [
+      # Most speech code is non-Android.
       "speech/audio_buffer.cc",
       "speech/audio_buffer.h",
       "speech/audio_encoder.cc",
@@ -1783,17 +1788,7 @@
       "speech/speech_recognizer_impl.cc",
       "speech/speech_recognizer_impl.h",
     ]
-    deps -= [ "//device/battery" ]
-    deps += [
-      "//content/public/android:jni",
-      "//media",
-      "//media/capture/content/android",
-      "//media/capture/video/android",
-      "//mojo/android:libsystem_java",
-      "//ui/android",
-    ]
-    defines += [ "APPCACHE_USE_SIMPLE_CACHE" ]
-    libs += [ "jnigraphics" ]
+    deps += [ "//third_party/flac" ]
   }
 
   if (is_mac) {
@@ -1937,10 +1932,6 @@
     deps += [ "//ui/compositor" ]
   }
 
-  if (enable_web_speech) {
-    deps += [ "//third_party/flac" ]
-  }
-
   if (is_linux && use_dbus) {
     deps += [ "//dbus" ]
   }
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 27079e1..5757ad3 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -9,6 +9,7 @@
   "+components/mime_util",
   "+components/payments",
   "+components/profile_service",
+  "+components/rappor/public",
   "+components/scheduler/common",
   "+components/tracing",
   "+components/url_formatter",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index a0d782e..75495b0 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -349,7 +349,7 @@
       continue;
 
     switch (value->GetType()) {
-      case base::Value::TYPE_STRING: {
+      case base::Value::Type::STRING: {
         base::string16 string_value;
         value->GetAsString(&string_value);
         WriteAttribute(false,
@@ -359,7 +359,7 @@
                        &line);
         break;
       }
-      case base::Value::TYPE_INTEGER: {
+      case base::Value::Type::INTEGER: {
         int int_value = 0;
         value->GetAsInteger(&int_value);
         WriteAttribute(false,
@@ -370,7 +370,7 @@
                        &line);
         break;
       }
-      case base::Value::TYPE_DOUBLE: {
+      case base::Value::Type::DOUBLE: {
         double double_value = 0.0;
         value->GetAsDouble(&double_value);
         WriteAttribute(false,
@@ -381,7 +381,7 @@
                        &line);
         break;
       }
-      case base::Value::TYPE_LIST: {
+      case base::Value::Type::LIST: {
         // Currently all list values are string and are written without
         // attribute names.
         const base::ListValue* list_value;
@@ -395,7 +395,7 @@
         }
         break;
       }
-      case base::Value::TYPE_DICTIONARY: {
+      case base::Value::Type::DICTIONARY: {
         // Currently all dictionary values are coordinates.
         // Revisit this if that changes.
         const base::DictionaryValue* dict_value;
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 93e95b3..ca16252b 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "cc/layers/layer.h"
@@ -888,6 +889,7 @@
     const JavaParamRef<jobject>& obj,
     jint orientation) {
   if (device_orientation_ != orientation) {
+    base::RecordAction(base::UserMetricsAction("ScreenOrientationChange"));
     device_orientation_ = orientation;
     SendOrientationChangeEventInternal();
   }
diff --git a/content/browser/android/java/gin_java_method_invocation_helper.cc b/content/browser/android/java/gin_java_method_invocation_helper.cc
index a2e73ef2..e6b90a7e0 100644
--- a/content/browser/android/java/gin_java_method_invocation_helper.cc
+++ b/content/browser/android/java/gin_java_method_invocation_helper.cc
@@ -52,15 +52,15 @@
 void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
     DispatcherDelegate* dispatcher,
     const base::Value& list_value) {
-  DCHECK(list_value.IsType(base::Value::TYPE_LIST));
+  DCHECK(list_value.IsType(base::Value::Type::LIST));
   const base::ListValue* list;
   list_value.GetAsList(&list);
   for (const auto& entry : *list) {
     if (AppendObjectRef(dispatcher, *entry))
       continue;
-    if (entry->IsType(base::Value::TYPE_LIST)) {
+    if (entry->IsType(base::Value::Type::LIST)) {
       BuildObjectRefsFromListValue(dispatcher, *entry);
-    } else if (entry->IsType(base::Value::TYPE_DICTIONARY)) {
+    } else if (entry->IsType(base::Value::Type::DICTIONARY)) {
       BuildObjectRefsFromDictionaryValue(dispatcher, *entry);
     }
   }
@@ -69,7 +69,7 @@
 void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
     DispatcherDelegate* dispatcher,
     const base::Value& dict_value) {
-  DCHECK(dict_value.IsType(base::Value::TYPE_DICTIONARY));
+  DCHECK(dict_value.IsType(base::Value::Type::DICTIONARY));
   const base::DictionaryValue* dict;
   dict_value.GetAsDictionary(&dict);
   for (base::DictionaryValue::Iterator iter(*dict);
@@ -77,9 +77,9 @@
        iter.Advance()) {
     if (AppendObjectRef(dispatcher, iter.value()))
       continue;
-    if (iter.value().IsType(base::Value::TYPE_LIST)) {
+    if (iter.value().IsType(base::Value::Type::LIST)) {
       BuildObjectRefsFromListValue(dispatcher, iter.value());
-    } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    } else if (iter.value().IsType(base::Value::Type::DICTIONARY)) {
       BuildObjectRefsFromDictionaryValue(dispatcher, iter.value());
     }
   }
diff --git a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
index 2af118d..afc7143 100644
--- a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
+++ b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
@@ -515,13 +515,13 @@
   // If the length property does not have numeric type, or is outside the valid
   // range for a Java array length, return null.
   jsize length = -1;
-  if (length_value->IsType(base::Value::TYPE_INTEGER)) {
+  if (length_value->IsType(base::Value::Type::INTEGER)) {
     int int_length;
     length_value->GetAsInteger(&int_length);
     if (int_length >= 0 && int_length <= std::numeric_limits<int32_t>::max()) {
       length = static_cast<jsize>(int_length);
     }
-  } else if (length_value->IsType(base::Value::TYPE_DOUBLE)) {
+  } else if (length_value->IsType(base::Value::Type::DOUBLE)) {
     double double_length;
     length_value->GetAsDouble(&double_length);
     if (double_length >= 0.0 &&
@@ -629,10 +629,10 @@
       result.z = JNI_FALSE;
       break;
     case JavaType::TypeArray:
-      if (value->IsType(base::Value::TYPE_DICTIONARY)) {
+      if (value->IsType(base::Value::Type::DICTIONARY)) {
         result.l = CoerceJavaScriptDictionaryToArray(
             env, value, target_type, object_refs, error);
-      } else if (value->IsType(base::Value::TYPE_LIST)) {
+      } else if (value->IsType(base::Value::Type::LIST)) {
         result.l = CoerceJavaScriptListToArray(
             env, value, target_type, object_refs, error);
       } else {
@@ -698,28 +698,28 @@
   // always be explicitly set, as jvalue does not initialize its fields.
 
   switch (value->GetType()) {
-    case base::Value::TYPE_INTEGER:
+    case base::Value::Type::INTEGER:
       return CoerceJavaScriptIntegerToJavaValue(
           env, value, target_type, coerce_to_string, error);
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value;
       value->GetAsDouble(&double_value);
       return CoerceJavaScriptDoubleToJavaValue(
           env, double_value, target_type, coerce_to_string, error);
     }
-    case base::Value::TYPE_BOOLEAN:
+    case base::Value::Type::BOOLEAN:
       return CoerceJavaScriptBooleanToJavaValue(
           env, value, target_type, coerce_to_string, error);
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::STRING:
       return CoerceJavaScriptStringToJavaValue(env, value, target_type, error);
-    case base::Value::TYPE_DICTIONARY:
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::DICTIONARY:
+    case base::Value::Type::LIST:
       return CoerceJavaScriptObjectToJavaValue(
           env, value, target_type, coerce_to_string, object_refs, error);
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return CoerceJavaScriptNullOrUndefinedToJavaValue(
           env, value, target_type, coerce_to_string, error);
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       return CoerceGinJavaBridgeValueToJavaValue(
           env, value, target_type, coerce_to_string, object_refs, error);
   }
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
index 7f29c3e..c079515 100644
--- a/content/browser/appcache/appcache_backend_impl.cc
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -8,6 +8,8 @@
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_service_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 
 namespace content {
 
@@ -166,4 +168,17 @@
   found->second = std::move(host);
 }
 
+void AppCacheBackendImpl::RegisterPrecreatedHost(
+    std::unique_ptr<AppCacheHost> host) {
+  DCHECK(IsBrowserSideNavigationEnabled());
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  DCHECK(host.get());
+  DCHECK(hosts_.find(host->host_id()) == hosts_.end());
+  // Switch the frontend proxy so that the host can make IPC calls from
+  // here on.
+  host->set_frontend(frontend_);
+  hosts_[host->host_id()] = std::move(host);
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index e7f9b84..b2aad33 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -66,6 +66,12 @@
   std::unique_ptr<AppCacheHost> TransferHostOut(int host_id);
   void TransferHostIn(int new_host_id, std::unique_ptr<AppCacheHost> host);
 
+  // PlzNaviate
+  // The AppCacheHost is precreated by the AppCacheNavigationHandleCore class
+  // when a navigation is initiated. We register the host with the backend in
+  // this function and ignore registrations for this host id from the renderer.
+  void RegisterPrecreatedHost(std::unique_ptr<AppCacheHost> host);
+
  private:
   AppCacheServiceImpl* service_;
   AppCacheFrontend* frontend_;
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc
index 2eb90dd..d8591f93 100644
--- a/content/browser/appcache/appcache_dispatcher_host.cc
+++ b/content/browser/appcache/appcache_dispatcher_host.cc
@@ -4,12 +4,15 @@
 
 #include "content/browser/appcache/appcache_dispatcher_host.h"
 
+#include <map>
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/bad_message.h"
 #include "content/common/appcache_messages.h"
 #include "content/public/browser/user_metrics.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 
 namespace content {
 
@@ -24,19 +27,17 @@
 }
 
 void AppCacheDispatcherHost::OnChannelConnected(int32_t peer_pid) {
-  if (appcache_service_.get()) {
-    backend_impl_.Initialize(
-        appcache_service_.get(), &frontend_proxy_, process_id_);
-    get_status_callback_ =
-        base::Bind(&AppCacheDispatcherHost::GetStatusCallback,
-                    weak_factory_.GetWeakPtr());
-    start_update_callback_ =
-        base::Bind(&AppCacheDispatcherHost::StartUpdateCallback,
-                    weak_factory_.GetWeakPtr());
-    swap_cache_callback_ =
-        base::Bind(&AppCacheDispatcherHost::SwapCacheCallback,
-                    weak_factory_.GetWeakPtr());
-  }
+  if (!appcache_service_.get())
+    return;
+
+  backend_impl_.Initialize(appcache_service_.get(), &frontend_proxy_,
+                           process_id_);
+  get_status_callback_ = base::Bind(&AppCacheDispatcherHost::GetStatusCallback,
+                                    weak_factory_.GetWeakPtr());
+  start_update_callback_ = base::Bind(
+      &AppCacheDispatcherHost::StartUpdateCallback, weak_factory_.GetWeakPtr());
+  swap_cache_callback_ = base::Bind(&AppCacheDispatcherHost::SwapCacheCallback,
+                                    weak_factory_.GetWeakPtr());
 }
 
 bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& message) {
@@ -62,10 +63,22 @@
   return handled;
 }
 
-AppCacheDispatcherHost::~AppCacheDispatcherHost() {}
+AppCacheDispatcherHost::~AppCacheDispatcherHost() {
+}
 
 void AppCacheDispatcherHost::OnRegisterHost(int host_id) {
   if (appcache_service_.get()) {
+    // PlzNavigate
+    // The AppCacheHost could have been precreated in which case we want to
+    // register it with the backend here.
+    if (IsBrowserSideNavigationEnabled()) {
+      std::unique_ptr<AppCacheHost> host =
+          AppCacheNavigationHandleCore::GetPrecreatedHost(host_id);
+      if (host.get()) {
+        backend_impl_.RegisterPrecreatedHost(std::move(host));
+        return;
+      }
+    }
     if (!backend_impl_.RegisterHost(host_id)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_REGISTER);
     }
@@ -94,8 +107,7 @@
     int64_t cache_document_was_loaded_from,
     const GURL& opt_manifest_url) {
   if (appcache_service_.get()) {
-    if (!backend_impl_.SelectCache(host_id,
-                                   document_url,
+    if (!backend_impl_.SelectCache(host_id, document_url,
                                    cache_document_was_loaded_from,
                                    opt_manifest_url)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_SELECT_CACHE);
@@ -108,8 +120,8 @@
 void AppCacheDispatcherHost::OnSelectCacheForWorker(
     int host_id, int parent_process_id, int parent_host_id) {
   if (appcache_service_.get()) {
-    if (!backend_impl_.SelectCacheForWorker(
-            host_id, parent_process_id, parent_host_id)) {
+    if (!backend_impl_.SelectCacheForWorker(host_id, parent_process_id,
+                                            parent_host_id)) {
       bad_message::ReceivedBadMessage(
           this, bad_message::ACDH_SELECT_CACHE_FOR_WORKER);
     }
@@ -134,8 +146,8 @@
     const GURL& document_url,
     int64_t cache_document_was_loaded_from) {
   if (appcache_service_.get()) {
-    if (!backend_impl_.MarkAsForeignEntry(
-            host_id, document_url, cache_document_was_loaded_from)) {
+    if (!backend_impl_.MarkAsForeignEntry(host_id, document_url,
+                                          cache_document_was_loaded_from)) {
       bad_message::ReceivedBadMessage(this,
                                       bad_message::ACDH_MARK_AS_FOREIGN_ENTRY);
     }
@@ -158,8 +170,8 @@
 
   pending_reply_msg_.reset(reply_msg);
   if (appcache_service_.get()) {
-    if (!backend_impl_.GetStatusWithCallback(
-            host_id, get_status_callback_, reply_msg)) {
+    if (!backend_impl_.GetStatusWithCallback(host_id, get_status_callback_,
+                                             reply_msg)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_GET_STATUS);
     }
     return;
@@ -179,8 +191,8 @@
 
   pending_reply_msg_.reset(reply_msg);
   if (appcache_service_.get()) {
-    if (!backend_impl_.StartUpdateWithCallback(
-            host_id, start_update_callback_, reply_msg)) {
+    if (!backend_impl_.StartUpdateWithCallback(host_id, start_update_callback_,
+                                               reply_msg)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_START_UPDATE);
     }
     return;
@@ -199,8 +211,8 @@
 
   pending_reply_msg_.reset(reply_msg);
   if (appcache_service_.get()) {
-    if (!backend_impl_.SwapCacheWithCallback(
-            host_id, swap_cache_callback_, reply_msg)) {
+    if (!backend_impl_.SwapCacheWithCallback(host_id, swap_cache_callback_,
+                                             reply_msg)) {
       bad_message::ReceivedBadMessage(this, bad_message::ACDH_SWAP_CACHE);
     }
     return;
diff --git a/content/browser/appcache/appcache_dispatcher_host.h b/content/browser/appcache/appcache_dispatcher_host.h
index 9b882c8..0b4b16e 100644
--- a/content/browser/appcache/appcache_dispatcher_host.h
+++ b/content/browser/appcache/appcache_dispatcher_host.h
@@ -62,7 +62,6 @@
   void StartUpdateCallback(bool result, void* param);
   void SwapCacheCallback(bool result, void* param);
 
-
   scoped_refptr<ChromeAppCacheService> appcache_service_;
   AppCacheFrontendProxy frontend_proxy_;
   AppCacheBackendImpl backend_impl_;
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index c5103c2d..b872494 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -163,9 +163,17 @@
   }
 
   int host_id() const { return host_id_; }
+
   AppCacheServiceImpl* service() const { return service_; }
   AppCacheStorage* storage() const { return storage_; }
   AppCacheFrontend* frontend() const { return frontend_; }
+
+  // PlzNavigate:
+  // The AppCacheHost instance is created with a dummy AppCacheFrontend
+  // pointer when the navigation starts. We need to switch it to the
+  // actual frontend when the navigation commits.
+  void set_frontend(AppCacheFrontend* frontend) { frontend_ = frontend; }
+
   AppCache* associated_cache() const { return associated_cache_.get(); }
 
   void enable_cache_selection(bool enable) {
diff --git a/content/browser/appcache/appcache_interceptor.cc b/content/browser/appcache/appcache_interceptor.cc
index acc98c0..8b91609 100644
--- a/content/browser/appcache/appcache_interceptor.cc
+++ b/content/browser/appcache/appcache_interceptor.cc
@@ -32,13 +32,12 @@
       request->GetUserData(&kHandlerKey));
 }
 
-void AppCacheInterceptor::SetExtraRequestInfo(
-    net::URLRequest* request,
-    AppCacheServiceImpl* service,
-    int process_id,
-    int host_id,
-    ResourceType resource_type,
-    bool should_reset_appcache) {
+void AppCacheInterceptor::SetExtraRequestInfo(net::URLRequest* request,
+                                              AppCacheServiceImpl* service,
+                                              int process_id,
+                                              int host_id,
+                                              ResourceType resource_type,
+                                              bool should_reset_appcache) {
   if (!service || (host_id == kAppCacheNoHostId))
     return;
 
@@ -52,6 +51,15 @@
   if (!host)
     return;
 
+  SetExtraRequestInfoForHost(request, host, resource_type,
+                             should_reset_appcache);
+}
+
+void AppCacheInterceptor::SetExtraRequestInfoForHost(
+    net::URLRequest* request,
+    AppCacheHost* host,
+    ResourceType resource_type,
+    bool should_reset_appcache) {
   // Create a handler for this request and associate it with the request.
   AppCacheRequestHandler* handler =
       host->CreateRequestHandler(request, resource_type, should_reset_appcache);
diff --git a/content/browser/appcache/appcache_interceptor.h b/content/browser/appcache/appcache_interceptor.h
index 40581f2..a650add 100644
--- a/content/browser/appcache/appcache_interceptor.h
+++ b/content/browser/appcache/appcache_interceptor.h
@@ -19,6 +19,7 @@
 }
 
 namespace content {
+class AppCacheHost;
 class AppCacheRequestHandler;
 class AppCacheServiceImpl;
 class ResourceRequesterInfo;
@@ -35,6 +36,13 @@
                                   ResourceType resource_type,
                                   bool should_reset_appcache);
 
+  // PlzNavigate
+  // Must be called to make a request eligible for retrieval from an appcache.
+  static void SetExtraRequestInfoForHost(net::URLRequest* request,
+                                         AppCacheHost* host,
+                                         ResourceType resource_type,
+                                         bool should_reset_appcache);
+
   // May be called after response headers are complete to retrieve extra
   // info about the response.
   static void GetExtraResponseInfo(net::URLRequest* request,
diff --git a/content/browser/appcache/appcache_navigation_handle.cc b/content/browser/appcache/appcache_navigation_handle.cc
new file mode 100644
index 0000000..a5de291
--- /dev/null
+++ b/content/browser/appcache/appcache_navigation_handle.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/appcache/appcache_navigation_handle.h"
+
+#include "base/bind.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
+#include "content/browser/appcache/chrome_appcache_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/appcache_info.h"
+
+namespace {
+// PlzNavigate: Used to generate the host id for a navigation initiated by the
+// browser. Starts at -2 and keeps going down.
+static int g_next_appcache_host_id = -1;
+}
+
+namespace content {
+
+AppCacheNavigationHandle::AppCacheNavigationHandle(
+    ChromeAppCacheService* appcache_service)
+    : appcache_host_id_(kAppCacheNoHostId),
+      core_(nullptr),
+      weak_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  appcache_host_id_ = g_next_appcache_host_id--;
+  core_.reset(new AppCacheNavigationHandleCore(
+      weak_factory_.GetWeakPtr(), appcache_service, appcache_host_id_));
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::Bind(&AppCacheNavigationHandleCore::Initialize,
+                                     base::Unretained(core_.get())));
+}
+
+AppCacheNavigationHandle::~AppCacheNavigationHandle() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Delete the AppCacheNavigationHandleCore on the IO thread.
+  BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, core_.release());
+}
+
+}  // namespace content
diff --git a/content/browser/appcache/appcache_navigation_handle.h b/content/browser/appcache/appcache_navigation_handle.h
new file mode 100644
index 0000000..535849a
--- /dev/null
+++ b/content/browser/appcache/appcache_navigation_handle.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_H_
+
+#include <memory>
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace content {
+
+class AppCacheNavigationHandleCore;
+class ChromeAppCacheService;
+
+// This class is used to manage the lifetime of AppCacheHosts created during
+// navigation. This is a UI thread class, with a pendant class on the IO
+// thread, the AppCacheNavigationHandleCore.
+//
+// The lifetime of the AppCacheNavigationHandle, the
+// AppCacheNavigationHandleCore and the AppCacheHost are the following :
+//   1) We create a AppCacheNavigationHandle on the UI thread with a
+//      app cache host id of -1. This also leads to the creation of a
+//      AppCacheNavigationHandleCore with an id of -1. Every time
+//      an AppCacheNavigationHandle instance is created the global host id is
+//      decremented by 1.
+//
+//   2) When the navigation request is sent to the IO thread, we include a
+//      pointer to the AppCacheNavigationHandleCore.
+//
+//   3. The AppCacheHost instance is created and its ownership is passed to the
+//      AppCacheNavigationHandleCore instance. Now the app cache host id is
+//      updated.
+//
+//   4) The AppCacheNavigationHandleCore instance informs the
+//      AppCacheNavigationHandle instance on the UI thread that the app cache
+//      host id was updated.
+//
+//   5) When the navigation is ready to commit, the NavigationRequest will
+//      update the RequestNavigationParams based on the id from the
+//      AppCacheNavigationHandle.
+//
+//   6. The commit leads to AppCache registrations happening from the renderer.
+//      This is via the IPC message AppCacheHostMsg_RegisterHost. The
+//      AppCacheDispatcherHost class which handles these IPCs will be informed
+//      about these hosts when the navigation commits. It will ignore the
+//      host registrations as they have already been registered. The
+//      ownership of the AppCacheHost is passed from the
+//      AppCacheNavigationHandle core to the AppCacheBackend.
+
+//   7) When the navigation finishes, the AppCacheNavigationHandle is
+//      destroyed. The destructor of the AppCacheNavigationHandle posts a
+//      task to destroy the AppacheNavigationHandleCore on the IO thread.
+
+class AppCacheNavigationHandle {
+ public:
+  AppCacheNavigationHandle(ChromeAppCacheService* appcache_service);
+  ~AppCacheNavigationHandle();
+
+  int appcache_host_id() const { return appcache_host_id_; }
+  AppCacheNavigationHandleCore* core() const { return core_.get(); }
+
+  // Called when a navigation is committed. The |process_id| parameter is
+  // is the process id of the renderer.
+  void CommitNavigation(int process_id);
+
+ private:
+  int appcache_host_id_;
+  std::unique_ptr<AppCacheNavigationHandleCore> core_;
+  base::WeakPtrFactory<AppCacheNavigationHandle> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCacheNavigationHandle);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_H_
diff --git a/content/browser/appcache/appcache_navigation_handle_core.cc b/content/browser/appcache/appcache_navigation_handle_core.cc
new file mode 100644
index 0000000..8cfdaa4
--- /dev/null
+++ b/content/browser/appcache/appcache_navigation_handle_core.cc
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
+
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_navigation_handle.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/chrome_appcache_service.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// Map of AppCache host id to the AppCacheNavigationHandleCore instance.
+// Accessed on the IO thread only.
+using AppCacheHandleMap =
+    std::map <int, content::AppCacheNavigationHandleCore*>;
+base::LazyInstance<AppCacheHandleMap> g_appcache_handle_map;
+
+}  // namespace
+
+namespace content {
+
+
+AppCacheNavigationHandleCore::AppCacheNavigationHandleCore(
+    base::WeakPtr<AppCacheNavigationHandle> ui_handle,
+    ChromeAppCacheService* appcache_service,
+    int appcache_host_id)
+    : appcache_service_(appcache_service),
+      appcache_host_id_(appcache_host_id),
+      ui_handle_(ui_handle) {
+  // The AppCacheNavigationHandleCore is created on the UI thread but
+  // should only be accessed from the IO thread afterwards.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+AppCacheNavigationHandleCore::~AppCacheNavigationHandleCore() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  precreated_host_.reset(nullptr);
+  g_appcache_handle_map.Get().erase(appcache_host_id_);
+}
+
+void AppCacheNavigationHandleCore::Initialize() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(precreated_host_.get() == nullptr);
+  precreated_host_.reset(
+      new AppCacheHost(appcache_host_id_, this, GetAppCacheService()));
+
+  DCHECK(g_appcache_handle_map.Get().find(appcache_host_id_) ==
+         g_appcache_handle_map.Get().end());
+  g_appcache_handle_map.Get()[appcache_host_id_] = this;
+}
+
+// static
+std::unique_ptr<AppCacheHost> AppCacheNavigationHandleCore::GetPrecreatedHost(
+    int host_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  auto index = g_appcache_handle_map.Get().find(host_id);
+  if (index != g_appcache_handle_map.Get().end()) {
+    AppCacheNavigationHandleCore* instance = index->second;
+    DCHECK(instance);
+    return std::move(instance->precreated_host_);
+  }
+  return std::unique_ptr<AppCacheHost>();
+}
+
+AppCacheServiceImpl* AppCacheNavigationHandleCore::GetAppCacheService() {
+  return static_cast<AppCacheServiceImpl*>(appcache_service_.get());
+}
+
+void AppCacheNavigationHandleCore::OnCacheSelected(int host_id,
+                                                   const AppCacheInfo& info) {
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnStatusChanged(
+    const std::vector<int>& host_ids,
+    AppCacheStatus status) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnEventRaised(
+    const std::vector<int>& host_ids,
+    AppCacheEventID event_id) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnProgressEventRaised(
+    const std::vector<int>& host_ids,
+    const GURL& url,
+    int num_total,
+    int num_complete) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnErrorEventRaised(
+    const std::vector<int>& host_ids,
+    const AppCacheErrorDetails& details) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnLogMessage(int host_id,
+                                                AppCacheLogLevel log_level,
+                                                const std::string& message) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+void AppCacheNavigationHandleCore::OnContentBlocked(int host_id,
+                                                    const GURL& manifest_url) {
+  // Should never be called.
+  DCHECK(false);
+}
+
+}  // namespace content
diff --git a/content/browser/appcache/appcache_navigation_handle_core.h b/content/browser/appcache/appcache_navigation_handle_core.h
new file mode 100644
index 0000000..fa9497a
--- /dev/null
+++ b/content/browser/appcache/appcache_navigation_handle_core.h
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_CORE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_CORE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/appcache_interfaces.h"
+
+namespace content {
+
+class AppCacheNavigationHandle;
+class AppCacheHost;
+class AppCacheServiceImpl;
+class ChromeAppCacheService;
+
+// PlzNavigate
+// This class is used to manage the lifetime of AppCacheHosts
+// created during navigations. This class is created on the UI thread, but
+// should only be accessed from the IO thread afterwards. It is the IO thread
+// pendant of AppCacheNavigationHandle. See the
+// AppCacheNavigationHandle header for more details about the lifetime of
+// both classes.
+class AppCacheNavigationHandleCore : public AppCacheFrontend {
+ public:
+  AppCacheNavigationHandleCore(
+      base::WeakPtr<AppCacheNavigationHandle> ui_handle,
+      ChromeAppCacheService* appcache_service,
+      int appcache_host_id);
+  ~AppCacheNavigationHandleCore() override;
+
+  // Returns the raw AppCacheHost pointer. Ownership remains with this class.
+  AppCacheHost* host() { return precreated_host_.get(); }
+
+  // Initializes this instance. Should be called on the IO thread.
+  void Initialize();
+
+  // Returns the precreated AppCacheHost pointer. Ownership of the host is
+  // released here.
+  static std::unique_ptr<AppCacheHost> GetPrecreatedHost(int host_id);
+
+  AppCacheServiceImpl* GetAppCacheService();
+
+ protected:
+  // AppCacheFrontend methods
+  // We don't expect calls on the AppCacheFrontend methods while the
+  // AppCacheHost is not registered with the AppCacheBackend.
+  void OnCacheSelected(int host_id, const AppCacheInfo& info) override;
+  void OnStatusChanged(const std::vector<int>& host_ids,
+                       AppCacheStatus status) override;
+  void OnEventRaised(const std::vector<int>& host_ids,
+                     AppCacheEventID event_id) override;
+  void OnProgressEventRaised(const std::vector<int>& host_ids,
+                             const GURL& url,
+                             int num_total,
+                             int num_complete) override;
+  void OnErrorEventRaised(const std::vector<int>& host_ids,
+                          const AppCacheErrorDetails& details) override;
+  void OnLogMessage(int host_id,
+                    AppCacheLogLevel log_level,
+                    const std::string& message) override;
+  void OnContentBlocked(int host_id, const GURL& manifest_url) override;
+
+ private:
+  std::unique_ptr<AppCacheHost> precreated_host_;
+  scoped_refptr<ChromeAppCacheService> appcache_service_;
+  int appcache_host_id_;
+  base::WeakPtr<AppCacheNavigationHandle> ui_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCacheNavigationHandleCore);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_CORE_H_
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index 9d815d54..b754546 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -1773,10 +1773,8 @@
       request_ = service()->request_context()->CreateRequest(
           manifest_url, net::DEFAULT_PRIORITY, &request_delegate_);
       AppCacheInterceptor::SetExtraRequestInfo(
-          request_.get(), service_.get(),
-          backend_->process_id(), host2->host_id(),
-          RESOURCE_TYPE_MAIN_FRAME,
-          false);
+          request_.get(), service_.get(), backend_->process_id(),
+          host2->host_id(), RESOURCE_TYPE_MAIN_FRAME, false);
       request_->Start();
     }
 
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 451e6f0..c4c661d 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -180,6 +180,7 @@
   MDDH_INVALID_UNSUBSCRIPTION_REQUEST = 156,
   AOAH_NONSENSE_DEVICE_ID = 157,
   BDH_INVALID_OPTIONS = 158,
+  RFH_DID_ADD_CONSOLE_MESSAGE_BAD_SEVERITY = 159,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc b/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
index 44b336c1..a375f8c1 100644
--- a/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
+++ b/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
@@ -127,7 +127,7 @@
 
 void CacheStorageBlobToDiskCache::ReadFromBlob() {
   int bytes_read = blob_request_->Read(buffer_.get(), buffer_->size());
-  if (bytes_read >= 0)
+  if (bytes_read != net::ERR_IO_PENDING)
     OnReadCompleted(blob_request_.get(), bytes_read);
 }
 
diff --git a/content/browser/compositor/image_transport_factory.h b/content/browser/compositor/image_transport_factory.h
index 7cd2574..ffd336a 100644
--- a/content/browser/compositor/image_transport_factory.h
+++ b/content/browser/compositor/image_transport_factory.h
@@ -16,10 +16,6 @@
 #include "ui/events/latency_info.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace cc {
-class SurfaceManager;
-}
-
 namespace gfx {
 enum class SwapResult;
 }
@@ -64,8 +60,6 @@
   // Gets the image transport factory as a context factory for the compositor.
   virtual ui::ContextFactory* GetContextFactory() = 0;
 
-  virtual cc::SurfaceManager* GetSurfaceManager() = 0;
-
   // Gets a GLHelper instance, associated with the shared context. This
   // GLHelper will get destroyed whenever the shared context is lost
   // (ImageTransportFactoryObserver::OnLostResources is called).
diff --git a/content/browser/compositor/mus_browser_compositor_output_surface.cc b/content/browser/compositor/mus_browser_compositor_output_surface.cc
index 3c08c90..ce3b426a 100644
--- a/content/browser/compositor/mus_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/mus_browser_compositor_output_surface.cc
@@ -68,7 +68,7 @@
   ui_frame.metadata.latency_info = std::move(frame.latency_info);
   // Reset latency_info to known empty state after moving contents.
   frame.latency_info.clear();
-  const cc::RenderPassId render_pass_id(1, 1);
+  const int render_pass_id = 1;
   std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
   const bool has_transparent_background = true;
   pass->SetAll(render_pass_id, bounds, bounds, gfx::Transform(),
diff --git a/content/browser/compositor/surface_utils.cc b/content/browser/compositor/surface_utils.cc
index 902b818..33e5468a 100644
--- a/content/browser/compositor/surface_utils.cc
+++ b/content/browser/compositor/surface_utils.cc
@@ -175,7 +175,7 @@
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   if (factory == NULL)
     return nullptr;
-  return factory->GetSurfaceManager();
+  return factory->GetContextFactory()->GetSurfaceManager();
 #endif
 }
 
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.cc b/content/browser/compositor/test/no_transport_image_transport_factory.cc
index 86ce0cbb..150c9a69 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.cc
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.cc
@@ -33,10 +33,6 @@
   return context_factory_.get();
 }
 
-cc::SurfaceManager* NoTransportImageTransportFactory::GetSurfaceManager() {
-  return surface_manager_.get();
-}
-
 display_compositor::GLHelper* NoTransportImageTransportFactory::GetGLHelper() {
   if (!gl_helper_) {
     context_provider_ = context_factory_->SharedMainThreadContextProvider();
diff --git a/content/browser/compositor/test/no_transport_image_transport_factory.h b/content/browser/compositor/test/no_transport_image_transport_factory.h
index 0f31aec..9211e1a 100644
--- a/content/browser/compositor/test/no_transport_image_transport_factory.h
+++ b/content/browser/compositor/test/no_transport_image_transport_factory.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "cc/surfaces/surface_manager.h"
 #include "content/browser/compositor/image_transport_factory.h"
 
 namespace cc {
@@ -29,7 +30,6 @@
 
   // ImageTransportFactory implementation.
   ui::ContextFactory* GetContextFactory() override;
-  cc::SurfaceManager* GetSurfaceManager() override;
   display_compositor::GLHelper* GetGLHelper() override;
   void SetGpuChannelEstablishFactory(
       gpu::GpuChannelEstablishFactory* factory) override;
diff --git a/content/browser/compositor/vulkan_browser_compositor_output_surface.cc b/content/browser/compositor/vulkan_browser_compositor_output_surface.cc
index 5cd2930..b606893c 100644
--- a/content/browser/compositor/vulkan_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/vulkan_browser_compositor_output_surface.cc
@@ -12,11 +12,9 @@
 
 VulkanBrowserCompositorOutputSurface::VulkanBrowserCompositorOutputSurface(
     scoped_refptr<cc::VulkanContextProvider> context,
-    const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
-    cc::SyntheticBeginFrameSource* begin_frame_source)
+    const UpdateVSyncParametersCallback& update_vsync_parameters_callback)
     : BrowserCompositorOutputSurface(std::move(context),
-                                     update_vsync_parameters_callback,
-                                     begin_frame_source),
+                                     update_vsync_parameters_callback),
       weak_ptr_factory_(this) {}
 
 VulkanBrowserCompositorOutputSurface::~VulkanBrowserCompositorOutputSurface() {
diff --git a/content/browser/compositor/vulkan_browser_compositor_output_surface.h b/content/browser/compositor/vulkan_browser_compositor_output_surface.h
index b145a28..1e4f19b 100644
--- a/content/browser/compositor/vulkan_browser_compositor_output_surface.h
+++ b/content/browser/compositor/vulkan_browser_compositor_output_surface.h
@@ -24,8 +24,7 @@
  public:
   VulkanBrowserCompositorOutputSurface(
       scoped_refptr<cc::VulkanContextProvider> context,
-      const UpdateVSyncParametersCallback& update_vsync_parameters_callback,
-      cc::SyntheticBeginFrameSource* begin_frame_source);
+      const UpdateVSyncParametersCallback& update_vsync_parameters_callback);
 
   ~VulkanBrowserCompositorOutputSurface() override;
 
diff --git a/content/browser/devtools/devtools_protocol_handler.cc b/content/browser/devtools/devtools_protocol_handler.cc
index e7dc2ec..984918e 100644
--- a/content/browser/devtools/devtools_protocol_handler.cc
+++ b/content/browser/devtools/devtools_protocol_handler.cc
@@ -94,7 +94,7 @@
 std::unique_ptr<base::DictionaryValue> DevToolsProtocolHandler::ParseCommand(
     int session_id,
     std::unique_ptr<base::Value> value) {
-  if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value || !value->IsType(base::Value::Type::DICTIONARY)) {
     client_.SendError(
         DevToolsCommandId(DevToolsCommandId::kNoId, session_id),
         Response(kStatusParseError, "Message must be in JSON format"));
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index ea5c2d44..eb6f29ca 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -194,7 +194,7 @@
   if (config.isJust()) {
     std::unique_ptr<base::Value> value =
         protocol::toBaseValue(config.fromJust()->toValue().get(), 1000);
-    if (value && value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (value && value->IsType(base::Value::Type::DICTIONARY)) {
       trace_config = GetTraceConfigFromDevToolsConfig(
           *static_cast<base::DictionaryValue*>(value.get()));
     }
@@ -345,7 +345,7 @@
 base::trace_event::TraceConfig TracingHandler::GetTraceConfigFromDevToolsConfig(
     const base::DictionaryValue& devtools_config) {
   std::unique_ptr<base::Value> value = ConvertDictKeyStyle(devtools_config);
-  DCHECK(value && value->IsType(base::Value::TYPE_DICTIONARY));
+  DCHECK(value && value->IsType(base::Value::Type::DICTIONARY));
   std::unique_ptr<base::DictionaryValue> tracing_dict(
       static_cast<base::DictionaryValue*>(value.release()));
 
diff --git a/content/browser/devtools/protocol_string.cc b/content/browser/devtools/protocol_string.cc
index 702bed2..7c15d1a0 100644
--- a/content/browser/devtools/protocol_string.cc
+++ b/content/browser/devtools/protocol_string.cc
@@ -16,29 +16,29 @@
     const base::Value* value, int depth) {
   if (!value || !depth)
     return nullptr;
-  if (value->IsType(base::Value::TYPE_NULL))
+  if (value->IsType(base::Value::Type::NONE))
     return protocol::Value::null();
-  if (value->IsType(base::Value::TYPE_BOOLEAN)) {
+  if (value->IsType(base::Value::Type::BOOLEAN)) {
     bool inner;
     value->GetAsBoolean(&inner);
     return protocol::FundamentalValue::create(inner);
   }
-  if (value->IsType(base::Value::TYPE_INTEGER)) {
+  if (value->IsType(base::Value::Type::INTEGER)) {
     int inner;
     value->GetAsInteger(&inner);
     return protocol::FundamentalValue::create(inner);
   }
-  if (value->IsType(base::Value::TYPE_DOUBLE)) {
+  if (value->IsType(base::Value::Type::DOUBLE)) {
     double inner;
     value->GetAsDouble(&inner);
     return protocol::FundamentalValue::create(inner);
   }
-  if (value->IsType(base::Value::TYPE_STRING)) {
+  if (value->IsType(base::Value::Type::STRING)) {
     std::string inner;
     value->GetAsString(&inner);
     return protocol::StringValue::create(inner);
   }
-  if (value->IsType(base::Value::TYPE_LIST)) {
+  if (value->IsType(base::Value::Type::LIST)) {
     const base::ListValue* list = nullptr;
     value->GetAsList(&list);
     std::unique_ptr<protocol::ListValue> result = protocol::ListValue::create();
@@ -52,7 +52,7 @@
     }
     return std::move(result);
   }
-  if (value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (value->IsType(base::Value::Type::DICTIONARY)) {
     const base::DictionaryValue* dictionary = nullptr;
     value->GetAsDictionary(&dictionary);
     std::unique_ptr<protocol::DictionaryValue> result =
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc
index a0acc44e..30bcd5e 100644
--- a/content/browser/download/download_resource_handler.cc
+++ b/content/browser/download/download_resource_handler.cc
@@ -17,13 +17,13 @@
 #include "content/browser/download/download_manager_impl.h"
 #include "content/browser/download/download_request_handle.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/resource_response.h"
diff --git a/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
index 83bda69..7f907174 100644
--- a/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
+++ b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
@@ -46,7 +46,7 @@
   ExpectFrameTreeNodeObject(event);
   EXPECT_TRUE(event->HasArg("snapshot"));
   EXPECT_TRUE(event->arg_values.at("snapshot")
-                  ->IsType(base::Value::Type::TYPE_DICTIONARY));
+                  ->IsType(base::Value::Type::DICTIONARY));
 }
 
 std::string GetParentNodeID(const trace_analyzer::TraceEvent* event) {
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index bb2dfbf..0152809 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -30,7 +30,6 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
@@ -1336,6 +1335,25 @@
   }
 }
 
+// Verify that reloading a page with url anchor scrolls to correct position.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, ReloadWithUrlAnchor) {
+  GURL url1(embedded_test_server()->GetURL(
+      "/navigation_controller/reload-with-url-anchor.html#d2"));
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+
+  std::string script =
+      "domAutomationController.send(document.getElementById('div').scrollTop)";
+  int value = 0;
+  EXPECT_TRUE(ExecuteScriptAndExtractInt(shell(), script, &value));
+  EXPECT_EQ(100, value);
+
+  // Reload.
+  ReloadBlockUntilNavigationsComplete(shell(), 1);
+
+  EXPECT_TRUE(ExecuteScriptAndExtractInt(shell(), script, &value));
+  EXPECT_EQ(100, value);
+}
+
 // Verify that empty GURL navigations are not classified as SAME_PAGE.
 // See https://crbug.com/534980.
 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
@@ -5314,9 +5332,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
   EXPECT_EQ(3, controller.GetEntryCount());
-  // TODO(creis): Replicate history length for OOPIFs: https://crbug.com/501116.
-  if (!AreAllSitesIsolatedForTesting())
-    EXPECT_EQ(3, RendererHistoryLength(shell()));
+  EXPECT_EQ(3, RendererHistoryLength(shell()));
   EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
 
   EXPECT_EQ(frame_url_2, frame->current_url());
@@ -5352,9 +5368,7 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
   EXPECT_EQ(2, controller.GetEntryCount());
-  // TODO(creis): Replicate history length for OOPIFs: https://crbug.com/501116.
-  if (!AreAllSitesIsolatedForTesting())
-    EXPECT_EQ(2, RendererHistoryLength(shell()));
+  EXPECT_EQ(2, RendererHistoryLength(shell()));
   EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
 
   // Go back.
@@ -5451,9 +5465,7 @@
   NavigateFrameToURL(frame, frame_url_3);
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   EXPECT_EQ(4, controller.GetEntryCount());
-  // TODO(creis): Replicate history length for OOPIFs: https://crbug.com/501116.
-  if (!AreAllSitesIsolatedForTesting())
-    EXPECT_EQ(4, RendererHistoryLength(shell()));
+  EXPECT_EQ(4, RendererHistoryLength(shell()));
   EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(frame_url_3, frame->current_url());
 
@@ -5465,9 +5477,7 @@
     observer.Wait();
   }
   EXPECT_EQ(4, controller.GetEntryCount());
-  // TODO(creis): Replicate history length for OOPIFs: https://crbug.com/501116.
-  if (!AreAllSitesIsolatedForTesting())
-    EXPECT_EQ(4, RendererHistoryLength(shell()));
+  EXPECT_EQ(4, RendererHistoryLength(shell()));
   EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(links_url, root->current_url());
 
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 5414107..1cfc4a4 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -6,6 +6,8 @@
 
 #include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
+#include "content/browser/appcache/appcache_navigation_handle.h"
+#include "content/browser/appcache/appcache_service_impl.h"
 #include "content/browser/browsing_data/clear_site_data_throttle.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
@@ -406,6 +408,12 @@
       new ServiceWorkerNavigationHandle(service_worker_context));
 }
 
+void NavigationHandleImpl::InitAppCacheHandle(
+    ChromeAppCacheService* appcache_service) {
+  DCHECK(IsBrowserSideNavigationEnabled());
+  appcache_handle_.reset(new AppCacheNavigationHandle(appcache_service));
+}
+
 void NavigationHandleImpl::WillStartRequest(
     const std::string& method,
     scoped_refptr<content::ResourceRequestBodyImpl> resource_request_body,
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index f81a34fc..abdd01b 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -32,6 +32,8 @@
 
 namespace content {
 
+class AppCacheNavigationHandle;
+class ChromeAppCacheService;
 class NavigationUIData;
 class NavigatorDelegate;
 class ResourceRequestBodyImpl;
@@ -196,6 +198,12 @@
     return service_worker_handle_.get();
   }
 
+  // PlzNavigate
+  void InitAppCacheHandle(ChromeAppCacheService* appcache_service);
+  AppCacheNavigationHandle* appcache_handle() const {
+    return appcache_handle_.get();
+  }
+
   typedef base::Callback<void(NavigationThrottle::ThrottleCheckResult)>
       ThrottleChecksFinishedCallback;
 
@@ -395,6 +403,12 @@
   // corresponding ServiceWorkerNetworkProvider is created in the renderer.
   std::unique_ptr<ServiceWorkerNavigationHandle> service_worker_handle_;
 
+  // PlzNavigate
+  // Manages the lifetime of a pre-created AppCacheHost until a browser side
+  // navigation is ready to be committed, i.e we have a renderer process ready
+  // to service the navigation request.
+  std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
+
   // Embedder data from the IO thread tied to this navigation.
   std::unique_ptr<NavigationData> navigation_data_;
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index b9d3d6e..f8b232d 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "content/browser/appcache/appcache_navigation_handle.h"
+#include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
 #include "content/browser/frame_host/frame_tree.h"
@@ -20,6 +22,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/common/appcache_interfaces.h"
 #include "content/common/resource_request_body_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
@@ -29,6 +32,7 @@
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/stream_handle.h"
+#include "content/public/common/appcache_info.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_response.h"
@@ -404,6 +408,11 @@
                 ->service_worker_provider_host_id()
           : kInvalidServiceWorkerProviderId;
 
+  request_params_.appcache_host_id =
+      navigation_handle_->appcache_handle()
+          ? navigation_handle_->appcache_handle()->appcache_host_id()
+          : kAppCacheNoHostId;
+
   // Update the lofi state of the request.
   if (response->head.is_using_lofi)
     common_params_.lofi_state = LOFI_ON;
@@ -509,6 +518,11 @@
     navigation_handle_->InitServiceWorkerHandle(service_worker_context);
   }
 
+  if (IsSchemeSupportedForAppCache(common_params_.url)) {
+    navigation_handle_->InitAppCacheHandle(
+        static_cast<ChromeAppCacheService*>(partition->GetAppCacheService()));
+  }
+
   // Mark the fetch_start (Navigation Timing API).
   request_params_.navigation_timing.fetch_start = base::TimeTicks::Now();
 
@@ -544,7 +558,8 @@
           frame_tree_node_->frame_tree_node_id(), is_for_guests_only,
           report_raw_headers),
       std::move(navigation_ui_data),
-      navigation_handle_->service_worker_handle(), this);
+      navigation_handle_->service_worker_handle(),
+      navigation_handle_->appcache_handle(), this);
 }
 
 void NavigationRequest::OnRedirectChecksComplete(
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 555da9b..2c96e12 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -209,7 +209,6 @@
 
   NavigationState state_;
 
-
   std::unique_ptr<NavigationURLLoader> loader_;
 
   // These next items are used in browser-initiated navigations to store
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index e725c8d..34c8572 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -968,6 +968,12 @@
     const base::string16& message,
     int32_t line_no,
     const base::string16& source_id) {
+  if (level < logging::LOG_VERBOSE || level > logging::LOG_FATAL) {
+    bad_message::ReceivedBadMessage(
+        GetProcess(), bad_message::RFH_DID_ADD_CONSOLE_MESSAGE_BAD_SEVERITY);
+    return;
+  }
+
   if (delegate_->DidAddMessageToConsole(level, message, line_no, source_id))
     return;
 
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
index 967f7e6..36fe6ea 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
@@ -29,6 +29,7 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/test/test_render_view_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/compositor.h"
 
 #if defined(OS_ANDROID)
 #include "content/browser/renderer_host/context_provider_factory_impl_android.h"
@@ -151,8 +152,7 @@
   frame.metadata.device_scale_factor = scale_factor;
 
   std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
-  pass->SetNew(cc::RenderPassId(1, 1), gfx::Rect(size), damage,
-               gfx::Transform());
+  pass->SetNew(1, gfx::Rect(size), damage, gfx::Transform());
   frame.render_pass_list.push_back(std::move(pass));
   return frame;
 }
@@ -182,7 +182,8 @@
   if (id.is_valid()) {
 #if !defined(OS_ANDROID)
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager = factory->GetSurfaceManager();
+    cc::SurfaceManager* manager =
+        factory->GetContextFactory()->GetSurfaceManager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
     // There should be a SurfaceSequence created by the RWHVChildFrame.
diff --git a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
index 737f652f..8bfa069 100644
--- a/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest_unittest.cc
@@ -29,6 +29,7 @@
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/compositor.h"
 
 #if defined(OS_ANDROID)
 #include "content/browser/renderer_host/context_provider_factory_impl_android.h"
@@ -250,8 +251,7 @@
   frame.metadata.device_scale_factor = scale_factor;
 
   std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
-  pass->SetNew(cc::RenderPassId(1, 1), gfx::Rect(size), damage,
-               gfx::Transform());
+  pass->SetNew(1, gfx::Rect(size), damage, gfx::Transform());
   frame.render_pass_list.push_back(std::move(pass));
   return frame;
 }
@@ -275,7 +275,8 @@
   if (id.is_valid()) {
 #if !defined(OS_ANDROID)
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager = factory->GetSurfaceManager();
+    cc::SurfaceManager* manager =
+        factory->GetContextFactory()->GetSurfaceManager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
     // There should be a SurfaceSequence created by the RWHVGuest.
@@ -298,7 +299,8 @@
   if (id.is_valid()) {
 #if !defined(OS_ANDROID)
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager = factory->GetSurfaceManager();
+    cc::SurfaceManager* manager =
+        factory->GetContextFactory()->GetSurfaceManager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
     // There should be a SurfaceSequence created by the RWHVGuest.
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
index 094adfa..5b9d77d 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
@@ -51,59 +51,6 @@
       FROM_HERE, base::Bind(destruction_callback, sync_token));
 }
 
-GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() {
-  GpuMemoryBufferConfigurationSet configurations;
-
-  if (BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled()) {
-    const gfx::BufferFormat kNativeFormats[] = {
-        gfx::BufferFormat::R_8,
-        gfx::BufferFormat::RG_88,
-        gfx::BufferFormat::BGR_565,
-        gfx::BufferFormat::RGBA_4444,
-        gfx::BufferFormat::RGBA_8888,
-        gfx::BufferFormat::BGRA_8888,
-        gfx::BufferFormat::UYVY_422,
-        gfx::BufferFormat::YVU_420,
-        gfx::BufferFormat::YUV_420_BIPLANAR};
-    const gfx::BufferUsage kNativeUsages[] = {
-        gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT,
-        gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
-        gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT};
-    for (auto& format : kNativeFormats) {
-      for (auto& usage : kNativeUsages) {
-        if (gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
-          configurations.insert(std::make_pair(format, usage));
-      }
-    }
-  }
-
-#if defined(USE_OZONE) || defined(OS_MACOSX)
-  // Disable native buffers only when using Mesa.
-  bool force_native_gpu_read_write_formats =
-      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kUseGL) != gl::kGLImplementationOSMesaName;
-#else
-  bool force_native_gpu_read_write_formats = false;
-#endif
-  if (force_native_gpu_read_write_formats) {
-    const gfx::BufferFormat kGPUReadWriteFormats[] = {
-        gfx::BufferFormat::BGR_565,   gfx::BufferFormat::RGBA_8888,
-        gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::BGRA_8888,
-        gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::UYVY_422,
-        gfx::BufferFormat::YVU_420,   gfx::BufferFormat::YUV_420_BIPLANAR};
-    const gfx::BufferUsage kGPUReadWriteUsages[] = {
-        gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT};
-    for (auto& format : kGPUReadWriteFormats) {
-      for (auto& usage : kGPUReadWriteUsages) {
-        if (gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
-          configurations.insert(std::make_pair(format, usage));
-      }
-    }
-  }
-
-  return configurations;
-}
-
 BrowserGpuMemoryBufferManager* g_gpu_memory_buffer_manager = nullptr;
 
 }  // namespace
@@ -151,7 +98,7 @@
 BrowserGpuMemoryBufferManager::BrowserGpuMemoryBufferManager(
     int gpu_client_id,
     uint64_t gpu_client_tracing_id)
-    : native_configurations_(GetNativeGpuMemoryBufferConfigurations()),
+    : native_configurations_(gpu::GetNativeGpuMemoryBufferConfigurations()),
       gpu_client_id_(gpu_client_id),
       gpu_client_tracing_id_(gpu_client_tracing_id),
       gpu_host_id_(0) {
@@ -169,28 +116,11 @@
 }
 
 // static
-bool BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled() {
-  // Disable native buffers when using Mesa.
-  if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kUseGL) == gl::kGLImplementationOSMesaName) {
-    return false;
-  }
-
-#if defined(OS_MACOSX)
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableNativeGpuMemoryBuffers);
-#else
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableNativeGpuMemoryBuffers);
-#endif
-}
-
-// static
 uint32_t BrowserGpuMemoryBufferManager::GetImageTextureTarget(
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
-  GpuMemoryBufferConfigurationSet native_configurations =
-      GetNativeGpuMemoryBufferConfigurations();
+  gpu::GpuMemoryBufferConfigurationSet native_configurations =
+      gpu::GetNativeGpuMemoryBufferConfigurations();
   if (native_configurations.find(std::make_pair(format, usage)) ==
       native_configurations.end()) {
     return GL_TEXTURE_2D;
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.h b/content/browser/gpu/browser_gpu_memory_buffer_manager.h
index 3073d6c..998abd4 100644
--- a/content/browser/gpu/browser_gpu_memory_buffer_manager.h
+++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.h
@@ -19,27 +19,7 @@
 #include "content/common/content_export.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/ipc/common/surface_handle.h"
-
-namespace content {
-
-using GpuMemoryBufferConfigurationKey =
-    std::pair<gfx::BufferFormat, gfx::BufferUsage>;
-using GpuMemoryBufferConfigurationSet =
-    base::hash_set<GpuMemoryBufferConfigurationKey>;
-
-}  // content
-
-namespace BASE_HASH_NAMESPACE {
-
-template <>
-struct hash<content::GpuMemoryBufferConfigurationKey> {
-  size_t operator()(const content::GpuMemoryBufferConfigurationKey& key) const {
-    return base::HashInts(static_cast<int>(key.first),
-                          static_cast<int>(key.second));
-  }
-};
-
-}  // namespace BASE_HASH_NAMESPACE
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
 
 namespace content {
 class GpuProcessHost;
@@ -58,8 +38,6 @@
 
   static BrowserGpuMemoryBufferManager* current();
 
-  static bool IsNativeGpuMemoryBuffersEnabled();
-
   static uint32_t GetImageTextureTarget(gfx::BufferFormat format,
                                         gfx::BufferUsage usage);
 
@@ -161,7 +139,7 @@
 
   uint64_t ClientIdToTracingProcessId(int client_id) const;
 
-  const GpuMemoryBufferConfigurationSet native_configurations_;
+  const gpu::GpuMemoryBufferConfigurationSet native_configurations_;
   const int gpu_client_id_;
   const uint64_t gpu_client_tracing_id_;
 
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 0afb42e..66de1fe1 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -26,6 +26,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/config/gpu_feature_type.h"
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
 #include "media/media_features.h"
 #include "ui/gl/gl_switches.h"
 
@@ -142,15 +143,14 @@
      NumberOfRendererRasterThreads() == 1, "Raster is using a single thread.",
      false},
     {kNativeGpuMemoryBuffersFeatureName, false,
-     !BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled(),
+     !gpu::AreNativeGpuMemoryBuffersEnabled(),
      "Native GpuMemoryBuffers have been disabled, either via about:flags"
      " or command line.",
      true},
-    {"vpx_decode",
-     manager->IsFeatureBlacklisted(
-         gpu::GPU_FEATURE_TYPE_ACCELERATED_VPX_DECODE) ||
-     manager->IsFeatureBlacklisted(
-         gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE),
+    {"vpx_decode", manager->IsFeatureBlacklisted(
+                       gpu::GPU_FEATURE_TYPE_ACCELERATED_VPX_DECODE) ||
+                       manager->IsFeatureBlacklisted(
+                           gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE),
      accelerated_vpx_disabled,
      "Accelerated VPx video decode has been disabled, either via blacklist"
      " or the command line.",
@@ -158,8 +158,7 @@
     {kWebGL2FeatureName,
      manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL2),
      command_line.HasSwitch(switches::kDisableES3APIs),
-     "WebGL2 has been disabled via blacklist or the command line.",
-     false},
+     "WebGL2 has been disabled via blacklist or the command line.", false},
   };
   DCHECK(index < arraysize(kGpuFeatureInfo));
   *eof = (index == arraysize(kGpuFeatureInfo) - 1);
@@ -232,7 +231,7 @@
   }
 
   // Native GPU memory buffers are required.
-  if (!BrowserGpuMemoryBufferManager::IsNativeGpuMemoryBuffersEnabled())
+  if (!gpu::AreNativeGpuMemoryBuffersEnabled())
     return false;
 
 #if defined(OS_MACOSX)
diff --git a/content/browser/hyphenation/hyphenation_impl.cc b/content/browser/hyphenation/hyphenation_impl.cc
index 32c9730e..02ba320 100644
--- a/content/browser/hyphenation/hyphenation_impl.cc
+++ b/content/browser/hyphenation/hyphenation_impl.cc
@@ -15,25 +15,24 @@
 #include "base/strings/stringprintf.h"
 #include "base/timer/elapsed_timer.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace {
 
 using DictionaryFileMap = std::unordered_map<std::string, base::File>;
 
-static bool IsValidLocale(const std::string& locale) {
+bool IsValidLocale(const std::string& locale) {
   return std::all_of(locale.cbegin(), locale.cend(), [](const char ch) {
     return base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch) || ch == '-';
   });
 }
 
-static base::File& GetDictionaryFile(const std::string& locale) {
+base::File GetDictionaryFile(const std::string& locale) {
   // Keep Files open in the cache for subsequent calls.
   CR_DEFINE_STATIC_LOCAL(DictionaryFileMap, cache, ());
 
   const auto& it = cache.find(locale);
   if (it != cache.end())
-    return it->second;
+    return it->second.Duplicate();
   const auto& inserted = cache.insert(std::make_pair(locale, base::File()));
   base::File& file = inserted.first->second;
   DCHECK(!file.IsValid());
@@ -48,7 +47,7 @@
   base::ElapsedTimer timer;
   file.Initialize(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
   UMA_HISTOGRAM_TIMES("Hyphenation.Open.File", timer.Elapsed());
-  return file;
+  return file.Duplicate();
 }
 
 }  // namespace
@@ -67,13 +66,10 @@
 
 void HyphenationImpl::OpenDictionary(const std::string& locale,
                                      const OpenDictionaryCallback& callback) {
-  mojo::ScopedHandle handle;
-  if (IsValidLocale(locale)) {
-    base::File& file = GetDictionaryFile(locale);
-    if (file.IsValid())
-      handle = mojo::WrapPlatformFile(file.Duplicate().TakePlatformFile());
-  }
-  callback.Run(std::move(handle));
+  if (IsValidLocale(locale))
+    callback.Run(GetDictionaryFile(locale));
+  else
+    callback.Run(base::File());
 }
 
 }  // namespace hyphenation
diff --git a/content/browser/loader/DEPS b/content/browser/loader/DEPS
index 84a7d8cc..c927c38a 100644
--- a/content/browser/loader/DEPS
+++ b/content/browser/loader/DEPS
@@ -14,13 +14,13 @@
     "+content/browser/loader/downloaded_temp_file_impl.h",
     "+content/browser/loader/netlog_observer.h",
     "+content/browser/loader/resource_buffer.h",
+    "+content/browser/loader/resource_controller.h",
     "+content/browser/loader/resource_dispatcher_host_impl.h",
     "+content/browser/loader/resource_handler.h",
     "+content/browser/loader/resource_message_delegate.h",
     "+content/browser/loader/resource_message_filter.h",
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/common/content_export.h",
-    "+content/public/browser/resource_controller.h",
     "+content/public/browser/resource_dispatcher_host_delegate.h",
     "+content/public/common/resource_response.h",
 
@@ -36,7 +36,6 @@
     "-content",
     "+content/browser/loader/async_revalidation_driver.h",
     "+content/common/content_export.h",
-    "+content/public/browser/resource_controller.h",
     "+content/public/browser/resource_throttle.h",
   ],
   "async_revalidation_driver_unittest\.cc": [
@@ -91,13 +90,13 @@
     "+content/browser/loader/mojo_async_resource_handler.h",
     "+content/browser/loader/test_url_loader_client.h",
     "+content/browser/loader/netlog_observer.h",
+    "+content/browser/loader/resource_controller.h",
     "+content/browser/loader/resource_dispatcher_host_impl.h",
     "+content/browser/loader/resource_handler.h",
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/common/content_export.h",
     "+content/common/resource_request_completion_status.h",
     "+content/public/browser/global_request_id.h",
-    "+content/public/browser/resource_controller.h",
     "+content/public/browser/resource_dispatcher_host_delegate.h",
     "+content/public/common/resource_response.h",
     "+content/common/url_loader.mojom.h",
@@ -107,6 +106,7 @@
     "+content/browser/loader/mojo_async_resource_handler.h",
     "+content/browser/loader/test_url_loader_client.h",
     "+content/public/browser/navigation_data.h",
+    "+content/browser/loader/resource_controller.h",
     "+content/browser/loader/resource_dispatcher_host_impl.h",
     "+content/browser/loader/resource_request_info_impl.h",
     "+content/common/resource_request.h",
@@ -114,7 +114,6 @@
     "+content/common/url_loader.mojom.h",
     "+content/public/browser/appcache_service.h",
     "+content/public/browser/resource_context.h",
-    "+content/public/browser/resource_controller.h",
     "+content/public/browser/resource_dispatcher_host_delegate.h",
     "+content/public/browser/resource_throttle.h",
     "+content/public/browser/stream_info.h",
@@ -167,6 +166,7 @@
 
     # TODO: These all have to be removed.
     "+content/browser/appcache/appcache_interceptor.h",
+    "+content/browser/appcache/appcache_navigation_handle_core.h",
     "+content/browser/appcache/chrome_appcache_service.h",
     "+content/browser/bad_message.h",
     "+content/browser/blob_storage/chrome_blob_storage_context.h",
@@ -225,6 +225,7 @@
   ],
   "resource_loader\.(cc|h)": [
     "-content",
+    "+content/browser/loader/resource_controller.h",
     "+content/browser/loader/resource_handler.h",
     "+content/browser/loader/resource_loader.h",
     "+content/browser/loader/resource_loader_delegate.h",
@@ -232,7 +233,6 @@
     "+content/browser/ssl/ssl_client_auth_handler.h",
     "+content/browser/ssl/ssl_error_handler.h",
     "+content/common/content_export.h",
-    "+content/public/browser/resource_controller.h",
     "+content/public/browser/resource_dispatcher_host_login_delegate.h",
     "+content/public/common/browser_side_navigation_policy.h",
     "+content/public/common/resource_response.h",
@@ -281,6 +281,7 @@
     "-content",
     "+content/browser/loader/resource_requester_info.h",
     "+content/common/content_export.h",
+    "+content/public/browser/resource_context.h",
     "+content/public/common/resource_type.h",
 
     # TODO: these all have to be removed.
@@ -288,6 +289,7 @@
     "+content/browser/blob_storage/chrome_blob_storage_context.h",
     "+content/browser/service_worker/service_worker_context_wrapper.h",
     "+content/public/browser/browser_thread.h",
+    "+content/public/common/browser_side_navigation_policy.h",
   ],
   "resource_scheduler\.(cc|h)": [
     "-content",
diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc
index c4c1f81..3f79218 100644
--- a/content/browser/loader/async_resource_handler.cc
+++ b/content/browser/loader/async_resource_handler.cc
@@ -19,13 +19,13 @@
 #include "base/time/time.h"
 #include "content/browser/loader/netlog_observer.h"
 #include "content/browser/loader/resource_buffer.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_message_filter.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/common/resource_messages.h"
 #include "content/common/resource_request_completion_status.h"
 #include "content/common/view_messages.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/resource_response.h"
diff --git a/content/browser/loader/async_revalidation_driver.cc b/content/browser/loader/async_revalidation_driver.cc
index 20cc1f1..795990e 100644
--- a/content/browser/loader/async_revalidation_driver.cc
+++ b/content/browser/loader/async_revalidation_driver.cc
@@ -45,7 +45,7 @@
       completion_callback_(completion_callback),
       weak_ptr_factory_(this) {
   request_->set_delegate(this);
-  throttle_->set_controller(this);
+  throttle_->set_delegate(this);
 }
 
 AsyncRevalidationDriver::~AsyncRevalidationDriver() {}
diff --git a/content/browser/loader/async_revalidation_driver.h b/content/browser/loader/async_revalidation_driver.h
index a5bf1ec7..b4a0019c 100644
--- a/content/browser/loader/async_revalidation_driver.h
+++ b/content/browser/loader/async_revalidation_driver.h
@@ -14,7 +14,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 #include "net/base/io_buffer.h"
 #include "net/url_request/url_request.h"
@@ -24,8 +23,9 @@
 // This class is responsible for driving the URLRequest for an async
 // revalidation. It is passed an instance of ResourceThrottle created by
 // content::ResourceScheduler to perform throttling on the request.
-class CONTENT_EXPORT AsyncRevalidationDriver : public net::URLRequest::Delegate,
-                                               public ResourceController {
+class CONTENT_EXPORT AsyncRevalidationDriver
+    : public net::URLRequest::Delegate,
+      public ResourceThrottle::Delegate {
  public:
   // |completion_callback| is guaranteed to be called on completion,
   // regardless of success or failure.
@@ -60,7 +60,7 @@
   void OnResponseStarted(net::URLRequest* request) override;
   void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
 
-  // ResourceController implementation:
+  // ResourceThrottle::Delegate implementation:
   void Resume() override;
 
   // For simplicity, this class assumes that ResourceScheduler never cancels
diff --git a/content/browser/loader/async_revalidation_driver_unittest.cc b/content/browser/loader/async_revalidation_driver_unittest.cc
index 955d53a..91e64ba0 100644
--- a/content/browser/loader/async_revalidation_driver_unittest.cc
+++ b/content/browser/loader/async_revalidation_driver_unittest.cc
@@ -201,8 +201,9 @@
   driver_->StartRequest();
   base::RunLoop().RunUntilIdle();
 
-  ResourceController* driver_as_resource_controller = driver_.get();
-  driver_as_resource_controller->Resume();
+  ResourceThrottle::Delegate* driver_as_resource_throttle_delegate =
+      driver_.get();
+  driver_as_resource_throttle_delegate->Resume();
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(async_revalidation_complete_called());
 }
diff --git a/content/browser/loader/cross_site_resource_handler_browsertest.cc b/content/browser/loader/cross_site_resource_handler_browsertest.cc
index cfa809f..ee916d25 100644
--- a/content/browser/loader/cross_site_resource_handler_browsertest.cc
+++ b/content/browser/loader/cross_site_resource_handler_browsertest.cc
@@ -12,7 +12,6 @@
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
@@ -108,7 +107,7 @@
       *defer = true;
       base::Closure resume_request_on_io_thread = base::Bind(
           base::IgnoreResult(&BrowserThread::PostTask), BrowserThread::IO,
-          FROM_HERE, base::Bind(&CallbackRunningResourceThrottle::Resume,
+          FROM_HERE, base::Bind(&CallbackRunningResourceThrottle::MarkAndResume,
                                 weak_factory_.GetWeakPtr()));
       BrowserThread::PostTask(
           BrowserThread::UI, FROM_HERE,
@@ -132,9 +131,9 @@
     }
 
    private:
-    void Resume() {
+    void MarkAndResume() {
       resumed_ = true;
-      controller()->Resume();
+      Resume();
     }
 
     bool resumed_;
diff --git a/content/browser/loader/detachable_resource_handler.h b/content/browser/loader/detachable_resource_handler.h
index c919949..f75506e 100644
--- a/content/browser/loader/detachable_resource_handler.h
+++ b/content/browser/loader/detachable_resource_handler.h
@@ -12,8 +12,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_handler.h"
-#include "content/public/browser/resource_controller.h"
 
 namespace net {
 class IOBuffer;
diff --git a/content/browser/loader/intercepting_resource_handler.h b/content/browser/loader/intercepting_resource_handler.h
index 3c796a40..f0a6082 100644
--- a/content/browser/loader/intercepting_resource_handler.h
+++ b/content/browser/loader/intercepting_resource_handler.h
@@ -12,9 +12,9 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/loader/layered_resource_handler.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_handler.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/resource_controller.h"
 #include "net/base/io_buffer.h"
 #include "net/url_request/url_request_status.h"
 
diff --git a/content/browser/loader/intercepting_resource_handler_unittest.cc b/content/browser/loader/intercepting_resource_handler_unittest.cc
index 9f2096ce..aea7584 100644
--- a/content/browser/loader/intercepting_resource_handler_unittest.cc
+++ b/content/browser/loader/intercepting_resource_handler_unittest.cc
@@ -16,8 +16,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/test_resource_handler.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/resource_response.h"
 #include "content/public/common/webplugininfo.h"
@@ -233,6 +233,11 @@
   std::string old_handler_body;
   std::unique_ptr<TestResourceHandler> old_handler_scoped(
       new TestResourceHandler(&old_handler_status, &old_handler_body));
+  // When sending a payload to the old ResourceHandler, the
+  // InterceptingResourceHandler doesn't send a final EOF read.
+  // TODO(mmenke):  Should it?  Or can we just get rid of that 0-byte read
+  // entirely?
+  old_handler_scoped->set_expect_eof_read(false);
   TestResourceHandler* old_handler = old_handler_scoped.get();
   scoped_refptr<net::IOBuffer> old_buffer = old_handler->buffer();
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
@@ -600,6 +605,11 @@
   std::string old_handler_body;
   std::unique_ptr<TestResourceHandler> old_handler(
       new TestResourceHandler(&old_handler_status, &old_handler_body));
+  // When sending a payload to the old ResourceHandler, the
+  // InterceptingResourceHandler doesn't send a final EOF read.
+  // TODO(mmenke):  Should it?  Or can we just get rid of that 0-byte read
+  // entirely?
+  old_handler->set_expect_eof_read(false);
   old_handler->SetBufferSize(10);
   old_handler->set_defer_on_read_completed(true);
 
@@ -687,6 +697,10 @@
   EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::SUCCESS);
   EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::IO_PENDING);
 
+  // Final EOF byte is read.
+  ASSERT_TRUE(intercepting_handler->OnReadCompleted(0, &defer));
+  ASSERT_FALSE(defer);
+
   defer = false;
   intercepting_handler->OnResponseCompleted({net::URLRequestStatus::SUCCESS, 0},
                                             &defer);
@@ -760,6 +774,11 @@
   std::string old_handler_body;
   std::unique_ptr<TestResourceHandler> old_handler(
       new TestResourceHandler(&old_handler_status, &old_handler_body));
+  // When sending a payload to the old ResourceHandler, the
+  // InterceptingResourceHandler doesn't send a final EOF read.
+  // TODO(mmenke):  Should it?  Or can we just get rid of that 0-byte read
+  // entirely?
+  old_handler->set_expect_eof_read(false);
 
   std::unique_ptr<InterceptingResourceHandler> intercepting_handler(
       new InterceptingResourceHandler(std::move(old_handler), request.get()));
diff --git a/content/browser/loader/mime_sniffing_resource_handler.h b/content/browser/loader/mime_sniffing_resource_handler.h
index 8465c07..0effee5e 100644
--- a/content/browser/loader/mime_sniffing_resource_handler.h
+++ b/content/browser/loader/mime_sniffing_resource_handler.h
@@ -11,8 +11,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/loader/layered_resource_handler.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/common/request_context_type.h"
 #include "ppapi/features/features.h"
 
diff --git a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
index 88da415..102b441 100644
--- a/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler_unittest.cc
@@ -16,9 +16,9 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/loader/intercepting_resource_handler.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/test_resource_handler.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/resource_response.h"
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc
index 60a0a9ed5..ea94fad 100644
--- a/content/browser/loader/mojo_async_resource_handler.cc
+++ b/content/browser/loader/mojo_async_resource_handler.cc
@@ -14,11 +14,11 @@
 #include "base/time/time.h"
 #include "content/browser/loader/downloaded_temp_file_impl.h"
 #include "content/browser/loader/netlog_observer.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/common/resource_request_completion_status.h"
 #include "content/public/browser/global_request_id.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/common/resource_response.h"
 #include "mojo/public/c/system/data_pipe.h"
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc
index 4ad1ba6..e67713a 100644
--- a/content/browser/loader/mojo_async_resource_handler_unittest.cc
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/test_url_loader_client.h"
@@ -23,7 +24,6 @@
 #include "content/public/browser/appcache_service.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/resource_context.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/browser/stream_info.h"
diff --git a/content/browser/loader/navigation_resource_handler.cc b/content/browser/loader/navigation_resource_handler.cc
index c9e212fdf..10a2f61 100644
--- a/content/browser/loader/navigation_resource_handler.cc
+++ b/content/browser/loader/navigation_resource_handler.cc
@@ -9,13 +9,13 @@
 #include "base/logging.h"
 #include "content/browser/loader/navigation_url_loader_impl_core.h"
 #include "content/browser/loader/netlog_observer.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_loader.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/resource_context_impl.h"
 #include "content/browser/streams/stream.h"
 #include "content/browser/streams/stream_context.h"
 #include "content/public/browser/navigation_data.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/browser/stream_handle.h"
diff --git a/content/browser/loader/navigation_resource_throttle.cc b/content/browser/loader/navigation_resource_throttle.cc
index 0d075cc4..b4637dba 100644
--- a/content/browser/loader/navigation_resource_throttle.cc
+++ b/content/browser/loader/navigation_resource_throttle.cc
@@ -21,7 +21,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/resource_context.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/ssl_status.h"
@@ -340,13 +339,13 @@
   }
 
   if (result == NavigationThrottle::CANCEL_AND_IGNORE) {
-    controller()->CancelAndIgnore();
+    CancelAndIgnore();
   } else if (result == NavigationThrottle::CANCEL) {
-    controller()->Cancel();
+    Cancel();
   } else if (result == NavigationThrottle::BLOCK_REQUEST) {
-    controller()->CancelWithError(net::ERR_BLOCKED_BY_CLIENT);
+    CancelWithError(net::ERR_BLOCKED_BY_CLIENT);
   } else {
-    controller()->Resume();
+    Resume();
   }
 }
 
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc
index 5e9f29d8..8d9df17a 100644
--- a/content/browser/loader/navigation_url_loader.cc
+++ b/content/browser/loader/navigation_url_loader.cc
@@ -20,6 +20,7 @@
     std::unique_ptr<NavigationRequestInfo> request_info,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     ServiceWorkerNavigationHandle* service_worker_handle,
+    AppCacheNavigationHandle* appcache_handle,
     NavigationURLLoaderDelegate* delegate) {
   if (g_factory) {
     return g_factory->CreateLoader(browser_context, std::move(request_info),
@@ -28,7 +29,7 @@
   }
   return std::unique_ptr<NavigationURLLoader>(new NavigationURLLoaderImpl(
       browser_context, std::move(request_info), std::move(navigation_ui_data),
-      service_worker_handle, delegate));
+      service_worker_handle, appcache_handle, delegate));
 }
 
 void NavigationURLLoader::SetFactoryForTesting(
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index a97e0d72..9d5b65f 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -12,6 +12,7 @@
 
 namespace content {
 
+class AppCacheNavigationHandle;
 class BrowserContext;
 class NavigationUIData;
 class NavigationURLLoaderDelegate;
@@ -38,6 +39,7 @@
       std::unique_ptr<NavigationRequestInfo> request_info,
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       ServiceWorkerNavigationHandle* service_worker_handle,
+      AppCacheNavigationHandle* appcache_handle,
       NavigationURLLoaderDelegate* delegate);
 
   // For testing purposes; sets the factory for use in testing.
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index e889f248..0c0e974 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/trace_event/trace_event.h"
+#include "content/browser/appcache/appcache_navigation_handle.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/browser/loader/navigation_url_loader_impl_core.h"
@@ -27,6 +29,7 @@
     std::unique_ptr<NavigationRequestInfo> request_info,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     ServiceWorkerNavigationHandle* service_worker_handle,
+    AppCacheNavigationHandle* appcache_handle,
     NavigationURLLoaderDelegate* delegate)
     : delegate_(delegate), weak_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -43,11 +46,14 @@
       "FrameTreeNode id", request_info->frame_tree_node_id);
   ServiceWorkerNavigationHandleCore* service_worker_handle_core =
       service_worker_handle ? service_worker_handle->core() : nullptr;
+  AppCacheNavigationHandleCore* appcache_handle_core =
+      appcache_handle ? appcache_handle->core() : nullptr;
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::Bind(&NavigationURLLoaderImplCore::Start, base::Unretained(core_),
                  browser_context->GetResourceContext(),
-                 service_worker_handle_core, base::Passed(&request_info),
+                 service_worker_handle_core, appcache_handle_core,
+                 base::Passed(&request_info),
                  base::Passed(&navigation_ui_data)));
 }
 
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index 8ceacd1..a0177d6 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -19,6 +19,7 @@
 
 namespace content {
 
+class AppCacheNavigationHandle;
 class NavigationURLLoaderImplCore;
 class NavigationData;
 class ServiceWorkerNavigationHandle;
@@ -34,6 +35,7 @@
                           std::unique_ptr<NavigationRequestInfo> request_info,
                           std::unique_ptr<NavigationUIData> navigation_ui_data,
                           ServiceWorkerNavigationHandle* service_worker_handle,
+                          AppCacheNavigationHandle* appcache_handle,
                           NavigationURLLoaderDelegate* delegate);
   ~NavigationURLLoaderImpl() override;
 
diff --git a/content/browser/loader/navigation_url_loader_impl_core.cc b/content/browser/loader/navigation_url_loader_impl_core.cc
index 77b8be2b..f7796bd0c 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.cc
+++ b/content/browser/loader/navigation_url_loader_impl_core.cc
@@ -42,6 +42,7 @@
 void NavigationURLLoaderImplCore::Start(
     ResourceContext* resource_context,
     ServiceWorkerNavigationHandleCore* service_worker_handle_core,
+    AppCacheNavigationHandleCore* appcache_handle_core,
     std::unique_ptr<NavigationRequestInfo> request_info,
     std::unique_ptr<NavigationUIData> navigation_ui_data) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -55,7 +56,7 @@
   if (ResourceDispatcherHostImpl::Get()) {
     ResourceDispatcherHostImpl::Get()->BeginNavigationRequest(
         resource_context, *request_info, std::move(navigation_ui_data), this,
-        service_worker_handle_core);
+        service_worker_handle_core, appcache_handle_core);
   }
 }
 
diff --git a/content/browser/loader/navigation_url_loader_impl_core.h b/content/browser/loader/navigation_url_loader_impl_core.h
index 33fccaf..bf8565a 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.h
+++ b/content/browser/loader/navigation_url_loader_impl_core.h
@@ -17,6 +17,7 @@
 
 namespace content {
 
+class AppCacheNavigationHandleCore;
 class NavigationResourceHandler;
 class NavigationData;
 class ResourceContext;
@@ -41,6 +42,7 @@
   // Starts the request.
   void Start(ResourceContext* resource_context,
              ServiceWorkerNavigationHandleCore* service_worker_handle_core,
+             AppCacheNavigationHandleCore* appcache_handle_core,
              std::unique_ptr<NavigationRequestInfo> request_info,
              std::unique_ptr<NavigationUIData> navigation_ui_data);
 
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index e475f3a..0447b14 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -117,7 +117,7 @@
 
     return NavigationURLLoader::Create(browser_context_.get(),
                                        std::move(request_info), nullptr,
-                                       nullptr, delegate);
+                                       nullptr, nullptr, delegate);
   }
 
   // Helper function for fetching the body of a URL to a string.
diff --git a/content/browser/loader/redirect_to_file_resource_handler.cc b/content/browser/loader/redirect_to_file_resource_handler.cc
index 7ddcdde..2d55420e 100644
--- a/content/browser/loader/redirect_to_file_resource_handler.cc
+++ b/content/browser/loader/redirect_to_file_resource_handler.cc
@@ -10,9 +10,9 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/threading/thread_restrictions.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/loader/temporary_file_stream.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/common/resource_response.h"
 #include "net/base/file_stream.h"
 #include "net/base/io_buffer.h"
diff --git a/content/browser/loader/resource_controller.h b/content/browser/loader/resource_controller.h
new file mode 100644
index 0000000..f21a203
--- /dev/null
+++ b/content/browser/loader/resource_controller.h
@@ -0,0 +1,30 @@
+// 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_LOADER_RESOURCE_CONTROLLER_H_
+#define CONTENT_BROWSER_LOADER_RESOURCE_CONTROLLER_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Used to either resume a deferred resource load or cancel a resource load at
+// any time.  CancelAndIgnore is a variation of Cancel that also causes the
+// requester of the resource to act like the request was never made.  By
+// default, load is cancelled with ERR_ABORTED code. CancelWithError can be used
+// to cancel load with any other error code.
+class CONTENT_EXPORT ResourceController {
+ public:
+  virtual void Cancel() = 0;
+  virtual void CancelAndIgnore() = 0;
+  virtual void CancelWithError(int error_code) = 0;
+  virtual void Resume() = 0;
+
+ protected:
+  virtual ~ResourceController() {}
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_LOADER_RESOURCE_CONTROLLER_H_
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index fad7d214..e9253e5 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -34,6 +34,7 @@
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/timer/timer.h"
 #include "content/browser/appcache/appcache_interceptor.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
@@ -1067,7 +1068,7 @@
     const ResourceRequest& request_data,
     mojom::URLLoaderAssociatedRequest mojo_request,
     mojom::URLLoaderClientAssociatedPtr url_loader_client) {
-  DCHECK(requester_info->IsRenderer());
+  DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
@@ -1273,11 +1274,14 @@
     int route_id,
     mojom::URLLoaderAssociatedRequest mojo_request,
     mojom::URLLoaderClientAssociatedPtr url_loader_client) {
-  DCHECK(requester_info->IsRenderer());
+  DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
   int child_id = requester_info->child_id();
 
   // Reject request id that's currently in use.
   if (IsRequestIDInUse(GlobalRequestID(child_id, request_id))) {
+    // Navigation preload requests have child_id's of -1 and monotonically
+    // increasing request IDs allocated by MakeRequestID.
+    DCHECK(requester_info->IsRenderer());
     bad_message::ReceivedBadMessage(requester_info->filter(),
                                     bad_message::RDH_INVALID_REQUEST_ID);
     return;
@@ -1289,6 +1293,8 @@
       IsResourceTypeFrame(request_data.resource_type);
   if (is_navigation_stream_request &&
       !request_data.resource_body_stream_url.SchemeIs(url::kBlobScheme)) {
+    // The resource_type of navigation preload requests must be SUB_RESOURCE.
+    DCHECK(requester_info->IsRenderer());
     bad_message::ReceivedBadMessage(requester_info->filter(),
                                     bad_message::RDH_INVALID_URL);
     return;
@@ -1297,6 +1303,9 @@
   // Reject invalid priority.
   if (request_data.priority < net::MINIMUM_PRIORITY ||
       request_data.priority > net::MAXIMUM_PRIORITY) {
+    // The priority of navigation preload requests are copied from the original
+    // request priority which must be checked beforehand.
+    DCHECK(requester_info->IsRenderer());
     bad_message::ReceivedBadMessage(requester_info->filter(),
                                     bad_message::RDH_INVALID_PRIORITY);
     return;
@@ -1378,12 +1387,14 @@
     mojom::URLLoaderClientAssociatedPtr url_loader_client,
     bool continue_request,
     int error_code) {
-  DCHECK(requester_info->IsRenderer());
+  DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
   if (!continue_request) {
-    // TODO(ananta): Find a way to specify the right error code here.  Passing
-    // in a non-content error code is not safe.
-    bad_message::ReceivedBadMessage(requester_info->filter(),
-                                    bad_message::RDH_ILLEGAL_ORIGIN);
+    if (requester_info->IsRenderer()) {
+      // TODO(ananta): Find a way to specify the right error code here. Passing
+      // in a non-content error code is not safe.
+      bad_message::ReceivedBadMessage(requester_info->filter(),
+                                      bad_message::RDH_ILLEGAL_ORIGIN);
+    }
     AbortRequestBeforeItStarts(requester_info->filter(), sync_result_handler,
                                request_id, std::move(url_loader_client));
     return;
@@ -1613,7 +1624,7 @@
     ResourceContext* resource_context,
     mojom::URLLoaderAssociatedRequest mojo_request,
     mojom::URLLoaderClientAssociatedPtr url_loader_client) {
-  DCHECK(requester_info->IsRenderer());
+  DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
@@ -1623,6 +1634,7 @@
   if (sync_result_handler) {
     // download_to_file is not supported for synchronous requests.
     if (request_data.download_to_file) {
+      DCHECK(requester_info->IsRenderer());
       bad_message::ReceivedBadMessage(requester_info->filter(),
                                       bad_message::RDH_BAD_DOWNLOAD);
       return std::unique_ptr<ResourceHandler>();
@@ -2112,7 +2124,8 @@
     const NavigationRequestInfo& info,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     NavigationURLLoaderImplCore* loader,
-    ServiceWorkerNavigationHandleCore* service_worker_handle_core) {
+    ServiceWorkerNavigationHandleCore* service_worker_handle_core,
+    AppCacheNavigationHandleCore* appcache_handle_core) {
   // PlzNavigate: BeginNavigationRequest currently should only be used for the
   // browser-side navigations project.
   CHECK(IsBrowserSideNavigationEnabled());
@@ -2268,21 +2281,25 @@
       info.begin_params.request_context_type, frame_type,
       info.are_ancestors_secure, info.common_params.post_data);
 
-  // TODO(davidben): Attach AppCacheInterceptor.
+  // Have the appcache associate its extra info with the request.
+  if (appcache_handle_core) {
+    AppCacheInterceptor::SetExtraRequestInfoForHost(
+        new_request.get(), appcache_handle_core->host(), resource_type, false);
+  }
 
   std::unique_ptr<ResourceHandler> handler(
       new NavigationResourceHandler(new_request.get(), loader, delegate()));
 
-  // TODO(davidben): Pass in the appropriate appcache_service. Also fix the
-  // dependency on child_id/route_id. Those are used by the ResourceScheduler;
-  // currently it's a no-op.
-  handler =
-      AddStandardHandlers(new_request.get(), resource_type, resource_context,
-                          info.begin_params.request_context_type,
-                          nullptr,  // appcache_service
-                          -1,       // child_id
-                          -1,       // route_id
-                          std::move(handler));
+  // TODO(davidben): Fix the dependency on child_id/route_id. Those are used
+  // by the ResourceScheduler. currently it's a no-op.
+  handler = AddStandardHandlers(
+      new_request.get(), resource_type, resource_context,
+      info.begin_params.request_context_type,
+      appcache_handle_core ? appcache_handle_core->GetAppCacheService()
+                           : nullptr,
+      -1,  // child_id
+      -1,  // route_id
+      std::move(handler));
 
   BeginRequestInternal(std::move(new_request), std::move(handler));
 }
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index 5d02f1d..90a0112 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -54,6 +54,7 @@
 }
 
 namespace content {
+class AppCacheNavigationHandleCore;
 class AppCacheService;
 class AsyncRevalidationManager;
 class LoaderDelegate;
@@ -277,7 +278,8 @@
       const NavigationRequestInfo& info,
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       NavigationURLLoaderImplCore* loader,
-      ServiceWorkerNavigationHandleCore* service_worker_handle_core);
+      ServiceWorkerNavigationHandleCore* service_worker_handle_core,
+      AppCacheNavigationHandleCore* appcache_handle_core);
 
   int num_in_flight_requests_for_testing() const {
     return num_in_flight_requests_;
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index 3f2555a..b5322891 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -672,9 +672,9 @@
 
     if (flags_ & CANCEL_BEFORE_START) {
       if (error_code_for_cancellation_ == USE_DEFAULT_CANCEL_ERROR_CODE) {
-        controller()->Cancel();
+        Cancel();
       } else {
-        controller()->CancelWithError(error_code_for_cancellation_);
+        CancelWithError(error_code_for_cancellation_);
       }
     }
   }
@@ -688,9 +688,9 @@
 
     if (flags_ & CANCEL_PROCESSING_RESPONSE) {
       if (error_code_for_cancellation_ == USE_DEFAULT_CANCEL_ERROR_CODE) {
-        controller()->Cancel();
+        Cancel();
       } else {
-        controller()->CancelWithError(error_code_for_cancellation_);
+        CancelWithError(error_code_for_cancellation_);
       }
     }
   }
@@ -699,10 +699,10 @@
     return "GenericResourceThrottle";
   }
 
-  void Resume() {
+  void AssertAndResume() {
     ASSERT_TRUE(this == active_throttle_);
     active_throttle_ = NULL;
-    controller()->Resume();
+    ResourceThrottle::Resume();
   }
 
   static GenericResourceThrottle* active_throttle() {
@@ -1093,7 +1093,7 @@
       std::unique_ptr<NavigationURLLoader> test_loader =
           NavigationURLLoader::Create(browser_context_.get(),
                                       std::move(request_info), nullptr, nullptr,
-                                      &delegate);
+                                      nullptr, &delegate);
 
       // The navigation should fail with the expected error code.
       delegate.WaitForRequestFailed();
@@ -1779,7 +1779,7 @@
   GenericResourceThrottle* throttle =
       GenericResourceThrottle::active_throttle();
   ASSERT_TRUE(throttle);
-  throttle->Resume();
+  throttle->AssertAndResume();
 
   // Now, the request completes.
   while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
@@ -1843,12 +1843,12 @@
   GenericResourceThrottle* first_throttle =
       GenericResourceThrottle::active_throttle();
   ASSERT_TRUE(first_throttle);
-  first_throttle->Resume();
+  first_throttle->AssertAndResume();
 
   // Make sure the second throttle blocked the request, and then resume.
   ASSERT_TRUE(GenericResourceThrottle::active_throttle());
   ASSERT_NE(first_throttle, GenericResourceThrottle::active_throttle());
-  GenericResourceThrottle::active_throttle()->Resume();
+  GenericResourceThrottle::active_throttle()->AssertAndResume();
 
   ASSERT_FALSE(GenericResourceThrottle::active_throttle());
 
@@ -2649,7 +2649,7 @@
                                   -1, false, false));
     std::unique_ptr<NavigationURLLoader> loader = NavigationURLLoader::Create(
         browser_context_.get(), std::move(request_info), nullptr, nullptr,
-        &delegate);
+        nullptr, &delegate);
 
     // Wait until a response has been received and proceed with the response.
     KickOffRequest();
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index 0b3833a6..bb77021 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -350,13 +350,13 @@
 
   CompleteResponseStarted();
 
-  if (is_deferred())
+  // If the handler deferred the request, it will resume the request later. If
+  // the request was cancelled, the request will call back into |this| with a
+  // bogus read completed error.
+  if (is_deferred() || !request_->status().is_success())
     return;
 
-  if (request_->status().is_success())
-    StartReading(false);  // Read the first chunk.
-  else
-    ResponseCompleted();
+  ReadMore(false);  // Read the first chunk.
 }
 
 void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
@@ -375,17 +375,14 @@
   CompleteRead(bytes_read);
 
   // If the handler cancelled or deferred the request, do not continue
-  // processing the read. If cancelled, the URLRequest has already been
-  // cancelled and will schedule an erroring OnReadCompleted later. If deferred,
-  // do nothing until resumed.
-  //
-  // Note: if bytes_read is 0 (EOF) and the handler defers, resumption will call
-  // ResponseCompleted().
+  // processing the read. If canceled, either the request will call into |this|
+  // with a bogus read error, or, if the request was completed, a task posted
+  // from ResourceLoader::CancelREquestInternal will run OnResponseCompleted.
   if (is_deferred() || !request_->status().is_success())
     return;
 
   if (bytes_read > 0) {
-    StartReading(true);  // Read the next chunk.
+    ReadMore(true);  // Read the next chunk.
   } else {
     // TODO(darin): Remove ScopedTracker below once crbug.com/475761 is fixed.
     tracked_objects::ScopedTracker tracking_profile(
@@ -557,42 +554,7 @@
   }
 }
 
-void ResourceLoader::StartReading(bool is_continuation) {
-  int bytes_read = 0;
-  ReadMore(&bytes_read);
-
-  // If IO is pending, wait for the URLRequest to call OnReadCompleted.
-  if (request_->status().is_io_pending())
-    return;
-
-  if (!is_continuation || bytes_read <= 0) {
-    OnReadCompleted(request_.get(), bytes_read);
-  } else {
-    // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
-    // thread in case the URLRequest can provide data synchronously.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&ResourceLoader::OnReadCompleted,
-                   weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
-  }
-}
-
-void ResourceLoader::ResumeReading() {
-  DCHECK(!is_deferred());
-
-  if (!read_deferral_start_time_.is_null()) {
-    UMA_HISTOGRAM_TIMES("Net.ResourceLoader.ReadDeferral",
-                        base::TimeTicks::Now() - read_deferral_start_time_);
-    read_deferral_start_time_ = base::TimeTicks();
-  }
-  if (request_->status().is_success()) {
-    StartReading(false);  // Read the next chunk (OK to complete synchronously).
-  } else {
-    ResponseCompleted();
-  }
-}
-
-void ResourceLoader::ReadMore(int* bytes_read) {
+void ResourceLoader::ReadMore(bool is_continuation) {
   TRACE_EVENT_WITH_FLOW0("loading", "ResourceLoader::ReadMore", this,
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
   DCHECK(!is_deferred());
@@ -608,6 +570,8 @@
         FROM_HERE_WITH_EXPLICIT_FUNCTION("475761 OnWillRead()"));
 
     if (!handler_->OnWillRead(&buf, &buf_size, -1)) {
+      // Cancel the request, which will then call back into |this| to inform it
+      // of a "read error".
       Cancel();
       return;
     }
@@ -616,10 +580,36 @@
   DCHECK(buf.get());
   DCHECK(buf_size > 0);
 
-  request_->Read(buf.get(), buf_size, bytes_read);
+  int result = request_->Read(buf.get(), buf_size);
 
-  // No need to check the return value here as we'll detect errors by
-  // inspecting the URLRequest's status.
+  if (result == net::ERR_IO_PENDING)
+    return;
+
+  if (!is_continuation || result <= 0) {
+    OnReadCompleted(request_.get(), result);
+  } else {
+    // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
+    // thread in case the URLRequest can provide data synchronously.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&ResourceLoader::OnReadCompleted,
+                   weak_ptr_factory_.GetWeakPtr(), request_.get(), result));
+  }
+}
+
+void ResourceLoader::ResumeReading() {
+  DCHECK(!is_deferred());
+
+  if (!read_deferral_start_time_.is_null()) {
+    UMA_HISTOGRAM_TIMES("Net.ResourceLoader.ReadDeferral",
+                        base::TimeTicks::Now() - read_deferral_start_time_);
+    read_deferral_start_time_ = base::TimeTicks();
+  }
+  if (request_->status().is_success()) {
+    ReadMore(false);  // Read the next chunk (OK to complete synchronously).
+  } else {
+    ResponseCompleted();
+  }
 }
 
 void ResourceLoader::CompleteRead(int bytes_read) {
diff --git a/content/browser/loader/resource_loader.h b/content/browser/loader/resource_loader.h
index 1dd2f57..43b7530 100644
--- a/content/browser/loader/resource_loader.h
+++ b/content/browser/loader/resource_loader.h
@@ -11,10 +11,10 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/ssl/ssl_client_auth_handler.h"
 #include "content/browser/ssl/ssl_error_handler.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/resource_controller.h"
 #include "net/url_request/url_request.h"
 
 namespace net {
@@ -84,9 +84,8 @@
   void StartRequestInternal();
   void CancelRequestInternal(int error, bool from_renderer);
   void CompleteResponseStarted();
-  void StartReading(bool is_continuation);
+  void ReadMore(bool is_continuation);
   void ResumeReading();
-  void ReadMore(int* bytes_read);
   // Passes a read result to the handler.
   void CompleteRead(int bytes_read);
   void ResponseCompleted();
diff --git a/content/browser/loader/resource_loader_unittest.cc b/content/browser/loader/resource_loader_unittest.cc
index 6ca2ac10..7aade94 100644
--- a/content/browser/loader/resource_loader_unittest.cc
+++ b/content/browser/loader/resource_loader_unittest.cc
@@ -22,6 +22,7 @@
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/loader/redirect_to_file_resource_handler.h"
 #include "content/browser/loader/resource_loader_delegate.h"
+#include "content/browser/loader/test_resource_handler.h"
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/content_paths.h"
@@ -49,6 +50,7 @@
 #include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/test_data_directory.h"
+#include "net/test/url_request/url_request_failed_job.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_interceptor.h"
@@ -265,177 +267,6 @@
 };
 
 // Arbitrary read buffer size.
-const int kReadBufSize = 1024;
-
-// Dummy implementation of ResourceHandler, instance of which is needed to
-// initialize ResourceLoader.
-class ResourceHandlerStub : public ResourceHandler {
- public:
-  explicit ResourceHandlerStub(net::URLRequest* request)
-      : ResourceHandler(request),
-        read_buffer_(new net::IOBuffer(kReadBufSize)),
-        defer_request_on_will_start_(false),
-        expect_reads_(true),
-        cancel_on_read_completed_(false),
-        defer_eof_(false),
-        received_on_will_read_(false),
-        received_eof_(false),
-        received_response_completed_(false),
-        received_request_redirected_(false),
-        total_bytes_downloaded_(0),
-        observed_effective_connection_type_(
-            net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {}
-
-  // If true, defers the resource load in OnWillStart.
-  void set_defer_request_on_will_start(bool defer_request_on_will_start) {
-    defer_request_on_will_start_ = defer_request_on_will_start;
-  }
-
-  // If true, expect OnWillRead / OnReadCompleted pairs for handling
-  // data. Otherwise, expect OnDataDownloaded.
-  void set_expect_reads(bool expect_reads) { expect_reads_ = expect_reads; }
-
-  // If true, cancel the request in OnReadCompleted by returning false.
-  void set_cancel_on_read_completed(bool cancel_on_read_completed) {
-    cancel_on_read_completed_ = cancel_on_read_completed;
-  }
-
-  // If true, cancel the request in OnReadCompleted by returning false.
-  void set_defer_eof(bool defer_eof) { defer_eof_ = defer_eof; }
-
-  const GURL& start_url() const { return start_url_; }
-  ResourceResponse* response() const { return response_.get(); }
-  ResourceResponse* redirect_response() const {
-    return redirect_response_.get();
-  }
-  bool received_response_completed() const {
-    return received_response_completed_;
-  }
-  bool received_request_redirected() const {
-    return received_request_redirected_;
-  }
-  const net::URLRequestStatus& status() const { return status_; }
-  int total_bytes_downloaded() const { return total_bytes_downloaded_; }
-
-  net::EffectiveConnectionType observed_effective_connection_type() const {
-    return observed_effective_connection_type_;
-  }
-
-  void Resume() {
-    controller()->Resume();
-  }
-
-  bool OnRequestRedirected(const net::RedirectInfo& redirect_info,
-                           ResourceResponse* response,
-                           bool* defer) override {
-    redirect_response_ = response;
-    received_request_redirected_ = true;
-    return true;
-  }
-
-  bool OnResponseStarted(ResourceResponse* response, bool* defer) override {
-    EXPECT_FALSE(response_.get());
-    response_ = response;
-    observed_effective_connection_type_ =
-        response->head.effective_connection_type;
-    return true;
-  }
-
-  bool OnWillStart(const GURL& url, bool* defer) override {
-    EXPECT_TRUE(start_url_.is_empty());
-    start_url_ = url;
-    if (defer_request_on_will_start_) {
-      *defer = true;
-      deferred_run_loop_.Quit();
-    }
-    return true;
-  }
-
-  bool OnWillRead(scoped_refptr<net::IOBuffer>* buf,
-                  int* buf_size,
-                  int min_size) override {
-    EXPECT_TRUE(expect_reads_);
-    EXPECT_FALSE(received_on_will_read_);
-    EXPECT_FALSE(received_eof_);
-    EXPECT_FALSE(received_response_completed_);
-
-    *buf = read_buffer_;
-    *buf_size = kReadBufSize;
-    received_on_will_read_ = true;
-    return true;
-  }
-
-  bool OnReadCompleted(int bytes_read, bool* defer) override {
-    EXPECT_TRUE(received_on_will_read_);
-    EXPECT_TRUE(expect_reads_);
-    EXPECT_FALSE(received_response_completed_);
-
-    if (bytes_read == 0) {
-      received_eof_ = true;
-      if (defer_eof_) {
-        defer_eof_ = false;
-        *defer = true;
-        deferred_run_loop_.Quit();
-      }
-    }
-
-    // Need another OnWillRead() call before seeing an OnReadCompleted().
-    received_on_will_read_ = false;
-
-    return !cancel_on_read_completed_;
-  }
-
-  void OnResponseCompleted(const net::URLRequestStatus& status,
-                           bool* defer) override {
-    EXPECT_FALSE(received_response_completed_);
-    if (status.is_success() && expect_reads_)
-      EXPECT_TRUE(received_eof_);
-
-    received_response_completed_ = true;
-    status_ = status;
-    response_completed_run_loop_.Quit();
-  }
-
-  void OnDataDownloaded(int bytes_downloaded) override {
-    EXPECT_FALSE(expect_reads_);
-    total_bytes_downloaded_ += bytes_downloaded;
-  }
-
-  // Waits for the the first deferred step to run, if there is one.
-  void WaitForDeferredStep() {
-    DCHECK(defer_request_on_will_start_ || defer_eof_);
-    deferred_run_loop_.Run();
-  }
-
-  // Waits until the response has completed.
-  void WaitForResponseComplete() {
-    response_completed_run_loop_.Run();
-    EXPECT_TRUE(received_response_completed_);
-  }
-
- private:
-  scoped_refptr<net::IOBuffer> read_buffer_;
-
-  bool defer_request_on_will_start_;
-  bool expect_reads_;
-  bool cancel_on_read_completed_;
-  bool defer_eof_;
-
-  GURL start_url_;
-  scoped_refptr<ResourceResponse> response_;
-  scoped_refptr<ResourceResponse> redirect_response_;
-  bool received_on_will_read_;
-  bool received_eof_;
-  bool received_response_completed_;
-  bool received_request_redirected_;
-  net::URLRequestStatus status_;
-  int total_bytes_downloaded_;
-  base::RunLoop deferred_run_loop_;
-  base::RunLoop response_completed_run_loop_;
-  std::unique_ptr<base::RunLoop> wait_for_progress_run_loop_;
-  net::EffectiveConnectionType observed_effective_connection_type_;
-};
-
 // Test browser client that captures calls to SelectClientCertificates and
 // records the arguments of the most recent call for later inspection.
 class SelectCertificateBrowserClient : public TestContentBrowserClient {
@@ -565,23 +396,30 @@
     test_url_request_context_.Init();
   }
 
-  GURL test_url() const { return net::URLRequestTestJob::test_url_1(); }
+  // URL with a response body of test_data().
+  GURL test_direct_url() const { return net::URLRequestTestJob::test_url_1(); }
 
-  TestNetworkQualityEstimator* network_quality_estimator() {
-    return &network_quality_estimator_;
+  // URL that redirects to test_direct_url(). The ResourceLoader is set up to
+  // use this URL by default.
+  GURL test_redirect_url() const {
+    return net::URLRequestTestJob::test_url_redirect_to_url_1();
   }
 
   std::string test_data() const {
     return net::URLRequestTestJob::test_data_1();
   }
 
+  TestNetworkQualityEstimator* network_quality_estimator() {
+    return &network_quality_estimator_;
+  }
+
   virtual std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>
   CreateProtocolHandler() {
     return net::URLRequestTestJob::CreateProtocolHandler();
   }
 
   virtual std::unique_ptr<ResourceHandler> WrapResourceHandler(
-      std::unique_ptr<ResourceHandlerStub> leaf_handler,
+      std::unique_ptr<TestResourceHandler> leaf_handler,
       net::URLRequest* request) {
     return std::move(leaf_handler);
   }
@@ -604,8 +442,8 @@
         rfh->GetRoutingID(), belongs_to_main_frame,
         false /* parent_is_main_frame */, true /* allow_download */,
         false /* is_async */, false /* is_using_lofi_ */);
-    std::unique_ptr<ResourceHandlerStub> resource_handler(
-        new ResourceHandlerStub(request.get()));
+    std::unique_ptr<TestResourceHandler> resource_handler(
+        new TestResourceHandler(nullptr, nullptr));
     raw_ptr_resource_handler_ = resource_handler.get();
     loader_.reset(new ResourceLoader(
         std::move(request),
@@ -613,19 +451,23 @@
         this));
   }
 
+  void SetUpResourceLoaderForUrl(const GURL& test_url) {
+    std::unique_ptr<net::URLRequest> request(
+        resource_context_.GetRequestContext()->CreateRequest(
+            test_url, net::DEFAULT_PRIORITY, nullptr /* delegate */));
+    SetUpResourceLoader(std::move(request), RESOURCE_TYPE_MAIN_FRAME, true);
+  }
+
   void SetUp() override {
     job_factory_.SetProtocolHandler("test", CreateProtocolHandler());
+    net::URLRequestFailedJob::AddUrlHandler();
 
     browser_context_.reset(new TestBrowserContext());
     scoped_refptr<SiteInstance> site_instance =
         SiteInstance::Create(browser_context_.get());
     web_contents_.reset(
         TestWebContents::Create(browser_context_.get(), site_instance.get()));
-
-    std::unique_ptr<net::URLRequest> request(
-        resource_context_.GetRequestContext()->CreateRequest(
-            test_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */));
-    SetUpResourceLoader(std::move(request), RESOURCE_TYPE_MAIN_FRAME, true);
+    SetUpResourceLoaderForUrl(test_redirect_url());
   }
 
   void TearDown() override {
@@ -633,6 +475,10 @@
     // |rvh_test_enabler_| and |thread_bundle_|. This lets asynchronous cleanup
     // tasks complete.
     web_contents_.reset();
+
+    // Clean up handlers.
+    net::URLRequestFilter::GetInstance()->ClearHandlers();
+
     base::RunLoop().RunUntilIdle();
   }
 
@@ -648,14 +494,36 @@
   }
   bool HandleExternalProtocol(ResourceLoader* loader,
                               const GURL& url) override {
+    EXPECT_EQ(loader, loader_.get());
     return false;
   }
-  void DidStartRequest(ResourceLoader* loader) override {}
+  void DidStartRequest(ResourceLoader* loader) override {
+    EXPECT_EQ(loader, loader_.get());
+    EXPECT_EQ(0, did_finish_loading_);
+    EXPECT_EQ(0, did_start_request_);
+    ++did_start_request_;
+  }
   void DidReceiveRedirect(ResourceLoader* loader,
                           const GURL& new_url,
-                          ResourceResponse* response) override {}
-  void DidReceiveResponse(ResourceLoader* loader) override {}
-  void DidFinishLoading(ResourceLoader* loader) override {}
+                          ResourceResponse* response) override {
+    EXPECT_EQ(loader, loader_.get());
+    EXPECT_EQ(0, did_finish_loading_);
+    EXPECT_EQ(0, did_receive_response_);
+    EXPECT_EQ(1, did_start_request_);
+    ++did_received_redirect_;
+  }
+  void DidReceiveResponse(ResourceLoader* loader) override {
+    EXPECT_EQ(loader, loader_.get());
+    EXPECT_EQ(0, did_finish_loading_);
+    EXPECT_EQ(0, did_receive_response_);
+    EXPECT_EQ(1, did_start_request_);
+    ++did_receive_response_;
+  }
+  void DidFinishLoading(ResourceLoader* loader) override {
+    EXPECT_EQ(loader, loader_.get());
+    EXPECT_EQ(0, did_finish_loading_);
+    ++did_finish_loading_;
+  }
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       ResourceLoader* loader) override {
     return std::move(dummy_cert_store_);
@@ -664,6 +532,12 @@
   TestBrowserThreadBundle thread_bundle_;
   RenderViewHostTestEnabler rvh_test_enabler_;
 
+  // Record which ResourceDispatcherHostDelegate methods have been invoked.
+  int did_start_request_ = 0;
+  int did_received_redirect_ = 0;
+  int did_receive_response_ = 0;
+  int did_finish_loading_ = 0;
+
   net::URLRequestJobFactoryImpl job_factory_;
   TestNetworkQualityEstimator network_quality_estimator_;
   net::TestURLRequestContext test_url_request_context_;
@@ -673,7 +547,7 @@
   std::unique_ptr<net::ClientCertStore> dummy_cert_store_;
 
   // The ResourceLoader owns the URLRequest and the ResourceHandler.
-  ResourceHandlerStub* raw_ptr_resource_handler_;
+  TestResourceHandler* raw_ptr_resource_handler_;
   net::URLRequest* raw_ptr_to_request_;
   std::unique_ptr<ResourceLoader> loader_;
 };
@@ -684,6 +558,12 @@
   CreateProtocolHandler() override {
     return base::WrapUnique(new MockClientCertJobProtocolHandler);
   }
+
+  void SetUp() override {
+    ResourceLoaderTest::SetUp();
+    // These tests don't expect any redirects.
+    SetUpResourceLoaderForUrl(test_direct_url());
+  }
 };
 
 // A ResourceLoaderTest that intercepts https://example.test and
@@ -740,7 +620,7 @@
   loader_->StartRequest();
   test_client.WaitForSelectCertificate();
 
-  EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
 
   // Check if the test store was queried against correct |cert_authorities|.
   EXPECT_EQ(1, store_request_count);
@@ -754,8 +634,8 @@
 
   // Continue the request.
   test_client.ContinueWithCertificate(nullptr);
-  raw_ptr_resource_handler_->WaitForResponseComplete();
-  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->status().error());
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
 
   // Restore the original content browser client.
   SetBrowserClientForTesting(old_client);
@@ -779,8 +659,8 @@
 
   // Continue the request.
   test_client.ContinueWithCertificate(nullptr);
-  raw_ptr_resource_handler_->WaitForResponseComplete();
-  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->status().error());
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
 
   // Restore the original content browser client.
   SetBrowserClientForTesting(old_client);
@@ -803,9 +683,9 @@
 
   // Cancel the request.
   test_client.CancelCertificateSelection();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
   EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
-            raw_ptr_resource_handler_->status().error());
+            raw_ptr_resource_handler_->final_status().error());
 
   // Restore the original content browser client.
   SetBrowserClientForTesting(old_client);
@@ -822,12 +702,12 @@
 
   // Start the request and wait for it to complete.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // Check that SelectClientCertificate wasn't called and the request aborted.
   EXPECT_EQ(0, test_client.call_count());
   EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
-            raw_ptr_resource_handler_->status().error());
+            raw_ptr_resource_handler_->final_status().error());
 
   // Restore the original content browser client.
   SetBrowserClientForTesting(old_client);
@@ -850,43 +730,424 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ResourceLoaderTest, ResumeCancelledRequest) {
-  raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
+// Test the case the ResourceHandler defers nothing.
+TEST_F(ResourceLoaderTest, SyncResourceHandler) {
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  EXPECT_EQ(1, did_start_request_);
+  EXPECT_EQ(1, did_received_redirect_);
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+}
+
+// Test the case the ResourceHandler defers everything.
+TEST_F(ResourceLoaderTest, AsyncResourceHandler) {
+  raw_ptr_resource_handler_->set_defer_on_will_start(true);
+  raw_ptr_resource_handler_->set_defer_on_request_redirected(true);
+  raw_ptr_resource_handler_->set_defer_on_response_started(true);
+  raw_ptr_resource_handler_->set_defer_on_read_completed(true);
+  raw_ptr_resource_handler_->set_defer_on_read_eof(true);
+  raw_ptr_resource_handler_->set_defer_on_response_completed(true);
+
+  // Start and run until OnWillStart.
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+
+  // Spinning the message loop should not advance the state further.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_start_request_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  // Resume and run until OnRequestRedirected.
+  raw_ptr_resource_handler_->Resume();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
+
+  // Spinning the message loop should not advance the state further.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_received_redirect_);
+  EXPECT_EQ(0, did_receive_response_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  // Resume and run until OnResponseStarted.
+  raw_ptr_resource_handler_->Resume();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+
+  // Spinning the message loop should not advance the state further.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(0, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  // Resume and run until OnReadCompleted.
+  raw_ptr_resource_handler_->Resume();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
+
+  // Spinning the message loop should not advance the state further.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  // Resume and run until the final 0-byte read, signalling EOF.
+  raw_ptr_resource_handler_->Resume();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof());
+
+  // Spinning the message loop should not advance the state further.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+
+  // Resume and run until OnResponseCompleted is called, which again defers the
+  // request.
+  raw_ptr_resource_handler_->Resume();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  EXPECT_EQ(0, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+
+  // Resume and run until all pending tasks. Note that OnResponseCompleted was
+  // invoked in the previous section, so can't use RunUntilCompleted().
+  raw_ptr_resource_handler_->Resume();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::OK, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnWillStart) {
+  raw_ptr_resource_handler_->set_on_will_start_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_start_request_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnRequestRedirected) {
+  raw_ptr_resource_handler_->set_on_request_redirected_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_received_redirect_);
+  EXPECT_EQ(0, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnResponseStarted) {
+  raw_ptr_resource_handler_->set_on_response_started_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnWillRead) {
+  raw_ptr_resource_handler_->set_on_will_read_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnReadCompleted) {
+  raw_ptr_resource_handler_->set_on_read_completed_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
+}
+
+TEST_F(ResourceLoaderTest, SyncCancelOnReceivedEof) {
+  raw_ptr_resource_handler_->set_on_on_read_eof_result(false);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_ABORTED,
+            raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, AsyncCancelOnWillStart) {
+  raw_ptr_resource_handler_->set_defer_on_will_start(true);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_start_request_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, AsyncCancelOnRequestRedirected) {
+  raw_ptr_resource_handler_->set_defer_on_request_redirected(true);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_received_redirect_);
+  EXPECT_EQ(0, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, AsyncCancelOnResponseStarted) {
+  raw_ptr_resource_handler_->set_defer_on_response_started(true);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_will_read_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, AsyncCancelOnReadCompleted) {
+  raw_ptr_resource_handler_->set_defer_on_read_completed(true);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_LT(0u, raw_ptr_resource_handler_->body().size());
+}
+
+TEST_F(ResourceLoaderTest, AsyncCancelOnReceivedEof) {
+  raw_ptr_resource_handler_->set_defer_on_read_eof(true);
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_read_eof());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ(test_data(), raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, RequestFailsOnStart) {
+  SetUpResourceLoaderForUrl(
+      net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
+          net::URLRequestFailedJob::START, net::ERR_FAILED));
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_received_redirect_);
+  EXPECT_EQ(0, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, RequestFailsOnReadSync) {
+  SetUpResourceLoaderForUrl(
+      net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
+          net::URLRequestFailedJob::READ_SYNC, net::ERR_FAILED));
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_received_redirect_);
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, RequestFailsOnReadAsync) {
+  SetUpResourceLoaderForUrl(
+      net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
+          net::URLRequestFailedJob::READ_ASYNC, net::ERR_FAILED));
+
+  loader_->StartRequest();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, did_received_redirect_);
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, OutOfBandCancelDuringStart) {
+  SetUpResourceLoaderForUrl(
+      net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
+          net::URLRequestFailedJob::START, net::ERR_IO_PENDING));
+
+  loader_->StartRequest();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+
+  EXPECT_EQ(0, did_received_redirect_);
+  EXPECT_EQ(0, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, OutOfBandCancelDuringRead) {
+  SetUpResourceLoaderForUrl(
+      net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
+          net::URLRequestFailedJob::READ_SYNC, net::ERR_IO_PENDING));
+
+  loader_->StartRequest();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
+
+  raw_ptr_resource_handler_->CancelWithError(net::ERR_FAILED);
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
+  EXPECT_EQ(0, did_received_redirect_);
+  EXPECT_EQ(1, did_receive_response_);
+  EXPECT_EQ(1, did_finish_loading_);
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_will_start_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_request_redirected_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_started_called());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_read_completed_called());
+  EXPECT_EQ(1, raw_ptr_resource_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, raw_ptr_resource_handler_->final_status().error());
+  EXPECT_EQ("", raw_ptr_resource_handler_->body());
+}
+
+TEST_F(ResourceLoaderTest, ResumeCanceledRequest) {
+  raw_ptr_resource_handler_->set_defer_on_will_start(true);
 
   loader_->StartRequest();
   loader_->CancelRequest(true);
   static_cast<ResourceController*>(loader_.get())->Resume();
 }
 
-// Tests that no invariants are broken if a ResourceHandler cancels during
-// OnReadCompleted.
-TEST_F(ResourceLoaderTest, CancelOnReadCompleted) {
-  raw_ptr_resource_handler_->set_cancel_on_read_completed(true);
-
-  loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
-
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
-  EXPECT_EQ(net::URLRequestStatus::CANCELED,
-            raw_ptr_resource_handler_->status().status());
-}
-
-// Tests that no invariants are broken if a ResourceHandler defers EOF.
-TEST_F(ResourceLoaderTest, DeferEOF) {
-  raw_ptr_resource_handler_->set_defer_eof(true);
-
-  loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForDeferredStep();
-
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
-  EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
-
-  raw_ptr_resource_handler_->Resume();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
-  EXPECT_EQ(net::URLRequestStatus::SUCCESS,
-            raw_ptr_resource_handler_->status().status());
-}
-
 class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest {
  public:
   ResourceLoaderRedirectToFileTest()
@@ -916,9 +1177,9 @@
   }
 
   std::unique_ptr<ResourceHandler> WrapResourceHandler(
-      std::unique_ptr<ResourceHandlerStub> leaf_handler,
+      std::unique_ptr<TestResourceHandler> leaf_handler,
       net::URLRequest* request) override {
-    leaf_handler->set_expect_reads(false);
+    leaf_handler->set_expect_on_data_downloaded(true);
 
     // Make a temporary file.
     CHECK(base::CreateTemporaryFile(&temp_path_));
@@ -968,14 +1229,15 @@
 TEST_F(ResourceLoaderRedirectToFileTest, Basic) {
   // Run it to completion.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // Check that the handler forwarded all information to the downstream handler.
-  EXPECT_EQ(temp_path(),
-            raw_ptr_resource_handler_->response()->head.download_file_path);
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_EQ(
+      temp_path(),
+      raw_ptr_resource_handler_->resource_response()->head.download_file_path);
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
   EXPECT_EQ(test_data().size(), static_cast<size_t>(
       raw_ptr_resource_handler_->total_bytes_downloaded()));
 
@@ -995,11 +1257,11 @@
 
   // Run it to completion.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // To downstream, the request was canceled.
   EXPECT_EQ(net::URLRequestStatus::CANCELED,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
 }
 
@@ -1009,15 +1271,16 @@
 
   // Run it to completion.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // To downstream, the request was canceled sometime after it started, but
   // before any data was written.
-  EXPECT_EQ(temp_path(),
-            raw_ptr_resource_handler_->response()->head.download_file_path);
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_EQ(
+      temp_path(),
+      raw_ptr_resource_handler_->resource_response()->head.download_file_path);
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
   EXPECT_EQ(net::URLRequestStatus::CANCELED,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
 }
 
@@ -1027,15 +1290,16 @@
 
   // Run it to completion.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // To downstream, the request was canceled sometime after it started, but
   // before any data was written.
-  EXPECT_EQ(temp_path(),
-            raw_ptr_resource_handler_->response()->head.download_file_path);
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_EQ(
+      temp_path(),
+      raw_ptr_resource_handler_->resource_response()->head.download_file_path);
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
   EXPECT_EQ(net::URLRequestStatus::CANCELED,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
 }
 
@@ -1057,48 +1321,50 @@
 
   // However, the resource loader stack is stuck somewhere after receiving the
   // response.
-  EXPECT_EQ(temp_path(),
-            raw_ptr_resource_handler_->response()->head.download_file_path);
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
-  EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+  EXPECT_EQ(
+      temp_path(),
+      raw_ptr_resource_handler_->resource_response()->head.download_file_path);
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
 
   // Now, release the floodgates.
   file_stream()->ReleaseCallbacks();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // Although the URLRequest was successful, the leaf handler sees a failure
   // because the write never completed.
   EXPECT_EQ(net::URLRequestStatus::CANCELED,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
 }
 
 // Tests that a RedirectToFileResourceHandler behaves properly when the
 // downstream handler defers OnWillStart.
 TEST_F(ResourceLoaderRedirectToFileTest, DownstreamDeferStart) {
   // Defer OnWillStart.
-  raw_ptr_resource_handler_->set_defer_request_on_will_start(true);
+  raw_ptr_resource_handler_->set_defer_on_will_start(true);
 
   // Run as far as we'll go.
   loader_->StartRequest();
-  raw_ptr_resource_handler_->WaitForDeferredStep();
+  raw_ptr_resource_handler_->WaitUntilDeferred();
 
   // The request should have stopped at OnWillStart.
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
-  EXPECT_FALSE(raw_ptr_resource_handler_->response());
-  EXPECT_FALSE(raw_ptr_resource_handler_->received_response_completed());
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_FALSE(raw_ptr_resource_handler_->resource_response());
+  EXPECT_EQ(0, raw_ptr_resource_handler_->on_response_completed_called());
   EXPECT_EQ(0, raw_ptr_resource_handler_->total_bytes_downloaded());
 
   // Now resume the request. Now we complete.
   raw_ptr_resource_handler_->Resume();
-  raw_ptr_resource_handler_->WaitForResponseComplete();
+  raw_ptr_resource_handler_->WaitUntilResponseComplete();
 
   // Check that the handler forwarded all information to the downstream handler.
-  EXPECT_EQ(temp_path(),
-            raw_ptr_resource_handler_->response()->head.download_file_path);
-  EXPECT_EQ(test_url(), raw_ptr_resource_handler_->start_url());
+  EXPECT_EQ(
+      temp_path(),
+      raw_ptr_resource_handler_->resource_response()->head.download_file_path);
+  EXPECT_EQ(test_redirect_url(), raw_ptr_resource_handler_->start_url());
   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
-            raw_ptr_resource_handler_->status().status());
+            raw_ptr_resource_handler_->final_status().status());
   EXPECT_EQ(test_data().size(), static_cast<size_t>(
       raw_ptr_resource_handler_->total_bytes_downloaded()));
 
@@ -1120,18 +1386,19 @@
     // Start the request and wait for it to finish.
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
-            test_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */));
+            test_redirect_url(), net::DEFAULT_PRIORITY,
+            nullptr /* delegate */));
     SetUpResourceLoader(std::move(request), resource_type,
                         belongs_to_main_frame);
 
     // Send the request and wait until it completes.
     loader_->StartRequest();
-    raw_ptr_resource_handler_->WaitForResponseComplete();
+    raw_ptr_resource_handler_->WaitUntilResponseComplete();
     ASSERT_EQ(net::URLRequestStatus::SUCCESS,
               raw_ptr_to_request_->status().status());
 
-    EXPECT_EQ(expected_type,
-              raw_ptr_resource_handler_->observed_effective_connection_type());
+    EXPECT_EQ(expected_type, raw_ptr_resource_handler_->resource_response()
+                                 ->head.effective_connection_type);
   }
 };
 
diff --git a/content/browser/loader/resource_requester_info.cc b/content/browser/loader/resource_requester_info.cc
index 3e71b29..7b4f1e5 100644
--- a/content/browser/loader/resource_requester_info.cc
+++ b/content/browser/loader/resource_requester_info.cc
@@ -10,10 +10,25 @@
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 #include "storage/browser/fileapi/file_system_context.h"
 
 namespace content {
 
+namespace {
+
+void GetContextsCallbackForNavigationPreload(
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
+    ResourceType resource_type,
+    ResourceContext** resource_context_out,
+    net::URLRequestContext** request_context_out) {
+  *resource_context_out = service_worker_context->resource_context();
+  *request_context_out = (*resource_context_out)->GetRequestContext();
+}
+
+}  // namespace
+
 ResourceRequesterInfo::ResourceRequesterInfo(
     RequesterType type,
     int child_id,
@@ -85,4 +100,31 @@
       GetContextsCallback()));
 }
 
+scoped_refptr<ResourceRequesterInfo>
+ResourceRequesterInfo::CreateForNavigationPreload(
+    ResourceRequesterInfo* original_request_info) {
+  GetContextsCallback get_contexts_callback =
+      original_request_info->get_contexts_callback_;
+  if (IsBrowserSideNavigationEnabled()) {
+    DCHECK(original_request_info->IsBrowserSideNavigation());
+    DCHECK(!get_contexts_callback);
+    DCHECK(original_request_info->service_worker_context());
+    // The requester info for browser side navigation doesn't have the
+    // get_contexts_callback. So create the callback here which gets the
+    // ResourceContext and the URLRequestContext form ServiceWorkerContext.
+    get_contexts_callback =
+        base::Bind(&GetContextsCallbackForNavigationPreload,
+                   scoped_refptr<ServiceWorkerContextWrapper>(
+                       original_request_info->service_worker_context()));
+  } else {
+    DCHECK(original_request_info->IsRenderer());
+    DCHECK(get_contexts_callback);
+  }
+
+  return scoped_refptr<ResourceRequesterInfo>(new ResourceRequesterInfo(
+      RequesterType::NAVIGATION_PRELOAD, -1, nullptr /* appcache_service */,
+      nullptr /* blob_storage_context */, nullptr /* file_system_context */,
+      original_request_info->service_worker_context(), get_contexts_callback));
+}
+
 }  // namespace content
diff --git a/content/browser/loader/resource_requester_info.h b/content/browser/loader/resource_requester_info.h
index 86e39d3..1de19d6 100644
--- a/content/browser/loader/resource_requester_info.h
+++ b/content/browser/loader/resource_requester_info.h
@@ -29,11 +29,12 @@
 class ServiceWorkerContextWrapper;
 
 // This class represents the type of resource requester.
-// Currently there are three types:
+// Currently there are four types:
 // - Requesters that request resources from renderer processes.
 // - Requesters that request resources within the browser process for browser
 //   side navigation (aka PlzNavigate).
 // - Requesters that request resources for download or page save.
+// - Requesters that request service worker navigation preload requests.
 class CONTENT_EXPORT ResourceRequesterInfo
     : public base::RefCountedThreadSafe<ResourceRequesterInfo> {
  public:
@@ -67,10 +68,19 @@
   static scoped_refptr<ResourceRequesterInfo> CreateForDownloadOrPageSave(
       int child_id);
 
+  // Creates a ResourceRequesterInfo for a service worker navigation preload
+  // request. When PlzNavigate is enabled, |original_request_info| must be
+  // browser side navigation type. Otherwise it must be renderer type.
+  static scoped_refptr<ResourceRequesterInfo> CreateForNavigationPreload(
+      ResourceRequesterInfo* original_request_info);
+
   bool IsRenderer() const { return type_ == RequesterType::RENDERER; }
   bool IsBrowserSideNavigation() const {
     return type_ == RequesterType::BROWSER_SIDE_NAVIGATION;
   }
+  bool IsNavigationPreload() const {
+    return type_ == RequesterType::NAVIGATION_PRELOAD;
+  }
 
   // Returns the renderer process ID associated with the requester. Returns -1
   // for browser side navigation requester. Even if the ResourceMessageFilter
@@ -88,7 +98,7 @@
 
   // Returns the ResourceContext and URLRequestContext associated to the
   // requester. Currently this method is available only for renderer type
-  // requester.
+  // requester and service worker navigation preload type.
   void GetContexts(ResourceType resource_type,
                    ResourceContext** resource_context,
                    net::URLRequestContext** request_context) const;
@@ -110,8 +120,9 @@
   }
 
   // Returns the ServiceWorkerContext associated with the requester. Currently
-  // this method is available for renderer type requester and browser side
-  // navigation type requester.
+  // this method is available for renderer type requester, browser side
+  // navigation type requester and service worker navigation preload type
+  // requester.
   ServiceWorkerContextWrapper* service_worker_context() {
     return service_worker_context_.get();
   }
@@ -122,7 +133,8 @@
   enum class RequesterType {
     RENDERER,
     BROWSER_SIDE_NAVIGATION,
-    DOWNLOAD_OR_PAGE_SAVE
+    DOWNLOAD_OR_PAGE_SAVE,
+    NAVIGATION_PRELOAD
   };
 
   ResourceRequesterInfo(RequesterType type,
diff --git a/content/browser/loader/resource_scheduler.cc b/content/browser/loader/resource_scheduler.cc
index ad67fa9..39ef40a 100644
--- a/content/browser/loader/resource_scheduler.cc
+++ b/content/browser/loader/resource_scheduler.cc
@@ -17,7 +17,6 @@
 #include "base/stl_util.h"
 #include "base/supports_user_data.h"
 #include "content/common/resource_messages.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/resource_throttle.h"
 #include "net/base/host_port_pair.h"
@@ -218,7 +217,7 @@
         return;
       }
       deferred_ = false;
-      controller()->Resume();
+      Resume();
     }
 
     ready_ = true;
diff --git a/content/browser/loader/resource_scheduler_unittest.cc b/content/browser/loader/resource_scheduler_unittest.cc
index cc40deab..83c2b26 100644
--- a/content/browser/loader/resource_scheduler_unittest.cc
+++ b/content/browser/loader/resource_scheduler_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/timer/mock_timer.h"
 #include "base/timer/timer.h"
 #include "content/public/browser/resource_context.h"
-#include "content/public/browser/resource_controller.h"
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
@@ -48,7 +47,7 @@
 const char kPrioritySupportedRequestsDelayable[] =
     "PrioritySupportedRequestsDelayable";
 
-class TestRequest : public ResourceController {
+class TestRequest : public ResourceThrottle::Delegate {
  public:
   TestRequest(std::unique_ptr<net::URLRequest> url_request,
               std::unique_ptr<ResourceThrottle> throttle,
@@ -57,7 +56,7 @@
         url_request_(std::move(url_request)),
         throttle_(std::move(throttle)),
         scheduler_(scheduler) {
-    throttle_->set_controller_for_testing(this);
+    throttle_->set_delegate_for_testing(this);
   }
   ~TestRequest() override {
     // The URLRequest must still be valid when the ScheduledResourceRequest is
@@ -86,7 +85,7 @@
   const net::URLRequest* url_request() const { return url_request_.get(); }
 
  protected:
-  // ResourceController interface:
+  // ResourceThrottle::Delegate interface:
   void CancelAndIgnore() override {}
   void CancelWithError(int error_code) override {}
   void Resume() override { started_ = true; }
diff --git a/content/browser/loader/stream_writer.cc b/content/browser/loader/stream_writer.cc
index ba356205..4e815553 100644
--- a/content/browser/loader/stream_writer.cc
+++ b/content/browser/loader/stream_writer.cc
@@ -5,9 +5,9 @@
 #include "content/browser/loader/stream_writer.h"
 
 #include "base/guid.h"
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/streams/stream.h"
 #include "content/browser/streams/stream_registry.h"
-#include "content/public/browser/resource_controller.h"
 #include "net/base/io_buffer.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
diff --git a/content/browser/loader/test_resource_handler.cc b/content/browser/loader/test_resource_handler.cc
index 0ee72ae6..f01092c 100644
--- a/content/browser/loader/test_resource_handler.cc
+++ b/content/browser/loader/test_resource_handler.cc
@@ -5,14 +5,18 @@
 #include "content/browser/loader/test_resource_handler.h"
 
 #include "base/logging.h"
-#include "net/url_request/url_request_status.h"
+#include "content/browser/loader/resource_controller.h"
+#include "content/public/common/resource_response.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
 TestResourceHandler::TestResourceHandler(net::URLRequestStatus* request_status,
                                          std::string* body)
-    : ResourceHandler(nullptr), request_status_(request_status), body_(body) {
+    : ResourceHandler(nullptr),
+      request_status_ptr_(request_status),
+      body_ptr_(body),
+      deferred_run_loop_(new base::RunLoop()) {
   SetBufferSize(2048);
 }
 
@@ -21,68 +25,122 @@
 
 TestResourceHandler::~TestResourceHandler() {}
 
-void TestResourceHandler::SetController(ResourceController* controller) {}
+void TestResourceHandler::SetController(ResourceController* controller) {
+  controller_ = controller;
+}
 
 bool TestResourceHandler::OnRequestRedirected(
     const net::RedirectInfo& redirect_info,
     ResourceResponse* response,
     bool* defer) {
-  NOTREACHED() << "Redirects are not supported by the TestResourceHandler.";
-  return false;
+  EXPECT_FALSE(canceled_);
+  EXPECT_EQ(1, on_will_start_called_);
+  EXPECT_EQ(0, on_response_started_called_);
+  EXPECT_EQ(0, on_response_completed_called_);
+  ++on_request_redirected_called_;
+
+  if (!on_request_redirected_result_) {
+    canceled_ = true;
+    return false;
+  }
+
+  *defer = defer_on_request_redirected_;
+  defer_on_request_redirected_ = false;
+  if (*defer)
+    deferred_run_loop_->Quit();
+  return true;
 }
 
 bool TestResourceHandler::OnResponseStarted(ResourceResponse* response,
                                             bool* defer) {
+  EXPECT_FALSE(canceled_);
   EXPECT_EQ(1, on_will_start_called_);
   EXPECT_EQ(0, on_response_started_called_);
   EXPECT_EQ(0, on_response_completed_called_);
   ++on_response_started_called_;
 
-  if (!on_response_started_result_)
+  EXPECT_FALSE(resource_response_);
+  resource_response_ = response;
+
+  if (!on_response_started_result_) {
+    canceled_ = true;
     return false;
+  }
+
   *defer = defer_on_response_started_;
   defer_on_response_started_ = false;
+  if (*defer)
+    deferred_run_loop_->Quit();
   return true;
 }
 
 bool TestResourceHandler::OnWillStart(const GURL& url, bool* defer) {
+  EXPECT_FALSE(canceled_);
   EXPECT_EQ(0, on_response_started_called_);
   EXPECT_EQ(0, on_will_start_called_);
   EXPECT_EQ(0, on_response_completed_called_);
   ++on_will_start_called_;
 
-  if (!on_will_start_result_)
+  start_url_ = url;
+
+  if (!on_will_start_result_) {
+    canceled_ = true;
     return false;
+  }
 
   *defer = defer_on_will_start_;
+  if (*defer)
+    deferred_run_loop_->Quit();
   return true;
 }
 
 bool TestResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
                                      int* buf_size,
                                      int min_size) {
+  EXPECT_FALSE(canceled_);
+  EXPECT_FALSE(expect_on_data_downloaded_);
   EXPECT_EQ(0, on_response_completed_called_);
   ++on_will_read_called_;
 
   *buf = buffer_;
   *buf_size = buffer_size_;
   memset(buffer_->data(), '\0', buffer_size_);
+  if (!on_will_read_result_)
+    canceled_ = true;
   return on_will_read_result_;
 }
 
 bool TestResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
+  EXPECT_FALSE(canceled_);
+  EXPECT_FALSE(expect_on_data_downloaded_);
   EXPECT_EQ(1, on_will_start_called_);
   EXPECT_EQ(1, on_response_started_called_);
   EXPECT_EQ(0, on_response_completed_called_);
+  EXPECT_EQ(0, on_read_eof_);
+
   ++on_read_completed_called_;
+  if (bytes_read == 0)
+    ++on_read_eof_;
 
   EXPECT_LE(static_cast<size_t>(bytes_read), buffer_size_);
-  if (body_)
-    body_->append(buffer_->data(), bytes_read);
-  if (!on_read_completed_result_)
+  if (body_ptr_)
+    body_ptr_->append(buffer_->data(), bytes_read);
+  body_.append(buffer_->data(), bytes_read);
+
+  if (!on_read_completed_result_ ||
+      (!on_on_read_eof_result_ && bytes_read == 0)) {
+    canceled_ = true;
     return false;
+  }
+
   *defer = defer_on_read_completed_;
   defer_on_read_completed_ = false;
+  if (bytes_read == 0 && defer_on_read_eof_)
+    *defer = true;
+
+  if (*defer)
+    deferred_run_loop_->Quit();
+
   return true;
 }
 
@@ -90,16 +148,34 @@
     const net::URLRequestStatus& status,
     bool* defer) {
   EXPECT_EQ(0, on_response_completed_called_);
+  if (status.is_success() && !expect_on_data_downloaded_ && expect_eof_read_)
+    EXPECT_EQ(1, on_read_eof_);
+
   ++on_response_completed_called_;
 
-  if (request_status_)
-    *request_status_ = status;
+  if (request_status_ptr_)
+    *request_status_ptr_ = status;
+  final_status_ = status;
   *defer = defer_on_response_completed_;
   defer_on_response_completed_ = false;
+
+  if (*defer)
+    deferred_run_loop_->Quit();
+  response_complete_run_loop_.Quit();
 }
 
 void TestResourceHandler::OnDataDownloaded(int bytes_downloaded) {
-  NOTREACHED() << "Saving to file is not supported by the TestResourceHandler.";
+  EXPECT_TRUE(expect_on_data_downloaded_);
+  total_bytes_downloaded_ += bytes_downloaded;
+}
+
+void TestResourceHandler::Resume() {
+  controller_->Resume();
+}
+
+void TestResourceHandler::CancelWithError(net::Error net_error) {
+  canceled_ = true;
+  controller_->CancelWithError(net_error);
 }
 
 void TestResourceHandler::SetBufferSize(int buffer_size) {
@@ -108,4 +184,13 @@
   memset(buffer_->data(), '\0', buffer_size);
 }
 
+void TestResourceHandler::WaitUntilDeferred() {
+  deferred_run_loop_->Run();
+  deferred_run_loop_.reset(new base::RunLoop());
+}
+
+void TestResourceHandler::WaitUntilResponseComplete() {
+  response_complete_run_loop_.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/loader/test_resource_handler.h b/content/browser/loader/test_resource_handler.h
index 664f505b..08b172e 100644
--- a/content/browser/loader/test_resource_handler.h
+++ b/content/browser/loader/test_resource_handler.h
@@ -5,14 +5,17 @@
 #ifndef CONTENT_BROWSER_LOADER_TEST_RESOURCE_HANDLER_H_
 #define CONTENT_BROWSER_LOADER_TEST_RESOURCE_HANDLER_H_
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
 #include "content/browser/loader/resource_handler.h"
 #include "net/base/io_buffer.h"
-
-class GURL;
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
 
 namespace net {
 class URLRequestStatus;
@@ -20,6 +23,7 @@
 
 namespace content {
 
+class ResourceController;
 class ResourceHandler;
 struct ResourceResponse;
 
@@ -52,6 +56,11 @@
                            bool* defer) override;
   void OnDataDownloaded(int bytes_downloaded) override;
 
+  // Invoke the corresponding methods on the ResourceHandler's
+  // ResourceController.
+  void Resume();
+  void CancelWithError(net::Error net_error);
+
   // Sets the size of the read buffer returned by OnWillRead. Releases reference
   // to previous read buffer. Default size is 2048 bytes.
   void SetBufferSize(int buffer_size);
@@ -62,6 +71,9 @@
   void set_on_will_start_result(bool on_will_start_result) {
     on_will_start_result_ = on_will_start_result;
   }
+  void set_on_request_redirected_result(bool on_request_redirected_result) {
+    on_request_redirected_result_ = on_request_redirected_result;
+  }
   void set_on_response_started_result(bool on_response_started_result) {
     on_response_started_result_ = on_response_started_result;
   }
@@ -71,6 +83,9 @@
   void set_on_read_completed_result(bool on_read_completed_result) {
     on_read_completed_result_ = on_read_completed_result;
   }
+  void set_on_on_read_eof_result(bool on_on_read_eof_result) {
+    on_on_read_eof_result_ = on_on_read_eof_result;
+  }
 
   // Cause |defer| to be set to true when the specified method is invoked. The
   // test itself is responsible for resuming the request after deferral.
@@ -78,6 +93,9 @@
   void set_defer_on_will_start(bool defer_on_will_start) {
     defer_on_will_start_ = defer_on_will_start;
   }
+  void set_defer_on_request_redirected(bool defer_on_request_redirected) {
+    defer_on_request_redirected_ = defer_on_request_redirected;
+  }
   void set_defer_on_response_started(bool defer_on_response_started) {
     defer_on_response_started_ = defer_on_response_started;
   }
@@ -85,44 +103,105 @@
   void set_defer_on_read_completed(bool defer_on_read_completed) {
     defer_on_read_completed_ = defer_on_read_completed;
   }
+  // The final-byte read will set |defer| to true.
+  void set_defer_on_read_eof(bool defer_on_read_eof) {
+    defer_on_read_eof_ = defer_on_read_eof;
+  }
   void set_defer_on_response_completed(bool defer_on_response_completed) {
     defer_on_response_completed_ = defer_on_response_completed;
   }
 
+  // Set if OnDataDownloaded calls are expected instead of
+  // OnWillRead/OnReadCompleted.
+  void set_expect_on_data_downloaded(bool expect_on_data_downloaded) {
+    expect_on_data_downloaded_ = expect_on_data_downloaded;
+  }
+
+  // Sets whether to expect a final 0-byte read on success. Defaults to true.
+  void set_expect_eof_read(bool expect_eof_read) {
+    expect_eof_read_ = expect_eof_read;
+  }
+
   // Return the number of times the corresponding method was invoked.
 
   int on_will_start_called() const { return on_will_start_called_; }
-  // Redirection currently not supported.
-  int on_request_redirected_called() const { return 0; }
+  int on_request_redirected_called() const {
+    return on_request_redirected_called_;
+  }
   int on_response_started_called() const { return on_response_started_called_; }
   int on_will_read_called() const { return on_will_read_called_; }
   int on_read_completed_called() const { return on_read_completed_called_; }
+  int on_read_eof() const { return on_read_eof_; }
   int on_response_completed_called() const {
     return on_response_completed_called_;
   }
 
+  // URL passed to OnResponseStarted, if it was called.
+  const GURL& start_url() const { return start_url_; }
+
+  ResourceResponse* resource_response() { return resource_response_.get(); };
+
+  int total_bytes_downloaded() const { return total_bytes_downloaded_; }
+
+  const std::string& body() const { return body_; }
+  net::URLRequestStatus final_status() const { return final_status_; }
+
+  // Spins the message loop until the request is deferred.  Using this is
+  // optional, but if used, must use it exclusively to wait for the request. If
+  // the request was deferred and then resumed/canceled without calling this
+  // method, behavior is undefined.
+  void WaitUntilDeferred();
+
+  void WaitUntilResponseComplete();
+
  private:
-  net::URLRequestStatus* request_status_;
-  std::string* body_;
+  // TODO(mmenke):  Remove these, in favor of final_status_ and body_.
+  net::URLRequestStatus* request_status_ptr_;
+  std::string* body_ptr_;
+
   scoped_refptr<net::IOBuffer> buffer_;
   size_t buffer_size_;
 
+  ResourceController* controller_;
+
   bool on_will_start_result_ = true;
+  bool on_request_redirected_result_ = true;
   bool on_response_started_result_ = true;
   bool on_will_read_result_ = true;
   bool on_read_completed_result_ = true;
+  bool on_on_read_eof_result_ = true;
 
   bool defer_on_will_start_ = false;
+  bool defer_on_request_redirected_ = false;
   bool defer_on_response_started_ = false;
   bool defer_on_read_completed_ = false;
+  bool defer_on_read_eof_ = false;
   bool defer_on_response_completed_ = false;
 
+  bool expect_on_data_downloaded_ = false;
+
+  bool expect_eof_read_ = true;
+
   int on_will_start_called_ = 0;
+  int on_request_redirected_called_ = 0;
   int on_response_started_called_ = 0;
   int on_will_read_called_ = 0;
   int on_read_completed_called_ = 0;
+  int on_read_eof_ = 0;
   int on_response_completed_called_ = 0;
 
+  GURL start_url_;
+  scoped_refptr<ResourceResponse> resource_response_;
+  int total_bytes_downloaded_ = 0;
+  std::string body_;
+  net::URLRequestStatus final_status_ =
+      net::URLRequestStatus::FromError(net::ERR_UNEXPECTED);
+  bool canceled_ = false;
+
+  std::unique_ptr<base::RunLoop> deferred_run_loop_;
+
+  base::RunLoop response_complete_run_loop_;
+
   DISALLOW_COPY_AND_ASSIGN(TestResourceHandler);
 };
 
diff --git a/content/browser/loader/throttling_resource_handler.cc b/content/browser/loader/throttling_resource_handler.cc
index cf8ad22..7095b09 100644
--- a/content/browser/loader/throttling_resource_handler.cc
+++ b/content/browser/loader/throttling_resource_handler.cc
@@ -6,8 +6,8 @@
 
 #include <utility>
 
+#include "content/browser/loader/resource_controller.h"
 #include "content/browser/loader/resource_request_info_impl.h"
-#include "content/public/browser/resource_throttle.h"
 #include "content/public/common/resource_response.h"
 #include "net/url_request/url_request.h"
 
@@ -23,7 +23,7 @@
       next_index_(0),
       cancelled_by_resource_throttle_(false) {
   for (size_t i = 0; i < throttles_.size(); ++i) {
-    throttles_[i]->set_controller(this);
+    throttles_[i]->set_delegate(this);
     // Throttles must have a name, as otherwise, bugs where a throttle fails
     // to resume a request can be very difficult to debug.
     DCHECK(throttles_[i]->GetNameForLogging());
@@ -38,8 +38,8 @@
     ResourceResponse* response,
     bool* defer) {
   DCHECK(!cancelled_by_resource_throttle_);
+  DCHECK(!*defer);
 
-  *defer = false;
   while (next_index_ < throttles_.size()) {
     int index = next_index_;
     throttles_[index]->WillRedirectRequest(redirect_info, defer);
@@ -47,7 +47,7 @@
     if (cancelled_by_resource_throttle_)
       return false;
     if (*defer) {
-      OnRequestDefered(index);
+      OnRequestDeferred(index);
       deferred_stage_ = DEFERRED_REDIRECT;
       deferred_redirect_ = redirect_info;
       deferred_response_ = response;
@@ -62,8 +62,8 @@
 
 bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) {
   DCHECK(!cancelled_by_resource_throttle_);
+  DCHECK(!*defer);
 
-  *defer = false;
   while (next_index_ < throttles_.size()) {
     int index = next_index_;
     throttles_[index]->WillStartRequest(defer);
@@ -71,7 +71,7 @@
     if (cancelled_by_resource_throttle_)
       return false;
     if (*defer) {
-      OnRequestDefered(index);
+      OnRequestDeferred(index);
       deferred_stage_ = DEFERRED_START;
       deferred_url_ = url;
       return true;  // Do not cancel.
@@ -86,6 +86,7 @@
 bool ThrottlingResourceHandler::OnResponseStarted(ResourceResponse* response,
                                                   bool* defer) {
   DCHECK(!cancelled_by_resource_throttle_);
+  DCHECK(!*defer);
 
   while (next_index_ < throttles_.size()) {
     int index = next_index_;
@@ -94,7 +95,7 @@
     if (cancelled_by_resource_throttle_)
       return false;
     if (*defer) {
-      OnRequestDefered(index);
+      OnRequestDeferred(index);
       deferred_stage_ = DEFERRED_RESPONSE;
       deferred_response_ = response;
       return true;  // Do not cancel.
@@ -188,7 +189,7 @@
   }
 }
 
-void ThrottlingResourceHandler::OnRequestDefered(int throttle_index) {
+void ThrottlingResourceHandler::OnRequestDeferred(int throttle_index) {
   request()->LogBlockedBy(throttles_[throttle_index]->GetNameForLogging());
 }
 
diff --git a/content/browser/loader/throttling_resource_handler.h b/content/browser/loader/throttling_resource_handler.h
index 4341515..b44cde32 100644
--- a/content/browser/loader/throttling_resource_handler.h
+++ b/content/browser/loader/throttling_resource_handler.h
@@ -10,7 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_vector.h"
 #include "content/browser/loader/layered_resource_handler.h"
-#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/resource_throttle.h"
 #include "net/url_request/redirect_info.h"
 #include "url/gurl.h"
 
@@ -20,12 +20,11 @@
 
 namespace content {
 
-class ResourceThrottle;
 struct ResourceResponse;
 
 // Used to apply a list of ResourceThrottle instances to an URLRequest.
 class ThrottlingResourceHandler : public LayeredResourceHandler,
-                                  public ResourceController {
+                                  public ResourceThrottle::Delegate {
  public:
   // Takes ownership of the ResourceThrottle instances.
   ThrottlingResourceHandler(std::unique_ptr<ResourceHandler> next_handler,
@@ -40,7 +39,7 @@
   bool OnResponseStarted(ResourceResponse* response, bool* defer) override;
   bool OnWillStart(const GURL& url, bool* defer) override;
 
-  // ResourceController implementation:
+  // ResourceThrottle::Delegate implementation:
   void Cancel() override;
   void CancelAndIgnore() override;
   void CancelWithError(int error_code) override;
@@ -53,7 +52,7 @@
 
   // Called when the throttle at |throttle_index| defers a request.  Logs the
   // name of the throttle that delayed the request.
-  void OnRequestDefered(int throttle_index);
+  void OnRequestDeferred(int throttle_index);
 
   enum DeferredStage {
     DEFERRED_NONE,
diff --git a/content/browser/loader/url_loader_factory_impl.cc b/content/browser/loader/url_loader_factory_impl.cc
index 050dc110..6613c49 100644
--- a/content/browser/loader/url_loader_factory_impl.cc
+++ b/content/browser/loader/url_loader_factory_impl.cc
@@ -36,8 +36,8 @@
 URLLoaderFactoryImpl::URLLoaderFactoryImpl(
     scoped_refptr<ResourceRequesterInfo> requester_info)
     : requester_info_(std::move(requester_info)) {
-  DCHECK(requester_info_->IsRenderer());
-  DCHECK(requester_info_->filter());
+  DCHECK((requester_info_->IsRenderer() && requester_info_->filter()) ||
+         requester_info_->IsNavigationPreload());
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
diff --git a/content/browser/media/session/media_metadata_sanitizer.cc b/content/browser/media/session/media_metadata_sanitizer.cc
index 0a53baa..a3b75ac4 100644
--- a/content/browser/media/session/media_metadata_sanitizer.cc
+++ b/content/browser/media/session/media_metadata_sanitizer.cc
@@ -32,7 +32,9 @@
 bool CheckMediaImageSrcSanity(const GURL& src) {
   if (!src.is_valid())
     return false;
-  if (!src.SchemeIsHTTPOrHTTPS() && !src.SchemeIs(url::kDataScheme))
+  if (!src.SchemeIsHTTPOrHTTPS() &&
+      !src.SchemeIs(url::kDataScheme) &&
+      !src.SchemeIs(url::kBlobScheme))
     return false;
   if (src.spec().size() > url::kMaxURLChars)
     return false;
diff --git a/content/browser/media/session/pepper_playback_observer.cc b/content/browser/media/session/pepper_playback_observer.cc
index 593d3d7..f854321 100644
--- a/content/browser/media/session/pepper_playback_observer.cc
+++ b/content/browser/media/session/pepper_playback_observer.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/media/session/pepper_playback_observer.h"
 
-#include "base/feature_list.h"
+#include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "content/browser/media/session/media_session_impl.h"
 #include "content/browser/media/session/pepper_player_delegate.h"
@@ -25,7 +25,7 @@
 
 }  // anonymous namespace
 
-PepperPlaybackObserver::PepperPlaybackObserver(WebContentsImpl *contents)
+PepperPlaybackObserver::PepperPlaybackObserver(WebContents* contents)
     : contents_(contents) {}
 
 PepperPlaybackObserver::~PepperPlaybackObserver() {
@@ -33,49 +33,73 @@
   // call this. MediaSession may decide to send further IPC messages
   // through PepperPlayerDelegates, which might be declined if the
   // RenderViewHost has been destroyed.
-  for (PlayersMap::iterator iter = players_map_.begin();
-       iter != players_map_.end();) {
-    MediaSessionImpl::Get(contents_)->RemovePlayer(
-        iter->second.get(), PepperPlayerDelegate::kPlayerId);
-    iter = players_map_.erase(iter);
+  for (auto it = players_played_sound_map_.begin();
+       it != players_played_sound_map_.end();) {
+    const PlayerId& id = (it++)->first;
+    PepperInstanceDeleted(id.first, id.second);
   }
 }
 
-void PepperPlaybackObserver::PepperInstanceCreated(int32_t pp_instance) {
-  players_played_sound_map_[pp_instance] = false;
+void PepperPlaybackObserver::RenderFrameDeleted(
+    RenderFrameHost* render_frame_host) {
+  std::vector<PlayerId> players_to_remove;
+  for (auto it = players_played_sound_map_.begin();
+       it != players_played_sound_map_.end();) {
+    const PlayerId& id = (it++)->first;
+    if (id.first == render_frame_host)
+      PepperInstanceDeleted(id.first, id.second);
+  }
 }
 
-void PepperPlaybackObserver::PepperInstanceDeleted(int32_t pp_instance) {
-  UMA_HISTOGRAM_BOOLEAN("Media.Pepper.PlayedSound",
-                        players_played_sound_map_[pp_instance]);
-  players_played_sound_map_.erase(pp_instance);
-
-  PepperStopsPlayback(pp_instance);
+void PepperPlaybackObserver::PepperInstanceCreated(
+    RenderFrameHost* render_frame_host, int32_t pp_instance) {
+  PlayerId id(render_frame_host, pp_instance);
+  players_played_sound_map_[id] = false;
 }
 
-void PepperPlaybackObserver::PepperStartsPlayback(int32_t pp_instance) {
-  players_played_sound_map_[pp_instance] = true;
+void PepperPlaybackObserver::PepperInstanceDeleted(
+    RenderFrameHost* render_frame_host, int32_t pp_instance) {
+  PlayerId id(render_frame_host, pp_instance);
 
-  if (players_map_.count(pp_instance))
+  auto iter = players_played_sound_map_.find(id);
+  if (iter == players_played_sound_map_.end())
     return;
 
-  players_map_[pp_instance].reset(new PepperPlayerDelegate(
-      contents_, pp_instance));
+  UMA_HISTOGRAM_BOOLEAN("Media.Pepper.PlayedSound", iter->second);
+  players_played_sound_map_.erase(iter);
+
+  PepperStopsPlayback(render_frame_host, pp_instance);
+}
+
+void PepperPlaybackObserver::PepperStartsPlayback(
+    RenderFrameHost* render_frame_host, int32_t pp_instance) {
+  PlayerId id(render_frame_host, pp_instance);
+
+  players_played_sound_map_[id] = true;
+
+  if (players_map_.count(id))
+    return;
+
+  players_map_[id].reset(new PepperPlayerDelegate(
+      render_frame_host, pp_instance));
 
   MediaSessionImpl::Get(contents_)->AddPlayer(
-      players_map_[pp_instance].get(), PepperPlayerDelegate::kPlayerId,
+      players_map_[id].get(), PepperPlayerDelegate::kPlayerId,
       ShouldDuckFlash() ? media::MediaContentType::Pepper
                         : media::MediaContentType::OneShot);
 }
 
-void PepperPlaybackObserver::PepperStopsPlayback(int32_t pp_instance) {
-  if (!players_map_.count(pp_instance))
+void PepperPlaybackObserver::PepperStopsPlayback(
+    RenderFrameHost* render_frame_host, int32_t pp_instance) {
+  PlayerId id(render_frame_host, pp_instance);
+
+  if (!players_map_.count(id))
     return;
 
   MediaSessionImpl::Get(contents_)->RemovePlayer(
-      players_map_[pp_instance].get(), PepperPlayerDelegate::kPlayerId);
+      players_map_[id].get(), PepperPlayerDelegate::kPlayerId);
 
-  players_map_.erase(pp_instance);
+  players_map_.erase(id);
 }
 
 }  // namespace content
diff --git a/content/browser/media/session/pepper_playback_observer.h b/content/browser/media/session/pepper_playback_observer.h
index 9aacf007..7641918 100644
--- a/content/browser/media/session/pepper_playback_observer.h
+++ b/content/browser/media/session/pepper_playback_observer.h
@@ -8,43 +8,55 @@
 #include <stdint.h>
 #include <map>
 #include <memory>
+#include <utility>
 
 #include "base/macros.h"
 
 namespace content {
 
 class PepperPlayerDelegate;
-class WebContentsImpl;
+class RenderFrameHost;
+class WebContents;
 
 // Class observing Pepper playback changes from WebContents, and update
-// MediaSession accordingly. Can only be a member of WebContentsImpl and must be
-// destroyed in ~WebContentsImpl().
+// MediaSession accordingly. Can only be a member of WebContents and must be
+// destroyed in ~WebContents().
 class PepperPlaybackObserver {
  public:
-  explicit PepperPlaybackObserver(WebContentsImpl* contents);
+  explicit PepperPlaybackObserver(WebContents* contents);
   virtual ~PepperPlaybackObserver();
 
-  void PepperInstanceCreated(int32_t pp_instance);
-  void PepperInstanceDeleted(int32_t pp_instance);
+  void RenderFrameDeleted(RenderFrameHost* render_frame_host);
+
+  void PepperInstanceCreated(RenderFrameHost* render_frame_host,
+                             int32_t pp_instance);
+  void PepperInstanceDeleted(RenderFrameHost* render_frame_host,
+                             int32_t pp_instance);
   // This method is called when a Pepper instance starts making sound.
-  void PepperStartsPlayback(int32_t pp_instance);
+  void PepperStartsPlayback(RenderFrameHost* render_frame_host,
+                            int32_t pp_instance);
   // This method is called when a Pepper instance stops making sound.
-  void PepperStopsPlayback(int32_t pp_instance);
+  void PepperStopsPlayback(RenderFrameHost* render_frame_host,
+                           int32_t pp_instance);
 
  private:
+  using PlayerId = std::pair<RenderFrameHost*, int32_t>;
+
   // Owning PepperPlayerDelegates.
-  using PlayersMap =
-      std::map<int32_t, std::unique_ptr<PepperPlayerDelegate>>;
+  using PlayersMap = std::map<PlayerId, std::unique_ptr<PepperPlayerDelegate>>;
   PlayersMap players_map_;
 
   // Map for whether Pepper players have ever played sound.
   // Used for recording UMA.
-  using PlayersPlayedSoundMap =
-      std::map<int32_t, bool>;
+  //
+  // The mapped player ids must be a super-set of player ids in |players_map_|,
+  // and the map is also used for cleaning up when RenderFrame is deleted or
+  // WebContents is destructed.
+  using PlayersPlayedSoundMap = std::map<PlayerId, bool>;
   PlayersPlayedSoundMap players_played_sound_map_;
 
   // Weak reference to WebContents.
-  WebContentsImpl* contents_;
+  WebContents* contents_;
 
   DISALLOW_COPY_AND_ASSIGN(PepperPlaybackObserver);
 };
diff --git a/content/browser/media/session/pepper_player_delegate.cc b/content/browser/media/session/pepper_player_delegate.cc
index b1d0d671..5e6f1689 100644
--- a/content/browser/media/session/pepper_player_delegate.cc
+++ b/content/browser/media/session/pepper_player_delegate.cc
@@ -6,7 +6,6 @@
 
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/media/session/pepper_playback_observer.h"
-#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "media/base/media_switches.h"
 
@@ -27,8 +26,8 @@
 const int PepperPlayerDelegate::kPlayerId = 0;
 
 PepperPlayerDelegate::PepperPlayerDelegate(
-    WebContentsImpl* contents, int32_t pp_instance)
-    : contents_(contents),
+    RenderFrameHost* render_frame_host, int32_t pp_instance)
+    : render_frame_host_(render_frame_host),
       pp_instance_(pp_instance) {}
 
 PepperPlayerDelegate::~PepperPlayerDelegate() = default;
@@ -60,13 +59,12 @@
 }
 
 RenderFrameHost* PepperPlayerDelegate::GetRenderFrameHost() const {
-  // TODO(zqzhang): Pepper player should be associated to a RenderFrameHost.
-  return nullptr;
+  return render_frame_host_;
 }
 
 void PepperPlayerDelegate::SetVolume(int player_id, double volume) {
-  contents_->Send(new FrameMsg_SetPepperVolume(
-      contents_->GetMainFrame()->routing_id(), pp_instance_, volume));
+  render_frame_host_->Send(new FrameMsg_SetPepperVolume(
+      render_frame_host_->GetRoutingID(), pp_instance_, volume));
 }
 
 }  // namespace content
diff --git a/content/browser/media/session/pepper_player_delegate.h b/content/browser/media/session/pepper_player_delegate.h
index 4b14b42..ae75ec87c 100644
--- a/content/browser/media/session/pepper_player_delegate.h
+++ b/content/browser/media/session/pepper_player_delegate.h
@@ -5,20 +5,22 @@
 #ifndef CONTENT_BROWSER_MEDIA_SESSION_PEPPER_PLAYER_DELEGATE_H_
 #define CONTENT_BROWSER_MEDIA_SESSION_PEPPER_PLAYER_DELEGATE_H_
 
+#include <stdint.h>
+
 #include "base/macros.h"
 #include "content/browser/media/session/media_session_player_observer.h"
-#include "content/browser/web_contents/web_contents_impl.h"
 
 namespace content {
 
+class RenderFrameHost;
+
 class PepperPlayerDelegate : public MediaSessionPlayerObserver {
  public:
   // The Id can only be 0 for PepperPlayerDelegate. Declare the constant here so
   // it can be used elsewhere.
   static const int kPlayerId;
 
-  PepperPlayerDelegate(
-      WebContentsImpl* contents, int32_t pp_instance);
+  PepperPlayerDelegate(RenderFrameHost* render_frame_host, int32_t pp_instance);
   ~PepperPlayerDelegate() override;
 
   // MediaSessionPlayerObserver implementation.
@@ -31,7 +33,7 @@
  private:
   void SetVolume(int player_id, double volume);
 
-  WebContentsImpl* contents_;
+  RenderFrameHost* render_frame_host_;
   int32_t pp_instance_;
 
   DISALLOW_COPY_AND_ASSIGN(PepperPlayerDelegate);
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 840d3a4..0ba9c7b 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -79,11 +79,13 @@
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   factory->GetContextFactory()->AddObserver(this);
   id_allocator_.reset(new cc::SurfaceIdAllocator());
-  factory->GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_);
-  factory->GetSurfaceManager()->RegisterSurfaceFactoryClient(frame_sink_id_,
-                                                             this);
+  factory->GetContextFactory()->GetSurfaceManager()->RegisterFrameSinkId(
+      frame_sink_id_);
+  factory->GetContextFactory()
+      ->GetSurfaceManager()
+      ->RegisterSurfaceFactoryClient(frame_sink_id_, this);
   surface_factory_ = base::MakeUnique<cc::SurfaceFactory>(
-      frame_sink_id_, factory->GetSurfaceManager(), this);
+      frame_sink_id_, factory->GetContextFactory()->GetSurfaceManager(), this);
 }
 
 void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) {
@@ -475,7 +477,8 @@
     EvictDelegatedFrame();
   } else {
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager = factory->GetSurfaceManager();
+    cc::SurfaceManager* manager =
+        factory->GetContextFactory()->GetSurfaceManager();
     bool allocated_new_local_frame_id = false;
     if (!local_frame_id_.is_valid() || frame_size != current_surface_size_ ||
         frame_size_in_dip != current_frame_size_in_dip_) {
@@ -830,8 +833,11 @@
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
   factory->GetContextFactory()->RemoveObserver(this);
   surface_factory_->EvictSurface();
-  factory->GetSurfaceManager()->UnregisterSurfaceFactoryClient(frame_sink_id_);
-  factory->GetSurfaceManager()->InvalidateFrameSinkId(frame_sink_id_);
+  factory->GetContextFactory()
+      ->GetSurfaceManager()
+      ->UnregisterSurfaceFactoryClient(frame_sink_id_);
+  factory->GetContextFactory()->GetSurfaceManager()->InvalidateFrameSinkId(
+      frame_sink_id_);
 
   DCHECK(!vsync_manager_.get());
 }
diff --git a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc
index f938e13..e90ba3f 100644
--- a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc
+++ b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc
@@ -59,11 +59,34 @@
   NOTIMPLEMENTED();
 }
 
+void OffscreenCanvasCompositorFrameSink::AddSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  NOTIMPLEMENTED();
+}
+
+void OffscreenCanvasCompositorFrameSink::RemoveSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  NOTIMPLEMENTED();
+}
+
 void OffscreenCanvasCompositorFrameSink::EvictFrame() {
   // TODO(fsamuel, staraz): Implement this
   NOTIMPLEMENTED();
 }
 
+void OffscreenCanvasCompositorFrameSink::Require(
+    const cc::LocalFrameId& local_frame_id,
+    const cc::SurfaceSequence& sequence) {
+  // TODO(staraz): Implement this.
+  NOTIMPLEMENTED();
+}
+
+void OffscreenCanvasCompositorFrameSink::Satisfy(
+    const cc::SurfaceSequence& sequence) {
+  // TODO(staraz): Implement this.
+  NOTIMPLEMENTED();
+}
+
 void OffscreenCanvasCompositorFrameSink::ReturnResources(
     const cc::ReturnedResourceArray& resources) {
   if (resources.empty())
diff --git a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h
index 27d0dc5..abd8187 100644
--- a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h
+++ b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h
@@ -5,6 +5,9 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_OFFSCREEN_CANVAS_COMPOSITOR_FRAME_SINK_H_
 #define CONTENT_BROWSER_RENDERER_HOST_OFFSCREEN_CANVAS_COMPOSITOR_FRAME_SINK_H_
 
+#include <memory>
+#include <vector>
+
 #include "cc/surfaces/surface_factory.h"
 #include "cc/surfaces/surface_factory_client.h"
 #include "third_party/WebKit/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom.h"
@@ -28,7 +31,14 @@
   void SubmitCompositorFrame(const cc::LocalFrameId& local_frame_id,
                              cc::CompositorFrame frame) override;
   void SetNeedsBeginFrame(bool needs_begin_frame) override;
+  void AddSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references) override;
+  void RemoveSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references) override;
   void EvictFrame() override;
+  void Require(const cc::LocalFrameId& local_frame_id,
+               const cc::SurfaceSequence& sequence) override;
+  void Satisfy(const cc::SurfaceSequence& sequence) override;
 
   // cc::SurfaceFactoryClient implementation.
   void ReturnResources(const cc::ReturnedResourceArray& resources) override;
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 20ef9e40..2cacf19 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1705,6 +1705,7 @@
     switches::kEnableRGBA4444Textures,
     switches::kEnableSkiaBenchmarking,
     switches::kEnableSlimmingPaintV2,
+    switches::kEnableSlimmingPaintInvalidation,
     switches::kEnableSmoothScrolling,
     switches::kEnableStatsTable,
     switches::kEnableThreadedCompositing,
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index ef0a9696..606370d 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -5,6 +5,7 @@
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 
 #include "build/build_config.h"
+#include "components/rappor/public/sample.h"
 #include "content/browser/renderer_host/render_view_host_delegate_view.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -92,4 +93,10 @@
   return false;
 }
 
+bool RenderWidgetHostDelegate::AddDomainInfoToRapporSample(
+    rappor::Sample* sample) {
+  sample->SetStringField("Domain", "Unknown");
+  return false;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 17b2170b..8319bc6 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -29,6 +29,10 @@
 class Size;
 }
 
+namespace rappor {
+class Sample;
+}
+
 namespace content {
 
 class BrowserAccessibilityManager;
@@ -235,6 +239,11 @@
   virtual void FocusOwningWebContents(
       RenderWidgetHostImpl* render_widget_host) {}
 
+  // Augment a Rappor sample with eTLD+1 context. The caller is still
+  // responsible for logging the sample to the RapporService. Returns false
+  // if the eTLD+1 is not known for |render_widget_host|.
+  virtual bool AddDomainInfoToRapporSample(rappor::Sample* sample);
+
  protected:
   virtual ~RenderWidgetHostDelegate() {}
 };
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 a762af3d..508ae90e 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1424,15 +1424,16 @@
     rfh->ExtendSelectionAndDelete(before, after);
 }
 
-void RenderWidgetHostViewAura::EnsureCaretInRect(const gfx::Rect& rect) {
-  gfx::Rect intersected_rect(
-      gfx::IntersectRects(rect, window_->GetBoundsInScreen()));
+void RenderWidgetHostViewAura::EnsureCaretNotInRect(const gfx::Rect& rect) {
+  gfx::Rect rect_in_local_space = ConvertRectFromScreen(rect);
+  gfx::Rect hiding_area_in_this_window =
+      gfx::IntersectRects(rect_in_local_space, window_->bounds());
 
-  if (intersected_rect.IsEmpty())
+  if (hiding_area_in_this_window.IsEmpty())
     return;
 
   host_->ScrollFocusedEditableNodeIntoRect(
-      ConvertRectFromScreen(intersected_rect));
+      gfx::SubtractRects(window_->bounds(), hiding_area_in_this_window));
 }
 
 bool RenderWidgetHostViewAura::IsTextEditCommandEnabled(
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 667641d..3fb3a3d 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -220,7 +220,7 @@
   bool ChangeTextDirectionAndLayoutAlignment(
       base::i18n::TextDirection direction) override;
   void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretInRect(const gfx::Rect& rect) override;
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override;
   bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
   void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index bdbfbba..022fd7ea 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -1668,8 +1668,7 @@
   frame.metadata.device_scale_factor = scale_factor;
 
   std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
-  pass->SetNew(
-      cc::RenderPassId(1, 1), gfx::Rect(size), damage, gfx::Transform());
+  pass->SetNew(1, gfx::Rect(size), damage, gfx::Transform());
   frame.render_pass_list.push_back(std::move(pass));
   if (!size.IsEmpty()) {
     cc::TransferableResource resource;
@@ -1685,7 +1684,8 @@
 TEST_F(RenderWidgetHostViewAuraTest, ResettingCompositorReturnsResources) {
   FakeSurfaceObserver manager_observer;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  cc::SurfaceManager* manager = factory->GetSurfaceManager();
+  cc::SurfaceManager* manager =
+      factory->GetContextFactory()->GetSurfaceManager();
   manager->AddObserver(&manager_observer);
 
   gfx::Size view_size(100, 100);
@@ -1752,7 +1752,8 @@
 TEST_F(RenderWidgetHostViewAuraTest, TwoOutputSurfaces) {
   FakeSurfaceObserver manager_observer;
   ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  cc::SurfaceManager* manager = factory->GetSurfaceManager();
+  cc::SurfaceManager* manager =
+      factory->GetContextFactory()->GetSurfaceManager();
   manager->AddObserver(&manager_observer);
 
   gfx::Size view_size(100, 100);
@@ -1917,7 +1918,8 @@
   cc::SurfaceId id = view_->GetDelegatedFrameHost()->SurfaceIdForTesting();
   if (id.is_valid()) {
     ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-    cc::SurfaceManager* manager = factory->GetSurfaceManager();
+    cc::SurfaceManager* manager =
+        factory->GetContextFactory()->GetSurfaceManager();
     cc::Surface* surface = manager->GetSurfaceForId(id);
     EXPECT_TRUE(surface);
 
diff --git a/content/browser/service_manager/merge_dictionary.cc b/content/browser/service_manager/merge_dictionary.cc
index 231264b..cee4593e 100644
--- a/content/browser/service_manager/merge_dictionary.cc
+++ b/content/browser/service_manager/merge_dictionary.cc
@@ -12,7 +12,7 @@
        it.Advance()) {
     const base::Value* merge_value = &it.value();
     // Check whether we have to merge dictionaries.
-    if (merge_value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (merge_value->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* sub_dict;
       if (target->GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
         MergeDictionary(
@@ -21,7 +21,7 @@
         continue;
       }
     }
-    if (merge_value->IsType(base::Value::TYPE_LIST)) {
+    if (merge_value->IsType(base::Value::Type::LIST)) {
       const base::ListValue* merge_list = nullptr;
       if (merge_value->GetAsList(&merge_list)) {
         base::ListValue* target_list = nullptr;
diff --git a/content/browser/service_worker/foreign_fetch_request_handler.cc b/content/browser/service_worker/foreign_fetch_request_handler.cc
index ab2d134..88973044 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler.cc
+++ b/content/browser/service_worker/foreign_fetch_request_handler.cc
@@ -82,8 +82,10 @@
   if (!IsForeignFetchEnabled())
     return;
 
-  if (!context_wrapper)
+  if (!context_wrapper || !context_wrapper->context() ||
+      provider_id == kInvalidServiceWorkerProviderId) {
     return;
+  }
 
   if (skip_service_worker == SkipServiceWorker::ALL)
     return;
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 9b2189a..bf62e06 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -50,6 +50,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
@@ -1467,6 +1468,16 @@
     return request.headers.find(kNavigationPreloadHeaderName)->second;
   }
 
+  static void CancellingInterceptorCallback(
+      const std::string& header,
+      const std::string& value,
+      int child_process_id,
+      content::ResourceContext* resource_context,
+      OnHeaderProcessedCallback callback) {
+    DCHECK_EQ(kNavigationPreloadHeaderName, header);
+    callback.Run(false, 0);
+  }
+
   void SetupForNavigationPreloadTest(const GURL& scope,
                                      const GURL& worker_url) {
     scoped_refptr<WorkerActivatedObserver> observer =
@@ -1935,6 +1946,33 @@
 }
 
 IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
+                       CanceledByInterceptor) {
+  content::ResourceDispatcherHost::Get()->RegisterInterceptor(
+      kNavigationPreloadHeaderName, "",
+      base::Bind(&CancellingInterceptorCallback));
+
+  const char kPageUrl[] = "/service_worker/navigation_preload.html";
+  const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
+  const GURL page_url = embedded_test_server()->GetURL(kPageUrl);
+  const GURL worker_url = embedded_test_server()->GetURL(kWorkerUrl);
+  RegisterStaticFile(
+      kWorkerUrl, kEnableNavigationPreloadScript + kPreloadResponseTestScript,
+      "text/javascript");
+
+  RegisterMonitorRequestHandler();
+  StartServerAndNavigateToSetup();
+  SetupForNavigationPreloadTest(page_url, worker_url);
+
+  const base::string16 title = base::ASCIIToUTF16("REJECTED");
+  TitleWatcher title_watcher(shell()->web_contents(), title);
+  title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("RESOLVED"));
+  NavigateToURL(shell(), page_url);
+  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
+  EXPECT_EQ("NetworkError: Service Worker navigation preload network error.",
+            GetTextContent());
+}
+
+IN_PROC_BROWSER_TEST_P(ServiceWorkerNavigationPreloadTest,
                        PreloadHeadersSimple) {
   const char kPageUrl[] = "/service_worker/navigation_preload.html";
   const char kWorkerUrl[] = "/service_worker/navigation_preload.js";
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index c899c8e..c074651 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -398,20 +398,20 @@
 
   ResourceRequestInfoImpl* original_info =
       ResourceRequestInfoImpl::ForRequest(original_request);
+  ResourceRequesterInfo* requester_info = original_info->requester_info();
   if (IsBrowserSideNavigationEnabled()) {
-    // TODO(horo): Support NavigationPreload with PlzNavigate.
-    DCHECK(original_info->requester_info()->IsBrowserSideNavigation());
-    NOTIMPLEMENTED();
-    return;
+    DCHECK(requester_info->IsBrowserSideNavigation());
+  } else {
+    DCHECK(requester_info->IsRenderer());
+    if (!requester_info->filter())
+      return;
   }
-  DCHECK(original_info->requester_info()->IsRenderer());
-  if (!original_info->requester_info()->filter())
-    return;
 
   DCHECK(!url_loader_factory_);
   mojom::URLLoaderFactoryPtr factory;
-  URLLoaderFactoryImpl::Create(original_info->requester_info(),
-                               mojo::GetProxy(&url_loader_factory_));
+  URLLoaderFactoryImpl::Create(
+      ResourceRequesterInfo::CreateForNavigationPreload(requester_info),
+      mojo::GetProxy(&url_loader_factory_));
 
   preload_handle_ = mojom::FetchEventPreloadHandle::New();
 
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc
index 0ffd4eca..1deab98e 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -18,6 +19,7 @@
 #include "content/common/service_worker/service_worker_utils.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/base/url_util.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
@@ -42,6 +44,18 @@
     "The script resource is behind a redirect, which is disallowed.";
 const char kServiceWorkerAllowed[] = "Service-Worker-Allowed";
 
+bool ShouldIgnoreSSLError(net::URLRequest* request) {
+  const net::HttpNetworkSession::Params* session_params =
+      request->context()->GetNetworkSessionParams();
+  if (session_params && session_params->ignore_certificate_errors)
+    return true;
+  bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kAllowInsecureLocalhost);
+  if (allow_localhost && net::IsLocalhost(request->url().host()))
+    return true;
+  return false;
+}
+
 }  // namespace
 
 const net::Error ServiceWorkerWriteToCacheJob::kIdenticalScriptError =
@@ -252,9 +266,10 @@
   DCHECK_EQ(net_request_.get(), request);
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerWriteToCacheJob::OnSSLCertificateError");
-  // TODO(michaeln): Pass this thru to our jobs client,
-  // see NotifySSLCertificateError.
-  NotifyStartErrorHelper(net::ERR_INSECURE_RESPONSE, kSSLError);
+  if (ShouldIgnoreSSLError(request))
+    request->ContinueDespiteLastError();
+  else
+    NotifyStartErrorHelper(net::ERR_INSECURE_RESPONSE, kSSLError);
 }
 
 void ServiceWorkerWriteToCacheJob::OnResponseStarted(net::URLRequest* request,
@@ -277,13 +292,10 @@
   }
   // OnSSLCertificateError is not called when the HTTPS connection is reused.
   // So we check cert_status here.
-  if (net::IsCertStatusError(request->ssl_info().cert_status)) {
-    const net::HttpNetworkSession::Params* session_params =
-        request->context()->GetNetworkSessionParams();
-    if (!session_params || !session_params->ignore_certificate_errors) {
-      NotifyStartErrorHelper(net::ERR_INSECURE_RESPONSE, kSSLError);
-      return;
-    }
+  if (net::IsCertStatusError(request->ssl_info().cert_status) &&
+      !ShouldIgnoreSSLError(request)) {
+    NotifyStartErrorHelper(net::ERR_INSECURE_RESPONSE, kSSLError);
+    return;
   }
 
   if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index 0365891..9215ade 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -125,6 +125,11 @@
     info.cert_status = net::CERT_STATUS_DATE_INVALID;
     NotifySSLCertificateError(info, true);
   }
+  void ContinueDespiteLastError() override {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&SSLCertificateErrorJob::StartAsync,
+                              weak_factory_.GetWeakPtr()));
+  }
 
  protected:
   ~SSLCertificateErrorJob() override {}
@@ -271,7 +276,13 @@
 class ServiceWorkerWriteToCacheJobTest : public testing::Test {
  public:
   ServiceWorkerWriteToCacheJobTest()
-      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+      : ServiceWorkerWriteToCacheJobTest("https://host/scope/",
+                                         "https://host/script.js") {}
+  ServiceWorkerWriteToCacheJobTest(const std::string& scope,
+                                   const std::string& script_url)
+      : scope_(scope),
+        script_url_(script_url),
+        browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
         mock_protocol_handler_(nullptr) {}
   ~ServiceWorkerWriteToCacheJobTest() override {}
 
@@ -324,8 +335,6 @@
     helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
 
     // A new unstored registration/version.
-    scope_ = GURL("https://host/scope/");
-    script_url_ = GURL("https://host/script.js");
     registration_ =
         new ServiceWorkerRegistration(scope_, 1L, context()->AsWeakPtr());
     version_ =
@@ -418,6 +427,9 @@
   void DisableCache() { context()->storage()->disk_cache()->Disable(); }
 
  protected:
+  const GURL scope_;
+  const GURL script_url_;
+
   TestBrowserThreadBundle browser_thread_bundle_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
   scoped_refptr<ServiceWorkerRegistration> registration_;
@@ -432,9 +444,6 @@
   content::MockResourceContext resource_context_;
 
   MockURLRequestDelegate url_request_delegate_;
-  GURL scope_;
-  GURL script_url_;
-
   int next_provider_id_ = 1;
   int64_t next_version_id_ = 1L;
 };
@@ -474,6 +483,70 @@
             version_->script_cache_map()->LookupResourceId(script_url_));
 }
 
+class ServiceWorkerWriteToCacheLocalhostTest
+    : public ServiceWorkerWriteToCacheJobTest {
+ public:
+  ServiceWorkerWriteToCacheLocalhostTest()
+      : ServiceWorkerWriteToCacheJobTest("https://localhost/scope/",
+                                         "https://localhost/script.js") {}
+  ~ServiceWorkerWriteToCacheLocalhostTest() override {}
+};
+
+class ServiceWorkerWriteToCacheLocalhostTestP
+    : public MojoServiceWorkerTestP<ServiceWorkerWriteToCacheLocalhostTest> {};
+
+TEST_P(ServiceWorkerWriteToCacheLocalhostTestP,
+       SSLCertificateError_AllowInsecureLocalhost) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kAllowInsecureLocalhost);
+
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateSSLCertificateErrorJob));
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::SUCCESS, request_->status().status());
+  EXPECT_EQ(net::OK, request_->status().error());
+  EXPECT_NE(kInvalidServiceWorkerResourceId,
+            version_->script_cache_map()->LookupResourceId(script_url_));
+}
+
+TEST_P(ServiceWorkerWriteToCacheLocalhostTestP, SSLCertificateError) {
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateSSLCertificateErrorJob));
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status());
+  EXPECT_EQ(net::ERR_INSECURE_RESPONSE, request_->status().error());
+  EXPECT_EQ(kInvalidServiceWorkerResourceId,
+            version_->script_cache_map()->LookupResourceId(script_url_));
+}
+
+TEST_P(ServiceWorkerWriteToCacheLocalhostTestP,
+       CertStatusError_AllowInsecureLocalhost) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kAllowInsecureLocalhost);
+
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateCertStatusErrorJob));
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::SUCCESS, request_->status().status());
+  EXPECT_EQ(net::OK, request_->status().error());
+  EXPECT_NE(kInvalidServiceWorkerResourceId,
+            version_->script_cache_map()->LookupResourceId(script_url_));
+}
+
+TEST_P(ServiceWorkerWriteToCacheLocalhostTestP, CertStatusError) {
+  mock_protocol_handler_->SetCreateJobCallback(
+      base::Bind(&CreateCertStatusErrorJob));
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(net::URLRequestStatus::FAILED, request_->status().status());
+  EXPECT_EQ(net::ERR_INSECURE_RESPONSE, request_->status().error());
+  EXPECT_EQ(kInvalidServiceWorkerResourceId,
+            version_->script_cache_map()->LookupResourceId(script_url_));
+}
+
 TEST_P(ServiceWorkerWriteToCacheJobTestP, CertStatusError) {
   mock_protocol_handler_->SetCreateJobCallback(
       base::Bind(&CreateCertStatusErrorJob));
@@ -623,5 +696,8 @@
 INSTANTIATE_TEST_CASE_P(ServiceWorkerWriteToCacheJobTest,
                         ServiceWorkerWriteToCacheJobTestP,
                         testing::Bool());
+INSTANTIATE_TEST_CASE_P(ServiceWorkerWriteToCacheLocalhostTest,
+                        ServiceWorkerWriteToCacheLocalhostTestP,
+                        testing::Bool());
 
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c6c043a..6813b2de 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -30,6 +30,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/mime_util/mime_util.h"
+#include "components/rappor/public/rappor_utils.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/browser/accessibility/accessibility_mode_helper.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
@@ -106,6 +107,7 @@
 #include "content/public/browser/resource_request_details.h"
 #include "content/public/browser/screen_orientation_provider.h"
 #include "content/public/browser/security_style_explanations.h"
+#include "content/public/browser/ssl_status.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_contents_binding_set.h"
@@ -3758,13 +3760,15 @@
 void WebContentsImpl::OnPepperInstanceCreated(int32_t pp_instance) {
   for (auto& observer : observers_)
     observer.PepperInstanceCreated();
-  pepper_playback_observer_->PepperInstanceCreated(pp_instance);
+  pepper_playback_observer_->PepperInstanceCreated(
+      render_frame_message_source_, pp_instance);
 }
 
 void WebContentsImpl::OnPepperInstanceDeleted(int32_t pp_instance) {
   for (auto& observer : observers_)
     observer.PepperInstanceDeleted();
-  pepper_playback_observer_->PepperInstanceDeleted(pp_instance);
+  pepper_playback_observer_->PepperInstanceDeleted(
+      render_frame_message_source_, pp_instance);
 }
 
 void WebContentsImpl::OnPepperPluginHung(int plugin_child_id,
@@ -3777,11 +3781,13 @@
 }
 
 void WebContentsImpl::OnPepperStartsPlayback(int32_t pp_instance) {
-  pepper_playback_observer_->PepperStartsPlayback(pp_instance);
+  pepper_playback_observer_->PepperStartsPlayback(
+      render_frame_message_source_, pp_instance);
 }
 
 void WebContentsImpl::OnPepperStopsPlayback(int32_t pp_instance) {
-  pepper_playback_observer_->PepperStopsPlayback(pp_instance);
+  pepper_playback_observer_->PepperStopsPlayback(
+      render_frame_message_source_, pp_instance);
 }
 
 void WebContentsImpl::OnPluginCrashed(const base::FilePath& plugin_path,
@@ -4082,6 +4088,9 @@
 void WebContentsImpl::RenderFrameDeleted(RenderFrameHost* render_frame_host) {
   for (auto& observer : observers_)
     observer.RenderFrameDeleted(render_frame_host);
+#if BUILDFLAG(ENABLE_PLUGINS)
+  pepper_playback_observer_->RenderFrameDeleted(render_frame_host);
+#endif
 }
 
 void WebContentsImpl::ShowContextMenu(RenderFrameHost* render_frame_host,
@@ -4512,6 +4521,8 @@
 
 void WebContentsImpl::DocumentOnLoadCompleted(
     RenderFrameHost* render_frame_host) {
+  ShowInsecureLocalhostWarningIfNeeded();
+
   for (auto& observer : observers_)
     observer.DocumentOnLoadCompletedInMainFrame();
 
@@ -5284,4 +5295,36 @@
     binding_sets_.erase(it);
 }
 
+bool WebContentsImpl::AddDomainInfoToRapporSample(rappor::Sample* sample) {
+  sample->SetStringField("Domain", ::rappor::GetDomainAndRegistrySampleFromGURL(
+                                       GetLastCommittedURL()));
+
+  return true;
+}
+
+void WebContentsImpl::ShowInsecureLocalhostWarningIfNeeded() {
+  bool allow_localhost = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kAllowInsecureLocalhost);
+  if (!allow_localhost)
+    return;
+
+  content::NavigationEntry* entry = GetController().GetLastCommittedEntry();
+  if (!entry || !net::IsLocalhost(entry->GetURL().host()))
+    return;
+
+  content::SSLStatus ssl_status = entry->GetSSL();
+  bool is_cert_error = net::IsCertStatusError(ssl_status.cert_status) &&
+                       !net::IsCertStatusMinorError(ssl_status.cert_status);
+  if (!is_cert_error)
+    return;
+
+  GetMainFrame()->AddMessageToConsole(
+      content::CONSOLE_MESSAGE_LEVEL_WARNING,
+      base::StringPrintf("This site does not have a valid SSL "
+                         "certificate! Without SSL, your site's and "
+                         "visitors' data is vulnerable to theft and "
+                         "tampering. Get a valid SSL certificate before"
+                         " releasing your website to the public."));
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 8802ff35..5644671e 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -667,6 +667,7 @@
   void OnFirstPaintAfterLoad(RenderWidgetHostImpl* render_widget_host) override;
   TextInputManager* GetTextInputManager() override;
   bool OnUpdateDragCursor() override;
+  bool AddDomainInfoToRapporSample(rappor::Sample* sample) override;
 
   // RenderFrameHostManager::Delegate ------------------------------------------
 
@@ -1140,6 +1141,10 @@
   // Removes a registered WebContentsBindingSet by interface name.
   void RemoveBindingSet(const std::string& interface_name);
 
+  // Prints a console warning when visiting a localhost site with a bad
+  // certificate via --allow-insecure-localhost.
+  void ShowInsecureLocalhostWarningIfNeeded();
+
   // Data for core operation ---------------------------------------------------
 
   // Delegate for notifying our owner about stuff. Not owned by us.
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index 56a54ec..36bb9a6 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -234,7 +234,7 @@
         &error_message);
 
     ASSERT_TRUE(value.get() != NULL) << error_message;
-    EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);
+    EXPECT_EQ(value->GetType(), base::Value::Type::LIST);
 
     base::ListValue* values;
     ASSERT_TRUE(value->GetAsList(&values));
diff --git a/content/browser/webrtc/webrtc_internals_browsertest.cc b/content/browser/webrtc/webrtc_internals_browsertest.cc
index 046fbf8..c3fcbd5 100644
--- a/content/browser/webrtc/webrtc_internals_browsertest.cc
+++ b/content/browser/webrtc/webrtc_internals_browsertest.cc
@@ -239,7 +239,7 @@
     std::unique_ptr<base::Value> value_requests =
         base::JSONReader::Read(json_requests);
 
-    EXPECT_EQ(base::Value::TYPE_LIST, value_requests->GetType());
+    EXPECT_EQ(base::Value::Type::LIST, value_requests->GetType());
 
     base::ListValue* list_request =
         static_cast<base::ListValue*>(value_requests.get());
@@ -439,7 +439,7 @@
                                int update_number,
                                int stats_number) {
     EXPECT_NE((base::Value*)NULL, dump);
-    EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType());
+    EXPECT_EQ(base::Value::Type::DICTIONARY, dump->GetType());
 
     base::DictionaryValue* dict_dump =
         static_cast<base::DictionaryValue*>(dump);
@@ -449,7 +449,7 @@
     for (; !it.IsAtEnd(); it.Advance()) {
       base::Value* value = NULL;
       dict_dump->Get(it.key(), &value);
-      EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+      EXPECT_EQ(base::Value::Type::DICTIONARY, value->GetType());
       base::DictionaryValue* pc_dump =
           static_cast<base::DictionaryValue*>(value);
       EXPECT_TRUE(pc_dump->HasKey("updateLog"));
@@ -457,13 +457,13 @@
 
       // Verifies the number of updates.
       pc_dump->Get("updateLog", &value);
-      EXPECT_EQ(base::Value::TYPE_LIST, value->GetType());
+      EXPECT_EQ(base::Value::Type::LIST, value->GetType());
       base::ListValue* list = static_cast<base::ListValue*>(value);
       EXPECT_EQ((size_t) update_number, list->GetSize());
 
       // Verifies the number of stats tables.
       pc_dump->Get("stats", &value);
-      EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+      EXPECT_EQ(base::Value::Type::DICTIONARY, value->GetType());
       base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value);
       EXPECT_EQ((size_t) stats_number, dict->size());
     }
@@ -477,7 +477,7 @@
                        const string& report_id,
                        const StatsUnit& stats) {
     EXPECT_NE((base::Value*)NULL, dump);
-    EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType());
+    EXPECT_EQ(base::Value::Type::DICTIONARY, dump->GetType());
 
     base::DictionaryValue* dict_dump =
         static_cast<base::DictionaryValue*>(dump);
@@ -488,7 +488,7 @@
     // Verifies there is one data series per stats name.
     value = NULL;
     pc_dump->Get("stats", &value);
-    EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+    EXPECT_EQ(base::Value::Type::DICTIONARY, value->GetType());
 
     base::DictionaryValue* dataSeries =
         static_cast<base::DictionaryValue*>(value);
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index 2d6d24a9..4c42580 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -131,7 +131,7 @@
 
   for (base::DictionaryValue::Iterator it(localized_strings); !it.IsAtEnd();
        it.Advance()) {
-    if (it.value().IsType(base::Value::TYPE_STRING)) {
+    if (it.value().IsType(base::Value::Type::STRING)) {
       std::string value;
       it.value().GetAsString(&value);
       replacements_[it.key()] = value;
diff --git a/content/child/appcache/web_application_cache_host_impl.cc b/content/child/appcache/web_application_cache_host_impl.cc
index 1493304b..ee07952 100644
--- a/content/child/appcache/web_application_cache_host_impl.cc
+++ b/content/child/appcache/web_application_cache_host_impl.cc
@@ -10,6 +10,7 @@
 #include "base/id_map.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "content/public/common/browser_side_navigation_policy.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"
@@ -57,16 +58,25 @@
 
 WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
     WebApplicationCacheHostClient* client,
-    AppCacheBackend* backend)
+    AppCacheBackend* backend,
+    int appcache_host_id)
     : client_(client),
       backend_(backend),
-      host_id_(all_hosts()->Add(this)),
       status_(APPCACHE_STATUS_UNCACHED),
       is_scheme_supported_(false),
       is_get_method_(false),
       is_new_master_entry_(MAYBE),
       was_select_cache_called_(false) {
-  DCHECK(client && backend && (host_id_ != kAppCacheNoHostId));
+  DCHECK(client && backend);
+  // PlzNavigate: The browser passes the ID to be used.
+  if (appcache_host_id != kAppCacheNoHostId) {
+    DCHECK(IsBrowserSideNavigationEnabled());
+    all_hosts()->AddWithID(this, appcache_host_id);
+    host_id_ = appcache_host_id;
+  } else {
+    host_id_ = all_hosts()->Add(this);
+  }
+  DCHECK(host_id_ != kAppCacheNoHostId);
 
   backend_->RegisterHost(host_id_);
 }
diff --git a/content/child/appcache/web_application_cache_host_impl.h b/content/child/appcache/web_application_cache_host_impl.h
index c50b45e..3a1cc2d 100644
--- a/content/child/appcache/web_application_cache_host_impl.h
+++ b/content/child/appcache/web_application_cache_host_impl.h
@@ -23,7 +23,8 @@
   static WebApplicationCacheHostImpl* FromId(int id);
 
   WebApplicationCacheHostImpl(blink::WebApplicationCacheHostClient* client,
-                              AppCacheBackend* backend);
+                              AppCacheBackend* backend,
+                              int appcache_host_id);
   ~WebApplicationCacheHostImpl() override;
 
   int host_id() const { return host_id_; }
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 86e22ad..1f401ec 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -632,9 +632,6 @@
     {"DocumentXMLTreeViewer.js",
      IDR_PRIVATE_SCRIPT_DOCUMENTXMLTREEVIEWER_JS,
      ui::SCALE_FACTOR_NONE},
-    {"HTMLMarqueeElement.js",
-     IDR_PRIVATE_SCRIPT_HTMLMARQUEEELEMENT_JS,
-     ui::SCALE_FACTOR_NONE},
     {"PrivateScriptRunner.js",
      IDR_PRIVATE_SCRIPT_PRIVATESCRIPTRUNNER_JS,
      ui::SCALE_FACTOR_NONE},
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 29adfca..7c1e8326 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -249,6 +249,9 @@
   if (command_line.HasSwitch(switches::kEnableSlimmingPaintV2))
     WebRuntimeFeatures::enableSlimmingPaintV2(true);
 
+  if (command_line.HasSwitch(switches::kEnableSlimmingPaintInvalidation))
+    WebRuntimeFeatures::enableSlimmingPaintInvalidation(true);
+
   if (base::FeatureList::IsEnabled(features::kDocumentWriteEvaluator))
     WebRuntimeFeatures::enableDocumentWriteEvaluator(true);
 
diff --git a/content/child/v8_value_converter_impl.cc b/content/child/v8_value_converter_impl.cc
index 89378a8c..5dcf636 100644
--- a/content/child/v8_value_converter_impl.cc
+++ b/content/child/v8_value_converter_impl.cc
@@ -232,45 +232,45 @@
     const base::Value* value) const {
   CHECK(value);
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return v8::Null(isolate);
 
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool val = false;
       CHECK(value->GetAsBoolean(&val));
       return v8::Boolean::New(isolate, val);
     }
 
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int val = 0;
       CHECK(value->GetAsInteger(&val));
       return v8::Integer::New(isolate, val);
     }
 
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double val = 0.0;
       CHECK(value->GetAsDouble(&val));
       return v8::Number::New(isolate, val);
     }
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string val;
       CHECK(value->GetAsString(&val));
       return v8::String::NewFromUtf8(
           isolate, val.c_str(), v8::String::kNormalString, val.length());
     }
 
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::LIST:
       return ToV8Array(isolate,
                        creation_context,
                        static_cast<const base::ListValue*>(value));
 
-    case base::Value::TYPE_DICTIONARY:
+    case base::Value::Type::DICTIONARY:
       return ToV8Object(isolate,
                         creation_context,
                         static_cast<const base::DictionaryValue*>(value));
 
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       return ToArrayBuffer(isolate,
                            creation_context,
                            static_cast<const base::BinaryValue*>(value));
@@ -615,7 +615,7 @@
     // there *is* a "windowId" property, but since it should be an int, code
     // on the browser which doesn't additionally check for null will fail.
     // We can avoid all bugs related to this by stripping null.
-    if (strip_null_from_objects_ && child->IsType(base::Value::TYPE_NULL))
+    if (strip_null_from_objects_ && child->IsType(base::Value::Type::NONE))
       continue;
 
     result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
diff --git a/content/child/v8_value_converter_impl_unittest.cc b/content/child/v8_value_converter_impl_unittest.cc
index aa95215..a403751b 100644
--- a/content/child/v8_value_converter_impl_unittest.cc
+++ b/content/child/v8_value_converter_impl_unittest.cc
@@ -131,7 +131,7 @@
       ADD_FAILURE();
       return false;
     }
-    return child->GetType() == base::Value::TYPE_NULL;
+    return child->GetType() == base::Value::Type::NONE;
   }
 
   bool IsNull(v8::Local<v8::Object> value, const std::string& key) {
@@ -150,7 +150,7 @@
       ADD_FAILURE();
       return false;
     }
-    return child->GetType() == base::Value::TYPE_NULL;
+    return child->GetType() == base::Value::Type::NONE;
   }
 
   bool IsNull(v8::Local<v8::Array> value, uint32_t index) {
@@ -208,7 +208,7 @@
       // types into null.
       base::Value* temp = NULL;
       ASSERT_TRUE(list->Get(0, &temp));
-      EXPECT_EQ(base::Value::TYPE_NULL, temp->GetType());
+      EXPECT_EQ(base::Value::Type::NONE, temp->GetType());
     }
   }
 
@@ -403,21 +403,21 @@
 
   V8ValueConverterImpl converter;
   TestWeirdType(converter, v8::Undefined(isolate_),
-                base::Value::TYPE_NULL,  // Arbitrary type, result is NULL.
+                base::Value::Type::NONE,  // Arbitrary type, result is NULL.
                 std::unique_ptr<base::Value>());
   TestWeirdType(converter, v8::Date::New(isolate_, 1000),
-                base::Value::TYPE_DICTIONARY,
+                base::Value::Type::DICTIONARY,
                 std::unique_ptr<base::Value>(new base::DictionaryValue()));
-  TestWeirdType(converter, regex, base::Value::TYPE_DICTIONARY,
+  TestWeirdType(converter, regex, base::Value::Type::DICTIONARY,
                 std::unique_ptr<base::Value>(new base::DictionaryValue()));
 
   converter.SetDateAllowed(true);
   TestWeirdType(converter, v8::Date::New(isolate_, 1000),
-                base::Value::TYPE_DOUBLE,
+                base::Value::Type::DOUBLE,
                 std::unique_ptr<base::Value>(new base::FundamentalValue(1.0)));
 
   converter.SetRegExpAllowed(true);
-  TestWeirdType(converter, regex, base::Value::TYPE_STRING,
+  TestWeirdType(converter, regex, base::Value::Type::STRING,
                 std::unique_ptr<base::Value>(new base::StringValue("/./")));
 }
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 4d49b09..3f3fb75 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -7,6 +7,7 @@
 import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//ppapi/features/features.gni")
+import("//sandbox/features.gni")
 import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
 if (is_mac) {
   import("//build/config/mac/mac_sdk.gni")
@@ -390,6 +391,7 @@
     "//net",
     "//ppapi/features",
     "//sandbox",
+    "//sandbox:sandbox_features",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
@@ -501,9 +503,7 @@
     sources -= [ "cursors/webcursor_aurawin.cc" ]
   }
 
-  if (use_seccomp_bpf) {
-    defines += [ "USE_SECCOMP_BPF" ]
-  } else {
+  if (!use_seccomp_bpf) {
     if (is_linux) {
       sources -= [
         "sandbox_linux/bpf_cros_arm_gpu_policy_linux.cc",
diff --git a/content/common/android/gin_java_bridge_value.cc b/content/common/android/gin_java_bridge_value.cc
index 6a742305..5961461 100644
--- a/content/common/android/gin_java_bridge_value.cc
+++ b/content/common/android/gin_java_bridge_value.cc
@@ -55,7 +55,7 @@
 
 // static
 bool GinJavaBridgeValue::ContainsGinJavaBridgeValue(const base::Value* value) {
-  if (!value->IsType(base::Value::TYPE_BINARY))
+  if (!value->IsType(base::Value::Type::BINARY))
     return false;
   const base::BinaryValue* binary_value =
       reinterpret_cast<const base::BinaryValue*>(value);
@@ -74,7 +74,7 @@
 std::unique_ptr<const GinJavaBridgeValue> GinJavaBridgeValue::FromValue(
     const base::Value* value) {
   return std::unique_ptr<const GinJavaBridgeValue>(
-      value->IsType(base::Value::TYPE_BINARY)
+      value->IsType(base::Value::Type::BINARY)
           ? new GinJavaBridgeValue(
                 reinterpret_cast<const base::BinaryValue*>(value))
           : NULL);
diff --git a/content/common/child_process_messages.h b/content/common/child_process_messages.h
index 91733d6d..e757ca1 100644
--- a/content/common/child_process_messages.h
+++ b/content/common/child_process_messages.h
@@ -41,7 +41,7 @@
 
 IPC_STRUCT_TRAITS_BEGIN(tracked_objects::BirthOnThreadSnapshot)
   IPC_STRUCT_TRAITS_MEMBER(location)
-  IPC_STRUCT_TRAITS_MEMBER(thread_name)
+  IPC_STRUCT_TRAITS_MEMBER(sanitized_thread_name)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(tracked_objects::DeathDataSnapshot)
@@ -63,7 +63,7 @@
 IPC_STRUCT_TRAITS_BEGIN(tracked_objects::TaskSnapshot)
   IPC_STRUCT_TRAITS_MEMBER(birth)
   IPC_STRUCT_TRAITS_MEMBER(death_data)
-  IPC_STRUCT_TRAITS_MEMBER(death_thread_name)
+  IPC_STRUCT_TRAITS_MEMBER(death_sanitized_thread_name)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(tracked_objects::ProcessDataPhaseSnapshot)
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 3d4ad4c..64bf4d7 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -384,6 +384,7 @@
   IPC_STRUCT_TRAITS_MEMBER(should_create_service_worker)
   IPC_STRUCT_TRAITS_MEMBER(navigation_timing)
   IPC_STRUCT_TRAITS_MEMBER(service_worker_provider_id)
+  IPC_STRUCT_TRAITS_MEMBER(appcache_host_id)
 #if defined(OS_ANDROID)
   IPC_STRUCT_TRAITS_MEMBER(data_url_as_string)
 #endif
diff --git a/content/common/frame_replication_state.h b/content/common/frame_replication_state.h
index 291287f6..6fb755f 100644
--- a/content/common/frame_replication_state.h
+++ b/content/common/frame_replication_state.h
@@ -123,9 +123,6 @@
   // True if a frame's origin is unique and should be considered potentially
   // trustworthy.
   bool has_potentially_trustworthy_unique_origin;
-
-  // TODO(alexmos): Eventually, this structure can also hold other state that
-  // needs to be replicated, such as frame sizing info.
 };
 
 }  // namespace content
diff --git a/content/common/indexed_db/indexed_db.mojom b/content/common/indexed_db/indexed_db.mojom
index d5babca..89f3bda 100644
--- a/content/common/indexed_db/indexed_db.mojom
+++ b/content/common/indexed_db/indexed_db.mojom
@@ -8,7 +8,9 @@
 
 module indexed_db.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file_path.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/time.mojom";
 import "url/mojo/origin.mojom";
 
 enum CursorDirection {
diff --git a/content/common/media/audio_messages.h b/content/common/media/audio_messages.h
index 32a4506..5a3f6316 100644
--- a/content/common/media/audio_messages.h
+++ b/content/common/media/audio_messages.h
@@ -27,7 +27,7 @@
                           media::AUDIO_INPUT_IPC_DELEGATE_STATE_LAST)
 
 IPC_ENUM_TRAITS_MAX_VALUE(media::OutputDeviceStatus,
-                          media::OUTPUT_DEVICE_STATUS_LAST)
+                          media::OUTPUT_DEVICE_STATUS_MAX)
 
 IPC_STRUCT_BEGIN(AudioInputHostMsg_CreateStream_Config)
   IPC_STRUCT_MEMBER(media::AudioParameters, params)
diff --git a/content/common/navigation_params.cc b/content/common/navigation_params.cc
index f1a7fb5f..24078e67 100644
--- a/content/common/navigation_params.cc
+++ b/content/common/navigation_params.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "content/public/common/appcache_info.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/url_constants.h"
 #include "url/gurl.h"
@@ -134,7 +135,8 @@
       is_view_source(false),
       should_clear_history_list(false),
       should_create_service_worker(false),
-      service_worker_provider_id(kInvalidServiceWorkerProviderId) {}
+      service_worker_provider_id(kInvalidServiceWorkerProviderId),
+      appcache_host_id(kAppCacheNoHostId) {}
 
 RequestNavigationParams::RequestNavigationParams(
     bool is_overriding_user_agent,
@@ -168,7 +170,8 @@
       is_view_source(is_view_source),
       should_clear_history_list(should_clear_history_list),
       should_create_service_worker(false),
-      service_worker_provider_id(kInvalidServiceWorkerProviderId) {}
+      service_worker_provider_id(kInvalidServiceWorkerProviderId),
+      appcache_host_id(kAppCacheNoHostId) {}
 
 RequestNavigationParams::RequestNavigationParams(
     const RequestNavigationParams& other) = default;
diff --git a/content/common/navigation_params.h b/content/common/navigation_params.h
index cb3048c..0937e42 100644
--- a/content/common/navigation_params.h
+++ b/content/common/navigation_params.h
@@ -325,6 +325,10 @@
   // it will always be equal to kInvalidServiceWorkerProviderId.
   int service_worker_provider_id;
 
+  // PlzNavigate
+  // The AppCache host id to be used to identify this navigation.
+  int appcache_host_id;
+
 #if defined(OS_ANDROID)
   // The real content of the data: URL. Only used in Android WebView for
   // implementing LoadDataWithBaseUrl API method to circumvent the restriction
diff --git a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
index e8264cfe..67b1d057 100644
--- a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
+++ b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
@@ -18,8 +18,9 @@
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/sandbox_features.h"
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
 
 #include "base/files/scoped_file.h"
 #include "base/posix/eintr_wrapper.h"
@@ -58,7 +59,7 @@
 
 namespace content {
 
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
 namespace {
 
 // This function takes ownership of |policy|.
@@ -231,7 +232,7 @@
 #if !defined(OS_NACL_NONSFI)
 bool SandboxSeccompBPF::ShouldEnableSeccompBPF(
     const std::string& process_type) {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   if (process_type == switches::kGpuProcess)
@@ -244,7 +245,7 @@
 #endif  // !defined(OS_NACL_NONSFI)
 
 bool SandboxSeccompBPF::SupportsSandbox() {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   return SandboxBPF::SupportsSeccompSandbox(
       SandboxBPF::SeccompLevel::SINGLE_THREADED);
 #endif
@@ -253,7 +254,7 @@
 
 #if !defined(OS_NACL_NONSFI)
 bool SandboxSeccompBPF::SupportsSandboxWithTsync() {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   return SandboxBPF::SupportsSeccompSandbox(
       SandboxBPF::SeccompLevel::MULTI_THREADED);
 #endif
@@ -262,7 +263,7 @@
 
 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type,
                                      base::ScopedFD proc_fd) {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
@@ -284,24 +285,24 @@
 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
     std::unique_ptr<sandbox::bpf_dsl::Policy> policy,
     base::ScopedFD proc_fd) {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   if (IsSeccompBPFDesired() && SupportsSandbox()) {
     CHECK(policy);
     StartSandboxWithPolicy(policy.release(), std::move(proc_fd));
     return true;
   }
-#endif  // defined(USE_SECCOMP_BPF)
+#endif  // BUILDFLAG(USE_SECCOMP_BPF)
   return false;
 }
 
 #if !defined(OS_NACL_NONSFI)
 std::unique_ptr<sandbox::bpf_dsl::Policy>
 SandboxSeccompBPF::GetBaselinePolicy() {
-#if defined(USE_SECCOMP_BPF)
+#if BUILDFLAG(USE_SECCOMP_BPF)
   return std::unique_ptr<sandbox::bpf_dsl::Policy>(new BaselinePolicy);
 #else
   return std::unique_ptr<sandbox::bpf_dsl::Policy>();
-#endif  // defined(USE_SECCOMP_BPF)
+#endif  // BUILDFLAG(USE_SECCOMP_BPF)
 }
 #endif  // !defined(OS_NACL_NONSFI)
 
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker_event_dispatcher.mojom
index 7a17c5f..16f4516 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker_event_dispatcher.mojom
@@ -5,7 +5,7 @@
 module content.mojom;
 
 import "content/common/url_loader.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 import "third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom";
 import "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom";
 
diff --git a/content/common/video_capture.mojom b/content/common/video_capture.mojom
index ca24beaf..358fb4ad98 100644
--- a/content/common/video_capture.mojom
+++ b/content/common/video_capture.mojom
@@ -7,7 +7,6 @@
 import "gpu/ipc/common/sync_token.mojom";
 import "media/mojo/interfaces/media_types.mojom";
 import "media/capture/mojo/video_capture_types.mojom";
-import "mojo/common/common_custom_types.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 // This file decribes the communication between a given Renderer Host interface
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index 9455eb5..72c88b9b 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -359,7 +359,8 @@
       new media::MediaGpuChannelManager(gpu_channel_manager_.get()));
 
   // Only set once per process instance.
-  service_factory_.reset(new GpuServiceFactory);
+  service_factory_.reset(
+      new GpuServiceFactory(media_gpu_channel_manager_->AsWeakPtr()));
 
   GetInterfaceRegistry()->AddInterface(base::Bind(
       &GpuChildThread::BindServiceFactoryRequest, base::Unretained(this)));
diff --git a/content/gpu/gpu_service_factory.cc b/content/gpu/gpu_service_factory.cc
index 50696ce4..5625a63 100644
--- a/content/gpu/gpu_service_factory.cc
+++ b/content/gpu/gpu_service_factory.cc
@@ -4,22 +4,32 @@
 
 #include "content/gpu/gpu_service_factory.h"
 
+#include <memory>
+
+#include "base/threading/thread_task_runner_handle.h"
+
 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
 #include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "media/mojo/services/media_service_factory.h"  // nogncheck
 #endif
 
 namespace content {
 
-GpuServiceFactory::GpuServiceFactory() {}
+GpuServiceFactory::GpuServiceFactory(
+    base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager) {
+#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
+  task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  media_gpu_channel_manager_ = std::move(media_gpu_channel_manager);
+#endif
+}
 
 GpuServiceFactory::~GpuServiceFactory() {}
 
 void GpuServiceFactory::RegisterServices(ServiceMap* services) {
 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
   ServiceInfo info;
-  info.factory = base::Bind(&media::CreateMediaService);
+  info.factory = base::Bind(&media::CreateGpuMediaService, task_runner_,
+                            media_gpu_channel_manager_);
   info.use_own_thread = true;
   services->insert(std::make_pair("media", info));
 #endif
diff --git a/content/gpu/gpu_service_factory.h b/content/gpu/gpu_service_factory.h
index 5d5d797f..8edca6b9 100644
--- a/content/gpu/gpu_service_factory.h
+++ b/content/gpu/gpu_service_factory.h
@@ -6,20 +6,37 @@
 #define CONTENT_GPU_GPU_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "content/child/service_factory.h"
 
+namespace media {
+class MediaGpuChannelManager;
+}
+
 namespace content {
 
 // Customization of ServiceFactory for the GPU process.
 class GpuServiceFactory : public ServiceFactory {
  public:
-  GpuServiceFactory();
+  explicit GpuServiceFactory(
+      base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager);
   ~GpuServiceFactory() override;
 
   // ServiceFactory overrides:
   void RegisterServices(ServiceMap* services) override;
 
  private:
+#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
+  // Task runner we were constructed on, and that |media_gpu_channel_manager_|
+  // must be accessed from (the GPU main thread task runner). We expect
+  // RegisterServices() to be called on this task runner as well, but the
+  // implementation doesn't care.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(GpuServiceFactory);
 };
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index 77138e8..9c49027 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -305,7 +305,12 @@
      */
     @Override
     public void finishActionMode() {
-        if (isActionModeValid()) mActionMode.finish();
+        if (isActionModeValid()) {
+            mActionMode.finish();
+
+            // Should be nulled out in case #onDestroyActionMode() is not invoked in response.
+            mActionMode = null;
+        }
     }
 
     /**
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 12d0357..02a484f 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -32,6 +32,7 @@
           "device::BatteryMonitor",
           "device::mojom::LightSensor",
           "device::mojom::MotionSensor",
+          "device::mojom::OrientationAbsoluteSensor",
           "device::mojom::OrientationSensor",
           "device::mojom::PowerMonitor",
           "device::mojom::TimeZoneMonitor",
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index 8363801..a170a19 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -19,7 +19,7 @@
       },
       "requires": {
         "content_browser": [ "renderer" ],
-        "ui": [ "ui:gpu_client" ]
+        "ui": [ "gpu_client" ]
       }
     },
     "navigation:frame": {
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index dc7ba177..5f8495e 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -205,7 +205,6 @@
     "render_widget_host_view_mac_delegate.h",
     "renderer_unresponsive_type.h",
     "resource_context.h",
-    "resource_controller.h",
     "resource_dispatcher_host.cc",
     "resource_dispatcher_host.h",
     "resource_dispatcher_host_delegate.cc",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 571a89c92..f6c8cdb 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -434,4 +434,8 @@
   return std::unique_ptr<MemoryCoordinatorDelegate>();
 }
 
+::rappor::RapporService* ContentBrowserClient::GetRapporService() {
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 9d1756d..43cf4f4 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -80,6 +80,10 @@
 class URLRequestContext;
 }
 
+namespace rappor {
+class RapporService;
+}
+
 namespace sandbox {
 class TargetPolicy;
 }
@@ -791,6 +795,9 @@
   virtual void CreateMediaRemoter(RenderFrameHost* render_frame_host,
                                   media::mojom::RemotingSourcePtr source,
                                   media::mojom::RemoterRequest request) {}
+
+  // Returns the RapporService from the browser process.
+  virtual ::rappor::RapporService* GetRapporService();
 };
 
 }  // namespace content
diff --git a/content/public/browser/resource_controller.h b/content/public/browser/resource_controller.h
deleted file mode 100644
index e18d0ae0..0000000
--- a/content/public/browser/resource_controller.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 CONTENT_PUBLIC_BROWSER_RESOURCE_CONTROLLER_H_
-#define CONTENT_PUBLIC_BROWSER_RESOURCE_CONTROLLER_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-// Used to either resume a deferred resource load or cancel a resource load at
-// any time.  CancelAndIgnore is a variation of Cancel that also causes the
-// requester of the resource to act like the request was never made.  By
-// default, load is cancelled with ERR_ABORTED code. CancelWithError can be used
-// to cancel load with any other error code.
-class CONTENT_EXPORT ResourceController {
- public:
-  virtual void Cancel() = 0;
-  virtual void CancelAndIgnore() = 0;
-  virtual void CancelWithError(int error_code) = 0;
-  virtual void Resume() = 0;
- protected:
-  virtual ~ResourceController() {}
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_RESOURCE_CONTROLLER_H_
diff --git a/content/public/browser/resource_throttle.cc b/content/public/browser/resource_throttle.cc
index 88a89a8..53b1e219 100644
--- a/content/public/browser/resource_throttle.cc
+++ b/content/public/browser/resource_throttle.cc
@@ -10,4 +10,20 @@
   return false;
 }
 
+void ResourceThrottle::Cancel() {
+  delegate_->Cancel();
+}
+
+void ResourceThrottle::CancelAndIgnore() {
+  delegate_->CancelAndIgnore();
+}
+
+void ResourceThrottle::CancelWithError(int error_code) {
+  delegate_->CancelWithError(error_code);
+}
+
+void ResourceThrottle::Resume() {
+  delegate_->Resume();
+}
+
 }  // namespace content
diff --git a/content/public/browser/resource_throttle.h b/content/public/browser/resource_throttle.h
index da1054c..4ac8fd4 100644
--- a/content/public/browser/resource_throttle.h
+++ b/content/public/browser/resource_throttle.h
@@ -24,6 +24,25 @@
 // time.
 class CONTENT_EXPORT ResourceThrottle {
  public:
+  // An interface to get notified when the throttle implementation considers
+  // it's ready to resume the deferred resource load. The throttle
+  // implementation may also tell the delegate to cancel the resource loading.
+  class CONTENT_EXPORT Delegate {
+   public:
+    // Cancels the resource load.
+    virtual void Cancel() = 0;
+    // Marks the resource load as ignored by the resource handler, and then
+    // cancels the resource load.
+    virtual void CancelAndIgnore() = 0;
+    // Cancels the resource load with the specified error code.
+    virtual void CancelWithError(int error_code) = 0;
+    // Tells the delegate to resume the deferred resource load.
+    virtual void Resume() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
   virtual ~ResourceThrottle() {}
 
   // Called before the resource request is started.
@@ -51,20 +70,25 @@
   // WillProcessResponse.
   virtual bool MustProcessResponseBeforeReadingBody();
 
-  void set_controller_for_testing(ResourceController* c) {
-    controller_ = c;
-  }
+  void set_delegate_for_testing(Delegate* delegate) { delegate_ = delegate; }
 
  protected:
-  ResourceThrottle() : controller_(nullptr) {}
-  ResourceController* controller() { return controller_; }
+  ResourceThrottle() : delegate_(nullptr) {}
+
+  // Helper methods for subclasses. When these methods are called, methods with
+  // the same name on |delegate_| are called.
+  void Cancel();
+  void CancelAndIgnore();
+  void CancelWithError(int error_code);
+  void Resume();
 
  private:
   friend class AsyncRevalidationDriver;
   friend class ThrottlingResourceHandler;
-  void set_controller(ResourceController* c) { controller_ = c; }
 
-  ResourceController* controller_;
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+  Delegate* delegate_;
 };
 
 }  // namespace content
diff --git a/content/public/child/v8_value_converter.h b/content/public/child/v8_value_converter.h
index aeb38c9..9afeb397 100644
--- a/content/public/child/v8_value_converter.h
+++ b/content/public/child/v8_value_converter.h
@@ -117,11 +117,11 @@
   //
   // Unsupported types (unless explicitly configured) are not converted, so
   // this method may return NULL -- the exception is when converting arrays,
-  // where unsupported types are converted to Value(TYPE_NULL).
+  // where unsupported types are converted to Value(Type::NONE).
   //
   // Likewise, if an object throws while converting a property it will not be
   // converted, whereas if an array throws while converting an item it will be
-  // converted to Value(TYPE_NULL).
+  // converted to Value(Type::NONE).
   virtual std::unique_ptr<base::Value> FromV8Value(
       v8::Local<v8::Value> value,
       v8::Local<v8::Context> context) const = 0;
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 09a17d46..dbf67ad 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -31,6 +31,10 @@
 // override for developers who need the old behavior for testing.
 const char kAllowFileAccessFromFiles[]      = "allow-file-access-from-files";
 
+// Enables TLS/SSL errors on localhost to be ignored (no interstitial,
+// no blocking of requests).
+const char kAllowInsecureLocalhost[] = "allow-insecure-localhost";
+
 // Allows loopback interface to be added in network list for peer connection.
 const char kAllowLoopbackInPeerConnection[] =
     "allow-loopback-in-peer-connection";
@@ -218,10 +222,6 @@
 // Disables usage of the namespace sandbox.
 const char kDisableNamespaceSandbox[]       = "disable-namespace-sandbox";
 
-// Disables native GPU memory buffer support.
-const char kDisableNativeGpuMemoryBuffers[] =
-    "disable-native-gpu-memory-buffers";
-
 // Disables the Web Notification and the Push APIs.
 const char kDisableNotifications[]          = "disable-notifications";
 
@@ -459,6 +459,11 @@
 // Enables slimming paint phase 2: http://www.chromium.org/blink/slimming-paint
 const char kEnableSlimmingPaintV2[]         = "enable-slimming-paint-v2";
 
+// Enables paint invalidation based on slimming paint but without the full
+// slimming paint v2 compositing code. See: https://goo.gl/eQczQW
+const char kEnableSlimmingPaintInvalidation[] =
+    "enable-slimming-paint-invalidation";
+
 // On platforms that support it, enables smooth scroll animation.
 const char kEnableSmoothScrolling[]         = "enable-smooth-scrolling";
 
@@ -858,9 +863,6 @@
 // Use the Mandoline UI Service in the Chrome render process.
 const char kUseMusInRenderer[] = "use-mus-in-renderer";
 
-// Enable native GPU memory buffer support when available.
-const char kEnableNativeGpuMemoryBuffers[] = "enable-native-gpu-memory-buffers";
-
 // Texture target for CHROMIUM_image backed content textures.
 const char kContentImageTextureTarget[] = "content-image-texture-target";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 444dc28..d01bd332 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -19,6 +19,7 @@
 CONTENT_EXPORT extern const char kAecRefinedAdaptiveFilter[];
 CONTENT_EXPORT extern const char kAgcStartupMinVolume[];
 CONTENT_EXPORT extern const char kAllowFileAccessFromFiles[];
+CONTENT_EXPORT extern const char kAllowInsecureLocalhost[];
 CONTENT_EXPORT extern const char kAllowLoopbackInPeerConnection[];
 CONTENT_EXPORT extern const char kAllowNoSandboxJob[];
 CONTENT_EXPORT extern const char kAllowSandboxDebugging[];
@@ -75,7 +76,6 @@
 CONTENT_EXPORT extern const char kDisableLogging[];
 CONTENT_EXPORT extern const char kDisableMojoServiceWorker[];
 CONTENT_EXPORT extern const char kDisableNamespaceSandbox[];
-CONTENT_EXPORT extern const char kDisableNativeGpuMemoryBuffers[];
 CONTENT_EXPORT extern const char kDisableNotifications[];
 CONTENT_EXPORT extern const char kDisablePartialRaster[];
 CONTENT_EXPORT extern const char kEnablePartialRaster[];
@@ -144,6 +144,7 @@
 CONTENT_EXPORT extern const char kEnableSandboxLogging[];
 extern const char kEnableSkiaBenchmarking[];
 CONTENT_EXPORT extern const char kEnableSlimmingPaintV2[];
+CONTENT_EXPORT extern const char kEnableSlimmingPaintInvalidation[];
 CONTENT_EXPORT extern const char kEnableSmoothScrolling[];
 CONTENT_EXPORT extern const char kEnableSpatialNavigation[];
 CONTENT_EXPORT extern const char kEnableStatsTable[];
@@ -242,7 +243,6 @@
 CONTENT_EXPORT extern const char kUIPrioritizeInGpuProcess[];
 CONTENT_EXPORT extern const char kUseFakeUIForMediaStream[];
 CONTENT_EXPORT extern const char kUseMusInRenderer[];
-CONTENT_EXPORT extern const char kEnableNativeGpuMemoryBuffers[];
 CONTENT_EXPORT extern const char kContentImageTextureTarget[];
 CONTENT_EXPORT extern const char kVideoImageTextureTarget[];
 CONTENT_EXPORT extern const char kUseMobileUserAgent[];
diff --git a/content/public/common/push_messaging_status.h b/content/public/common/push_messaging_status.h
index 8b8d62d..56f8874c9 100644
--- a/content/public/common/push_messaging_status.h
+++ b/content/public/common/push_messaging_status.h
@@ -90,13 +90,15 @@
   // Automatic - incoming message's service worker was not found.
   PUSH_UNREGISTRATION_REASON_DELIVERY_NO_SERVICE_WORKER = 5,
 
+  // Automatic - GCM Store reset due to corruption.
+  PUSH_UNREGISTRATION_REASON_GCM_STORE_RESET = 6,
+
   // NOTE: Do not renumber these as that would confuse interpretation of
   // previously logged data. When making changes, also update the enum list
   // in tools/metrics/histograms/histograms.xml to keep it in sync, and
   // update PUSH_UNREGISTRATION_REASON_LAST below.
 
-  PUSH_UNREGISTRATION_REASON_LAST =
-      PUSH_UNREGISTRATION_REASON_DELIVERY_NO_SERVICE_WORKER
+  PUSH_UNREGISTRATION_REASON_LAST = PUSH_UNREGISTRATION_REASON_GCM_STORE_RESET
 };
 
 // Push unregistration success/error codes for internal use & reporting in UMA.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index ad951b54..f86bc6a 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -699,6 +699,7 @@
       "//third_party/webrtc/base:rtc_base",
       "//third_party/webrtc/common_video",
       "//third_party/webrtc/media:rtc_media",
+      "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/audio_device",
       "//third_party/webrtc/modules/audio_processing",
       "//third_party/webrtc/modules/video_coding:webrtc_h264",
@@ -892,8 +893,8 @@
     ]
   }
 
-  if (use_seccomp_bpf) {
-    defines += [ "USE_SECCOMP_BPF" ]
+  if (is_linux || is_android) {
+    deps += [ "//sandbox:sandbox_features" ]
   }
 
   if (use_ozone) {
diff --git a/content/renderer/DEPS b/content/renderer/DEPS
index 961a568..a8d7a26a 100644
--- a/content/renderer/DEPS
+++ b/content/renderer/DEPS
@@ -32,7 +32,9 @@
     "+content/shell",
   ],
   "render_thread_impl_browsertest\.cc": [
-    "+components/discardable_memory/service",
     "+content/app/mojo/mojo_init.h",
   ],
+  "render_thread_impl_discardable_memory_browsertest\.cc": [
+    "+components/discardable_memory/service",
+  ],
 }
diff --git a/content/renderer/android/synchronous_compositor_frame_sink.cc b/content/renderer/android/synchronous_compositor_frame_sink.cc
index c0ff4c0f..228b569 100644
--- a/content/renderer/android/synchronous_compositor_frame_sink.cc
+++ b/content/renderer/android/synchronous_compositor_frame_sink.cc
@@ -279,7 +279,7 @@
 
     // The embedding RenderPass covers the entire Display's area.
     const auto& embed_render_pass = embed_frame.render_pass_list.back();
-    embed_render_pass->SetAll(cc::RenderPassId(1, 1), gfx::Rect(display_size),
+    embed_render_pass->SetAll(1, gfx::Rect(display_size),
                               gfx::Rect(display_size), gfx::Transform(), false);
 
     // The RenderPass has a single SurfaceDrawQuad (and SharedQuadState for it).
diff --git a/content/renderer/java/gin_java_bridge_value_converter_unittest.cc b/content/renderer/java/gin_java_bridge_value_converter_unittest.cc
index e766c0752..642d1d77 100644
--- a/content/renderer/java/gin_java_bridge_value_converter_unittest.cc
+++ b/content/renderer/java/gin_java_bridge_value_converter_unittest.cc
@@ -128,7 +128,8 @@
     std::unique_ptr<base::Value> list_value(
         converter->FromV8Value(v8_typed_array, context));
     ASSERT_TRUE(list_value.get()) << typed_array_type;
-    EXPECT_TRUE(list_value->IsType(base::Value::TYPE_LIST)) << typed_array_type;
+    EXPECT_TRUE(list_value->IsType(base::Value::Type::LIST))
+        << typed_array_type;
     base::ListValue* list;
     ASSERT_TRUE(list_value->GetAsList(&list)) << typed_array_type;
     EXPECT_EQ(1u, list->GetSize()) << typed_array_type;
diff --git a/content/renderer/java/gin_java_function_invocation_helper.cc b/content/renderer/java/gin_java_function_invocation_helper.cc
index c1729e5..9bdf744 100644
--- a/content/renderer/java/gin_java_function_invocation_helper.cc
+++ b/content/renderer/java/gin_java_function_invocation_helper.cc
@@ -78,7 +78,7 @@
         args->isolate(), GinJavaBridgeErrorToString(error))));
     return v8::Undefined(args->isolate());
   }
-  if (!result->IsType(base::Value::TYPE_BINARY)) {
+  if (!result->IsType(base::Value::Type::BINARY)) {
     return converter_->ToV8Value(result.get(),
                                  args->isolate()->GetCurrentContext());
   }
diff --git a/content/renderer/media/android/media_player_renderer_client.cc b/content/renderer/media/android/media_player_renderer_client.cc
index 85d782f..3bd225c 100644
--- a/content/renderer/media/android/media_player_renderer_client.cc
+++ b/content/renderer/media/android/media_player_renderer_client.cc
@@ -45,16 +45,23 @@
       base::Bind(&MediaPlayerRendererClient::OnFrameAvailable,
                  base::Unretained(this)),
       gfx::Size(1, 1), compositor_task_runner_,
-      base::Bind(&MediaPlayerRendererClient::InitializeRemoteRenderer,
+      base::Bind(&MediaPlayerRendererClient::OnStreamTextureWrapperInitialized,
                  weak_factory_.GetWeakPtr(), demuxer_stream_provider));
 }
 
-void MediaPlayerRendererClient::InitializeRemoteRenderer(
-    media::DemuxerStreamProvider* demuxer_stream_provider) {
+void MediaPlayerRendererClient::OnStreamTextureWrapperInitialized(
+    media::DemuxerStreamProvider* demuxer_stream_provider,
+    bool success) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
+  if (!success) {
+    base::ResetAndReturn(&init_cb_).Run(
+        media::PipelineStatus::PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
   mojo_renderer_->Initialize(
       demuxer_stream_provider, this,
-      base::Bind(&MediaPlayerRendererClient::CompleteInitialization,
+      base::Bind(&MediaPlayerRendererClient::OnRemoteRendererInitialized,
                  weak_factory_.GetWeakPtr()));
 }
 
@@ -64,7 +71,7 @@
   stream_texture_wrapper_->ForwardStreamTextureForSurfaceRequest(request_token);
 }
 
-void MediaPlayerRendererClient::CompleteInitialization(
+void MediaPlayerRendererClient::OnRemoteRendererInitialized(
     media::PipelineStatus status) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
   DCHECK(!init_cb_.is_null());
diff --git a/content/renderer/media/android/media_player_renderer_client.h b/content/renderer/media/android/media_player_renderer_client.h
index 6e056e36..1f63e358 100644
--- a/content/renderer/media/android/media_player_renderer_client.h
+++ b/content/renderer/media/android/media_player_renderer_client.h
@@ -71,9 +71,10 @@
   void OnFrameAvailable();
 
  private:
-  void InitializeRemoteRenderer(
-      media::DemuxerStreamProvider* demuxer_stream_provider);
-  void CompleteInitialization(media::PipelineStatus status);
+  void OnStreamTextureWrapperInitialized(
+      media::DemuxerStreamProvider* demuxer_stream_provider,
+      bool success);
+  void OnRemoteRendererInitialized(media::PipelineStatus status);
 
   void OnScopedSurfaceRequested(const base::UnguessableToken& request_token);
 
diff --git a/content/renderer/media/android/stream_texture_wrapper_impl.cc b/content/renderer/media/android/stream_texture_wrapper_impl.cc
index 67ecb15..036612f 100644
--- a/content/renderer/media/android/stream_texture_wrapper_impl.cc
+++ b/content/renderer/media/android/stream_texture_wrapper_impl.cc
@@ -129,6 +129,10 @@
     return;
   }
 
+  // InitializeOnMainThread() hasn't run, or failed.
+  if (!stream_texture_proxy_)
+    return;
+
   if (natural_size_ == new_size)
     return;
 
@@ -142,7 +146,7 @@
     const base::Closure& received_frame_cb,
     const gfx::Size& natural_size,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
-    const base::Closure& init_cb) {
+    const StreamTextureWrapperInitCB& init_cb) {
   DVLOG(2) << __func__;
 
   compositor_task_runner_ = compositor_task_runner;
@@ -156,24 +160,23 @@
 
 void StreamTextureWrapperImpl::InitializeOnMainThread(
     const base::Closure& received_frame_cb,
-    const base::Closure& init_cb) {
+    const StreamTextureWrapperInitCB& init_cb) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   DVLOG(2) << __func__;
 
   stream_texture_proxy_ = factory_->CreateProxy(
       kGLTextureExternalOES, &texture_id_, &texture_mailbox_);
-  if (!stream_texture_proxy_)
+  if (!stream_texture_proxy_) {
+    init_cb.Run(false);
     return;
+  }
 
   ReallocateVideoFrame(natural_size_);
 
   stream_texture_proxy_->BindToTaskRunner(received_frame_cb,
                                           compositor_task_runner_);
 
-  // TODO(tguilbert): Register the surface properly. See crbug.com/627658.
-
-  // |init_cb| is bound to the thread that originally called Initialize().
-  init_cb.Run();
+  init_cb.Run(true);
 }
 
 void StreamTextureWrapperImpl::Destroy() {
diff --git a/content/renderer/media/android/stream_texture_wrapper_impl.h b/content/renderer/media/android/stream_texture_wrapper_impl.h
index b18b18d..048cc9c 100644
--- a/content/renderer/media/android/stream_texture_wrapper_impl.h
+++ b/content/renderer/media/android/stream_texture_wrapper_impl.h
@@ -55,14 +55,15 @@
   // Additional threading considerations:
   //   - Can be called from any thread.
   //   - Initialization will be posted to |main_task_runner_|.
-  //   - |init_cb| will be run on the calling thread.
+  //   - |init_cb| will be run on the calling thread, and will be passed a bool
+  //     indicating whether the initialization was successful.
   //   - New frames will be signaled on |compositor_task_runner| via |client|'s
   //     DidReceiveFrame() method.
   void Initialize(
       const base::Closure& received_frame_cb,
       const gfx::Size& natural_size,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
-      const base::Closure& init_cb) override;
+      const StreamTextureWrapperInitCB& init_cb) override;
 
   // Should be called when the Video size changes.
   // Can be called from any thread, but runs on |main_task_runner_|.
@@ -92,7 +93,7 @@
   void Destroy() override;
 
   void InitializeOnMainThread(const base::Closure& received_frame_cb,
-                              const base::Closure& init_cb);
+                              const StreamTextureWrapperInitCB& init_cb);
 
   void ReallocateVideoFrame(const gfx::Size& natural_size);
 
diff --git a/content/renderer/media/cdm/ppapi_decryptor.cc b/content/renderer/media/cdm/ppapi_decryptor.cc
index b76c0218..c22887a 100644
--- a/content/renderer/media/cdm/ppapi_decryptor.cc
+++ b/content/renderer/media/cdm/ppapi_decryptor.cc
@@ -416,9 +416,8 @@
                               std::move(keys_info));
 }
 
-void PpapiDecryptor::OnSessionExpirationUpdate(
-    const std::string& session_id,
-    const base::Time& new_expiry_time) {
+void PpapiDecryptor::OnSessionExpirationUpdate(const std::string& session_id,
+                                               base::Time new_expiry_time) {
   DCHECK(render_task_runner_->BelongsToCurrentThread());
   session_expiration_update_cb_.Run(session_id, new_expiry_time);
 }
diff --git a/content/renderer/media/cdm/ppapi_decryptor.h b/content/renderer/media/cdm/ppapi_decryptor.h
index bd57098..64b1507 100644
--- a/content/renderer/media/cdm/ppapi_decryptor.h
+++ b/content/renderer/media/cdm/ppapi_decryptor.h
@@ -119,7 +119,7 @@
                            bool has_additional_usable_key,
                            media::CdmKeysInfo keys_info);
   void OnSessionExpirationUpdate(const std::string& session_id,
-                                 const base::Time& new_expiry_time);
+                                 base::Time new_expiry_time);
   void OnSessionClosed(const std::string& session_id);
 
   void AttemptToResumePlayback();
diff --git a/content/renderer/media/html_audio_element_capturer_source_unittest.cc b/content/renderer/media/html_audio_element_capturer_source_unittest.cc
index 10d24ed..7c0c8852 100644
--- a/content/renderer/media/html_audio_element_capturer_source_unittest.cc
+++ b/content/renderer/media/html_audio_element_capturer_source_unittest.cc
@@ -11,6 +11,7 @@
 #include "media/audio/null_audio_sink.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/fake_audio_render_callback.h"
+#include "media/base/media_log.h"
 #include "media/blink/webaudiosourceprovider_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -66,7 +67,8 @@
   HTMLAudioElementCapturerSourceTest()
       : fake_callback_(0.1),
         audio_source_(new media::WebAudioSourceProviderImpl(
-            new media::NullAudioSink(base::ThreadTaskRunnerHandle::Get()))) {}
+            new media::NullAudioSink(base::ThreadTaskRunnerHandle::Get()),
+            new media::MediaLog())) {}
 
   void SetUp() final {
     const media::AudioParameters params(
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 09d026fb..a50204f 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -67,6 +67,7 @@
 #include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
+#include "content/public/common/appcache_info.h"
 #include "content/public/common/associated_interface_provider.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/browser_side_navigation_policy.h"
@@ -2891,9 +2892,19 @@
     blink::WebApplicationCacheHostClient* client) {
   if (!frame_ || !frame_->view())
     return NULL;
+
+  DocumentState* document_state =
+      frame_->provisionalDataSource()
+          ? DocumentState::FromDataSource(frame_->provisionalDataSource())
+          : DocumentState::FromDataSource(frame_->dataSource());
+
+  NavigationStateImpl* navigation_state =
+      static_cast<NavigationStateImpl*>(document_state->navigation_state());
+
   return new RendererWebApplicationCacheHostImpl(
       RenderViewImpl::FromWebView(frame_->view()), client,
-      RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy());
+      RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy(),
+      navigation_state->request_params().appcache_host_id);
 }
 
 blink::WebWorkerContentSettingsClientProxy*
@@ -5041,6 +5052,7 @@
 
   NavigateInternal(common_params, StartNavigationParams(), request_params,
                    std::move(stream_override));
+
   browser_side_navigation_pending_ = false;
 }
 
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 69eeeec..597668c6 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -125,29 +125,16 @@
   bool visible_;
 };
 
-#if defined(OS_ANDROID)
-// See https://crbug.com/472717
-#define MAYBE_SubframeWidget DISABLED_SubframeWidget
-#define MAYBE_FrameResize DISABLED_FrameResize
-#define MAYBE_FrameWasShown DISABLED_FrameWasShown
-#define MAYBE_FrameWasShownAfterWidgetClose DISABLED_FrameWasShownAfterWidgetClose
-#else
-#define MAYBE_SubframeWidget SubframeWidget
-#define MAYBE_FrameResize FrameResize
-#define MAYBE_FrameWasShown FrameWasShown
-#define MAYBE_FrameWasShownAfterWidgetClose FrameWasShownAfterWidgetClose
-#endif
-
 // Verify that a frame with a RenderFrameProxy as a parent has its own
 // RenderWidget.
-TEST_F(RenderFrameImplTest, MAYBE_SubframeWidget) {
+TEST_F(RenderFrameImplTest, SubframeWidget) {
   EXPECT_TRUE(frame_widget());
   EXPECT_NE(frame_widget(), static_cast<RenderViewImpl*>(view_)->GetWidget());
 }
 
 // Verify a subframe RenderWidget properly processes its viewport being
 // resized.
-TEST_F(RenderFrameImplTest, MAYBE_FrameResize) {
+TEST_F(RenderFrameImplTest, FrameResize) {
   ResizeParams resize_params;
   gfx::Size size(200, 200);
   resize_params.screen_info = ScreenInfo();
@@ -164,7 +151,7 @@
 }
 
 // Verify a subframe RenderWidget properly processes a WasShown message.
-TEST_F(RenderFrameImplTest, MAYBE_FrameWasShown) {
+TEST_F(RenderFrameImplTest, FrameWasShown) {
   RenderFrameTestObserver observer(frame());
 
   ViewMsg_WasShown was_shown_message(0, true, ui::LatencyInfo());
@@ -176,7 +163,7 @@
 
 // Ensure that a RenderFrameImpl does not crash if the RenderView receives
 // a WasShown message after the frame's widget has been closed.
-TEST_F(RenderFrameImplTest, MAYBE_FrameWasShownAfterWidgetClose) {
+TEST_F(RenderFrameImplTest, FrameWasShownAfterWidgetClose) {
   RenderFrameTestObserver observer(frame());
 
   ViewMsg_Close close_message(0);
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index aedf2fa4..fbb1c17 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -475,9 +475,7 @@
       request.getReferrerPolicy());
   params.disposition = WindowOpenDisposition::CURRENT_TAB;
   params.should_replace_current_entry = should_replace_current_entry;
-  params.user_gesture =
-      blink::WebUserGestureIndicator::isProcessingUserGesture();
-  blink::WebUserGestureIndicator::consumeUserGesture();
+  params.user_gesture = request.hasUserGesture();
   Send(new FrameHostMsg_OpenURL(routing_id_, params));
 }
 
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index 85bfe6e1..eef795a 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -17,10 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
 #include "cc/output/buffer_to_texture_target_map.h"
-#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
-#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
 #include "content/app/mojo/mojo_init.h"
 #include "content/child/child_gpu_memory_buffer_manager.h"
 #include "content/common/in_process_child_thread_params.h"
@@ -41,6 +38,7 @@
 #include "content/renderer/render_process_impl.h"
 #include "content/test/mock_render_process.h"
 #include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/ipc/host/gpu_switches.h"
 #include "ipc/ipc.mojom.h"
 #include "ipc/ipc_channel_mojo.h"
 #include "mojo/edk/embedder/embedder.h"
@@ -389,109 +387,5 @@
                                          gfx::BufferFormat::BGRA_8888,
                                          gfx::BufferFormat::YVU_420)));
 
-class RenderThreadImplDiscardableMemoryBrowserTest : public ContentBrowserTest {
- public:
-  RenderThreadImplDiscardableMemoryBrowserTest()
-      : child_discardable_shared_memory_manager_(nullptr) {}
-
-  // Overridden from BrowserTestBase:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(switches::kSingleProcess);
-  }
-  void SetUpOnMainThread() override {
-    NavigateToURL(shell(), GURL(url::kAboutBlankURL));
-    PostTaskToInProcessRendererAndWait(base::Bind(
-        &RenderThreadImplDiscardableMemoryBrowserTest::SetUpOnRenderThread,
-        base::Unretained(this)));
-  }
-
-  discardable_memory::ClientDiscardableSharedMemoryManager*
-  child_discardable_shared_memory_manager() {
-    return child_discardable_shared_memory_manager_;
-  }
-
- private:
-  void SetUpOnRenderThread() {
-    child_discardable_shared_memory_manager_ =
-        RenderThreadImpl::current()->GetDiscardableSharedMemoryManagerForTest();
-  }
-
-  discardable_memory::ClientDiscardableSharedMemoryManager*
-      child_discardable_shared_memory_manager_;
-};
-
-IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
-                       LockDiscardableMemory) {
-  const size_t kSize = 1024 * 1024;  // 1MiB.
-
-  std::unique_ptr<base::DiscardableMemory> memory =
-      child_discardable_shared_memory_manager()
-          ->AllocateLockedDiscardableMemory(kSize);
-
-  ASSERT_TRUE(memory);
-  void* addr = memory->data();
-  ASSERT_NE(nullptr, addr);
-
-  memory->Unlock();
-
-  // Purge all unlocked memory.
-  discardable_memory::DiscardableSharedMemoryManager::GetInstance()
-      ->SetMemoryLimit(0);
-
-  // Should fail as memory should have been purged.
-  EXPECT_FALSE(memory->Lock());
-}
-
-// Disable the test for the Android asan build.
-// See http://crbug.com/667837 for detail.
-#if !(defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
-IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
-                       DiscardableMemoryAddressSpace) {
-  const size_t kLargeSize = 4 * 1024 * 1024;   // 4MiB.
-  const size_t kNumberOfInstances = 1024 + 1;  // >4GiB total.
-
-  ScopedVector<base::DiscardableMemory> instances;
-  for (size_t i = 0; i < kNumberOfInstances; ++i) {
-    std::unique_ptr<base::DiscardableMemory> memory =
-        child_discardable_shared_memory_manager()
-            ->AllocateLockedDiscardableMemory(kLargeSize);
-    ASSERT_TRUE(memory);
-    void* addr = memory->data();
-    ASSERT_NE(nullptr, addr);
-    memory->Unlock();
-    instances.push_back(std::move(memory));
-  }
-}
-#endif
-
-IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
-                       ReleaseFreeDiscardableMemory) {
-  const size_t kSize = 1024 * 1024;  // 1MiB.
-
-  std::unique_ptr<base::DiscardableMemory> memory =
-      child_discardable_shared_memory_manager()
-          ->AllocateLockedDiscardableMemory(kSize);
-
-  EXPECT_TRUE(memory);
-  memory.reset();
-
-  EXPECT_GE(discardable_memory::DiscardableSharedMemoryManager::GetInstance()
-                ->GetBytesAllocated(),
-            kSize);
-
-  child_discardable_shared_memory_manager()->ReleaseFreeMemory();
-
-  // Busy wait for host memory usage to be reduced.
-  base::TimeTicks end =
-      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(5);
-  while (base::TimeTicks::Now() < end) {
-    if (!discardable_memory::DiscardableSharedMemoryManager::GetInstance()
-             ->GetBytesAllocated())
-      break;
-  }
-
-  EXPECT_LT(base::TimeTicks::Now(), end);
-}
-
 }  // namespace
 }  // namespace content
diff --git a/content/renderer/render_thread_impl_discardable_memory_browsertest.cc b/content/renderer/render_thread_impl_discardable_memory_browsertest.cc
new file mode 100644
index 0000000..aebe5145
--- /dev/null
+++ b/content/renderer/render_thread_impl_discardable_memory_browsertest.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 "content/renderer/render_thread_impl.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/discardable_memory.h"
+#include "base/memory/scoped_vector.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
+#include "components/discardable_memory/service/discardable_shared_memory_manager.h"
+#include "content/child/child_gpu_memory_buffer_manager.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "gpu/ipc/client/gpu_memory_buffer_impl.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "url/gurl.h"
+
+namespace content {
+namespace {
+
+class RenderThreadImplDiscardableMemoryBrowserTest : public ContentBrowserTest {
+ public:
+  RenderThreadImplDiscardableMemoryBrowserTest()
+      : child_discardable_shared_memory_manager_(nullptr) {}
+
+  // Overridden from BrowserTestBase:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kSingleProcess);
+  }
+
+  void SetUpOnMainThread() override {
+    NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+    PostTaskToInProcessRendererAndWait(base::Bind(
+        &RenderThreadImplDiscardableMemoryBrowserTest::SetUpOnRenderThread,
+        base::Unretained(this)));
+  }
+
+  discardable_memory::ClientDiscardableSharedMemoryManager*
+  child_discardable_shared_memory_manager() {
+    return child_discardable_shared_memory_manager_;
+  }
+
+ private:
+  void SetUpOnRenderThread() {
+    child_discardable_shared_memory_manager_ =
+        RenderThreadImpl::current()->GetDiscardableSharedMemoryManagerForTest();
+  }
+
+  discardable_memory::ClientDiscardableSharedMemoryManager*
+      child_discardable_shared_memory_manager_;
+};
+
+IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
+                       LockDiscardableMemory) {
+  const size_t kSize = 1024 * 1024;  // 1MiB.
+
+  std::unique_ptr<base::DiscardableMemory> memory =
+      child_discardable_shared_memory_manager()
+          ->AllocateLockedDiscardableMemory(kSize);
+
+  ASSERT_TRUE(memory);
+  void* addr = memory->data();
+  ASSERT_NE(nullptr, addr);
+
+  memory->Unlock();
+
+  // Purge all unlocked memory.
+  discardable_memory::DiscardableSharedMemoryManager::GetInstance()
+      ->SetMemoryLimit(0);
+
+  // Should fail as memory should have been purged.
+  EXPECT_FALSE(memory->Lock());
+}
+
+// Disable the test for the Android asan build.
+// See http://crbug.com/667837 for detail.
+#if !(defined(OS_ANDROID) && defined(ADDRESS_SANITIZER))
+IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
+                       DiscardableMemoryAddressSpace) {
+  const size_t kLargeSize = 4 * 1024 * 1024;   // 4MiB.
+  const size_t kNumberOfInstances = 1024 + 1;  // >4GiB total.
+
+  ScopedVector<base::DiscardableMemory> instances;
+  for (size_t i = 0; i < kNumberOfInstances; ++i) {
+    std::unique_ptr<base::DiscardableMemory> memory =
+        child_discardable_shared_memory_manager()
+            ->AllocateLockedDiscardableMemory(kLargeSize);
+    ASSERT_TRUE(memory);
+    void* addr = memory->data();
+    ASSERT_NE(nullptr, addr);
+    memory->Unlock();
+    instances.push_back(std::move(memory));
+  }
+}
+#endif
+
+IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest,
+                       ReleaseFreeDiscardableMemory) {
+  const size_t kSize = 1024 * 1024;  // 1MiB.
+
+  std::unique_ptr<base::DiscardableMemory> memory =
+      child_discardable_shared_memory_manager()
+          ->AllocateLockedDiscardableMemory(kSize);
+
+  EXPECT_TRUE(memory);
+  memory.reset();
+
+  EXPECT_GE(discardable_memory::DiscardableSharedMemoryManager::GetInstance()
+                ->GetBytesAllocated(),
+            kSize);
+
+  child_discardable_shared_memory_manager()->ReleaseFreeMemory();
+
+  // Busy wait for host memory usage to be reduced.
+  base::TimeTicks end =
+      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(5);
+  while (base::TimeTicks::Now() < end) {
+    if (!discardable_memory::DiscardableSharedMemoryManager::GetInstance()
+             ->GetBytesAllocated())
+      break;
+  }
+
+  EXPECT_LT(base::TimeTicks::Now(), end);
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 637ad275..e672024 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -2308,9 +2308,14 @@
 
 blink::WebInputMethodController* RenderWidget::GetInputMethodController()
     const {
-  // TODO(ekaramad): Remove this CHECK when GetWebWidget() is
-  // always a WebFrameWidget.
-  CHECK(GetWebWidget()->isWebFrameWidget());
+  if (!GetWebWidget()->isWebFrameWidget()) {
+    // TODO(ekaramad): We should not get here in response to IME IPC or updates
+    // when the RenderWidget is swapped out. We should top sending IPCs from the
+    // browser side (https://crbug.com/669219).
+    // If there is no WebFrameWidget, then there will be no
+    // InputMethodControllers for a WebLocalFrame.
+    return nullptr;
+  }
   return static_cast<blink::WebFrameWidget*>(GetWebWidget())
       ->getActiveWebInputMethodController();
 }
diff --git a/content/renderer/renderer_main_platform_delegate_android.cc b/content/renderer/renderer_main_platform_delegate_android.cc
index 0c9b6cb..b41da3c2 100644
--- a/content/renderer/renderer_main_platform_delegate_android.cc
+++ b/content/renderer/renderer_main_platform_delegate_android.cc
@@ -9,8 +9,9 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "sandbox/sandbox_features.h"
 
-#ifdef USE_SECCOMP_BPF
+#if BUILDFLAG(USE_SECCOMP_BPF)
 #include "content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h"
 #include "content/public/common/content_features.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
@@ -47,7 +48,7 @@
   DISALLOW_COPY_AND_ASSIGN(RecordSeccompStatus);
 };
 
-#ifdef USE_SECCOMP_BPF
+#if BUILDFLAG(USE_SECCOMP_BPF)
 // Determines if the running device should support Seccomp, based on the Android
 // SDK version.
 bool IsSeccompBPFSupportedBySDK() {
@@ -92,7 +93,7 @@
 bool RendererMainPlatformDelegate::EnableSandbox() {
   RecordSeccompStatus status_uma;
 
-#ifdef USE_SECCOMP_BPF
+#if BUILDFLAG(USE_SECCOMP_BPF)
   // Determine if Seccomp is available via the Android SDK version.
   if (!IsSeccompBPFSupportedBySDK())
     return true;
diff --git a/content/renderer/renderer_webapplicationcachehost_impl.cc b/content/renderer/renderer_webapplicationcachehost_impl.cc
index 817d69d..7ac1620 100644
--- a/content/renderer/renderer_webapplicationcachehost_impl.cc
+++ b/content/renderer/renderer_webapplicationcachehost_impl.cc
@@ -18,8 +18,9 @@
 RendererWebApplicationCacheHostImpl::RendererWebApplicationCacheHostImpl(
     RenderViewImpl* render_view,
     WebApplicationCacheHostClient* client,
-    AppCacheBackend* backend)
-    : WebApplicationCacheHostImpl(client, backend),
+    AppCacheBackend* backend,
+    int appcache_host_id)
+    : WebApplicationCacheHostImpl(client, backend, appcache_host_id),
       routing_id_(render_view->GetRoutingID()) {}
 
 void RendererWebApplicationCacheHostImpl::OnLogMessage(
diff --git a/content/renderer/renderer_webapplicationcachehost_impl.h b/content/renderer/renderer_webapplicationcachehost_impl.h
index c8efaaf..0250719 100644
--- a/content/renderer/renderer_webapplicationcachehost_impl.h
+++ b/content/renderer/renderer_webapplicationcachehost_impl.h
@@ -15,7 +15,8 @@
   RendererWebApplicationCacheHostImpl(
       RenderViewImpl* render_view,
       blink::WebApplicationCacheHostClient* client,
-      AppCacheBackend* backend);
+      AppCacheBackend* backend,
+      int appcache_host_id);
 
   // WebApplicationCacheHostImpl:
   void OnLogMessage(AppCacheLogLevel log_level,
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index 4b0d291..963b11d 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -18,6 +18,7 @@
 #include "content/child/shared_worker_devtools_agent.h"
 #include "content/child/webmessageportchannel_impl.h"
 #include "content/common/worker_messages.h"
+#include "content/public/common/appcache_info.h"
 #include "content/public/common/origin_util.h"
 #include "content/renderer/devtools/devtools_agent.h"
 #include "content/renderer/render_thread_impl.h"
@@ -40,10 +41,10 @@
  public:
   SharedWorkerWebApplicationCacheHostImpl(
       blink::WebApplicationCacheHostClient* client)
-      : WebApplicationCacheHostImpl(client,
-                                    RenderThreadImpl::current()
-                                        ->appcache_dispatcher()
-                                        ->backend_proxy()) {}
+      : WebApplicationCacheHostImpl(
+            client,
+            RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy(),
+            kAppCacheNoHostId) {}
 
   // Main resource loading is different for workers. The main resource is
   // loaded by the worker using WorkerScriptLoader.
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index ad98ab1..2400fd2 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -308,10 +308,6 @@
   if (use_x11) {
     # Some tests rely on this tool at runtime. Note: it might be better if
     # the tests that needed it had this as a dep instead of adding it here.
-    data_deps = [
-      "//tools/xdisplaycheck",
-    ]
-
     deps += [ "//ui/events/devices/x11" ]
   }
 
diff --git a/content/shell/browser/shell_devtools_frontend.cc b/content/shell/browser/shell_devtools_frontend.cc
index 6a81d71c..43828c93 100644
--- a/content/shell/browser/shell_devtools_frontend.cc
+++ b/content/shell/browser/shell_devtools_frontend.cc
@@ -200,7 +200,7 @@
   if (!parsed || !parsed->GetAsDictionary(&dict))
     return;
   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
-    if (!it.value().IsType(base::Value::TYPE_STRING))
+    if (!it.value().IsType(base::Value::Type::STRING))
       continue;
     preferences_.SetWithoutPathExpansion(it.key(), it.value().CreateDeepCopy());
   }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 2dc8a3c..435d9664 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -317,7 +317,7 @@
       "//third_party/webrtc/api:libjingle_peerconnection",
       "//third_party/webrtc/api:rtc_stats_api",
       "//third_party/webrtc/base:rtc_base",
-      "//third_party/webrtc/media:rtc_media",
+      "//third_party/webrtc/media:rtc_media_base",
       "//third_party/webrtc/modules/video_capture",
       "//third_party/webrtc/stats:rtc_stats",
     ]
@@ -700,6 +700,7 @@
     "//device/wake_lock",
     "//gin",
     "//gpu",
+    "//gpu/ipc/host",
     "//ipc:mojom",
     "//ipc:test_support",
     "//media",
@@ -824,6 +825,9 @@
         "${target_gen_dir}/content_browsertests_manifest/AndroidManifest.xml"
     android_manifest_dep = ":content_browsertests_manifest"
     use_default_launcher = false
+  } else {
+    # Non-Android.
+    sources += [ "../browser/speech/speech_recognition_browsertest.cc" ]
   }
 
   if (is_mac) {
@@ -886,16 +890,13 @@
     data_deps += [ "//ppapi:ppapi_tests" ]
   }
 
-  if (enable_web_speech) {
-    sources += [ "../browser/speech/speech_recognition_browsertest.cc" ]
-  }
-
   if (!is_chrome_branded) {
     # These tests depend on single process mode, which is disabled
     # in official builds.
     sources += [
       "../renderer/browser_render_view_browsertest.cc",
       "../renderer/dom_serializer_browsertest.cc",
+      "../renderer/render_thread_impl_discardable_memory_browsertest.cc",
       "../renderer/resource_fetcher_browsertest.cc",
       "../renderer/savable_resources_browsertest.cc",
     ]
@@ -1583,15 +1584,6 @@
     ]
   }
 
-  if (enable_web_speech) {
-    sources += [
-      "../browser/speech/chunked_byte_buffer_unittest.cc",
-      "../browser/speech/endpointer/endpointer_unittest.cc",
-      "../browser/speech/speech_recognition_engine_unittest.cc",
-      "../browser/speech/speech_recognizer_impl_unittest.cc",
-    ]
-  }
-
   if (is_linux) {
     if (use_dbus) {
       deps += [ "//dbus:test_support" ]
@@ -1639,8 +1631,14 @@
     ]
 
     defines += [ "APPCACHE_USE_SIMPLE_CACHE" ]
-  }
-  if (!is_android) {
+  } else {
+    # Non-Android.
+    sources += [
+      "../browser/speech/chunked_byte_buffer_unittest.cc",
+      "../browser/speech/endpointer/endpointer_unittest.cc",
+      "../browser/speech/speech_recognition_engine_unittest.cc",
+      "../browser/speech/speech_recognizer_impl_unittest.cc",
+    ]
     deps += [ "//third_party/libvpx" ]
   }
 
diff --git a/content/test/data/navigation_controller/reload-with-url-anchor.html b/content/test/data/navigation_controller/reload-with-url-anchor.html
new file mode 100644
index 0000000..7b1ba4d
--- /dev/null
+++ b/content/test/data/navigation_controller/reload-with-url-anchor.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+
+<style>
+div {
+  height: 100px;
+}
+
+#div {
+  overflow-y: scroll;
+}
+</style>
+
+<div id='div'>
+  <div>d1</div>
+  <div id='d2'>d2</div>
+</div>
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 3e6b0f6..ab4687a 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -163,7 +163,7 @@
       'swarming_dimensions': [
         {
           'gpu': '10de:104a',
-          'os': 'Windows-2012ServerR2-SP0'
+          'os': 'Windows-10-10586'
         },
       ],
       'build_config': 'Release',
@@ -174,7 +174,29 @@
       'swarming_dimensions': [
         {
           'gpu': '10de:104a',
-          'os': 'Windows-2012ServerR2-SP0'
+          'os': 'Windows-10-10586'
+        },
+      ],
+      'build_config': 'Debug',
+      'swarming': True,
+      'os_type': 'win',
+    },
+    'Win10 Release (NVIDIA)': {
+      'swarming_dimensions': [
+        {
+          'gpu': '10de:104a',
+          'os': 'Windows-10-10586'
+        },
+      ],
+      'build_config': 'Release',
+      'swarming': True,
+      'os_type': 'win',
+    },
+    'Win10 Debug (NVIDIA)': {
+      'swarming_dimensions': [
+        {
+          'gpu': '10de:104a',
+          'os': 'Windows-10-10586'
         },
       ],
       'build_config': 'Debug',
@@ -726,6 +748,9 @@
         ],
       },
     ],
+    'args': [
+      '--test-launcher-batch-limit=400'
+    ]
   },
 
   'angle_deqp_gles2_d3d11_tests': {
@@ -754,7 +779,10 @@
       'shards': 4,
     },
     'test': 'angle_deqp_gles2_tests',
-    'args': ['--deqp-egl-display-type=angle-d3d11']
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-d3d11'
+    ]
   },
 
   'angle_deqp_gles2_gl_tests': {
@@ -778,7 +806,10 @@
       'shards': 4,
     },
     'test': 'angle_deqp_gles2_tests',
-    'args': ['--deqp-egl-display-type=angle-gl'],
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-gl'
+    ]
   },
 
   'angle_deqp_gles2_gles_tests': {
@@ -803,7 +834,10 @@
     # Only pass the display type to desktop. The Android runner doesn't support
     # passing args to the executable but only one display type is supported on
     # Android anyways.
-    'desktop_args': ['--deqp-egl-display-type=angle-gles'],
+    'desktop_args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-gles'
+    ],
     'android_args': ['--enable-xml-result-parsing']
   },
 
@@ -829,7 +863,10 @@
       'shards': 12,
     },
     'test': 'angle_deqp_gles3_tests',
-    'args': ['--deqp-egl-display-type=angle-d3d11']
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-d3d11'
+    ]
   },
 
   'angle_deqp_gles3_gl_tests': {
@@ -853,7 +890,37 @@
       'shards': 12,
     },
     'test': 'angle_deqp_gles3_tests',
-    'args': ['--deqp-egl-display-type=angle-gl']
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-gl'
+    ]
+  },
+
+  'angle_deqp_gles31_d3d11_tests': {
+    'tester_configs': [
+      {
+        'fyi_only': True,
+        'run_on_optional': False,
+        # Run on the Win Release NVIDIA bots.
+        'build_configs': ['Release'],
+        'swarming_dimension_sets': [
+          {
+            'gpu': '10de:104a',
+            'os': 'Windows-2008ServerR2-SP1'
+          }
+        ],
+      }
+    ],
+    'swarming': {
+      # TODO(geofflang): Increase the number of shards as more tests start to
+      # pass and runtime increases.
+      'shards': 4,
+    },
+    'test': 'angle_deqp_gles31_tests',
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-d3d11'
+    ]
   },
 
   'angle_deqp_gles31_gl_tests': {
@@ -881,7 +948,10 @@
       'shards': 4,
     },
     'test': 'angle_deqp_gles31_tests',
-    'args': ['--deqp-egl-display-type=angle-gl']
+    'args': [
+      '--test-launcher-batch-limit=400',
+      '--deqp-egl-display-type=angle-gl'
+    ]
   },
 
   # Until we have more capacity, run angle_end2end_tests only on the
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 7a6d171..cdf4bff 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -153,7 +153,7 @@
     # new AMD (the whole test suite is flaky on the old config).
     # Mark as Fail since it often flakes in all 3 retries
     self.Fail('conformance/extensions/oes-texture-half-float.html',
-              ['win', ('amd', 0x6613)], bug=653533)
+              ['win', 'no_passthrough', ('amd', 0x6613)], bug=653533)
 
     # Win / AMD D3D9 failures
     self.Fail('conformance/extensions/angle-instanced-arrays.html',
@@ -213,12 +213,43 @@
         ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
     self.Fail('conformance/uniforms/uniform-default-values.html',
         ['win', 'intel', 'opengl'], bug=1007) # angle bug ID
+    self.Fail('conformance/glsl/bugs/sampler-struct-function-arg.html',
+        ['win10', 'intel', 'opengl'], bug=1007) # angle bug ID
+    self.Fail('conformance/glsl/variables/gl-pointcoord.html',
+        ['win10', 'intel', 'opengl'], bug=1007) # angle bug ID
 
     # Win / Passthrough command decoder
     self.Fail('conformance/attribs/gl-vertexattribpointer.html',
         ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
-    self.Fail('conformance/extensions/*', ['win', 'passthrough', 'd3d11'],
-        bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/angle-instanced-arrays.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/' +
+        'angle-instanced-arrays-out-of-bounds.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/ext-disjoint-timer-query.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/ext-frag-depth.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/ext-shader-texture-lod.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/oes-standard-derivatives.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/oes-texture-float.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/oes-texture-float-linear.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/oes-texture-half-float.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/oes-texture-half-float-linear.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/webgl-compressed-texture-s3tc.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/webgl-debug-shaders.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/webgl-depth-texture.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
+    self.Fail('conformance/extensions/webgl-draw-buffers.html',
+        ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
     self.Fail('conformance/canvas/framebuffer-bindings-unaffected-on-' +
         'resize.html', ['win', 'passthrough', 'd3d11'], bug=665521)
     self.Fail('conformance/glsl/bugs/essl3-shaders-with-webgl1.html',
@@ -243,8 +274,6 @@
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/limits/gl-line-width.html',
         ['win', 'passthrough', 'd3d11'], bug=1523) # angle bug ID
-    self.Fail('conformance/misc/error-reporting.html',
-        ['win', 'passthrough', 'd3d11'], bug=602688)
     self.Fail('conformance/misc/invalid-passed-params.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/misc/object-deletion-behaviour.html',
@@ -277,18 +306,18 @@
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/more/functions/vertexAttribPointerBadArgs.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
-    self.Fail('conformance/programs/get-active-test.html',
-        ['win', 'passthrough', 'd3d11'], bug=602688)
-    self.Fail('conformance/programs/program-test.html',
-        ['win', 'passthrough', 'd3d11'], bug=602688)
     self.Fail('conformance/reading/read-pixels-test.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/renderbuffers/feedback-loop.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/renderbuffers/framebuffer-object-attachment.html',
         ['win', 'passthrough', 'd3d11'], bug=602688)
+    self.Fail('conformance/renderbuffers/framebuffer-state-restoration.html',
+        ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/renderbuffers/framebuffer-test.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
+    self.Fail('conformance/renderbuffers/renderbuffer-initialization.html',
+        ['win', 'passthrough', 'd3d11'], bug=1635) # angle bug ID
     self.Fail('conformance/rendering/draw-arrays-out-of-bounds.html',
         ['win', 'passthrough', 'd3d11'], bug=1639) # angle bug ID
     self.Fail('conformance/rendering/draw-elements-out-of-bounds.html',
diff --git a/courgette/courgette.h b/courgette/courgette.h
index 5f3dbb8..2dacc787 100644
--- a/courgette/courgette.h
+++ b/courgette/courgette.h
@@ -63,7 +63,6 @@
 class SinkStream;
 class SinkStreamSet;
 class SourceStream;
-class SourceStreamSet;
 
 class AssemblyProgram;
 class EncodedProgram;
diff --git a/courgette/disassembler_elf_32_arm.h b/courgette/disassembler_elf_32_arm.h
index eec5b38..120380616 100644
--- a/courgette/disassembler_elf_32_arm.h
+++ b/courgette/disassembler_elf_32_arm.h
@@ -16,7 +16,6 @@
 
 namespace courgette {
 
-class AssemblyProgram;
 class InstructionReceptor;
 
 enum ARM_RVA {
diff --git a/courgette/disassembler_elf_32_x86.h b/courgette/disassembler_elf_32_x86.h
index ee05588..71e61ab 100644
--- a/courgette/disassembler_elf_32_x86.h
+++ b/courgette/disassembler_elf_32_x86.h
@@ -16,7 +16,6 @@
 
 namespace courgette {
 
-class AssemblyProgram;
 class InstructionReceptor;
 
 class DisassemblerElf32X86 : public DisassemblerElf32 {
diff --git a/courgette/disassembler_win32_x64.h b/courgette/disassembler_win32_x64.h
index 7c0ac01..a5e8317 100644
--- a/courgette/disassembler_win32_x64.h
+++ b/courgette/disassembler_win32_x64.h
@@ -15,7 +15,6 @@
 
 namespace courgette {
 
-class AssemblyProgram;
 class InstructionReceptor;
 
 class DisassemblerWin32X64 : public DisassemblerWin32 {
diff --git a/courgette/disassembler_win32_x86.h b/courgette/disassembler_win32_x86.h
index d73d4063..f747ef5 100644
--- a/courgette/disassembler_win32_x86.h
+++ b/courgette/disassembler_win32_x86.h
@@ -15,7 +15,6 @@
 
 namespace courgette {
 
-class AssemblyProgram;
 class InstructionReceptor;
 
 class DisassemblerWin32X86 : public DisassemblerWin32 {
diff --git a/crypto/nss_util.h b/crypto/nss_util.h
index a8b57ff..5c34fc8 100644
--- a/crypto/nss_util.h
+++ b/crypto/nss_util.h
@@ -14,7 +14,6 @@
 #include "crypto/crypto_export.h"
 
 namespace base {
-class FilePath;
 class Lock;
 class Time;
 }  // namespace base
diff --git a/dbus/bus.h b/dbus/bus.h
index d66048b..59a1972 100644
--- a/dbus/bus.h
+++ b/dbus/bus.h
@@ -28,10 +28,6 @@
 class TaskRunner;
 }
 
-namespace tracked_objects {
-class Location;
-}
-
 namespace dbus {
 
 class ExportedObject;
diff --git a/dbus/values_util.cc b/dbus/values_util.cc
index bea7bea7..4367791 100644
--- a/dbus/values_util.cc
+++ b/dbus/values_util.cc
@@ -67,19 +67,19 @@
 // Gets the D-Bus type signature for the value.
 std::string GetTypeSignature(const base::Value& value) {
   switch (value.GetType()) {
-    case base::Value::TYPE_BOOLEAN:
+    case base::Value::Type::BOOLEAN:
       return "b";
-    case base::Value::TYPE_INTEGER:
+    case base::Value::Type::INTEGER:
       return "i";
-    case base::Value::TYPE_DOUBLE:
+    case base::Value::Type::DOUBLE:
       return "d";
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::STRING:
       return "s";
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       return "ay";
-    case base::Value::TYPE_DICTIONARY:
+    case base::Value::Type::DICTIONARY:
       return "a{sv}";
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::LIST:
       return "av";
     default:
       DLOG(ERROR) << "Unexpected type " << value.GetType();
@@ -219,28 +219,28 @@
 
 void AppendBasicTypeValueData(MessageWriter* writer, const base::Value& value) {
   switch (value.GetType()) {
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool bool_value = false;
       bool success = value.GetAsBoolean(&bool_value);
       DCHECK(success);
       writer->AppendBool(bool_value);
       break;
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int int_value = 0;
       bool success = value.GetAsInteger(&int_value);
       DCHECK(success);
       writer->AppendInt32(int_value);
       break;
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double double_value = 0;
       bool success = value.GetAsDouble(&double_value);
       DCHECK(success);
       writer->AppendDouble(double_value);
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string string_value;
       bool success = value.GetAsString(&string_value);
       DCHECK(success);
@@ -263,7 +263,7 @@
 
 void AppendValueData(MessageWriter* writer, const base::Value& value) {
   switch (value.GetType()) {
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dictionary = NULL;
       value.GetAsDictionary(&dictionary);
       dbus::MessageWriter array_writer(NULL);
@@ -279,7 +279,7 @@
       writer->CloseContainer(&array_writer);
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list = NULL;
       value.GetAsList(&list);
       dbus::MessageWriter array_writer(NULL);
@@ -290,10 +290,10 @@
       writer->CloseContainer(&array_writer);
       break;
     }
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_INTEGER:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::INTEGER:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::STRING:
       AppendBasicTypeValueData(writer, value);
       break;
     default:
diff --git a/device/geolocation/network_location_request.cc b/device/geolocation/network_location_request.cc
index 00a142b..191f7a5 100644
--- a/device/geolocation/network_location_request.cc
+++ b/device/geolocation/network_location_request.cc
@@ -367,7 +367,7 @@
     return false;
   }
 
-  if (!response_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!response_value->IsType(base::Value::Type::DICTIONARY)) {
     VLOG(1) << "ParseServerResponse() : Unexpected response type "
             << response_value->GetType();
     return false;
@@ -388,8 +388,8 @@
   }
   DCHECK(location_value);
 
-  if (!location_value->IsType(base::Value::TYPE_DICTIONARY)) {
-    if (!location_value->IsType(base::Value::TYPE_NULL)) {
+  if (!location_value->IsType(base::Value::Type::DICTIONARY)) {
+    if (!location_value->IsType(base::Value::Type::NONE)) {
       VLOG(1) << "ParseServerResponse() : Unexpected location type "
               << location_value->GetType();
       // If the network provider was unable to provide a position fix, it should
diff --git a/docs/common_build_tasks.md b/docs/common_build_tasks.md
index 65a0b5f7..c042169 100644
--- a/docs/common_build_tasks.md
+++ b/docs/common_build_tasks.md
@@ -56,7 +56,8 @@
 
 ### Linux
 
-Linux has its own page on [making the build faster](linux_faster_builds.md).
+The Linux build instructions page has its own section on [making the build
+faster](linux_build_instructions.md#faster-builds).
 
 ## Configuring the Build
 
diff --git a/docs/es6_chromium.md b/docs/es6_chromium.md
index 17d083e8..055edc6 100644
--- a/docs/es6_chromium.md
+++ b/docs/es6_chromium.md
@@ -1,9 +1,9 @@
-<!-- This is feature markdown template
+<!-- Feature template markdown:
 ## Header
 
 **Usage Example:**
 
-``` js
+```js
 
 ```
 
@@ -12,10 +12,8 @@
 **Discussion Notes / Link to Thread:**
 
 hyphen-hyphen-hyphen (change to actual hyphen)
-
 -->
 
-
 <style type="text/css">
   .doc {
     font-size: 16px;
@@ -57,30 +55,30 @@
 </style>
 
 <script>
-document.addEventListener("DOMContentLoaded", function(event) {
+document.addEventListener('DOMContentLoaded', function(event) {
   // Move all headers and corresponding contents to an accordion container.
-  document.querySelectorAll('h2[id]').forEach(function(header){
+  document.querySelectorAll('h2[id]').forEach(function(header) {
     var container = document.createElement('div');
     container.classList.add('feature-container');
     header.parentNode.insertBefore(container, header);
 
-    // Add all the following siblings until it hits an <hr>
-    var ele = header;
-    while(ele && ele.tagName !== 'HR') {
-      var nextEle = ele.nextElementSibling;
-      container.append(ele);
-      ele = nextEle;
+    // Add all the following siblings until it hits an <hr>.
+    var el = header;
+    while (el && el.tagName !== 'HR') {
+      var nextEl = el.nextElementSibling;
+      container.append(el);
+      el = nextEl;
     }
 
     // Add handler to open accordion on click.
-    header.addEventListener('click', () => {
+    header.addEventListener('click', function() {
       header.parentNode.classList.toggle('open');
     });
   });
 
   // Then remove all <hr>s since everything's accordionized.
-  document.querySelectorAll('hr').forEach(function(ele){
-    ele.parentNode.removeChild(ele);
+  document.querySelectorAll('hr').forEach(function(el) {
+    el.parentNode.removeChild(el);
   });
 });
 </script>
@@ -89,13 +87,15 @@
 
 # ES6 Support In Chromium
 
-This is a list of new/updated features in ES6 specs that is being considered to
-be supported for Chromium development.
+This is a list of [ECMAScript6](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla)
+features allowed in Chromium code.
 
->**TBD:** Do we want to differenciate allow/ban status between subprojects? If
-so, how to denote?
+This is **not** a status list of [v8](https://developers.google.com/v8/)'s
+support for language features.
 
->**TBD:** Cross platform-build support?
+> **TBD:** Do we need to differentiate per-project?
+
+> **TBD:** Cross-platform build support? As in: transpilers?
 
 You can propose changing the status of a feature by sending an email to
 chromium-dev@chromium.org. Include a short blurb on what the feature is and why
@@ -103,8 +103,8 @@
 previous discussion. If the list arrives at some consensus, send a codereview
 to change this file accordingly, linking to your discussion thread.
 
->Some descriptions and Usage examples are from [kangax](https://kangax.github.
-io/compat-table/es6/) and [http://es6-features.org/](http://es6-features.org/)
+> Some descriptions and Usage examples are from [kangax](https://kangax.github.io/compat-table/es6/)
+and [http://es6-features.org/](http://es6-features.org/)
 
 # Allowed Features
 
@@ -112,31 +112,31 @@
 
 ## `Promise`
 
-Built-in representation of a value that might be determined asynchronously,
-relieving developers from "callback hell".
+The Promise object is used for asynchronous computations. A Promise represents a
+value which may be available now, or in the future, or never.
 
 **Usage Example:**
 
-``` js
-function promiseA() {
-  return new Promise((resolve, reject) => setTimeout(resolve, 100));
-}
+```js
+/** @type {!Promise} */
+var fullyLoaded = new Promise(function(resolve) {
+  function isLoaded() { return document.readyState == 'complete'; }
 
-function promiseB() {
-  return new Promise((resolve, reject) => setTimeout(resolve, 200));
-}
-
-function promiseC() {
-  return new Promise((resolve, reject) => setTimeout(resolve, 300));
-}
-
-Promise.all([promiseA(), promiseB(), promiseC()]).then(([a, b, c]) => {
-  someFunction(a, b, c);
+  if (isLoaded()) {
+    resolve();
+  } else {
+    document.onreadystatechange = function() {
+      if (isLoaded()) resolve();
+    };
+  }
 });
+
+// ... some time later ...
+loaded.then(startTheApp).then(maybeShowFirstRun);
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-promise-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
 
 **Discussion Notes:** Feature already extensively used prior to creation of
 this document.
@@ -155,36 +155,42 @@
 
 ## `let` (Block-Scoped Variables)
 
-Declare variable that exists within the block scope. `let` can generally be
-used to replace `var` but `let` in global scope, unlike `var`, does not
-introduce a property on the global object.
+`let` declares a variable within the scope of a block.  This differs from `var`,
+which uses function level scope.
 
 **Usage Example:**
 
-``` js
-// This will make all buttons output "3".
-for(var i = 0; i < 3; i++) {
-  buttons[i].onclick = function() {
-    console.log(i);
-  };
+```js
+// Scope.
+function varTest() {
+  var x = 1;
+  if (true) {
+    var x = 2;  // Same variable!
+    console.log(x);  // 2
+  }
+  console.log(x);  // 2
 }
 
-// This will make buttons output corresponding "i" values.
-for(let i = 0; i < 3; i++) {
-  buttons[i].onclick = function() {
-    console.log(i);
-  };
+function letTest() {
+  let x = 1;
+  if (true) {
+    let x = 2;  // Different variable.
+    console.log(x);  // 2
+  }
+  console.log(x);  // 1
 }
 
-var bar = 1;
-var bar = 1; // No error thrown.
+// Redeclaration throws.
+function f() {
+  var a = 'hello';
+  var a = 'hola';  // No error!
 
-let foo = 1;
-let foo = 1; // TypeError: Identifier 'foo' has already been declared.
+  let b = 'world';
+  let b = 'mundo;  // TypeError Identifier 'b' has already been declared.
+}
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-let-and-const-declarations)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-let-and-const-declarations)
 
 **Discussion Notes / Link to Thread:**
 
@@ -196,21 +202,23 @@
 re-assigned new content. Note that if the value is an object, the object itself
 is still mutable.
 
-`const` has traditionally been supported as a "function scoped" declaration
-like `var` (except in Internet Explorer), however in VMs supporting ES6 `const`
-is now a block scope declaration.
+Also note that in Chrome, `const` is block scoped like `let`.
 
 **Usage Example:**
 
-``` js
+```js
 const gravity = 9.81;
-gravity = 0; // TypeError: Assignment to constant variable.
+gravity = 0;  // TypeError: Assignment to constant variable.
+gravity === 9.81;  // true
 
-gravity === 9.81; // true
+const frobber = {isFrobbing: true};
+frobber = {isFrobbing: false};  // TypeError: Assignment to constant variable.
+frobber.isFrobbing = false;  // Works.
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-let-and-const-declarations)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-let-and-const-declarations)
+
+**See also:** [Object.freeze()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
 
 **Discussion Notes / Link to Thread:**
 
@@ -219,17 +227,16 @@
 ## `=>` (Arrow Functions)
 
 Arrow functions provide a concise syntax to create a function, and fix a number
-of difficulties with this (e.g. eliminating the need to write `const self =
-this`. Particularly useful for nested functions or callbacks.
+of difficulties with `this` (e.g. eliminating the need to write `const self =
+this`). Particularly useful for nested functions or callbacks.
 
-Prefer arrow functions over the function keyword, over `f.bind(this)`, and
-especially over `goog.bind(f, this)`.
+Prefer arrow functions over `.bind(this)`.
 
-Arrow functions has an implicit return when used without a body block.
+Arrow functions have an implicit return when used without a body block.
 
 **Usage Example:**
 
-``` js
+```js
 // General usage, eliminating need for .bind(this).
 setTimeout(() => {
   this.doSomething();
@@ -241,13 +248,12 @@
 });  // no need for .bind(this) or const self = this.
 
 // Implicit return: returns the value if expression not inside a body block.
-() => 1 // returns 1
-() => {1} // returns undefined - body block does not implicitly return.
-() => {return 1;} // returns 1
+() => 1  // returns 1.
+() => {1}  // returns undefined - body block does not implicitly return.
+() => {return 1;}  // returns 1.
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-arrow-function-definitions)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions)
 
 **Discussion Notes / Link to Thread:**
 
@@ -255,30 +261,35 @@
 
 ## Classes
 
-OOP-style and boilerplate-free class syntax, including inheritance, super(),
+OOP-style and boilerplate-free class syntax, including inheritance, `super()`,
 static members, and getters and setters.
 
 **Usage Example:**
 
-``` js
+```js
+class Shape {
+  constructor(x, y) {
+    this.x = x;
+    this.y = y;
+  }
+}
+// Note: let Shape = class {...}; is also valid.
+
 class Rectangle extends Shape {
-  constructor(id, x, y, width, height) {
+  constructor(x, y, width, height) {
     super(id, x, y);
     this.width  = width;
     this.height = height;
   }
-  static defaultRectangle() {
-    return new Rectangle('default', 0, 0, 100, 100);
+
+  static goldenRectangle() {
+		var PHI = (1 + Math.sqrt(5)) / 2;
+    return new Rectangle(0, 0, PHI, 1);
   }
-  move(x, y) {
-    this.x = x;
-    this.y = y;
-  }
-};
+}
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-class-definitions)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions)
 
 **Discussion Notes / Link to Thread:**
 
@@ -288,7 +299,7 @@
 
 **Usage Example:**
 
-``` js
+```js
 {
   function foo() {
     return 1;
@@ -304,8 +315,7 @@
 }
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-functiondeclarationinstantiation)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-functiondeclarationinstantiation)
 
 **Discussion Notes / Link to Thread:**
 
@@ -313,17 +323,30 @@
 
 ## Default Function Parameters
 
+Initialize parameters with default values if no value or `undefined` is passed.
+
 **Usage Example:**
 
-``` js
-function f(x, y = 7, z = 42) {
-  return x + y + z;
+```js
+/**
+ * @param {!Element} element An element to hide.
+ * @param {boolean=} animate Whether to animatedly hide |element|.
+ */
+function hide(element, animate=true) {
+  function setHidden() { element.hidden = true; }
+
+  if (animate)
+    element.animate({...}).then(setHidden);
+  else
+    setHidden();
 }
-// f(1) === 50;
+
+hide(document.body);  // Animated, animate=true by default.
+hide(document.body, false);  // Not animated.
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-functiondeclarationinstantiation)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-functiondeclarationinstantiation)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters)
 
 **Discussion Notes / Link to Thread:**
 
@@ -335,16 +358,17 @@
 
 **Usage Example:**
 
-``` js
-function f(x, y, ...a) {
-  // for f(1, 2, 3, 4, 5)...
-    // x = 1, y = 2
-    // a = [3, 4, 5]
+```js
+function usesRestParams(a, b, ...theRest) {
+  console.log(a);  // 'a'
+  console.log(b);  // 'b'
+  console.log(theRest);  // [1, 2, 3]
 }
+
+usesRestParams('a', 'b', 1, 2, 3);
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-function-definitions)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions)
 
 **Discussion Notes / Link to Thread:**
 
@@ -357,19 +381,17 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Spreading an Array
-var params = [ 'hello', true, 7 ];
-var other = [ 1, 2, ...params ]; // [ 1, 2, 'hello', true, 7 ]
-f(1, 2, ...params) === 9;
+var params = ['hello', true, 7];
+var other = [1, 2, ...params];  // [1, 2, 'hello', true, 7]
 
 // Spreading a String
 var str = 'foo';
-var chars = [ ...str ]; // [ 'f', 'o', 'o' ]
+var chars = [...str];  // ['f', 'o', 'o']
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-argument-lists-runtime-semantics-argumentlistevaluation)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-argument-lists-runtime-semantics-argumentlistevaluation)
 
 **Discussion Notes / Link to Thread:**
 
@@ -381,23 +403,33 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Computed property name
-var x = 'key';
-var obj = {[x]: 1};
+var prop = 'foo';
+var o = {
+  [prop]: 'hey',
+  ['b' + 'ar']: 'there',
+};
+console.log(o);  // {foo: 'hey', bar: 'there'}
 
 // Shorthand property
-var obj = {x, y}; //equivalent to {x:x, y:y}
+var foo = 1;
+var bar = 2;
+var o = {foo, bar};
+console.log(o);  // {foo: 1, bar: 2}
 
 // Method property
-var obj = {
-  foo() {...},
-  bar() {...}
-}
+var clearSky = {
+  // Basically the same as clouds: function() { return 0; }.
+  clouds() { return 0; },
+  color() { return 'blue'; },
+};
+console.log(clearSky.color());  // 'blue'
+console.log(clearSky.clouds());  // 0
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-object-initialiser)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initialiser)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
 
 **Discussion Notes / Link to Thread:**
 
@@ -410,29 +442,16 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Simple example
 var greeting = 'hello';
 var myName = {first: 'Foo', last: 'Bar'};
 var message = `${greeting},
 my name is ${myName.first + myName.last}`;
 // message == 'hello,\nmy name is FooBar'
-
-// Custom interpolation
-function foo (strings, ...values) {
-  // for foo`bar${42}baz`...
-    // strings[0] === 'bar';
-    // strings[1] === 'baz';
-    // values[0] === 42;
-
-  return strings[1] + strings[0] + values[0];
-}
-
-var newString = foo`bar${42}baz`; // 'bazbar42'
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-template-literals)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-template-literals)
 
 **Discussion Notes / Link to Thread:**
 
@@ -442,13 +461,12 @@
 
 **Usage Example:**
 
-``` js
+```js
 0b111110111 === 503;
 0o767 === 503;
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-literals-numeric-literals)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-literals-numeric-literals)
 
 **Discussion Notes / Link to Thread:**
 
@@ -458,12 +476,11 @@
 
 **Usage Example:**
 
-``` js
+```js
 'ð ®·'.match(/./u)[0].length === 2;
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-get-regexp.prototype.sticky)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-get-regexp.prototype.sticky)
 
 **Discussion Notes / Link to Thread:**
 
@@ -473,12 +490,11 @@
 
 **Usage Example:**
 
-``` js
-'\u{1d306}' == '\ud834\udf06'; // true
+```js
+'\u{1d306}' == '\ud834\udf06';  // true
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-literals-string-literals)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-literals)
 
 **Discussion Notes / Link to Thread:**
 
@@ -487,20 +503,19 @@
 ## `/y` Regex Sticky Matching
 
 Keep the matching position sticky between matches and this way support
-efficient parsing of arbitrary long input strings, even with an arbitrary
+efficient parsing of arbitrarily long input strings, even with an arbitrary
 number of distinct regular expressions.
 
 **Usage Example:**
 
-``` js
+```js
 var re = new RegExp('yy', 'y');
 re.lastIndex = 3;
 var result = re.exec('xxxyyxx')[0];
-result === 'yy' && re.lastIndex === 5; // true
+result === 'yy' && re.lastIndex === 5;  // true
 ```
 
-**Documentation:** [link](http://es6-features.org
-/#RegularExpressionStickyMatching)
+**Documentation:** [link](http://es6-features.org/#RegularExpressionStickyMatching)
 
 **Discussion Notes / Link to Thread:**
 
@@ -512,35 +527,33 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Array
-var list = [ 1, 2, 3 ];
-var [ a, , b ] = list;
-// a = 1, b = 3
+var [a, , b] = [1, 2, 3];  // a = 1, b = 3
 
 // Object
-var {width, height, area: a} = rect;
-// width = rect.width, height = rect.height, a = rect.area
+var {width, height} = document.body.getBoundingClientRect();
+// width = rect.width, height = rect.height
 
 // Parameters
-function f ([ name, val ]) {
-  console.log(name, val);
+function f([name, val]) {
+  console.log(name, val);  // 'bar', 42
 }
-function g ({ name: n, val: v }) {
-  console.log(n, v);
-}
-function h ({ name, val }) {
-  console.log(name, val);
-}
+f(['bar', 42, 'extra 1', 'extra 2']);  // 'extra 1' and 'extra 2' are ignored.
 
-f([ 'bar', 42 ]);
-g({ name: 'foo', val:  7 });
-h({ name: 'bar', val: 42 });
+function g({name: n, val: v}) {
+  console.log(n, v);  // 'foo', 7
+}
+g({name: 'foo', val:  7});
+
+function h({name, val}) {
+  console.log(name, val);  // 'bar', 42
+}
+h({name: 'bar', val: 42});
 
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-destructuring-assignment)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-assignment)
 
 **Discussion Notes / Link to Thread:**
 
@@ -553,18 +566,17 @@
 
 **Usage Example:**
 
-``` js
+```js
 // lib/rect.js
 export function getArea() {...};
-export { width, height, unimportant };
+export {width, height, unimportant};
 
 // app.js
 import {getArea, width, height} from 'lib/rect';
 
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-modules)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-modules)
 
 **Discussion Notes / Link to Thread:**
 
@@ -577,22 +589,21 @@
 
 **Usage Example:**
 
-``` js
+```js
 const foo = Symbol();
 const bar = Symbol();
-typeof foo === 'symbol'; // true
-typeof bar === 'symbol'; // true
+typeof foo === 'symbol';  // true
+typeof bar === 'symbol';  // true
 let obj = {};
 obj[foo] = 'foo';
 obj[bar] = 'bar';
-JSON.stringify(obj); // {}
-Object.keys(obj); // []
-Object.getOwnPropertyNames(obj); // []
-Object.getOwnPropertySymbols(obj); // [ foo, bar ]
+JSON.stringify(obj);  // {}
+Object.keys(obj);  // []
+Object.getOwnPropertyNames(obj);  // []
+Object.getOwnPropertySymbols(obj);  // [foo, bar]
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-symbol-constructor)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-symbol-constructor)
 
 **Discussion Notes / Link to Thread:**
 
@@ -600,19 +611,20 @@
 
 ## `for ...of` Loops
 
-Convenient operator to iterate over all values of an iterable object.
+Convenient operator to iterate over all values in an iterable collection. This
+differs from `for ...in`, which iterates over all iterable properties.
 
 **Usage Example:**
 
-``` js
-// Given an iterable  collection `fibonacci`...
-for (let n of fibonacci) {
-  console.log(n);
+```js
+// Given an iterable collection fibonacci numbers...
+for (var n of fibonacci) {
+  console.log(n);  // 1, 1, 2, 3, ...
 }
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-for-in-and-for-of-statements)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
 
 **Discussion Notes / Link to Thread:**
 
@@ -622,20 +634,19 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Object.assign
 var o = Object.assign({a:true}, {b:true}, {c:true});
-'a' in o && 'b' in o && 'c' in o; // true
+'a' in o && 'b' in o && 'c' in o;  // true
 
 // Object.setPrototypeOf
-Object.setPrototypeOf({}, Array.prototype) instanceof Array; //true
+Object.setPrototypeOf({}, Array.prototype) instanceof Array;  // true
 
 // Object.is
 // Object.getOwnPropertySymbols
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-properties-of-the-object-constructor)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-object-constructor)
 
 **Discussion Notes / Link to Thread:**
 
@@ -645,7 +656,7 @@
 
 **Usage Example:**
 
-``` js
+```js
 // String.raw
 // String.fromCodePoint
 
@@ -657,8 +668,7 @@
 // String.prototype.includes
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-properties-of-the-string-constructor)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-string-constructor)
 
 **Discussion Notes / Link to Thread:**
 
@@ -668,7 +678,7 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Array.from
 // Array.of
 
@@ -681,8 +691,7 @@
 // Array.prototype.entries
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-properties-of-the-array-constructor)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-array-constructor)
 
 **Discussion Notes / Link to Thread:**
 
@@ -692,7 +701,7 @@
 
 **Usage Example:**
 
-``` js
+```js
 // Number.isFinite
 // Number.isInteger
 // Number.isSafeInteger
@@ -702,8 +711,7 @@
 // Number.MAX_SAFE_INTEGER
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-isfinite-number)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-isfinite-number)
 
 **Discussion Notes / Link to Thread:**
 
@@ -713,14 +721,14 @@
 
 **Usage Example:**
 
-``` js
+```js
 let fibonacci = {
   [Symbol.iterator]() {
     let pre = 0, cur = 1;
     return {
       next () {
-        [ pre, cur ] = [ cur, pre + cur ];
-        return { done: false, value: cur };
+        [pre, cur] = [cur, pre + cur];
+        return {done: false, value: cur};
       }
     };
   }
@@ -739,7 +747,7 @@
 
 **Usage Example:**
 
-``` js
+```js
 function* range(start, end, step) {
   while (start < end) {
     yield start;
@@ -748,13 +756,12 @@
 }
 
 for (let i of range(0, 10, 2)) {
-  console.log(i); // 0, 2, 4, 6, 8
+  console.log(i);  // 0, 2, 4, 6, 8
 }
 
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-generator-function-definitions)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions)
 
 **Discussion Notes / Link to Thread:**
 
@@ -762,19 +769,29 @@
 
 ## `Map`
 
+A simple key/value map in which any value (both objects and primitive values)
+may be used as either a key or a value.
+
 **Usage Example:**
 
-``` js
-var key = {};
+```js
 var map = new Map();
+map.size === 0;  // true
+map.get('foo');  // undefined
 
+var key = {};
 map.set(key, 123);
+map.size === 1;  // true
+map.has(key);  // true
+map.get(key);  // 123
 
-map.has(key) && map.get(key) === 123; // true
+map.delete(key);
+map.has(key);  // false
+map.size === 0;  // true
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-map-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-map-objects)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
 
 **Discussion Notes / Link to Thread:**
 
@@ -782,20 +799,24 @@
 
 ## `Set`
 
+An object that lets you store unique values of any type, whether primitive
+values or object references.
+
 **Usage Example:**
 
-``` js
-var obj = {};
+```js
 var set = new Set();
 
 set.add(123);
-set.add(123);
+set.size();  // 1
+set.has(123);  // true
 
-set.has(123); // true
+set.add(123);
+set.size();  // 1
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-set-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-set-objects)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
 
 **Discussion Notes / Link to Thread:**
 
@@ -808,17 +829,16 @@
 
 **Usage Example:**
 
-``` js
+```js
 var key = {};
 var weakmap = new WeakMap();
 
 weakmap.set(key, 123);
 
-weakmap.has(key) && weakmap.get(key) === 123; // true
+weakmap.has(key) && weakmap.get(key) === 123;  // true
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-weakmap-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-weakmap-objects)
 
 **Discussion Notes / Link to Thread:**
 
@@ -831,18 +851,17 @@
 
 **Usage Example:**
 
-``` js
+```js
 var obj1 = {};
 var weakset = new WeakSet();
 
 weakset.add(obj1);
 weakset.add(obj1);
 
-weakset.has(obj1); // true
+weakset.has(obj1);  // true
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-weakset-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-weakset-objects)
 
 **Discussion Notes / Link to Thread:**
 
@@ -854,15 +873,14 @@
 
 **Usage Example:**
 
-``` js
+```js
 new Int8Array();
 new UInt8Array();
-new UInt8ClampedArray()
-// ...You get the idea. Click on the Documentation link below to see all.
+new UInt8ClampedArray();
+// ... You get the idea. Click on the Documentation link below to see all.
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-typedarray-objects)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects)
 
 **Discussion Notes / Link to Thread:**
 
@@ -874,21 +892,27 @@
 
 **Usage Example:**
 
-``` js
-let target = {
-  foo: 'Welcome, foo'
-};
-let proxy = new Proxy(target, {
-  get (receiver, name) {
-    return name in receiver ? receiver[name] : `Hello, ${name}`;
-  }
+```js
+var keyTracker = new Proxy({}, {
+  keysCreated: 0,
+
+  get (receiver, key) {
+    if (key in receiver) {
+      console.log('key already exists');
+    } else {
+      ++this.keysCreated;
+      console.log(this.keysCreated + ' keys created!');
+      receiver[key] = true;
+    }
+  },
 });
-proxy.foo === 'Welcome, foo'; // true
-proxy.world === 'Hello, world'; // true
+
+keyTracker.key1;  // '1 keys created!'
+keyTracker.key1;  // 'key already exists'
+keyTracker.key2;  // '2 keys created!'
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-proxy-object-internal-methods-and-internal-slots)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots)
 
 **Discussion Notes / Link to Thread:**
 
@@ -900,15 +924,14 @@
 
 **Usage Example:**
 
-``` js
-let obj = { a: 1 };
-Object.defineProperty(obj, 'b', { value: 2 });
+```js
+let obj = {a: 1};
+Object.defineProperty(obj, 'b', {value: 2});
 obj[Symbol('c')] = 3;
-Reflect.ownKeys(obj); // ['a', 'b', Symbol(c)]
+Reflect.ownKeys(obj);  // ['a', 'b', Symbol(c)]
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-reflection)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-reflection)
 
 **Discussion Notes / Link to Thread:**
 
@@ -920,12 +943,11 @@
 
 **Usage Example:**
 
-``` js
+```js
 // See Doc
 ```
 
-**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0
-/#sec-math)
+**Documentation:** [link](http://www.ecma-international.org/ecma-262/6.0/#sec-math)
 
 **Discussion Notes / Link to Thread:**
 
diff --git a/docs/google_play_services.md b/docs/google_play_services.md
new file mode 100644
index 0000000..b526cb9
--- /dev/null
+++ b/docs/google_play_services.md
@@ -0,0 +1,51 @@
+# Google Play services in Chrome for Android
+
+[TOC]
+
+Google employee? See [go/chrome-gms](https://goto.google.com/chrome-gms) for
+more info.
+
+## General setup
+
+The Google Play services are a combination of [services][play_store] exposed on
+Android devices and [libraries][dev_doc] to interact with them. Chrome relies
+on them for critical features like Sign in, Feedback or Cast.
+
+The standard way of adding the Google Play services as a dependency to a project
+is to import it through the Android SDK manager as a Maven repository. That
+repository contains multiple versions of the library split into separate APIs
+(for example Cast, GCM, Android Pay, etc). To avoid downloading a lot of data we
+don't need to build Chrome, android checkouts of Chromium download an archive
+containing only the APIs we currently need in Chrome, and in a single version.
+
+The up to date list of clients and version used can be seen in
+[//build/android/play_services/config.json][config_json_rel_path].
+
+**Note**: If you are working on a feature that requires different or more recent
+APIs, you will need to locally download the Google Play services SDK repository.
+
+The simplest way to download the latest SDK is to run:
+
+```
+$CHROMIUM_SRC/build/android/play_services/update.py sdk
+```
+
+Check out the help of that script for more info.
+
+[play_store]: https://play.google.com/store/apps/details?id=com.google.android.gms
+[dev_doc]: https://developers.google.com/android/guides/overview
+[config_json_rel_path]: ../build/android/play_services/config.json
+
+## Adding a dependency on new APIs
+
+As explained above, the default checkout has access to only a specific set of
+APIs during builds. If your CL depends on some APIs that are not included in the
+build, you will need [file an issue][bug_link] to request an update of our
+dependencies.
+
+Not doing so could make the CL fail on the trybots and commit queue. Even if it
+passes, it might fail on the internal bots and result in the CL getting
+reverted, so please make sure the APIs are available to the bots before
+submitting.
+
+[bug_link]:https://bugs.chromium.org/p/chromium/issues/entry?labels=Restrict-View-Google,pri-1,Hotlist-GooglePlayServices&owner=dgn@chromium.org&os=Android
diff --git a/docs/linux_build_instructions.md b/docs/linux_build_instructions.md
index 53ac6c9b..0fe97e6 100644
--- a/docs/linux_build_instructions.md
+++ b/docs/linux_build_instructions.md
@@ -117,7 +117,7 @@
 * For more info on GN, run `gn help` on the command line or read the
   [quick start guide](../tools/gn/docs/quick_start.md).
 
-### Faster builds
+### <a id=faster-builds></a>Faster builds
 
 This section contains some things you can change to speed up your builds,
 sorted so that the things that make the biggest difference are first.
@@ -137,9 +137,6 @@
 line-by-line debugging. Setting `symbol_level=0` will include no debug
 symbols at all. Either will speed up the build compared to full symbols.
 
-See [faster builds on Linux](linux_faster_builds.md) for various tips and
-settings that may speed up your build.
-
 #### Disable debug symbols for Blink
 
 Due to its extensive use of templates, the Blink code produces about half
@@ -337,14 +334,34 @@
 
 ### Debian
 
-`build/install-build-deps.sh` doesn't currently run on Debian, but you can
-probably hack it to get it to work. You will probably need to update the
-following package names:
+Some tests require the `ttf-mscorefonts-installer` package from the `contrib`
+component. `contrib` packages may have dependencies on non-free software.
 
-*   `libexpat-dev` → `libexpat1-dev`
-*   `freetype-dev` → `libfreetype6-dev`
-*   `libbzip2-dev` → `libbz2-dev`
-*   `libcupsys2-dev` → `libcups2-dev`
+If you need to run tests requiring MS TTF fonts, you can edit your apt
+`sources.list` by adding `contrib` to the end of each line beginning with `deb`.
+You might end up with something like this:
+
+```
+deb http://ftp.us.debian.org/debian/ jessie main contrib
+deb-src http://ftp.us.debian.org/debian/ jessie main contrib
+
+deb http://security.debian.org/ jessie/updates main contrib
+deb-src http://security.debian.org/ jessie/updates main contrib
+
+# jessie-updates, previously known as 'volatile'
+deb http://ftp.us.debian.org/debian/ jessie-updates main contrib
+deb-src http://ftp.us.debian.org/debian/ jessie-updates main contrib
+```
+
+Next, run:
+
+``` shell
+$ sudo apt-get update
+$ sudo apt-get install ttf-mscorefonts-installer
+```
+
+If you already have the `contrib` component enabled, `install-build-deps.sh`
+will install `ttf-mscorefonts-installer` for you.
 
 ### Fedora
 
diff --git a/docs/linux_debugging.md b/docs/linux_debugging.md
index 0049fd9d..3f53fc3 100644
--- a/docs/linux_debugging.md
+++ b/docs/linux_debugging.md
@@ -364,7 +364,7 @@
 
 Alternatively, you can use testing/xvfb.py to set up your environment for you:
 
-    testing/xvfb.py out/Debug out/Debug/browser_tests \
+    testing/xvfb.py out/Debug/browser_tests \
         --gtest_filter="MyBrowserTest.MyActivateWindowTest"
 
 ### BROWSER_WRAPPER
diff --git a/extensions/browser/api/app_window/app_window_api.cc b/extensions/browser/api/app_window/app_window_api.cc
index e92e7e7..9b6e70d4 100644
--- a/extensions/browser/api/app_window/app_window_api.cc
+++ b/extensions/browser/api/app_window/app_window_api.cc
@@ -404,7 +404,6 @@
   }
 
   SendResponse(true);
-  app_window->WindowEventsReady();
 
   return true;
 }
diff --git a/extensions/browser/api/cast_channel/cast_channel_api.cc b/extensions/browser/api/cast_channel/cast_channel_api.cc
index cc16c39b..dc4da0f0 100644
--- a/extensions/browser/api/cast_channel/cast_channel_api.cc
+++ b/extensions/browser/api/cast_channel/cast_channel_api.cc
@@ -379,8 +379,8 @@
     return false;
   }
   switch (params_->message.data->GetType()) {
-    case base::Value::TYPE_STRING:
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::STRING:
+    case base::Value::Type::BINARY:
       break;
     default:
       SetError("Invalid type of message_info.data");
diff --git a/extensions/browser/api/cast_channel/cast_message_util.cc b/extensions/browser/api/cast_channel/cast_message_util.cc
index 84af993..c5166eb 100644
--- a/extensions/browser/api/cast_channel/cast_message_util.cc
+++ b/extensions/browser/api/cast_channel/cast_message_util.cc
@@ -40,14 +40,14 @@
   base::BinaryValue* real_value;
   switch (message.data->GetType()) {
     // JS string
-    case base::Value::TYPE_STRING:
+    case base::Value::Type::STRING:
       if (message.data->GetAsString(&data)) {
         message_proto->set_payload_type(CastMessage_PayloadType_STRING);
         message_proto->set_payload_utf8(data);
       }
       break;
     // JS ArrayBuffer
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BINARY:
       real_value = static_cast<base::BinaryValue*>(message.data.get());
       if (real_value->GetBuffer()) {
         message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
diff --git a/extensions/browser/api/declarative/declarative_api.cc b/extensions/browser/api/declarative/declarative_api.cc
index a0354663..6fdeacc 100644
--- a/extensions/browser/api/declarative/declarative_api.cc
+++ b/extensions/browser/api/declarative/declarative_api.cc
@@ -56,15 +56,15 @@
   size_t index = 0;
   for (base::ListValue::iterator iter = args->begin(); iter != args->end();
        ++iter, ++index) {
-    if ((*iter)->IsType(base::Value::TYPE_BINARY)) {
+    if ((*iter)->IsType(base::Value::Type::BINARY)) {
       base::BinaryValue* binary = NULL;
       if (args->GetBinary(index, &binary))
         args->Set(index, ConvertBinaryToBase64(binary).release());
-    } else if ((*iter)->IsType(base::Value::TYPE_LIST)) {
+    } else if ((*iter)->IsType(base::Value::Type::LIST)) {
       base::ListValue* list;
       (*iter)->GetAsList(&list);
       ConvertBinaryListElementsToBase64(list);
-    } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
+    } else if ((*iter)->IsType(base::Value::Type::DICTIONARY)) {
       base::DictionaryValue* dict;
       (*iter)->GetAsDictionary(&dict);
       ConvertBinaryDictionaryValuesToBase64(dict);
@@ -78,15 +78,15 @@
 void ConvertBinaryDictionaryValuesToBase64(base::DictionaryValue* dict) {
   for (base::DictionaryValue::Iterator iter(*dict); !iter.IsAtEnd();
        iter.Advance()) {
-    if (iter.value().IsType(base::Value::TYPE_BINARY)) {
+    if (iter.value().IsType(base::Value::Type::BINARY)) {
       base::BinaryValue* binary = NULL;
       if (dict->GetBinary(iter.key(), &binary))
         dict->Set(iter.key(), ConvertBinaryToBase64(binary).release());
-    } else if (iter.value().IsType(base::Value::TYPE_LIST)) {
+    } else if (iter.value().IsType(base::Value::Type::LIST)) {
       const base::ListValue* list;
       iter.value().GetAsList(&list);
       ConvertBinaryListElementsToBase64(const_cast<base::ListValue*>(list));
-    } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    } else if (iter.value().IsType(base::Value::Type::DICTIONARY)) {
       const base::DictionaryValue* dict;
       iter.value().GetAsDictionary(&dict);
       ConvertBinaryDictionaryValuesToBase64(
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
index f3aa8d35..72fc2f7 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.cc
@@ -507,7 +507,7 @@
     std::vector<std::unique_ptr<const StringMatchTest>>* tests =
         is_name ? &name_match : &value_match;
     switch (content->GetType()) {
-      case base::Value::TYPE_LIST: {
+      case base::Value::Type::LIST: {
         const base::ListValue* list = NULL;
         CHECK(content->GetAsList(&list));
         for (const auto& it : *list) {
@@ -515,7 +515,7 @@
         }
         break;
       }
-      case base::Value::TYPE_STRING: {
+      case base::Value::Type::STRING: {
         tests->push_back(
             StringMatchTest::Create(*content, match_type, !is_name));
         break;
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
index 87d5f303..732d1d4 100644
--- a/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
+++ b/extensions/browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
@@ -385,7 +385,7 @@
       if (!dictionary->GetWithoutPathExpansion(*name, &entry))
         return std::unique_ptr<base::DictionaryValue>();
       switch (entry->GetType()) {
-        case base::Value::TYPE_STRING:
+        case base::Value::Type::STRING:
           // Replace the present string with a list.
           list = new base::ListValue;
           // Ignoring return value, we already verified the entry is there.
@@ -394,7 +394,7 @@
           list->AppendString(*value);
           dictionary->SetWithoutPathExpansion(*name, base::WrapUnique(list));
           break;
-        case base::Value::TYPE_LIST:  // Just append to the list.
+        case base::Value::Type::LIST:  // Just append to the list.
           CHECK(entry->GetAsList(&list));
           list->AppendString(*value);
           break;
diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc
index fb4c4198..0c9d325 100644
--- a/extensions/browser/api/management/management_api.cc
+++ b/extensions/browser/api/management/management_api.cc
@@ -346,7 +346,7 @@
 
 void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess(
     std::unique_ptr<base::Value> value) {
-  if (!value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value->IsType(base::Value::Type::DICTIONARY)) {
     OnParseFailure(keys::kManifestParseError);
     return;
   }
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
index 946473a..c83490a0 100644
--- a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc
@@ -65,7 +65,7 @@
       api_test_utils::RunFunctionAndReturnSingleResult(
           socket_create_function.get(), "[]", browser_context()));
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, result->GetType());
   std::unique_ptr<base::DictionaryValue> value =
       base::DictionaryValue::From(std::move(result));
   int socketId = -1;
diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_apitest.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_apitest.cc
index e076db9..03a3f0a 100644
--- a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_apitest.cc
+++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_apitest.cc
@@ -63,7 +63,7 @@
   std::unique_ptr<base::Value> result(
       api_test_utils::RunFunctionAndReturnSingleResult(
           socket_create_function.get(), "[]", browser_context()));
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, result->GetType());
   std::unique_ptr<base::DictionaryValue> value =
       base::DictionaryValue::From(std::move(result));
   int socketId = -1;
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
index 6fd79ef..1bb7d08 100644
--- a/extensions/browser/api/storage/storage_api.cc
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -149,23 +149,23 @@
     return BadMessage();
 
   switch (input->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return UseReadResult(storage->Get());
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string as_string;
       input->GetAsString(&as_string);
       return UseReadResult(storage->Get(as_string));
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       std::vector<std::string> as_string_list;
       AddAllStringValues(*static_cast<base::ListValue*>(input),
                          &as_string_list);
       return UseReadResult(storage->Get(as_string_list));
     }
 
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       base::DictionaryValue* as_dict =
           static_cast<base::DictionaryValue*>(input);
       ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict));
@@ -193,18 +193,18 @@
   size_t bytes_in_use = 0;
 
   switch (input->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       bytes_in_use = storage->GetBytesInUse();
       break;
 
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string as_string;
       input->GetAsString(&as_string);
       bytes_in_use = storage->GetBytesInUse(as_string);
       break;
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       std::vector<std::string> as_string_list;
       AddAllStringValues(*static_cast<base::ListValue*>(input),
                          &as_string_list);
@@ -240,13 +240,13 @@
     return BadMessage();
 
   switch (input->GetType()) {
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string as_string;
       input->GetAsString(&as_string);
       return UseWriteResult(storage->Remove(as_string));
     }
 
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       std::vector<std::string> as_string_list;
       AddAllStringValues(*static_cast<base::ListValue*>(input),
                          &as_string_list);
diff --git a/extensions/browser/api/system_network/system_network_apitest.cc b/extensions/browser/api/system_network/system_network_apitest.cc
index b496575..38022b8 100644
--- a/extensions/browser/api/system_network/system_network_apitest.cc
+++ b/extensions/browser/api/system_network/system_network_apitest.cc
@@ -36,7 +36,7 @@
 
   std::unique_ptr<base::Value> result(RunFunctionAndReturnSingleResult(
       socket_function.get(), "[]", browser_context()));
-  ASSERT_EQ(base::Value::TYPE_LIST, result->GetType());
+  ASSERT_EQ(base::Value::Type::LIST, result->GetType());
 
   // All we can confirm is that we have at least one address, but not what it
   // is.
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index cee54392..682ac11 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -29,7 +28,6 @@
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_side_navigation_policy.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/common/media_stream_request.h"
 #include "extensions/browser/app_window/app_delegate.h"
 #include "extensions/browser/app_window/app_web_contents_helper.h"
@@ -50,7 +48,6 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "extensions/common/permissions/permissions_data.h"
-#include "extensions/common/switches.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -247,12 +244,8 @@
       window_type_(WINDOW_TYPE_DEFAULT),
       app_delegate_(app_delegate),
       fullscreen_types_(FULLSCREEN_TYPE_NONE),
-      show_on_first_paint_(false),
-      first_paint_complete_(false),
       has_been_shown_(false),
-      can_send_events_(false),
       is_hidden_(false),
-      delayed_show_type_(SHOW_ACTIVE),
       cached_always_on_top_(false),
       requested_alpha_enabled_(false),
       is_ime_window_(false),
@@ -351,17 +344,6 @@
                  base::Unretained(native_app_window_.get())));
 
   app_window_contents_->LoadContents(new_params.creator_process_id);
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          extensions::switches::kEnableAppsShowOnFirstPaint)) {
-    // We want to show the window only when the content has been painted. For
-    // that to happen, we need to define a size for the content, otherwise the
-    // layout will happen in a 0x0 area.
-    gfx::Insets frame_insets = native_app_window_->GetFrameInsets();
-    gfx::Rect initial_bounds = new_params.GetInitialWindowBounds(frame_insets);
-    initial_bounds.Inset(frame_insets);
-    app_delegate_->ResizeWebContents(web_contents(), initial_bounds.size());
-  }
 }
 
 AppWindow::~AppWindow() {
@@ -469,15 +451,6 @@
   app_delegate_->RenderViewCreated(render_view_host);
 }
 
-void AppWindow::DidFirstVisuallyNonEmptyPaint() {
-  first_paint_complete_ = true;
-  if (show_on_first_paint_) {
-    DCHECK(delayed_show_type_ == SHOW_ACTIVE ||
-           delayed_show_type_ == SHOW_INACTIVE);
-    Show(delayed_show_type_);
-  }
-}
-
 void AppWindow::SetOnFirstCommitCallback(const base::Closure& callback) {
   DCHECK(on_first_commit_callback_.is_null());
   on_first_commit_callback_ = callback;
@@ -492,7 +465,6 @@
   // would happen before the navigation starts, but PlzNavigate must wait until
   // this point in time in the navigation.
 
-  WindowEventsReady();
   if (on_first_commit_callback_.is_null())
     return;
   // It is important that the callback executes after the calls to
@@ -725,16 +697,6 @@
   bool was_hidden = is_hidden_ || !has_been_shown_;
   is_hidden_ = false;
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableAppsShowOnFirstPaint)) {
-    show_on_first_paint_ = true;
-
-    if (!first_paint_complete_) {
-      delayed_show_type_ = show_type;
-      return;
-    }
-  }
-
   switch (show_type) {
     case SHOW_ACTIVE:
       GetBaseWindow()->Show();
@@ -744,18 +706,11 @@
       break;
   }
   AppWindowRegistry::Get(browser_context_)->AppWindowShown(this, was_hidden);
-
   has_been_shown_ = true;
-  SendOnWindowShownIfShown();
 }
 
 void AppWindow::Hide() {
-  // This is there to prevent race conditions with Hide() being called before
-  // there was a non-empty paint. It should have no effect in a non-racy
-  // scenario where the application is hiding then showing a window: the second
-  // show will not be delayed.
   is_hidden_ = true;
-  show_on_first_paint_ = false;
   GetBaseWindow()->Hide();
   AppWindowRegistry::Get(browser_context_)->AppWindowHidden(this);
   app_delegate_->OnHide();
@@ -783,11 +738,6 @@
     UpdateNativeAlwaysOnTop();
 }
 
-void AppWindow::WindowEventsReady() {
-  can_send_events_ = true;
-  SendOnWindowShownIfShown();
-}
-
 void AppWindow::NotifyRenderViewReady() {
   if (app_window_contents_)
     app_window_contents_->OnWindowReady();
@@ -939,16 +889,6 @@
   }
 }
 
-void AppWindow::SendOnWindowShownIfShown() {
-  if (!can_send_events_ || !has_been_shown_)
-    return;
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kTestType)) {
-    app_window_contents_->DispatchWindowShownForTests();
-  }
-}
-
 void AppWindow::CloseContents(WebContents* contents) {
   native_app_window_->Close();
 }
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index fdf51c2..66fdd4c 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -71,9 +71,6 @@
   // Called when the native window closes.
   virtual void NativeWindowClosed() = 0;
 
-  // Called in tests when the window is shown
-  virtual void DispatchWindowShownForTests() const = 0;
-
   // Called when the renderer notifies the browser that the window is ready.
   virtual void OnWindowReady() = 0;
 
@@ -359,10 +356,6 @@
   // the renderer.
   void GetSerializedState(base::DictionaryValue* properties) const;
 
-  // Called by the window API when events can be sent to the window for this
-  // app.
-  void WindowEventsReady();
-
   // Notifies the window's contents that the render view is ready and it can
   // unblock resource requests.
   void NotifyRenderViewReady();
@@ -446,7 +439,6 @@
 
   // content::WebContentsObserver implementation.
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
-  void DidFirstVisuallyNonEmptyPaint() override;
 
   // ExtensionFunctionDispatcher::Delegate implementation.
   WindowController* GetExtensionWindowController() const override;
@@ -545,19 +537,9 @@
   // Bit field of FullscreenType.
   int fullscreen_types_;
 
-  // Show has been called, so the window should be shown once the first visually
-  // non-empty paint occurs.
-  bool show_on_first_paint_;
-
-  // The first visually non-empty paint has completed.
-  bool first_paint_complete_;
-
   // Whether the window has been shown or not.
   bool has_been_shown_;
 
-  // Whether events can be sent to the window.
-  bool can_send_events_;
-
   // Whether the window is hidden or not. Hidden in this context means actively
   // by the chrome.app.window API, not in an operating system context. For
   // example windows which are minimized are not hidden, and windows which are
@@ -566,9 +548,6 @@
   // hidden, are considered hidden.
   bool is_hidden_;
 
-  // Whether the delayed Show() call was for an active or inactive window.
-  ShowType delayed_show_type_;
-
   // Cache the desired value of the always-on-top property. When windows enter
   // fullscreen or overlap the Windows taskbar, this property will be
   // automatically and silently switched off for security reasons. It is
diff --git a/extensions/browser/app_window/app_window_contents.cc b/extensions/browser/app_window/app_window_contents.cc
index e40be76..b09d7c9 100644
--- a/extensions/browser/app_window/app_window_contents.cc
+++ b/extensions/browser/app_window/app_window_contents.cc
@@ -82,14 +82,6 @@
   rvh->Send(new ExtensionMsg_AppWindowClosed(rvh->GetRoutingID()));
 }
 
-void AppWindowContentsImpl::DispatchWindowShownForTests() const {
-  base::ListValue args;
-  content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
-  rfh->Send(new ExtensionMsg_MessageInvoke(rfh->GetRoutingID(),
-                                           host_->extension_id(), "app.window",
-                                           "appWindowShownForTests", args));
-}
-
 void AppWindowContentsImpl::OnWindowReady() {
   is_window_ready_ = true;
   if (is_blocking_requests_) {
diff --git a/extensions/browser/app_window/app_window_contents.h b/extensions/browser/app_window/app_window_contents.h
index 85b56c7..4afa13d 100644
--- a/extensions/browser/app_window/app_window_contents.h
+++ b/extensions/browser/app_window/app_window_contents.h
@@ -39,7 +39,6 @@
   void LoadContents(int32_t creator_process_id) override;
   void NativeWindowChanged(NativeAppWindow* native_app_window) override;
   void NativeWindowClosed() override;
-  void DispatchWindowShownForTests() const override;
   void OnWindowReady() override;
   content::WebContents* GetWebContents() const override;
   WindowController* GetWindowController() const override;
diff --git a/extensions/browser/app_window/test_app_window_contents.cc b/extensions/browser/app_window/test_app_window_contents.cc
index 5613a209..a0c2d67 100644
--- a/extensions/browser/app_window/test_app_window_contents.cc
+++ b/extensions/browser/app_window/test_app_window_contents.cc
@@ -28,9 +28,6 @@
 void TestAppWindowContents::NativeWindowClosed() {
 }
 
-void TestAppWindowContents::DispatchWindowShownForTests() const {
-}
-
 void TestAppWindowContents::OnWindowReady() {}
 
 content::WebContents* TestAppWindowContents::GetWebContents() const {
diff --git a/extensions/browser/app_window/test_app_window_contents.h b/extensions/browser/app_window/test_app_window_contents.h
index 8c082cb..2bebf674 100644
--- a/extensions/browser/app_window/test_app_window_contents.h
+++ b/extensions/browser/app_window/test_app_window_contents.h
@@ -30,7 +30,6 @@
   void LoadContents(int32_t creator_process_id) override;
   void NativeWindowChanged(NativeAppWindow* native_app_window) override;
   void NativeWindowClosed() override;
-  void DispatchWindowShownForTests() const override;
   void OnWindowReady() override;
   content::WebContents* GetWebContents() const override;
   WindowController* GetWindowController() const override;
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index 4e56265a..c9a76ac5a 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -422,7 +422,7 @@
 
 bool ExtensionFunction::HasOptionalArgument(size_t index) {
   base::Value* value;
-  return args_->Get(index, &value) && !value->IsType(base::Value::TYPE_NULL);
+  return args_->Get(index, &value) && !value->IsType(base::Value::Type::NONE);
 }
 
 void ExtensionFunction::SendResponseImpl(bool success) {
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 657da21..a3ade02 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -332,9 +332,9 @@
 
 // Explicit instantiations for Dictionary and List value types.
 template class ExtensionPrefs::ScopedUpdate<base::DictionaryValue,
-                                            base::Value::TYPE_DICTIONARY>;
+                                            base::Value::Type::DICTIONARY>;
 template class ExtensionPrefs::ScopedUpdate<base::ListValue,
-                                            base::Value::TYPE_LIST>;
+                                            base::Value::Type::LIST>;
 
 //
 // ExtensionPrefs
@@ -818,7 +818,7 @@
 
   for (base::DictionaryValue::Iterator it(*extensions);
        !it.IsAtEnd(); it.Advance()) {
-    if (!it.value().IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!it.value().IsType(base::Value::Type::DICTIONARY)) {
       NOTREACHED() << "Invalid pref for extension " << it.key();
       continue;
     }
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index af4f412d..226db3e 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -117,9 +117,9 @@
 
     DISALLOW_COPY_AND_ASSIGN(ScopedUpdate);
   };
-  typedef ScopedUpdate<base::DictionaryValue, base::Value::TYPE_DICTIONARY>
+  typedef ScopedUpdate<base::DictionaryValue, base::Value::Type::DICTIONARY>
       ScopedDictionaryUpdate;
-  typedef ScopedUpdate<base::ListValue, base::Value::TYPE_LIST>
+  typedef ScopedUpdate<base::ListValue, base::Value::Type::LIST>
       ScopedListUpdate;
 
   // Creates an ExtensionPrefs object.
diff --git a/extensions/browser/extension_request_limiting_throttle.cc b/extensions/browser/extension_request_limiting_throttle.cc
index 05d04101..2e213485 100644
--- a/extensions/browser/extension_request_limiting_throttle.cc
+++ b/extensions/browser/extension_request_limiting_throttle.cc
@@ -5,7 +5,6 @@
 #include "extensions/browser/extension_request_limiting_throttle.h"
 
 #include "base/logging.h"
-#include "content/public/browser/resource_controller.h"
 #include "extensions/browser/extension_throttle_entry.h"
 #include "extensions/browser/extension_throttle_manager.h"
 #include "net/base/net_errors.h"
@@ -27,7 +26,7 @@
 void ExtensionRequestLimitingThrottle::WillStartRequest(bool* defer) {
   throttling_entry_ = manager_->RegisterRequestUrl(request_->url());
   if (throttling_entry_->ShouldRejectRequest(*request_))
-    controller()->CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
+    CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
 }
 
 void ExtensionRequestLimitingThrottle::WillRedirectRequest(
@@ -40,7 +39,7 @@
 
   throttling_entry_ = manager_->RegisterRequestUrl(redirect_info.new_url);
   if (throttling_entry_->ShouldRejectRequest(*request_))
-    controller()->CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
+    CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
 }
 
 void ExtensionRequestLimitingThrottle::WillProcessResponse(bool* defer) {
diff --git a/extensions/browser/extension_zoom_request_client.cc b/extensions/browser/extension_zoom_request_client.cc
index c462a2a..aebc33d 100644
--- a/extensions/browser/extension_zoom_request_client.cc
+++ b/extensions/browser/extension_zoom_request_client.cc
@@ -17,11 +17,11 @@
 
 bool ExtensionZoomRequestClient::ShouldSuppressBubble() const {
   const Feature* feature =
-      FeatureProvider::GetBehaviorFeature(BehaviorFeature::kZoomWithoutBubble);
+      FeatureProvider::GetBehaviorFeature(behavior_feature::kZoomWithoutBubble);
   return feature && feature->IsAvailableToExtension(extension()).is_available();
 }
 
 ExtensionZoomRequestClient::~ExtensionZoomRequestClient() {
 }
 
-}  // namespace Extensions
+}  // namespace extensions
diff --git a/extensions/browser/verified_contents.cc b/extensions/browser/verified_contents.cc
index b8acb9c..2940484 100644
--- a/extensions/browser/verified_contents.cc
+++ b/extensions/browser/verified_contents.cc
@@ -97,7 +97,7 @@
     return false;
 
   std::unique_ptr<base::Value> value(base::JSONReader::Read(payload));
-  if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
+  if (!value.get() || !value->IsType(Value::Type::DICTIONARY))
     return false;
   DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get());
 
@@ -236,7 +236,7 @@
   if (!base::ReadFileToString(path, &contents))
     return false;
   std::unique_ptr<base::Value> value(base::JSONReader::Read(contents));
-  if (!value.get() || !value->IsType(Value::TYPE_LIST))
+  if (!value.get() || !value->IsType(Value::Type::LIST))
     return false;
   ListValue* top_list = static_cast<ListValue*>(value.get());
 
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index b63e6b0..ee47cfb 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -43,6 +43,8 @@
       "api/declarative/declarative_manifest_handler.cc",
       "api/declarative/declarative_manifest_handler.h",
       "api/messaging/message.h",
+      "api/messaging/port_id.cc",
+      "api/messaging/port_id.h",
       "api/printer_provider/usb_printer_manifest_data.cc",
       "api/printer_provider/usb_printer_manifest_data.h",
       "api/printer_provider/usb_printer_manifest_handler.cc",
diff --git a/extensions/common/api/app_current_window_internal.idl b/extensions/common/api/app_current_window_internal.idl
index e6dc17d..344ef1c 100644
--- a/extensions/common/api/app_current_window_internal.idl
+++ b/extensions/common/api/app_current_window_internal.idl
@@ -61,7 +61,5 @@
     static void onMaximized();
     static void onRestored();
     static void onAlphaEnabledChanged();
-    // Only sent in tests.
-    static void onWindowShownForTests();
   };
 };
diff --git a/extensions/common/api/app_window.idl b/extensions/common/api/app_window.idl
index 9e3ba8c2..999b999 100644
--- a/extensions/common/api/app_window.idl
+++ b/extensions/common/api/app_window.idl
@@ -385,7 +385,6 @@
     [nodoc] boolean hasFrameColor;
     [nodoc] long activeFrameColor;
     [nodoc] long inactiveFrameColor;
-    [nodoc] boolean? firstShowHasHappened;
 
     // Set whether the window should stay above most other windows. Requires the
     // <code>alwaysOnTopWindows</code> permission.
@@ -488,8 +487,5 @@
 
     // Fired when the window's ability to use alpha transparency changes.
     [nocompile, nodoc] static void onAlphaEnabledChanged();
-
-    // Event for testing. Lets tests wait until a window has been shown.
-    [nocompile, nodoc] static void onWindowFirstShown();
   };
 };
diff --git a/extensions/common/api/messaging/port_id.cc b/extensions/common/api/messaging/port_id.cc
new file mode 100644
index 0000000..105ff9f
--- /dev/null
+++ b/extensions/common/api/messaging/port_id.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/api/messaging/port_id.h"
+
+#include <tuple>
+
+namespace extensions {
+
+PortId::PortId() {}
+PortId::PortId(const base::UnguessableToken& context_id,
+               int port_number,
+               bool is_opener)
+    : context_id(context_id), port_number(port_number), is_opener(is_opener) {}
+PortId::~PortId() {}
+PortId::PortId(PortId&& other) = default;
+PortId::PortId(const PortId& other) = default;
+PortId& PortId::operator=(const PortId& other) = default;
+
+bool PortId::operator==(const PortId& other) const {
+  return context_id == other.context_id && port_number == other.port_number &&
+         is_opener == other.is_opener;
+}
+
+bool PortId::operator<(const PortId& other) const {
+  return std::tie(context_id, port_number, is_opener) <
+         std::tie(other.context_id, other.port_number, other.is_opener);
+}
+
+}  // namespace extensions
diff --git a/extensions/common/api/messaging/port_id.h b/extensions/common/api/messaging/port_id.h
new file mode 100644
index 0000000..e44aa1f
--- /dev/null
+++ b/extensions/common/api/messaging/port_id.h
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_API_MESSAGING_PORT_ID_H_
+#define EXTENSIONS_COMMON_API_MESSAGING_PORT_ID_H_
+
+#include <utility>
+
+#include "base/unguessable_token.h"
+
+namespace extensions {
+
+// A unique identifier for the Channel a port is on; i.e., the two-way
+// communication between opener and receiver(s). We can use the pair of
+// <context_id, port_number> which is the same between opener and receivers
+// but unique amongst all other connections.
+using ChannelId = std::pair<base::UnguessableToken, int>;
+
+// A unique identifier for an extension port. The id is composed of three parts:
+// - context_id: An UnguessableToken that uniquely identifies the creation
+//               context of the port.
+// - port_number: A simple identifer that uniquely identifies the port *within
+//                the creation context*. That is, each creation context may
+//                have a port with number '1', but there should only be a single
+//                port with the number '1' in each.
+// - is_opener: Whether or not this port id is for the opener port (false
+//              indicating it is the receiver port).
+// A few more notes:
+// - There should only be a single existent opener port. That is, in all the
+//   contexts, there should only be one with a given context_id, port_number,
+//   and is_opener set to true. However, each port can have multiple receivers,
+//   thus there may be multiple ports with a given context_id, port_number, and
+//   is_opener set to false.
+// - The context_id and port_number refer to the values at *creation*, and are
+//   conceptually immutable. Receiver ports will always have a context id other
+//   than the one they are hosted in (since we don't dispatch messages to the
+//   same context). Only in the case of opener ports do these identify the
+//   current context.
+// - Context id and port number are set in the renderer process. Theoretically,
+//   this means that multiple contexts could have the same id. However, GUIDs
+//   are sufficiently random as to be globally unique in practice (the chance
+//   of a duplicate is about the same as the sun exploding right now).
+struct PortId {
+  // See class comments for the description of these fields.
+  base::UnguessableToken context_id;
+  int port_number = 0;
+  bool is_opener = false;
+
+  PortId();
+  PortId(const base::UnguessableToken& context_id,
+         int port_number,
+         bool is_opener);
+  ~PortId();
+  PortId(PortId&& other);
+  PortId(const PortId& other);
+  PortId& operator=(const PortId& other);
+
+  ChannelId GetChannelId() const {
+    return std::make_pair(context_id, port_number);
+  }
+
+  // Returns the identifier for the opposite port(s). That is, a call on a
+  // receiver port returns the ID of the opener, and a call on the opener port
+  // returns the ID of all receivers.
+  PortId GetOppositePortId() const {
+    return PortId(context_id, port_number, !is_opener);
+  }
+
+  bool operator==(const PortId& other) const;
+  bool operator<(const PortId& other) const;
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_COMMON_API_MESSAGING_PORT_ID_H_
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc
index cc3c5f7..a32b095 100644
--- a/extensions/common/extension_api.cc
+++ b/extensions/common/extension_api.cc
@@ -47,14 +47,13 @@
 
   // Tracking down http://crbug.com/121424
   char buf[128];
-  base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'",
-      name.c_str(),
-      result.get() ? result->GetType() : -1,
-      error_message.c_str());
+  base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'", name.c_str(),
+                 result.get() ? static_cast<int>(result->GetType()) : -1,
+                 error_message.c_str());
 
   CHECK(result.get()) << error_message << " for schema " << schema;
-  CHECK(result->IsType(base::Value::TYPE_DICTIONARY)) << " for schema "
-                                                      << schema;
+  CHECK(result->IsType(base::Value::Type::DICTIONARY)) << " for schema "
+                                                       << schema;
   return base::DictionaryValue::From(std::move(result));
 }
 
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index e70a2a6a..f7952e8 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -16,6 +16,7 @@
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/socket_permission_request.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/draggable_region.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extensions_client.h"
@@ -260,6 +261,12 @@
   IPC_STRUCT_TRAITS_MEMBER(user_gesture)
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(extensions::PortId)
+  IPC_STRUCT_TRAITS_MEMBER(context_id)
+  IPC_STRUCT_TRAITS_MEMBER(port_number)
+  IPC_STRUCT_TRAITS_MEMBER(is_opener)
+IPC_STRUCT_TRAITS_END()
+
 // Singly-included section for custom IPC traits.
 #ifndef EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
 #define EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
@@ -589,11 +596,11 @@
 // Check whether the Port for extension messaging exists in the frame. If the
 // port ID is unknown, the frame replies with ExtensionHostMsg_CloseMessagePort.
 IPC_MESSAGE_ROUTED1(ExtensionMsg_ValidateMessagePort,
-                    int /* port_id */)
+                    extensions::PortId /* port_id */)
 
 // Dispatch the Port.onConnect event for message channels.
 IPC_MESSAGE_ROUTED5(ExtensionMsg_DispatchOnConnect,
-                    int /* target_port_id */,
+                    extensions::PortId /* target_port_id */,
                     std::string /* channel_name */,
                     ExtensionMsg_TabConnectionInfo /* source */,
                     ExtensionMsg_ExternalConnectionInfo,
@@ -601,12 +608,12 @@
 
 // Deliver a message sent with ExtensionHostMsg_PostMessage.
 IPC_MESSAGE_ROUTED2(ExtensionMsg_DeliverMessage,
-                    int /* target_port_id */,
+                    extensions::PortId /* target_port_id */,
                     extensions::Message)
 
 // Dispatch the Port.onDisconnect event for message channels.
 IPC_MESSAGE_ROUTED2(ExtensionMsg_DispatchOnDisconnect,
-                    int /* port_id */,
+                    extensions::PortId /* port_id */,
                     std::string /* error_message */)
 
 // Informs the renderer what channel (dev, beta, stable, etc) and user session
@@ -702,27 +709,12 @@
                      ExtensionMsg_ExternalConnectionInfo,
                      std::string /* channel_name */,
                      bool /* include_tls_channel_id */,
-                     int /* request_id */)
-
-// Same as ExtensionHostMsg_OpenChannelToExtension, but assigns the port id
-// synchronously.
-IPC_SYNC_MESSAGE_CONTROL4_1(ExtensionHostMsg_OpenChannelToExtensionSync,
-                            int /* frame_routing_id */,
-                            ExtensionMsg_ExternalConnectionInfo,
-                            std::string /* channel_name */,
-                            bool /* include_tls_channel_id */,
-                            int /* port_id */)
-
-// The response to a request to open an extension message port, including the
-// global port id and the request id.
-IPC_MESSAGE_ROUTED2(ExtensionMsg_AssignPortId,
-                    int /*port_id */,
-                    int /* request_id */)
+                     extensions::PortId /* port_id */)
 
 IPC_MESSAGE_CONTROL3(ExtensionHostMsg_OpenChannelToNativeApp,
                      int /* frame_routing_id */,
                      std::string /* native_app_name */,
-                     int /* request_id */)
+                     extensions::PortId /* port_id */)
 
 // Get a port handle to the given tab.  The handle can be used for sending
 // messages to the extension.
@@ -731,25 +723,25 @@
                      ExtensionMsg_TabTargetConnectionInfo,
                      std::string /* extension_id */,
                      std::string /* channel_name */,
-                     int /* request_id */)
+                     extensions::PortId /* port_id */)
 
 // Sent in response to ExtensionMsg_DispatchOnConnect when the port is accepted.
 // The handle is the value returned by ExtensionHostMsg_OpenChannelTo*.
 IPC_MESSAGE_CONTROL2(ExtensionHostMsg_OpenMessagePort,
                      int /* frame_routing_id */,
-                     int /* port_id */)
+                     extensions::PortId /* port_id */)
 
 // Sent in response to ExtensionMsg_DispatchOnConnect and whenever the port is
 // closed. The handle is the value returned by ExtensionHostMsg_OpenChannelTo*.
 IPC_MESSAGE_CONTROL3(ExtensionHostMsg_CloseMessagePort,
                      int /* frame_routing_id */,
-                     int /* port_id */,
+                     extensions::PortId /* port_id */,
                      bool /* force_close */)
 
 // Send a message to an extension process.  The handle is the value returned
 // by ExtensionHostMsg_OpenChannelTo*.
 IPC_MESSAGE_ROUTED2(ExtensionHostMsg_PostMessage,
-                    int /* port_id */,
+                    extensions::PortId /* port_id */,
                     extensions::Message)
 
 // Used to get the extension message bundle.
diff --git a/extensions/common/features/api_feature.h b/extensions/common/features/api_feature.h
deleted file mode 100644
index bf494f2..0000000
--- a/extensions/common/features/api_feature.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_COMMON_FEATURES_API_FEATURE_H_
-#define EXTENSIONS_COMMON_FEATURES_API_FEATURE_H_
-
-#include "extensions/common/features/simple_feature.h"
-
-namespace extensions {
-
-// TODO(devlin): APIFeatures are just SimpleFeatures now. However, there's a
-// bunch of stuff (including the feature compiler) that relies on the presence
-// of an APIFeature class. For now, just typedef this, but eventually we should
-// remove it.
-using APIFeature = SimpleFeature;
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_COMMON_FEATURES_API_FEATURE_H_
diff --git a/extensions/common/features/behavior_feature.cc b/extensions/common/features/behavior_feature.cc
index 687ce7b..519b829 100644
--- a/extensions/common/features/behavior_feature.cc
+++ b/extensions/common/features/behavior_feature.cc
@@ -6,16 +6,19 @@
 
 namespace extensions {
 
-const char BehaviorFeature::kWhitelistedForIncognito[] =
-    "whitelisted_for_incognito";
+namespace behavior_feature {
 
-const char BehaviorFeature::kDoNotSync[] = "do_not_sync";
+const char kWhitelistedForIncognito[] = "whitelisted_for_incognito";
 
-const char BehaviorFeature::kZoomWithoutBubble[] = "zoom_without_bubble";
+const char kDoNotSync[] = "do_not_sync";
 
-const char BehaviorFeature::kAllowUsbDevicesPermissionInterfaceClass[] =
+const char kZoomWithoutBubble[] = "zoom_without_bubble";
+
+const char kAllowUsbDevicesPermissionInterfaceClass[] =
     "allow_usb_devices_permission_interface_class";
 
-const char BehaviorFeature::kSigninScreen[] = "signin_screen";
+const char kSigninScreen[] = "signin_screen";
+
+}  // namespace behavior_feature
 
 }  // namespace extensions
diff --git a/extensions/common/features/behavior_feature.h b/extensions/common/features/behavior_feature.h
index 2b38d43..190cf7d 100644
--- a/extensions/common/features/behavior_feature.h
+++ b/extensions/common/features/behavior_feature.h
@@ -5,26 +5,19 @@
 #ifndef EXTENSIONS_COMMON_FEATURES_BEHAVIOR_FEATURE_H_
 #define EXTENSIONS_COMMON_FEATURES_BEHAVIOR_FEATURE_H_
 
-#include <string>
-
-#include "extensions/common/features/simple_feature.h"
-
 namespace extensions {
 
-// Implementation of the features in _behavior_features.json.
-//
-// For now, this is just constants + a vacuous implementation of SimpleFeature,
-// for consistency with the other Feature types. One day we may add some
-// additional functionality. One day we may also generate the feature names.
-class BehaviorFeature : public SimpleFeature {
- public:
-  // Constants corresponding to keys in _behavior_features.json.
-  static const char kWhitelistedForIncognito[];
-  static const char kDoNotSync[];
-  static const char kZoomWithoutBubble[];
-  static const char kAllowUsbDevicesPermissionInterfaceClass[];
-  static const char kSigninScreen[];
-};
+// Contains constants corresponding to keys in _behavior_features.json.
+// One day we might want to auto generate these.
+namespace behavior_feature {
+
+extern const char kWhitelistedForIncognito[];
+extern const char kDoNotSync[];
+extern const char kZoomWithoutBubble[];
+extern const char kAllowUsbDevicesPermissionInterfaceClass[];
+extern const char kSigninScreen[];
+
+}  // namespace behavior_feature
 
 }  // namespace extensions
 
diff --git a/extensions/common/features/json_feature_provider_source.cc b/extensions/common/features/json_feature_provider_source.cc
index 94064a1..caf0914 100644
--- a/extensions/common/features/json_feature_provider_source.cc
+++ b/extensions/common/features/json_feature_provider_source.cc
@@ -31,7 +31,7 @@
 
   std::unique_ptr<base::DictionaryValue> value_as_dict;
   if (value) {
-    CHECK(value->IsType(base::Value::TYPE_DICTIONARY)) << name_;
+    CHECK(value->IsType(base::Value::Type::DICTIONARY)) << name_;
     value_as_dict = base::DictionaryValue::From(std::move(value));
   } else {
     // There was some error loading the features file.
diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc
index 5506d25..9117e7c 100644
--- a/extensions/common/features/simple_feature_unittest.cc
+++ b/extensions/common/features/simple_feature_unittest.cc
@@ -14,11 +14,9 @@
 #include "base/stl_util.h"
 #include "base/test/scoped_command_line.h"
 #include "base/values.h"
-#include "extensions/common/features/api_feature.h"
 #include "extensions/common/features/complex_feature.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_session_type.h"
-#include "extensions/common/features/permission_feature.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -45,11 +43,6 @@
   std::initializer_list<FeatureSessionType> feature_session_types;
 };
 
-template <class FeatureClass>
-SimpleFeature* CreateFeature() {
-  return new FeatureClass();
-}
-
 Feature::AvailabilityResult IsAvailableInChannel(Channel channel_for_feature,
                                                  Channel channel_for_testing) {
   ScopedCurrentChannel current_channel(channel_for_testing);
@@ -757,10 +750,10 @@
 TEST_F(SimpleFeatureTest, SimpleFeatureAvailability) {
   std::unique_ptr<ComplexFeature> complex_feature;
   {
-    std::unique_ptr<SimpleFeature> feature1(CreateFeature<SimpleFeature>());
+    std::unique_ptr<SimpleFeature> feature1(new SimpleFeature());
     feature1->channel_.reset(new Channel(Channel::BETA));
     feature1->extension_types_.push_back(Manifest::TYPE_EXTENSION);
-    std::unique_ptr<SimpleFeature> feature2(CreateFeature<SimpleFeature>());
+    std::unique_ptr<SimpleFeature> feature2(new SimpleFeature());
     feature2->channel_.reset(new Channel(Channel::BETA));
     feature2->extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
     std::vector<Feature*> list;
@@ -808,10 +801,10 @@
   std::unique_ptr<ComplexFeature> complex_feature;
   {
     // Rule: "extension", channel trunk.
-    std::unique_ptr<SimpleFeature> feature1(CreateFeature<SimpleFeature>());
+    std::unique_ptr<SimpleFeature> feature1(new SimpleFeature());
     feature1->channel_.reset(new Channel(Channel::UNKNOWN));
     feature1->extension_types_.push_back(Manifest::TYPE_EXTENSION);
-    std::unique_ptr<SimpleFeature> feature2(CreateFeature<SimpleFeature>());
+    std::unique_ptr<SimpleFeature> feature2(new SimpleFeature());
     // Rule: "legacy_packaged_app", channel stable.
     feature2->channel_.reset(new Channel(Channel::STABLE));
     feature2->extension_types_.push_back(Manifest::TYPE_LEGACY_PACKAGED_APP);
diff --git a/extensions/common/file_util.cc b/extensions/common/file_util.cc
index 91d49d03..4bc1b5d 100644
--- a/extensions/common/file_util.cc
+++ b/extensions/common/file_util.cc
@@ -261,7 +261,7 @@
     return NULL;
   }
 
-  if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!root->IsType(base::Value::Type::DICTIONARY)) {
     *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID);
     return NULL;
   }
diff --git a/extensions/common/file_util_unittest.cc b/extensions/common/file_util_unittest.cc
index 5ab3742..c3b588cf 100644
--- a/extensions/common/file_util_unittest.cc
+++ b/extensions/common/file_util_unittest.cc
@@ -52,7 +52,7 @@
   std::unique_ptr<base::Value> result = deserializer.Deserialize(NULL, error);
   if (!result.get())
     return NULL;
-  CHECK_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
+  CHECK_EQ(base::Value::Type::DICTIONARY, result->GetType());
   return LoadExtensionManifest(*base::DictionaryValue::From(std::move(result)),
                                manifest_dir, location, extra_flags, error);
 }
diff --git a/extensions/common/manifest_handlers/background_info.cc b/extensions/common/manifest_handlers/background_info.cc
index 7f39b00..3cd0bc4 100644
--- a/extensions/common/manifest_handlers/background_info.cc
+++ b/extensions/common/manifest_handlers/background_info.cc
@@ -126,7 +126,7 @@
     return true;
 
   CHECK(background_scripts_value);
-  if (background_scripts_value->GetType() != base::Value::TYPE_LIST) {
+  if (background_scripts_value->GetType() != base::Value::Type::LIST) {
     *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
     return false;
   }
@@ -233,7 +233,7 @@
                                   &allow_js_access))
     return true;
 
-  if (!allow_js_access->IsType(base::Value::TYPE_BOOLEAN) ||
+  if (!allow_js_access->IsType(base::Value::Type::BOOLEAN) ||
       !allow_js_access->GetAsBoolean(&allow_js_access_)) {
     *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
     return false;
diff --git a/extensions/common/permissions/usb_device_permission.cc b/extensions/common/permissions/usb_device_permission.cc
index 5b0a3fe..4e2f2d5 100644
--- a/extensions/common/permissions/usb_device_permission.cc
+++ b/extensions/common/permissions/usb_device_permission.cc
@@ -5,6 +5,8 @@
 #include "extensions/common/permissions/usb_device_permission.h"
 
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -31,7 +33,7 @@
 
 bool IsInterfaceClassPermissionAlowed(const Extension* extension) {
   const Feature* feature = FeatureProvider::GetBehaviorFeature(
-      BehaviorFeature::kAllowUsbDevicesPermissionInterfaceClass);
+      behavior_feature::kAllowUsbDevicesPermissionInterfaceClass);
   if (!feature)
     return false;
   if (!extension)
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc
index 1545442..1879b879 100644
--- a/extensions/common/switches.cc
+++ b/extensions/common/switches.cc
@@ -20,11 +20,6 @@
 // a new tab.
 const char kEmbeddedExtensionOptions[] = "embedded-extension-options";
 
-// Show apps windows after the first paint. Windows will be shown significantly
-// later for heavy apps loading resources synchronously but it will be
-// insignificant for apps that load most of their resources asynchronously.
-const char kEnableAppsShowOnFirstPaint[] = "enable-apps-show-on-first-paint";
-
 // Enables the <window-controls> tag in platform apps.
 const char kEnableAppWindowControls[] = "enable-app-window-controls";
 
diff --git a/extensions/common/switches.h b/extensions/common/switches.h
index d90fe156..8f01e70f 100644
--- a/extensions/common/switches.h
+++ b/extensions/common/switches.h
@@ -17,7 +17,6 @@
 extern const char kDisableDesktopCapturePickerNewUI[];
 extern const char kDisableTabForDesktopShare[];
 extern const char kEmbeddedExtensionOptions[];
-extern const char kEnableAppsShowOnFirstPaint[];
 extern const char kEnableAppWindowControls[];
 extern const char kEnableEmbeddedExtensionOptions[];
 extern const char kEnableExperimentalExtensionApis[];
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 21acb51..c17e69c 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -177,6 +177,7 @@
     "service_worker_request_sender.h",
     "set_icon_natives.cc",
     "set_icon_natives.h",
+    "source_map.h",
     "static_v8_external_one_byte_string_resource.cc",
     "static_v8_external_one_byte_string_resource.h",
     "test_features_native_handler.cc",
diff --git a/extensions/renderer/activity_log_converter_strategy_unittest.cc b/extensions/renderer/activity_log_converter_strategy_unittest.cc
index 2dfcd8c..5d82de9 100644
--- a/extensions/renderer/activity_log_converter_strategy_unittest.cc
+++ b/extensions/renderer/activity_log_converter_strategy_unittest.cc
@@ -33,7 +33,7 @@
   testing::AssertionResult VerifyNull(v8::Local<v8::Value> v8_value) {
     std::unique_ptr<base::Value> value(
         converter_->FromV8Value(v8_value, context()));
-    if (value->IsType(base::Value::TYPE_NULL))
+    if (value->IsType(base::Value::Type::NONE))
       return testing::AssertionSuccess();
     return testing::AssertionFailure();
   }
@@ -43,7 +43,7 @@
     bool out;
     std::unique_ptr<base::Value> value(
         converter_->FromV8Value(v8_value, context()));
-    if (value->IsType(base::Value::TYPE_BOOLEAN)
+    if (value->IsType(base::Value::Type::BOOLEAN)
         && value->GetAsBoolean(&out)
         && out == expected)
       return testing::AssertionSuccess();
@@ -55,7 +55,7 @@
     int out;
     std::unique_ptr<base::Value> value(
         converter_->FromV8Value(v8_value, context()));
-    if (value->IsType(base::Value::TYPE_INTEGER)
+    if (value->IsType(base::Value::Type::INTEGER)
         && value->GetAsInteger(&out)
         && out == expected)
       return testing::AssertionSuccess();
@@ -67,7 +67,7 @@
     double out;
     std::unique_ptr<base::Value> value(
         converter_->FromV8Value(v8_value, context()));
-    if (value->IsType(base::Value::TYPE_DOUBLE)
+    if (value->IsType(base::Value::Type::DOUBLE)
         && value->GetAsDouble(&out)
         && out == expected)
       return testing::AssertionSuccess();
@@ -79,7 +79,7 @@
     std::string out;
     std::unique_ptr<base::Value> value(
         converter_->FromV8Value(v8_value, context()));
-    if (value->IsType(base::Value::TYPE_STRING)
+    if (value->IsType(base::Value::Type::STRING)
         && value->GetAsString(&out)
         && out == expected)
       return testing::AssertionSuccess();
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 8e32524..7e464779 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -804,8 +804,8 @@
   resources.push_back(
       std::make_pair("chrome/browser/media/router/mojo/media_router.mojom",
                      IDR_MEDIA_ROUTER_MOJOM_JS));
-  resources.push_back(std::make_pair("mojo/common/common_custom_types.mojom",
-                                     IDR_MOJO_COMMON_CUSTOM_TYPES_MOJOM_JS));
+  resources.push_back(
+      std::make_pair("mojo/common/time.mojom", IDR_MOJO_TIME_MOJOM_JS));
   resources.push_back(
       std::make_pair("media_router_bindings", IDR_MEDIA_ROUTER_BINDINGS_JS));
 #endif  // defined(ENABLE_MEDIA_ROUTER)
@@ -999,7 +999,7 @@
                 base::DictionaryValue());
 }
 
-void Dispatcher::OnDeliverMessage(int target_port_id,
+void Dispatcher::OnDeliverMessage(const PortId& target_port_id,
                                   const Message& message) {
   MessagingBindings::DeliverMessage(*script_context_set_, target_port_id,
                                     message,
@@ -1007,12 +1007,12 @@
 }
 
 void Dispatcher::OnDispatchOnConnect(
-    int target_port_id,
+    const PortId& target_port_id,
     const std::string& channel_name,
     const ExtensionMsg_TabConnectionInfo& source,
     const ExtensionMsg_ExternalConnectionInfo& info,
     const std::string& tls_channel_id) {
-  DCHECK_EQ(1, target_port_id % 2);  // target renderer ports have odd IDs.
+  DCHECK(!target_port_id.is_opener);
 
   MessagingBindings::DispatchOnConnect(*script_context_set_, target_port_id,
                                        channel_name, source, info,
@@ -1020,7 +1020,7 @@
                                        NULL);  // All render frames.
 }
 
-void Dispatcher::OnDispatchOnDisconnect(int port_id,
+void Dispatcher::OnDispatchOnDisconnect(const PortId& port_id,
                                         const std::string& error_message) {
   MessagingBindings::DispatchOnDisconnect(*script_context_set_, port_id,
                                           error_message,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 0342cc9..7f1910c 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -59,6 +59,7 @@
 class ScriptContext;
 class ScriptInjectionManager;
 struct Message;
+struct PortId;
 
 // Dispatches extension control messages sent to the renderer and stores
 // renderer extension related state.
@@ -155,14 +156,14 @@
 
   void OnActivateExtension(const std::string& extension_id);
   void OnCancelSuspend(const std::string& extension_id);
-  void OnDeliverMessage(int target_port_id,
-                        const Message& message);
-  void OnDispatchOnConnect(int target_port_id,
+  void OnDeliverMessage(const PortId& target_port_id, const Message& message);
+  void OnDispatchOnConnect(const PortId& target_port_id,
                            const std::string& channel_name,
                            const ExtensionMsg_TabConnectionInfo& source,
                            const ExtensionMsg_ExternalConnectionInfo& info,
                            const std::string& tls_channel_id);
-  void OnDispatchOnDisconnect(int port_id, const std::string& error_message);
+  void OnDispatchOnDisconnect(const PortId& port_id,
+                              const std::string& error_message);
   void OnLoaded(
       const std::vector<ExtensionMsg_Loaded_Params>& loaded_extensions);
   void OnMessageInvoke(const std::string& extension_id,
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index df1de04..e65d824 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -264,7 +264,7 @@
         content::V8ValueConverter::create());
     std::unique_ptr<base::Value> filter_value(converter->FromV8Value(
         v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()));
-    if (!filter_value || !filter_value->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!filter_value || !filter_value->IsType(base::Value::Type::DICTIONARY)) {
       args.GetReturnValue().Set(static_cast<int32_t>(-1));
       return;
     }
diff --git a/extensions/renderer/extension_frame_helper.cc b/extensions/renderer/extension_frame_helper.cc
index c0b6878..5d3709e4 100644
--- a/extensions/renderer/extension_frame_helper.cc
+++ b/extensions/renderer/extension_frame_helper.cc
@@ -9,6 +9,7 @@
 #include "base/timer/elapsed_timer.h"
 #include "content/public/renderer/render_frame.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_handlers/background_info.h"
@@ -90,19 +91,6 @@
 
 }  // namespace
 
-struct ExtensionFrameHelper::PendingPortRequest {
-  PendingPortRequest(PortType type, const base::Callback<void(int)>& callback)
-      : type(type), callback(callback) {}
-  ~PendingPortRequest() {}
-
-  base::ElapsedTimer timer;
-  PortType type;
-  base::Callback<void(int)> callback;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PendingPortRequest);
-};
-
 ExtensionFrameHelper::ExtensionFrameHelper(content::RenderFrame* render_frame,
                                            Dispatcher* extension_dispatcher)
     : content::RenderFrameObserver(render_frame),
@@ -112,7 +100,6 @@
       browser_window_id_(-1),
       extension_dispatcher_(extension_dispatcher),
       did_create_current_document_element_(false),
-      next_port_request_id_(0),
       weak_ptr_factory_(this) {
   g_frame_helpers.Get().insert(this);
 }
@@ -194,64 +181,6 @@
   document_load_finished_callbacks_.push_back(callback);
 }
 
-void ExtensionFrameHelper::RequestPortId(
-    const ExtensionMsg_ExternalConnectionInfo& info,
-    const std::string& channel_name,
-    bool include_tls_channel_id,
-    const base::Callback<void(int)>& callback) {
-  int port_request_id = next_port_request_id_++;
-  pending_port_requests_[port_request_id] =
-      base::MakeUnique<PendingPortRequest>(PortType::EXTENSION, callback);
-  {
-    SCOPED_UMA_HISTOGRAM_TIMER(
-        "Extensions.Messaging.GetPortIdSyncTime.Extension");
-    render_frame()->Send(new ExtensionHostMsg_OpenChannelToExtension(
-        render_frame()->GetRoutingID(), info, channel_name,
-        include_tls_channel_id, port_request_id));
-  }
-}
-
-void ExtensionFrameHelper::RequestTabPortId(
-    const ExtensionMsg_TabTargetConnectionInfo& info,
-    const std::string& extension_id,
-    const std::string& channel_name,
-    const base::Callback<void(int)>& callback) {
-  int port_request_id = next_port_request_id_++;
-  pending_port_requests_[port_request_id] =
-      base::MakeUnique<PendingPortRequest>(PortType::TAB, callback);
-  {
-    SCOPED_UMA_HISTOGRAM_TIMER("Extensions.Messaging.GetPortIdSyncTime.Tab");
-    render_frame()->Send(new ExtensionHostMsg_OpenChannelToTab(
-        render_frame()->GetRoutingID(), info, extension_id, channel_name,
-        port_request_id));
-  }
-}
-
-void ExtensionFrameHelper::RequestNativeAppPortId(
-    const std::string& native_app_name,
-    const base::Callback<void(int)>& callback) {
-  int port_request_id = next_port_request_id_++;
-  pending_port_requests_[port_request_id] =
-      base::MakeUnique<PendingPortRequest>(PortType::NATIVE_APP, callback);
-  {
-    SCOPED_UMA_HISTOGRAM_TIMER(
-        "Extensions.Messaging.GetPortIdSyncTime.NativeApp");
-    render_frame()->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
-        render_frame()->GetRoutingID(), native_app_name, port_request_id));
-  }
-}
-
-int ExtensionFrameHelper::RequestSyncPortId(
-    const ExtensionMsg_ExternalConnectionInfo& info,
-    const std::string& channel_name,
-    bool include_tls_channel_id) {
-  int port_id = 0;
-  render_frame()->Send(new ExtensionHostMsg_OpenChannelToExtensionSync(
-      render_frame()->GetRoutingID(), info, channel_name,
-      include_tls_channel_id, &port_id));
-  return port_id;
-}
-
 void ExtensionFrameHelper::DidMatchCSS(
     const blink::WebVector<blink::WebString>& newly_matching_selectors,
     const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
@@ -317,19 +246,18 @@
                         OnNotifyRendererViewType)
     IPC_MESSAGE_HANDLER(ExtensionMsg_Response, OnExtensionResponse)
     IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_AssignPortId, OnAssignPortId)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
 }
 
-void ExtensionFrameHelper::OnExtensionValidateMessagePort(int port_id) {
+void ExtensionFrameHelper::OnExtensionValidateMessagePort(const PortId& id) {
   MessagingBindings::ValidateMessagePort(
-      extension_dispatcher_->script_context_set(), port_id, render_frame());
+      extension_dispatcher_->script_context_set(), id, render_frame());
 }
 
 void ExtensionFrameHelper::OnExtensionDispatchOnConnect(
-    int target_port_id,
+    const PortId& target_port_id,
     const std::string& channel_name,
     const ExtensionMsg_TabConnectionInfo& source,
     const ExtensionMsg_ExternalConnectionInfo& info,
@@ -344,7 +272,7 @@
       render_frame());
 }
 
-void ExtensionFrameHelper::OnExtensionDeliverMessage(int target_id,
+void ExtensionFrameHelper::OnExtensionDeliverMessage(const PortId& target_id,
                                                      const Message& message) {
   MessagingBindings::DeliverMessage(
       extension_dispatcher_->script_context_set(), target_id, message,
@@ -352,10 +280,10 @@
 }
 
 void ExtensionFrameHelper::OnExtensionDispatchOnDisconnect(
-    int port_id,
+    const PortId& id,
     const std::string& error_message) {
   MessagingBindings::DispatchOnDisconnect(
-      extension_dispatcher_->script_context_set(), port_id, error_message,
+      extension_dispatcher_->script_context_set(), id, error_message,
       render_frame());
 }
 
@@ -394,31 +322,6 @@
       render_frame(), extension_id, module_name, function_name, args);
 }
 
-void ExtensionFrameHelper::OnAssignPortId(int port_id, int request_id) {
-  auto iter = pending_port_requests_.find(request_id);
-  DCHECK(iter != pending_port_requests_.end());
-  PendingPortRequest& request = *iter->second;
-  switch (request.type) {
-    case PortType::EXTENSION: {
-      UMA_HISTOGRAM_TIMES("Extensions.Messaging.GetPortIdAsyncTime.Extension",
-                          request.timer.Elapsed());
-      break;
-    }
-    case PortType::TAB: {
-      UMA_HISTOGRAM_TIMES("Extensions.Messaging.GetPortIdAsyncTime.Tab",
-                          request.timer.Elapsed());
-      break;
-    }
-    case PortType::NATIVE_APP: {
-      UMA_HISTOGRAM_TIMES("Extensions.Messaging.GetPortIdAsyncTime.NativeApp",
-                          request.timer.Elapsed());
-      break;
-    }
-  }
-  request.callback.Run(port_id);
-  pending_port_requests_.erase(iter);
-}
-
 void ExtensionFrameHelper::OnDestruct() {
   delete this;
 }
diff --git a/extensions/renderer/extension_frame_helper.h b/extensions/renderer/extension_frame_helper.h
index 2fd322a..d55f624f 100644
--- a/extensions/renderer/extension_frame_helper.h
+++ b/extensions/renderer/extension_frame_helper.h
@@ -16,7 +16,6 @@
 
 struct ExtensionMsg_ExternalConnectionInfo;
 struct ExtensionMsg_TabConnectionInfo;
-struct ExtensionMsg_TabTargetConnectionInfo;
 
 namespace base {
 class ListValue;
@@ -26,6 +25,7 @@
 
 class Dispatcher;
 struct Message;
+struct PortId;
 class ScriptContext;
 
 // RenderFrame-level plumbing for extension features.
@@ -82,25 +82,7 @@
   // Schedule a callback, to be run at the next RunScriptsAtDocumentEnd call.
   void ScheduleAtDocumentEnd(const base::Closure& callback);
 
-  // Sends a message to the browser requesting a port id. |callback| will be
-  // invoked with the global port id when the browser responds.
-  void RequestPortId(const ExtensionMsg_ExternalConnectionInfo& info,
-                     const std::string& channel_name,
-                     bool include_tls_channel_id,
-                     const base::Callback<void(int)>& callback);
-  void RequestTabPortId(const ExtensionMsg_TabTargetConnectionInfo& info,
-                        const std::string& extension_id,
-                        const std::string& channel_name,
-                        const base::Callback<void(int)>& callback);
-  void RequestNativeAppPortId(const std::string& native_app_name,
-                              const base::Callback<void(int)>& callback);
-  int RequestSyncPortId(const ExtensionMsg_ExternalConnectionInfo& info,
-                        const std::string& channel_name,
-                        bool include_tls_channel_id);
-
  private:
-  struct PendingPortRequest;
-
   // RenderFrameObserver implementation.
   void DidCreateDocumentElement() override;
   void DidCreateNewDocument() override;
@@ -117,16 +99,16 @@
   void OnDestruct() override;
 
   // IPC handlers.
-  void OnExtensionValidateMessagePort(int port_id);
+  void OnExtensionValidateMessagePort(const PortId& id);
   void OnExtensionDispatchOnConnect(
-      int target_port_id,
+      const PortId& target_port_id,
       const std::string& channel_name,
       const ExtensionMsg_TabConnectionInfo& source,
       const ExtensionMsg_ExternalConnectionInfo& info,
       const std::string& tls_channel_id);
-  void OnExtensionDeliverMessage(int target_port_id,
+  void OnExtensionDeliverMessage(const PortId& target_port_id,
                                  const Message& message);
-  void OnExtensionDispatchOnDisconnect(int port_id,
+  void OnExtensionDispatchOnDisconnect(const PortId& id,
                                        const std::string& error_message);
   void OnExtensionSetTabId(int tab_id);
   void OnUpdateBrowserWindowId(int browser_window_id);
@@ -139,7 +121,6 @@
                                 const std::string& module_name,
                                 const std::string& function_name,
                                 const base::ListValue& args);
-  void OnAssignPortId(int port_id, int request_id);
 
   // Type of view associated with the RenderFrame.
   ViewType view_type_;
@@ -161,12 +142,6 @@
   // Callbacks to be run at the next RunScriptsAtDocumentEnd notification.
   std::vector<base::Closure> document_load_finished_callbacks_;
 
-  // Counter for port requests.
-  int next_port_request_id_;
-
-  // Map of port requests to callbacks.
-  std::map<int, std::unique_ptr<PendingPortRequest>> pending_port_requests_;
-
   bool delayed_main_world_script_initialization_ = false;
 
   base::WeakPtrFactory<ExtensionFrameHelper> weak_ptr_factory_;
diff --git a/extensions/renderer/extension_port.cc b/extensions/renderer/extension_port.cc
index 7ba4571..47a472e 100644
--- a/extensions/renderer/extension_port.cc
+++ b/extensions/renderer/extension_port.cc
@@ -6,59 +6,36 @@
 
 #include "content/public/renderer/render_frame.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/renderer/script_context.h"
 
 namespace extensions {
 
-ExtensionPort::ExtensionPort(ScriptContext* script_context, int local_id)
-    : script_context_(script_context), local_id_(local_id) {}
+ExtensionPort::ExtensionPort(ScriptContext* script_context,
+                             const PortId& id,
+                             int js_id)
+    : script_context_(script_context), id_(id), js_id_(js_id) {}
 
 ExtensionPort::~ExtensionPort() {}
 
-void ExtensionPort::SetGlobalId(int id) {
-  global_id_ = id;
-  content::RenderFrame* render_frame = script_context_->GetRenderFrame();
-  for (const auto& message : pending_messages_)
-    PostMessageImpl(render_frame, *message);
-  if (is_disconnected_)
-    SendDisconnected(render_frame);
-  pending_messages_.clear();
-}
-
 void ExtensionPort::PostExtensionMessage(std::unique_ptr<Message> message) {
-  if (!initialized()) {
-    pending_messages_.push_back(std::move(message));
-    return;
-  }
-  PostMessageImpl(script_context_->GetRenderFrame(), *message);
-}
-
-void ExtensionPort::Close(bool close_channel) {
-  is_disconnected_ = true;
-  close_channel_ = close_channel;
-  if (!initialized())
-    return;
-
-  SendDisconnected(script_context_->GetRenderFrame());
-}
-
-void ExtensionPort::PostMessageImpl(content::RenderFrame* render_frame,
-                                    const Message& message) {
+  content::RenderFrame* render_frame = script_context_->GetRenderFrame();
   // TODO(devlin): What should we do if there's no render frame? Up until now,
   // we've always just dropped the messages, but we might need to figure this
   // out for service workers.
   if (!render_frame)
     return;
   render_frame->Send(new ExtensionHostMsg_PostMessage(
-      render_frame->GetRoutingID(), global_id_, message));
+      render_frame->GetRoutingID(), id_, *message));
 }
 
-void ExtensionPort::SendDisconnected(content::RenderFrame* render_frame) {
+void ExtensionPort::Close(bool close_channel) {
+  content::RenderFrame* render_frame = script_context_->GetRenderFrame();
   if (!render_frame)
     return;
   render_frame->Send(new ExtensionHostMsg_CloseMessagePort(
-      render_frame->GetRoutingID(), global_id_, close_channel_));
+      render_frame->GetRoutingID(), id_, close_channel));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/extension_port.h b/extensions/renderer/extension_port.h
index def5910..aa584a9 100644
--- a/extensions/renderer/extension_port.h
+++ b/extensions/renderer/extension_port.h
@@ -6,31 +6,25 @@
 #define EXTENSIONS_RENDERER_EXTENSION_PORT_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
-
-namespace content {
-class RenderFrame;
-}
+#include "extensions/common/api/messaging/port_id.h"
 
 namespace extensions {
-
 struct Message;
+struct PortId;
 class ScriptContext;
 
 // A class representing information about a specific extension message port that
-// handles sending related IPCs to the browser. Since global port IDs are
-// assigned asynchronously, this object caches pending messages.
+// handles sending related IPCs to the browser. This consists of a port id and
+// a separate js_id which is exposed only to the JavaScript context.
 class ExtensionPort {
  public:
-  ExtensionPort(ScriptContext* script_context, int local_id);
+  ExtensionPort(ScriptContext* script_context, const PortId& id, int js_id);
   ~ExtensionPort();
 
-  // Sets the global id of the port (that is, the id used in the browser
-  // process to send/receive messages). Any pending messages will be sent.
-  void SetGlobalId(int id);
-
   // Posts a new message to the port. If the port is not initialized, the
   // message will be queued until it is.
   void PostExtensionMessage(std::unique_ptr<Message> message);
@@ -39,36 +33,20 @@
   // assuming initialization completes (after which, the port will close).
   void Close(bool close_channel);
 
-  int local_id() const { return local_id_; }
-  int global_id() const { return global_id_; }
-  bool initialized() const { return global_id_ != -1; }
+  const PortId& id() const { return id_; }
+  int js_id() const { return js_id_; }
 
  private:
-  // Helper function to send the post message IPC.
-  void PostMessageImpl(content::RenderFrame* render_frame,
-                       const Message& message);
-
-  // Sends the message to the browser that this port has been disconnected.
-  void SendDisconnected(content::RenderFrame* render_frame);
-
   // The associated ScriptContext for this port. Since these objects are owned
   // by a NativeHandler, this should always be valid.
   ScriptContext* script_context_ = nullptr;
 
-  // The local id of the port (used within JS bindings).
-  int local_id_ = -1;
+  const PortId id_;
 
-  // The global id of the port (used by the browser process).
-  int global_id_ = -1;
-
-  // The queue of any pending messages to be sent once the port is initialized.
-  std::vector<std::unique_ptr<Message>> pending_messages_;
-
-  // Whether or not the port is disconnected.
-  bool is_disconnected_ = false;
-
-  // Whether to close the full message channel.
-  bool close_channel_ = false;
+  // The id used in the JS bindings. If this is a receiver port, this is not the
+  // same as the port_number in the PortId. This should be used only as an
+  // identifier in the JS context this port is from.
+  int js_id_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionPort);
 };
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc
index b82e84b..c06fe1d 100644
--- a/extensions/renderer/messaging_bindings.cc
+++ b/extensions/renderer/messaging_bindings.cc
@@ -22,6 +22,7 @@
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "extensions/common/api/messaging/message.h"
+#include "extensions/common/api/messaging/port_id.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_handlers/externally_connectable.h"
 #include "extensions/renderer/extension_frame_helper.h"
@@ -55,7 +56,7 @@
 base::LazyInstance<std::map<ScriptContext*, MessagingBindings*>>
     g_messaging_map = LAZY_INSTANCE_INITIALIZER;
 
-void HasMessagePort(int global_port_id,
+void HasMessagePort(const PortId& port_id,
                     bool* has_port,
                     ScriptContext* script_context) {
   if (*has_port)
@@ -63,12 +64,12 @@
 
   MessagingBindings* bindings = g_messaging_map.Get()[script_context];
   DCHECK(bindings);
-  if (bindings->GetPortWithGlobalId(global_port_id))
-    *has_port = true;
+  // No need for |=; we know this is false right now from above.
+  *has_port = bindings->GetPortWithId(port_id) != nullptr;
 }
 
 void DispatchOnConnectToScriptContext(
-    int global_target_port_id,
+    const PortId& target_port_id,
     const std::string& channel_name,
     const ExtensionMsg_TabConnectionInfo* source,
     const ExtensionMsg_ExternalConnectionInfo& info,
@@ -78,17 +79,17 @@
   MessagingBindings* bindings = g_messaging_map.Get()[script_context];
   DCHECK(bindings);
 
-  int opposite_port_id = global_target_port_id ^ 1;
-  if (bindings->GetPortWithGlobalId(opposite_port_id))
-    return;  // The channel was opened by this same context; ignore it.
+  // If the channel was opened by this same context, ignore it. This should only
+  // happen when messages are sent to an entire process (rather than a single
+  // frame) as an optimization; otherwise the browser process filters this out.
+  if (bindings->context_id() == target_port_id.context_id)
+    return;
 
-  ExtensionPort* port =
-      bindings->CreateNewPortWithGlobalId(global_target_port_id);
-  int local_port_id = port->local_id();
+  ExtensionPort* port = bindings->CreateNewPortWithId(target_port_id);
   // Remove the port.
   base::ScopedClosureRunner remove_port(
-      base::Bind(&MessagingBindings::RemovePortWithLocalId,
-                 bindings->GetWeakPtr(), local_port_id));
+      base::Bind(&MessagingBindings::RemovePortWithJsId, bindings->GetWeakPtr(),
+                 port->js_id()));
 
   v8::Isolate* isolate = script_context->isolate();
   v8::HandleScope handle_scope(isolate);
@@ -141,7 +142,7 @@
 
   v8::Local<v8::Value> arguments[] = {
       // portId
-      v8::Integer::New(isolate, local_port_id),
+      v8::Integer::New(isolate, port->js_id()),
       // channelName
       v8_channel_name,
       // sourceTab
@@ -178,11 +179,11 @@
 }
 
 void DeliverMessageToScriptContext(const Message& message,
-                                   int global_target_port_id,
+                                   const PortId& target_port_id,
                                    ScriptContext* script_context) {
   MessagingBindings* bindings = g_messaging_map.Get()[script_context];
   DCHECK(bindings);
-  ExtensionPort* port = bindings->GetPortWithGlobalId(global_target_port_id);
+  ExtensionPort* port = bindings->GetPortWithId(target_port_id);
   if (!port)
     return;
 
@@ -190,7 +191,7 @@
   v8::HandleScope handle_scope(isolate);
 
   v8::Local<v8::Value> port_id_handle =
-      v8::Integer::New(isolate, port->local_id());
+      v8::Integer::New(isolate, port->js_id());
 
   v8::Local<v8::String> v8_data;
   if (!ToV8String(isolate, message.data.c_str(), &v8_data))
@@ -217,12 +218,12 @@
       "messaging", "dispatchOnMessage", &arguments);
 }
 
-void DispatchOnDisconnectToScriptContext(int global_port_id,
+void DispatchOnDisconnectToScriptContext(const PortId& port_id,
                                          const std::string& error_message,
                                          ScriptContext* script_context) {
   MessagingBindings* bindings = g_messaging_map.Get()[script_context];
   DCHECK(bindings);
-  ExtensionPort* port = bindings->GetPortWithGlobalId(global_port_id);
+  ExtensionPort* port = bindings->GetPortWithId(port_id);
   if (!port)
     return;
 
@@ -230,7 +231,7 @@
   v8::HandleScope handle_scope(isolate);
 
   std::vector<v8::Local<v8::Value>> arguments;
-  arguments.push_back(v8::Integer::New(isolate, port->local_id()));
+  arguments.push_back(v8::Integer::New(isolate, port->js_id()));
   v8::Local<v8::String> v8_error_message;
   if (!error_message.empty())
     ToV8String(isolate, error_message.c_str(), &v8_error_message);
@@ -247,7 +248,9 @@
 }  // namespace
 
 MessagingBindings::MessagingBindings(ScriptContext* context)
-    : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
+    : ObjectBackedNativeHandler(context),
+      context_id_(base::UnguessableToken::Create()),
+      weak_ptr_factory_(this) {
   g_messaging_map.Get()[context] = this;
   RouteFunction("CloseChannel", base::Bind(&MessagingBindings::CloseChannel,
                                            base::Unretained(this)));
@@ -269,49 +272,41 @@
 
 MessagingBindings::~MessagingBindings() {
   g_messaging_map.Get().erase(context());
-  if (ports_created_normal_ > 0 || ports_created_in_before_unload_ > 0 ||
-      ports_created_in_unload_ > 0) {
+  if (num_extension_ports_ > 0) {
     UMA_HISTOGRAM_COUNTS_1000(
-        "Extensions.Messaging.ExtensionPortsCreated.InBeforeUnload",
-        ports_created_in_before_unload_);
-    UMA_HISTOGRAM_COUNTS_1000(
-        "Extensions.Messaging.ExtensionPortsCreated.InUnload",
-        ports_created_in_unload_);
-    UMA_HISTOGRAM_COUNTS_1000(
-        "Extensions.Messaging.ExtensionPortsCreated.Normal",
-        ports_created_normal_);
+        "Extensions.Messaging.ExtensionPortsCreated.Total", next_js_id_);
   }
 }
 
 // static
 void MessagingBindings::ValidateMessagePort(
     const ScriptContextSet& context_set,
-    int global_port_id,
+    const PortId& port_id,
     content::RenderFrame* render_frame) {
   int routing_id = render_frame->GetRoutingID();
 
   bool has_port = false;
   context_set.ForEach(render_frame,
-                      base::Bind(&HasMessagePort, global_port_id, &has_port));
+                      base::Bind(&HasMessagePort, port_id, &has_port));
 
   // A reply is only sent if the port is missing, because the port is assumed to
   // exist unless stated otherwise.
   if (!has_port) {
     content::RenderThread::Get()->Send(
-        new ExtensionHostMsg_CloseMessagePort(routing_id,
-                                              global_port_id, false));
+        new ExtensionHostMsg_CloseMessagePort(routing_id, port_id, false));
   }
 }
 
 // static
 void MessagingBindings::DispatchOnConnect(
     const ScriptContextSet& context_set,
-    int target_port_id,
+    const PortId& target_port_id,
     const std::string& channel_name,
     const ExtensionMsg_TabConnectionInfo& source,
     const ExtensionMsg_ExternalConnectionInfo& info,
     const std::string& tls_channel_id,
     content::RenderFrame* restrict_to_render_frame) {
+  DCHECK(!target_port_id.is_opener);
   int routing_id = restrict_to_render_frame
                        ? restrict_to_render_frame->GetRoutingID()
                        : MSG_ROUTING_NONE;
@@ -334,7 +329,7 @@
 // static
 void MessagingBindings::DeliverMessage(
     const ScriptContextSet& context_set,
-    int target_port_id,
+    const PortId& target_port_id,
     const Message& message,
     content::RenderFrame* restrict_to_render_frame) {
   context_set.ForEach(
@@ -345,7 +340,7 @@
 // static
 void MessagingBindings::DispatchOnDisconnect(
     const ScriptContextSet& context_set,
-    int port_id,
+    const PortId& port_id,
     const std::string& error_message,
     content::RenderFrame* restrict_to_render_frame) {
   context_set.ForEach(
@@ -353,27 +348,23 @@
       base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message));
 }
 
-ExtensionPort* MessagingBindings::GetPortWithGlobalId(int id) {
+ExtensionPort* MessagingBindings::GetPortWithId(const PortId& id) {
   for (const auto& key_value : ports_) {
-    if (key_value.second->global_id() == id)
+    if (key_value.second->id() == id)
       return key_value.second.get();
   }
   return nullptr;
 }
 
-ExtensionPort* MessagingBindings::CreateNewPortWithGlobalId(int global_id) {
-  int local_id = GetNextLocalId();
-  ExtensionPort* port =
-      ports_
-          .insert(std::make_pair(
-              local_id, base::MakeUnique<ExtensionPort>(context(), local_id)))
-          .first->second.get();
-  port->SetGlobalId(global_id);
-  return port;
+ExtensionPort* MessagingBindings::CreateNewPortWithId(const PortId& id) {
+  int js_id = GetNextJsId();
+  auto port = base::MakeUnique<ExtensionPort>(context(), id, js_id);
+  return ports_.insert(std::make_pair(js_id, std::move(port)))
+      .first->second.get();
 }
 
-void MessagingBindings::RemovePortWithLocalId(int local_id) {
-  ports_.erase(local_id);
+void MessagingBindings::RemovePortWithJsId(int js_id) {
+  ports_.erase(js_id);
 }
 
 base::WeakPtr<MessagingBindings> MessagingBindings::GetWeakPtr() {
@@ -387,8 +378,8 @@
   CHECK(args[0]->IsInt32());
   CHECK(args[1]->IsString());
 
-  int port_id = args[0].As<v8::Int32>()->Value();
-  auto iter = ports_.find(port_id);
+  int js_port_id = args[0].As<v8::Int32>()->Value();
+  auto iter = ports_.find(js_port_id);
   if (iter != ports_.end()) {
     iter->second->PostExtensionMessage(base::MakeUnique<Message>(
         *v8::String::Utf8Value(args[1]),
@@ -403,9 +394,9 @@
   CHECK(args[0]->IsInt32());
   CHECK(args[1]->IsBoolean());
 
-  int port_id = args[0].As<v8::Int32>()->Value();
+  int js_port_id = args[0].As<v8::Int32>()->Value();
   bool force_close = args[1].As<v8::Boolean>()->Value();
-  ClosePort(port_id, force_close);
+  ClosePort(js_port_id, force_close);
 }
 
 void MessagingBindings::BindToGC(
@@ -414,16 +405,16 @@
   CHECK(args[0]->IsObject());
   CHECK(args[1]->IsFunction());
   CHECK(args[2]->IsInt32());
-  int local_port_id = args[2].As<v8::Int32>()->Value();
+  int js_port_id = args[2].As<v8::Int32>()->Value();
   base::Closure fallback = base::Bind(&base::DoNothing);
-  if (local_port_id >= 0) {
+  if (js_port_id >= 0) {
     // TODO(robwu): Falling back to closing the port shouldn't be needed. If
     // the script context is destroyed, then the frame has navigated. But that
     // is already detected by the browser, so this logic is redundant. Remove
     // this fallback (and move BindToGC out of messaging because it is also
     // used in other places that have nothing to do with messaging...).
     fallback = base::Bind(&MessagingBindings::ClosePort,
-                          weak_ptr_factory_.GetWeakPtr(), local_port_id,
+                          weak_ptr_factory_.GetWeakPtr(), js_port_id,
                           false /* force_close */);
   }
   // Destroys itself when the object is GC'd or context is invalidated.
@@ -443,8 +434,9 @@
   CHECK(args[1]->IsString());
   CHECK(args[2]->IsBoolean());
 
-  int local_id = GetNextLocalId();
-  ports_[local_id] = base::MakeUnique<ExtensionPort>(context(), local_id);
+  int js_id = GetNextJsId();
+  PortId port_id(context_id_, js_id, true);
+  ports_[js_id] = base::MakeUnique<ExtensionPort>(context(), port_id, js_id);
 
   ExtensionMsg_ExternalConnectionInfo info;
   // For messaging APIs, hosted apps should be considered a web page so hide
@@ -460,34 +452,16 @@
   bool include_tls_channel_id =
       args.Length() > 2 ? args[2]->BooleanValue() : false;
 
-  ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
-  DCHECK(frame_helper);
-
-  blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
-  // If the frame is unloading, we need to assign the port id synchronously to
-  // avoid dropping the message on the floor; see crbug.com/660706.
-  // TODO(devlin): Investigate whether we need to continue supporting this long-
-  // term, and, if so, find an alternative that doesn't require synchronous
-  // IPCs.
-  if (!web_frame->document().isNull() &&
-      (web_frame->document().unloadStartedDoNotUse() ||
-       web_frame->document().processingBeforeUnloadDoNotUse())) {
-    ports_created_in_before_unload_ +=
-        web_frame->document().processingBeforeUnloadDoNotUse() ? 1 : 0;
-    ports_created_in_unload_ +=
-        web_frame->document().unloadStartedDoNotUse() ? 1 : 0;
-    int global_id = frame_helper->RequestSyncPortId(info, channel_name,
-                                                    include_tls_channel_id);
-    ports_[local_id]->SetGlobalId(global_id);
-  } else {
-    ++ports_created_normal_;
-    frame_helper->RequestPortId(
-        info, channel_name, include_tls_channel_id,
-        base::Bind(&MessagingBindings::SetGlobalPortId,
-                   weak_ptr_factory_.GetWeakPtr(), local_id));
+  {
+    SCOPED_UMA_HISTOGRAM_TIMER(
+        "Extensions.Messaging.SetPortIdTime.Extension");
+    render_frame->Send(new ExtensionHostMsg_OpenChannelToExtension(
+        render_frame->GetRoutingID(), info, channel_name,
+        include_tls_channel_id, port_id));
   }
 
-  args.GetReturnValue().Set(static_cast<int32_t>(local_id));
+  ++num_extension_ports_;
+  args.GetReturnValue().Set(static_cast<int32_t>(js_id));
 }
 
 void MessagingBindings::OpenChannelToNativeApp(
@@ -504,16 +478,18 @@
 
   std::string native_app_name = *v8::String::Utf8Value(args[0]);
 
-  int local_id = GetNextLocalId();
-  ports_[local_id] = base::MakeUnique<ExtensionPort>(context(), local_id);
+  int js_id = GetNextJsId();
+  PortId port_id(context_id_, js_id, true);
+  ports_[js_id] = base::MakeUnique<ExtensionPort>(context(), port_id, js_id);
 
-  ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
-  DCHECK(frame_helper);
-  frame_helper->RequestNativeAppPortId(
-      native_app_name,
-      base::Bind(&MessagingBindings::SetGlobalPortId,
-                 weak_ptr_factory_.GetWeakPtr(), local_id));
-  args.GetReturnValue().Set(static_cast<int32_t>(local_id));
+  {
+    SCOPED_UMA_HISTOGRAM_TIMER(
+        "Extensions.Messaging.SetPortIdTime.NativeApp");
+    render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
+        render_frame->GetRoutingID(), native_app_name, port_id));
+  }
+
+  args.GetReturnValue().Set(static_cast<int32_t>(js_id));
 }
 
 void MessagingBindings::OpenChannelToTab(
@@ -535,8 +511,9 @@
   CHECK(args[2]->IsString());
   CHECK(args[3]->IsString());
 
-  int local_id = GetNextLocalId();
-  ports_[local_id] = base::MakeUnique<ExtensionPort>(context(), local_id);
+  int js_id = GetNextJsId();
+  PortId port_id(context_id_, js_id, true);
+  ports_[js_id] = base::MakeUnique<ExtensionPort>(context(), port_id, js_id);
 
   ExtensionMsg_TabTargetConnectionInfo info;
   info.tab_id = args[0]->Int32Value();
@@ -547,48 +524,30 @@
 
   ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
   DCHECK(frame_helper);
-  frame_helper->RequestTabPortId(
-      info, extension_id, channel_name,
-      base::Bind(&MessagingBindings::SetGlobalPortId,
-                 weak_ptr_factory_.GetWeakPtr(), local_id));
 
-  args.GetReturnValue().Set(static_cast<int32_t>(local_id));
+  {
+    SCOPED_UMA_HISTOGRAM_TIMER("Extensions.Messaging.SetPortIdTime.Tab");
+    render_frame->Send(new ExtensionHostMsg_OpenChannelToTab(
+        render_frame->GetRoutingID(), info, extension_id, channel_name,
+        port_id));
+  }
+
+  args.GetReturnValue().Set(static_cast<int32_t>(js_id));
 }
 
-void MessagingBindings::ClosePort(int local_port_id, bool force_close) {
+void MessagingBindings::ClosePort(int js_port_id, bool force_close) {
   // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC
   // has been addressed.
-  auto iter = ports_.find(local_port_id);
+  auto iter = ports_.find(js_port_id);
   if (iter != ports_.end()) {
     std::unique_ptr<ExtensionPort> port = std::move(iter->second);
     ports_.erase(iter);
     port->Close(force_close);
-    // If the port hasn't been initialized, we can't delete it just yet, because
-    // we need to wait for it to send any pending messages. This is important
-    // to support the flow:
-    //  var port = chrome.runtime.connect();
-    //  port.postMessage({message});
-    //  port.disconnect();
-    if (!port->initialized())
-      disconnected_ports_[port->local_id()] = std::move(port);
   }
 }
 
-void MessagingBindings::SetGlobalPortId(int local_id, int global_id) {
-  auto iter = ports_.find(local_id);
-  if (iter != ports_.end()) {
-    iter->second->SetGlobalId(global_id);
-    return;
-  }
-
-  iter = disconnected_ports_.find(local_id);
-  DCHECK(iter != disconnected_ports_.end());
-  iter->second->SetGlobalId(global_id);
-  // Setting the global id dispatches pending messages, so we can delete the
-  // port now.
-  disconnected_ports_.erase(iter);
+int MessagingBindings::GetNextJsId() {
+  return next_js_id_++;
 }
 
-int MessagingBindings::GetNextLocalId() { return next_local_id_++; }
-
 }  // namespace extensions
diff --git a/extensions/renderer/messaging_bindings.h b/extensions/renderer/messaging_bindings.h
index 758187e..f21bb4a 100644
--- a/extensions/renderer/messaging_bindings.h
+++ b/extensions/renderer/messaging_bindings.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/unguessable_token.h"
 #include "extensions/renderer/object_backed_native_handler.h"
 
 struct ExtensionMsg_ExternalConnectionInfo;
@@ -21,6 +22,7 @@
 namespace extensions {
 class ExtensionPort;
 struct Message;
+struct PortId;
 class ScriptContextSet;
 
 // Manually implements JavaScript bindings for extension messaging.
@@ -32,14 +34,14 @@
   // Checks whether the port exists in the given frame. If it does not, a reply
   // is sent back to the browser.
   static void ValidateMessagePort(const ScriptContextSet& context_set,
-                                  int port_id,
+                                  const PortId& port_id,
                                   content::RenderFrame* render_frame);
 
   // Dispatches the onConnect content script messaging event to some contexts
   // in |context_set|. If |restrict_to_render_frame| is specified, only contexts
   // in that render frame will receive the message.
   static void DispatchOnConnect(const ScriptContextSet& context_set,
-                                int target_port_id,
+                                const PortId& target_port_id,
                                 const std::string& channel_name,
                                 const ExtensionMsg_TabConnectionInfo& source,
                                 const ExtensionMsg_ExternalConnectionInfo& info,
@@ -50,26 +52,28 @@
   // contexts in |bindings_context_set|. If |restrict_to_render_frame| is
   // specified, only contexts in that render view will receive the message.
   static void DeliverMessage(const ScriptContextSet& context_set,
-                             int target_port_id,
+                             const PortId& target_port_id,
                              const Message& message,
                              content::RenderFrame* restrict_to_render_frame);
 
   // Dispatches the onDisconnect event in response to the channel being closed.
   static void DispatchOnDisconnect(
       const ScriptContextSet& context_set,
-      int port_id,
+      const PortId& port_id,
       const std::string& error_message,
       content::RenderFrame* restrict_to_render_frame);
 
-  // Returns an existing port with the given |global_id|, or null.
-  ExtensionPort* GetPortWithGlobalId(int global_id);
+  // Returns an existing port with the given |id|, or null.
+  ExtensionPort* GetPortWithId(const PortId& id);
 
-  // Creates a new port with the given |global_id|. MessagingBindings owns the
+  // Creates a new port with the given |id|. MessagingBindings owns the
   // returned port.
-  ExtensionPort* CreateNewPortWithGlobalId(int global_id);
+  ExtensionPort* CreateNewPortWithId(const PortId& id);
 
-  // Removes the port with the given |local_id|.
-  void RemovePortWithLocalId(int local_id);
+  // Removes the port with the given |js_id|.
+  void RemovePortWithJsId(int js_id);
+
+  const base::UnguessableToken& context_id() const { return context_id_; }
 
   base::WeakPtr<MessagingBindings> GetWeakPtr();
 
@@ -100,32 +104,21 @@
 
   // Helper function to close a port. See CloseChannel() for |force_close|
   // documentation.
-  void ClosePort(int port_id, bool force_close);
+  void ClosePort(int local_port_id, bool force_close);
 
-  // Sets the global id for the port with |local_id|.
-  void SetGlobalPortId(int local_id, int global_id);
-
-  int GetNextLocalId();
+  int GetNextJsId();
 
   // Active ports, mapped by local port id.
   PortMap ports_;
 
-  // Ports which are disconnected, but haven't been fully initialized. Once
-  // initialized and any pending messages are sent, these ports are removed.
-  PortMap disconnected_ports_;
+  // The next available js id for a port.
+  size_t next_js_id_ = 0;
 
-  // The next available local id for a port.
-  size_t next_local_id_ = 0;
+  // The number of extension ports created.
+  size_t num_extension_ports_ = 0;
 
-  // The number of ports created in the 'beforeunload' event handler.
-  size_t ports_created_in_before_unload_ = 0;
-
-  // The number of ports created in the 'unload' event handler.
-  size_t ports_created_in_unload_ = 0;
-
-  // The number of ports created during during any time that isn't in the unload
-  // or beforeunload handlers.
-  int ports_created_normal_ = 0;
+  // A unique identifier for this JS context.
+  const base::UnguessableToken context_id_;
 
   base::WeakPtrFactory<MessagingBindings> weak_ptr_factory_;
 
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index a8e3bdb..034291c 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -21,6 +21,7 @@
 #include "extensions/renderer/safe_builtins.h"
 #include "extensions/renderer/script_context.h"
 #include "extensions/renderer/script_context_set.h"
+#include "extensions/renderer/source_map.h"
 #include "extensions/renderer/v8_helpers.h"
 #include "gin/modules/module_registry.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
@@ -545,14 +546,6 @@
                              base::Unretained(exception_handler_.get())));
 }
 
-v8::Local<v8::Value> ModuleSystem::GetSource(const std::string& module_name) {
-  v8::EscapableHandleScope handle_scope(GetIsolate());
-  if (!source_map_->Contains(module_name))
-    return v8::Undefined(GetIsolate());
-  return handle_scope.Escape(
-      v8::Local<v8::Value>(source_map_->GetSource(GetIsolate(), module_name)));
-}
-
 void ModuleSystem::RequireNative(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK_EQ(1, args.Length());
@@ -668,13 +661,13 @@
   v8::Local<v8::Context> v8_context = context()->v8_context();
   v8::Context::Scope context_scope(v8_context);
 
-  v8::Local<v8::Value> source(GetSource(module_name));
-  if (source.IsEmpty() || source->IsUndefined()) {
+  v8::Local<v8::String> source =
+      source_map_->GetSource(GetIsolate(), module_name);
+  if (source.IsEmpty()) {
     Fatal(context_, "No source for require(" + module_name + ")");
     return v8::Undefined(GetIsolate());
   }
-  v8::Local<v8::String> wrapped_source(
-      WrapSource(v8::Local<v8::String>::Cast(source)));
+  v8::Local<v8::String> wrapped_source(WrapSource(source));
   v8::Local<v8::String> v8_module_name;
   if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name)) {
     NOTREACHED() << "module_name is too long";
diff --git a/extensions/renderer/module_system.h b/extensions/renderer/module_system.h
index ab5aea23..9d7dbd27 100644
--- a/extensions/renderer/module_system.h
+++ b/extensions/renderer/module_system.h
@@ -22,6 +22,7 @@
 namespace extensions {
 
 class ScriptContext;
+class SourceMap;
 
 // A module system for JS similar to node.js' require() function.
 // Each module has three variables in the global scope:
@@ -42,14 +43,6 @@
 class ModuleSystem : public ObjectBackedNativeHandler,
                      public gin::ModuleRegistryObserver {
  public:
-  class SourceMap {
-   public:
-    virtual ~SourceMap() {}
-    virtual v8::Local<v8::Value> GetSource(v8::Isolate* isolate,
-                                           const std::string& name) const = 0;
-    virtual bool Contains(const std::string& name) const = 0;
-  };
-
   class ExceptionHandler {
    public:
     explicit ExceptionHandler(ScriptContext* context) : context_(context) {}
diff --git a/extensions/renderer/module_system_test.cc b/extensions/renderer/module_system_test.cc
index 87f6786..3ea5ce5 100644
--- a/extensions/renderer/module_system_test.cc
+++ b/extensions/renderer/module_system_test.cc
@@ -23,6 +23,7 @@
 #include "extensions/renderer/logging_native_handler.h"
 #include "extensions/renderer/object_backed_native_handler.h"
 #include "extensions/renderer/safe_builtins.h"
+#include "extensions/renderer/source_map.h"
 #include "extensions/renderer/utils_native_handler.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -100,17 +101,16 @@
 };
 
 // Source map that operates on std::strings.
-class ModuleSystemTestEnvironment::StringSourceMap
-    : public ModuleSystem::SourceMap {
+class ModuleSystemTestEnvironment::StringSourceMap : public SourceMap {
  public:
   StringSourceMap() {}
   ~StringSourceMap() override {}
 
-  v8::Local<v8::Value> GetSource(v8::Isolate* isolate,
+  v8::Local<v8::String> GetSource(v8::Isolate* isolate,
                                  const std::string& name) const override {
     const auto& source_map_iter = source_map_.find(name);
     if (source_map_iter == source_map_.end())
-      return v8::Undefined(isolate);
+      return v8::Local<v8::String>();
     return v8::String::NewFromUtf8(isolate, source_map_iter->second.c_str());
   }
 
diff --git a/extensions/renderer/resource_bundle_source_map.cc b/extensions/renderer/resource_bundle_source_map.cc
index a260b54..073afe8 100644
--- a/extensions/renderer/resource_bundle_source_map.cc
+++ b/extensions/renderer/resource_bundle_source_map.cc
@@ -36,20 +36,20 @@
   resource_id_map_[name] = resource_id;
 }
 
-v8::Local<v8::Value> ResourceBundleSourceMap::GetSource(
+v8::Local<v8::String> ResourceBundleSourceMap::GetSource(
     v8::Isolate* isolate,
     const std::string& name) const {
   const auto& resource_id_iter = resource_id_map_.find(name);
   if (resource_id_iter == resource_id_map_.end()) {
     NOTREACHED() << "No module is registered with name \"" << name << "\"";
-    return v8::Undefined(isolate);
+    return v8::Local<v8::String>();
   }
   base::StringPiece resource =
       resource_bundle_->GetRawDataResource(resource_id_iter->second);
   if (resource.empty()) {
     NOTREACHED()
         << "Module resource registered as \"" << name << "\" not found";
-    return v8::Undefined(isolate);
+    return v8::Local<v8::String>();
   }
   return ConvertString(isolate, resource);
 }
diff --git a/extensions/renderer/resource_bundle_source_map.h b/extensions/renderer/resource_bundle_source_map.h
index 18ed4d2..42d3ba61 100644
--- a/extensions/renderer/resource_bundle_source_map.h
+++ b/extensions/renderer/resource_bundle_source_map.h
@@ -8,8 +8,8 @@
 #include <map>
 #include <string>
 
-#include "base/compiler_specific.h"
-#include "extensions/renderer/module_system.h"
+#include "base/macros.h"
+#include "extensions/renderer/source_map.h"
 #include "v8/include/v8.h"
 
 namespace ui {
@@ -18,13 +18,13 @@
 
 namespace extensions {
 
-class ResourceBundleSourceMap : public extensions::ModuleSystem::SourceMap {
+class ResourceBundleSourceMap : public SourceMap {
  public:
   explicit ResourceBundleSourceMap(const ui::ResourceBundle* resource_bundle);
   ~ResourceBundleSourceMap() override;
 
-  v8::Local<v8::Value> GetSource(v8::Isolate* isolate,
-                                 const std::string& name) const override;
+  v8::Local<v8::String> GetSource(v8::Isolate* isolate,
+                                  const std::string& name) const override;
   bool Contains(const std::string& name) const override;
 
   void RegisterSource(const std::string& name, int resource_id);
diff --git a/extensions/renderer/resources/app_window_custom_bindings.js b/extensions/renderer/resources/app_window_custom_bindings.js
index 546183f8..376e367 100644
--- a/extensions/renderer/resources/app_window_custom_bindings.js
+++ b/extensions/renderer/resources/app_window_custom_bindings.js
@@ -222,7 +222,6 @@
     AppWindow.prototype.resizeTo = $Function.bind(window.resizeTo, window);
     AppWindow.prototype.contentWindow = window;
     AppWindow.prototype.onClosed = new Event();
-    AppWindow.prototype.onWindowFirstShownForTests = new Event();
     AppWindow.prototype.close = function() {
       this.contentWindow.close();
     };
@@ -253,15 +252,6 @@
     AppWindow.prototype.alphaEnabled = function() {
       return appWindowData.alphaEnabled;
     };
-    AppWindow.prototype.handleWindowFirstShownForTests = function(callback) {
-      // This allows test apps to get have their callback run even if they
-      // call this after the first show has happened.
-      if (this.firstShowHasHappened) {
-        callback();
-        return;
-      }
-      this.onWindowFirstShownForTests.addListener(callback);
-    }
 
     Object.defineProperty(AppWindow.prototype, 'id', {get: function() {
       return appWindowData.id;
@@ -366,16 +356,6 @@
     dispatchEventIfExists(currentWindow, "onAlphaEnabledChanged");
 };
 
-function onAppWindowShownForTests() {
-  if (!currentAppWindow)
-    return;
-
-  if (!currentAppWindow.firstShowHasHappened)
-    dispatchEventIfExists(currentAppWindow, "onWindowFirstShownForTests");
-
-  currentAppWindow.firstShowHasHappened = true;
-}
-
 function onAppWindowClosed() {
   if (!currentAppWindow)
     return;
@@ -407,4 +387,3 @@
 exports.$set('binding', appWindow.generate());
 exports.$set('onAppWindowClosed', onAppWindowClosed);
 exports.$set('updateAppWindowProperties', updateAppWindowProperties);
-exports.$set('appWindowShownForTests', onAppWindowShownForTests);
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd
index 2e4502a..dd5811a1 100644
--- a/extensions/renderer/resources/extensions_renderer_resources.grd
+++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -90,7 +90,7 @@
       <if expr="enable_media_router">
         <include name="IDR_MEDIA_ROUTER_MOJOM_JS" file="${mojom_root}\chrome\browser\media\router\mojo\media_router.mojom.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_MEDIA_ROUTER_BINDINGS_JS" file="media_router_bindings.js" type="BINDATA" />
-        <include name="IDR_MOJO_COMMON_CUSTOM_TYPES_MOJOM_JS" file="${mojom_root}\mojo\common\common_custom_types.mojom.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_MOJO_TIME_MOJOM_JS" file="${mojom_root}\mojo\common\time.mojom.js" use_base_dir="false" type="BINDATA" />
       </if>
     </includes>
     <structures>
diff --git a/extensions/renderer/resources/media_router_bindings.js b/extensions/renderer/resources/media_router_bindings.js
index 825a9e0..16c8643 100644
--- a/extensions/renderer/resources/media_router_bindings.js
+++ b/extensions/renderer/resources/media_router_bindings.js
@@ -10,7 +10,7 @@
     'content/public/renderer/frame_interfaces',
     'chrome/browser/media/router/mojo/media_router.mojom',
     'extensions/common/mojo/keep_alive.mojom',
-    'mojo/common/common_custom_types.mojom',
+    'mojo/common/time.mojom',
     'mojo/public/js/connection',
     'mojo/public/js/router',
 ], function(bindings,
@@ -18,7 +18,7 @@
             frameInterfaces,
             mediaRouterMojom,
             keepAliveMojom,
-            commonCustomTypesMojom,
+            timeMojom,
             connector,
             routerModule) {
   'use strict';
diff --git a/extensions/renderer/send_request_natives.cc b/extensions/renderer/send_request_natives.cc
index c27b5332..4a32814 100644
--- a/extensions/renderer/send_request_natives.cc
+++ b/extensions/renderer/send_request_natives.cc
@@ -53,7 +53,7 @@
 
   std::unique_ptr<base::Value> value_args(
       converter->FromV8Value(args[1], context()->v8_context()));
-  if (!value_args.get() || !value_args->IsType(base::Value::TYPE_LIST)) {
+  if (!value_args.get() || !value_args->IsType(base::Value::Type::LIST)) {
     NOTREACHED() << "Unable to convert args passed to StartRequest";
     return;
   }
diff --git a/extensions/renderer/source_map.h b/extensions/renderer/source_map.h
new file mode 100644
index 0000000..6833181
--- /dev/null
+++ b/extensions/renderer/source_map.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_RENDERER_SOURCE_MAP_H_
+#define EXTENSIONS_RENDERER_SOURCE_MAP_H_
+
+#include <string>
+
+#include "v8/include/v8.h"
+
+namespace extensions {
+
+// A map storing resources associated with a given API or utility.
+class SourceMap {
+ public:
+  virtual ~SourceMap() {}
+
+  // Gets the source for the given resource name.
+  virtual v8::Local<v8::String> GetSource(v8::Isolate* isolate,
+                                          const std::string& name) const = 0;
+
+  // Returns true if the map contains an entry for the given |name|.
+  virtual bool Contains(const std::string& name) const = 0;
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_RENDERER_SOURCE_MAP_H_
diff --git a/extensions/shell/common/api/BUILD.gn b/extensions/shell/common/api/BUILD.gn
index a5834ad7..a2aee1e9 100644
--- a/extensions/shell/common/api/BUILD.gn
+++ b/extensions/shell/common/api/BUILD.gn
@@ -28,7 +28,7 @@
 }
 
 json_features("shell_api_features") {
-  feature_class = "APIFeature"
+  feature_type = "APIFeature"
   provider_class = "ShellAPIFeatureProvider"
   sources = [
     "../../../common/api/_api_features.json",
@@ -37,7 +37,7 @@
 }
 
 json_features("shell_permission_features") {
-  feature_class = "PermissionFeature"
+  feature_type = "PermissionFeature"
   provider_class = "ShellPermissionFeatureProvider"
   sources = [
     "../../../common/api/_permission_features.json",
@@ -45,7 +45,7 @@
 }
 
 json_features("shell_manifest_features") {
-  feature_class = "ManifestFeature"
+  feature_type = "ManifestFeature"
   provider_class = "ShellManifestFeatureProvider"
   sources = [
     "../../../common/api/_manifest_features.json",
@@ -53,7 +53,7 @@
 }
 
 json_features("shell_behavior_features") {
-  feature_class = "BehaviorFeature"
+  feature_type = "BehaviorFeature"
   provider_class = "ShellBehaviorFeatureProvider"
   sources = [
     "../../../common/api/_behavior_features.json",
diff --git a/extensions/shell/common/shell_extensions_client.cc b/extensions/shell/common/shell_extensions_client.cc
index ff29085..89246e5 100644
--- a/extensions/shell/common/shell_extensions_client.cc
+++ b/extensions/shell/common/shell_extensions_client.cc
@@ -4,6 +4,9 @@
 
 #include "extensions/shell/common/shell_extensions_client.h"
 
+#include <memory>
+#include <string>
+
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -11,11 +14,7 @@
 #include "extensions/common/common_manifest_handlers.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/extensions_aliases.h"
-#include "extensions/common/features/api_feature.h"
-#include "extensions/common/features/behavior_feature.h"
 #include "extensions/common/features/json_feature_provider_source.h"
-#include "extensions/common/features/manifest_feature.h"
-#include "extensions/common/features/permission_feature.h"
 #include "extensions/common/features/simple_feature.h"
 #include "extensions/common/manifest_handler.h"
 #include "extensions/common/permissions/permission_message_provider.h"
@@ -34,11 +33,6 @@
 
 namespace {
 
-template <class FeatureClass>
-SimpleFeature* CreateFeature() {
-  return new FeatureClass;
-}
-
 // TODO(jamescook): Refactor ChromePermissionsMessageProvider so we can share
 // code. For now, this implementation does nothing.
 class ShellPermissionMessageProvider : public PermissionMessageProvider {
diff --git a/extensions/test/BUILD.gn b/extensions/test/BUILD.gn
index 76924c6..e04c482 100644
--- a/extensions/test/BUILD.gn
+++ b/extensions/test/BUILD.gn
@@ -5,7 +5,7 @@
 import("//tools/json_schema_compiler/json_features.gni")
 
 json_features("test_api_features") {
-  feature_class = "APIFeature"
+  feature_type = "APIFeature"
   provider_class = "TestAPIFeatureProvider"
   sources = [
     "../common/api/_api_features.json",
@@ -13,7 +13,7 @@
 }
 
 json_features("test_permission_features") {
-  feature_class = "PermissionFeature"
+  feature_type = "PermissionFeature"
   provider_class = "TestPermissionFeatureProvider"
   sources = [
     "../common/api/_permission_features.json",
@@ -21,7 +21,7 @@
 }
 
 json_features("test_manifest_features") {
-  feature_class = "ManifestFeature"
+  feature_type = "ManifestFeature"
   provider_class = "TestManifestFeatureProvider"
   sources = [
     "../common/api/_manifest_features.json",
@@ -29,7 +29,7 @@
 }
 
 json_features("test_behavior_features") {
-  feature_class = "BehaviorFeature"
+  feature_type = "BehaviorFeature"
   provider_class = "TestBehaviorFeatureProvider"
   sources = [
     "../common/api/_behavior_features.json",
diff --git a/extensions/test/test_extensions_client.cc b/extensions/test/test_extensions_client.cc
index 68233ccd..e71d89ed9 100644
--- a/extensions/test/test_extensions_client.cc
+++ b/extensions/test/test_extensions_client.cc
@@ -4,17 +4,18 @@
 
 #include "extensions/test/test_extensions_client.h"
 
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/files/file_path.h"
 #include "base/stl_util.h"
 #include "extensions/common/api/generated_schemas.h"
 #include "extensions/common/common_manifest_handlers.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/extensions_aliases.h"
-#include "extensions/common/features/api_feature.h"
-#include "extensions/common/features/behavior_feature.h"
 #include "extensions/common/features/feature_provider.h"
 #include "extensions/common/features/json_feature_provider_source.h"
-#include "extensions/common/features/manifest_feature.h"
-#include "extensions/common/features/permission_feature.h"
 #include "extensions/common/manifest_handler.h"
 #include "extensions/common/permissions/extensions_api_permissions.h"
 #include "extensions/common/permissions/permissions_info.h"
@@ -28,15 +29,6 @@
 
 namespace extensions {
 
-namespace {
-
-template <class FeatureClass>
-SimpleFeature* CreateFeature() {
-  return new FeatureClass;
-}
-
-}  // namespace
-
 TestExtensionsClient::TestExtensionsClient()
     : webstore_base_url_(extension_urls::kChromeWebstoreBaseURL),
       webstore_update_url_(extension_urls::kChromeWebstoreUpdateURL) {}
diff --git a/extensions/utility/unpacker.cc b/extensions/utility/unpacker.cc
index 3620f388..cec0db8 100644
--- a/extensions/utility/unpacker.cc
+++ b/extensions/utility/unpacker.cc
@@ -164,7 +164,7 @@
     return nullptr;
   }
 
-  if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!root->IsType(base::Value::Type::DICTIONARY)) {
     *error = errors::kInvalidManifest;
     return nullptr;
   }
diff --git a/gin/function_template.h b/gin/function_template.h
index 69ce797..a4c22f2 100644
--- a/gin/function_template.h
+++ b/gin/function_template.h
@@ -17,8 +17,6 @@
 
 namespace gin {
 
-class PerIsolateData;
-
 enum CreateFunctionTemplateFlags {
   HolderIsFirstArgument = 1 << 0,
 };
diff --git a/google_apis/drive/drive_api_parser_unittest.cc b/google_apis/drive/drive_api_parser_unittest.cc
index 712e0ec6..01f930c 100644
--- a/google_apis/drive/drive_api_parser_unittest.cc
+++ b/google_apis/drive/drive_api_parser_unittest.cc
@@ -19,7 +19,7 @@
       test_util::LoadJSONFile("drive/about.json");
   ASSERT_TRUE(document.get());
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, document->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, document->GetType());
   std::unique_ptr<AboutResource> resource(new AboutResource());
   EXPECT_TRUE(resource->Parse(*document));
 
@@ -36,7 +36,7 @@
       test_util::LoadJSONFile("drive/applist.json");
   ASSERT_TRUE(document.get());
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, document->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, document->GetType());
   std::unique_ptr<AppList> applist(new AppList);
   EXPECT_TRUE(applist->Parse(*document));
 
@@ -114,7 +114,7 @@
       test_util::LoadJSONFile("drive/filelist.json");
   ASSERT_TRUE(document.get());
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, document->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, document->GetType());
   std::unique_ptr<FileList> filelist(new FileList);
   EXPECT_TRUE(filelist->Parse(*document));
 
@@ -216,7 +216,7 @@
       test_util::LoadJSONFile("drive/changelist.json");
   ASSERT_TRUE(document.get());
 
-  ASSERT_EQ(base::Value::TYPE_DICTIONARY, document->GetType());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, document->GetType());
   std::unique_ptr<ChangeList> changelist(new ChangeList);
   EXPECT_TRUE(changelist->Parse(*document));
 
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc
index 0940d3c..58561d21 100644
--- a/google_apis/gaia/gaia_auth_fetcher.cc
+++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -53,7 +53,7 @@
   DCHECK(expires_in_secs);
 
   std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
-  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
+  if (!value.get() || value->GetType() != base::Value::Type::DICTIONARY)
     return false;
 
   base::DictionaryValue* dict =
@@ -481,7 +481,7 @@
   DCHECK(login_hint);
 
   std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
-  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
+  if (!value.get() || value->GetType() != base::Value::Type::DICTIONARY)
     return false;
 
   base::DictionaryValue* dict =
diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc
index 5fe7610..ed344082 100644
--- a/google_apis/gaia/gaia_oauth_client.cc
+++ b/google_apis/gaia/gaia_oauth_client.cc
@@ -271,7 +271,7 @@
     source->GetResponseAsString(&data);
     std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data);
     if (message_value.get() &&
-        message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+        message_value->IsType(base::Value::Type::DICTIONARY)) {
       response_dict.reset(
           static_cast<base::DictionaryValue*>(message_value.release()));
     }
diff --git a/google_apis/gaia/gaia_oauth_client_unittest.cc b/google_apis/gaia/gaia_oauth_client_unittest.cc
index 721291b..0b73277 100644
--- a/google_apis/gaia/gaia_oauth_client_unittest.cc
+++ b/google_apis/gaia/gaia_oauth_client_unittest.cc
@@ -372,7 +372,7 @@
   std::unique_ptr<base::Value> value =
       base::JSONReader::Read(kDummyFullUserInfoResult);
   DCHECK(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
   base::DictionaryValue* expected_result;
   value->GetAsDictionary(&expected_result);
 
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc
index 267388f..37f98e7 100644
--- a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc
+++ b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc
@@ -120,7 +120,7 @@
   std::string data;
   source->GetResponseAsString(&data);
   std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
-  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY)
+  if (!value.get() || value->GetType() != base::Value::Type::DICTIONARY)
     value.reset();
 
   return std::unique_ptr<base::DictionaryValue>(
diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl.h b/google_apis/gaia/oauth2_access_token_fetcher_impl.h
index c0529dd0..9e87596 100644
--- a/google_apis/gaia/oauth2_access_token_fetcher_impl.h
+++ b/google_apis/gaia/oauth2_access_token_fetcher_impl.h
@@ -25,7 +25,6 @@
 namespace net {
 class URLFetcher;
 class URLRequestContextGetter;
-class URLRequestStatus;
 }
 
 // Abstracts the details to get OAuth2 access token token from
diff --git a/google_apis/gaia/oauth2_api_call_flow.h b/google_apis/gaia/oauth2_api_call_flow.h
index c092099..846a907 100644
--- a/google_apis/gaia/oauth2_api_call_flow.h
+++ b/google_apis/gaia/oauth2_api_call_flow.h
@@ -13,9 +13,6 @@
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
-class GoogleServiceAuthError;
-class OAuth2MintTokenFlowTest;
-
 namespace net {
 class URLFetcher;
 class URLRequestContextGetter;
diff --git a/google_apis/gaia/oauth2_mint_token_flow.h b/google_apis/gaia/oauth2_mint_token_flow.h
index 1871cea..ff597cc 100644
--- a/google_apis/gaia/oauth2_mint_token_flow.h
+++ b/google_apis/gaia/oauth2_mint_token_flow.h
@@ -26,10 +26,6 @@
 class URLFetcher;
 }
 
-namespace net {
-class URLRequestContextGetter;
-}
-
 // IssueAdvice: messages to show to the user to get a user's approval.
 // The structure is as follows:
 // * Description 1
diff --git a/google_apis/gaia/oauth2_mint_token_flow_unittest.cc b/google_apis/gaia/oauth2_mint_token_flow_unittest.cc
index 7feb5905..85d0b82 100644
--- a/google_apis/gaia/oauth2_mint_token_flow_unittest.cc
+++ b/google_apis/gaia/oauth2_mint_token_flow_unittest.cc
@@ -180,7 +180,7 @@
   static base::DictionaryValue* ParseJson(const std::string& str) {
     std::unique_ptr<base::Value> value = base::JSONReader::Read(str);
     EXPECT_TRUE(value.get());
-    EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+    EXPECT_EQ(base::Value::Type::DICTIONARY, value->GetType());
     return static_cast<base::DictionaryValue*>(value.release());
   }
 
diff --git a/google_apis/gaia/oauth2_token_service_delegate.h b/google_apis/gaia/oauth2_token_service_delegate.h
index 3a6c00c..1f6477b 100644
--- a/google_apis/gaia/oauth2_token_service_delegate.h
+++ b/google_apis/gaia/oauth2_token_service_delegate.h
@@ -15,8 +15,6 @@
 class URLRequestContextGetter;
 }
 
-class SigninClient;
-
 // Abstract base class to fetch and maintain refresh tokens from various
 // entities. Concrete subclasses should implement RefreshTokenIsAvailable and
 // CreateAccessTokenFetcher properly.
diff --git a/google_apis/gcm/base/mcs_util.h b/google_apis/gcm/base/mcs_util.h
index e23c391..0b7973a6 100644
--- a/google_apis/gcm/base/mcs_util.h
+++ b/google_apis/gcm/base/mcs_util.h
@@ -21,10 +21,6 @@
 class Clock;
 }
 
-namespace net {
-class StreamSocket;
-}
-
 namespace gcm {
 
 // MCS Message tags.
diff --git a/google_apis/gcm/engine/connection_event_tracker.cc b/google_apis/gcm/engine/connection_event_tracker.cc
index d1e85b8..d8c33131 100644
--- a/google_apis/gcm/engine/connection_event_tracker.cc
+++ b/google_apis/gcm/engine/connection_event_tracker.cc
@@ -36,7 +36,7 @@
   if (completed_events_.size() == kMaxClientEvents) {
     // Don't let the completed events grow beyond the max.
     completed_events_.pop_front();
-    // TODO(harkness): Keep track of deleted events.
+    number_discarded_events_++;
   }
 
   // Current event is now completed, so add it to our list of completed events.
@@ -56,6 +56,7 @@
   // A completed connection means that the old client event data has now been
   // sent to GCM. Delete old data.
   completed_events_.clear();
+  number_discarded_events_ = 0;
 }
 
 void ConnectionEventTracker::ConnectionLoginFailed() {
@@ -79,6 +80,14 @@
 void ConnectionEventTracker::WriteToLoginRequest(
     mcs_proto::LoginRequest* request) {
   DCHECK(request);
+
+  // Add an event to represented the discarded events if needed.
+  if (number_discarded_events_ > 0) {
+    mcs_proto::ClientEvent* event = request->add_client_event();
+    event->set_type(mcs_proto::ClientEvent::DISCARDED_EVENTS);
+    event->set_number_discarded_events(number_discarded_events_);
+  }
+
   for (const mcs_proto::ClientEvent& event : completed_events_)
     request->add_client_event()->CopyFrom(event);
 }
diff --git a/google_apis/gcm/engine/connection_event_tracker.h b/google_apis/gcm/engine/connection_event_tracker.h
index 574ecaf5..2e18273 100644
--- a/google_apis/gcm/engine/connection_event_tracker.h
+++ b/google_apis/gcm/engine/connection_event_tracker.h
@@ -5,6 +5,8 @@
 #ifndef GOOGLE_APIS_GCM_ENGINE_CONNECTION_EVENT_TRACKER_H_
 #define GOOGLE_APIS_GCM_ENGINE_CONNECTION_EVENT_TRACKER_H_
 
+#include <stdint.h>
+
 #include <deque>
 
 #include "base/macros.h"
@@ -49,6 +51,10 @@
   // Current connection attempt.
   mcs_proto::ClientEvent current_event_;
 
+  // Number of events which were discarded due to exceeding the total number of
+  // events collected. This is sent to GCM to represent those events.
+  uint32_t number_discarded_events_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(ConnectionEventTracker);
 };
 
diff --git a/google_apis/gcm/engine/connection_factory_impl.h b/google_apis/gcm/engine/connection_factory_impl.h
index 63d3e2e0..ff7de143d 100644
--- a/google_apis/gcm/engine/connection_factory_impl.h
+++ b/google_apis/gcm/engine/connection_factory_impl.h
@@ -30,7 +30,6 @@
 
 namespace gcm {
 
-class ConnectionHandlerImpl;
 class GCMStatsRecorder;
 
 class GCM_EXPORT ConnectionFactoryImpl :
diff --git a/google_apis/gcm/engine/connection_factory_impl_unittest.cc b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
index 15e39d5..914f5a5 100644
--- a/google_apis/gcm/engine/connection_factory_impl_unittest.cc
+++ b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
@@ -599,14 +599,25 @@
   WaitForConnections();
 
   // There should be one failed client event for each failed connection, but
-  // there is a maximum cap of kMaxClientEvents, which is 30.
+  // there is a maximum cap of kMaxClientEvents, which is 30. There should also
+  // be a single event which records the events which were discarded.
   const auto client_events = GetClientEvents();
-  ASSERT_EQ(30, client_events.size());
+  ASSERT_EQ(31, client_events.size());
 
+  bool found_discarded_events = false;
   for (const auto& client_event : client_events) {
-    EXPECT_EQ(mcs_proto::ClientEvent::FAILED_CONNECTION, client_event.type());
-    EXPECT_EQ(net::ERR_CONNECTION_FAILED, client_event.error_code());
+    if (client_event.type() == mcs_proto::ClientEvent::DISCARDED_EVENTS) {
+      // There should only be one event for discarded events.
+      EXPECT_FALSE(found_discarded_events);
+      found_discarded_events = true;
+      // There should be 50-30=20 discarded events.
+      EXPECT_EQ(20U, client_event.number_discarded_events());
+    } else {
+      EXPECT_EQ(mcs_proto::ClientEvent::FAILED_CONNECTION, client_event.type());
+      EXPECT_EQ(net::ERR_CONNECTION_FAILED, client_event.error_code());
+    }
   }
+  EXPECT_TRUE(found_discarded_events);
 
   factory()->SetConnectResult(net::OK);
   WaitForConnections();
diff --git a/google_apis/gcm/engine/connection_handler.h b/google_apis/gcm/engine/connection_handler.h
index da12722..68720a9 100644
--- a/google_apis/gcm/engine/connection_handler.h
+++ b/google_apis/gcm/engine/connection_handler.h
@@ -26,9 +26,6 @@
 
 namespace gcm {
 
-class SocketInputStream;
-class SocketOutputStream;
-
 // Handles performing the protocol handshake and sending/receiving protobuf
 // messages. Note that no retrying or queueing is enforced at this layer.
 // Once a connection error is encountered, the ConnectionHandler will disconnect
diff --git a/google_apis/gcm/engine/connection_handler_impl.h b/google_apis/gcm/engine/connection_handler_impl.h
index 9b7d5a34..89fc972b 100644
--- a/google_apis/gcm/engine/connection_handler_impl.h
+++ b/google_apis/gcm/engine/connection_handler_impl.h
@@ -21,6 +21,9 @@
 
 namespace gcm {
 
+class SocketInputStream;
+class SocketOutputStream;
+
 class GCM_EXPORT ConnectionHandlerImpl : public ConnectionHandler {
  public:
   // |read_callback| will be invoked with the contents of any received protobuf
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index a5cf95b2..bca49db 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1539,6 +1539,14 @@
       'GL_COMPARE_REF_TO_TEXTURE',
     ],
   },
+  'TextureSrgbDecodeExt': {
+    'type': 'GLenum',
+    'is_complete': True,
+    'valid': [
+      'GL_DECODE_EXT',
+      'GL_SKIP_DECODE_EXT',
+    ],
+  },
   'TextureSwizzle': {
     'type': 'GLenum',
     'is_complete': True,
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
index 64a72fc..1775e7d 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
@@ -96,6 +96,7 @@
     uint32_t value);
 static std::string GetStringTextureSizedTextureFilterableInternalFormat(
     uint32_t value);
+static std::string GetStringTextureSrgbDecodeExt(uint32_t value);
 static std::string GetStringTextureStencilRenderableInternalFormat(
     uint32_t value);
 static std::string GetStringTextureSwizzle(uint32_t value);
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 9d6b4a5..5a6325c 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -4828,6 +4828,15 @@
                                            arraysize(string_table), value);
 }
 
+std::string GLES2Util::GetStringTextureSrgbDecodeExt(uint32_t value) {
+  static const EnumToString string_table[] = {
+      {GL_DECODE_EXT, "GL_DECODE_EXT"},
+      {GL_SKIP_DECODE_EXT, "GL_SKIP_DECODE_EXT"},
+  };
+  return GLES2Util::GetQualifiedEnumString(string_table,
+                                           arraysize(string_table), value);
+}
+
 std::string GLES2Util::GetStringTextureStencilRenderableInternalFormat(
     uint32_t value) {
   static const EnumToString string_table[] = {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 4e80bfb..ba7eccf 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -619,6 +619,12 @@
     }
   }
 
+  // The extension GL_EXT_texture_sRGB_decode is the same on desktop and GLES.
+  if (extensions.Contains("GL_EXT_texture_sRGB_decode") && !IsWebGLContext()) {
+    AddExtensionString("GL_EXT_texture_sRGB_decode");
+    validators_.texture_parameter.AddValue(GL_TEXTURE_SRGB_DECODE_EXT);
+  }
+
   // On desktop, GL_EXT_texture_sRGB is required regardless of GL version,
   // since the sRGB formats in OpenGL 3.0 Core do not support S3TC.
   // TODO(kainino): Support GL_EXT_texture_compression_s3tc_srgb once ratified.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 289898a..986dbba7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -6734,17 +6734,16 @@
     case GL_SAMPLER_BINDING:
       *num_written = 1;
       if (params) {
-        // TODO(vmiura): Need to implement this for ES3 clients.  WebGL 2 tracks
-        // this on the client side.
-        *params = 0;
+        DCHECK_LT(state_.active_texture_unit, state_.sampler_units.size());
+        Sampler* sampler =
+            state_.sampler_units[state_.active_texture_unit].get();
+        *params = sampler ? sampler->client_id() : 0;
       }
       return true;
     case GL_TRANSFORM_FEEDBACK_BINDING:
       *num_written = 1;
       if (params) {
-        // TODO(vmiura): Need to implement this for ES3 clients.  WebGL 2 tracks
-        // this on the client side.
-        *params = 0;
+        *params = state_.bound_transform_feedback->client_id();
       }
       return true;
     case GL_NUM_PROGRAM_BINARY_FORMATS:
@@ -7016,11 +7015,9 @@
   // At this point, the program's shaders may not be translated yet,
   // therefore, we may not find the hashed attribute name.
   // glBindAttribLocation call with original name is useless.
-  // So instead, we should simply cache the binding, and then call
+  // So instead, we simply cache the binding, and then call
   // Program::ExecuteBindAttribLocationCalls() right before link.
   program->SetAttribLocationBinding(name, static_cast<GLint>(index));
-  // TODO(zmo): Get rid of the following glBindAttribLocation call.
-  glBindAttribLocation(program->service_id(), index, name.c_str());
 }
 
 error::Error GLES2DecoderImpl::HandleBindAttribLocationBucket(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index 6ac0643e..e362fde 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -194,12 +194,14 @@
                                GLuint index,
                                GLint* size,
                                GLenum* type,
-                               std::string* name);
+                               std::string* name,
+                               int32_t* success);
 error::Error DoGetActiveUniform(GLuint program,
                                 GLuint index,
                                 GLint* size,
                                 GLenum* type,
-                                std::string* name);
+                                std::string* name,
+                                int32_t* success);
 error::Error DoGetActiveUniformBlockiv(GLuint program,
                                        GLuint index,
                                        GLenum pname,
@@ -305,7 +307,8 @@
 error::Error DoGetShaderPrecisionFormat(GLenum shadertype,
                                         GLenum precisiontype,
                                         GLint* range,
-                                        GLint* precision);
+                                        GLint* precision,
+                                        int32_t* success);
 error::Error DoGetShaderSource(GLuint shader, std::string* source);
 error::Error DoGetString(GLenum name, const char** result);
 error::Error DoGetSynciv(GLuint sync,
@@ -327,7 +330,8 @@
                                            GLuint index,
                                            GLsizei* size,
                                            GLenum* type,
-                                           std::string* name);
+                                           std::string* name,
+                                           int32_t* success);
 error::Error DoGetUniformBlockIndex(GLuint program,
                                     const char* name,
                                     GLint* index);
@@ -415,7 +419,8 @@
                           GLenum type,
                           GLsizei bufsize,
                           GLsizei* length,
-                          void* pixels);
+                          void* pixels,
+                          int32_t* success);
 error::Error DoReleaseShaderCompiler();
 error::Error DoRenderbufferStorage(GLenum target,
                                    GLenum internalformat,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 6933abf..82f0e00 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -838,18 +838,49 @@
                                                             GLuint index,
                                                             GLint* size,
                                                             GLenum* type,
-                                                            std::string* name) {
-  NOTIMPLEMENTED();
+                                                            std::string* name,
+                                                            int32_t* success) {
+  FlushErrors();
+
+  GLuint service_id = GetProgramServiceID(program, resources_);
+  GLint active_attribute_max_length = 0;
+  glGetProgramiv(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
+                 &active_attribute_max_length);
+  if (FlushErrors()) {
+    *success = 0;
+    return error::kNoError;
+  }
+
+  std::vector<char> name_buffer(active_attribute_max_length, 0);
+  glGetActiveAttrib(service_id, index, name_buffer.size(), nullptr, size, type,
+                    name_buffer.data());
+  *name = std::string(name_buffer.data());
+  *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
 
-error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniform(
-    GLuint program,
-    GLuint index,
-    GLint* size,
-    GLenum* type,
-    std::string* name) {
-  NOTIMPLEMENTED();
+error::Error GLES2DecoderPassthroughImpl::DoGetActiveUniform(GLuint program,
+                                                             GLuint index,
+                                                             GLint* size,
+                                                             GLenum* type,
+                                                             std::string* name,
+                                                             int32_t* success) {
+  FlushErrors();
+
+  GLuint service_id = GetProgramServiceID(program, resources_);
+  GLint active_uniform_max_length = 0;
+  glGetProgramiv(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH,
+                 &active_uniform_max_length);
+  if (FlushErrors()) {
+    *success = 0;
+    return error::kNoError;
+  }
+
+  std::vector<char> name_buffer(active_uniform_max_length, 0);
+  glGetActiveUniform(service_id, index, name_buffer.size(), nullptr, size, type,
+                     name_buffer.data());
+  *name = std::string(name_buffer.data());
+  *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -1118,8 +1149,11 @@
     GLenum shadertype,
     GLenum precisiontype,
     GLint* range,
-    GLint* precision) {
+    GLint* precision,
+    int32_t* success) {
+  FlushErrors();
   glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+  *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -1189,8 +1223,24 @@
     GLuint index,
     GLsizei* size,
     GLenum* type,
-    std::string* name) {
-  NOTIMPLEMENTED();
+    std::string* name,
+    int32_t* success) {
+  FlushErrors();
+
+  GLuint service_id = GetProgramServiceID(program, resources_);
+  GLint transform_feedback_varying_max_length = 0;
+  glGetProgramiv(service_id, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
+                 &transform_feedback_varying_max_length);
+  if (FlushErrors()) {
+    *success = 0;
+    return error::kNoError;
+  }
+
+  std::vector<char> name_buffer(transform_feedback_varying_max_length, 0);
+  glGetTransformFeedbackVarying(service_id, index, name_buffer.size(), nullptr,
+                                size, type, name_buffer.data());
+  *name = std::string(name_buffer.data());
+  *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
 
@@ -1451,9 +1501,12 @@
                                                        GLenum type,
                                                        GLsizei bufsize,
                                                        GLsizei* length,
-                                                       void* pixels) {
+                                                       void* pixels,
+                                                       int32_t* success) {
+  FlushErrors();
   glReadPixelsRobustANGLE(x, y, width, height, format, type, bufsize, length,
                           pixels);
+  *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
index 7f40591..d07e49b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
@@ -170,13 +170,13 @@
   }
 
   std::string name;
-  error::Error error =
-      DoGetActiveAttrib(program, index, &result->size, &result->type, &name);
+  error::Error error = DoGetActiveAttrib(
+      program, index, &result->size, &result->type, &name, &result->success);
   if (error != error::kNoError) {
+    result->success = 0;
     return error;
   }
 
-  result->success = 1;  // true.
   Bucket* bucket = CreateBucket(name_bucket_id);
   bucket->SetFromString(name.c_str());
   return error::kNoError;
@@ -202,13 +202,13 @@
   }
 
   std::string name;
-  error::Error error =
-      DoGetActiveUniform(program, index, &result->size, &result->type, &name);
+  error::Error error = DoGetActiveUniform(
+      program, index, &result->size, &result->type, &name, &result->success);
   if (error != error::kNoError) {
+    result->success = 0;
     return error;
   }
 
-  result->success = 1;  // true.
   Bucket* bucket = CreateBucket(name_bucket_id);
   bucket->SetFromString(name.c_str());
   return error::kNoError;
@@ -518,13 +518,13 @@
 
   GLint range[2] = {0, 0};
   GLint precision = 0;
-  error::Error error = DoGetShaderPrecisionFormat(shader_type, precision_type,
-                                                  range, &precision);
+  error::Error error = DoGetShaderPrecisionFormat(
+      shader_type, precision_type, range, &precision, &result->success);
   if (error != error::kNoError) {
+    result->success = 0;
     return error;
   }
 
-  result->success = 1;  // true
   result->min_range = range[0];
   result->max_range = range[1];
   result->precision = precision;
@@ -596,13 +596,13 @@
   GLsizei size = 0;
   GLenum type = 0;
   std::string name;
-  error::Error error =
-      DoGetTransformFeedbackVarying(program, index, &size, &type, &name);
+  error::Error error = DoGetTransformFeedbackVarying(
+      program, index, &size, &type, &name, &result->success);
   if (error != error::kNoError) {
+    result->success = 0;
     return error;
   }
 
-  result->success = 1;  // true.
   result->size = static_cast<int32_t>(size);
   result->type = static_cast<uint32_t>(type);
   Bucket* bucket = CreateBucket(name_bucket_id);
@@ -853,8 +853,9 @@
 
   GLsizei bufsize = buffer_size;
   GLsizei length = 0;
-  error::Error error =
-      DoReadPixels(x, y, width, height, format, type, bufsize, &length, pixels);
+  int32_t success = 0;
+  error::Error error = DoReadPixels(x, y, width, height, format, type, bufsize,
+                                    &length, pixels, &success);
   if (error != error::kNoError) {
     return error;
   }
@@ -876,7 +877,7 @@
   }
 
   if (result) {
-    result->success = 1;
+    result->success = success;
     result->row_length = static_cast<uint32_t>(width);
     result->num_rows = static_cast<uint32_t>(height);
   }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 211792c..5e02f31 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1534,6 +1534,56 @@
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
 
+TEST_P(GLES3DecoderTest, GetSamplerBinding) {
+  const GLuint kClientID = 12;
+  const GLuint kServiceID = 1012;
+  const GLuint kUnit = 0;
+  DoCreateSampler(kClientID, kServiceID);
+  DoBindSampler(kUnit, kClientID, kServiceID);
+
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+
+  typedef cmds::GetIntegerv::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::GetIntegerv cmd;
+  cmd.Init(GL_SAMPLER_BINDING, shared_memory_id_, shared_memory_offset_);
+  result->size = 0;
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(1, result->GetNumResults());
+  EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0]));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
+TEST_P(GLES3DecoderTest, GetTransformFeedbackBinding) {
+  const GLuint kClientID = 12;
+  const GLuint kServiceID = 1012;
+  const GLenum kTarget = GL_TRANSFORM_FEEDBACK;
+  DoCreateTransformFeedback(kClientID, kServiceID);
+  DoBindTransformFeedback(kTarget, kClientID, kServiceID);
+
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+
+  typedef cmds::GetIntegerv::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::GetIntegerv cmd;
+  cmd.Init(
+      GL_TRANSFORM_FEEDBACK_BINDING, shared_memory_id_, shared_memory_offset_);
+  result->size = 0;
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(1, result->GetNumResults());
+  EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0]));
+
+  DoBindTransformFeedback(kTarget, 0, kServiceDefaultTransformFeedbackId);
+  DoDeleteTransformFeedback(kClientID, kServiceID);
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
 // Test that processing with 0 entries does nothing.
 TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOneOfZero) {
   int num_processed = -1;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 5142a3f..ab8e52d0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -655,6 +655,64 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
 }
 
+void GLES2DecoderTestBase::DoCreateSampler(
+    GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, GenSamplers(1, _))
+      .WillOnce(SetArgPointee<1>(service_id));
+  cmds::GenSamplersImmediate* cmd =
+      GetImmediateAs<cmds::GenSamplersImmediate>();
+  GLuint temp = client_id;
+  cmd->Init(1, &temp);
+  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(*cmd, sizeof(temp)));
+}
+
+void GLES2DecoderTestBase::DoBindSampler(
+    GLuint unit, GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, BindSampler(unit, service_id))
+      .Times(1)
+      .RetiresOnSaturation();
+  cmds::BindSampler cmd;
+  cmd.Init(unit, client_id);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+}
+
+void GLES2DecoderTestBase::DoDeleteSampler(
+    GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, DeleteSamplers(1, Pointee(service_id)))
+      .Times(1)
+      .RetiresOnSaturation();
+  GenHelper<cmds::DeleteSamplersImmediate>(client_id);
+}
+
+void GLES2DecoderTestBase::DoCreateTransformFeedback(
+    GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, GenTransformFeedbacks(1, _))
+      .WillOnce(SetArgPointee<1>(service_id));
+  cmds::GenTransformFeedbacksImmediate* cmd =
+      GetImmediateAs<cmds::GenTransformFeedbacksImmediate>();
+  GLuint temp = client_id;
+  cmd->Init(1, &temp);
+  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(*cmd, sizeof(temp)));
+}
+
+void GLES2DecoderTestBase::DoBindTransformFeedback(
+    GLenum target, GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, BindTransformFeedback(target, service_id))
+      .Times(1)
+      .RetiresOnSaturation();
+  cmds::BindTransformFeedback cmd;
+  cmd.Init(target, client_id);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+}
+
+void GLES2DecoderTestBase::DoDeleteTransformFeedback(
+    GLuint client_id, GLuint service_id) {
+  EXPECT_CALL(*gl_, DeleteTransformFeedbacks(1, Pointee(service_id)))
+      .Times(1)
+      .RetiresOnSaturation();
+  GenHelper<cmds::DeleteTransformFeedbacksImmediate>(client_id);
+}
+
 void GLES2DecoderTestBase::SetBucketData(
     uint32_t bucket_id, const void* data, uint32_t data_size) {
   DCHECK(data || data_size == 0);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index ffb4dde7..d7f71fa 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -168,6 +168,8 @@
   void DoCreateProgram(GLuint client_id, GLuint service_id);
   void DoCreateShader(GLenum shader_type, GLuint client_id, GLuint service_id);
   void DoFenceSync(GLuint client_id, GLuint service_id);
+  void DoCreateSampler(GLuint client_id, GLuint service_id);
+  void DoCreateTransformFeedback(GLuint client_id, GLuint service_id);
 
   void SetBucketData(uint32_t bucket_id, const void* data, uint32_t data_size);
   void SetBucketAsCString(uint32_t bucket_id, const char* str);
@@ -269,6 +271,9 @@
   void EnsureRenderbufferBound(bool expect_bind);
   void DoBindTexture(GLenum target, GLuint client_id, GLuint service_id);
   void DoBindVertexArrayOES(GLuint client_id, GLuint service_id);
+  void DoBindSampler(GLuint unit, GLuint client_id, GLuint service_id);
+  void DoBindTransformFeedback(
+      GLenum target, GLuint client_id, GLuint service_id);
 
   bool DoIsBuffer(GLuint client_id);
   bool DoIsFramebuffer(GLuint client_id);
@@ -286,6 +291,8 @@
   void DoDeleteRenderbuffer(GLuint client_id, GLuint service_id);
   void DoDeleteShader(GLuint client_id, GLuint service_id);
   void DoDeleteTexture(GLuint client_id, GLuint service_id);
+  void DoDeleteSampler(GLuint client_id, GLuint service_id);
+  void DoDeleteTransformFeedback(GLuint client_id, GLuint service_id);
 
   void DoCompressedTexImage2D(GLenum target,
                               GLint level,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
index d32d866..b80fbc0f3 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_programs.cc
@@ -1868,9 +1868,6 @@
   const uint32_t kBucketId = 123;
   const GLint kLocation = 2;
   const char* kName = "testing";
-  EXPECT_CALL(*gl_,
-              BindAttribLocation(kServiceProgramId, kLocation, StrEq(kName)))
-      .Times(1);
   SetBucketAsCString(kBucketId, kName);
   BindAttribLocationBucket cmd;
   cmd.Init(client_program_id_, kLocation, kBucketId);
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
index 1223a22..2be3bab 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -344,6 +344,12 @@
 ValueValidator<GLenum> texture_parameter;
 ValueValidator<GLenum> texture_sized_color_renderable_internal_format;
 ValueValidator<GLenum> texture_sized_texture_filterable_internal_format;
+class TextureSrgbDecodeExtValidator {
+ public:
+  bool IsValid(const GLenum value) const;
+};
+TextureSrgbDecodeExtValidator texture_srgb_decode_ext;
+
 ValueValidator<GLenum> texture_stencil_renderable_internal_format;
 class TextureSwizzleValidator {
  public:
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 632eaeed..280f736 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -1216,6 +1216,16 @@
         GL_RGB_YCBCR_420V_CHROMIUM,
 };
 
+bool Validators::TextureSrgbDecodeExtValidator::IsValid(
+    const GLenum value) const {
+  switch (value) {
+    case GL_DECODE_EXT:
+    case GL_SKIP_DECODE_EXT:
+      return true;
+  }
+  return false;
+};
+
 static const GLenum
     valid_texture_stencil_renderable_internal_format_table_es3[] = {
         GL_STENCIL_INDEX8, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8,
diff --git a/gpu/command_buffer/service/sampler_manager.cc b/gpu/command_buffer/service/sampler_manager.cc
index c76cbf9..9b8ec3c4 100644
--- a/gpu/command_buffer/service/sampler_manager.cc
+++ b/gpu/command_buffer/service/sampler_manager.cc
@@ -30,8 +30,9 @@
       min_lod(-1000.0f) {
 }
 
-Sampler::Sampler(SamplerManager* manager, GLuint service_id)
+Sampler::Sampler(SamplerManager* manager, GLuint client_id, GLuint service_id)
     : manager_(manager),
+      client_id_(client_id),
       service_id_(service_id),
       deleted_(false) {
   DCHECK(manager);
@@ -147,7 +148,7 @@
 Sampler* SamplerManager::CreateSampler(GLuint client_id, GLuint service_id) {
   DCHECK_NE(0u, service_id);
   auto result = samplers_.insert(std::make_pair(client_id,
-      scoped_refptr<Sampler>(new Sampler(this, service_id))));
+      scoped_refptr<Sampler>(new Sampler(this, client_id, service_id))));
   DCHECK(result.second);
   return result.first->second.get();
 }
diff --git a/gpu/command_buffer/service/sampler_manager.h b/gpu/command_buffer/service/sampler_manager.h
index 8d85343..61c5116 100644
--- a/gpu/command_buffer/service/sampler_manager.h
+++ b/gpu/command_buffer/service/sampler_manager.h
@@ -37,9 +37,12 @@
 
 class GPU_EXPORT Sampler : public base::RefCounted<Sampler> {
  public:
-  Sampler(SamplerManager* manager, GLuint service_id);
+  Sampler(SamplerManager* manager, GLuint client_id, GLuint service_id);
 
-  // The service side OpenGL id of the texture.
+  GLuint client_id() const {
+    return client_id_;
+  }
+
   GLuint service_id() const {
     return service_id_;
   }
@@ -114,7 +117,7 @@
   // The manager that owns this Sampler.
   SamplerManager* manager_;
 
-    // The id of the texure
+  GLuint client_id_;
   GLuint service_id_;
 
   // Sampler parameters.
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 0d5bda6..e9307a24 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -1353,6 +1353,11 @@
       }
       swizzle_a_ = param;
       break;
+    case GL_TEXTURE_SRGB_DECODE_EXT:
+      if (!feature_info->validators()->texture_srgb_decode_ext.IsValid(param)) {
+        return GL_INVALID_ENUM;
+      }
+      break;
     case GL_TEXTURE_IMMUTABLE_FORMAT:
     case GL_TEXTURE_IMMUTABLE_LEVELS:
       return GL_INVALID_ENUM;
diff --git a/gpu/command_buffer/service/transform_feedback_manager.cc b/gpu/command_buffer/service/transform_feedback_manager.cc
index 5ceb1b7..7f20e973 100644
--- a/gpu/command_buffer/service/transform_feedback_manager.cc
+++ b/gpu/command_buffer/service/transform_feedback_manager.cc
@@ -11,16 +11,19 @@
 namespace gles2 {
 
 TransformFeedback::TransformFeedback(TransformFeedbackManager* manager,
+                                     GLuint client_id,
                                      GLuint service_id)
     : IndexedBufferBindingHost(
           manager->max_transform_feedback_separate_attribs(),
           manager->needs_emulation()),
       manager_(manager),
+      client_id_(client_id),
       service_id_(service_id),
       has_been_bound_(false),
       active_(false),
       paused_(false),
       primitive_mode_(GL_NONE) {
+  DCHECK_LE(0u, client_id);
   DCHECK_LT(0u, service_id);
 }
 
@@ -95,7 +98,7 @@
 TransformFeedback* TransformFeedbackManager::CreateTransformFeedback(
     GLuint client_id, GLuint service_id) {
   scoped_refptr<TransformFeedback> transform_feedback(
-      new TransformFeedback(this, service_id));
+      new TransformFeedback(this, client_id, service_id));
   auto result = transform_feedbacks_.insert(
       std::make_pair(client_id, transform_feedback));
   DCHECK(result.second);
diff --git a/gpu/command_buffer/service/transform_feedback_manager.h b/gpu/command_buffer/service/transform_feedback_manager.h
index 17b3507..edb5227 100644
--- a/gpu/command_buffer/service/transform_feedback_manager.h
+++ b/gpu/command_buffer/service/transform_feedback_manager.h
@@ -22,7 +22,8 @@
 // Info about TransformFeedbacks currently in the system.
 class GPU_EXPORT TransformFeedback : public IndexedBufferBindingHost {
  public:
-  TransformFeedback(TransformFeedbackManager* manager, GLuint service_id);
+  TransformFeedback(
+      TransformFeedbackManager* manager, GLuint client_id, GLuint service_id);
 
   // All the following functions do state update and call the underlying GL
   // function.  All validations have been done already and the GL function is
@@ -33,6 +34,10 @@
   void DoPauseTransformFeedback();
   void DoResumeTransformFeedback();
 
+  GLuint client_id() const {
+    return client_id_;
+  }
+
   GLuint service_id() const {
     return service_id_;
   }
@@ -59,6 +64,7 @@
   // The manager that owns this Buffer.
   TransformFeedbackManager* manager_;
 
+  GLuint client_id_;
   GLuint service_id_;
 
   bool has_been_bound_;
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index 833023f..9b31996 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -18,7 +18,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "12.04",
+  "version": "12.06",
   "entries": [
     {
       "id": 1,
@@ -1254,7 +1254,7 @@
     },
     {
       "id": 122,
-      "description": "GPU rasterization should only be enabled on NVIDIA Pascal and Maxwell, Intel Broadwell+, and AMD RX-R5 GPUs for now.",
+      "description": "GPU rasterization should only be enabled on NVIDIA Pascal and Maxwell, Intel Broadwell+, and AMD RX-R2 GPUs for now.",
       "cr_bugs": [643850],
       "os": {
         "type": "win"
@@ -1291,7 +1291,7 @@
             "type": "win",
             "version": {
               "op": ">=",
-              "value": "10.0"
+              "value": "8.1"
             }
           },
           "vendor_id": "0x8086",
@@ -1316,34 +1316,22 @@
             "type": "win",
             "version": {
               "op": ">=",
-              "value": "10.0"
+              "value": "8.1"
             }
           },
           "vendor_id": "0x1002",
-          "device_id": ["0x1309", "0x130a", "0x130c", "0x130d", "0x130e",
-                        "0x130f", "0x130f", "0x130f", "0x130f", "0x130f",
-                        "0x1313", "0x1313", "0x1313", "0x1313", "0x1315",
-                        "0x1315", "0x1315", "0x1315", "0x1315", "0x1316",
-                        "0x1318", "0x131c", "0x131d", "0x6600", "0x6604",
-                        "0x6604", "0x6605", "0x6605", "0x6610", "0x6610",
-                        "0x6610", "0x6611", "0x6617", "0x6640", "0x6646",
-                        "0x6646", "0x6647", "0x6647", "0x6658", "0x665d",
-                        "0x665f", "0x6660", "0x6660", "0x6663", "0x6664",
-                        "0x6665", "0x6665", "0x6665", "0x6667", "0x67b0",
-                        "0x67b0", "0x67b1", "0x67b1", "0x67b9", "0x67df",
-                        "0x67df", "0x67ef", "0x67ef", "0x67ef", "0x67ef",
-                        "0x6810", "0x6811", "0x6820", "0x6820", "0x6821",
-                        "0x6821", "0x682b", "0x6835", "0x6900", "0x6900",
-                        "0x6900", "0x6900", "0x6900", "0x6901", "0x6907",
-                        "0x6907", "0x6920", "0x6920", "0x6921", "0x6938",
-                        "0x6938", "0x6938", "0x6939", "0x6939", "0x6939",
-                        "0x7300", "0x7300", "0x7300", "0x7300", "0x9851",
-                        "0x9851", "0x9855", "0x9855", "0x9874", "0x9874",
-                        "0x9874", "0x9874", "0x9874", "0x9874", "0x9874",
-                        "0x9874", "0x9874", "0x9874", "0x9874", "0x9874",
-                        "0x9874", "0x9874", "0x9874", "0x9874", "0x9874",
-                        "0x9874", "0x9874", "0x9874", "0x9874", "0x98e4",
-                        "0x98e4", "0x98e4"]
+          "device_id": ["0x1309", "0x130a", "0x130b", "0x130c", "0x130d",
+                        "0x130e", "0x130f", "0x1313", "0x1315", "0x1316",
+                        "0x1318", "0x131b", "0x131c", "0x131d", "0x6600",
+                        "0x6604", "0x6605", "0x6610", "0x6611", "0x6617",
+                        "0x6640", "0x6646", "0x6647", "0x6647", "0x6658",
+                        "0x665d", "0x665f", "0x6660", "0x6663", "0x6664",
+                        "0x6665", "0x6667", "0x67b0", "0x67b1", "0x67b9",
+                        "0x67df", "0x67ef", "0x6810", "0x6811", "0x6820",
+                        "0x6821", "0x682b", "0x6835", "0x6900", "0x6901",
+                        "0x6907", "0x6920", "0x6921", "0x6938", "0x6939",
+                        "0x7300", "0x9851", "0x9852", "0x9853", "0x9854",
+                        "0x9855", "0x9856", "0x9874", "0x98e4"]
         }
       ]
     },
@@ -1419,6 +1407,22 @@
       "features": [
         "webgl2"
       ]
+    },
+    {
+      "id":129,
+      "description": "Intel drivers are buggy on Linux 2.x",
+      "cr_bugs": [662909],
+      "os": {
+        "type": "linux",
+        "version": {
+          "op": "<",
+          "value": "3.0"
+        }
+      },
+      "vendor_id": "0x8086",
+      "features": [
+        "all"
+      ]
     }
   ]
 }
diff --git a/gpu/ipc/BUILD.gn b/gpu/ipc/BUILD.gn
index 5c03cf5..61afbc6f 100644
--- a/gpu/ipc/BUILD.gn
+++ b/gpu/ipc/BUILD.gn
@@ -16,7 +16,12 @@
   }
 }
 
-source_set("command_buffer_sources") {
+if (is_component_build) {
+  link_target_type = "source_set"
+} else {
+  link_target_type = "static_library"
+}
+target(link_target_type, "command_buffer_sources") {
   visibility = [ "//gpu/*" ]
   sources = [
     "gpu_in_process_thread_service.cc",
diff --git a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
index 8e84b7dd..e85f23be 100644
--- a/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
+++ b/gpu/ipc/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc
@@ -47,22 +47,26 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     const DestructionCallback& callback) {
-  DCHECK_EQ(handle.native_pixmap_handle.fds.size(), 1u);
+  DCHECK_LE(handle.native_pixmap_handle.fds.size(), 1u);
 
   // GpuMemoryBufferImpl needs the FD to implement GetHandle() but
   // ui::ClientNativePixmapFactory::ImportFromHandle is expected to take
   // ownership of the FD passed in the handle so we have to dup it here in
   // order to pass a valid FD to the GpuMemoryBufferImpl ctor.
-  base::ScopedFD scoped_fd(
-      HANDLE_EINTR(dup(handle.native_pixmap_handle.fds[0].fd)));
-  if (!scoped_fd.is_valid()) {
-    PLOG(ERROR) << "dup";
-    return nullptr;
+  base::ScopedFD scoped_fd;
+  if (!handle.native_pixmap_handle.fds.empty()) {
+    scoped_fd.reset(HANDLE_EINTR(dup(handle.native_pixmap_handle.fds[0].fd)));
+    if (!scoped_fd.is_valid()) {
+      PLOG(ERROR) << "dup";
+      return nullptr;
+    }
   }
 
   gfx::NativePixmapHandle native_pixmap_handle;
-  native_pixmap_handle.fds.emplace_back(handle.native_pixmap_handle.fds[0].fd,
-                                        true /* auto_close */);
+  if (scoped_fd.is_valid()) {
+    native_pixmap_handle.fds.emplace_back(handle.native_pixmap_handle.fds[0].fd,
+                                          true /* auto_close */);
+  }
   native_pixmap_handle.planes = handle.native_pixmap_handle.planes;
   std::unique_ptr<ui::ClientNativePixmap> native_pixmap =
       ui::ClientNativePixmapFactory::GetInstance()->ImportFromHandle(
@@ -125,8 +129,10 @@
   gfx::GpuMemoryBufferHandle handle;
   handle.type = gfx::OZONE_NATIVE_PIXMAP;
   handle.id = id_;
-  handle.native_pixmap_handle.fds.emplace_back(fd_.get(),
-                                               false /* auto_close */);
+  if (fd_.is_valid()) {
+    handle.native_pixmap_handle.fds.emplace_back(fd_.get(),
+                                                 false /* auto_close */);
+  }
   handle.native_pixmap_handle.planes = planes_;
   return handle;
 }
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index 0e950cb..ded83fa 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -6,7 +6,8 @@
 module gpu.mojom;
 
 import "gpu/ipc/common/dx_diag_node.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
+import "mojo/common/version.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 // gpu::GPUInfo::GPUDevice
diff --git a/gpu/ipc/host/BUILD.gn b/gpu/ipc/host/BUILD.gn
new file mode 100644
index 0000000..9b0ba044
--- /dev/null
+++ b/gpu/ipc/host/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+source_set("host") {
+  sources = [
+    "gpu_memory_buffer_support.cc",
+    "gpu_memory_buffer_support.h",
+    "gpu_switches.cc",
+    "gpu_switches.h",
+  ]
+
+  deps = [
+    "//base",
+    "//gpu/ipc/common",
+    "//ui/gfx",
+    "//ui/gl",
+  ]
+}
diff --git a/gpu/ipc/host/gpu_memory_buffer_support.cc b/gpu/ipc/host/gpu_memory_buffer_support.cc
new file mode 100644
index 0000000..566a9a3a
--- /dev/null
+++ b/gpu/ipc/host/gpu_memory_buffer_support.cc
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "gpu/ipc/host/gpu_switches.h"
+#include "ui/gl/gl_switches.h"
+
+namespace gpu {
+
+bool AreNativeGpuMemoryBuffersEnabled() {
+  // Disable native buffers when using Mesa.
+  if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kUseGL) == gl::kGLImplementationOSMesaName) {
+    return false;
+  }
+
+#if defined(OS_MACOSX)
+  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kDisableNativeGpuMemoryBuffers);
+#else
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableNativeGpuMemoryBuffers);
+#endif
+}
+
+GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations() {
+  GpuMemoryBufferConfigurationSet configurations;
+
+  if (AreNativeGpuMemoryBuffersEnabled()) {
+    const gfx::BufferFormat kNativeFormats[] = {
+        gfx::BufferFormat::R_8,
+        gfx::BufferFormat::RG_88,
+        gfx::BufferFormat::BGR_565,
+        gfx::BufferFormat::RGBA_4444,
+        gfx::BufferFormat::RGBA_8888,
+        gfx::BufferFormat::BGRA_8888,
+        gfx::BufferFormat::UYVY_422,
+        gfx::BufferFormat::YVU_420,
+        gfx::BufferFormat::YUV_420_BIPLANAR};
+    const gfx::BufferUsage kNativeUsages[] = {
+        gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT,
+        gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
+        gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT};
+    for (auto& format : kNativeFormats) {
+      for (auto& usage : kNativeUsages) {
+        if (IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
+          configurations.insert(std::make_pair(format, usage));
+      }
+    }
+  }
+
+#if defined(USE_OZONE) || defined(OS_MACOSX)
+  // Disable native buffers only when using Mesa.
+  bool force_native_gpu_read_write_formats =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kUseGL) != gl::kGLImplementationOSMesaName;
+#else
+  bool force_native_gpu_read_write_formats = false;
+#endif
+  if (force_native_gpu_read_write_formats) {
+    const gfx::BufferFormat kGPUReadWriteFormats[] = {
+        gfx::BufferFormat::BGR_565,   gfx::BufferFormat::RGBA_8888,
+        gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::BGRA_8888,
+        gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::UYVY_422,
+        gfx::BufferFormat::YVU_420,   gfx::BufferFormat::YUV_420_BIPLANAR};
+    const gfx::BufferUsage kGPUReadWriteUsages[] = {gfx::BufferUsage::GPU_READ,
+                                                    gfx::BufferUsage::SCANOUT};
+    for (auto& format : kGPUReadWriteFormats) {
+      for (auto& usage : kGPUReadWriteUsages) {
+        if (IsNativeGpuMemoryBufferConfigurationSupported(format, usage))
+          configurations.insert(std::make_pair(format, usage));
+      }
+    }
+  }
+
+  return configurations;
+}
+
+}  // namespace gpu
diff --git a/gpu/ipc/host/gpu_memory_buffer_support.h b/gpu/ipc/host/gpu_memory_buffer_support.h
new file mode 100644
index 0000000..8dff0f6f
--- /dev/null
+++ b/gpu/ipc/host/gpu_memory_buffer_support.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_IPC_HOST_GPU_MEMORY_BUFFER_SUPPORT_H_
+#define GPU_IPC_HOST_GPU_MEMORY_BUFFER_SUPPORT_H_
+
+#include "base/containers/hash_tables.h"
+#include "base/hash.h"
+#include "ui/gfx/buffer_types.h"
+
+namespace gpu {
+
+using GpuMemoryBufferConfigurationKey =
+    std::pair<gfx::BufferFormat, gfx::BufferUsage>;
+using GpuMemoryBufferConfigurationSet =
+    base::hash_set<GpuMemoryBufferConfigurationKey>;
+
+}  // namespace gpu
+
+namespace BASE_HASH_NAMESPACE {
+
+template <>
+struct hash<gpu::GpuMemoryBufferConfigurationKey> {
+  size_t operator()(const gpu::GpuMemoryBufferConfigurationKey& key) const {
+    return base::HashInts(static_cast<int>(key.first),
+                          static_cast<int>(key.second));
+  }
+};
+
+}  // namespace BASE_HASH_NAMESPACE
+
+namespace gpu {
+
+bool AreNativeGpuMemoryBuffersEnabled();
+
+// Returns the set of supported configurations.
+GpuMemoryBufferConfigurationSet GetNativeGpuMemoryBufferConfigurations();
+
+}  // namespace gpu
+
+#endif  // GPU_IPC_HOST_GPU_MEMORY_BUFFER_SUPPORT_H_
diff --git a/gpu/ipc/host/gpu_switches.cc b/gpu/ipc/host/gpu_switches.cc
new file mode 100644
index 0000000..1834e3c6
--- /dev/null
+++ b/gpu/ipc/host/gpu_switches.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/ipc/host/gpu_switches.h"
+
+namespace switches {
+
+// Enable native GPU memory buffer support when available.
+const char kEnableNativeGpuMemoryBuffers[] = "enable-native-gpu-memory-buffers";
+
+// Disables native GPU memory buffer support.
+const char kDisableNativeGpuMemoryBuffers[] =
+    "disable-native-gpu-memory-buffers";
+
+}  // namespace switches
diff --git a/gpu/ipc/host/gpu_switches.h b/gpu/ipc/host/gpu_switches.h
new file mode 100644
index 0000000..7f205af
--- /dev/null
+++ b/gpu/ipc/host/gpu_switches.h
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines all the command-line switches used by gpu/ipc/host
+
+#ifndef GPU_IPC_HOST_GPU_SWITCHES_H_
+#define GPU_IPC_HOST_GPU_SWITCHES_H_
+
+namespace switches {
+
+extern const char kEnableNativeGpuMemoryBuffers[];
+extern const char kDisableNativeGpuMemoryBuffers[];
+
+}  // namespace switches
+
+#endif  // GPU_IPC_HOST_GPU_SWITCHES_H_
diff --git a/gpu/tools/compositor_model_bench/render_tree.cc b/gpu/tools/compositor_model_bench/render_tree.cc
index 642c5fe..752975c28 100644
--- a/gpu/tools/compositor_model_bench/render_tree.cc
+++ b/gpu/tools/compositor_model_bench/render_tree.cc
@@ -119,21 +119,21 @@
 
 std::string ValueTypeAsString(Value::Type type) {
   switch (type) {
-    case Value::TYPE_NULL:
+    case Value::Type::NONE:
       return "NULL";
-    case Value::TYPE_BOOLEAN:
+    case Value::Type::BOOLEAN:
       return "BOOLEAN";
-    case Value::TYPE_INTEGER:
+    case Value::Type::INTEGER:
       return "INTEGER";
-    case Value::TYPE_DOUBLE:
+    case Value::Type::DOUBLE:
       return "DOUBLE";
-    case Value::TYPE_STRING:
+    case Value::Type::STRING:
       return "STRING";
-    case Value::TYPE_BINARY:
+    case Value::Type::BINARY:
       return "BINARY";
-    case Value::TYPE_DICTIONARY:
+    case Value::Type::DICTIONARY:
       return "DICTIONARY";
-    case Value::TYPE_LIST:
+    case Value::Type::LIST:
       return "LIST";
     default:
       return "(UNKNOWN TYPE)";
@@ -180,12 +180,12 @@
 }
 
 bool InterpretCommonContents(const base::DictionaryValue& node, RenderNode* c) {
-  if (!VerifyDictionaryEntry(node, "layerID", Value::TYPE_INTEGER) ||
-      !VerifyDictionaryEntry(node, "width", Value::TYPE_INTEGER) ||
-      !VerifyDictionaryEntry(node, "height", Value::TYPE_INTEGER) ||
-      !VerifyDictionaryEntry(node, "drawsContent", Value::TYPE_BOOLEAN) ||
-      !VerifyDictionaryEntry(node, "targetSurfaceID", Value::TYPE_INTEGER) ||
-      !VerifyDictionaryEntry(node, "transform", Value::TYPE_LIST)) {
+  if (!VerifyDictionaryEntry(node, "layerID", Value::Type::INTEGER) ||
+      !VerifyDictionaryEntry(node, "width", Value::Type::INTEGER) ||
+      !VerifyDictionaryEntry(node, "height", Value::Type::INTEGER) ||
+      !VerifyDictionaryEntry(node, "drawsContent", Value::Type::BOOLEAN) ||
+      !VerifyDictionaryEntry(node, "targetSurfaceID", Value::Type::INTEGER) ||
+      !VerifyDictionaryEntry(node, "transform", Value::Type::LIST)) {
     return false;
   }
 
@@ -213,7 +213,7 @@
   }
   float transform_mat[16];
   for (int i = 0; i < 16; ++i) {
-    if (!VerifyListEntry(*transform, i, Value::TYPE_DOUBLE, "Transform"))
+    if (!VerifyListEntry(*transform, i, Value::Type::DOUBLE, "Transform"))
       return false;
     double el;
     transform->GetDouble(i, &el);
@@ -224,16 +224,16 @@
   if (!node.HasKey("tiles"))
     return true;
 
-  if (!VerifyDictionaryEntry(node, "tiles", Value::TYPE_DICTIONARY))
+  if (!VerifyDictionaryEntry(node, "tiles", Value::Type::DICTIONARY))
     return false;
   const base::DictionaryValue* tiles_dict;
   node.GetDictionary("tiles", &tiles_dict);
-  if (!VerifyDictionaryEntry(*tiles_dict, "dim", Value::TYPE_LIST))
+  if (!VerifyDictionaryEntry(*tiles_dict, "dim", Value::Type::LIST))
     return false;
   const base::ListValue* dim;
   tiles_dict->GetList("dim", &dim);
-  if (!VerifyListEntry(*dim, 0, Value::TYPE_INTEGER, "Tile dimension") ||
-      !VerifyListEntry(*dim, 1, Value::TYPE_INTEGER, "Tile dimension")) {
+  if (!VerifyListEntry(*dim, 0, Value::Type::INTEGER, "Tile dimension") ||
+      !VerifyListEntry(*dim, 1, Value::Type::INTEGER, "Tile dimension")) {
     return false;
   }
   int tile_width;
@@ -243,25 +243,25 @@
   dim->GetInteger(1, &tile_height);
   c->set_tile_height(tile_height);
 
-  if (!VerifyDictionaryEntry(*tiles_dict, "info", Value::TYPE_LIST))
+  if (!VerifyDictionaryEntry(*tiles_dict, "info", Value::Type::LIST))
     return false;
   const base::ListValue* tiles;
   tiles_dict->GetList("info", &tiles);
   for (unsigned int i = 0; i < tiles->GetSize(); ++i) {
-    if (!VerifyListEntry(*tiles, i, Value::TYPE_DICTIONARY, "Tile info"))
+    if (!VerifyListEntry(*tiles, i, Value::Type::DICTIONARY, "Tile info"))
       return false;
     const base::DictionaryValue* tdict;
     tiles->GetDictionary(i, &tdict);
 
-    if (!VerifyDictionaryEntry(*tdict, "x", Value::TYPE_INTEGER) ||
-        !VerifyDictionaryEntry(*tdict, "y", Value::TYPE_INTEGER)) {
+    if (!VerifyDictionaryEntry(*tdict, "x", Value::Type::INTEGER) ||
+        !VerifyDictionaryEntry(*tdict, "y", Value::Type::INTEGER)) {
       return false;
     }
     Tile t;
     tdict->GetInteger("x", &t.x);
     tdict->GetInteger("y", &t.y);
     if (tdict->HasKey("texID")) {
-      if (!VerifyDictionaryEntry(*tdict, "texID", Value::TYPE_INTEGER))
+      if (!VerifyDictionaryEntry(*tdict, "texID", Value::Type::INTEGER))
         return false;
       tdict->GetInteger("texID", &t.texID);
     } else {
@@ -273,9 +273,9 @@
 }
 
 bool InterpretCCData(const base::DictionaryValue& node, CCNode* c) {
-  if (!VerifyDictionaryEntry(node, "vertex_shader", Value::TYPE_STRING) ||
-      !VerifyDictionaryEntry(node, "fragment_shader", Value::TYPE_STRING) ||
-      !VerifyDictionaryEntry(node, "textures", Value::TYPE_LIST)) {
+  if (!VerifyDictionaryEntry(node, "vertex_shader", Value::Type::STRING) ||
+      !VerifyDictionaryEntry(node, "fragment_shader", Value::Type::STRING) ||
+      !VerifyDictionaryEntry(node, "textures", Value::Type::LIST)) {
     return false;
   }
   std::string vertex_shader_name, fragment_shader_name;
@@ -287,15 +287,15 @@
   const base::ListValue* textures;
   node.GetList("textures", &textures);
   for (unsigned int i = 0; i < textures->GetSize(); ++i) {
-    if (!VerifyListEntry(*textures, i, Value::TYPE_DICTIONARY, "Tex list"))
+    if (!VerifyListEntry(*textures, i, Value::Type::DICTIONARY, "Tex list"))
       return false;
     const base::DictionaryValue* tex;
     textures->GetDictionary(i, &tex);
 
-    if (!VerifyDictionaryEntry(*tex, "texID", Value::TYPE_INTEGER) ||
-        !VerifyDictionaryEntry(*tex, "height", Value::TYPE_INTEGER) ||
-        !VerifyDictionaryEntry(*tex, "width", Value::TYPE_INTEGER) ||
-        !VerifyDictionaryEntry(*tex, "format", Value::TYPE_STRING)) {
+    if (!VerifyDictionaryEntry(*tex, "texID", Value::Type::INTEGER) ||
+        !VerifyDictionaryEntry(*tex, "height", Value::Type::INTEGER) ||
+        !VerifyDictionaryEntry(*tex, "width", Value::Type::INTEGER) ||
+        !VerifyDictionaryEntry(*tex, "format", Value::Type::STRING)) {
       return false;
     }
     Texture t;
@@ -338,9 +338,9 @@
   if (!InterpretCommonContents(node, n.get()))
     return nullptr;
 
-  if (!VerifyDictionaryEntry(node, "type", Value::TYPE_STRING) ||
-      !VerifyDictionaryEntry(node, "skipsDraw", Value::TYPE_BOOLEAN) ||
-      !VerifyDictionaryEntry(node, "children", Value::TYPE_LIST)) {
+  if (!VerifyDictionaryEntry(node, "type", Value::Type::STRING) ||
+      !VerifyDictionaryEntry(node, "skipsDraw", Value::Type::BOOLEAN) ||
+      !VerifyDictionaryEntry(node, "children", Value::Type::LIST)) {
     return nullptr;
   }
 
@@ -371,7 +371,7 @@
   if (!InterpretCommonContents(node, n.get()))
     return nullptr;
 
-  if (!VerifyDictionaryEntry(node, "type", Value::TYPE_STRING))
+  if (!VerifyDictionaryEntry(node, "type", Value::Type::STRING))
     return nullptr;
 
   std::string type;
@@ -390,7 +390,7 @@
   if (!InterpretCommonContents(node, n.get()))
     return nullptr;
 
-  if (!VerifyDictionaryEntry(node, "type", Value::TYPE_STRING))
+  if (!VerifyDictionaryEntry(node, "type", Value::Type::STRING))
     return nullptr;
 
   std::string type;
@@ -409,7 +409,7 @@
   if (!InterpretCommonContents(node, n.get()))
     return nullptr;
 
-  if (!VerifyDictionaryEntry(node, "type", Value::TYPE_STRING))
+  if (!VerifyDictionaryEntry(node, "type", Value::Type::STRING))
     return nullptr;
 
   std::string type;
@@ -423,7 +423,7 @@
 }
 
 std::unique_ptr<RenderNode> InterpretNode(const base::DictionaryValue& node) {
-  if (!VerifyDictionaryEntry(node, "type", Value::TYPE_STRING))
+  if (!VerifyDictionaryEntry(node, "type", Value::Type::STRING))
     return nullptr;
 
   std::string type;
diff --git a/headless/lib/browser/devtools_api/domain_cc.template b/headless/lib/browser/devtools_api/domain_cc.template
index 3345a4a..dc04d6e 100644
--- a/headless/lib/browser/devtools_api/domain_cc.template
+++ b/headless/lib/browser/devtools_api/domain_cc.template
@@ -103,7 +103,7 @@
   if (callback.is_null())
     return;
   // This is an error response.
-  if (response.IsType(base::Value::TYPE_NULL)) {
+  if (response.IsType(base::Value::Type::NONE)) {
     callback.Run(nullptr);
     return;
   }
diff --git a/headless/lib/browser/headless_browser_main_parts.h b/headless/lib/browser/headless_browser_main_parts.h
index 97bc9e9e..e29b92a 100644
--- a/headless/lib/browser/headless_browser_main_parts.h
+++ b/headless/lib/browser/headless_browser_main_parts.h
@@ -12,7 +12,6 @@
 
 namespace headless {
 
-class HeadlessBrowserContextImpl;
 class HeadlessBrowserImpl;
 
 class HeadlessBrowserMainParts : public content::BrowserMainParts {
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index 8e9651e9..511083bf 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -10,8 +10,6 @@
 namespace headless {
 
 class HeadlessBrowserImpl;
-class HeadlessBrowserMainParts;
-class HeadlessDevToolsManagerDelegate;
 
 class HeadlessContentBrowserClient : public content::ContentBrowserClient {
  public:
diff --git a/headless/lib/browser/headless_devtools_manager_delegate.h b/headless/lib/browser/headless_devtools_manager_delegate.h
index 4d43fc7..3b800d53 100644
--- a/headless/lib/browser/headless_devtools_manager_delegate.h
+++ b/headless/lib/browser/headless_devtools_manager_delegate.h
@@ -17,7 +17,6 @@
 namespace headless {
 class HeadlessBrowserImpl;
 class HeadlessBrowserContext;
-class HeadlessWebContentsImpl;
 
 class HeadlessDevToolsManagerDelegate
     : public content::DevToolsManagerDelegate {
diff --git a/headless/lib/browser/headless_screen.h b/headless/lib/browser/headless_screen.h
index ed9e388f..8c5cb98 100644
--- a/headless/lib/browser/headless_screen.h
+++ b/headless/lib/browser/headless_screen.h
@@ -19,7 +19,6 @@
 
 namespace aura {
 class Window;
-class WindowParentingClient;
 class WindowTreeHost;
 }
 
diff --git a/headless/lib/browser/headless_url_request_context_getter.h b/headless/lib/browser/headless_url_request_context_getter.h
index 166f6a9..e1bee1ea 100644
--- a/headless/lib/browser/headless_url_request_context_getter.h
+++ b/headless/lib/browser/headless_url_request_context_getter.h
@@ -19,18 +19,9 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_job_factory.h"
 
-namespace base {
-class MessageLoop;
-}
-
 namespace net {
 class HostResolver;
-class MappedHostResolver;
-class NetworkDelegate;
-class NetLog;
 class ProxyConfigService;
-class ProxyService;
-class URLRequestContextStorage;
 }
 
 namespace headless {
diff --git a/headless/lib/browser/headless_web_contents_impl.h b/headless/lib/browser/headless_web_contents_impl.h
index 2e9a9b9..a18a46a 100644
--- a/headless/lib/browser/headless_web_contents_impl.h
+++ b/headless/lib/browser/headless_web_contents_impl.h
@@ -20,7 +20,6 @@
 }
 
 namespace content {
-class BrowserContext;
 class DevToolsAgentHost;
 class WebContents;
 }
@@ -30,7 +29,6 @@
 }
 
 namespace headless {
-class HeadlessDevToolsHostImpl;
 class HeadlessBrowserImpl;
 class WebContentsObserverAdapter;
 
diff --git a/headless/lib/headless_content_main_delegate.h b/headless/lib/headless_content_main_delegate.h
index 00a20cf..6aa3e5e 100644
--- a/headless/lib/headless_content_main_delegate.h
+++ b/headless/lib/headless_content_main_delegate.h
@@ -12,10 +12,6 @@
 #include "content/public/app/content_main_delegate.h"
 #include "headless/lib/headless_content_client.h"
 
-namespace content {
-class BrowserContext;
-}
-
 namespace headless {
 
 class HeadlessBrowserImpl;
diff --git a/headless/public/domains/types_unittest.cc b/headless/public/domains/types_unittest.cc
index 860f69ba5..db07df1 100644
--- a/headless/public/domains/types_unittest.cc
+++ b/headless/public/domains/types_unittest.cc
@@ -204,11 +204,11 @@
           .SetValue(std::move(value))
           .Build());
   EXPECT_TRUE(object);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, object->GetValue()->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, object->GetValue()->GetType());
 
   std::unique_ptr<accessibility::AXValue> clone(object->Clone());
   EXPECT_TRUE(clone);
-  EXPECT_EQ(base::Value::TYPE_INTEGER, clone->GetValue()->GetType());
+  EXPECT_EQ(base::Value::Type::INTEGER, clone->GetValue()->GetType());
 
   int clone_value;
   EXPECT_TRUE(clone->GetValue()->GetAsInteger(&clone_value));
diff --git a/headless/public/util/deterministic_http_protocol_handler.h b/headless/public/util/deterministic_http_protocol_handler.h
index da0a70d..89d76cb 100644
--- a/headless/public/util/deterministic_http_protocol_handler.h
+++ b/headless/public/util/deterministic_http_protocol_handler.h
@@ -17,7 +17,6 @@
 
 namespace headless {
 class DeterministicDispatcher;
-class HeadlessBrowserContext;
 
 // A deterministic protocol handler.  Requests made to this protocol handler
 // will return in order of creation, regardless of what order the network
diff --git a/headless/public/util/http_url_fetcher.h b/headless/public/util/http_url_fetcher.h
index 2b17287..aa9cdb6 100644
--- a/headless/public/util/http_url_fetcher.h
+++ b/headless/public/util/http_url_fetcher.h
@@ -10,7 +10,6 @@
 
 namespace net {
 class URLRequestContext;
-class URLRequestJobFactory;
 }  // namespace
 
 namespace headless {
diff --git a/headless/public/util/in_memory_request_job.h b/headless/public/util/in_memory_request_job.h
index 1526c9a..7810422 100644
--- a/headless/public/util/in_memory_request_job.h
+++ b/headless/public/util/in_memory_request_job.h
@@ -9,11 +9,6 @@
 #include "headless/public/util/in_memory_protocol_handler.h"
 #include "net/url_request/url_request_job.h"
 
-namespace net {
-class StringIOBuffer;
-class DrainableIOBuffer;
-}
-
 namespace headless {
 class InMemoryRequestJob : public net::URLRequestJob {
  public:
diff --git a/headless/public/util/testing/generic_url_request_mocks.h b/headless/public/util/testing/generic_url_request_mocks.h
index 7dc683f..fb8f0be 100644
--- a/headless/public/util/testing/generic_url_request_mocks.h
+++ b/headless/public/util/testing/generic_url_request_mocks.h
@@ -16,14 +16,6 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job_factory.h"
 
-namespace net {
-class URLRequestJob;
-}  // namespace net
-
-namespace htmlrender_webkit_headless_proto {
-class Resource;
-}  // htmlrender_webkit_headless_proto net
-
 namespace headless {
 
 class MockGenericURLRequestJobDelegate : public GenericURLRequestJob::Delegate {
diff --git a/ios/build/bots/chromium.fyi/ios-simulator.json b/ios/build/bots/chromium.fyi/ios-simulator.json
new file mode 100644
index 0000000..cd3a9d5
--- /dev/null
+++ b/ios/build/bots/chromium.fyi/ios-simulator.json
@@ -0,0 +1,74 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Runs tests on @3x, @2x, 64-bit, 32-bit, phone, tablet, iOS 10, and iOS 9."
+  ],
+  "xcode version": "8.0",
+  "gn_args": [
+    "additional_target_cpus=[\"x86\"]",
+    "goma_dir=\"$(goma_dir)\"",
+    "ios_enable_code_signing=false",
+    "is_component_build=false",
+    "is_debug=true",
+    "target_cpu=\"x64\"",
+    "target_os=\"ios\"",
+    "use_goma=true"
+  ],
+  "compiler": "ninja",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator10.0",
+  "tests": [
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 6s Plus",
+      "os": "10.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 6s",
+      "os": "10.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 5",
+      "os": "10.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPad Air 2",
+      "os": "10.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPad Retina",
+      "os": "10.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 6s Plus",
+      "os": "9.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 6s",
+      "os": "9.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPhone 5",
+      "os": "9.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPad Air 2",
+      "os": "9.0"
+    },
+    {
+      "include": "common_tests.json",
+      "device type": "iPad Retina",
+      "os": "9.0"
+    }
+  ]
+}
diff --git a/ios/chrome/BUILD.gn b/ios/chrome/BUILD.gn
index 05ccaa8..66eff91 100644
--- a/ios/chrome/BUILD.gn
+++ b/ios/chrome/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/browser/net:unit_tests",
     "//ios/chrome/browser/passwords:unit_tests",
     "//ios/chrome/browser/reading_list:unit_tests",
+    "//ios/chrome/browser/signin:unit_tests",
     "//ios/chrome/browser/snapshots:unit_tests",
     "//ios/chrome/browser/ssl:unit_tests",
     "//ios/chrome/browser/suggestions:unit_tests",
@@ -51,6 +52,7 @@
     "//ios/chrome/common:unit_tests",
     "//ios/chrome/test:unit_tests",
     "//ios/chrome/test/base:unit_tests",
+    "//ios/web/public/image_fetcher:unit_tests",
   ]
 
   assert_no_deps = ios_assert_no_deps
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index f695ea5..4d197e3 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -13,6 +13,7 @@
   ]
 
   deps = [
+    ":resources",
     "//base",
   ]
 
@@ -49,3 +50,13 @@
     ":tests_hook",
   ]
 }
+
+bundle_data("resources") {
+  visibility = [ ":app" ]
+  sources = [
+    "resources/SafeModeCrashingModules.plist",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/{{source_file_part}}",
+  ]
+}
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index 484ebf7..59974ad6 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -2,9 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chrome_build.gni")
+import("//build/config/ios/rules.gni")
 import("//build/config/locales.gni")
-import("//tools/grit/grit_rule.gni")
 import("//ios/chrome/app/resources/ios_chrome_repack.gni")
+import("//ios/public/provider/chrome/browser/build_config.gni")
+import("//tools/grit/grit_rule.gni")
 
 grit("resources") {
   source = "ios_resources.grd"
@@ -59,6 +62,24 @@
   copy_data_to_bundle = true
 }
 
+bundle_data("chromium_icons") {
+  sources = [
+    "chromium/Icon-120.png",
+    "chromium/Icon-152.png",
+    "chromium/Icon-167.png",
+    "chromium/Icon-180.png",
+    "chromium/Icon-29.png",
+    "chromium/Icon-40.png",
+    "chromium/Icon-58.png",
+    "chromium/Icon-76.png",
+    "chromium/Icon-80.png",
+    "chromium/Icon-87.png",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/{{source_file_part}}",
+  ]
+}
+
 bundle_data("launchscreen_assets") {
   sources = [
     "launchscreen_images.xcassets/Contents.json",
@@ -71,3 +92,57 @@
     "{{bundle_resources_dir}}/{{source_file_part}}",
   ]
 }
+
+bundle_data_xib("launchscreen_xib") {
+  source = "LaunchScreen.xib"
+  deps = [
+    ios_launchscreen_assets_target,
+  ]
+}
+
+bundle_data("terms_of_service_bundle") {
+  sources = [
+    "//components/resources/terms/terms_ar.html",
+    "//components/resources/terms/terms_bg.html",
+    "//components/resources/terms/terms_ca.html",
+    "//components/resources/terms/terms_cs.html",
+    "//components/resources/terms/terms_da.html",
+    "//components/resources/terms/terms_de.html",
+    "//components/resources/terms/terms_el.html",
+    "//components/resources/terms/terms_en-GB.html",
+    "//components/resources/terms/terms_en.html",
+    "//components/resources/terms/terms_es-419.html",
+    "//components/resources/terms/terms_es.html",
+    "//components/resources/terms/terms_fa.html",
+    "//components/resources/terms/terms_fi.html",
+    "//components/resources/terms/terms_fr.html",
+    "//components/resources/terms/terms_he.html",
+    "//components/resources/terms/terms_hi.html",
+    "//components/resources/terms/terms_hr.html",
+    "//components/resources/terms/terms_hu.html",
+    "//components/resources/terms/terms_id.html",
+    "//components/resources/terms/terms_it.html",
+    "//components/resources/terms/terms_ja.html",
+    "//components/resources/terms/terms_ko.html",
+    "//components/resources/terms/terms_lt.html",
+    "//components/resources/terms/terms_nb.html",
+    "//components/resources/terms/terms_nl.html",
+    "//components/resources/terms/terms_pl.html",
+    "//components/resources/terms/terms_pt-BR.html",
+    "//components/resources/terms/terms_pt-PT.html",
+    "//components/resources/terms/terms_ro.html",
+    "//components/resources/terms/terms_ru.html",
+    "//components/resources/terms/terms_sk.html",
+    "//components/resources/terms/terms_sr.html",
+    "//components/resources/terms/terms_sv.html",
+    "//components/resources/terms/terms_th.html",
+    "//components/resources/terms/terms_tr.html",
+    "//components/resources/terms/terms_uk.html",
+    "//components/resources/terms/terms_vi.html",
+    "//components/resources/terms/terms_zh-CN.html",
+    "//components/resources/terms/terms_zh-TW.html",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/{{source_file_part}}",
+  ]
+}
diff --git a/ios/chrome/app/resources/LaunchScreen.xib b/ios/chrome/app/resources/LaunchScreen.xib
new file mode 100644
index 0000000..c25ae05
--- /dev/null
+++ b/ios/chrome/app/resources/LaunchScreen.xib
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
+        <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
+        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <viewController id="Ssz-5V-cv2">
+            <layoutGuides>
+                <viewControllerLayoutGuide type="top" id="cfQ-4H-yja"/>
+                <viewControllerLayoutGuide type="bottom" id="TrJ-rv-PCn"/>
+            </layoutGuides>
+            <view key="view" contentMode="scaleToFill" id="tRS-Cx-RH3">
+                <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launchscreen_app_logo" translatesAutoresizingMaskIntoConstraints="NO" id="K7H-Iv-QNk">
+                        <rect key="frame" x="204" y="204" width="192" height="192"/>
+                        <constraints>
+                            <constraint firstAttribute="width" relation="lessThanOrEqual" constant="192" id="Ybu-iB-Ad4"/>
+                            <constraint firstAttribute="height" relation="lessThanOrEqual" constant="192" id="gGV-z8-zNx"/>
+                            <constraint firstAttribute="width" secondItem="K7H-Iv-QNk" secondAttribute="height" multiplier="1:1" id="ion-RZ-6NS"/>
+                        </constraints>
+                    </imageView>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launchscreen_brand_name" translatesAutoresizingMaskIntoConstraints="NO" id="bAU-qs-X5w">
+                        <rect key="frame" x="247" y="545" width="107" height="35"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="107" id="0Mf-i7-mvo"/>
+                            <constraint firstAttribute="height" constant="35" id="7Gs-G7-OR2"/>
+                        </constraints>
+                    </imageView>
+                </subviews>
+                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                <constraints>
+                    <constraint firstItem="K7H-Iv-QNk" firstAttribute="width" secondItem="tRS-Cx-RH3" secondAttribute="width" multiplier="0.381966" id="3hJ-Yo-1Tg"/>
+                    <constraint firstItem="K7H-Iv-QNk" firstAttribute="height" secondItem="tRS-Cx-RH3" secondAttribute="height" multiplier="0.381966" id="962-tL-cOd"/>
+                    <constraint firstItem="K7H-Iv-QNk" firstAttribute="centerY" secondItem="tRS-Cx-RH3" secondAttribute="centerY" id="N7G-n6-9nU">
+                        <variation key="heightClass=compact" constant="-10"/>
+                    </constraint>
+                    <constraint firstItem="K7H-Iv-QNk" firstAttribute="centerX" secondItem="tRS-Cx-RH3" secondAttribute="centerX" id="V5t-fF-KbQ"/>
+                    <constraint firstItem="bAU-qs-X5w" firstAttribute="centerX" secondItem="K7H-Iv-QNk" secondAttribute="centerX" id="f8n-OV-luE"/>
+                    <constraint firstAttribute="bottomMargin" secondItem="bAU-qs-X5w" secondAttribute="bottom" constant="20" id="p2z-ft-dzH"/>
+                </constraints>
+                <variation key="default">
+                    <mask key="constraints">
+                        <exclude reference="3hJ-Yo-1Tg"/>
+                        <exclude reference="962-tL-cOd"/>
+                    </mask>
+                </variation>
+                <variation key="heightClass=compact">
+                    <mask key="constraints">
+                        <include reference="962-tL-cOd"/>
+                    </mask>
+                </variation>
+                <variation key="heightClass=regular-widthClass=compact">
+                    <mask key="constraints">
+                        <include reference="3hJ-Yo-1Tg"/>
+                    </mask>
+                </variation>
+            </view>
+            <point key="canvasLocation" x="548" y="1086"/>
+        </viewController>
+    </objects>
+    <resources>
+        <image name="launchscreen_app_logo" width="192" height="192"/>
+        <image name="launchscreen_brand_name" width="107" height="35"/>
+    </resources>
+</document>
diff --git a/ios/chrome/app/resources/SafeModeCrashingModules.plist b/ios/chrome/app/resources/SafeModeCrashingModules.plist
new file mode 100644
index 0000000..074fce55
--- /dev/null
+++ b/ios/chrome/app/resources/SafeModeCrashingModules.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>StartupCrashModules</key>
+  <dict>
+    <key>/Library/MobileSubstrate/DynamicLibraries/CDM.dylib</key>
+    <dict>
+      <key>ModuleFriendlyName</key>
+      <string>Chrome Download Manager</string>
+    </dict>
+  </dict>
+</dict>
+</plist>
diff --git a/ios/chrome/app/resources/chromium/Icon-120.png b/ios/chrome/app/resources/chromium/Icon-120.png
new file mode 100644
index 0000000..5a59378
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-120.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-152.png b/ios/chrome/app/resources/chromium/Icon-152.png
new file mode 100644
index 0000000..7c4f8b9
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-152.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-167.png b/ios/chrome/app/resources/chromium/Icon-167.png
new file mode 100644
index 0000000..2f6b743
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-167.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-180.png b/ios/chrome/app/resources/chromium/Icon-180.png
new file mode 100644
index 0000000..efbdd17
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-180.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-29.png b/ios/chrome/app/resources/chromium/Icon-29.png
new file mode 100644
index 0000000..57c58c9
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-29.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-40.png b/ios/chrome/app/resources/chromium/Icon-40.png
new file mode 100644
index 0000000..0406667
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-40.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-58.png b/ios/chrome/app/resources/chromium/Icon-58.png
new file mode 100644
index 0000000..2a7b8547
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-58.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-76.png b/ios/chrome/app/resources/chromium/Icon-76.png
new file mode 100644
index 0000000..4b354006
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-76.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-80.png b/ios/chrome/app/resources/chromium/Icon-80.png
new file mode 100644
index 0000000..c78cba89
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-80.png
Binary files differ
diff --git a/ios/chrome/app/resources/chromium/Icon-87.png b/ios/chrome/app/resources/chromium/Icon-87.png
new file mode 100644
index 0000000..4c038d3d
--- /dev/null
+++ b/ios/chrome/app/resources/chromium/Icon-87.png
Binary files differ
diff --git a/ios/chrome/app/safe_mode/BUILD.gn b/ios/chrome/app/safe_mode/BUILD.gn
new file mode 100644
index 0000000..17b18e6b
--- /dev/null
+++ b/ios/chrome/app/safe_mode/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+bundle_data("resources") {
+  sources = [
+    "resources/fatal_error.png",
+    "resources/fatal_error@2x.png",
+    "resources/fatal_error@3x.png",
+  ]
+  outputs = [
+    "{{bundle_resources_dir}}/{{source_file_part}}",
+  ]
+}
diff --git a/ios/chrome/app/safe_mode/resources/fatal_error.png b/ios/chrome/app/safe_mode/resources/fatal_error.png
new file mode 100644
index 0000000..9d435ed
--- /dev/null
+++ b/ios/chrome/app/safe_mode/resources/fatal_error.png
Binary files differ
diff --git a/ios/chrome/app/safe_mode/resources/fatal_error@2x.png b/ios/chrome/app/safe_mode/resources/fatal_error@2x.png
new file mode 100644
index 0000000..46f545f
--- /dev/null
+++ b/ios/chrome/app/safe_mode/resources/fatal_error@2x.png
Binary files differ
diff --git a/ios/chrome/app/safe_mode/resources/fatal_error@3x.png b/ios/chrome/app/safe_mode/resources/fatal_error@3x.png
new file mode 100644
index 0000000..75629884
--- /dev/null
+++ b/ios/chrome/app/safe_mode/resources/fatal_error@3x.png
Binary files differ
diff --git a/ios/chrome/app/safe_mode_crashing_modules_config.mm b/ios/chrome/app/safe_mode_crashing_modules_config.mm
index 9106d65..e9ec67c 100644
--- a/ios/chrome/app/safe_mode_crashing_modules_config.mm
+++ b/ios/chrome/app/safe_mode_crashing_modules_config.mm
@@ -32,7 +32,7 @@
   self = [super init];
   if (self) {
     NSString* configPath =
-        [[NSBundle mainBundle] pathForResource:@"CrashingModules"
+        [[NSBundle mainBundle] pathForResource:@"SafeModeCrashingModules"
                                         ofType:@"plist"];
     _configuration.reset(
         [[NSDictionary alloc] initWithContentsOfFile:configPath]);
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index eb62cf5..4d8d1a6 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -678,7 +678,7 @@
         Google Apps
       </message>
       <message name="IDS_IOS_HISTORY_HAS_SYNCED_RESULTS" desc="The notification at the top of the history page indicating that it is showing visits synced from other devices.">
-        Showing history from your signed-in devices. <ph name="BEGIN_LINK">BEGIN_LINK</ph>Learn more<ph name="END_LINK">END_LINK</ph>
+        Showing history from your signed-in devices. <ph name="BEGIN_LINK">BEGIN_LINK</ph>Learn more<ph name="END_LINK">END_LINK</ph>.
       </message>
       <message name="IDS_IOS_HISTORY_NO_SYNCED_RESULTS" desc="The notification at the top of the history page indicating that it does not include visits from other devices.">
         Showing history from this device. <ph name="BEGIN_LINK">BEGIN_LINK</ph>Learn more.<ph name="END_LINK">END_LINK</ph>
diff --git a/ios/chrome/browser/application_context.h b/ios/chrome/browser/application_context.h
index 0e060d9..cf49fd2 100644
--- a/ios/chrome/browser/application_context.h
+++ b/ios/chrome/browser/application_context.h
@@ -46,7 +46,7 @@
 }
 
 namespace rappor {
-class RapporService;
+class RapporServiceImpl;
 }
 
 namespace variations {
@@ -102,8 +102,8 @@
   // Gets the VariationsService used by this application.
   virtual variations::VariationsService* GetVariationsService() = 0;
 
-  // Gets the RapporService. May return null.
-  virtual rappor::RapporService* GetRapporService() = 0;
+  // Gets the RapporServiceImpl. May return null.
+  virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0;
 
   // Gets the ChromeNetLog.
   virtual net_log::ChromeNetLog* GetNetLog() = 0;
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc
index b7a0acb..88bc2271 100644
--- a/ios/chrome/browser/application_context_impl.cc
+++ b/ios/chrome/browser/application_context_impl.cc
@@ -227,9 +227,9 @@
   return GetMetricsServicesManager()->GetVariationsService();
 }
 
-rappor::RapporService* ApplicationContextImpl::GetRapporService() {
+rappor::RapporServiceImpl* ApplicationContextImpl::GetRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return GetMetricsServicesManager()->GetRapporService();
+  return GetMetricsServicesManager()->GetRapporServiceImpl();
 }
 
 net_log::ChromeNetLog* ApplicationContextImpl::GetNetLog() {
diff --git a/ios/chrome/browser/application_context_impl.h b/ios/chrome/browser/application_context_impl.h
index d1a69f0..7a2474e 100644
--- a/ios/chrome/browser/application_context_impl.h
+++ b/ios/chrome/browser/application_context_impl.h
@@ -51,7 +51,7 @@
       override;
   metrics::MetricsService* GetMetricsService() override;
   variations::VariationsService* GetVariationsService() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   net_log::ChromeNetLog* GetNetLog() override;
   network_time::NetworkTimeTracker* GetNetworkTimeTracker() override;
   IOSChromeIOThread* GetIOSChromeIOThread() override;
diff --git a/ios/chrome/browser/bookmarks/bookmark_client_impl.cc b/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
index 4cf27d8..1f85bb7 100644
--- a/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
+++ b/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
@@ -38,29 +38,28 @@
       page_url, type, callback, tracker);
 }
 
-bool BookmarkClientImpl::SupportsTypedCountForNodes() {
+bool BookmarkClientImpl::SupportsTypedCountForUrls() {
   return true;
 }
 
-void BookmarkClientImpl::GetTypedCountForNodes(
-    const NodeSet& nodes,
-    NodeTypedCountPairs* node_typed_count_pairs) {
+void BookmarkClientImpl::GetTypedCountForUrls(
+    UrlTypedCountMap* url_typed_count_map) {
   history::HistoryService* history_service =
       ios::HistoryServiceFactory::GetForBrowserState(
           browser_state_, ServiceAccessType::EXPLICIT_ACCESS);
   history::URLDatabase* url_db =
       history_service ? history_service->InMemoryDatabase() : nullptr;
-  for (const auto* node : nodes) {
+  for (auto& url_typed_count_pair : *url_typed_count_map) {
     // If |url_db| is the InMemoryDatabase, it might not cache all URLRows, but
     // it guarantees to contain those with |typed_count| > 0. Thus, if fetching
     // the URLRow fails, it is safe to assume that its |typed_count| is 0.
     int typed_count = 0;
-    history::URLRow url;
-    if (url_db && url_db->GetRowForURL(node->url(), &url))
-      typed_count = url.typed_count();
+    history::URLRow url_row;
+    const GURL* url = url_typed_count_pair.first;
+    if (url_db && url && url_db->GetRowForURL(*url, &url_row))
+      typed_count = url_row.typed_count();
 
-    NodeTypedCountPair pair(node, typed_count);
-    node_typed_count_pairs->push_back(pair);
+    url_typed_count_pair.second = typed_count;
   }
 }
 
diff --git a/ios/chrome/browser/bookmarks/bookmark_client_impl.h b/ios/chrome/browser/bookmarks/bookmark_client_impl.h
index 597d984..44a32d5 100644
--- a/ios/chrome/browser/bookmarks/bookmark_client_impl.h
+++ b/ios/chrome/browser/bookmarks/bookmark_client_impl.h
@@ -35,10 +35,8 @@
       favicon_base::IconType type,
       const favicon_base::FaviconImageCallback& callback,
       base::CancelableTaskTracker* tracker) override;
-  bool SupportsTypedCountForNodes() override;
-  void GetTypedCountForNodes(
-      const NodeSet& nodes,
-      NodeTypedCountPairs* node_typed_count_pairs) override;
+  bool SupportsTypedCountForUrls() override;
+  void GetTypedCountForUrls(UrlTypedCountMap* url_typed_count_map) override;
   bool IsPermanentNodeVisible(
       const bookmarks::BookmarkPermanentNode* node) override;
   void RecordAction(const base::UserMetricsAction& action) override;
diff --git a/ios/chrome/browser/device_sharing/BUILD.gn b/ios/chrome/browser/device_sharing/BUILD.gn
index 6c10dd9..2c22242 100644
--- a/ios/chrome/browser/device_sharing/BUILD.gn
+++ b/ios/chrome/browser/device_sharing/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("device_sharing") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "device_sharing_manager.h",
     "device_sharing_manager.mm",
diff --git a/ios/chrome/browser/device_sharing/device_sharing_manager.mm b/ios/chrome/browser/device_sharing/device_sharing_manager.mm
index e84f257a..1039eb5 100644
--- a/ios/chrome/browser/device_sharing/device_sharing_manager.mm
+++ b/ios/chrome/browser/device_sharing/device_sharing_manager.mm
@@ -15,6 +15,10 @@
 #include "ios/chrome/browser/prefs/pref_observer_bridge.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface DeviceSharingManager ()<PrefObserverDelegate> {
   ios::ChromeBrowserState* _browserState;  // weak
 
@@ -77,11 +81,11 @@
   }
 
   if (!_handoffManager)
-    _handoffManager.reset([[[self class] createHandoffManager] retain]);
+    _handoffManager.reset([[self class] createHandoffManager]);
 }
 
 + (HandoffManager*)createHandoffManager {
-  return [[[HandoffManager alloc] init] autorelease];
+  return [[HandoffManager alloc] init];
 }
 
 #pragma mark - PrefObserverDelegate
diff --git a/ios/chrome/browser/experimental_flags.h b/ios/chrome/browser/experimental_flags.h
index b12e2fc..fed8a64 100644
--- a/ios/chrome/browser/experimental_flags.h
+++ b/ios/chrome/browser/experimental_flags.h
@@ -12,30 +12,78 @@
 
 namespace experimental_flags {
 
+enum GaiaEnvironment {
+  GAIA_ENVIRONMENT_PROD,
+  GAIA_ENVIRONMENT_STAGING,
+  GAIA_ENVIRONMENT_TEST,
+};
+
+enum WhatsNewPromoStatus {
+  WHATS_NEW_DEFAULT = 0,          // Not forced to enable a promo.
+  WHATS_NEW_APP_RATING,           // Force enable App Rating Promo.
+  WHATS_NEW_MOVE_TO_DOCK_FASTER,  // Force enable Move To Dock Faster Access
+  WHATS_NEW_MOVE_TO_DOCK_LOVE,    // Force enable Move To Dock Love Chrome
+  WHATS_NEW_MOVE_TO_DOCK_TIP,     // Force enable Move To Dock Tip promo.
+};
+
+// Whether the First Run UI will be always be displayed.
+bool AlwaysDisplayFirstRun();
+
+GaiaEnvironment GetGaiaEnvironment();
+
+// Returns the host name for an alternative Origin Server host for use by
+// |BrandCode| startup ping. Returns empty string if there is no alternative
+// host specified.
+std::string GetOriginServerHost();
+
+// Returns the promo force enabled, as determined by the experimental flags.
+// If |WHATS_NEW_DEFAULT| is returned, no promo is force enabled.
+WhatsNewPromoStatus GetWhatsNewPromoStatus();
+
 // Whether background crash report upload should generate a local notification.
 bool IsAlertOnBackgroundUploadEnabled();
 
+// Whether the All Bookmarks view is visible in bookmarks.
+bool IsAllBookmarksEnabled();
+
+// Whether auto-reload is enabled.
+bool IsAutoReloadEnabled();
+
+// Whether the credential management API is enabled.
+bool IsCredentialManagementEnabled();
+
+// Whether "Save Image" should be renamed as "Download Image".
+bool IsDownloadRenamingEnabled();
+
+// Whether the external applicaiton prompt is enabled.
+bool IsExternalApplicationPromptEnabled();
+
+// Whether contextual search must be reset to undecided state.
+bool IsForceResetContextualSearchEnabled();
+
 // Whether the lru snapshot cache experiment is enabled.
 bool IsLRUSnapshotCacheEnabled();
 
-// Whether viewing and copying passwords is enabled.
-bool IsViewCopyPasswordsEnabled();
+// Whether the iOS MDM integration is enabled.
+bool IsMDMIntegrationEnabled();
+
+// Whether memory debugging tools are enabled.
+bool IsMemoryDebuggingEnabled();
+
+// Whether the Clear Browsing Data counters and time selection UI is enabled.
+bool IsNewClearBrowsingDataUIEnabled();
+
+// Whether the page icon for downgraded HTTPS is enabled.
+bool IsPageIconForDowngradedHTTPSEnabled();
 
 // Whether password generation is enabled.
 bool IsPasswordGenerationEnabled();
 
-// Whether password generation fields are determined using local heuristics
-// only.
-bool UseOnlyLocalHeuristicsForPasswordGeneration();
+// Whether the Payment Request API is enabled or not.
+bool IsPaymentRequestEnabled();
 
-// Whether the Tab Switcher is enabled for iPad or not.
-bool IsTabSwitcherEnabled();
-
-// Whether the reading list is enabled.
-bool IsReadingListEnabled();
-
-// Whether the All Bookmarks view is visible in bookmarks.
-bool IsAllBookmarksEnabled();
+// Whether the back-forward navigation uses pending index.
+bool IsPendingIndexNavigationEnabled();
 
 // Whether the Physical Web feature is enabled.
 bool IsPhysicalWebEnabled();
@@ -43,23 +91,31 @@
 // Whether the QR Code Reader is enabled.
 bool IsQRCodeReaderEnabled();
 
-// Whether the Clear Browsing Data counters and time selection UI is enabled.
-bool IsNewClearBrowsingDataUIEnabled();
+// Whether reader mode is enabled.
+bool IsReaderModeEnabled();
 
-// Whether the Payment Request API is enabled or not.
-bool IsPaymentRequestEnabled();
+// Whether the reading list is enabled.
+bool IsReadingListEnabled();
+
+// Whether the Sign In Flow via SFSafariViewController is enabled.
+bool IsSafariVCSignInEnabled();
 
 // Whether launching actions from Spotlight is enabled.
 bool IsSpotlightActionsEnabled();
 
-// Whether the iOS MDM integration is enabled.
-bool IsMDMIntegrationEnabled();
+// Whether startup crash is enabled.
+bool IsStartupCrashEnabled();
 
-// Whether the back-forward navigation uses pending index.
-bool IsPendingIndexNavigationEnabled();
+// Whether the Tab Switcher is enabled for iPad or not.
+bool IsTabSwitcherEnabled();
 
-// Whether "Save Image" should be renamed as "Download Image".
-bool IsDownloadRenamingEnabled();
+// Whether viewing and copying passwords is enabled.
+bool IsViewCopyPasswordsEnabled();
+
+// Whether password generation fields are determined using local heuristics
+// only.
+bool UseOnlyLocalHeuristicsForPasswordGeneration();
+
 }  // namespace experimental_flags
 
 #endif  // IOS_CHROME_BROWSER_EXPERIMENTAL_FLAGS_H_
diff --git a/ios/chrome/browser/experimental_flags.mm b/ios/chrome/browser/experimental_flags.mm
index 5a4ef07..5dd2c08 100644
--- a/ios/chrome/browser/experimental_flags.mm
+++ b/ios/chrome/browser/experimental_flags.mm
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/reading_list/core/reading_list_switches.h"
 #include "components/variations/variations_associated_data.h"
@@ -22,92 +23,64 @@
 #include "ios/web/public/web_view_creation_util.h"
 
 namespace {
+
 NSString* const kEnableAlertOnBackgroundUpload =
     @"EnableAlertsOnBackgroundUpload";
+NSString* const kEnableNewClearBrowsingDataUI = @"EnableNewClearBrowsingDataUI";
+NSString* const kEnableStartupCrash = @"EnableStartupCrash";
 NSString* const kEnableViewCopyPasswords = @"EnableViewCopyPasswords";
+NSString* const kExternalAppPromptDisabled = @"ExternalAppPromptDisabled";
+NSString* const kFirstRunForceEnabled = @"FirstRunForceEnabled";
+NSString* const kForceResetContextualSearch = @"ForceResetContextualSearch";
+NSString* const kGaiaEnvironment = @"GAIAEnvironment";
 NSString* const kHeuristicsForPasswordGeneration =
     @"HeuristicsForPasswordGeneration";
-NSString* const kEnableNewClearBrowsingDataUI = @"EnableNewClearBrowsingDataUI";
 NSString* const kMDMIntegrationDisabled = @"MDMIntegrationDisabled";
+NSString* const kOriginServerHost = @"AlternateOriginServerHost";
 NSString* const kPendingIndexNavigationDisabled =
     @"PendingIndexNavigationDisabled";
+NSString* const kSafariVCSignInDisabled = @"SafariVCSignInDisabled";
+NSString* const kWhatsNewPromoStatus = @"WhatsNewPromoStatus";
+
 const base::Feature kIOSDownloadImageRenaming{
     "IOSDownloadImageRenaming", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace
 
 namespace experimental_flags {
 
+bool AlwaysDisplayFirstRun() {
+  return
+      [[NSUserDefaults standardUserDefaults] boolForKey:kFirstRunForceEnabled];
+}
+
+GaiaEnvironment GetGaiaEnvironment() {
+  NSString* gaia_environment =
+      [[NSUserDefaults standardUserDefaults] objectForKey:kGaiaEnvironment];
+  if ([gaia_environment isEqualToString:@"Staging"])
+    return GAIA_ENVIRONMENT_STAGING;
+  if ([gaia_environment isEqualToString:@"Test"])
+    return GAIA_ENVIRONMENT_TEST;
+  return GAIA_ENVIRONMENT_PROD;
+}
+
+std::string GetOriginServerHost() {
+  NSString* alternateHost =
+      [[NSUserDefaults standardUserDefaults] stringForKey:kOriginServerHost];
+  return base::SysNSStringToUTF8(alternateHost);
+}
+
+WhatsNewPromoStatus GetWhatsNewPromoStatus() {
+  NSInteger status = [[NSUserDefaults standardUserDefaults]
+      integerForKey:kWhatsNewPromoStatus];
+  return static_cast<WhatsNewPromoStatus>(status);
+}
+
 bool IsAlertOnBackgroundUploadEnabled() {
   return [[NSUserDefaults standardUserDefaults]
       boolForKey:kEnableAlertOnBackgroundUpload];
 }
 
-bool IsLRUSnapshotCacheEnabled() {
-  // Check if the experimental flag is forced on or off.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableLRUSnapshotCache)) {
-    return true;
-  } else if (command_line->HasSwitch(switches::kDisableLRUSnapshotCache)) {
-    return false;
-  }
-
-  // Check if the finch experiment is turned on.
-  std::string group_name =
-      base::FieldTrialList::FindFullName("IOSLRUSnapshotCache");
-  return base::StartsWith(group_name, "Enabled",
-                          base::CompareCase::INSENSITIVE_ASCII);
-}
-
-bool IsViewCopyPasswordsEnabled() {
-  NSString* viewCopyPasswordFlag = [[NSUserDefaults standardUserDefaults]
-      objectForKey:kEnableViewCopyPasswords];
-  if ([viewCopyPasswordFlag isEqualToString:@"Enabled"])
-    return true;
-  return false;
-}
-
-bool IsPasswordGenerationEnabled() {
-  // This call activates the field trial, if needed, so it must come before any
-  // early returns.
-  std::string group_name =
-      base::FieldTrialList::FindFullName("PasswordGeneration");
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableIOSPasswordGeneration))
-    return true;
-  if (command_line->HasSwitch(switches::kDisableIOSPasswordGeneration))
-    return false;
-  return group_name != "Disabled";
-}
-
-bool UseOnlyLocalHeuristicsForPasswordGeneration() {
-  if ([[NSUserDefaults standardUserDefaults]
-          boolForKey:kHeuristicsForPasswordGeneration]) {
-    return true;
-  }
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  return command_line->HasSwitch(
-      autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration);
-}
-
-bool IsTabSwitcherEnabled() {
-  // Check if the experimental flag is forced on or off.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTabSwitcher)) {
-    return true;
-  } else if (command_line->HasSwitch(switches::kDisableTabSwitcher)) {
-    return false;
-  }
-
-  // Check if the finch experiment is turned on.
-  std::string group_name = base::FieldTrialList::FindFullName("IOSTabSwitcher");
-  return base::StartsWith(group_name, "Enabled",
-                          base::CompareCase::INSENSITIVE_ASCII);
-}
-
-bool IsReadingListEnabled() {
-  return reading_list::switches::IsReadingListEnabled();
-}
-
 bool IsAllBookmarksEnabled() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kEnableAllBookmarksView)) {
@@ -127,6 +100,132 @@
                           base::CompareCase::INSENSITIVE_ASCII);
 }
 
+bool IsAutoReloadEnabled() {
+  std::string group_name = base::FieldTrialList::FindFullName("IOSAutoReload");
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableOfflineAutoReload))
+    return true;
+  if (command_line->HasSwitch(switches::kDisableOfflineAutoReload))
+    return false;
+  return base::StartsWith(group_name, "Enabled",
+                          base::CompareCase::INSENSITIVE_ASCII);
+}
+
+bool IsCredentialManagementEnabled() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(switches::kEnableCredentialManagerAPI);
+}
+
+bool IsDownloadRenamingEnabled() {
+  // Check if the experimental flag is forced on or off.
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableDownloadImageRenaming)) {
+    return true;
+  } else if (command_line->HasSwitch(switches::kDisableDownloadImageRenaming)) {
+    return false;
+  }
+
+  // Check if the finch experiment is turned on.
+  return base::FeatureList::IsEnabled(kIOSDownloadImageRenaming);
+}
+
+bool IsExternalApplicationPromptEnabled() {
+  return ![[NSUserDefaults standardUserDefaults]
+      boolForKey:kExternalAppPromptDisabled];
+}
+
+bool IsForceResetContextualSearchEnabled() {
+  return [[NSUserDefaults standardUserDefaults]
+      boolForKey:kForceResetContextualSearch];
+}
+
+bool IsLRUSnapshotCacheEnabled() {
+  // Check if the experimental flag is forced on or off.
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableLRUSnapshotCache)) {
+    return true;
+  } else if (command_line->HasSwitch(switches::kDisableLRUSnapshotCache)) {
+    return false;
+  }
+
+  // Check if the finch experiment is turned on.
+  std::string group_name =
+      base::FieldTrialList::FindFullName("IOSLRUSnapshotCache");
+  return base::StartsWith(group_name, "Enabled",
+                          base::CompareCase::INSENSITIVE_ASCII);
+}
+
+bool IsMDMIntegrationEnabled() {
+  return ![[NSUserDefaults standardUserDefaults]
+      boolForKey:kMDMIntegrationDisabled];
+}
+
+bool IsMemoryDebuggingEnabled() {
+// Always return true for Chromium builds, but check the user default for
+// official builds because memory debugging should never be enabled on stable.
+#if CHROMIUM_BUILD
+  return true;
+#else
+  return [[NSUserDefaults standardUserDefaults]
+      boolForKey:@"EnableMemoryDebugging"];
+#endif  // CHROMIUM_BUILD
+}
+
+bool IsNewClearBrowsingDataUIEnabled() {
+  NSString* countersFlag = [[NSUserDefaults standardUserDefaults]
+      objectForKey:kEnableNewClearBrowsingDataUI];
+  if ([countersFlag isEqualToString:@"Enabled"])
+    return true;
+  return false;
+}
+
+// Emergency switch for https://crbug.com/527084 in case of unforeseen UX
+// regressions.
+// Defaults to Enabled unless the Finch trial has explicitly disabled it.
+bool IsPageIconForDowngradedHTTPSEnabled() {
+  std::string group_name =
+      base::FieldTrialList::FindFullName("IOSPageIconForDowngradedHTTPS");
+  return !base::StartsWith(group_name, "Disabled",
+                           base::CompareCase::INSENSITIVE_ASCII);
+}
+
+bool IsPasswordGenerationEnabled() {
+  // This call activates the field trial, if needed, so it must come before any
+  // early returns.
+  std::string group_name =
+      base::FieldTrialList::FindFullName("PasswordGeneration");
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnableIOSPasswordGeneration))
+    return true;
+  if (command_line->HasSwitch(switches::kDisableIOSPasswordGeneration))
+    return false;
+  return group_name != "Disabled";
+}
+
+bool IsPaymentRequestEnabled() {
+  // This call activates the field trial, if needed, so it must come before any
+  // early returns.
+  std::string group_name =
+      base::FieldTrialList::FindFullName("IOSPaymentRequest");
+
+  // Check if the experimental flag is forced on or off.
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kEnablePaymentRequest)) {
+    return true;
+  } else if (command_line->HasSwitch(switches::kDisablePaymentRequest)) {
+    return false;
+  }
+
+  // Check if the Finch experiment is turned on.
+  return base::StartsWith(group_name, "Enabled",
+                          base::CompareCase::INSENSITIVE_ASCII);
+}
+
+bool IsPendingIndexNavigationEnabled() {
+  return ![[NSUserDefaults standardUserDefaults]
+      boolForKey:kPendingIndexNavigationDisabled];
+}
+
 bool IsPhysicalWebEnabled() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kEnableIOSPhysicalWeb)) {
@@ -147,31 +246,18 @@
   return !command_line->HasSwitch(switches::kDisableQRScanner);
 }
 
-bool IsNewClearBrowsingDataUIEnabled() {
-  NSString* countersFlag = [[NSUserDefaults standardUserDefaults]
-      objectForKey:kEnableNewClearBrowsingDataUI];
-  if ([countersFlag isEqualToString:@"Enabled"])
-    return true;
-  return false;
+bool IsReaderModeEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableReaderModeToolbarIcon);
 }
 
-bool IsPaymentRequestEnabled() {
-  // This call activates the field trial, if needed, so it must come before any
-  // early returns.
-  std::string group_name =
-      base::FieldTrialList::FindFullName("IOSPaymentRequest");
+bool IsReadingListEnabled() {
+  return reading_list::switches::IsReadingListEnabled();
+}
 
-  // Check if the experimental flag is forced on or off.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnablePaymentRequest)) {
-    return true;
-  } else if (command_line->HasSwitch(switches::kDisablePaymentRequest)) {
-    return false;
-  }
-
-  // Check if the Finch experiment is turned on.
-  return base::StartsWith(group_name, "Enabled",
-                          base::CompareCase::INSENSITIVE_ASCII);
+bool IsSafariVCSignInEnabled() {
+  return ![[NSUserDefaults standardUserDefaults]
+      boolForKey:kSafariVCSignInDisabled];
 }
 
 bool IsSpotlightActionsEnabled() {
@@ -179,27 +265,41 @@
   return !command_line->HasSwitch(switches::kDisableSpotlightActions);
 }
 
-bool IsMDMIntegrationEnabled() {
-  return ![[NSUserDefaults standardUserDefaults]
-      boolForKey:kMDMIntegrationDisabled];
+bool IsStartupCrashEnabled() {
+  return [[NSUserDefaults standardUserDefaults] boolForKey:kEnableStartupCrash];
 }
 
-bool IsPendingIndexNavigationEnabled() {
-  return ![[NSUserDefaults standardUserDefaults]
-      boolForKey:kPendingIndexNavigationDisabled];
-}
-
-bool IsDownloadRenamingEnabled() {
+bool IsTabSwitcherEnabled() {
   // Check if the experimental flag is forced on or off.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableDownloadImageRenaming)) {
+  if (command_line->HasSwitch(switches::kEnableTabSwitcher)) {
     return true;
-  } else if (command_line->HasSwitch(switches::kDisableDownloadImageRenaming)) {
+  } else if (command_line->HasSwitch(switches::kDisableTabSwitcher)) {
     return false;
   }
 
   // Check if the finch experiment is turned on.
-  return base::FeatureList::IsEnabled(kIOSDownloadImageRenaming);
+  std::string group_name = base::FieldTrialList::FindFullName("IOSTabSwitcher");
+  return base::StartsWith(group_name, "Enabled",
+                          base::CompareCase::INSENSITIVE_ASCII);
+}
+
+bool IsViewCopyPasswordsEnabled() {
+  NSString* viewCopyPasswordFlag = [[NSUserDefaults standardUserDefaults]
+      objectForKey:kEnableViewCopyPasswords];
+  if ([viewCopyPasswordFlag isEqualToString:@"Enabled"])
+    return true;
+  return false;
+}
+
+bool UseOnlyLocalHeuristicsForPasswordGeneration() {
+  if ([[NSUserDefaults standardUserDefaults]
+          boolForKey:kHeuristicsForPasswordGeneration]) {
+    return true;
+  }
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(
+      autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration);
 }
 
 }  // namespace experimental_flags
diff --git a/ios/chrome/browser/find_in_page/js_findinpage_manager.mm b/ios/chrome/browser/find_in_page/js_findinpage_manager.mm
index 856f153..96d4b60 100644
--- a/ios/chrome/browser/find_in_page/js_findinpage_manager.mm
+++ b/ios/chrome/browser/find_in_page/js_findinpage_manager.mm
@@ -177,7 +177,7 @@
   std::unique_ptr<base::Value> root(base::JSONReader::Read(json, false));
   if (!root.get())
     return YES;
-  if (!root->IsType(base::Value::TYPE_LIST))
+  if (!root->IsType(base::Value::Type::LIST))
     return YES;
 
   base::ListValue* resultList = static_cast<base::ListValue*>(root.get());
@@ -226,7 +226,7 @@
   if (!root.get())
     return kFindInPageEntryZero;
 
-  if (!root->IsType(base::Value::TYPE_LIST))
+  if (!root->IsType(base::Value::Type::LIST))
     return kFindInPageEntryZero;
 
   base::ListValue* position = static_cast<base::ListValue*>(root.get());
diff --git a/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm b/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
index f575e16..f1fa47e 100644
--- a/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
+++ b/ios/chrome/browser/interstitials/ios_chrome_metrics_helper.mm
@@ -6,7 +6,7 @@
 
 #include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/core/service_access_type.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -27,8 +27,8 @@
               ios::ChromeBrowserState::FromBrowserState(
                   web_state->GetBrowserState()),
               ServiceAccessType::EXPLICIT_ACCESS),
-          GetApplicationContext()->GetRapporService()
-              ? GetApplicationContext()->GetRapporService()->AsWeakPtr()
+          GetApplicationContext()->GetRapporServiceImpl()
+              ? GetApplicationContext()->GetRapporServiceImpl()->AsWeakPtr()
               : base::WeakPtr<rappor::RapporService>()) {}
 
 IOSChromeMetricsHelper::~IOSChromeMetricsHelper() {}
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index ab03d810..722cad2b 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -24,7 +24,7 @@
 #include "components/open_from_clipboard/clipboard_recent_content.h"
 #include "components/prefs/json_pref_store.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/task_scheduler_util/initialization_util.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/variations/field_trial_config/field_trial_util.h"
diff --git a/ios/chrome/browser/itunes_links/BUILD.gn b/ios/chrome/browser/itunes_links/BUILD.gn
index 16a4cc95..7faa8c1 100644
--- a/ios/chrome/browser/itunes_links/BUILD.gn
+++ b/ios/chrome/browser/itunes_links/BUILD.gn
@@ -17,6 +17,7 @@
 }
 
 source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "itunes_links_observer_unittest.mm",
diff --git a/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm b/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm
index a726370..ddee1266 100644
--- a/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm
+++ b/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm
@@ -6,7 +6,6 @@
 
 #import "ios/chrome/browser/itunes_links/itunes_links_observer.h"
 
-#import "base/mac/scoped_nsobject.h"
 #import "ios/web/public/test/test_web_state.h"
 #import "ios/chrome/browser/storekit_launcher.h"
 #include "testing/gtest_mac.h"
@@ -15,30 +14,38 @@
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "url/gurl.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 class ITunesLinksObserverTest : public PlatformTest {
  protected:
   void SetUp() override {
-    mocked_store_kit_launcher_.reset(
-        [[OCMockObject mockForProtocol:@protocol(StoreKitLauncher)] retain]);
-    link_observer_.reset(
-        [[ITunesLinksObserver alloc] initWithWebState:&web_state_]);
+    mocked_store_kit_launcher_ =
+        [OCMockObject mockForProtocol:@protocol(StoreKitLauncher)];
+    link_observer_ = [[ITunesLinksObserver alloc] initWithWebState:&web_state_];
     [link_observer_ setStoreKitLauncher:mocked_store_kit_launcher_];
   }
   void VerifyOpeningOfAppStore(NSString* expected_product_id,
                                std::string const& url_string) {
     if (expected_product_id)
-      [[mocked_store_kit_launcher_.get() expect]
-          openAppStore:expected_product_id];
+      [[mocked_store_kit_launcher_ expect] openAppStore:expected_product_id];
     web_state_.SetCurrentURL(GURL(url_string));
     [link_observer_ webStateDidLoadPage:&web_state_];
-    EXPECT_OCMOCK_VERIFY(mocked_store_kit_launcher_.get());
+    EXPECT_OCMOCK_VERIFY(mocked_store_kit_launcher_);
   }
+
+  void TearDown() override {
+    // |link_observer_| must be destroyed before web_state_.
+    link_observer_ = nil;
+  }
+
   web::TestWebState web_state_;
-  base::scoped_nsobject<id> mocked_store_kit_launcher_;
+  id mocked_store_kit_launcher_;
   // |link_observer_| must be destroyed before web_state_.
-  base::scoped_nsobject<ITunesLinksObserver> link_observer_;
+  ITunesLinksObserver* link_observer_;
 };
 
 // Verifies that navigating to URLs not concerning a product hosted on the
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
index 0c48329..874e6736 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
@@ -34,7 +34,7 @@
   class IOSChromeEnabledStateProvider;
 
   // metrics_services_manager::MetricsServicesManagerClient:
-  std::unique_ptr<rappor::RapporService> CreateRapporService() override;
+  std::unique_ptr<rappor::RapporServiceImpl> CreateRapporServiceImpl() override;
   std::unique_ptr<variations::VariationsService> CreateVariationsService()
       override;
   std::unique_ptr<metrics::MetricsServiceClient> CreateMetricsServiceClient()
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
index e569339..1afc1478 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
@@ -10,7 +10,7 @@
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/service/variations_service.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/chrome_switches.h"
@@ -58,10 +58,10 @@
 IOSChromeMetricsServicesManagerClient::
     ~IOSChromeMetricsServicesManagerClient() = default;
 
-std::unique_ptr<rappor::RapporService>
-IOSChromeMetricsServicesManagerClient::CreateRapporService() {
+std::unique_ptr<rappor::RapporServiceImpl>
+IOSChromeMetricsServicesManagerClient::CreateRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return base::MakeUnique<rappor::RapporService>(
+  return base::MakeUnique<rappor::RapporServiceImpl>(
       local_state_, base::Bind(&::IsOffTheRecordSessionActive));
 }
 
diff --git a/ios/chrome/browser/native_app_launcher/BUILD.gn b/ios/chrome/browser/native_app_launcher/BUILD.gn
index 7c32d813..0bac471d 100644
--- a/ios/chrome/browser/native_app_launcher/BUILD.gn
+++ b/ios/chrome/browser/native_app_launcher/BUILD.gn
@@ -26,6 +26,7 @@
 }
 
 source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "native_app_infobar_controller_unittest.mm",
diff --git a/ios/chrome/browser/native_app_launcher/native_app_infobar_controller_unittest.mm b/ios/chrome/browser/native_app_launcher/native_app_infobar_controller_unittest.mm
index 888fdb1d..45b0d5d 100644
--- a/ios/chrome/browser/native_app_launcher/native_app_infobar_controller_unittest.mm
+++ b/ios/chrome/browser/native_app_launcher/native_app_infobar_controller_unittest.mm
@@ -12,6 +12,10 @@
 #import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller_protocol.h"
 #include "testing/platform_test.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface NativeAppInfoBarController (Testing)
 - (void)infoBarButtonDidPress:(UIButton*)button;
 - (void)setInfoBarDelegate:(NativeAppInfoBarDelegate*)delegate;
diff --git a/ios/chrome/browser/native_app_launcher/native_app_infobar_delegate_unittest.mm b/ios/chrome/browser/native_app_launcher/native_app_infobar_delegate_unittest.mm
index a8a5273b..72b31ac 100644
--- a/ios/chrome/browser/native_app_launcher/native_app_infobar_delegate_unittest.mm
+++ b/ios/chrome/browser/native_app_launcher/native_app_infobar_delegate_unittest.mm
@@ -11,6 +11,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 class NativeAppInfoBarDelegateTest : public PlatformTest {};
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index d68d201d..04ba014 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -455,7 +455,7 @@
   std::string errorMessage;
   std::unique_ptr<base::Value> jsonData(base::JSONReader::ReadAndReturnError(
       JSONString, false, &errorCode, &errorMessage));
-  if (errorCode || !jsonData || !jsonData->IsType(base::Value::TYPE_LIST)) {
+  if (errorCode || !jsonData || !jsonData->IsType(base::Value::Type::LIST)) {
     VLOG(1) << "JSON parse error " << errorMessage
             << " JSON string: " << JSONString;
     return;
@@ -528,7 +528,7 @@
     return NO;
   }
 
-  if (errorCode || !JSONData->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (errorCode || !JSONData->IsType(base::Value::Type::DICTIONARY)) {
     VLOG(1) << "JSON parse error " << errorMessage
             << " JSON string: " << JSONString;
     return NO;
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 57f18dfb..7f0c97d 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -22,7 +22,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
-#include "components/rappor/rappor_service.h"
+#include "components/rappor/rappor_service_impl.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/signin/core/common/signin_pref_names.h"
 #include "components/ssl_config/ssl_config_service_manager.h"
@@ -56,7 +56,7 @@
   network_time::NetworkTimeTracker::RegisterPrefs(registry);
   ios::NotificationPromo::RegisterPrefs(registry);
   PrefProxyConfigTrackerImpl::RegisterPrefs(registry);
-  rappor::RapporService::RegisterPrefs(registry);
+  rappor::RapporServiceImpl::RegisterPrefs(registry);
   ssl_config::SSLConfigServiceManager::RegisterPrefs(registry);
   update_client::RegisterPrefs(registry);
   variations::VariationsService::RegisterPrefs(registry);
diff --git a/ios/chrome/browser/reading_list/BUILD.gn b/ios/chrome/browser/reading_list/BUILD.gn
index 7c7148b..d68daec 100644
--- a/ios/chrome/browser/reading_list/BUILD.gn
+++ b/ios/chrome/browser/reading_list/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("reading_list") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "offline_url_utils.cc",
     "offline_url_utils.h",
diff --git a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm
index 085492c5..3b34fc6 100644
--- a/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm
+++ b/ios/chrome/browser/reading_list/reading_list_entry_loading_util.mm
@@ -12,6 +12,10 @@
 #import "ios/web/public/web_state/web_state.h"
 #include "net/base/network_change_notifier.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace reading_list {
 
 void LoadReadingListEntry(ReadingListEntry const& entry,
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
index bc6e6aaa..c2dc319 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -15,6 +15,10 @@
 #include "ios/web/public/navigation_manager.h"
 #include "ios/web/public/web_state/web_state_user_data.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 #pragma mark - ReadingListWebStateObserverUserDataWrapper
 
 namespace {
diff --git a/ios/chrome/browser/reading_list/url_downloader.cc b/ios/chrome/browser/reading_list/url_downloader.cc
index 0a573f8..bd69408 100644
--- a/ios/chrome/browser/reading_list/url_downloader.cc
+++ b/ios/chrome/browser/reading_list/url_downloader.cc
@@ -207,6 +207,10 @@
         images) {
   std::string mutable_html = html;
   for (size_t i = 0; i < images.size(); i++) {
+    if (images[i].url.SchemeIs(url::kDataScheme)) {
+      // Data URI, the data part of the image is empty, no need to store it.
+      continue;
+    }
     base::FilePath local_image_path;
     std::string local_image_name;
     if (!SaveImage(url, images[i].url, images[i].data, &local_image_name)) {
diff --git a/ios/chrome/browser/search_engines/template_url_service_factory.cc b/ios/chrome/browser/search_engines/template_url_service_factory.cc
index 7149b01..65b0a25 100644
--- a/ios/chrome/browser/search_engines/template_url_service_factory.cc
+++ b/ios/chrome/browser/search_engines/template_url_service_factory.cc
@@ -51,7 +51,7 @@
           ios::HistoryServiceFactory::GetForBrowserState(
               browser_state, ServiceAccessType::EXPLICIT_ACCESS))),
       ios::GoogleURLTrackerFactory::GetForBrowserState(browser_state),
-      GetApplicationContext()->GetRapporService(),
+      GetApplicationContext()->GetRapporServiceImpl(),
       GetDefaultSearchProviderChangedCallback());
 }
 
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
index 85cceb2..422e6b0 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_unittest.mm
@@ -36,7 +36,7 @@
     }
     return mock_web_view_;
   }
-  scoped_nsobject<id> mock_web_view_;
+  base::scoped_nsobject<id> mock_web_view_;
 };
 
 class MockGaiaConsumer : public GaiaAuthConsumer {
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.h b/ios/chrome/browser/sync/ios_chrome_sync_client.h
index de1cf51..013c5e4 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.h
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.h
@@ -78,8 +78,6 @@
 
   std::unique_ptr<sync_sessions::SyncSessionsClient> sync_sessions_client_;
 
-  const scoped_refptr<syncer::ExtensionsActivity> dummy_extensions_activity_;
-
   base::WeakPtrFactory<IOSChromeSyncClient> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(IOSChromeSyncClient);
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 7b5bf9b..79d38cd 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -30,7 +30,6 @@
 #include "components/reading_list/ios/reading_list_model.h"
 #include "components/search_engines/search_engine_data_type_controller.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/sync/base/extensions_activity.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/driver/sync_api_component_factory.h"
 #include "components/sync/driver/sync_util.h"
@@ -140,7 +139,6 @@
 IOSChromeSyncClient::IOSChromeSyncClient(ios::ChromeBrowserState* browser_state)
     : browser_state_(browser_state),
       sync_sessions_client_(new SyncSessionsClientImpl(browser_state)),
-      dummy_extensions_activity_(new syncer::ExtensionsActivity()),
       weak_ptr_factory_(this) {}
 
 IOSChromeSyncClient::~IOSChromeSyncClient() {}
@@ -239,9 +237,7 @@
 
 scoped_refptr<syncer::ExtensionsActivity>
 IOSChromeSyncClient::GetExtensionsActivity() {
-  // TODO(crbug.com/562048) Get rid of dummy_extensions_activity_ and return
-  // nullptr.
-  return dummy_extensions_activity_;
+  return nullptr;
 }
 
 sync_sessions::SyncSessionsClient*
diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.h b/ios/chrome/browser/ui/autofill/autofill_client_ios.h
index 20c1f91..68855edf 100644
--- a/ios/chrome/browser/ui/autofill/autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.h
@@ -53,7 +53,7 @@
   PrefService* GetPrefs() override;
   syncer::SyncService* GetSyncService() override;
   IdentityProvider* GetIdentityProvider() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   void ShowAutofillSettings() override;
   void ShowUnmaskPrompt(const CreditCard& card,
                         UnmaskCardReason reason,
diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm
index f84844cd..9447a8f 100644
--- a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm
@@ -65,8 +65,8 @@
   return identity_provider_.get();
 }
 
-rappor::RapporService* AutofillClientIOS::GetRapporService() {
-  return GetApplicationContext()->GetRapporService();
+rappor::RapporServiceImpl* AutofillClientIOS::GetRapporServiceImpl() {
+  return GetApplicationContext()->GetRapporServiceImpl();
 }
 
 void AutofillClientIOS::ShowAutofillSettings() {
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index b47d2a8..7a673fd 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -13,6 +13,9 @@
     "resources/reading_list_icon.png",
     "resources/reading_list_icon@2x.png",
     "resources/reading_list_icon@3x.png",
+    "resources/reading_list_side_swipe.png",
+    "resources/reading_list_side_swipe@2x.png",
+    "resources/reading_list_side_swipe@3x.png",
     "resources/share_icon.png",
     "resources/share_icon@2x.png",
     "resources/share_icon@3x.png",
diff --git a/ios/chrome/browser/ui/reading_list/resources/distillation_success.png b/ios/chrome/browser/ui/reading_list/resources/distillation_success.png
index c47197da..368452e 100644
--- a/ios/chrome/browser/ui/reading_list/resources/distillation_success.png
+++ b/ios/chrome/browser/ui/reading_list/resources/distillation_success.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/distillation_success@2x.png b/ios/chrome/browser/ui/reading_list/resources/distillation_success@2x.png
index c1c8fb0..719d6576d 100644
--- a/ios/chrome/browser/ui/reading_list/resources/distillation_success@2x.png
+++ b/ios/chrome/browser/ui/reading_list/resources/distillation_success@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/distillation_success@3x.png b/ios/chrome/browser/ui/reading_list/resources/distillation_success@3x.png
index c13819a..df30dbe0 100644
--- a/ios/chrome/browser/ui/reading_list/resources/distillation_success@3x.png
+++ b/ios/chrome/browser/ui/reading_list/resources/distillation_success@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.png
new file mode 100644
index 0000000..69c1f5a
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@2x.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@2x.png
new file mode 100644
index 0000000..b8d99dd
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@3x.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@3x.png
new file mode 100644
index 0000000..601c28c
--- /dev/null
+++ b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe@3x.png
Binary files differ
diff --git a/ios/chrome/browser/web_resource/web_resource_util.cc b/ios/chrome/browser/web_resource/web_resource_util.cc
index f759da2..080296c 100644
--- a/ios/chrome/browser/web_resource/web_resource_util.cc
+++ b/ios/chrome/browser/web_resource/web_resource_util.cc
@@ -49,7 +49,7 @@
     return;
   }
 
-  if (!value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value->IsType(base::Value::Type::DICTIONARY)) {
     PostErrorTask(task_runner, error_callback, kUnexpectedJSONFormatError);
     return;
   }
diff --git a/ios/chrome/test/testing_application_context.h b/ios/chrome/test/testing_application_context.h
index 0f6af8e..0f59101 100644
--- a/ios/chrome/test/testing_application_context.h
+++ b/ios/chrome/test/testing_application_context.h
@@ -41,7 +41,7 @@
       override;
   metrics::MetricsService* GetMetricsService() override;
   variations::VariationsService* GetVariationsService() override;
-  rappor::RapporService* GetRapporService() override;
+  rappor::RapporServiceImpl* GetRapporServiceImpl() override;
   net_log::ChromeNetLog* GetNetLog() override;
   network_time::NetworkTimeTracker* GetNetworkTimeTracker() override;
   IOSChromeIOThread* GetIOSChromeIOThread() override;
diff --git a/ios/chrome/test/testing_application_context.mm b/ios/chrome/test/testing_application_context.mm
index 162f023..e275b6f7 100644
--- a/ios/chrome/test/testing_application_context.mm
+++ b/ios/chrome/test/testing_application_context.mm
@@ -111,7 +111,7 @@
   return nullptr;
 }
 
-rappor::RapporService* TestingApplicationContext::GetRapporService() {
+rappor::RapporServiceImpl* TestingApplicationContext::GetRapporServiceImpl() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return nullptr;
 }
diff --git a/ios/public/provider/chrome/browser/build_config.gni b/ios/public/provider/chrome/browser/build_config.gni
index 0c563f85..14d9d84 100644
--- a/ios/public/provider/chrome/browser/build_config.gni
+++ b/ios/public/provider/chrome/browser/build_config.gni
@@ -4,20 +4,23 @@
 
 import("//build/config/chrome_build.gni")
 
-_default_ios_launchscreen_assets_target =
-    "//ios/chrome/app/resources:launchscreen_assets"
-_default_ios_provider_target = "//ios/chrome/browser/providers:provider_factory"
-
 declare_args() {
+  # Label of the target providing application icons.  This target must be a
+  # bundle_data target that copy Icon-*.png files in the application bundle.
+  ios_application_icons_target = "//ios/chrome/app/resources:chromium_icons"
+
   # Label of the target providing image assets for the launchscreen.  This
   # target must be an asset catalog that contains at least two imagesets, one
   # named "launchscreen_app_logo" and one named "launchscreen_brand_name".
-  ios_launchscreen_assets_target = _default_ios_launchscreen_assets_target
+  ios_launchscreen_assets_target =
+      "//ios/chrome/app/resources:launchscreen_assets"
+
+  # Label of the target providing packed resources (localizable strings,
+  # scalable and unscalable resources).  This target must copy the packed
+  # resources to the application bundle when depended on.
+  ios_packed_resources_target = "//ios/chrome/app/resources:packed_resources"
 
   # Label of the target providing implementation for ChromeBrowserProvider.
   # Overridden when using the Google-internal repository to build Chrome on iOS.
-  ios_provider_target = _default_ios_provider_target
+  ios_provider_target = "//ios/chrome/browser/providers:provider_factory"
 }
-
-# True if the provider target used has not been overridden.
-use_default_ios_provider = ios_provider_target == _default_ios_provider_target
diff --git a/ios/third_party/material_text_accessibility_ios/README.chromium b/ios/third_party/material_text_accessibility_ios/README.chromium
index 12f3e05..e4a2683 100644
--- a/ios/third_party/material_text_accessibility_ios/README.chromium
+++ b/ios/third_party/material_text_accessibility_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Text Accessibility iOS
 URL: None
 License: Apache 2.0
-License File: LICENSE
+License File: src/LICENSE
 Security Critical: yes
 
 Description:
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
index 08819be..9f923336 100644
--- a/ios/web/public/web_state/web_state.h
+++ b/ios/web/public/web_state/web_state.h
@@ -141,7 +141,7 @@
   // Runs JavaScript in the main frame's context. If a callback is provided, it
   // will be used to return the result, when the result is available or script
   // execution has failed due to an error.
-  // NOTE: Integer values will be returned as TYPE_DOUBLE because of underlying
+  // NOTE: Integer values will be returned as Type::DOUBLE because of underlying
   // library limitation.
   typedef base::Callback<void(const base::Value*)> JavaScriptResultCallback;
   virtual void ExecuteJavaScript(const base::string16& javascript) = 0;
diff --git a/ios/web/web_state/ui/web_view_js_utils.mm b/ios/web/web_state/ui/web_view_js_utils.mm
index 5036d48..fb402fd 100644
--- a/ios/web/web_state/ui/web_view_js_utils.mm
+++ b/ios/web/web_state/ui/web_view_js_utils.mm
@@ -33,17 +33,17 @@
   CFTypeID result_type = CFGetTypeID(wk_result);
   if (result_type == CFStringGetTypeID()) {
     result.reset(new base::StringValue(base::SysNSStringToUTF16(wk_result)));
-    DCHECK(result->IsType(base::Value::TYPE_STRING));
+    DCHECK(result->IsType(base::Value::Type::STRING));
   } else if (result_type == CFNumberGetTypeID()) {
     result.reset(new base::FundamentalValue([wk_result doubleValue]));
-    DCHECK(result->IsType(base::Value::TYPE_DOUBLE));
+    DCHECK(result->IsType(base::Value::Type::DOUBLE));
   } else if (result_type == CFBooleanGetTypeID()) {
     result.reset(
         new base::FundamentalValue(static_cast<bool>([wk_result boolValue])));
-    DCHECK(result->IsType(base::Value::TYPE_BOOLEAN));
+    DCHECK(result->IsType(base::Value::Type::BOOLEAN));
   } else if (result_type == CFNullGetTypeID()) {
     result = base::Value::CreateNullValue();
-    DCHECK(result->IsType(base::Value::TYPE_NULL));
+    DCHECK(result->IsType(base::Value::Type::NONE));
   } else if (result_type == CFDictionaryGetTypeID()) {
     std::unique_ptr<base::DictionaryValue> dictionary =
         base::MakeUnique<base::DictionaryValue>();
diff --git a/ios/web/web_state/ui/web_view_js_utils_unittest.mm b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
index 17213b8d..44ae5c3f1 100644
--- a/ios/web/web_state/ui/web_view_js_utils_unittest.mm
+++ b/ios/web/web_state/ui/web_view_js_utils_unittest.mm
@@ -17,54 +17,54 @@
   EXPECT_FALSE(ValueResultFromWKResult(nil));
 }
 
-// Tests that ValueResultFromWKResult converts string to Value::TYPE_STRING.
+// Tests that ValueResultFromWKResult converts string to Value::Type::STRING.
 TEST(WebViewJsUtilsTest, ValueResultFromStringWKResult) {
   std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@"test"));
   EXPECT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_STRING, value->GetType());
+  EXPECT_EQ(base::Value::Type::STRING, value->GetType());
   std::string converted_result;
   value->GetAsString(&converted_result);
   EXPECT_EQ("test", converted_result);
 }
 
-// Tests that ValueResultFromWKResult converts inetger to Value::TYPE_DOUBLE.
+// Tests that ValueResultFromWKResult converts inetger to Value::Type::DOUBLE.
 // NOTE: WKWebView API returns all numbers as kCFNumberFloat64Type, so there is
 // no way to tell if the result is integer or double.
 TEST(WebViewJsUtilsTest, ValueResultFromIntegerWKResult) {
   std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@1));
   EXPECT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_DOUBLE, value->GetType());
+  EXPECT_EQ(base::Value::Type::DOUBLE, value->GetType());
   double converted_result = 0;
   value->GetAsDouble(&converted_result);
   EXPECT_EQ(1, converted_result);
 }
 
-// Tests that ValueResultFromWKResult converts double to Value::TYPE_DOUBLE.
+// Tests that ValueResultFromWKResult converts double to Value::Type::DOUBLE.
 TEST(WebViewJsUtilsTest, ValueResultFromDoubleWKResult) {
   std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@3.14));
   EXPECT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_DOUBLE, value->GetType());
+  EXPECT_EQ(base::Value::Type::DOUBLE, value->GetType());
   double converted_result = 0;
   value->GetAsDouble(&converted_result);
   EXPECT_EQ(3.14, converted_result);
 }
 
-// Tests that ValueResultFromWKResult converts bool to Value::TYPE_BOOLEAN.
+// Tests that ValueResultFromWKResult converts bool to Value::Type::BOOLEAN.
 TEST(WebViewJsUtilsTest, ValueResultFromBoolWKResult) {
   std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@YES));
   EXPECT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_BOOLEAN, value->GetType());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, value->GetType());
   bool converted_result = false;
   value->GetAsBoolean(&converted_result);
   EXPECT_TRUE(converted_result);
 }
 
-// Tests that ValueResultFromWKResult converts null to Value::TYPE_NULL.
+// Tests that ValueResultFromWKResult converts null to Value::Type::NONE.
 TEST(WebViewJsUtilsTest, ValueResultFromNullWKResult) {
   std::unique_ptr<base::Value> value(
       web::ValueResultFromWKResult([NSNull null]));
   EXPECT_TRUE(value);
-  EXPECT_EQ(base::Value::TYPE_NULL, value->GetType());
+  EXPECT_EQ(base::Value::Type::NONE, value->GetType());
 }
 
 // Tests that ValueResultFromWKResult converts NSDictionaries to properly
diff --git a/ios/web/webui/mojo_facade.mm b/ios/web/webui/mojo_facade.mm
index a0a9ff5..bdea3d3 100644
--- a/ios/web/webui/mojo_facade.mm
+++ b/ios/web/webui/mojo_facade.mm
@@ -25,7 +25,7 @@
 
 namespace {
 
-// Wraps an integer into |base::Value| as |TYPE_INTEGER|.
+// Wraps an integer into |base::Value| as |Type::INTEGER|.
 template <typename IntegerT>
 std::unique_ptr<base::Value> ValueFromInteger(IntegerT handle) {
   return std::unique_ptr<base::Value>(
@@ -136,14 +136,14 @@
   const base::Value* options_as_value = nullptr;
   CHECK(args->Get("optionsDict", &options_as_value));
 
-  if (options_as_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (options_as_value->IsType(base::Value::Type::DICTIONARY)) {
     // There are no options defined for CreateMessagePipe yet.
     const base::DictionaryValue* options_as_dict;
     options_as_value->GetAsDictionary(&options_as_dict);
     CHECK(options_as_dict->empty());
   }
 
-  CHECK(options_as_value->IsType(base::Value::TYPE_NULL));
+  CHECK(options_as_value->IsType(base::Value::Type::NONE));
 
   mojo::MessagePipe message_pipe;
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue);
diff --git a/ipc/handle_win.h b/ipc/handle_win.h
index 2fb5b54..96c1b0b42 100644
--- a/ipc/handle_win.h
+++ b/ipc/handle_win.h
@@ -19,8 +19,6 @@
 
 namespace IPC {
 
-class Message;
-
 // HandleWin is a wrapper around a Windows HANDLE that can be transported
 // across Chrome IPC channels that support attachment brokering. The HANDLE will
 // be duplicated into the destination process.
diff --git a/ipc/ipc_channel_nacl.h b/ipc/ipc_channel_nacl.h
index 34afe6e..1860922 100644
--- a/ipc/ipc_channel_nacl.h
+++ b/ipc/ipc_channel_nacl.h
@@ -19,6 +19,8 @@
 
 namespace IPC {
 
+class MessageAttachment;
+
 // Contains the results from one call to imc_recvmsg (data and file
 // descriptors).
 struct MessageContents;
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 3a94f33..5c19476 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -34,7 +34,6 @@
 class ChannelFactory;
 class MessageFilter;
 class MessageFilterRouter;
-class SendCallbackHelper;
 
 //-----------------------------------------------------------------------------
 // IPC::ChannelProxy
diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h
index 41f57af..43e9ae3 100644
--- a/ipc/ipc_message.h
+++ b/ipc/ipc_message.h
@@ -30,7 +30,6 @@
 //------------------------------------------------------------------------------
 
 struct LogData;
-class MessageAttachment;
 class MessageAttachmentSet;
 
 class IPC_EXPORT Message : public base::Pickle {
diff --git a/ipc/ipc_message_pipe_reader.h b/ipc/ipc_message_pipe_reader.h
index 65e01ae..686982a9 100644
--- a/ipc/ipc_message_pipe_reader.h
+++ b/ipc/ipc_message_pipe_reader.h
@@ -26,8 +26,6 @@
 namespace IPC {
 namespace internal {
 
-class AsyncHandleWaiter;
-
 // A helper class to handle bytestream directly over mojo::MessagePipe
 // in template-method pattern. MessagePipeReader manages the lifetime
 // of given MessagePipe and participates the event loop, and
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index be83e5a..1e0cad5 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -84,18 +84,18 @@
 
   sizer->AddInt();
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       break;
-    case base::Value::TYPE_BOOLEAN:
+    case base::Value::Type::BOOLEAN:
       sizer->AddBool();
       break;
-    case base::Value::TYPE_INTEGER:
+    case base::Value::Type::INTEGER:
       sizer->AddInt();
       break;
-    case base::Value::TYPE_DOUBLE:
+    case base::Value::Type::DOUBLE:
       sizer->AddDouble();
       break;
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       const base::StringValue* result;
       value->GetAsString(&result);
       if (value->GetAsString(&result)) {
@@ -109,13 +109,13 @@
       }
       break;
     }
-    case base::Value::TYPE_BINARY: {
+    case base::Value::Type::BINARY: {
       const base::BinaryValue* binary =
           static_cast<const base::BinaryValue*>(value);
       sizer->AddData(static_cast<int>(binary->GetSize()));
       break;
     }
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       sizer->AddInt();
       const base::DictionaryValue* dict =
           static_cast<const base::DictionaryValue*>(value);
@@ -126,7 +126,7 @@
       }
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       sizer->AddInt();
       const base::ListValue* list = static_cast<const base::ListValue*>(value);
       for (const auto& entry : *list) {
@@ -146,46 +146,46 @@
     return;
   }
 
-  m->WriteInt(value->GetType());
+  m->WriteInt(static_cast<int>(value->GetType()));
 
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
     break;
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool val;
       result = value->GetAsBoolean(&val);
       DCHECK(result);
       WriteParam(m, val);
       break;
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int val;
       result = value->GetAsInteger(&val);
       DCHECK(result);
       WriteParam(m, val);
       break;
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double val;
       result = value->GetAsDouble(&val);
       DCHECK(result);
       WriteParam(m, val);
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string val;
       result = value->GetAsString(&val);
       DCHECK(result);
       WriteParam(m, val);
       break;
     }
-    case base::Value::TYPE_BINARY: {
+    case base::Value::Type::BINARY: {
       const base::BinaryValue* binary =
           static_cast<const base::BinaryValue*>(value);
       m->WriteData(binary->GetBuffer(), static_cast<int>(binary->GetSize()));
       break;
     }
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dict =
           static_cast<const base::DictionaryValue*>(value);
 
@@ -198,7 +198,7 @@
       }
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       const base::ListValue* list = static_cast<const base::ListValue*>(value);
       WriteParam(m, static_cast<int>(list->GetSize()));
       for (const auto& entry : *list) {
@@ -264,39 +264,39 @@
   if (!ReadParam(m, iter, &type))
     return false;
 
-  switch (type) {
-    case base::Value::TYPE_NULL:
+  switch (static_cast<base::Value::Type>(type)) {
+    case base::Value::Type::NONE:
       *value = base::Value::CreateNullValue().release();
     break;
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool val;
       if (!ReadParam(m, iter, &val))
         return false;
       *value = new base::FundamentalValue(val);
       break;
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int val;
       if (!ReadParam(m, iter, &val))
         return false;
       *value = new base::FundamentalValue(val);
       break;
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double val;
       if (!ReadParam(m, iter, &val))
         return false;
       *value = new base::FundamentalValue(val);
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string val;
       if (!ReadParam(m, iter, &val))
         return false;
       *value = new base::StringValue(val);
       break;
     }
-    case base::Value::TYPE_BINARY: {
+    case base::Value::Type::BINARY: {
       const char* data;
       int length;
       if (!iter->ReadData(&data, &length))
@@ -306,14 +306,14 @@
       *value = val.release();
       break;
     }
-    case base::Value::TYPE_DICTIONARY: {
+    case base::Value::Type::DICTIONARY: {
       std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
       if (!ReadDictionaryValue(m, iter, val.get(), recursion))
         return false;
       *value = val.release();
       break;
     }
-    case base::Value::TYPE_LIST: {
+    case base::Value::Type::LIST: {
       std::unique_ptr<base::ListValue> val(new base::ListValue());
       if (!ReadListValue(m, iter, val.get(), recursion))
         return false;
@@ -602,7 +602,8 @@
                                               base::PickleIterator* iter,
                                               param_type* r) {
   int type;
-  if (!ReadParam(m, iter, &type) || type != base::Value::TYPE_DICTIONARY)
+  if (!ReadParam(m, iter, &type) ||
+      type != static_cast<int>(base::Value::Type::DICTIONARY))
     return false;
 
   return ReadDictionaryValue(m, iter, r, 0);
@@ -818,7 +819,8 @@
                                         base::PickleIterator* iter,
                                         param_type* r) {
   int type;
-  if (!ReadParam(m, iter, &type) || type != base::Value::TYPE_LIST)
+  if (!ReadParam(m, iter, &type) ||
+      type != static_cast<int>(base::Value::Type::LIST))
     return false;
 
   return ReadListValue(m, iter, r, 0);
diff --git a/jingle/glue/utils.cc b/jingle/glue/utils.cc
index 923f021..4ea1d36 100644
--- a/jingle/glue/utils.cc
+++ b/jingle/glue/utils.cc
@@ -72,7 +72,7 @@
                              cricket::Candidate* candidate) {
   std::unique_ptr<base::Value> value(
       base::JSONReader::Read(candidate_str, base::JSON_ALLOW_TRAILING_COMMAS));
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY)) {
     return false;
   }
 
diff --git a/jingle/notifier/communicator/login.h b/jingle/notifier/communicator/login.h
index 98e77543..8f115f4 100644
--- a/jingle/notifier/communicator/login.h
+++ b/jingle/notifier/communicator/login.h
@@ -21,7 +21,6 @@
 #include "webrtc/libjingle/xmpp/xmppengine.h"
 
 namespace buzz {
-class XmppClient;
 class XmppClientSettings;
 class XmppTaskParentInterface;
 }  // namespace buzz
diff --git a/mash/browser/BUILD.gn b/mash/browser/BUILD.gn
index 771516d..00776bb 100644
--- a/mash/browser/BUILD.gn
+++ b/mash/browser/BUILD.gn
@@ -19,6 +19,7 @@
 
   deps = [
     "//base",
+    "//content/public/common:service_names",
     "//mash/public/interfaces",
     "//mojo/public/cpp/bindings",
     "//services/catalog/public/interfaces",
diff --git a/mash/browser/DEPS b/mash/browser/DEPS
index 5a67902..ba0eaa4 100644
--- a/mash/browser/DEPS
+++ b/mash/browser/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+content/public/common/service_names.mojom.h",
   "+services/navigation/public",
 ]
diff --git a/mash/browser/browser.cc b/mash/browser/browser.cc
index adc06988..b30e670 100644
--- a/mash/browser/browser.cc
+++ b/mash/browser/browser.cc
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/timer.h"
+#include "content/public/common/service_names.mojom.h"
 #include "mash/browser/debug_view.h"
 #include "mash/public/interfaces/launchable.mojom.h"
 #include "services/navigation/public/cpp/view.h"
@@ -865,7 +866,8 @@
 
 std::unique_ptr<navigation::View> Browser::CreateView() {
   navigation::mojom::ViewFactoryPtr factory;
-  context()->connector()->ConnectToInterface("navigation", &factory);
+  context()->connector()->ConnectToInterface(
+      content::mojom::kBrowserServiceName, &factory);
   return base::MakeUnique<navigation::View>(std::move(factory));
 }
 
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index 5c50a07..56c58f9b 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -74,7 +74,7 @@
         (traits & PANEL) != 0 ? views::Widget::InitParams::TYPE_PANEL
                               : views::Widget::InitParams::TYPE_WINDOW);
     if ((traits & PANEL) != 0) {
-      params.mus_properties[ui::mojom::WindowManager::kInitialBounds_Property] =
+      params.mus_properties[ui::mojom::WindowManager::kBounds_InitProperty] =
           mojo::TypeConverter<std::vector<uint8_t>, gfx::Rect>::Convert(
               gfx::Rect(100, 100, 300, 300));
     }
diff --git a/mash/login/login.cc b/mash/login/login.cc
index d28929d..73d4688d 100644
--- a/mash/login/login.cc
+++ b/mash/login/login.cc
@@ -56,7 +56,7 @@
     params.delegate = ui;
 
     std::map<std::string, std::vector<uint8_t>> properties;
-    properties[ui::mojom::WindowManager::kInitialContainerId_Property] =
+    properties[ui::mojom::WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(
             ash::kShellWindowId_LockScreenContainer);
     ui::Window* window =
diff --git a/mash/login/manifest.json b/mash/login/manifest.json
index 4c4f33d..817341d 100644
--- a/mash/login/manifest.json
+++ b/mash/login/manifest.json
@@ -9,7 +9,7 @@
       "requires": {
         "*": [ "app" ],
         "mash_init": [ "mash_init:init" ],
-        "ui": [ "ui:user_access_manager" ],
+        "ui": [ "user_access_manager" ],
         "service_manager": [ "service_manager:all_users" ]
       }
     }
diff --git a/mash/package/BUILD.gn b/mash/package/BUILD.gn
index e0ab71b..7fe1221 100644
--- a/mash/package/BUILD.gn
+++ b/mash/package/BUILD.gn
@@ -9,9 +9,6 @@
   ]
 
   deps = [
-    "//ash/autoclick/mus:lib",
-    "//ash/mus:lib",
-    "//ash/touch_hud/mus:lib",
     "//mash/catalog_viewer:lib",
     "//mash/catalog_viewer/public/interfaces:constants",
     "//mash/public/interfaces",
@@ -27,6 +24,14 @@
     "//services/ui/ime/test_ime_driver:lib",
   ]
 
+  if (is_chromeos) {
+    deps += [
+      "//ash/autoclick/mus:lib",
+      "//ash/mus:lib",
+      "//ash/touch_hud/mus:lib",
+    ]
+  }
+
   if (is_linux && !is_android) {
     deps += [ "//components/font_service:lib" ]
   }
diff --git a/mash/package/mash_packaged_service.cc b/mash/package/mash_packaged_service.cc
index e309da0..869fddb 100644
--- a/mash/package/mash_packaged_service.cc
+++ b/mash/package/mash_packaged_service.cc
@@ -4,9 +4,6 @@
 
 #include "mash/package/mash_packaged_service.h"
 
-#include "ash/autoclick/mus/autoclick_application.h"
-#include "ash/mus/window_manager_application.h"
-#include "ash/touch_hud/mus/touch_hud_application.h"
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/debugger.h"
@@ -18,11 +15,18 @@
 #include "mash/session/session.h"
 #include "mash/task_viewer/public/interfaces/constants.mojom.h"
 #include "mash/task_viewer/task_viewer.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/ui/ime/test_ime_driver/test_ime_application.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "services/ui/service.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/autoclick/mus/autoclick_application.h"
+#include "ash/mus/window_manager_application.h"
+#include "ash/touch_hud/mus/touch_hud_application.h"
+#endif
+
 #if defined(OS_LINUX)
 #include "components/font_service/font_service_app.h"
 #endif
@@ -77,14 +81,17 @@
       base::debug::WaitForDebugger(120, true);
     }
   }
+
+#if defined(OS_CHROMEOS)
   if (name == "ash")
     return base::WrapUnique(new ash::mus::WindowManagerApplication);
   if (name == "accessibility_autoclick")
     return base::WrapUnique(new ash::autoclick::AutoclickApplication);
-  if (name == catalog_viewer::mojom::kServiceName)
-    return base::WrapUnique(new mash::catalog_viewer::CatalogViewer);
   if (name == "touch_hud")
     return base::WrapUnique(new ash::touch_hud::TouchHudApplication);
+#endif
+  if (name == catalog_viewer::mojom::kServiceName)
+    return base::WrapUnique(new mash::catalog_viewer::CatalogViewer);
   if (name == session::mojom::kServiceName)
     return base::WrapUnique(new mash::session::Session);
   if (name == ui::mojom::kServiceName)
diff --git a/mash/screenlock/screenlock.cc b/mash/screenlock/screenlock.cc
index b2a726b..426d0ba 100644
--- a/mash/screenlock/screenlock.cc
+++ b/mash/screenlock/screenlock.cc
@@ -96,7 +96,7 @@
   params.delegate = new ScreenlockView(context()->connector());
 
   std::map<std::string, std::vector<uint8_t>> properties;
-  properties[ui::mojom::WindowManager::kInitialContainerId_Property] =
+  properties[ui::mojom::WindowManager::kContainerId_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           ash::kShellWindowId_LockScreenContainer);
   ui::Window* window =
diff --git a/mash/simple_wm/manifest.json b/mash/simple_wm/manifest.json
index 32164e3..d0f38b6 100644
--- a/mash/simple_wm/manifest.json
+++ b/mash/simple_wm/manifest.json
@@ -5,7 +5,7 @@
     "service_manager:connector": {
       "requires": {
         "*": [ "app" ],
-        "ui": [ "ui:window_manager" ]
+        "ui": [ "window_manager" ]
       }
     }
   }
diff --git a/mash/unittests_manifest.json b/mash/unittests_manifest.json
index 07d87bd04..76940ab 100644
--- a/mash/unittests_manifest.json
+++ b/mash/unittests_manifest.json
@@ -5,7 +5,8 @@
     "service_manager:connector": {
       "requires": {
         "*": [ "app", "test" ],
-        "ash": [ "mus:window_manager" ]
+        "ash": [ "mus:window_manager" ],
+        "ui": [ "app" ]
       }
     }
   }
diff --git a/media/audio/audio_output_device.cc b/media/audio/audio_output_device.cc
index 2779174b..ae88ddef 100644
--- a/media/audio/audio_output_device.cc
+++ b/media/audio/audio_output_device.cc
@@ -325,8 +325,11 @@
   // different from OUTPUT_DEVICE_STATUS_OK, so the AudioOutputDevice
   // will enter the IPC_CLOSED state anyway, which is the safe thing to do.
   // This is preferable to holding a lock.
-  if (!did_receive_auth_.IsSignaled())
+  if (!did_receive_auth_.IsSignaled()) {
     device_status_ = device_status;
+    UMA_HISTOGRAM_ENUMERATION("Media.Audio.Render.OutputDeviceStatus",
+                              device_status, OUTPUT_DEVICE_STATUS_MAX + 1);
+  }
 
   if (device_status == OUTPUT_DEVICE_STATUS_OK) {
     state_ = AUTHORIZED;
diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc
index 9e3d1f8..78dfbc9 100644
--- a/media/audio/null_audio_sink.cc
+++ b/media/audio/null_audio_sink.cc
@@ -76,7 +76,7 @@
 }
 
 OutputDeviceInfo NullAudioSink::GetOutputDeviceInfo() {
-  return OutputDeviceInfo();
+  return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
 }
 
 bool NullAudioSink::CurrentThreadIsRenderingThread() {
diff --git a/media/base/android/stream_texture_wrapper.h b/media/base/android/stream_texture_wrapper.h
index 0d9dc2f8..ee092cd1 100644
--- a/media/base/android/stream_texture_wrapper.h
+++ b/media/base/android/stream_texture_wrapper.h
@@ -15,6 +15,8 @@
 // and registration for later retrieval (in the Browser process).
 class MEDIA_EXPORT StreamTextureWrapper {
  public:
+  using StreamTextureWrapperInitCB = base::Callback<void(bool)>;
+
   StreamTextureWrapper() {}
 
   // Initialize the underlying StreamTexture.
@@ -23,7 +25,7 @@
       const base::Closure& received_frame_cb,
       const gfx::Size& natural_size,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
-      const base::Closure& init_cb) = 0;
+      const StreamTextureWrapperInitCB& init_cb) = 0;
 
   // Called whenever the video's natural size changes.
   // See StreamTextureWrapperImpl.
diff --git a/media/base/cdm_session_tracker.cc b/media/base/cdm_session_tracker.cc
index a60de035..b5b89908f2 100644
--- a/media/base/cdm_session_tracker.cc
+++ b/media/base/cdm_session_tracker.cc
@@ -9,7 +9,7 @@
 CdmSessionTracker::CdmSessionTracker() {}
 
 CdmSessionTracker::~CdmSessionTracker() {
-  DCHECK(session_ids_.empty());
+  DCHECK(!HasRemainingSessions());
 }
 
 void CdmSessionTracker::AddSession(const std::string& session_id) {
@@ -32,4 +32,8 @@
     session_closed_cb.Run(session_id);
 }
 
+bool CdmSessionTracker::HasRemainingSessions() const {
+  return !session_ids_.empty();
+}
+
 }  // namespace media
diff --git a/media/base/cdm_session_tracker.h b/media/base/cdm_session_tracker.h
index c72e25c7..98cfbbc 100644
--- a/media/base/cdm_session_tracker.h
+++ b/media/base/cdm_session_tracker.h
@@ -22,16 +22,19 @@
   CdmSessionTracker();
   ~CdmSessionTracker();
 
-  // Add |session_id| to the list of sessions being tracked.
+  // Adds |session_id| to the list of sessions being tracked.
   void AddSession(const std::string& session_id);
 
-  // Remove |session_id| from the list of sessions being tracked.
+  // Removes |session_id| from the list of sessions being tracked.
   void RemoveSession(const std::string& session_id);
 
-  // Call |session_closed_cb| on any remaining sessions in the list and then
+  // Calls |session_closed_cb| on any remaining sessions in the list and then
   // clear the list.
   void CloseRemainingSessions(const SessionClosedCB& session_closed_cb);
 
+  // Returns whether there are any remaining sessions being tracked.
+  bool HasRemainingSessions() const;
+
  private:
   std::unordered_set<std::string> session_ids_;
 
diff --git a/media/base/media_keys.h b/media/base/media_keys.h
index ecf048f5..1b0303c 100644
--- a/media/base/media_keys.h
+++ b/media/base/media_keys.h
@@ -189,7 +189,7 @@
 // Called when the CDM changes the expiration time of a session.
 // See http://w3c.github.io/encrypted-media/#update-expiration
 typedef base::Callback<void(const std::string& session_id,
-                            const base::Time& new_expiry_time)>
+                            base::Time new_expiry_time)>
     SessionExpirationUpdateCB;
 
 }  // namespace media
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index f188d81..19ace24 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -126,6 +126,10 @@
 
 MockTextTrack::~MockTextTrack() {}
 
+MockCdmClient::MockCdmClient() {}
+
+MockCdmClient::~MockCdmClient() {}
+
 MockDecryptor::MockDecryptor() {}
 
 MockDecryptor::~MockDecryptor() {}
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index fc393c1d..bf58eea 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -16,9 +16,11 @@
 #include "media/base/audio_decoder_config.h"
 #include "media/base/audio_renderer.h"
 #include "media/base/cdm_context.h"
+#include "media/base/cdm_key_information.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decryptor.h"
 #include "media/base/demuxer.h"
+#include "media/base/media_keys.h"
 #include "media/base/media_track.h"
 #include "media/base/pipeline.h"
 #include "media/base/pipeline_status.h"
@@ -331,6 +333,41 @@
   DISALLOW_COPY_AND_ASSIGN(MockTextTrack);
 };
 
+// Mock CDM callbacks.
+// TODO(xhwang): This could be a subclass of CdmClient if we plan to add one.
+// See http://crbug.com/657940
+class MockCdmClient {
+ public:
+  MockCdmClient();
+  virtual ~MockCdmClient();
+
+  MOCK_METHOD3(OnSessionMessage,
+               void(const std::string& session_id,
+                    MediaKeys::MessageType message_type,
+                    const std::vector<uint8_t>& message));
+  MOCK_METHOD1(OnSessionClosed, void(const std::string& session_id));
+
+  // MOCK methods don't work with move-only types like CdmKeysInfo. Add an extra
+  // OnSessionKeysChangeCalled() function to work around this.
+  MOCK_METHOD2(OnSessionKeysChangeCalled,
+               void(const std::string& session_id,
+                    bool has_additional_usable_key));
+  void OnSessionKeysChange(const std::string& session_id,
+                           bool has_additional_usable_key,
+                           CdmKeysInfo keys_info) {
+    keys_info_.swap(keys_info);
+    OnSessionKeysChangeCalled(session_id, has_additional_usable_key);
+  }
+
+  MOCK_METHOD2(OnSessionExpirationUpdate,
+               void(const std::string& session_id, base::Time new_expiry_time));
+
+  const CdmKeysInfo& keys_info() const { return keys_info_; }
+
+ private:
+  CdmKeysInfo keys_info_;
+};
+
 class MockDecryptor : public Decryptor {
  public:
   MockDecryptor();
diff --git a/media/base/output_device_info.h b/media/base/output_device_info.h
index 9f90fed..96469d55 100644
--- a/media/base/output_device_info.h
+++ b/media/base/output_device_info.h
@@ -20,7 +20,10 @@
   OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
   OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT,
   OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
-  OUTPUT_DEVICE_STATUS_LAST = OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
+  // Add new values only above this line; do not changes existing values. Make
+  // sure OutputDeviceStatus enum in histoframs.xml is updated with any change
+  // here.
+  OUTPUT_DEVICE_STATUS_MAX = OUTPUT_DEVICE_STATUS_ERROR_INTERNAL
 };
 
 using OutputDeviceStatusCB = base::Callback<void(OutputDeviceStatus)>;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 3963eab..85d470a 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -571,6 +571,16 @@
     const std::vector<MediaTrack::Id>& enabledTrackIds) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
 
+  // If the pipeline has been created, but not started yet, we may still receive
+  // track notifications from blink level (e.g. when video track gets deselected
+  // due to player/pipeline belonging to a background tab). We can safely ignore
+  // these, since WebMediaPlayerImpl will ensure that demuxer stream / track
+  // status is in sync with blink after pipeline is started.
+  if (state_ == kCreated) {
+    DCHECK(!demuxer_);
+    return;
+  }
+
   // Track status notifications might be delivered asynchronously. If we receive
   // a notification when pipeline is stopped/shut down, it's safe to ignore it.
   if (state_ == kStopping || state_ == kStopped) {
@@ -590,6 +600,16 @@
     const std::vector<MediaTrack::Id>& selectedTrackId) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
 
+  // If the pipeline has been created, but not started yet, we may still receive
+  // track notifications from blink level (e.g. when video track gets deselected
+  // due to player/pipeline belonging to a background tab). We can safely ignore
+  // these, since WebMediaPlayerImpl will ensure that demuxer stream / track
+  // status is in sync with blink after pipeline is started.
+  if (state_ == kCreated) {
+    DCHECK(!demuxer_);
+    return;
+  }
+
   // Track status notifications might be delivered asynchronously. If we receive
   // a notification when pipeline is stopped/shut down, it's safe to ignore it.
   if (state_ == kStopping || state_ == kStopped) {
diff --git a/media/base/sinc_resampler.cc b/media/base/sinc_resampler.cc
index 99ef3741..cffb0c9d 100644
--- a/media/base/sinc_resampler.cc
+++ b/media/base/sinc_resampler.cc
@@ -81,7 +81,6 @@
 #include <cmath>
 #include <limits>
 
-#include "base/debug/alias.h"
 #include "base/logging.h"
 #include "build/build_config.h"
 
@@ -197,10 +196,9 @@
 
       // Compute the sinc with offset, then window the sinc() function and store
       // at the correct offset.
-      kernel_storage_[idx] = static_cast<float>(window *
-          ((pre_sinc == 0) ?
-              sinc_scale_factor :
-              (sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
+      kernel_storage_[idx] = static_cast<float>(
+          window * (pre_sinc ? sin(sinc_scale_factor * pre_sinc) / pre_sinc
+                             : sinc_scale_factor));
     }
   }
 }
@@ -223,10 +221,9 @@
       const float window = kernel_window_storage_[idx];
       const float pre_sinc = kernel_pre_sinc_storage_[idx];
 
-      kernel_storage_[idx] = static_cast<float>(window *
-          ((pre_sinc == 0) ?
-              sinc_scale_factor :
-              (sin(sinc_scale_factor * pre_sinc) / pre_sinc)));
+      kernel_storage_[idx] = static_cast<float>(
+          window * (pre_sinc ? sin(sinc_scale_factor * pre_sinc) / pre_sinc
+                             : sinc_scale_factor));
     }
   }
 }
@@ -240,33 +237,20 @@
     buffer_primed_ = true;
   }
 
-  // TODO(dalecurtis): Temporary debugging for http://crbug.com/663814
-  const double starting_idx = virtual_source_idx_;
-  CHECK(!std::isnan(virtual_source_idx_));
-
-  // Step (2) -- Resample!  const what we can outside of the loop for speed.  It
-  // actually has an impact on ARM performance.  See inner loop comment below.
-  const double current_io_ratio = io_sample_rate_ratio_;
-  const float* const kernel_ptr = kernel_storage_.get();
+  // Step (2) -- Resample!
   while (remaining_frames) {
-    // Note: The loop construct here can severely impact performance on ARM
-    // or when built with clang.  See https://codereview.chromium.org/18566009/
-    int source_idx = static_cast<int>(virtual_source_idx_);
-    // TODO(dalecurtis): Temporary debugging for http://crbug.com/663814
-    CHECK_GE(source_idx, 0);
-    while (source_idx < block_size_) {
+    while (virtual_source_idx_ < block_size_) {
       // |virtual_source_idx_| lies in between two kernel offsets so figure out
       // what they are.
-      const double subsample_remainder = virtual_source_idx_ - source_idx;
-
+      const int source_idx = static_cast<int>(virtual_source_idx_);
       const double virtual_offset_idx =
-          subsample_remainder * kKernelOffsetCount;
+          (virtual_source_idx_ - source_idx) * kKernelOffsetCount;
       const int offset_idx = static_cast<int>(virtual_offset_idx);
 
       // We'll compute "convolutions" for the two kernels which straddle
       // |virtual_source_idx_|.
-      const float* const k1 = kernel_ptr + offset_idx * kKernelSize;
-      const float* const k2 = k1 + kKernelSize;
+      const float* k1 = kernel_storage_.get() + offset_idx * kKernelSize;
+      const float* k2 = k1 + kKernelSize;
 
       // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage.  Should always be
       // true so long as kKernelSize is a multiple of 16.
@@ -274,22 +258,16 @@
       DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(k2) & 0x0F);
 
       // Initialize input pointer based on quantized |virtual_source_idx_|.
-      const float* const input_ptr = r1_ + source_idx;
+      const float* input_ptr = r1_ + source_idx;
 
       // Figure out how much to weight each kernel's "convolution".
       const double kernel_interpolation_factor =
           virtual_offset_idx - offset_idx;
-      *destination++ = CONVOLVE_FUNC(
-          input_ptr, k1, k2, kernel_interpolation_factor);
+      *destination++ =
+          CONVOLVE_FUNC(input_ptr, k1, k2, kernel_interpolation_factor);
 
       // Advance the virtual index.
-      virtual_source_idx_ += current_io_ratio;
-      source_idx = static_cast<int>(virtual_source_idx_);
-
-      // TODO(dalecurtis): Temporary debugging for http://crbug.com/663814
-      base::debug::Alias(&starting_idx);
-      CHECK(!std::isnan(virtual_source_idx_));
-      CHECK_GE(source_idx, 0);
+      virtual_source_idx_ += io_sample_rate_ratio_;
       if (!--remaining_frames)
         return;
     }
@@ -298,10 +276,6 @@
     DCHECK_GE(virtual_source_idx_, block_size_);
     virtual_source_idx_ -= block_size_;
 
-    // TODO(dalecurtis): Temporary debugging for http://crbug.com/663814
-    base::debug::Alias(&starting_idx);
-    CHECK(!std::isnan(virtual_source_idx_));
-
     // Step (3) -- Copy r3_, r4_ to r1_, r2_.
     // This wraps the last input frames back to the start of the buffer.
     memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize);
@@ -332,11 +306,7 @@
 }
 
 double SincResampler::BufferedFrames() const {
-  if (buffer_primed_) {
-    return request_frames_ - virtual_source_idx_;
-  } else {
-    return 0.0;
-  }
+  return buffer_primed_ ? request_frames_ - virtual_source_idx_ : 0;
 }
 
 float SincResampler::Convolve_C(const float* input_ptr, const float* k1,
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index c532f16c..2a0c321 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -152,6 +152,11 @@
 }
 
 // static
+VideoDecoderConfig TestVideoConfig::NormalH264() {
+  return GetTestConfig(kCodecH264, kNormalSize, false);
+}
+
+// static
 VideoDecoderConfig TestVideoConfig::NormalEncrypted() {
   return GetTestConfig(kCodecVP8, kNormalSize, true);
 }
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index 833c0466..afad1f7 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -84,6 +84,7 @@
   static VideoDecoderConfig Invalid();
 
   static VideoDecoderConfig Normal();
+  static VideoDecoderConfig NormalH264();
   static VideoDecoderConfig NormalEncrypted();
 
   // Returns a configuration that is larger in dimensions than Normal().
diff --git a/media/base/video_frame_metadata.cc b/media/base/video_frame_metadata.cc
index 388e001..5d670a19 100644
--- a/media/base/video_frame_metadata.cc
+++ b/media/base/video_frame_metadata.cc
@@ -170,7 +170,7 @@
   const base::Value* internal_value = nullptr;
   if (dictionary_.GetWithoutPathExpansion(ToInternalKey(key),
                                           &internal_value) &&
-      internal_value->GetType() == base::Value::TYPE_BINARY) {
+      internal_value->GetType() == base::Value::Type::BINARY) {
     return static_cast<const base::BinaryValue*>(internal_value);
   }
   return nullptr;
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc
index 865db82..3fe70e93 100644
--- a/media/base/video_frame_unittest.cc
+++ b/media/base/video_frame_unittest.cc
@@ -544,7 +544,7 @@
     EXPECT_TRUE(metadata.HasKey(key));
     const base::Value* const null_value = metadata.GetValue(key);
     EXPECT_TRUE(null_value);
-    EXPECT_EQ(base::Value::TYPE_NULL, null_value->GetType());
+    EXPECT_EQ(base::Value::Type::NONE, null_value->GetType());
     metadata.Clear();
   }
 }
diff --git a/media/blink/cdm_session_adapter.cc b/media/blink/cdm_session_adapter.cc
index 97d3f7a..9f87ca930 100644
--- a/media/blink/cdm_session_adapter.cc
+++ b/media/blink/cdm_session_adapter.cc
@@ -191,9 +191,8 @@
                                  std::move(keys_info));
 }
 
-void CdmSessionAdapter::OnSessionExpirationUpdate(
-    const std::string& session_id,
-    const base::Time& new_expiry_time) {
+void CdmSessionAdapter::OnSessionExpirationUpdate(const std::string& session_id,
+                                                  base::Time new_expiry_time) {
   WebContentDecryptionModuleSessionImpl* session = GetSession(session_id);
   DLOG_IF(WARNING, !session) << __func__ << " for unknown session "
                              << session_id;
diff --git a/media/blink/cdm_session_adapter.h b/media/blink/cdm_session_adapter.h
index ca75340..ca2d6f12 100644
--- a/media/blink/cdm_session_adapter.h
+++ b/media/blink/cdm_session_adapter.h
@@ -123,7 +123,7 @@
                            bool has_additional_usable_key,
                            CdmKeysInfo keys_info);
   void OnSessionExpirationUpdate(const std::string& session_id,
-                                 const base::Time& new_expiry_time);
+                                 base::Time new_expiry_time);
   void OnSessionClosed(const std::string& session_id);
 
   // Helper function of the callbacks.
diff --git a/media/blink/webaudiosourceprovider_impl.cc b/media/blink/webaudiosourceprovider_impl.cc
index 8d320c8..abe275c 100644
--- a/media/blink/webaudiosourceprovider_impl.cc
+++ b/media/blink/webaudiosourceprovider_impl.cc
@@ -11,8 +11,12 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "media/audio/null_audio_sink.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/media_log.h"
 #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"
 
 using blink::WebVector;
@@ -94,12 +98,14 @@
 };
 
 WebAudioSourceProviderImpl::WebAudioSourceProviderImpl(
-    const scoped_refptr<SwitchableAudioRendererSink>& sink)
+    scoped_refptr<SwitchableAudioRendererSink> sink,
+    scoped_refptr<MediaLog> media_log)
     : volume_(1.0),
       state_(kStopped),
       client_(nullptr),
-      sink_(sink),
+      sink_(std::move(sink)),
       tee_filter_(new TeeFilter()),
+      media_log_(std::move(media_log)),
       weak_factory_(this) {}
 
 WebAudioSourceProviderImpl::~WebAudioSourceProviderImpl() {
@@ -115,7 +121,8 @@
   base::AutoLock auto_lock(sink_lock_);
   if (client) {
     // Detach the audio renderer from normal playback.
-    sink_->Stop();
+    if (sink_)
+      sink_->Stop();
 
     // The client will now take control by calling provideInput() periodically.
     client_ = client;
@@ -134,11 +141,13 @@
 
   // Restore normal playback.
   client_ = nullptr;
-  sink_->SetVolume(volume_);
-  if (state_ >= kStarted)
-    sink_->Start();
-  if (state_ >= kPlaying)
-    sink_->Play();
+  if (sink_) {
+    sink_->SetVolume(volume_);
+    if (state_ >= kStarted)
+      sink_->Start();
+    if (state_ >= kPlaying)
+      sink_->Play();
+  }
 }
 
 void WebAudioSourceProviderImpl::provideInput(
@@ -177,6 +186,22 @@
   base::AutoLock auto_lock(sink_lock_);
   DCHECK_EQ(state_, kStopped);
 
+  OutputDeviceStatus device_status =
+      sink_ ? sink_->GetOutputDeviceInfo().device_status()
+            : OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND;
+
+  UMA_HISTOGRAM_ENUMERATION("Media.WebAudioSourceProvider.SinkStatus",
+                            device_status, OUTPUT_DEVICE_STATUS_MAX + 1);
+
+  if (device_status != OUTPUT_DEVICE_STATUS_OK) {
+    // Since null sink is always OK, we will fall back to it once and forever.
+    if (sink_)
+      sink_->Stop();
+    sink_ = CreateFallbackSink();
+    MEDIA_LOG(ERROR, media_log_)
+        << "Output device error, falling back to null sink";
+  }
+
   tee_filter_->Initialize(renderer, params.channels(), params.sample_rate());
 
   sink_->Initialize(params, tee_filter_.get());
@@ -220,14 +245,15 @@
 bool WebAudioSourceProviderImpl::SetVolume(double volume) {
   base::AutoLock auto_lock(sink_lock_);
   volume_ = volume;
-  if (!client_)
+  if (!client_ && sink_)
     sink_->SetVolume(volume);
   return true;
 }
 
-media::OutputDeviceInfo WebAudioSourceProviderImpl::GetOutputDeviceInfo() {
+OutputDeviceInfo WebAudioSourceProviderImpl::GetOutputDeviceInfo() {
   base::AutoLock auto_lock(sink_lock_);
-  return sink_->GetOutputDeviceInfo();
+  return sink_ ? sink_->GetOutputDeviceInfo()
+               : OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND);
 }
 
 bool WebAudioSourceProviderImpl::CurrentThreadIsRenderingThread() {
@@ -240,8 +266,8 @@
     const url::Origin& security_origin,
     const OutputDeviceStatusCB& callback) {
   base::AutoLock auto_lock(sink_lock_);
-  if (client_)
-    callback.Run(media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
+  if (client_ || !sink_)
+    callback.Run(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
   else
     sink_->SwitchOutputDevice(device_id, security_origin, callback);
 }
@@ -276,6 +302,12 @@
   client_->setFormat(tee_filter_->channels(), tee_filter_->sample_rate());
 }
 
+scoped_refptr<SwitchableAudioRendererSink>
+WebAudioSourceProviderImpl::CreateFallbackSink() {
+  // Assuming it is called on media thread.
+  return new NullAudioSink(base::ThreadTaskRunnerHandle::Get());
+}
+
 int WebAudioSourceProviderImpl::TeeFilter::Render(
     base::TimeDelta delay,
     base::TimeTicks delay_timestamp,
diff --git a/media/blink/webaudiosourceprovider_impl.h b/media/blink/webaudiosourceprovider_impl.h
index 1ffdd90..aa43595 100644
--- a/media/blink/webaudiosourceprovider_impl.h
+++ b/media/blink/webaudiosourceprovider_impl.h
@@ -23,6 +23,7 @@
 }
 
 namespace media {
+class MediaLog;
 
 // WebAudioSourceProviderImpl is either one of two things (but not both):
 // - a connection between a RestartableAudioRendererSink (the |sink_|) passed in
@@ -47,8 +48,8 @@
                                           uint32_t frames_delayed,
                                           int sample_rate)>;
 
-  explicit WebAudioSourceProviderImpl(
-      const scoped_refptr<SwitchableAudioRendererSink>& sink);
+  WebAudioSourceProviderImpl(scoped_refptr<SwitchableAudioRendererSink> sink,
+                             scoped_refptr<MediaLog> media_log);
 
   // blink::WebAudioSourceProvider implementation.
   void setClient(blink::WebAudioSourceProviderClient* client) override;
@@ -75,9 +76,12 @@
 
   int RenderForTesting(AudioBus* audio_bus);
 
+ protected:
+  virtual scoped_refptr<SwitchableAudioRendererSink> CreateFallbackSink();
+  ~WebAudioSourceProviderImpl() override;
+
  private:
   friend class WebAudioSourceProviderImplTest;
-  ~WebAudioSourceProviderImpl() override;
 
   // Calls setFormat() on |client_| from the Blink renderer thread.
   void OnSetFormat();
@@ -96,13 +100,15 @@
 
   // Where audio ends up unless overridden by |client_|.
   base::Lock sink_lock_;
-  const scoped_refptr<SwitchableAudioRendererSink> sink_;
+  scoped_refptr<SwitchableAudioRendererSink> sink_;
   std::unique_ptr<AudioBus> bus_wrapper_;
 
   // An inner class acting as a T filter where actual data can be tapped.
   class TeeFilter;
   const std::unique_ptr<TeeFilter> tee_filter_;
 
+  const scoped_refptr<MediaLog> media_log_;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<WebAudioSourceProviderImpl> weak_factory_;
 
diff --git a/media/blink/webaudiosourceprovider_impl_unittest.cc b/media/blink/webaudiosourceprovider_impl_unittest.cc
index 4a6d22f..fdc51b1e 100644
--- a/media/blink/webaudiosourceprovider_impl_unittest.cc
+++ b/media/blink/webaudiosourceprovider_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/run_loop.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/fake_audio_render_callback.h"
+#include "media/base/media_log.h"
 #include "media/base/mock_audio_renderer_sink.h"
 #include "media/blink/webaudiosourceprovider_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -22,38 +23,75 @@
 
 namespace {
 const float kTestVolume = 0.25;
+
+class WebAudioSourceProviderImplUnderTest : public WebAudioSourceProviderImpl {
+ public:
+  WebAudioSourceProviderImplUnderTest(
+      scoped_refptr<SwitchableAudioRendererSink> sink)
+      : WebAudioSourceProviderImpl(std::move(sink), new MediaLog()),
+        fallback_sink_(new MockAudioRendererSink()) {}
+
+  MockAudioRendererSink* fallback_sink() { return fallback_sink_.get(); }
+
+ protected:
+  scoped_refptr<SwitchableAudioRendererSink> CreateFallbackSink() override {
+    return fallback_sink_;
+  }
+
+ private:
+  ~WebAudioSourceProviderImplUnderTest() override = default;
+  scoped_refptr<MockAudioRendererSink> fallback_sink_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAudioSourceProviderImplUnderTest);
+};
+
+enum class WaspSinkStatus { WASP_SINK_OK, WASP_SINK_ERROR, WASP_SINK_NULL };
+
+scoped_refptr<MockAudioRendererSink> CreateWaspMockSink(WaspSinkStatus status) {
+  if (status == WaspSinkStatus::WASP_SINK_NULL)
+    return nullptr;
+  return new MockAudioRendererSink(status == WaspSinkStatus::WASP_SINK_OK
+                                       ? OUTPUT_DEVICE_STATUS_OK
+                                       : OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
+}
+
 }  // namespace
 
 class WebAudioSourceProviderImplTest
-    : public testing::Test,
+    : public testing::TestWithParam<WaspSinkStatus>,
       public blink::WebAudioSourceProviderClient {
  public:
   WebAudioSourceProviderImplTest()
       : params_(AudioParameters::AUDIO_PCM_LINEAR,
-                CHANNEL_LAYOUT_STEREO, 48000, 16, 64),
+                CHANNEL_LAYOUT_STEREO,
+                48000,
+                16,
+                64),
         fake_callback_(0.1),
-        mock_sink_(new MockAudioRendererSink()),
-        wasp_impl_(new WebAudioSourceProviderImpl(mock_sink_)) {
-  }
+        mock_sink_(CreateWaspMockSink(GetParam())),
+        wasp_impl_(new WebAudioSourceProviderImplUnderTest(mock_sink_)),
+        expected_sink_(GetParam() == WaspSinkStatus::WASP_SINK_OK
+                           ? mock_sink_.get()
+                           : wasp_impl_->fallback_sink()) {}
 
   virtual ~WebAudioSourceProviderImplTest() {}
 
   void CallAllSinkMethodsAndVerify(bool verify) {
     testing::InSequence s;
 
-    EXPECT_CALL(*mock_sink_.get(), Start()).Times(verify);
+    EXPECT_CALL(*expected_sink_, Start()).Times(verify);
     wasp_impl_->Start();
 
-    EXPECT_CALL(*mock_sink_.get(), Play()).Times(verify);
+    EXPECT_CALL(*expected_sink_, Play()).Times(verify);
     wasp_impl_->Play();
 
-    EXPECT_CALL(*mock_sink_.get(), Pause()).Times(verify);
+    EXPECT_CALL(*expected_sink_, Pause()).Times(verify);
     wasp_impl_->Pause();
 
-    EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume)).Times(verify);
+    EXPECT_CALL(*expected_sink_, SetVolume(kTestVolume)).Times(verify);
     wasp_impl_->SetVolume(kTestVolume);
 
-    EXPECT_CALL(*mock_sink_.get(), Stop()).Times(verify);
+    EXPECT_CALL(*expected_sink_, Stop()).Times(verify);
     wasp_impl_->Stop();
 
     testing::Mock::VerifyAndClear(mock_sink_.get());
@@ -63,13 +101,14 @@
     testing::InSequence s;
 
     if (client) {
-      EXPECT_CALL(*mock_sink_.get(), Stop());
+      EXPECT_CALL(*expected_sink_, Stop());
       EXPECT_CALL(*this, setFormat(params_.channels(), params_.sample_rate()));
     }
     wasp_impl_->setClient(client);
     base::RunLoop().RunUntilIdle();
 
     testing::Mock::VerifyAndClear(mock_sink_.get());
+    testing::Mock::VerifyAndClear(wasp_impl_->fallback_sink());
     testing::Mock::VerifyAndClear(this);
   }
 
@@ -101,21 +140,41 @@
     return wasp_impl_->RenderForTesting(audio_bus);
   }
 
+  void ExpectUnhealthySinkToStop() {
+    if (GetParam() == WaspSinkStatus::WASP_SINK_ERROR)
+      EXPECT_CALL(*mock_sink_.get(), Stop());
+  }
+
  protected:
   AudioParameters params_;
   FakeAudioRenderCallback fake_callback_;
   scoped_refptr<MockAudioRendererSink> mock_sink_;
-  scoped_refptr<WebAudioSourceProviderImpl> wasp_impl_;
+  scoped_refptr<WebAudioSourceProviderImplUnderTest> wasp_impl_;
+  MockAudioRendererSink* expected_sink_;
   base::MessageLoop message_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(WebAudioSourceProviderImplTest);
 };
 
-TEST_F(WebAudioSourceProviderImplTest, SetClientBeforeInitialize) {
+TEST_P(WebAudioSourceProviderImplTest, SetClientBeforeInitialize) {
   // setClient() with a NULL client should do nothing if no client is set.
   wasp_impl_->setClient(NULL);
 
-  EXPECT_CALL(*mock_sink_.get(), Stop());
+  // If |mock_sink_| is not null, it should be stopped during setClient(this).
+  // If it is unhealthy, it should also be stopped during fallback in
+  // Initialize().
+  if (mock_sink_)
+    EXPECT_CALL(*mock_sink_.get(), Stop())
+        .Times(2 + static_cast<int>(GetParam()));
+
+  wasp_impl_->setClient(this);
+  base::RunLoop().RunUntilIdle();
+
+  if (mock_sink_)
+    EXPECT_CALL(*mock_sink_.get(), SetVolume(1)).Times(1);
+  wasp_impl_->setClient(NULL);
+  base::RunLoop().RunUntilIdle();
+
   wasp_impl_->setClient(this);
   base::RunLoop().RunUntilIdle();
 
@@ -131,7 +190,8 @@
 }
 
 // Verify AudioRendererSink functionality w/ and w/o a client.
-TEST_F(WebAudioSourceProviderImplTest, SinkMethods) {
+TEST_P(WebAudioSourceProviderImplTest, SinkMethods) {
+  ExpectUnhealthySinkToStop();
   wasp_impl_->Initialize(params_, &fake_callback_);
 
   // Without a client all WASP calls should fall through to the underlying sink.
@@ -143,22 +203,23 @@
   CallAllSinkMethodsAndVerify(false);
 
   // Removing the client should cause WASP to revert to the underlying sink.
-  EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume));
+  EXPECT_CALL(*expected_sink_, SetVolume(kTestVolume));
   SetClient(NULL);
   CallAllSinkMethodsAndVerify(true);
 }
 
 // Verify underlying sink state is restored after client removal.
-TEST_F(WebAudioSourceProviderImplTest, SinkStateRestored) {
+TEST_P(WebAudioSourceProviderImplTest, SinkStateRestored) {
+  ExpectUnhealthySinkToStop();
   wasp_impl_->Initialize(params_, &fake_callback_);
 
   // Verify state set before the client is set propagates back afterward.
-  EXPECT_CALL(*mock_sink_.get(), Start());
+  EXPECT_CALL(*expected_sink_, Start());
   wasp_impl_->Start();
   SetClient(this);
 
-  EXPECT_CALL(*mock_sink_.get(), SetVolume(1.0));
-  EXPECT_CALL(*mock_sink_.get(), Start());
+  EXPECT_CALL(*expected_sink_, SetVolume(1.0));
+  EXPECT_CALL(*expected_sink_, Start());
   SetClient(NULL);
 
   // Verify state set while the client was attached propagates back afterward.
@@ -166,14 +227,15 @@
   wasp_impl_->Play();
   wasp_impl_->SetVolume(kTestVolume);
 
-  EXPECT_CALL(*mock_sink_.get(), SetVolume(kTestVolume));
-  EXPECT_CALL(*mock_sink_.get(), Start());
-  EXPECT_CALL(*mock_sink_.get(), Play());
+  EXPECT_CALL(*expected_sink_, SetVolume(kTestVolume));
+  EXPECT_CALL(*expected_sink_, Start());
+  EXPECT_CALL(*expected_sink_, Play());
   SetClient(NULL);
 }
 
 // Test the AudioRendererSink state machine and its effects on provideInput().
-TEST_F(WebAudioSourceProviderImplTest, ProvideInput) {
+TEST_P(WebAudioSourceProviderImplTest, ProvideInput) {
+  ExpectUnhealthySinkToStop();
   std::unique_ptr<AudioBus> bus1 = AudioBus::Create(params_);
   std::unique_ptr<AudioBus> bus2 = AudioBus::Create(params_);
 
@@ -259,7 +321,9 @@
 }
 
 // Verify CopyAudioCB is called if registered.
-TEST_F(WebAudioSourceProviderImplTest, CopyAudioCB) {
+TEST_P(WebAudioSourceProviderImplTest, CopyAudioCB) {
+  ExpectUnhealthySinkToStop();
+
   testing::InSequence s;
   wasp_impl_->Initialize(params_, &fake_callback_);
   wasp_impl_->SetCopyAudioCallback(base::Bind(
@@ -276,4 +340,11 @@
   testing::Mock::VerifyAndClear(mock_sink_.get());
 }
 
+INSTANTIATE_TEST_CASE_P(
+    /* prefix intentionally left blank due to only one parameterization */,
+    WebAudioSourceProviderImplTest,
+    testing::Values(WaspSinkStatus::WASP_SINK_OK,
+                    WaspSinkStatus::WASP_SINK_ERROR,
+                    WaspSinkStatus::WASP_SINK_NULL));
+
 }  // namespace media
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc
index 7499874..5b2aadb 100644
--- a/media/blink/webcontentdecryptionmodulesession_impl.cc
+++ b/media/blink/webcontentdecryptionmodulesession_impl.cc
@@ -488,7 +488,7 @@
 }
 
 void WebContentDecryptionModuleSessionImpl::OnSessionExpirationUpdate(
-    const base::Time& new_expiry_time) {
+    base::Time new_expiry_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
   client_->expirationChanged(new_expiry_time.ToJsTime());
 }
diff --git a/media/blink/webcontentdecryptionmodulesession_impl.h b/media/blink/webcontentdecryptionmodulesession_impl.h
index 6824e6cc..8af796cc 100644
--- a/media/blink/webcontentdecryptionmodulesession_impl.h
+++ b/media/blink/webcontentdecryptionmodulesession_impl.h
@@ -56,7 +56,7 @@
                         const std::vector<uint8_t>& message);
   void OnSessionKeysChange(bool has_additional_usable_key,
                            CdmKeysInfo keys_info);
-  void OnSessionExpirationUpdate(const base::Time& new_expiry_time);
+  void OnSessionExpirationUpdate(base::Time new_expiry_time);
   void OnSessionClosed();
 
  private:
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 578f7ed..a105327d 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -189,7 +189,7 @@
       highest_ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
       preload_(MultibufferDataSource::AUTO),
       buffering_strategy_(MultibufferDataSource::BUFFERING_STRATEGY_NORMAL),
-      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      main_task_runner_(frame->loadingTaskRunner()),
       media_task_runner_(params.media_task_runner()),
       worker_task_runner_(params.worker_task_runner()),
       media_log_(params.media_log()),
@@ -264,15 +264,8 @@
 
   // TODO(xhwang): When we use an external Renderer, many methods won't work,
   // e.g. GetCurrentFrameFromCompositor(). See http://crbug.com/434861
-
-  // Use the null sink if no valid sink was provided.
-  audio_source_provider_ = new WebAudioSourceProviderImpl(
-      params.audio_renderer_sink().get() &&
-              params.audio_renderer_sink()
-                      ->GetOutputDeviceInfo()
-                      .device_status() == OUTPUT_DEVICE_STATUS_OK
-          ? params.audio_renderer_sink()
-          : new NullAudioSink(media_task_runner_));
+  audio_source_provider_ =
+      new WebAudioSourceProviderImpl(params.audio_renderer_sink(), media_log_);
 }
 
 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
diff --git a/media/capture/mojo/video_capture_types.mojom b/media/capture/mojo/video_capture_types.mojom
index 55a4e0cd..d020527 100644
--- a/media/capture/mojo/video_capture_types.mojom
+++ b/media/capture/mojo/video_capture_types.mojom
@@ -5,7 +5,8 @@
 module media.mojom;
 
 import "media/mojo/interfaces/media_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
+import "mojo/common/values.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 enum ResolutionChangePolicy {
diff --git a/media/capture/video/linux/video_capture_device_linux.cc b/media/capture/video/linux/video_capture_device_linux.cc
index a1b285505..c6269ac 100644
--- a/media/capture/video/linux/video_capture_device_linux.cc
+++ b/media/capture/video/linux/video_capture_device_linux.cc
@@ -69,6 +69,10 @@
                  params.requested_format.frame_size.width(),
                  params.requested_format.frame_size.height(),
                  params.requested_format.frame_rate, base::Passed(&client)));
+
+  for (const auto& request : photo_requests_queue_)
+    v4l2_thread_.task_runner()->PostTask(FROM_HERE, request);
+  photo_requests_queue_.clear();
 }
 
 void VideoCaptureDeviceLinux::StopAndDeAllocate() {
@@ -84,31 +88,40 @@
 
 void VideoCaptureDeviceLinux::TakePhoto(TakePhotoCallback callback) {
   DCHECK(capture_impl_);
-  if (!v4l2_thread_.IsRunning())
+  auto functor = base::Bind(&V4L2CaptureDelegate::TakePhoto, capture_impl_,
+                            base::Passed(&callback));
+  if (!v4l2_thread_.IsRunning()) {
+    // We have to wait until we get the device AllocateAndStart()ed.
+    photo_requests_queue_.push_back(std::move(functor));
     return;
-  v4l2_thread_.task_runner()->PostTask(
-      FROM_HERE, base::Bind(&V4L2CaptureDelegate::TakePhoto, capture_impl_,
-                            base::Passed(&callback)));
+  }
+  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
 }
 
 void VideoCaptureDeviceLinux::GetPhotoCapabilities(
     GetPhotoCapabilitiesCallback callback) {
-  if (!v4l2_thread_.IsRunning())
-    return;  // Wrong state.
-  v4l2_thread_.task_runner()->PostTask(
-      FROM_HERE, base::Bind(&V4L2CaptureDelegate::GetPhotoCapabilities,
-                            capture_impl_, base::Passed(&callback)));
+  auto functor = base::Bind(&V4L2CaptureDelegate::GetPhotoCapabilities,
+                            capture_impl_, base::Passed(&callback));
+  if (!v4l2_thread_.IsRunning()) {
+    // We have to wait until we get the device AllocateAndStart()ed.
+    photo_requests_queue_.push_back(std::move(functor));
+    return;
+  }
+  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
 }
 
 void VideoCaptureDeviceLinux::SetPhotoOptions(
     mojom::PhotoSettingsPtr settings,
     SetPhotoOptionsCallback callback) {
-  if (!v4l2_thread_.IsRunning())
-    return;  // Wrong state.
-  v4l2_thread_.task_runner()->PostTask(
-      FROM_HERE,
+  auto functor =
       base::Bind(&V4L2CaptureDelegate::SetPhotoOptions, capture_impl_,
-                 base::Passed(&settings), base::Passed(&callback)));
+                 base::Passed(&settings), base::Passed(&callback));
+  if (!v4l2_thread_.IsRunning()) {
+    // We have to wait until we get the device AllocateAndStart()ed.
+    photo_requests_queue_.push_back(std::move(functor));
+    return;
+  }
+  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
 }
 
 void VideoCaptureDeviceLinux::SetRotation(int rotation) {
diff --git a/media/capture/video/linux/video_capture_device_linux.h b/media/capture/video/linux/video_capture_device_linux.h
index e8adbde..0fcf1cd8 100644
--- a/media/capture/video/linux/video_capture_device_linux.h
+++ b/media/capture/video/linux/video_capture_device_linux.h
@@ -55,6 +55,9 @@
   // VideoCaptureDeviceLinux lives but otherwise operating on |v4l2_thread_|.
   scoped_refptr<V4L2CaptureDelegate> capture_impl_;
 
+  // Photo-related requests waiting for |v4l2_thread_| to be active.
+  std::list<base::Closure> photo_requests_queue_;
+
   base::Thread v4l2_thread_;  // Thread used for reading data from the device.
 
   const VideoCaptureDeviceDescriptor device_descriptor_;
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index 11dc054..c4c191d1 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -38,15 +38,18 @@
 using ::testing::IsNull;
 using ::testing::NotNull;
 using ::testing::SaveArg;
+using ::testing::StrictMock;
 using ::testing::StrNe;
 using ::testing::Unused;
 
 MATCHER(IsEmpty, "") { return arg.empty(); }
-MATCHER(IsNotEmpty, "") { return !arg.empty(); }
+MATCHER(NotEmpty, "") {
+  return !arg.empty();
+}
 MATCHER(IsJSONDictionary, "") {
   std::string result(arg.begin(), arg.end());
   std::unique_ptr<base::Value> root(base::JSONReader().ReadToValue(result));
-  return (root.get() && root->GetType() == base::Value::TYPE_DICTIONARY);
+  return (root.get() && root->GetType() == base::Value::Type::DICTIONARY);
 }
 
 namespace media {
@@ -238,12 +241,12 @@
     if (GetParam() == "AesDecryptor") {
       OnCdmCreated(
           new AesDecryptor(GURL::EmptyGURL(),
-                           base::Bind(&AesDecryptorTest::OnSessionMessage,
-                                      base::Unretained(this)),
-                           base::Bind(&AesDecryptorTest::OnSessionClosed,
-                                      base::Unretained(this)),
-                           base::Bind(&AesDecryptorTest::OnSessionKeysChange,
-                                      base::Unretained(this))),
+                           base::Bind(&MockCdmClient::OnSessionMessage,
+                                      base::Unretained(&cdm_client_)),
+                           base::Bind(&MockCdmClient::OnSessionClosed,
+                                      base::Unretained(&cdm_client_)),
+                           base::Bind(&MockCdmClient::OnSessionKeysChange,
+                                      base::Unretained(&cdm_client_))),
           std::string());
     } else if (GetParam() == "CdmAdapter") {
       CdmConfig cdm_config;  // default settings of false are sufficient.
@@ -254,14 +257,14 @@
           helper_->KeySystemName(), helper_->LibraryPath(), cdm_config,
           std::move(allocator), base::Bind(&AesDecryptorTest::CreateCdmFileIO,
                                            base::Unretained(this)),
-          base::Bind(&AesDecryptorTest::OnSessionMessage,
-                     base::Unretained(this)),
-          base::Bind(&AesDecryptorTest::OnSessionClosed,
-                     base::Unretained(this)),
-          base::Bind(&AesDecryptorTest::OnSessionKeysChange,
-                     base::Unretained(this)),
-          base::Bind(&AesDecryptorTest::OnSessionExpirationUpdate,
-                     base::Unretained(this)),
+          base::Bind(&MockCdmClient::OnSessionMessage,
+                     base::Unretained(&cdm_client_)),
+          base::Bind(&MockCdmClient::OnSessionClosed,
+                     base::Unretained(&cdm_client_)),
+          base::Bind(&MockCdmClient::OnSessionKeysChange,
+                     base::Unretained(&cdm_client_)),
+          base::Bind(&MockCdmClient::OnSessionExpirationUpdate,
+                     base::Unretained(&cdm_client_)),
           base::Bind(&AesDecryptorTest::OnCdmCreated, base::Unretained(this)));
 
       base::RunLoop().RunUntilIdle();
@@ -323,7 +326,8 @@
   // Creates a new session using |key_id|. Returns the session ID.
   std::string CreateSession(const std::vector<uint8_t>& key_id) {
     DCHECK(!key_id.empty());
-    EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsJSONDictionary()));
+    EXPECT_CALL(cdm_client_,
+                OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
     cdm_->CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
                                           EmeInitDataType::WEBM, key_id,
                                           CreateSessionPromise(RESOLVED));
@@ -334,7 +338,7 @@
 
   // Closes the session specified by |session_id|.
   void CloseSession(const std::string& session_id) {
-    EXPECT_CALL(*this, OnSessionClosed(session_id));
+    EXPECT_CALL(cdm_client_, OnSessionClosed(session_id));
     cdm_->CloseSession(session_id, CreatePromise(RESOLVED));
   }
 
@@ -344,17 +348,6 @@
     cdm_->RemoveSession(session_id, CreatePromise(REJECTED));
   }
 
-  MOCK_METHOD2(OnSessionKeysChangeCalled,
-               void(const std::string& session_id,
-                    bool has_additional_usable_key));
-
-  void OnSessionKeysChange(const std::string& session_id,
-                           bool has_additional_usable_key,
-                           CdmKeysInfo keys_info) {
-    keys_info_.swap(keys_info);
-    OnSessionKeysChangeCalled(session_id, has_additional_usable_key);
-  }
-
   // Updates the session specified by |session_id| with |key|. |result|
   // tests that the update succeeds or generates an error.
   void UpdateSessionAndExpect(std::string session_id,
@@ -364,10 +357,10 @@
     DCHECK(!key.empty());
 
     if (expected_result == RESOLVED) {
-      EXPECT_CALL(*this,
+      EXPECT_CALL(cdm_client_,
                   OnSessionKeysChangeCalled(session_id, new_key_expected));
     } else {
-      EXPECT_CALL(*this, OnSessionKeysChangeCalled(_, _)).Times(0);
+      EXPECT_CALL(cdm_client_, OnSessionKeysChangeCalled(_, _)).Times(0);
     }
 
     cdm_->UpdateSession(session_id,
@@ -375,8 +368,8 @@
                         CreatePromise(expected_result));
   }
 
-  bool KeysInfoContains(std::vector<uint8_t> expected) {
-    for (auto* key_id : keys_info_) {
+  bool KeysInfoContains(const std::vector<uint8_t>& expected) {
+    for (auto* key_id : cdm_client_.keys_info()) {
       if (key_id->key_id == expected)
         return true;
     }
@@ -452,20 +445,11 @@
     return nullptr;
   }
 
-  MOCK_METHOD3(OnSessionMessage,
-               void(const std::string& session_id,
-                    MediaKeys::MessageType message_type,
-                    const std::vector<uint8_t>& message));
-  MOCK_METHOD1(OnSessionClosed, void(const std::string& session_id));
-  MOCK_METHOD2(OnSessionExpirationUpdate,
-               void(const std::string& session_id,
-                    const base::Time& new_expiry_time));
-
+  StrictMock<MockCdmClient> cdm_client_;
   scoped_refptr<MediaKeys> cdm_;
   Decryptor* decryptor_;
   Decryptor::DecryptCB decrypt_cb_;
   std::string session_id_;
-  CdmKeysInfo keys_info_;
 
   // Helper class to load/unload External Clear Key Library, if necessary.
   std::unique_ptr<ExternalClearKeyTestHelper> helper_;
@@ -497,16 +481,19 @@
 TEST_P(AesDecryptorTest, CreateSessionWithVariousLengthInitData_WebM) {
   std::vector<uint8_t> init_data;
   init_data.resize(1);
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(init_data), CreateSessionPromise(RESOLVED));
 
   init_data.resize(16);  // The expected size.
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(init_data), CreateSessionPromise(RESOLVED));
 
   init_data.resize(512);
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(init_data), CreateSessionPromise(RESOLVED));
@@ -518,17 +505,17 @@
 }
 
 TEST_P(AesDecryptorTest, MultipleCreateSession) {
-  EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsNotEmpty()));
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(1), CreateSessionPromise(RESOLVED));
 
-  EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsNotEmpty()));
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(1), CreateSessionPromise(RESOLVED));
 
-  EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsNotEmpty()));
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::WEBM,
       std::vector<uint8_t>(1), CreateSessionPromise(RESOLVED));
@@ -551,7 +538,7 @@
   };
 
 #if defined(USE_PROPRIETARY_CODECS)
-  EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsJSONDictionary()));
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::CENC,
       std::vector<uint8_t>(init_data, init_data + arraysize(init_data)),
@@ -568,7 +555,7 @@
   const char init_data[] =
       "{\"kids\":[\"AQI\",\"AQIDBA\",\"AQIDBAUGBwgJCgsMDQ4PEA\"]}";
 
-  EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsJSONDictionary()));
+  EXPECT_CALL(cdm_client_, OnSessionMessage(NotEmpty(), _, IsJSONDictionary()));
   cdm_->CreateSessionAndGenerateRequest(
       MediaKeys::TEMPORARY_SESSION, EmeInitDataType::KEYIDS,
       std::vector<uint8_t>(init_data, init_data + arraysize(init_data) - 1),
diff --git a/media/cdm/cdm_adapter_unittest.cc b/media/cdm/cdm_adapter_unittest.cc
index 2b01928..57294cd7f 100644
--- a/media/cdm/cdm_adapter_unittest.cc
+++ b/media/cdm/cdm_adapter_unittest.cc
@@ -14,6 +14,7 @@
 #include "media/base/cdm_callback_promise.h"
 #include "media/base/cdm_key_information.h"
 #include "media/base/media_keys.h"
+#include "media/base/mock_filters.h"
 #include "media/cdm/cdm_file_io.h"
 #include "media/cdm/external_clear_key_test_helper.h"
 #include "media/cdm/simple_cdm_allocator.h"
@@ -22,6 +23,8 @@
 
 using ::testing::_;
 using ::testing::SaveArg;
+using ::testing::StrictMock;
+
 MATCHER(IsNotEmpty, "") {
   return !arg.empty();
 }
@@ -88,12 +91,14 @@
     CdmAdapter::Create(
         helper_.KeySystemName(), library_path, cdm_config, std::move(allocator),
         base::Bind(&CdmAdapterTest::CreateCdmFileIO, base::Unretained(this)),
-        base::Bind(&CdmAdapterTest::OnSessionMessage, base::Unretained(this)),
-        base::Bind(&CdmAdapterTest::OnSessionClosed, base::Unretained(this)),
-        base::Bind(&CdmAdapterTest::OnSessionKeysChange,
-                   base::Unretained(this)),
-        base::Bind(&CdmAdapterTest::OnSessionExpirationUpdate,
-                   base::Unretained(this)),
+        base::Bind(&MockCdmClient::OnSessionMessage,
+                   base::Unretained(&cdm_client_)),
+        base::Bind(&MockCdmClient::OnSessionClosed,
+                   base::Unretained(&cdm_client_)),
+        base::Bind(&MockCdmClient::OnSessionKeysChange,
+                   base::Unretained(&cdm_client_)),
+        base::Bind(&MockCdmClient::OnSessionExpirationUpdate,
+                   base::Unretained(&cdm_client_)),
         base::Bind(&CdmAdapterTest::OnCdmCreated, base::Unretained(this),
                    expected_result));
     RunUntilIdle();
@@ -108,7 +113,7 @@
     DCHECK(!key_id.empty());
 
     if (expected_result == SUCCESS) {
-      EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, _));
+      EXPECT_CALL(cdm_client_, OnSessionMessage(IsNotEmpty(), _, _));
     }
 
     adapter_->CreateSessionAndGenerateRequest(
@@ -139,10 +144,10 @@
     DCHECK(!key.empty());
 
     if (expected_result == SUCCESS) {
-      EXPECT_CALL(*this,
+      EXPECT_CALL(cdm_client_,
                   OnSessionKeysChangeCalled(session_id, new_key_expected));
     } else {
-      EXPECT_CALL(*this, OnSessionKeysChangeCalled(_, _)).Times(0);
+      EXPECT_CALL(cdm_client_, OnSessionKeysChangeCalled(_, _)).Times(0);
     }
 
     adapter_->UpdateSession(session_id,
@@ -217,24 +222,7 @@
                     uint32_t system_code,
                     const std::string& error_message));
 
-  // Methods used for the events possibly generated by CdmAdapater.
-  MOCK_METHOD3(OnSessionMessage,
-               void(const std::string& session_id,
-                    MediaKeys::MessageType message_type,
-                    const std::vector<uint8_t>& message));
-  MOCK_METHOD1(OnSessionClosed, void(const std::string& session_id));
-  MOCK_METHOD2(OnSessionKeysChangeCalled,
-               void(const std::string& session_id,
-                    bool has_additional_usable_key));
-  void OnSessionKeysChange(const std::string& session_id,
-                           bool has_additional_usable_key,
-                           CdmKeysInfo keys_info) {
-    // MOCK methods don't like CdmKeysInfo.
-    OnSessionKeysChangeCalled(session_id, has_additional_usable_key);
-  }
-  MOCK_METHOD2(OnSessionExpirationUpdate,
-               void(const std::string& session_id,
-                    const base::Time& new_expiry_time));
+  StrictMock<MockCdmClient> cdm_client_;
 
   // Helper class to load/unload External Clear Key Library.
   ExternalClearKeyTestHelper helper_;
diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc
index 60e73e8..04f94cf 100644
--- a/media/cdm/json_web_key.cc
+++ b/media/cdm/json_web_key.cc
@@ -171,7 +171,7 @@
   }
 
   std::unique_ptr<base::Value> root(base::JSONReader().ReadToValue(jwk_set));
-  if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (!root.get() || root->GetType() != base::Value::Type::DICTIONARY) {
     DVLOG(1) << "Not valid JSON: " << jwk_set << ", root: " << root.get();
     return false;
   }
@@ -240,7 +240,7 @@
   }
 
   std::unique_ptr<base::Value> root(base::JSONReader().ReadToValue(input));
-  if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (!root.get() || root->GetType() != base::Value::Type::DICTIONARY) {
     error_message->assign("Not valid JSON: ");
     error_message->append(ShortenTo64Characters(input));
     return false;
@@ -375,7 +375,7 @@
 
   std::unique_ptr<base::Value> root(
       base::JSONReader().ReadToValue(license_as_str));
-  if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
+  if (!root.get() || root->GetType() != base::Value::Type::DICTIONARY) {
     DVLOG(1) << "Not valid JSON: " << license_as_str;
     return false;
   }
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index eab193e..0c532d0e 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -74,7 +74,7 @@
                                         base::TimeDelta start_time_estimate) {
   DCHECK(start_time_estimate != kNoTimestamp);
   if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) {
-    return start_time_estimate == kInfiniteDuration ? kNoTimestamp
+    return start_time_estimate == kInfiniteDuration ? base::TimeDelta()
                                                     : start_time_estimate;
   }
 
@@ -1347,11 +1347,6 @@
 
     const base::TimeDelta start_time =
         ExtractStartTime(stream, start_time_estimates[i]);
-    const bool has_start_time = start_time != kNoTimestamp;
-
-    if (!has_start_time)
-      continue;
-
     streams_[i]->set_start_time(start_time);
     if (start_time < start_time_) {
       start_time_ = start_time;
@@ -1531,6 +1526,20 @@
   media_log_->AddEvent(std::move(metadata_event));
 }
 
+FFmpegDemuxerStream* FFmpegDemuxer::FindStreamWithLowestStartTimestamp(
+    bool enabled) {
+  FFmpegDemuxerStream* lowest_start_time_stream = nullptr;
+  for (const auto& stream : streams_) {
+    if (!stream || stream->enabled() != enabled)
+      continue;
+    if (!lowest_start_time_stream ||
+        stream->start_time() < lowest_start_time_stream->start_time()) {
+      lowest_start_time_stream = stream.get();
+    }
+  }
+  return lowest_start_time_stream;
+}
+
 FFmpegDemuxerStream* FFmpegDemuxer::FindPreferredStreamForSeeking(
     base::TimeDelta seek_time) {
   // If we have a selected/enabled video stream and its start time is lower
@@ -1539,8 +1548,7 @@
   for (const auto& stream : streams_) {
     if (stream && stream->type() == DemuxerStream::VIDEO && stream->enabled()) {
       video_stream = stream.get();
-      if (video_stream->start_time() == kNoTimestamp ||
-          video_stream->start_time() <= seek_time) {
+      if (video_stream->start_time() <= seek_time) {
         return video_stream;
       }
       break;
@@ -1549,25 +1557,30 @@
 
   // If video stream is not present or |seek_time| is lower than the video start
   // time, then try to find an enabled stream with the lowest start time.
-  FFmpegDemuxerStream* lowest_start_time_stream = nullptr;
-  for (const auto& stream : streams_) {
-    if (!stream || !stream->enabled() || stream->start_time() == kNoTimestamp)
-      continue;
-    if (!lowest_start_time_stream ||
-        stream->start_time() < lowest_start_time_stream->start_time()) {
-      lowest_start_time_stream = stream.get();
-    }
-  }
-  // If we found a stream with start time lower than |seek_time|, then use it.
-  if (lowest_start_time_stream &&
-      lowest_start_time_stream->start_time() <= seek_time) {
-    return lowest_start_time_stream;
+  FFmpegDemuxerStream* lowest_start_time_enabled_stream =
+      FindStreamWithLowestStartTimestamp(true);
+  if (lowest_start_time_enabled_stream &&
+      lowest_start_time_enabled_stream->start_time() <= seek_time) {
+    return lowest_start_time_enabled_stream;
   }
 
-  // If we couldn't find any streams with the start time lower than |seek_time|
-  // then use either video (if one exists) or any audio stream.
-  return video_stream ? video_stream : static_cast<FFmpegDemuxerStream*>(
-                                           GetStream(DemuxerStream::AUDIO));
+  // If there's no enabled streams to consider from, try a disabled stream with
+  // the lowest known start time.
+  FFmpegDemuxerStream* lowest_start_time_disabled_stream =
+      FindStreamWithLowestStartTimestamp(false);
+  if (lowest_start_time_disabled_stream &&
+      lowest_start_time_disabled_stream->start_time() <= seek_time) {
+    return lowest_start_time_disabled_stream;
+  }
+
+  // Otherwise fall back to any other stream.
+  for (const auto& stream : streams_) {
+    if (stream)
+      return stream.get();
+  }
+
+  NOTREACHED();
+  return nullptr;
 }
 
 void FFmpegDemuxer::OnSeekFrameDone(int result) {
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 8b9320ed..2011d821 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -329,10 +329,15 @@
   // based timeline.
   base::TimeDelta start_time_;
 
+  // Finds the stream with the lowest known start time (i.e. not kNoTimestamp
+  // start time) with enabled status matching |enabled|.
+  FFmpegDemuxerStream* FindStreamWithLowestStartTimestamp(bool enabled);
+
   // Finds a preferred stream for seeking to |seek_time|. Preference is
   // typically given to video streams, unless the |seek_time| is earlier than
   // the start time of the video stream. In that case a stream with the earliest
-  // start time is preferred. Disabled streams are not considered.
+  // start time is preferred. Disabled streams are considered only as the last
+  // fallback option.
   FFmpegDemuxerStream* FindPreferredStreamForSeeking(base::TimeDelta seek_time);
 
   // The Time associated with timestamp 0. Set to a null
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index eda72002..d6a2bffb 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -536,15 +536,22 @@
   // prefer the video stream for seeking.
   EXPECT_EQ(video, preferred_seeking_stream(video_start_time));
 
-  // A disabled stream should not be preferred for seeking.
+  // A disabled stream should be preferred only when there's no other viable
+  // option among enabled streams.
   audio->set_enabled(false, base::TimeDelta());
-  EXPECT_EQ(video, preferred_seeking_stream(base::TimeDelta()));
-  EXPECT_EQ(video, preferred_seeking_stream(audio_start_time));
   EXPECT_EQ(video, preferred_seeking_stream(video_start_time));
+  // Audio stream is preferred, even though it is disabled, since video stream
+  // start time is higher than the seek target == audio_start_time in this case.
+  EXPECT_EQ(audio, preferred_seeking_stream(audio_start_time));
 
   audio->set_enabled(true, base::TimeDelta());
   video->set_enabled(false, base::TimeDelta());
-  EXPECT_EQ(audio, preferred_seeking_stream(base::TimeDelta()));
+  EXPECT_EQ(audio, preferred_seeking_stream(audio_start_time));
+  EXPECT_EQ(audio, preferred_seeking_stream(video_start_time));
+
+  // When both audio and video streams are disabled and there's no enabled
+  // streams, then audio is preferred since it has lower start time.
+  audio->set_enabled(false, base::TimeDelta());
   EXPECT_EQ(audio, preferred_seeking_stream(audio_start_time));
   EXPECT_EQ(audio, preferred_seeking_stream(video_start_time));
 }
@@ -1475,4 +1482,38 @@
   EXPECT_EQ(kSampleFormatS32, audio_config.sample_format());
 }
 
+// Verify that FFmpeg demuxer falls back to choosing disabled streams for
+// seeking if there's no suitable enabled stream found.
+TEST_F(FFmpegDemuxerTest, Seek_FallbackToDisabledVideoStream) {
+  // Input has only video stream, no audio.
+  CreateDemuxer("bear-320x240-video-only.webm");
+  InitializeDemuxer();
+  EXPECT_EQ(nullptr, demuxer_->GetStream(DemuxerStream::AUDIO));
+  DemuxerStream* vstream = demuxer_->GetStream(DemuxerStream::VIDEO);
+  EXPECT_NE(nullptr, vstream);
+  EXPECT_EQ(vstream, preferred_seeking_stream(base::TimeDelta()));
+
+  // Now pretend that video stream got disabled, e.g. due to current tab going
+  // into background.
+  vstream->set_enabled(false, base::TimeDelta());
+  // Since there's no other enabled streams, the preferred seeking stream should
+  // still be the video stream.
+  EXPECT_EQ(vstream, preferred_seeking_stream(base::TimeDelta()));
+}
+
+TEST_F(FFmpegDemuxerTest, Seek_FallbackToDisabledAudioStream) {
+  CreateDemuxer("bear-320x240-audio-only.webm");
+  InitializeDemuxer();
+  DemuxerStream* astream = demuxer_->GetStream(DemuxerStream::AUDIO);
+  EXPECT_NE(nullptr, astream);
+  EXPECT_EQ(nullptr, demuxer_->GetStream(DemuxerStream::VIDEO));
+  EXPECT_EQ(astream, preferred_seeking_stream(base::TimeDelta()));
+
+  // Now pretend that audio stream got disabled.
+  astream->set_enabled(false, base::TimeDelta());
+  // Since there's no other enabled streams, the preferred seeking stream should
+  // still be the audio stream.
+  EXPECT_EQ(astream, preferred_seeking_stream(base::TimeDelta()));
+}
+
 }  // namespace media
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index b2963c3..76fff7a 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -8,6 +8,12 @@
 import("//media/media_options.gni")
 import("//testing/test.gni")
 
+declare_args() {
+  # A temporary arg for building MCVD while it's being implemented.
+  # See http://crbug.com/660942
+  enable_media_codec_video_decoder = false
+}
+
 if (is_mac) {
   import("//build/config/mac/mac_sdk.gni")
 }
@@ -190,6 +196,13 @@
       ]
     }
 
+    if (enable_media_codec_video_decoder) {
+      sources += [
+        "android/media_codec_video_decoder.cc",
+        "android/media_codec_video_decoder.h",
+      ]
+    }
+
     # TODO(xhwang): This is needed for AVDA to access the CDM directly.
     # Remove this dependency after VDAs are also running as part of the mojo
     # media service. See http://crbug.com/522298
@@ -415,6 +428,9 @@
       "android_video_decode_accelerator_unittest.cc",
       "avda_codec_allocator_unittest.cc",
     ]
+    if (enable_media_codec_video_decoder) {
+      sources += [ "android/media_codec_video_decoder_unittest.cc" ]
+    }
     deps = [
       ":gpu",
       "//base/test:test_support",
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 5982156..f3fda16 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -4,106 +4,31 @@
 
 #include "media/gpu/android/media_codec_video_decoder.h"
 
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/android/build_info.h"
-#include "base/auto_reset.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram.h"
-#include "base/sys_info.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/trace_event.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "gpu/command_buffer/service/mailbox_manager.h"
-#include "gpu/ipc/service/gpu_channel.h"
-#include "media/base/android/media_codec_bridge.h"
 #include "media/base/android/media_codec_util.h"
-#include "media/base/bind_to_current_loop.h"
-#include "media/base/bitstream_buffer.h"
-#include "media/base/limits.h"
-#include "media/base/media.h"
-#include "media/base/timestamp_constants.h"
+#include "media/base/android/sdk_media_codec_bridge.h"
+#include "media/base/video_codecs.h"
 #include "media/base/video_decoder_config.h"
-#include "media/gpu/avda_picture_buffer_manager.h"
-#include "media/gpu/shared_memory_region.h"
-#include "media/video/picture.h"
-#include "ui/gl/android/scoped_java_surface.h"
-#include "ui/gl/android/surface_texture.h"
-#include "ui/gl/gl_bindings.h"
-
-#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-#include "media/mojo/services/mojo_cdm_service.h"
-#endif
-
-#define NOTIFY_ERROR(error_code, error_message)      \
-  do {                                               \
-    DLOG(ERROR) << error_message;                    \
-    NotifyError(VideoDecodeAccelerator::error_code); \
-  } while (0)
 
 namespace media {
-
 namespace {
 
-// Max number of bitstreams notified to the client with
-// NotifyEndOfBitstreamBuffer() before getting output from the bitstream.
-enum { kMaxBitstreamsNotifiedInAdvance = 32 };
-
-// Because MediaCodec is thread-hostile (must be poked on a single thread) and
-// has no callback mechanism (b/11990118), we must drive it by polling for
-// complete frames (and available input buffers, when the codec is fully
-// saturated).  This function defines the polling delay.  The value used is an
-// arbitrary choice that trades off CPU utilization (spinning) against latency.
-// Mirrors android_video_encode_accelerator.cc:EncodePollDelay().
-//
-// An alternative to this polling scheme could be to dedicate a new thread
-// (instead of using the ChildThread) to run the MediaCodec, and make that
-// thread use the timeout-based flavor of MediaCodec's dequeue methods when it
-// believes the codec should complete "soon" (e.g. waiting for an input
-// buffer, or waiting for a picture when it knows enough complete input
-// pictures have been fed to saturate any internal buffering).  This is
-// speculative and it's unclear that this would be a win (nor that there's a
-// reasonably device-agnostic way to fill in the "believes" above).
-constexpr base::TimeDelta DecodePollDelay =
-    base::TimeDelta::FromMilliseconds(10);
-
-constexpr base::TimeDelta NoWaitTimeOut = base::TimeDelta::FromMicroseconds(0);
-
-constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1);
-
-// On low end devices (< KitKat is always low-end due to buggy MediaCodec),
-// defer the surface creation until the codec is actually used if we know no
-// software fallback exists.
-bool ShouldDeferSurfaceCreation(int surface_id, VideoCodec codec) {
-  return surface_id == SurfaceManager::kNoSurfaceID && codec == kCodecH264 &&
-         AVDACodecAllocator::Instance()->IsAnyRegisteredAVDA() &&
-         (base::android::BuildInfo::GetInstance()->sdk_int() <= 18 ||
-          base::SysInfo::IsLowEndDevice());
-}
-
-// Don't use MediaCodecs internal software decoders when we have more secure and
-// up to date versions in the renderer process.
+// Don't use MediaCodec's internal software decoders when we have more secure
+// and up to date versions in the renderer process.
 bool IsMediaCodecSoftwareDecodingForbidden(const VideoDecoderConfig& config) {
   return !config.is_encrypted() &&
-         (config.codec() == kCodecVP8 || _config.codec() == kCodecVP9);
+         (config.codec() == kCodecVP8 || config.codec() == kCodecVP9);
 }
 
 bool ConfigSupported(const VideoDecoderConfig& config) {
-  const auto codec = config.codec();
+  // Don't support larger than 4k because it won't perform well on many devices.
+  const auto size = config.coded_size();
+  if (size.width() > 3840 || size.height() > 2160)
+    return false;
 
   // Only use MediaCodec for VP8 or VP9 if it's likely backed by hardware or if
   // the stream is encrypted.
+  const auto codec = config.codec();
   if (IsMediaCodecSoftwareDecodingForbidden(config) &&
       VideoCodecBridge::IsKnownUnaccelerated(codec, MEDIA_CODEC_DECODER)) {
     DVLOG(1) << "Config not supported: " << GetCodecName(codec)
@@ -111,11 +36,6 @@
     return false;
   }
 
-  // Don't support larger than 4k because it won't perform well on many devices.
-  const auto size = config.coded_size();
-  if (size.width() > 3840 || size.height() > 2160)
-    return false;
-
   switch (codec) {
     case kCodecVP8:
     case kCodecVP9: {
@@ -149,1141 +69,42 @@
 
 }  // namespace
 
-// MCVDManager manages shared resources for a number of MCVD instances.
-// Its responsibilities include:
-//  - Starting and stopping a shared "construction" thread for instantiating and
-//    releasing MediaCodecs.
-//  - Detecting when a task has hung on the construction thread so MCVDs can
-//    stop using it.
-//  - Running a RepeatingTimer so that MCVDs can get a regular callback to
-//    DoIOTask().
-//  - Tracking the allocation of surfaces to MCVDs and delivering callbacks when
-//    surfaces are released.
-class MCVDManager {
- public:
-  // Request periodic callback of |mcvd|->DoIOTask(). Does nothing if the
-  // instance is already registered and the timer started. The first request
-  // will start the repeating timer on an interval of DecodePollDelay.
-  void StartTimer(MediaCodecVideoDecoder* mcvd) {
-    DCHECK(thread_checker_.CalledOnValidThread());
+MediaCodecVideoDecoder::MediaCodecVideoDecoder() {}
 
-    timer_mcvd_instances_.insert(mcvd);
+MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {}
 
-    // If the timer is running, StopTimer() might have been called earlier, if
-    // so remove the instance from the pending erasures.
-    if (timer_running_)
-      pending_erase_.erase(mcvd);
-
-    if (io_timer_.IsRunning())
-      return;
-    io_timer_.Start(FROM_HERE, DecodePollDelay, this, &MCVDManager::RunTimer);
-  }
-
-  // Stop callbacks to |mcvd|->DoIOTask(). Does nothing if the instance is not
-  // registered. If there are no instances left, the repeating timer will be
-  // stopped.
-  void StopTimer(MediaCodecVideoDecoder* mcvd) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-
-    // If the timer is running, defer erasures to avoid iterator invalidation.
-    if (timer_running_) {
-      pending_erase_.insert(mcvd);
-      return;
-    }
-
-    timer_mcvd_instances_.erase(mcvd);
-    if (timer_mcvd_instances_.empty())
-      io_timer_.Stop();
-  }
-
- private:
-  friend struct base::DefaultLazyInstanceTraits<MCVDManager>;
-
-  MCVDManager() {}
-  ~MCVDManager() { NOTREACHED(); }
-
-  void RunTimer() {
-    {
-      // Call out to all MCVD instances, some of which may attempt to remove
-      // themselves from the list during this operation; those removals will be
-      // deferred until after all iterations are complete.
-      base::AutoReset<bool> scoper(&timer_running_, true);
-      for (auto* mcvd : timer_mcvd_instances_)
-        mcvd->DoIOTask(false);
-    }
-
-    // Take care of any deferred erasures.
-    for (auto* mcvd : pending_erase_)
-      StopTimer(mcvd);
-    pending_erase_.clear();
-
-    // TODO(dalecurtis): We may want to consider chunking this if task execution
-    // takes too long for the combined timer.
-  }
-
-  // All MCVD instances that would like us to poll DoIOTask.
-  std::set<MediaCodecVideoDecoder*> timer_mcvd_instances_;
-
-  // Since we can't delete while iterating when using a set, defer erasure until
-  // after iteration complete.
-  bool timer_running_ = false;
-  std::set<MediaCodecVideoDecoder*> pending_erase_;
-
-  // Repeating timer responsible for draining pending IO to the codecs.
-  base::RepeatingTimer io_timer_;
-
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(MCVDManager);
-};
-
-static base::LazyInstance<MCVDManager>::Leaky g_mcvd_manager =
-    LAZY_INSTANCE_INITIALIZER;
-
-MediaCodecVideoDecoder::BitstreamRecord::BitstreamRecord(
-    const BitstreamBuffer& bitstream_buffer)
-    : buffer(bitstream_buffer) {
-  if (buffer.id() != -1)
-    memory.reset(new SharedMemoryRegion(buffer, true));
+void MediaCodecVideoDecoder::Initialize(const VideoDecoderConfig& config,
+                                        bool low_delay,
+                                        CdmContext* cdm_context,
+                                        const InitCB& init_cb,
+                                        const OutputCB& output_cb) {
+  init_cb.Run(ConfigSupported(config));
 }
 
-MediaCodecVideoDecoder::BitstreamRecord::BitstreamRecord(
-    BitstreamRecord&& other)
-    : buffer(std::move(other.buffer)), memory(std::move(other.memory)) {}
-
-MediaCodecVideoDecoder::BitstreamRecord::~BitstreamRecord() {}
-
-MediaCodecVideoDecoder::MediaCodecVideoDecoder(
-    const MakeGLContextCurrentCallback& make_context_current_cb,
-    const GetGLES2DecoderCallback& get_gles2_decoder_cb)
-    : client_(NULL),
-      make_context_current_cb_(make_context_current_cb),
-      get_gles2_decoder_cb_(get_gles2_decoder_cb),
-      state_(NO_ERROR),
-      picture_buffer_manager_(get_gles2_decoder_cb),
-      drain_type_(DRAIN_TYPE_NONE),
-      media_drm_bridge_cdm_context_(nullptr),
-      cdm_registration_id_(0),
-      pending_input_buf_index_(-1),
-      deferred_initialization_pending_(false),
-      codec_needs_reset_(false),
-      defer_surface_creation_(false),
-      weak_this_factory_(this) {}
-
-MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  g_mcvd_manager.Get().StopTimer(this);
-  AVDACodecAllocator::Instance()->StopThread(this);
-
-#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-  if (!media_drm_bridge_cdm_context_)
-    return;
-
-  DCHECK(cdm_registration_id_);
-
-  // Cancel previously registered callback (if any).
-  media_drm_bridge_cdm_context_->SetMediaCryptoReadyCB(
-      MediaDrmBridgeCdmContext::MediaCryptoReadyCB());
-
-  media_drm_bridge_cdm_context_->UnregisterPlayer(cdm_registration_id_);
-#endif  // defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
-}
-
-bool MediaCodecVideoDecoder::Initialize(const Config& config, Client* client) {
-  DVLOG(1) << __func__ << ": " << config.AsHumanReadableString();
-  TRACE_EVENT0("media", "MCVD::Initialize");
-  DCHECK(!media_codec_);
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (make_context_current_cb_.is_null() || get_gles2_decoder_cb_.is_null()) {
-    DLOG(ERROR) << "GL callbacks are required for this VDA";
-    return false;
-  }
-
-  if (config.output_mode != Config::OutputMode::ALLOCATE) {
-    DLOG(ERROR) << "Only ALLOCATE OutputMode is supported by this VDA";
-    return false;
-  }
-
-  DCHECK(client);
-  client_ = client;
-  config_ = config;
-  codec_config_ = new CodecConfig();
-  codec_config_->codec_ = VideoCodecProfileToVideoCodec(config.profile);
-  codec_config_->initial_expected_coded_size_ =
-      config.initial_expected_coded_size;
-
-  if (codec_config_->codec_ != kCodecVP8 &&
-      codec_config_->codec_ != kCodecVP9 &&
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-      codec_config_->codec_ != kCodecHEVC &&
-#endif
-      codec_config_->codec_ != kCodecH264) {
-    DLOG(ERROR) << "Unsupported profile: " << config.profile;
-    return false;
-  }
-
-  if (codec_config_->codec_ == kCodecH264) {
-    codec_config_->csd0_ = config.sps;
-    codec_config_->csd1_ = config.pps;
-  }
-
-  // Only use MediaCodec for VP8/9 if it's likely backed by hardware
-  // or if the stream is encrypted.
-  if (IsMediaCodecSoftwareDecodingForbidden() &&
-      VideoCodecBridge::IsKnownUnaccelerated(codec_config_->codec_,
-                                             MEDIA_CODEC_DECODER)) {
-    DVLOG(1) << "Initialization failed: "
-             << (codec_config_->codec_ == kCodecVP8 ? "vp8" : "vp9")
-             << " is not hardware accelerated";
-    return false;
-  }
-
-  auto gles_decoder = get_gles2_decoder_cb_.Run();
-  if (!gles_decoder) {
-    DLOG(ERROR) << "Failed to get gles2 decoder instance.";
-    return false;
-  }
-
-  // SetSurface() can't be called before Initialize(), so we pick up our first
-  // surface ID from the codec configuration.
-  DCHECK(!pending_surface_id_);
-
-  // If we're low on resources, we may decide to defer creation of the surface
-  // until the codec is actually used.
-  if (ShouldDeferSurfaceCreation(config_.surface_id, codec_config_->codec_)) {
-    DCHECK(!deferred_initialization_pending_);
-    // We should never be here if a SurfaceView is required.
-    DCHECK_EQ(config_.surface_id, SurfaceManager::kNoSurfaceID);
-    defer_surface_creation_ = true;
-    NotifyInitializationComplete(true);
-    return true;
-  }
-
-  // We signaled that we support deferred initialization, so see if the client
-  // does also.
-  deferred_initialization_pending_ = config.is_deferred_initialization_allowed;
-  if (config_.is_encrypted && !deferred_initialization_pending_) {
-    DLOG(ERROR) << "Deferred initialization must be used for encrypted streams";
-    return false;
-  }
-
-  if (AVDACodecAllocator::Instance()->AllocateSurface(this,
-                                                      config_.surface_id)) {
-    // We now own the surface, so finish initialization.
-    return InitializePictureBufferManager();
-  }
-
-  // We have to wait for some other MCVD instance to free up the surface.
-  // OnSurfaceAvailable will be called when it's available.
-  return true;
-}
-
-void MediaCodecVideoDecoder::OnSurfaceAvailable(bool success) {
-  DCHECK(deferred_initialization_pending_);
-  DCHECK(!defer_surface_creation_);
-
-  if (!success || !InitializePictureBufferManager()) {
-    NotifyInitializationComplete(false);
-    deferred_initialization_pending_ = false;
-  }
-}
-
-bool MediaCodecVideoDecoder::InitializePictureBufferManager() {
-  if (!make_context_current_cb_.Run()) {
-    LOG(ERROR) << "Failed to make this decoder's GL context current.";
-    return false;
-  }
-
-  codec_config_->surface_ =
-      picture_buffer_manager_.Initialize(config_.surface_id);
-  if (codec_config_->surface_.IsEmpty())
-    return false;
-
-  if (!AVDACodecAllocator::Instance()->StartThread(this))
-    return false;
-
-  // If we are encrypted, then we aren't able to create the codec yet.
-  if (config_.is_encrypted) {
-    InitializeCdm();
-    return true;
-  }
-
-  if (deferred_initialization_pending_ || defer_surface_creation_) {
-    defer_surface_creation_ = false;
-    ConfigureMediaCodecAsynchronously();
-    return true;
-  }
-}
-
-void MediaCodecVideoDecoder::DoIOTask(bool start_timer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "MCVD::DoIOTask");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
-      state_ == SURFACE_DESTROYED) {
-    return;
-  }
-
-  picture_buffer_manager_.MaybeRenderEarly();
-  bool did_work = false, did_input = false, did_output = false;
-  do {
-    did_input = QueueInput();
-    did_output = DequeueOutput();
-    if (did_input || did_output)
-      did_work = true;
-  } while (did_input || did_output);
-
-  ManageTimer(did_work || start_timer);
-}
-
-bool MediaCodecVideoDecoder::QueueInput() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "MCVD::QueueInput");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
-      state_ == WAITING_FOR_KEY) {
-    return false;
-  }
-  if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance)
-    return false;
-  if (pending_bitstream_records_.empty())
-    return false;
-
-  int input_buf_index = pending_input_buf_index_;
-
-  // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY.
-  // That status does not return this buffer back to the pool of
-  // available input buffers. We have to reuse it in QueueSecureInputBuffer().
-  if (input_buf_index == -1) {
-    MediaCodecStatus status =
-        media_codec_->DequeueInputBuffer(NoWaitTimeOut, &input_buf_index);
-    switch (status) {
-      case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
-        return false;
-      case MEDIA_CODEC_ERROR:
-        NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueInputBuffer failed");
-        return false;
-      case MEDIA_CODEC_OK:
-        break;
-      default:
-        NOTREACHED();
-        return false;
-    }
-  }
-
-  DCHECK_NE(input_buf_index, -1);
-
-  BitstreamBuffer bitstream_buffer = pending_bitstream_records_.front().buffer;
-
-  if (bitstream_buffer.id() == -1) {
-    pending_bitstream_records_.pop();
-    TRACE_COUNTER1("media", "MCVD::PendingBitstreamBufferCount",
-                   pending_bitstream_records_.size());
-
-    media_codec_->QueueEOS(input_buf_index);
-    return true;
-  }
-
-  std::unique_ptr<SharedMemoryRegion> shm;
-
-  if (pending_input_buf_index_ == -1) {
-    // When |pending_input_buf_index_| is not -1, the buffer is already dequeued
-    // from MediaCodec, filled with data and bitstream_buffer.handle() is
-    // closed.
-    shm = std::move(pending_bitstream_records_.front().memory);
-
-    if (!shm->Map()) {
-      NOTIFY_ERROR(UNREADABLE_INPUT, "SharedMemoryRegion::Map() failed");
-      return false;
-    }
-  }
-
-  const base::TimeDelta presentation_timestamp =
-      bitstream_buffer.presentation_timestamp();
-  DCHECK(presentation_timestamp != kNoTimestamp)
-      << "Bitstream buffers must have valid presentation timestamps";
-
-  // There may already be a bitstream buffer with this timestamp, e.g., VP9 alt
-  // ref frames, but it's OK to overwrite it because we only expect a single
-  // output frame to have that timestamp. MCVD clients only use the bitstream
-  // buffer id in the returned Pictures to map a bitstream buffer back to a
-  // timestamp on their side, so either one of the bitstream buffer ids will
-  // result in them finding the right timestamp.
-  bitstream_buffers_in_decoder_[presentation_timestamp] = bitstream_buffer.id();
-
-  // Notice that |memory| will be null if we repeatedly enqueue the same buffer,
-  // this happens after MEDIA_CODEC_NO_KEY.
-  const uint8_t* memory =
-      shm ? static_cast<const uint8_t*>(shm->memory()) : nullptr;
-  const std::string& key_id = bitstream_buffer.key_id();
-  const std::string& iv = bitstream_buffer.iv();
-  const std::vector<SubsampleEntry>& subsamples = bitstream_buffer.subsamples();
-
-  MediaCodecStatus status;
-  if (key_id.empty() || iv.empty()) {
-    status = media_codec_->QueueInputBuffer(input_buf_index, memory,
-                                            bitstream_buffer.size(),
-                                            presentation_timestamp);
-  } else {
-    status = media_codec_->QueueSecureInputBuffer(
-        input_buf_index, memory, bitstream_buffer.size(), key_id, iv,
-        subsamples, presentation_timestamp);
-  }
-
-  DVLOG(2) << __func__
-           << ": Queue(Secure)InputBuffer: pts:" << presentation_timestamp
-           << " status:" << status;
-
-  if (status == MEDIA_CODEC_NO_KEY) {
-    // Keep trying to enqueue the same input buffer.
-    // The buffer is owned by us (not the MediaCodec) and is filled with data.
-    DVLOG(1) << "QueueSecureInputBuffer failed: NO_KEY";
-    pending_input_buf_index_ = input_buf_index;
-    state_ = WAITING_FOR_KEY;
-    return false;
-  }
-
-  pending_input_buf_index_ = -1;
-  pending_bitstream_records_.pop();
-  TRACE_COUNTER1("media", "MCVD::PendingBitstreamBufferCount",
-                 pending_bitstream_records_.size());
-  // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output
-  // will be returned from the bitstream buffer. However, MediaCodec API is
-  // not enough to guarantee it.
-  // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to
-  // keep getting more bitstreams from the client, and throttle them by using
-  // |bitstreams_notified_in_advance_|.
-  // TODO(dwkang): check if there is a way to remove this workaround.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&MediaCodecVideoDecoder::NotifyEndOfBitstreamBuffer,
-                 weak_this_factory_.GetWeakPtr(), bitstream_buffer.id()));
-  bitstreams_notified_in_advance_.push_back(bitstream_buffer.id());
-
-  if (status != MEDIA_CODEC_OK) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "QueueInputBuffer failed:" << status);
-    return false;
-  }
-
-  return true;
-}
-
-bool MediaCodecVideoDecoder::DequeueOutput() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "MCVD::DequeueOutput");
-  if (state_ == ERROR || state_ == WAITING_FOR_CODEC)
-    return false;
-  if (!output_picture_buffers_.empty() && free_picture_ids_.empty() &&
-      !IsDrainingForResetOrDestroy()) {
-    // Don't have any picture buffer to send. Need to wait.
-    return false;
-  }
-
-  // If we're waiting to switch surfaces pause output release until we have all
-  // picture buffers returned. This is so we can ensure the right flags are set
-  // on the picture buffers returned to the client.
-  if (pending_surface_id_) {
-    if (picture_buffer_manager_.HasUnrenderedPictures())
-      return false;
-    if (!UpdateSurface())
-      return false;
-  }
-
-  bool eos = false;
-  base::TimeDelta presentation_timestamp;
-  int32_t buf_index = 0;
-  do {
-    size_t offset = 0;
-    size_t size = 0;
-
-    TRACE_EVENT_BEGIN0("media", "MCVD::DequeueOutput");
-    MediaCodecStatus status = media_codec_->DequeueOutputBuffer(
-        NoWaitTimeOut, &buf_index, &offset, &size, &presentation_timestamp,
-        &eos, NULL);
-    TRACE_EVENT_END2("media", "MCVD::DequeueOutput", "status", status,
-                     "presentation_timestamp (ms)",
-                     presentation_timestamp.InMilliseconds());
-
-    switch (status) {
-      case MEDIA_CODEC_ERROR:
-        // Do not post an error if we are draining for reset and destroy.
-        // Instead, run the drain completion task.
-        if (IsDrainingForResetOrDestroy()) {
-          DVLOG(1) << __func__ << ": error while codec draining";
-          state_ = ERROR;
-          OnDrainCompleted();
-        } else {
-          NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed.");
-        }
-        return false;
-
-      case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
-        return false;
-
-      case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: {
-        // An OUTPUT_FORMAT_CHANGED is not reported after flush() if the frame
-        // size does not change. Therefore we have to keep track on the format
-        // even if draining, unless we are draining for destroy.
-        if (drain_type_ == DRAIN_FOR_DESTROY)
-          return true;  // ignore
-
-        if (media_codec_->GetOutputSize(&size_) != MEDIA_CODEC_OK) {
-          NOTIFY_ERROR(PLATFORM_FAILURE, "GetOutputSize failed.");
-          return false;
-        }
-
-        DVLOG(3) << __func__
-                 << " OUTPUT_FORMAT_CHANGED, new size: " << size_.ToString();
-        return true;
-      }
-
-      case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
-        break;
-
-      case MEDIA_CODEC_OK:
-        DCHECK_GE(buf_index, 0);
-        DVLOG(3) << __func__ << ": pts:" << presentation_timestamp
-                 << " buf_index:" << buf_index << " offset:" << offset
-                 << " size:" << size << " eos:" << eos;
-        break;
-
-      default:
-        NOTREACHED();
-        break;
-    }
-  } while (buf_index < 0);
-
-  if (eos) {
-    OnDrainCompleted();
-    return false;
-  }
-
-  if (IsDrainingForResetOrDestroy()) {
-    media_codec_->ReleaseOutputBuffer(buf_index, false);
-    return true;
-  }
-
-  // TODO(watk): Handle the case where we get a decoded buffer before
-  // FORMAT_CHANGED.
-  // In 0.01% of playbacks MediaCodec returns a frame before FORMAT_CHANGED.
-  // Occurs on JB and M. (See the Media.MCVD.MissingFormatChanged histogram.)
-
-  // Get the bitstream buffer id from the timestamp.
-  auto it = bitstream_buffers_in_decoder_.find(presentation_timestamp);
-
-  if (it != bitstream_buffers_in_decoder_.end()) {
-    const int32_t bitstream_buffer_id = it->second;
-    bitstream_buffers_in_decoder_.erase(bitstream_buffers_in_decoder_.begin(),
-                                        ++it);
-    SendDecodedFrameToClient(buf_index, bitstream_buffer_id);
-
-    // Removes ids former or equal than the id from decoder. Note that
-    // |bitstreams_notified_in_advance_| does not mean bitstream ids in decoder
-    // because of frame reordering issue. We just maintain this roughly and use
-    // it for throttling.
-    for (auto bitstream_it = bitstreams_notified_in_advance_.begin();
-         bitstream_it != bitstreams_notified_in_advance_.end();
-         ++bitstream_it) {
-      if (*bitstream_it == bitstream_buffer_id) {
-        bitstreams_notified_in_advance_.erase(
-            bitstreams_notified_in_advance_.begin(), ++bitstream_it);
-        break;
-      }
-    }
-  } else {
-    // Normally we assume that the decoder makes at most one output frame for
-    // each distinct input timestamp. However MediaCodecBridge uses timestamp
-    // correction and provides a non-decreasing timestamp sequence, which might
-    // result in timestamp duplicates. Discard the frame if we cannot get the
-    // corresponding buffer id.
-    DVLOG(3) << __func__ << ": Releasing buffer with unexpected PTS: "
-             << presentation_timestamp;
-    media_codec_->ReleaseOutputBuffer(buf_index, false);
-  }
-
-  // We got a decoded frame, so try for another.
-  return true;
-}
-
-void MediaCodecVideoDecoder::SendDecodedFrameToClient(
-    int32_t codec_buffer_index,
-    int32_t bitstream_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_NE(bitstream_id, -1);
-  DCHECK(!free_picture_ids_.empty());
-  TRACE_EVENT0("media", "MCVD::SendDecodedFrameToClient");
-
-  if (!make_context_current_cb_.Run()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to make the GL context current.");
-    return;
-  }
-
-  int32_t picture_buffer_id = free_picture_ids_.front();
-  free_picture_ids_.pop();
-  TRACE_COUNTER1("media", "MCVD::FreePictureIds", free_picture_ids_.size());
-
-  const auto it = output_picture_buffers_.find(picture_buffer_id);
-  if (it == output_picture_buffers_.end()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Can't find PictureBuffer id: " << picture_buffer_id);
-    return;
-  }
-
-  PictureBuffer& picture_buffer = it->second;
-  const bool size_changed = picture_buffer.size() != size_;
-  if (size_changed)
-    picture_buffer.set_size(size_);
-
-  const bool allow_overlay = picture_buffer_manager_.ArePicturesOverlayable();
-  UMA_HISTOGRAM_BOOLEAN("Media.AVDA.FrameSentAsOverlay", allow_overlay);
-  // TODO(hubbe): Insert the correct color space. http://crbug.com/647725
-  Picture picture(picture_buffer_id, bitstream_id, gfx::Rect(size_),
-                  gfx::ColorSpace(), allow_overlay);
-  picture.set_size_changed(size_changed);
-
-  // Notify picture ready before calling UseCodecBufferForPictureBuffer() since
-  // that process may be slow and shouldn't delay delivery of the frame to the
-  // renderer. The picture is only used on the same thread as this method is
-  // called, so it is safe to do this.
-  NotifyPictureReady(picture);
-
-  // Connect the PictureBuffer to the decoded frame.
-  if (!picture_buffer_manager_.UseCodecBufferForPictureBuffer(
-          codec_buffer_index, picture_buffer, size_)) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Failed to attach the codec buffer to a picture buffer.");
-  }
-}
-
-void MediaCodecVideoDecoder::Decode(const BitstreamBuffer& bitstream_buffer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (defer_surface_creation_ && !InitializePictureBufferManager()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Failed deferred surface and MediaCodec initialization.");
-    return;
-  }
-
-  // If we previously deferred a codec restart, take care of it now. This can
-  // happen on older devices where configuration changes require a codec reset.
-  if (codec_needs_reset_) {
-    DCHECK_EQ(drain_type_, DRAIN_TYPE_NONE);
-    ResetCodecState();
-  }
-
-  if (bitstream_buffer.id() >= 0 && bitstream_buffer.size() > 0) {
-    DecodeBuffer(bitstream_buffer);
-    return;
-  }
-
-  if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle()))
-    base::SharedMemory::CloseHandle(bitstream_buffer.handle());
-
-  if (bitstream_buffer.id() < 0) {
-    NOTIFY_ERROR(INVALID_ARGUMENT,
-                 "Invalid bistream_buffer, id: " << bitstream_buffer.id());
-  } else {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&MediaCodecVideoDecoder::NotifyEndOfBitstreamBuffer,
-                   weak_this_factory_.GetWeakPtr(), bitstream_buffer.id()));
-  }
-}
-
-void MediaCodecVideoDecoder::DecodeBuffer(
-    const BitstreamBuffer& bitstream_buffer) {
-  pending_bitstream_records_.push(BitstreamRecord(bitstream_buffer));
-  TRACE_COUNTER1("media", "MCVD::PendingBitstreamBufferCount",
-                 pending_bitstream_records_.size());
-
-  DoIOTask(true);
-}
-
-void MediaCodecVideoDecoder::Flush() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (state_ == SURFACE_DESTROYED || defer_surface_creation_)
-    NotifyFlushDone();
-  else
-    StartCodecDrain(DRAIN_FOR_FLUSH);
-}
-
-void MediaCodecVideoDecoder::ConfigureMediaCodecAsynchronously() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  DCHECK_NE(state_, WAITING_FOR_CODEC);
-  state_ = WAITING_FOR_CODEC;
-
-  if (media_codec_) {
-    AVDACodecAllocator::Instance()->ReleaseMediaCodec(
-        std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
-    picture_buffer_manager_.CodecChanged(nullptr);
-  }
-
-  codec_config_->task_type_ =
-      AVDACodecAllocator::Instance()->TaskTypeForAllocation();
-  if (codec_config_->task_type_ == TaskType::FAILED_CODEC) {
-    // If there is no free thread, then just fail.
-    OnCodecConfigured(nullptr);
-    return;
-  }
-
-  // If autodetection is disallowed, fall back to Chrome's software decoders
-  // instead of using the software decoders provided by MediaCodec.
-  if (codec_config_->task_type_ == TaskType::SW_CODEC &&
-      IsMediaCodecSoftwareDecodingForbidden()) {
-    OnCodecConfigured(nullptr);
-    return;
-  }
-
-  AVDACodecAllocator::Instance()->CreateMediaCodecAsync(
-      weak_this_factory_.GetWeakPtr(), codec_config_);
-}
-
-void MediaCodecVideoDecoder::OnCodecConfigured(
-    std::unique_ptr<VideoCodecBridge> media_codec) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED);
-
-  // If we are supposed to notify that initialization is complete, then do so
-  // now.  Otherwise, this is a reconfiguration.
-  if (deferred_initialization_pending_) {
-    // Losing the output surface is not considered an error state, so notify
-    // success. The client will destroy this soon.
-    NotifyInitializationComplete(state_ == SURFACE_DESTROYED ? true
-                                                             : !!media_codec);
-    deferred_initialization_pending_ = false;
-  }
-
-  // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec,
-  // then the codec is already invalid so we return early and drop it.
-  if (state_ == SURFACE_DESTROYED)
-    return;
-
-  DCHECK(!media_codec_);
-  media_codec_ = std::move(media_codec);
-  picture_buffer_manager_.CodecChanged(media_codec_.get());
-  if (!media_codec_) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec");
-    return;
-  }
-
-  state_ = NO_ERROR;
-
-  ManageTimer(true);
-}
-
-void MediaCodecVideoDecoder::StartCodecDrain(DrainType drain_type) {
-  DVLOG(2) << __func__ << " drain_type:" << drain_type;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // We assume that DRAIN_FOR_FLUSH and DRAIN_FOR_RESET cannot come while
-  // another drain request is present, but DRAIN_FOR_DESTROY can.
-  DCHECK_NE(drain_type, DRAIN_TYPE_NONE);
-  DCHECK(drain_type_ == DRAIN_TYPE_NONE || drain_type == DRAIN_FOR_DESTROY)
-      << "Unexpected StartCodecDrain() with drain type " << drain_type
-      << " while already draining with drain type " << drain_type_;
-
-  const bool enqueue_eos = drain_type_ == DRAIN_TYPE_NONE;
-  drain_type_ = drain_type;
-
-  if (enqueue_eos)
-    DecodeBuffer(BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
-}
-
-bool MediaCodecVideoDecoder::IsDrainingForResetOrDestroy() const {
-  return drain_type_ == DRAIN_FOR_RESET || drain_type_ == DRAIN_FOR_DESTROY;
-}
-
-void MediaCodecVideoDecoder::OnDrainCompleted() {
-  DVLOG(2) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // If we were waiting for an EOS, clear the state and reset the MediaCodec
-  // as normal.
-  //
-  // Some Android platforms seem to send an EOS buffer even when we're not
-  // expecting it. In this case, destroy and reset the codec but don't notify
-  // flush done since it violates the state machine. http://crbug.com/585959.
-
-  switch (drain_type_) {
-    case DRAIN_TYPE_NONE:
-      // Unexpected EOS.
-      state_ = ERROR;
-      ResetCodecState();
-      break;
-    case DRAIN_FOR_FLUSH:
-      ResetCodecState();
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&MediaCodecVideoDecoder::NotifyFlushDone,
-                                weak_this_factory_.GetWeakPtr()));
-      break;
-    case DRAIN_FOR_RESET:
-      ResetCodecState();
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&MediaCodecVideoDecoder::NotifyResetDone,
-                                weak_this_factory_.GetWeakPtr()));
-      break;
-    case DRAIN_FOR_DESTROY:
-      ResetCodecState();
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&MediaCodecVideoDecoder::ActualDestroy,
-                                weak_this_factory_.GetWeakPtr()));
-      break;
-  }
-  drain_type_ = DRAIN_TYPE_NONE;
-}
-
-void MediaCodecVideoDecoder::ResetCodecState() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // If there is already a reset in flight, then that counts.  This can really
-  // only happen if somebody calls Reset.
-  // If the surface is destroyed there's nothing to do.
-  if (state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED)
-    return;
-
-  bitstream_buffers_in_decoder_.clear();
-
-  if (pending_input_buf_index_ != -1) {
-    // The data for that index exists in the input buffer, but corresponding
-    // shm block been deleted. Check that it is safe to flush the codec, i.e.
-    // |pending_bitstream_records_| is empty.
-    // TODO(timav): keep shm block for that buffer and remove this restriction.
-    DCHECK(pending_bitstream_records_.empty());
-    pending_input_buf_index_ = -1;
-  }
-
-  const bool did_codec_error_happen = state_ == ERROR;
-  state_ = NO_ERROR;
-
-  // Don't reset the codec here if there's no error and we're only flushing;
-  // instead defer until the next decode call; this prevents us from unbacking
-  // frames that might be out for display at end of stream.
-  codec_needs_reset_ = false;
-  if (drain_type_ == DRAIN_FOR_FLUSH && !did_codec_error_happen) {
-    codec_needs_reset_ = true;
-    return;
-  }
-
-  // Flush the codec if possible, or create a new one if not.
-  if (!did_codec_error_happen &&
-      !MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) {
-    DVLOG(3) << __func__ << " Flushing MediaCodec.";
-    media_codec_->Flush();
-    // Since we just flushed all the output buffers, make sure that nothing is
-    // using them.
-    picture_buffer_manager_.CodecChanged(media_codec_.get());
-  } else {
-    DVLOG(3) << __func__ << " Deleting the MediaCodec and creating a new one.";
-    g_mcvd_manager.Get().StopTimer(this);
-    ConfigureMediaCodecAsynchronously();
-  }
-}
-
-void MediaCodecVideoDecoder::Reset() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-  TRACE_EVENT0("media", "MCVD::Reset");
-
-  if (defer_surface_creation_) {
-    DCHECK(!media_codec_);
-    DCHECK(pending_bitstream_records_.empty());
-    DCHECK_EQ(state_, NO_ERROR);
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&MediaCodecVideoDecoder::NotifyResetDone,
-                              weak_this_factory_.GetWeakPtr()));
-    return;
-  }
-
-  while (!pending_bitstream_records_.empty()) {
-    int32_t bitstream_buffer_id =
-        pending_bitstream_records_.front().buffer.id();
-    pending_bitstream_records_.pop();
-
-    if (bitstream_buffer_id != -1) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::Bind(&MediaCodecVideoDecoder::NotifyEndOfBitstreamBuffer,
-                     weak_this_factory_.GetWeakPtr(), bitstream_buffer_id));
-    }
-  }
-  TRACE_COUNTER1("media", "MCVD::PendingBitstreamBufferCount", 0);
-  bitstreams_notified_in_advance_.clear();
-
-  picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
-
-  // Some VP8 files require complete MediaCodec drain before we can call
-  // MediaCodec.flush() or MediaCodec.reset(). http://crbug.com/598963.
-  if (media_codec_ && codec_config_->codec_ == kCodecVP8 &&
-      !bitstream_buffers_in_decoder_.empty()) {
-    // Postpone ResetCodecState() after the drain.
-    StartCodecDrain(DRAIN_FOR_RESET);
-  } else {
-    ResetCodecState();
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&MediaCodecVideoDecoder::NotifyResetDone,
-                              weak_this_factory_.GetWeakPtr()));
-  }
-}
-
-void MediaCodecVideoDecoder::SetSurface(int32_t surface_id) {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (surface_id == config_.surface_id) {
-    pending_surface_id_.reset();
-    return;
-  }
-
-  // Surface changes never take effect immediately, they will be handled during
-  // DequeOutput() once we get to a good switch point or immediately during an
-  // OnSurfaceDestroyed() call.
-  pending_surface_id_ = surface_id;
-}
-
-void MediaCodecVideoDecoder::Destroy() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  picture_buffer_manager_.Destroy(output_picture_buffers_);
-
-  client_ = nullptr;
-
-  // Some VP8 files require a complete MediaCodec drain before we can call
-  // MediaCodec.flush() or MediaCodec.release(). http://crbug.com/598963. In
-  // that case, postpone ActualDestroy() until after the drain.
-  if (media_codec_ && codec_config_->codec_ == kCodecVP8) {
-    // Clear |pending_bitstream_records_|.
-    while (!pending_bitstream_records_.empty())
-      pending_bitstream_records_.pop();
-
-    StartCodecDrain(DRAIN_FOR_DESTROY);
-  } else {
-    ActualDestroy();
-  }
-}
-
-void MediaCodecVideoDecoder::ActualDestroy() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Note that async codec construction might still be in progress.  In that
-  // case, the codec will be deleted when it completes once we invalidate all
-  // our weak refs.
-  weak_this_factory_.InvalidateWeakPtrs();
-  g_mcvd_manager.Get().StopTimer(this);
-  if (media_codec_) {
-    AVDACodecAllocator::Instance()->ReleaseMediaCodec(
-        std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
-  }
-
-  // We no longer care about |surface_id|, in case we did before.  It's okay
-  // if we have no surface and/or weren't the owner or a waiter.
-  AVDACodecAllocator::Instance()->DeallocateSurface(this, config_.surface_id);
-
-  delete this;
-}
-
-void MediaCodecVideoDecoder::OnSurfaceDestroyed() {
-  DVLOG(1) << __func__;
-  TRACE_EVENT0("media", "MCVD::OnSurfaceDestroyed");
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // If the API is available avoid having to restart the decoder in order to
-  // leave fullscreen. If we don't clear the surface immediately during this
-  // callback, the MediaCodec will throw an error as the surface is destroyed.
-  if (base::android::BuildInfo::GetInstance()->sdk_int() >= 23) {
-    // Since we can't wait for a transition, we must invalidate all outstanding
-    // picture buffers to avoid putting the GL system in a broken state.
-    picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_);
-
-    // Switch away from the surface being destroyed to a surface texture.
-    DCHECK_NE(config_.surface_id, SurfaceManager::kNoSurfaceID);
-
-    // The leaving fullscreen notification may come in before this point.
-    if (pending_surface_id_)
-      DCHECK_EQ(pending_surface_id_.value(), SurfaceManager::kNoSurfaceID);
-
-    pending_surface_id_ = SurfaceManager::kNoSurfaceID;
-    UpdateSurface();
-    return;
-  }
-
-  // If we're currently asynchronously configuring a codec, it will be destroyed
-  // when configuration completes and it notices that |state_| has changed to
-  // SURFACE_DESTROYED.
-  state_ = SURFACE_DESTROYED;
-  if (media_codec_) {
-    AVDACodecAllocator::Instance()->ReleaseMediaCodec(
-        std::move(media_codec_), codec_config_->task_type_, config_.surface_id);
-    picture_buffer_manager_.CodecChanged(nullptr);
-  }
-
-  // If we're draining, signal completion now because the drain can no longer
-  // proceed.
-  if (drain_type_ != DRAIN_TYPE_NONE)
-    OnDrainCompleted();
-}
-
-void MediaCodecVideoDecoder::InitializeCdm() {
-  DVLOG(2) << __func__ << ": " << config_.cdm_id;
-
-#if !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
+void MediaCodecVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+                                    const DecodeCB& decode_cb) {
   NOTIMPLEMENTED();
-  NotifyInitializationComplete(false);
-#else
-  // Store the CDM to hold a reference to it.
-  cdm_for_reference_holding_only_ =
-      MojoCdmService::LegacyGetCdm(config_.cdm_id);
-  DCHECK(cdm_for_reference_holding_only_);
-
-  // On Android platform the CdmContext must be a MediaDrmBridgeCdmContext.
-  media_drm_bridge_cdm_context_ = static_cast<MediaDrmBridgeCdmContext*>(
-      cdm_for_reference_holding_only_->GetCdmContext());
-  DCHECK(media_drm_bridge_cdm_context_);
-
-  // Register CDM callbacks. The callbacks registered will be posted back to
-  // this thread via BindToCurrentLoop.
-
-  // Since |this| holds a reference to the |cdm_|, by the time the CDM is
-  // destructed, UnregisterPlayer() must have been called and |this| has been
-  // destructed as well. So the |cdm_unset_cb| will never have a chance to be
-  // called.
-  // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms.
-  cdm_registration_id_ = media_drm_bridge_cdm_context_->RegisterPlayer(
-      BindToCurrentLoop(base::Bind(&MediaCodecVideoDecoder::OnKeyAdded,
-                                   weak_this_factory_.GetWeakPtr())),
-      base::Bind(&base::DoNothing));
-
-  // Deferred initialization will continue in OnMediaCryptoReady().
-  media_drm_bridge_cdm_context_->SetMediaCryptoReadyCB(
-      BindToCurrentLoop(base::Bind(&MediaCodecVideoDecoder::OnMediaCryptoReady,
-                                   weak_this_factory_.GetWeakPtr())));
-#endif  // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
 }
 
-void MediaCodecVideoDecoder::OnMediaCryptoReady(
-    MediaDrmBridgeCdmContext::JavaObjectPtr media_crypto,
-    bool needs_protected_surface) {
-  DVLOG(1) << __func__;
-
-  if (!media_crypto) {
-    LOG(ERROR) << "MediaCrypto is not available, can't play encrypted stream.";
-    cdm_for_reference_holding_only_ = nullptr;
-    media_drm_bridge_cdm_context_ = nullptr;
-    NotifyInitializationComplete(false);
-    return;
-  }
-
-  DCHECK(!media_crypto->is_null());
-
-  // We assume this is a part of the initialization process, thus MediaCodec
-  // is not created yet.
-  DCHECK(!media_codec_);
-
-  codec_config_->media_crypto_ = std::move(media_crypto);
-  codec_config_->needs_protected_surface_ = needs_protected_surface;
-
-  // After receiving |media_crypto_| we can configure MediaCodec.
-  ConfigureMediaCodecAsynchronously();
+void MediaCodecVideoDecoder::Reset(const base::Closure& closure) {
+  NOTIMPLEMENTED();
 }
 
-void MediaCodecVideoDecoder::OnKeyAdded() {
-  DVLOG(1) << __func__;
-
-  if (state_ == WAITING_FOR_KEY)
-    state_ = NO_ERROR;
-
-  DoIOTask(true);
+std::string MediaCodecVideoDecoder::GetDisplayName() const {
+  return "MediaCodecVideoDecoder";
 }
 
-void MediaCodecVideoDecoder::NotifyError(Error error) {
-  state_ = ERROR;
-  if (client_)
-    client_->NotifyError(error);
+bool MediaCodecVideoDecoder::NeedsBitstreamConversion() const {
+  return true;
 }
 
-void MediaCodecVideoDecoder::ManageTimer(bool did_work) {
-  bool should_be_running = true;
-
-  base::TimeTicks now = base::TimeTicks::Now();
-  if (!did_work && !most_recent_work_.is_null()) {
-    // Make sure that we have done work recently enough, else stop the timer.
-    if (now - most_recent_work_ > IdleTimerTimeOut) {
-      most_recent_work_ = base::TimeTicks();
-      should_be_running = false;
-    }
-  } else {
-    most_recent_work_ = now;
-  }
-
-  if (should_be_running)
-    g_mcvd_manager.Get().StartTimer(this);
-  else
-    g_mcvd_manager.Get().StopTimer(this);
+bool MediaCodecVideoDecoder::CanReadWithoutStalling() const {
+  NOTIMPLEMENTED();
+  return false;
 }
 
-
-bool MediaCodecVideoDecoder::UpdateSurface() {
-  DCHECK(pending_surface_id_);
-  DCHECK_NE(config_.surface_id, pending_surface_id_.value());
-  DCHECK(config_.surface_id == SurfaceManager::kNoSurfaceID ||
-         pending_surface_id_.value() == SurfaceManager::kNoSurfaceID);
-
-  const int previous_surface_id = config_.surface_id;
-  const int new_surface_id = pending_surface_id_.value();
-  pending_surface_id_.reset();
-  bool success = true;
-
-  // TODO(watk): Fix this so we can wait for the new surface to be allocated.
-  if (!AVDACodecAllocator::Instance()->AllocateSurface(this, new_surface_id)) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to allocate the new surface");
-    success = false;
-  }
-
-  // Ensure the current context is active when switching surfaces; we may need
-  // to create a new texture.
-  if (success && !make_context_current_cb_.Run()) {
-    NOTIFY_ERROR(PLATFORM_FAILURE,
-                 "Failed to make this decoder's GL context current when "
-                 "switching surfaces.");
-    success = false;
-  }
-
-  if (success) {
-    codec_config_->surface_ =
-        picture_buffer_manager_.Initialize(new_surface_id);
-    if (codec_config_->surface_.IsEmpty()) {
-      NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to switch surfaces.");
-      success = false;
-    }
-  }
-
-  if (success && media_codec_ &&
-      !media_codec_->SetSurface(codec_config_->surface_.j_surface().obj())) {
-    NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces.");
-    success = false;
-  }
-
-  if (success) {
-    config_.surface_id = new_surface_id;
-  } else {
-    // This might be called from OnSurfaceDestroyed(), so we have to release the
-    // MediaCodec if we failed to switch the surface.
-    if (media_codec_) {
-      AVDACodecAllocator::Instance()->ReleaseMediaCodec(
-          std::move(media_codec_), codec_config_->task_type_,
-          previous_surface_id);
-      picture_buffer_manager_.CodecChanged(nullptr);
-    }
-    AVDACodecAllocator::Instance()->DeallocateSurface(this, new_surface_id);
-  }
-
-  // Regardless of whether we succeeded, we no longer own the previous surface.
-  AVDACodecAllocator::Instance()->DeallocateSurface(this, previous_surface_id);
-
-  return success;
+int MediaCodecVideoDecoder::GetMaxDecodeRequests() const {
+  return 4;
 }
 
 }  // namespace media
diff --git a/media/gpu/android/media_codec_video_decoder.h b/media/gpu/android/media_codec_video_decoder.h
index dccd4a3a..8a5f9ef 100644
--- a/media/gpu/android/media_codec_video_decoder.h
+++ b/media/gpu/android/media_codec_video_decoder.h
@@ -5,250 +5,34 @@
 #ifndef MEDIA_GPU_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
 #define MEDIA_GPU_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
 
-#include <stdint.h>
-
-#include <list>
-#include <map>
-#include <queue>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/optional.h"
 #include "base/threading/thread_checker.h"
-#include "base/timer/timer.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
-#include "media/base/android/media_drm_bridge_cdm_context.h"
-#include "media/base/android/sdk_media_codec_bridge.h"
-#include "media/base/media_keys.h"
-#include "media/gpu/avda_codec_allocator.h"
-#include "media/gpu/avda_picture_buffer_manager.h"
-#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
+#include "media/base/video_decoder.h"
 #include "media/gpu/media_gpu_export.h"
-#include "media/video/video_decode_accelerator.h"
-#include "ui/gl/android/scoped_java_surface.h"
 
 namespace media {
 
 // An Android VideoDecoder that delegates to MediaCodec.
-class MEDIA_GPU_EXPORT MediaCodecVideoDecoder
-    : public AVDACodecAllocatorClient {
+class MEDIA_GPU_EXPORT MediaCodecVideoDecoder : public VideoDecoder {
  public:
-  MediaCodecVideoDecoder(
-      const MakeGLContextCurrentCallback& make_context_current_cb,
-      const GetGLES2DecoderCallback& get_gles2_decoder_cb);
-
+  MediaCodecVideoDecoder();
   ~MediaCodecVideoDecoder() override;
 
-  // VideoDecodeAccelerator implementation:
-  bool Initialize(const Config& config, Client* client) override;
-  void Decode(const BitstreamBuffer& bitstream_buffer) override;
-  void Flush() override;
-  void Reset() override;
-  void SetSurface(int32_t surface_id) override;
-  void Destroy() override;
-
-  // AVDACodecAllocatorClient implementation:
-  void OnSurfaceAvailable(bool success) override;
-  void OnSurfaceDestroyed() override;
-  void OnCodecConfigured(
-      std::unique_ptr<VideoCodecBridge> media_codec) override;
+  // VideoDecoder implementation:
+  std::string GetDisplayName() const override;
+  void Initialize(const VideoDecoderConfig& config,
+                  bool low_delay,
+                  CdmContext* cdm_context,
+                  const InitCB& init_cb,
+                  const OutputCB& output_cb) override;
+  void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+              const DecodeCB& decode_cb) override;
+  void Reset(const base::Closure& closure) override;
+  bool NeedsBitstreamConversion() const override;
+  bool CanReadWithoutStalling() const override;
+  int GetMaxDecodeRequests() const override;
 
  private:
-  friend class MCVDManager;
-
-  enum State {
-    NO_ERROR,
-    ERROR,
-    // Set when we are asynchronously constructing the codec.  Will transition
-    // to NO_ERROR or ERROR depending on success.
-    WAITING_FOR_CODEC,
-    // Set when we have a codec, but it doesn't yet have a key.
-    WAITING_FOR_KEY,
-    // The output surface was destroyed. We must not configure a new MediaCodec
-    // with the destroyed surface.
-    SURFACE_DESTROYED,
-  };
-
-  enum DrainType {
-    DRAIN_TYPE_NONE,
-    DRAIN_FOR_FLUSH,
-    DRAIN_FOR_RESET,
-    DRAIN_FOR_DESTROY,
-  };
-
-  // Initialize of the picture buffer manager.  This is to be called when the
-  // SurfaceView in |surface_id_|, if any, is no longer busy.  It will return
-  // false on failure, and true if initialization was successful.  This includes
-  // synchronous and asynchronous init; the MCVD might not yet have a codec on
-  // success, but async init will at least be in progress.
-  bool InitializePictureBufferManager();
-
-  // A part of destruction process that is sometimes postponed after the drain.
-  void ActualDestroy();
-
-  // Configures |media_codec_| with the given codec parameters from the client.
-  // This configuration will (probably) not be complete before this call
-  // returns.  Multiple calls before completion will be ignored.  |state_|
-  // must be NO_ERROR or WAITING_FOR_CODEC.  Note that, once you call this,
-  // you should be careful to avoid modifying members of |codec_config_| until
-  // |state_| is no longer WAITING_FOR_CODEC.
-  void ConfigureMediaCodecAsynchronously();
-
-  // Sends the decoded frame specified by |codec_buffer_index| to the client.
-  void SendDecodedFrameToClient(int32_t codec_buffer_index,
-                                int32_t bitstream_id);
-
-  // Does pending IO tasks if any. Once this is called, it polls |media_codec_|
-  // until it finishes pending tasks. For the polling, |kDecodePollDelay| is
-  // used.
-  void DoIOTask(bool start_timer);
-
-  // Feeds input data to |media_codec_|. This checks
-  // |pending_bitstream_buffers_| and queues a buffer to |media_codec_|.
-  // Returns true if any input was processed.
-  bool QueueInput();
-
-  // Dequeues output from |media_codec_| and feeds the decoded frame to the
-  // client.  Returns a hint about whether calling again might produce
-  // more output.
-  bool DequeueOutput();
-
-  // Decode the content in the |bitstream_buffer|. Note that a
-  // |bitstream_buffer| of id as -1 indicates a flush command.
-  void DecodeBuffer(const BitstreamBuffer& bitstream_buffer);
-
-  // Called during Initialize() for encrypted streams to set up the CDM.
-  void InitializeCdm();
-
-  // Called after the CDM obtains a MediaCrypto object.
-  void OnMediaCryptoReady(MediaDrmBridgeCdmContext::JavaObjectPtr media_crypto,
-                          bool needs_protected_surface);
-
-  // Called when a new key is added to the CDM.
-  void OnKeyAdded();
-
-  // Notifies the client about the error and sets |state_| to |ERROR|.
-  void NotifyError(Error error);
-
-  // Start or stop our work-polling timer based on whether we did any work, and
-  // how long it has been since we've done work.  Calling this with true will
-  // start the timer.  Calling it with false may stop the timer.
-  void ManageTimer(bool did_work);
-
-  // Start the MediaCodec drain process by adding end_of_stream() buffer to the
-  // encoded buffers queue. When we receive EOS from the output buffer the drain
-  // process completes and we perform the action depending on the |drain_type|.
-  void StartCodecDrain(DrainType drain_type);
-
-  // Returns true if we are currently draining the codec and doing that as part
-  // of Reset() or Destroy() VP8 workaround. (http://crbug.com/598963). We won't
-  // display any frames and disable normal errors handling.
-  bool IsDrainingForResetOrDestroy() const;
-
-  // A helper method that performs the operation required after the drain
-  // completion (usually when we receive EOS in the output). The operation
-  // itself depends on the |drain_type_|.
-  void OnDrainCompleted();
-
-  // Resets MediaCodec and buffers/containers used for storing output. These
-  // components need to be reset upon EOS to decode a later stream. Input state
-  // (e.g. queued BitstreamBuffers) is not reset, as input following an EOS
-  // is still valid and should be processed.
-  void ResetCodecState();
-
-  // Indicates if MediaCodec should not be used for software decoding since we
-  // have safer versions elsewhere.
-  bool IsMediaCodecSoftwareDecodingForbidden() const;
-
-  // On platforms which support seamless surface changes, this will reinitialize
-  // the picture buffer manager with the new surface. This function reads and
-  // clears the surface id from |pending_surface_id_|. It will issue a decode
-  // error if the surface change fails. Returns false on failure.
-  bool UpdateSurface();
-
-  // Used to DCHECK that we are called on the correct thread.
-  base::ThreadChecker thread_checker_;
-
-  // Callback to set the correct gl context.
-  MakeGLContextCurrentCallback make_context_current_cb_;
-
-  // Callback to get the GLES2Decoder instance.
-  GetGLES2DecoderCallback get_gles2_decoder_cb_;
-
-  State state_;
-
-  // The low-level decoder which Android SDK provides.
-  std::unique_ptr<VideoCodecBridge> media_codec_;
-
-  // The resolution of the stream.
-  gfx::Size size_;
-
-  // Encoded bitstream buffers to be passed to media codec, queued until an
-  // input buffer is available.
-  std::queue<BitstreamRecord> pending_bitstream_records_;
-
-  // A map of presentation timestamp to bitstream buffer id for the bitstream
-  // buffers that have been submitted to the decoder but haven't yet produced an
-  // output frame with the same timestamp. Note: there will only be one entry
-  // for multiple bitstream buffers that have the same presentation timestamp.
-  std::map<base::TimeDelta, int32_t> bitstream_buffers_in_decoder_;
-
-  // Keeps track of bitstream ids notified to the client with
-  // NotifyEndOfBitstreamBuffer() before getting output from the bitstream.
-  std::list<int32_t> bitstreams_notified_in_advance_;
-
-  AVDAPictureBufferManager picture_buffer_manager_;
-
-  // Time at which we last did useful work on io_timer_.
-  base::TimeTicks most_recent_work_;
-
-  // Type of a drain request. We need to distinguish between DRAIN_FOR_FLUSH
-  // and other types, see IsDrainingForResetOrDestroy().
-  DrainType drain_type_;
-
-  // Holds a ref-count to the CDM to avoid using the CDM after it's destroyed.
-  scoped_refptr<MediaKeys> cdm_for_reference_holding_only_;
-
-  MediaDrmBridgeCdmContext* media_drm_bridge_cdm_context_;
-
-  // MediaDrmBridge requires registration/unregistration of the player, this
-  // registration id is used for this.
-  int cdm_registration_id_;
-
-  // Configuration that we use for MediaCodec.
-  // Do not update any of its members while |state_| is WAITING_FOR_CODEC.
-  scoped_refptr<CodecConfig> codec_config_;
-
-  // Index of the dequeued and filled buffer that we keep trying to enqueue.
-  // Such buffer appears in MEDIA_CODEC_NO_KEY processing.
-  int pending_input_buf_index_;
-
-  // True if and only if VDA initialization is deferred, and we have not yet
-  // called NotifyInitializationComplete.
-  bool deferred_initialization_pending_;
-
-  // Indicates if ResetCodecState() should be called upon the next call to
-  // Decode(). Allows us to avoid trashing the last few frames of a playback
-  // when the EOS buffer is received.
-  bool codec_needs_reset_;
-
-  // True if surface creation and |picture_buffer_manager_| initialization has
-  // been defered until the first Decode() call.
-  bool defer_surface_creation_;
-
-  // Has a value if a SetSurface() call has occurred and a new surface should be
-  // switched to when possible. Cleared during OnSurfaceDestroyed() and if all
-  // pictures have been rendered in DequeueOutput().
-  base::Optional<int32_t> pending_surface_id_;
-
-  // Copy of the VDA::Config we were given.
-  Config config_;
-
-  // WeakPtrFactory for posting tasks back to |this|.
-  base::WeakPtrFactory<MediaCodecVideoDecoder> weak_this_factory_;
-
-  friend class MediaCodecVideoDecoderTest;
+  DISALLOW_COPY_AND_ASSIGN(MediaCodecVideoDecoder);
 };
 
 }  // namespace media
diff --git a/media/gpu/ipc/service/media_gpu_channel_manager.h b/media/gpu/ipc/service/media_gpu_channel_manager.h
index e0ff821e..a89727c 100644
--- a/media/gpu/ipc/service/media_gpu_channel_manager.h
+++ b/media/gpu/ipc/service/media_gpu_channel_manager.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/scoped_ptr_hash_map.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
 #include "media/video/video_decode_accelerator.h"
@@ -23,7 +24,8 @@
 
 class MediaGpuChannel;
 
-class MediaGpuChannelManager {
+class MediaGpuChannelManager
+    : public base::SupportsWeakPtr<MediaGpuChannelManager> {
  public:
   explicit MediaGpuChannelManager(gpu::GpuChannelManager* channel_manager);
   ~MediaGpuChannelManager();
diff --git a/media/midi/BUILD.gn b/media/midi/BUILD.gn
index 4d37bdf..38100e1 100644
--- a/media/midi/BUILD.gn
+++ b/media/midi/BUILD.gn
@@ -209,10 +209,6 @@
     sources += [ "midi_manager_alsa_unittest.cc" ]
   }
 
-  if (use_x11) {
-    deps += [ "//tools/xdisplaycheck" ]
-  }
-
   # This target should not require the Chrome executable to run.
   assert_no_deps = [ "//chrome" ]
 }
diff --git a/media/mojo/BUILD.gn b/media/mojo/BUILD.gn
index 27af0fe3..bcaaa4c 100644
--- a/media/mojo/BUILD.gn
+++ b/media/mojo/BUILD.gn
@@ -8,6 +8,7 @@
   testonly = true
   sources = [
     "clients/mojo_audio_decoder_unittest.cc",
+    "clients/mojo_cdm_unittest.cc",
     "clients/mojo_renderer_unittest.cc",
     "common/media_type_converters_unittest.cc",
     "common/mojo_decoder_buffer_converter_unittest.cc",
diff --git a/media/mojo/clients/mojo_cdm.cc b/media/mojo/clients/mojo_cdm.cc
index 31f1575..042ef15 100644
--- a/media/mojo/clients/mojo_cdm.cc
+++ b/media/mojo/clients/mojo_cdm.cc
@@ -112,7 +112,7 @@
   }
 
   // Otherwise, set an error handler to catch the connection error.
-  remote_cdm_.set_connection_error_handler(
+  remote_cdm_.set_connection_error_with_reason_handler(
       base::Bind(&MojoCdm::OnConnectionError, base::Unretained(this)));
 
   pending_init_promise_ = std::move(promise);
@@ -122,15 +122,22 @@
       base::Bind(&MojoCdm::OnCdmInitialized, base::Unretained(this)));
 }
 
-void MojoCdm::OnConnectionError() {
-  LOG(ERROR) << "Remote CDM connection error.";
+// TODO(xhwang): Properly handle CDM calls after connection error.
+// See http://crbug.com/671362
+void MojoCdm::OnConnectionError(uint32_t custom_reason,
+                                const std::string& description) {
+  LOG(ERROR) << "Remote CDM connection error: custom_reason=" << custom_reason
+             << ", description=\"" << description << "\"";
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Handle initial connection error.
   if (pending_init_promise_) {
+    DCHECK(!cdm_session_tracker_.HasRemainingSessions());
     pending_init_promise_->reject(CdmPromise::NOT_SUPPORTED_ERROR, 0,
                                   "Mojo CDM creation failed.");
+    // Dropping the promise could cause |this| to be destructed.
     pending_init_promise_.reset();
+    return;
   }
 
   cdm_session_tracker_.CloseRemainingSessions(session_closed_cb_);
diff --git a/media/mojo/clients/mojo_cdm.h b/media/mojo/clients/mojo_cdm.h
index cf754f9c..0971c62 100644
--- a/media/mojo/clients/mojo_cdm.h
+++ b/media/mojo/clients/mojo_cdm.h
@@ -89,7 +89,8 @@
                      const CdmConfig& cdm_config,
                      std::unique_ptr<CdmInitializedPromise> promise);
 
-  void OnConnectionError();
+  void OnConnectionError(uint32_t custom_reason,
+                         const std::string& description);
 
   // mojom::ContentDecryptionModuleClient implementation.
   void OnSessionMessage(const std::string& session_id,
diff --git a/media/mojo/clients/mojo_cdm_unittest.cc b/media/mojo/clients/mojo_cdm_unittest.cc
new file mode 100644
index 0000000..3dee666e
--- /dev/null
+++ b/media/mojo/clients/mojo_cdm_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/test_message_loop.h"
+#include "media/base/cdm_config.h"
+#include "media/base/mock_filters.h"
+#include "media/cdm/default_cdm_factory.h"
+#include "media/mojo/clients/mojo_cdm.h"
+#include "media/mojo/interfaces/content_decryption_module.mojom.h"
+#include "media/mojo/services/mojo_cdm_service.h"
+#include "media/mojo/services/mojo_cdm_service_context.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::StrictMock;
+
+namespace media {
+
+namespace {
+
+const char kClearKeyKeySystem[] = "org.w3.clearkey";
+const char kTestSecurityOrigin[] = "https://www.test.com";
+
+}  // namespace
+
+class MojoCdmTest : public ::testing::Test {
+ public:
+  enum ExpectedResult { SUCCESS, CONNECTION_ERROR };
+
+  MojoCdmTest()
+      : mojo_cdm_service_(base::MakeUnique<MojoCdmService>(
+            mojo_cdm_service_context_.GetWeakPtr(),
+            &cdm_factory_)),
+        cdm_binding_(mojo_cdm_service_.get()) {}
+
+  virtual ~MojoCdmTest() {}
+
+  void Initialize(ExpectedResult expected_result) {
+    mojom::ContentDecryptionModulePtr remote_cdm;
+    auto cdm_request = mojo::GetProxy(&remote_cdm);
+
+    switch (expected_result) {
+      case SUCCESS:
+        cdm_binding_.Bind(std::move(cdm_request));
+        break;
+      case CONNECTION_ERROR:
+        cdm_request.ResetWithReason(0, "Request dropped for testing.");
+        break;
+    }
+
+    MojoCdm::Create(kClearKeyKeySystem, GURL(kTestSecurityOrigin), CdmConfig(),
+                    std::move(remote_cdm),
+                    base::Bind(&MockCdmClient::OnSessionMessage,
+                               base::Unretained(&cdm_client_)),
+                    base::Bind(&MockCdmClient::OnSessionClosed,
+                               base::Unretained(&cdm_client_)),
+                    base::Bind(&MockCdmClient::OnSessionKeysChange,
+                               base::Unretained(&cdm_client_)),
+                    base::Bind(&MockCdmClient::OnSessionExpirationUpdate,
+                               base::Unretained(&cdm_client_)),
+                    base::Bind(&MojoCdmTest::OnCdmCreated,
+                               base::Unretained(this), expected_result));
+
+    base::RunLoop().RunUntilIdle();
+
+    if (expected_result == SUCCESS) {
+      EXPECT_TRUE(mojo_cdm_);
+    } else {
+      EXPECT_FALSE(mojo_cdm_);
+    }
+  }
+
+  void OnCdmCreated(ExpectedResult expected_result,
+                    const scoped_refptr<MediaKeys>& cdm,
+                    const std::string& error_message) {
+    if (!cdm) {
+      DVLOG(1) << error_message;
+      return;
+    }
+
+    EXPECT_EQ(SUCCESS, expected_result);
+    mojo_cdm_ = cdm;
+  }
+
+  // Fixture members.
+  base::TestMessageLoop message_loop_;
+
+  MojoCdmServiceContext mojo_cdm_service_context_;
+  StrictMock<MockCdmClient> cdm_client_;
+
+  // TODO(jrummell): Use a MockCdmFactory to create a MockCdm here for more test
+  // coverage.
+  DefaultCdmFactory cdm_factory_;
+
+  std::unique_ptr<MojoCdmService> mojo_cdm_service_;
+  mojo::Binding<mojom::ContentDecryptionModule> cdm_binding_;
+  scoped_refptr<MediaKeys> mojo_cdm_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MojoCdmTest);
+};
+
+TEST_F(MojoCdmTest, Create_Success) {
+  Initialize(SUCCESS);
+}
+
+TEST_F(MojoCdmTest, Create_ConnectionError) {
+  Initialize(CONNECTION_ERROR);
+}
+
+// TODO(xhwang): Add more test cases!
+
+}  // namespace media
diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom
index cd098adb..0bd3b3a 100644
--- a/media/mojo/interfaces/media_types.mojom
+++ b/media/mojo/interfaces/media_types.mojom
@@ -5,7 +5,7 @@
 module media.mojom;
 
 import "ui/gfx/geometry/mojo/geometry.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 
 // See media/base/buffering_state.h for descriptions.
 [Native]
diff --git a/media/mojo/interfaces/renderer.mojom b/media/mojo/interfaces/renderer.mojom
index 6476bb5..38ca440 100644
--- a/media/mojo/interfaces/renderer.mojom
+++ b/media/mojo/interfaces/renderer.mojom
@@ -6,7 +6,8 @@
 
 import "media/mojo/interfaces/demuxer_stream.mojom";
 import "media/mojo/interfaces/media_types.mojom";
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
+import "mojo/common/unguessable_token.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojo/url.mojom";
 
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index a27ce33..162d435d 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -61,6 +61,8 @@
   sources = [
     "demuxer_stream_provider_shim.cc",
     "demuxer_stream_provider_shim.h",
+    "gpu_mojo_media_client.cc",
+    "gpu_mojo_media_client.h",
     "interface_factory_impl.cc",
     "interface_factory_impl.h",
     "media_mojo_export.h",
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
new file mode 100644
index 0000000..1c0e2f9
--- /dev/null
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/mojo/services/gpu_mojo_media_client.h"
+
+#include "media/base/audio_decoder.h"
+#include "media/base/cdm_factory.h"
+#include "media/base/video_decoder.h"
+
+#if defined(OS_ANDROID)
+#include "base/memory/ptr_util.h"
+#include "media/base/android/android_cdm_factory.h"
+#include "media/filters/android/media_codec_audio_decoder.h"
+#include "media/mojo/interfaces/provision_fetcher.mojom.h"
+#include "media/mojo/services/mojo_provision_fetcher.h"
+#include "services/service_manager/public/cpp/connect.h"
+#endif  // defined(OS_ANDROID)
+
+namespace media {
+
+namespace {
+
+#if defined(OS_ANDROID)
+std::unique_ptr<ProvisionFetcher> CreateProvisionFetcher(
+    service_manager::mojom::InterfaceProvider* interface_provider) {
+  mojom::ProvisionFetcherPtr provision_fetcher_ptr;
+  service_manager::GetInterface(interface_provider, &provision_fetcher_ptr);
+  return base::MakeUnique<MojoProvisionFetcher>(
+      std::move(provision_fetcher_ptr));
+}
+#endif  // defined(OS_ANDROID)
+
+}  // namespace
+
+GpuMojoMediaClient::GpuMojoMediaClient(
+    scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+    base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager)
+    : gpu_task_runner_(std::move(gpu_task_runner)),
+      media_gpu_channel_manager_(std::move(media_gpu_channel_manager)) {}
+
+GpuMojoMediaClient::~GpuMojoMediaClient() {}
+
+std::unique_ptr<AudioDecoder> GpuMojoMediaClient::CreateAudioDecoder(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+#if defined(OS_ANDROID)
+  return base::MakeUnique<MediaCodecAudioDecoder>(task_runner);
+#else
+  return nullptr;
+#endif  // defined(OS_ANDROID)
+}
+
+std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  static_cast<void>(media_gpu_channel_manager_);
+
+  // TODO(sandersd): Factory for VideoDecoders.
+  return nullptr;
+}
+
+std::unique_ptr<CdmFactory> GpuMojoMediaClient::CreateCdmFactory(
+    service_manager::mojom::InterfaceProvider* interface_provider) {
+#if defined(OS_ANDROID)
+  return base::MakeUnique<AndroidCdmFactory>(
+      base::Bind(&CreateProvisionFetcher, interface_provider));
+#else
+  return nullptr;
+#endif  // defined(OS_ANDROID)
+}
+
+}  // namespace media
diff --git a/media/mojo/services/gpu_mojo_media_client.h b/media/mojo/services/gpu_mojo_media_client.h
new file mode 100644
index 0000000..1e5c4b1
--- /dev/null
+++ b/media/mojo/services/gpu_mojo_media_client.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_MOJO_SERVICES_GPU_MOJO_MEDIA_CLIENT_H_
+#define MEDIA_MOJO_SERVICES_GPU_MOJO_MEDIA_CLIENT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "media/mojo/services/mojo_media_client.h"
+
+namespace media {
+
+class MediaGpuChannelManager;
+
+class GpuMojoMediaClient : public MojoMediaClient {
+ public:
+  // |media_gpu_channel_manager| must only be used on |gpu_task_runner|, which
+  // is expected to be the GPU main thread task runner.
+  GpuMojoMediaClient(
+      scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+      base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager);
+  ~GpuMojoMediaClient() final;
+
+  // MojoMediaClient implementation.
+  std::unique_ptr<AudioDecoder> CreateAudioDecoder(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) final;
+  std::unique_ptr<VideoDecoder> CreateVideoDecoder(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) final;
+  std::unique_ptr<CdmFactory> CreateCdmFactory(
+      service_manager::mojom::InterfaceProvider* interface_provider) final;
+
+ private:
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
+  base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuMojoMediaClient);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_SERVICES_GPU_MOJO_MEDIA_CLIENT_H_
diff --git a/media/mojo/services/media_service_factory.cc b/media/mojo/services/media_service_factory.cc
index 8c71701..ef8d61c 100644
--- a/media/mojo/services/media_service_factory.cc
+++ b/media/mojo/services/media_service_factory.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "media/mojo/services/gpu_mojo_media_client.h"
 #include "media/mojo/services/media_service.h"
 #include "media/mojo/services/test_mojo_media_client.h"
 
@@ -27,6 +28,14 @@
 #endif
 }
 
+std::unique_ptr<service_manager::Service> CreateGpuMediaService(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager) {
+  return std::unique_ptr<service_manager::Service>(
+      new MediaService(base::MakeUnique<GpuMojoMediaClient>(
+          task_runner, media_gpu_channel_manager)));
+}
+
 std::unique_ptr<service_manager::Service> CreateMediaServiceForTesting() {
   return std::unique_ptr<service_manager::Service>(
       new MediaService(base::MakeUnique<TestMojoMediaClient>()));
diff --git a/media/mojo/services/media_service_factory.h b/media/mojo/services/media_service_factory.h
index 8e89c207..40e34a3 100644
--- a/media/mojo/services/media_service_factory.h
+++ b/media/mojo/services/media_service_factory.h
@@ -7,18 +7,30 @@
 
 #include <memory>
 
-#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "services/service_manager/public/cpp/service.h"
 
 namespace media {
 
+class MediaGpuChannelManager;
+
 // Creates a MediaService instance using the default MojoMediaClient on each
 // platform. Uses the TestMojoMediaClient if |enable_test_mojo_media_client| is
 // true.
 std::unique_ptr<service_manager::Service> MEDIA_MOJO_EXPORT
 CreateMediaService();
 
+// Creates a MediaService instance using the GpuMojoMediaClient.
+// |media_gpu_channel_manager| must only be used on |task_runner|, which is
+// expected to be the GPU main thread task runner.
+std::unique_ptr<service_manager::Service> MEDIA_MOJO_EXPORT
+CreateGpuMediaService(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager);
+
 // Creates a MediaService instance using the TestMojoMediaClient.
 std::unique_ptr<service_manager::Service> MEDIA_MOJO_EXPORT
 CreateMediaServiceForTesting();
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc
index 679ad4a..ca418dc 100644
--- a/media/mojo/services/mojo_cdm_service.cc
+++ b/media/mojo/services/mojo_cdm_service.cc
@@ -232,9 +232,8 @@
                                std::move(keys_data));
 }
 
-void MojoCdmService::OnSessionExpirationUpdate(
-    const std::string& session_id,
-    const base::Time& new_expiry_time_sec) {
+void MojoCdmService::OnSessionExpirationUpdate(const std::string& session_id,
+                                               base::Time new_expiry_time_sec) {
   DVLOG(2) << __func__ << " expiry=" << new_expiry_time_sec;
   client_->OnSessionExpirationUpdate(session_id,
                                      new_expiry_time_sec.ToDoubleT());
diff --git a/media/mojo/services/mojo_cdm_service.h b/media/mojo/services/mojo_cdm_service.h
index e69ee63..3b87d1a 100644
--- a/media/mojo/services/mojo_cdm_service.h
+++ b/media/mojo/services/mojo_cdm_service.h
@@ -90,7 +90,7 @@
                            bool has_additional_usable_key,
                            CdmKeysInfo keys_info);
   void OnSessionExpirationUpdate(const std::string& session_id,
-                                 const base::Time& new_expiry_time);
+                                 base::Time new_expiry_time);
   void OnSessionClosed(const std::string& session_id);
 
   // Callback for when |decryptor_| loses connectivity.
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index c924195..4899be0 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -344,8 +344,8 @@
   // failed.
   init_cb_ = BindToCurrentLoop(init_cb);
 
-  const AudioParameters& hw_params =
-      sink_->GetOutputDeviceInfo().output_params();
+  auto output_device_info = sink_->GetOutputDeviceInfo();
+  const AudioParameters& hw_params = output_device_info.output_params();
   expecting_config_changes_ = stream->SupportsConfigChanges();
   if (!expecting_config_changes_ || !hw_params.IsValid() ||
       hw_params.format() == AudioParameters::AUDIO_FAKE) {
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 0bdb781c..18cbb3b3 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -988,6 +988,12 @@
   EXPECT_HASH_EQ("fd59357dfd9c144ab4fb8181b2de32c3", GetVideoHash());
 }
 
+TEST_F(PipelineIntegrationTest, TrackStatusChangesBeforePipelineStarted) {
+  std::vector<MediaTrack::Id> empty_track_ids;
+  pipeline_->OnEnabledAudioTracksChanged(empty_track_ids);
+  pipeline_->OnSelectedVideoTrackChanged(empty_track_ids);
+}
+
 TEST_F(PipelineIntegrationTest, TrackStatusChangesAfterPipelineEnded) {
   ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed));
   Play();
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 73ad1d5..60d523f 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -14,7 +14,13 @@
 
 mojom("common_custom_types") {
   sources = [
-    "common_custom_types.mojom",
+    "file.mojom",
+    "file_path.mojom",
+    "string16.mojom",
+    "time.mojom",
+    "unguessable_token.mojom",
+    "values.mojom",
+    "version.mojom",
   ]
 }
 
diff --git a/mojo/common/common_custom_types.mojom b/mojo/common/common_custom_types.mojom
deleted file mode 100644
index bd3c5d97..0000000
--- a/mojo/common/common_custom_types.mojom
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module mojo.common.mojom;
-
-[Native]
-struct FilePath;
-
-[Native]
-struct ListValue;
-
-[Native]
-struct DictionaryValue;
-
-[Native]
-struct Time;
-
-struct TimeDelta {
-  int64 microseconds;
-};
-
-[Native]
-struct TimeTicks;
-
-// Corresponds to |base::string16| in base/strings/string16.h
-// Corresponds to |WTF::String| in
-// third_party/WebKit/Source/wtf/text/WTFString.h.
-struct String16 {
-  array<uint16> data;
-};
-
-// Corresponds to |base::UnguessableToken| in base/unguessable_token.h
-struct UnguessableToken {
-  uint64 high;
-  uint64 low;
-};
-
-// Corresponds to |base::Version| in base/version.h
-struct Version {
-  array<uint32> components;
-};
-
-// Corresponds to |base::File| in base/files/file.h
-struct File {
-  handle fd;
-};
diff --git a/mojo/common/common_custom_types.typemap b/mojo/common/common_custom_types.typemap
deleted file mode 100644
index f0f9e80f..0000000
--- a/mojo/common/common_custom_types.typemap
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//mojo/common/common_custom_types.mojom"
-public_headers = [
-  "//base/files/file.h",
-  "//base/files/file_path.h",
-  "//base/strings/string16.h",
-  "//base/time/time.h",
-  "//base/unguessable_token.h",
-  "//base/values.h",
-  "//base/version.h",
-]
-traits_headers = [
-  "//ipc/ipc_message_utils.h",
-  "//mojo/common/common_custom_types_struct_traits.h",
-]
-public_deps = [
-  "//ipc",
-  "//mojo/common:struct_traits",
-]
-
-type_mappings = [
-  "mojo.common.mojom.File=base::File[move_only,nullable_is_same_type]",
-  "mojo.common.mojom.FilePath=base::FilePath",
-  "mojo.common.mojom.DictionaryValue=base::DictionaryValue",
-  "mojo.common.mojom.ListValue=base::ListValue",
-  "mojo.common.mojom.UnguessableToken=base::UnguessableToken",
-  "mojo.common.mojom.String16=base::string16",
-  "mojo.common.mojom.Time=base::Time[copyable_pass_by_value]",
-  "mojo.common.mojom.TimeDelta=base::TimeDelta[copyable_pass_by_value]",
-  "mojo.common.mojom.TimeTicks=base::TimeTicks[copyable_pass_by_value]",
-  "mojo.common.mojom.Version=base::Version",
-]
diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h
index 39a424f..c45ab90 100644
--- a/mojo/common/common_custom_types_struct_traits.h
+++ b/mojo/common/common_custom_types_struct_traits.h
@@ -9,8 +9,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/unguessable_token.h"
 #include "base/version.h"
-#include "mojo/common/common_custom_types.mojom-shared.h"
+#include "mojo/common/file.mojom-shared.h"
 #include "mojo/common/mojo_common_export.h"
+#include "mojo/common/string16.mojom-shared.h"
+#include "mojo/common/time.mojom-shared.h"
+#include "mojo/common/unguessable_token.mojom-shared.h"
+#include "mojo/common/version.mojom-shared.h"
 
 namespace mojo {
 
diff --git a/mojo/common/common_custom_types_unittest.cc b/mojo/common/common_custom_types_unittest.cc
index b288677..bf57d20 100644
--- a/mojo/common/common_custom_types_unittest.cc
+++ b/mojo/common/common_custom_types_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "mojo/common/common_custom_types.mojom.h"
 #include "mojo/common/test_common_custom_types.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/common/file.mojom b/mojo/common/file.mojom
new file mode 100644
index 0000000..fe224734
--- /dev/null
+++ b/mojo/common/file.mojom
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+// Corresponds to |base::File| in base/files/file.h
+struct File {
+  handle fd;
+};
diff --git a/mojo/common/file.typemap b/mojo/common/file.typemap
new file mode 100644
index 0000000..26d4941
--- /dev/null
+++ b/mojo/common/file.typemap
@@ -0,0 +1,13 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/file.mojom"
+public_headers = [ "//base/files/file.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+public_deps = [
+  "//mojo/common:struct_traits",
+]
+
+type_mappings =
+    [ "mojo.common.mojom.File=base::File[move_only,nullable_is_same_type]" ]
diff --git a/mojo/common/file_path.mojom b/mojo/common/file_path.mojom
new file mode 100644
index 0000000..10ebe05
--- /dev/null
+++ b/mojo/common/file_path.mojom
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+[Native]
+struct FilePath;
diff --git a/mojo/common/file_path.typemap b/mojo/common/file_path.typemap
new file mode 100644
index 0000000..66d8c54d
--- /dev/null
+++ b/mojo/common/file_path.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/file_path.mojom"
+public_headers = [ "//base/files/file_path.h" ]
+traits_headers = [ "//ipc/ipc_message_utils.h" ]
+public_deps = [
+  "//ipc",
+]
+
+type_mappings = [ "mojo.common.mojom.FilePath=base::FilePath" ]
diff --git a/mojo/common/string16.mojom b/mojo/common/string16.mojom
new file mode 100644
index 0000000..173c8670
--- /dev/null
+++ b/mojo/common/string16.mojom
@@ -0,0 +1,12 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+// Corresponds to |base::string16| in base/strings/string16.h
+// Corresponds to |WTF::String| in
+// third_party/WebKit/Source/wtf/text/WTFString.h.
+struct String16 {
+  array<uint16> data;
+};
diff --git a/mojo/common/string16.typemap b/mojo/common/string16.typemap
new file mode 100644
index 0000000..223de29
--- /dev/null
+++ b/mojo/common/string16.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/string16.mojom"
+public_headers = [ "//base/strings/string16.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+public_deps = [
+  "//mojo/common:struct_traits",
+]
+
+type_mappings = [ "mojo.common.mojom.String16=base::string16" ]
diff --git a/mojo/common/test_common_custom_types.mojom b/mojo/common/test_common_custom_types.mojom
index 146c8c6..aa4cbc3 100644
--- a/mojo/common/test_common_custom_types.mojom
+++ b/mojo/common/test_common_custom_types.mojom
@@ -4,7 +4,12 @@
 
 module mojo.common.test;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file.mojom";
+import "mojo/common/file_path.mojom";
+import "mojo/common/string16.mojom";
+import "mojo/common/time.mojom";
+import "mojo/common/unguessable_token.mojom";
+import "mojo/common/values.mojom";
 
 interface TestFilePath {
   BounceFilePath(mojo.common.mojom.FilePath in)
diff --git a/mojo/common/time.mojom b/mojo/common/time.mojom
new file mode 100644
index 0000000..43dfcc8
--- /dev/null
+++ b/mojo/common/time.mojom
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+[Native]
+struct Time;
+
+struct TimeDelta {
+  int64 microseconds;
+};
+
+[Native]
+struct TimeTicks;
diff --git a/mojo/common/time.typemap b/mojo/common/time.typemap
new file mode 100644
index 0000000..99e9e3a
--- /dev/null
+++ b/mojo/common/time.typemap
@@ -0,0 +1,20 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/time.mojom"
+public_headers = [ "//base/time/time.h" ]
+traits_headers = [
+  "//ipc/ipc_message_utils.h",
+  "//mojo/common/common_custom_types_struct_traits.h",
+]
+public_deps = [
+  "//ipc",
+  "//mojo/common:struct_traits",
+]
+
+type_mappings = [
+  "mojo.common.mojom.Time=base::Time[copyable_pass_by_value]",
+  "mojo.common.mojom.TimeDelta=base::TimeDelta[copyable_pass_by_value]",
+  "mojo.common.mojom.TimeTicks=base::TimeTicks[copyable_pass_by_value]",
+]
diff --git a/mojo/common/traits_test_service.mojom b/mojo/common/traits_test_service.mojom
index bf508cd..7659eea 100644
--- a/mojo/common/traits_test_service.mojom
+++ b/mojo/common/traits_test_service.mojom
@@ -4,7 +4,7 @@
 
 module mojo.common.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/version.mojom";
 
 // All functions on this interface echo their arguments to test StructTraits
 // serialization and deserialization.
diff --git a/mojo/common/typemaps.gni b/mojo/common/typemaps.gni
index 4c3809d..38c99961 100644
--- a/mojo/common/typemaps.gni
+++ b/mojo/common/typemaps.gni
@@ -2,4 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-typemaps = [ "//mojo/common/common_custom_types.typemap" ]
+typemaps = [
+  "//mojo/common/file.typemap",
+  "//mojo/common/file_path.typemap",
+  "//mojo/common/string16.typemap",
+  "//mojo/common/time.typemap",
+  "//mojo/common/unguessable_token.typemap",
+  "//mojo/common/values.typemap",
+  "//mojo/common/version.typemap",
+]
diff --git a/mojo/common/unguessable_token.mojom b/mojo/common/unguessable_token.mojom
new file mode 100644
index 0000000..3279717
--- /dev/null
+++ b/mojo/common/unguessable_token.mojom
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+// Corresponds to |base::UnguessableToken| in base/unguessable_token.h
+struct UnguessableToken {
+  uint64 high;
+  uint64 low;
+};
diff --git a/mojo/common/unguessable_token.typemap b/mojo/common/unguessable_token.typemap
new file mode 100644
index 0000000..ec7b194
--- /dev/null
+++ b/mojo/common/unguessable_token.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/unguessable_token.mojom"
+public_headers = [ "//base/unguessable_token.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+public_deps = [
+  "//mojo/common:struct_traits",
+]
+
+type_mappings = [ "mojo.common.mojom.UnguessableToken=base::UnguessableToken" ]
diff --git a/mojo/common/values.mojom b/mojo/common/values.mojom
new file mode 100644
index 0000000..822df34
--- /dev/null
+++ b/mojo/common/values.mojom
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+[Native]
+struct ListValue;
+
+[Native]
+struct DictionaryValue;
diff --git a/mojo/common/values.typemap b/mojo/common/values.typemap
new file mode 100644
index 0000000..6bda9fa
--- /dev/null
+++ b/mojo/common/values.typemap
@@ -0,0 +1,15 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/values.mojom"
+public_headers = [ "//base/values.h" ]
+traits_headers = [ "//ipc/ipc_message_utils.h" ]
+public_deps = [
+  "//ipc",
+]
+
+type_mappings = [
+  "mojo.common.mojom.DictionaryValue=base::DictionaryValue",
+  "mojo.common.mojom.ListValue=base::ListValue",
+]
diff --git a/mojo/common/version.mojom b/mojo/common/version.mojom
new file mode 100644
index 0000000..6ddf6e6
--- /dev/null
+++ b/mojo/common/version.mojom
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.common.mojom;
+
+// Corresponds to |base::Version| in base/version.h
+struct Version {
+  array<uint32> components;
+};
diff --git a/mojo/common/version.typemap b/mojo/common/version.typemap
new file mode 100644
index 0000000..fa7fed9a
--- /dev/null
+++ b/mojo/common/version.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/version.mojom"
+public_headers = [ "//base/version.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+public_deps = [
+  "//mojo/common:struct_traits",
+]
+
+type_mappings = [ "mojo.common.mojom.Version=base::Version" ]
diff --git a/mojo/edk/js/tests/binding_tests.js b/mojo/edk/js/tests/binding_tests.js
index 26031d0..3b90cf7 100644
--- a/mojo/edk/js/tests/binding_tests.js
+++ b/mojo/edk/js/tests/binding_tests.js
@@ -15,6 +15,8 @@
             gc) {
   testIsBound()
       .then(testReusable)
+      .then(testConnectionError)
+      .then(testUnbind)
       .then(function() {
     this.result = "PASS";
     gc.collectGarbage();  // should not crash
@@ -77,4 +79,43 @@
 
     return promise;
   }
+
+  function testConnectionError() {
+    var calc = new math.CalculatorPtr();
+    var calcBinding = new bindings.Binding(math.Calculator,
+                                           new CalculatorImpl(),
+                                           bindings.makeRequest(calc));
+
+    var promise = new Promise(function(resolve, reject) {
+      calcBinding.setConnectionErrorHandler(function() {
+        resolve();
+      });
+      calc.ptr.reset();
+    });
+
+    return promise;
+  }
+
+  function testUnbind() {
+    var calc = new math.CalculatorPtr();
+    var calcBinding = new bindings.Binding(math.Calculator,
+                                           new CalculatorImpl(),
+                                           bindings.makeRequest(calc));
+    var newCalcBinding = null;
+
+    var promise = calc.add(2).then(function(response) {
+      expect(response.value).toBe(2);
+      var interfaceRequest = calcBinding.unbind();
+      expect(calcBinding.isBound()).toBeFalsy();
+      newCalcBinding = new bindings.Binding(math.Calculator,
+                                            new CalculatorImpl(),
+                                            interfaceRequest);
+      return calc.add(2);
+    }).then(function(response) {
+      expect(response.value).toBe(2);
+      return Promise.resolve();
+    });
+
+    return promise;
+  }
 });
diff --git a/mojo/edk/js/tests/interface_ptr_tests.js b/mojo/edk/js/tests/interface_ptr_tests.js
index 44f5d1c6..d2bb178 100644
--- a/mojo/edk/js/tests/interface_ptr_tests.js
+++ b/mojo/edk/js/tests/interface_ptr_tests.js
@@ -16,6 +16,8 @@
   testIsBound()
       .then(testEndToEnd)
       .then(testReusable)
+      .then(testConnectionError)
+      .then(testPassInterface)
       .then(function() {
     this.result = "PASS";
     gc.collectGarbage();  // should not crash
@@ -99,4 +101,41 @@
 
     return promise;
   }
+
+  function testConnectionError() {
+    var calc = new math.CalculatorPtr();
+    var calcBinding = new bindings.Binding(math.Calculator,
+                                           new CalculatorImpl(),
+                                           bindings.makeRequest(calc));
+
+    var promise = new Promise(function(resolve, reject) {
+      calc.ptr.setConnectionErrorHandler(function() {
+        resolve();
+      });
+      calcBinding.close();
+    });
+
+    return promise;
+  }
+
+  function testPassInterface() {
+    var calc = new math.CalculatorPtr();
+    var newCalc = null;
+    var calcBinding = new bindings.Binding(math.Calculator,
+                                           new CalculatorImpl(),
+                                           bindings.makeRequest(calc));
+
+    var promise = calc.add(2).then(function(response) {
+      expect(response.value).toBe(2);
+      newCalc = new math.CalculatorPtr();
+      newCalc.ptr.bind(calc.ptr.passInterface());
+      expect(calc.ptr.isBound()).toBeFalsy();
+      return newCalc.add(2);
+    }).then(function(response) {
+      expect(response.value).toBe(4);
+      return Promise.resolve();
+    });
+
+    return promise;
+  }
 });
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index 6a7fb1c..8239b11 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -24,7 +24,6 @@
 namespace mojo {
 namespace edk {
 
-struct DataPipeControlMessage;
 class NodeController;
 
 // This is the Dispatcher implementation for the consumer handle for data
diff --git a/mojo/edk/system/data_pipe_control_message.h b/mojo/edk/system/data_pipe_control_message.h
index 82ee594..ec84ea3c 100644
--- a/mojo/edk/system/data_pipe_control_message.h
+++ b/mojo/edk/system/data_pipe_control_message.h
@@ -17,7 +17,6 @@
 namespace edk {
 
 class NodeController;
-class PortsMessage;
 
 enum DataPipeCommand : uint32_t {
   // Signal to the consumer that new data is available.
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index fddd0fd..6743222b 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -21,7 +21,6 @@
 namespace edk {
 
 class NodeController;
-class PortsMessage;
 
 class MessagePipeDispatcher : public Dispatcher {
  public:
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 1648dd2..6015595 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -21,7 +21,6 @@
 
 namespace edk {
 class NodeController;
-class PlatformSupport;
 
 class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher {
  public:
diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h
index 942a29b..dd9bd6a6 100644
--- a/mojo/edk/test/multiprocess_test_helper.h
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -19,7 +19,6 @@
 namespace mojo {
 
 namespace edk {
-class PlatformChannelPair;
 
 namespace test {
 
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index 8a6e2ed..0d589987 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -37,8 +37,6 @@
 
 namespace mojo {
 
-class AssociatedGroup;
-
 namespace internal {
 
 // MultiplexRouter supports routing messages for multiple interfaces over a
diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.h b/mojo/public/cpp/bindings/lib/native_struct_data.h
index e4f3aaf0..1c7cd81 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_data.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_data.h
@@ -14,7 +14,6 @@
 namespace mojo {
 namespace internal {
 
-class Buffer;
 class ValidationContext;
 
 class MOJO_CPP_BINDINGS_EXPORT NativeStruct_Data {
diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js
index 652a4b40..8d8565c 100644
--- a/mojo/public/js/bindings.js
+++ b/mojo/public/js/bindings.js
@@ -69,9 +69,25 @@
     this.connection = null;
   }
 
+  InterfacePtrController.prototype.setConnectionErrorHandler
+      = function(callback) {
+    if (!this.isBound())
+      throw new Error("Cannot set connection error handler if not bound.");
+    this.connection.router_.setErrorHandler(callback);
+  }
+
+  InterfacePtrController.prototype.passInterface = function() {
+    if (!this.isBound())
+      return new InterfacePtrInfo(null, 0);
+
+    var result = new InterfacePtrInfo(
+        this.connection.router_.connector_.handle_, this.version);
+    this.connection.router_.connector_.handle_ = null;
+    this.reset();
+    return result;
+  }
+
   // TODO(yzshen): Implement the following methods.
-  //   InterfacePtrController.prototype.setConnectionErrorHandler
-  //   InterfacePtrController.prototype.passInterface
   //   InterfacePtrController.prototype.queryVersion
   //   InterfacePtrController.prototype.requireVersion
 
@@ -119,9 +135,26 @@
     this.stub_ = null;
   }
 
-  // TODO(yzshen): Implement the following methods.
-  //   Binding.prototype.setConnectionErrorHandler
-  //   Binding.prototype.unbind
+  Binding.prototype.setConnectionErrorHandler
+      = function(callback) {
+    if (!this.isBound())
+      throw new Error("Cannot set connection error handler if not bound.");
+    connection.StubBindings(this.stub_).connection.router_.setErrorHandler(
+        callback);
+  }
+
+  Binding.prototype.unbind = function() {
+    if (!this.isBound())
+      return new InterfaceRequest(null);
+
+    var result = new InterfaceRequest(
+        connection.StubBindings(this.stub_).connection.router_.connector_
+            .handle_);
+    connection.StubBindings(this.stub_).connection.router_.connector_.handle_ =
+        null;
+    this.close();
+    return result;
+  }
 
   var exports = {};
   exports.InterfacePtrInfo = InterfacePtrInfo;
diff --git a/native_client_sdk/src/libraries/nacl_io/devfs/dev_fs.h b/native_client_sdk/src/libraries/nacl_io/devfs/dev_fs.h
index 872f77e..4effe1b 100644
--- a/native_client_sdk/src/libraries/nacl_io/devfs/dev_fs.h
+++ b/native_client_sdk/src/libraries/nacl_io/devfs/dev_fs.h
@@ -10,8 +10,6 @@
 
 namespace nacl_io {
 
-class Node;
-
 class DevFs : public Filesystem {
  public:
   virtual Error OpenWithMode(const Path& path, int open_flags, mode_t mode,
diff --git a/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_node.h b/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_node.h
index c1bd820..5787176 100644
--- a/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_node.h
+++ b/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_node.h
@@ -13,12 +13,6 @@
 
 namespace nacl_io {
 
-class MessagingInterface;
-class VarInterface;
-class VarArrayInterface;
-class VarArrayBufferInterface;
-class VarDictionaryInterface;
-
 /**
  * JSPipeNode represents a two-way channel for communicating with JavaScript
  * via calls to PostMessage.  In order to use these some amount of logic on
diff --git a/native_client_sdk/src/libraries/nacl_io/fs_factory.h b/native_client_sdk/src/libraries/nacl_io/fs_factory.h
index e90ebef..0efe614 100644
--- a/native_client_sdk/src/libraries/nacl_io/fs_factory.h
+++ b/native_client_sdk/src/libraries/nacl_io/fs_factory.h
@@ -13,8 +13,6 @@
 
 namespace nacl_io {
 
-class PepperInterface;
-
 class FsFactory {
  public:
   virtual ~FsFactory() {}
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
index 8a4e505..161b1762 100644
--- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
+++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
@@ -28,8 +28,6 @@
 }
 #endif
 
-struct fuse_operations;
-
 /*
  * The kernel intercept module provides a C->C++ thunk between the libc
  * kernel calls and the KernelProxy singleton.
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/tcp_event_emitter.h b/native_client_sdk/src/libraries/nacl_io/socket/tcp_event_emitter.h
index 707cb17..632b728 100644
--- a/native_client_sdk/src/libraries/nacl_io/socket/tcp_event_emitter.h
+++ b/native_client_sdk/src/libraries/nacl_io/socket/tcp_event_emitter.h
@@ -16,7 +16,6 @@
 namespace nacl_io {
 
 class TcpEventEmitter;
-class Node;
 
 typedef sdk_util::ScopedRef<TcpEventEmitter> ScopedTcpEventEmitter;
 
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h
index 29af1b5..4fb89ef2 100644
--- a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h
+++ b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h
@@ -15,7 +15,6 @@
 namespace nacl_io {
 
 class UnixEventEmitter;
-class Node;
 
 typedef sdk_util::ScopedRef<UnixEventEmitter> ScopedUnixEventEmitter;
 
diff --git a/native_client_sdk/src/libraries/nacl_io/stream/stream_fs.h b/native_client_sdk/src/libraries/nacl_io/stream/stream_fs.h
index d9f77c78..c4c40fe 100644
--- a/native_client_sdk/src/libraries/nacl_io/stream/stream_fs.h
+++ b/native_client_sdk/src/libraries/nacl_io/stream/stream_fs.h
@@ -17,7 +17,6 @@
 // a background thread for dispatching completion callbacks.
 
 class StreamFs;
-class StreamNode;
 
 class StreamFs : public Filesystem {
  public:
diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h
index 047d6c19..1c838901 100644
--- a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h
+++ b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_host_resolver_interface.h
@@ -15,7 +15,6 @@
 #include "sdk_util/macros.h"
 
 class FakePepperInterface;
-class FakeVarManager;
 
 class FakeHostResolverInterface : public nacl_io::HostResolverInterface {
  public:
diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.h b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.h
index ae2a8ae2..1ff29c1 100644
--- a/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.h
+++ b/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_net_address_interface.h
@@ -10,7 +10,6 @@
 #include "nacl_io/pepper_interface.h"
 #include "sdk_util/macros.h"
 
-class FakeVarManager;
 class FakePepperInterface;
 
 class FakeNetAddressInterface : public nacl_io::NetAddressInterface {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index ecb397ca..c7da483 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -86,11 +86,22 @@
   net_configs += [ "//build/config/linux:libresolv" ]
 }
 
+source_set("constants") {
+  sources = [
+    "base/trace_constants.cc",
+    "base/trace_constants.h",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
 component("net") {
   sources = gypi_values.net_nacl_common_sources
   net_unfiltered_sources = []
 
   deps = [
+    ":constants",
     ":net_resources",
     "//base",
     "//net/base/registry_controlled_domains",
@@ -687,6 +698,7 @@
     ]
 
     public_deps = [
+      ":constants",
       ":net",
     ]
     deps = [
@@ -1013,6 +1025,8 @@
     sources = [
       "tools/quic/platform/impl/quic_epoll_clock.cc",
       "tools/quic/platform/impl/quic_epoll_clock.h",
+      "tools/quic/platform/impl/quic_socket_utils.cc",
+      "tools/quic/platform/impl/quic_socket_utils.h",
       "tools/quic/quic_client.cc",
       "tools/quic/quic_client.h",
       "tools/quic/quic_default_packet_writer.cc",
@@ -1027,8 +1041,6 @@
       "tools/quic/quic_packet_writer_wrapper.h",
       "tools/quic/quic_server.cc",
       "tools/quic/quic_server.h",
-      "tools/quic/quic_socket_utils.cc",
-      "tools/quic/quic_socket_utils.h",
     ]
     deps = [
       ":epoll_server",
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 192b9e37..81164320 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -9,6 +9,7 @@
 #include "base/trace_event/trace_event.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/proxy/proxy_info.h"
 #include "net/url_request/url_request.h"
 
@@ -17,8 +18,7 @@
 int NetworkDelegate::NotifyBeforeURLRequest(
     URLRequest* request, const CompletionCallback& callback,
     GURL* new_url) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "NetworkDelegate::NotifyBeforeURLRequest");
+  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyBeforeURLRequest");
   DCHECK(CalledOnValidThread());
   DCHECK(request);
   DCHECK(!callback.is_null());
@@ -33,7 +33,7 @@
     URLRequest* request,
     const CompletionCallback& callback,
     HttpRequestHeaders* headers) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
+  TRACE_EVENT0(kNetTracingCategory,
                "NetworkDelegate::NotifyBeforeStartTransation");
   DCHECK(CalledOnValidThread());
   DCHECK(headers);
@@ -54,8 +54,7 @@
 void NetworkDelegate::NotifyStartTransaction(
     URLRequest* request,
     const HttpRequestHeaders& headers) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "NetworkDelegate::NotifyStartTransaction");
+  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyStartTransaction");
   DCHECK(CalledOnValidThread());
   OnStartTransaction(request, headers);
 }
@@ -66,8 +65,7 @@
     const HttpResponseHeaders* original_response_headers,
     scoped_refptr<HttpResponseHeaders>* override_response_headers,
     GURL* allowed_unsafe_redirect_url) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "NetworkDelegate::NotifyHeadersReceived");
+  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyHeadersReceived");
   DCHECK(CalledOnValidThread());
   DCHECK(original_response_headers);
   DCHECK(!callback.is_null());
@@ -88,7 +86,7 @@
 
 void NetworkDelegate::NotifyNetworkBytesReceived(URLRequest* request,
                                                  int64_t bytes_received) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
+  TRACE_EVENT0(kNetTracingCategory,
                "NetworkDelegate::NotifyNetworkBytesReceived");
   DCHECK(CalledOnValidThread());
   DCHECK_GT(bytes_received, 0);
@@ -112,8 +110,7 @@
 void NetworkDelegate::NotifyCompleted(URLRequest* request,
                                       bool started,
                                       int net_error) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "NetworkDelegate::NotifyCompleted");
+  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyCompleted");
   DCHECK(CalledOnValidThread());
   DCHECK(request);
   // TODO(cbentzel): Remove ScopedTracker below once crbug.com/475753 is fixed.
@@ -124,7 +121,7 @@
 }
 
 void NetworkDelegate::NotifyURLRequestDestroyed(URLRequest* request) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
+  TRACE_EVENT0(kNetTracingCategory,
                "NetworkDelegate::NotifyURLRequestDestroyed");
   DCHECK(CalledOnValidThread());
   DCHECK(request);
@@ -170,8 +167,7 @@
 bool NetworkDelegate::CanEnablePrivacyMode(
     const GURL& url,
     const GURL& first_party_for_cookies) const {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "NetworkDelegate::CanEnablePrivacyMode");
+  TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::CanEnablePrivacyMode");
   DCHECK(CalledOnValidThread());
   return OnCanEnablePrivacyMode(url, first_party_for_cookies);
 }
diff --git a/net/base/trace_constants.cc b/net/base/trace_constants.cc
new file mode 100644
index 0000000..ceade2c
--- /dev/null
+++ b/net/base/trace_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/trace_constants.h"
+
+#include "base/trace_event/common/trace_event_common.h"
+
+namespace net {
+
+const char kNetTracingCategory[] = TRACE_DISABLED_BY_DEFAULT("net");
+
+}  // namespace net
diff --git a/net/base/trace_constants.h b/net/base/trace_constants.h
new file mode 100644
index 0000000..dc5d84aa
--- /dev/null
+++ b/net/base/trace_constants.h
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_TRACE_CONSTANTS_H_
+#define NET_BASE_TRACE_CONSTANTS_H_
+
+namespace net {
+
+// Net Category used in Tracing.
+extern const char kNetTracingCategory[];
+
+}  // namespace net
+
+#endif  // NET_BASE_TRACE_CONSTANTS_H_
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 862f7c8..96ce38c 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -38,6 +38,7 @@
 #endif
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mac_util.h"
 #include "net/cert/test_keychain_search_list_mac.h"
 #endif
 
@@ -122,6 +123,16 @@
   return true;
 }
 
+bool WeakKeysAreInvalid() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // Starting with Mac OS 10.12, certs with weak keys are treated as
+  // (recoverable) invalid certificate errors.
+  return base::mac::IsAtLeastOS10_12();
+#else
+  return false;
+#endif
+}
+
 // Template helper to load a series of certificate files into a CertificateList.
 // Like CertTestUtil's CreateCertificateListFromFile, except it can load a
 // series of individual certificates (to make the tests clearer).
@@ -407,7 +418,7 @@
         EXPECT_NE(OK, error);
         EXPECT_EQ(CERT_STATUS_WEAK_KEY,
                   verify_result.cert_status & CERT_STATUS_WEAK_KEY);
-        EXPECT_NE(CERT_STATUS_INVALID,
+        EXPECT_EQ(WeakKeysAreInvalid() ? CERT_STATUS_INVALID : 0,
                   verify_result.cert_status & CERT_STATUS_INVALID);
       } else {
         EXPECT_THAT(error, IsOk());
@@ -1392,6 +1403,11 @@
 // The verifier should rollback until it just tries A(B) alone, at which point
 // it will pull B(F) & F(E) from the keychain and succeed.
 TEST_F(CertVerifyProcTest, MacCRLIntermediate) {
+  if (base::mac::IsAtLeastOS10_12()) {
+    // TODO(crbug.com/671889): Investigate SecTrustSetKeychains issue on Sierra.
+    LOG(INFO) << "Skipping test, SecTrustSetKeychains does not work on 10.12";
+    return;
+  }
   const char* const kPath2Files[] = {
       "multi-root-A-by-B.pem", "multi-root-B-by-C.pem", "multi-root-C-by-E.pem",
       "multi-root-E-by-E.pem"};
@@ -1748,12 +1764,7 @@
     {"weak_digest_md2_root.pem", "weak_digest_sha1_intermediate.pem",
      "weak_digest_sha1_ee.pem", EXPECT_SHA1 | EXPECT_SHA1_LEAF},
 };
-#if defined(OS_ANDROID)
-#define MAYBE_VerifyRoot DISABLED_VerifyRoot
-#else
-#define MAYBE_VerifyRoot VerifyRoot
-#endif
-INSTANTIATE_TEST_CASE_P(MAYBE_VerifyRoot,
+INSTANTIATE_TEST_CASE_P(VerifyRoot,
                         CertVerifyProcWeakDigestTest,
                         testing::ValuesIn(kVerifyRootCATestData));
 
@@ -1770,7 +1781,7 @@
      "weak_digest_sha1_ee.pem", EXPECT_MD2 | EXPECT_SHA1 | EXPECT_SHA1_LEAF},
 };
 // Disabled on NSS - MD4 is not supported, and MD2 and MD5 are disabled.
-#if defined(USE_NSS_CERTS) || defined(OS_IOS) || defined(OS_ANDROID)
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
 #define MAYBE_VerifyIntermediate DISABLED_VerifyIntermediate
 #else
 #define MAYBE_VerifyIntermediate VerifyIntermediate
@@ -1795,7 +1806,7 @@
 // Disabled on NSS - NSS caches chains/signatures in such a way that cannot
 // be cleared until NSS is cleanly shutdown, which is not presently supported
 // in Chromium.
-#if defined(USE_NSS_CERTS) || defined(OS_IOS) || defined(OS_ANDROID)
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
 #define MAYBE_VerifyEndEntity DISABLED_VerifyEndEntity
 #else
 #define MAYBE_VerifyEndEntity VerifyEndEntity
@@ -1818,7 +1829,7 @@
 };
 // Disabled on NSS - libpkix does not return constructed chains on error,
 // preventing us from detecting/inspecting the verified chain.
-#if defined(USE_NSS_CERTS) || defined(OS_IOS) || defined(OS_ANDROID)
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
 #define MAYBE_VerifyIncompleteIntermediate \
     DISABLED_VerifyIncompleteIntermediate
 #else
@@ -1843,7 +1854,7 @@
 };
 // Disabled on NSS - libpkix does not return constructed chains on error,
 // preventing us from detecting/inspecting the verified chain.
-#if defined(USE_NSS_CERTS) || defined(OS_IOS) || defined(OS_ANDROID)
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
 #define MAYBE_VerifyIncompleteEndEntity DISABLED_VerifyIncompleteEndEntity
 #else
 #define MAYBE_VerifyIncompleteEndEntity VerifyIncompleteEndEntity
@@ -1868,7 +1879,7 @@
 };
 // NSS does not support MD4 and does not enable MD2 by default, making all
 // permutations invalid.
-#if defined(USE_NSS_CERTS) || defined(OS_IOS) || defined(OS_ANDROID)
+#if defined(USE_NSS_CERTS) || defined(OS_IOS)
 #define MAYBE_VerifyMixed DISABLED_VerifyMixed
 #else
 #define MAYBE_VerifyMixed VerifyMixed
@@ -1947,6 +1958,9 @@
 // Test that CertVerifyProcMac reacts appropriately when Apple's certificate
 // verifier rejects a certificate with a fatal error. This is a regression
 // test for https://crbug.com/472291.
+// (Since 10.12, this causes a recoverable error instead of a fatal one.)
+// TODO(mattm): Try to find a different way to cause a fatal error that works
+// on 10.12.
 TEST_F(CertVerifyProcTest, LargeKey) {
   // Load root_ca_cert.pem into the test root store.
   ScopedTestRoot test_root(
@@ -1963,7 +1977,7 @@
   int error = Verify(cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_,
                      &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
-  EXPECT_EQ(CERT_STATUS_INVALID, verify_result.cert_status);
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
 
diff --git a/net/cert/crl_set_storage.cc b/net/cert/crl_set_storage.cc
index a4e3fd1..007cf3c1 100644
--- a/net/cert/crl_set_storage.cc
+++ b/net/cert/crl_set_storage.cc
@@ -14,6 +14,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "crypto/sha2.h"
+#include "net/base/trace_constants.h"
 #include "third_party/zlib/zlib.h"
 
 namespace net {
@@ -132,7 +133,7 @@
   if (header.get() == NULL)
     return NULL;
 
-  if (!header->IsType(base::Value::TYPE_DICTIONARY))
+  if (!header->IsType(base::Value::Type::DICTIONARY))
     return NULL;
   return static_cast<base::DictionaryValue*>(header.release());
 }
@@ -294,7 +295,7 @@
 // static
 bool CRLSetStorage::Parse(base::StringPiece data,
                           scoped_refptr<CRLSet>* out_crl_set) {
-  TRACE_EVENT0("net", "CRLSetStorage::Parse");
+  TRACE_EVENT0(kNetTracingCategory, "CRLSetStorage::Parse");
   // Other parts of Chrome assume that we're little endian, so we don't lose
   // anything by doing this.
 #if defined(__BYTE_ORDER)
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 061ecd9..8312d413 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -24,6 +24,7 @@
 #include "base/values.h"
 #include "net/base/hash_value.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/crl_set.h"
@@ -189,7 +190,7 @@
                             const CertificateList& additional_trust_anchors,
                             int* error,
                             CertVerifyResult* result) {
-  TRACE_EVENT0("net", "DoVerifyOnWorkerThread");
+  TRACE_EVENT0(kNetTracingCategory, "DoVerifyOnWorkerThread");
   *error = verify_proc->Verify(cert.get(), hostname, ocsp_response, flags,
                                crl_set.get(), additional_trust_anchors, result);
 
@@ -308,7 +309,7 @@
   }
 
   void OnJobCompleted(std::unique_ptr<ResultHelper> verify_result) {
-    TRACE_EVENT0("net", "CertVerifierJob::OnJobCompleted");
+    TRACE_EVENT0(kNetTracingCategory, "CertVerifierJob::OnJobCompleted");
     std::unique_ptr<CertVerifierJob> keep_alive =
         cert_verifier_->RemoveJob(this);
 
diff --git a/net/disk_cache/blockfile/in_flight_io.cc b/net/disk_cache/blockfile/in_flight_io.cc
index cc65384a..1c8157d 100644
--- a/net/disk_cache/blockfile/in_flight_io.cc
+++ b/net/disk_cache/blockfile/in_flight_io.cc
@@ -12,6 +12,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "net/base/trace_constants.h"
 
 namespace disk_cache {
 
@@ -26,7 +27,7 @@
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION("477117 BackgroundIO::OnIOSignalled"));
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"), "BackgroundIO::OnIOSignalled");
+  TRACE_EVENT0(net::kNetTracingCategory, "BackgroundIO::OnIOSignalled");
   if (controller_)
     controller_->InvokeCallback(this, false);
 }
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 881f514..1ff1755 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/dns/dns_util.h"
 
 namespace net {
@@ -162,7 +163,7 @@
                     const Entry& entry,
                     base::TimeTicks now,
                     base::TimeDelta ttl) {
-  TRACE_EVENT0("net", "HostCache::Set");
+  TRACE_EVENT0(kNetTracingCategory, "HostCache::Set");
   DCHECK(CalledOnValidThread());
   if (caching_is_disabled())
     return;
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index fceb67e..e4ee073 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -44,6 +44,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/base/url_util.h"
 #include "net/dns/address_sorter.h"
 #include "net/dns/dns_client.h"
@@ -770,7 +771,7 @@
                         const uint32_t attempt_number,
                         int error,
                         const int os_error) {
-    TRACE_EVENT0("net", "ProcTask::OnLookupComplete");
+    TRACE_EVENT0(kNetTracingCategory, "ProcTask::OnLookupComplete");
     DCHECK(network_task_runner_->BelongsToCurrentThread());
     // If results are empty, we should return an error.
     bool empty_list_on_ok = (error == OK && results.empty());
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 018a8929..dbe1154f 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -135,8 +135,8 @@
   char separator = epsv_line[start + 1];
 
   // Make sure we have "(<d><d><d>...", where <d> is not a number.
-  if (isdigit(separator) || epsv_line[start + 2] != separator ||
-      epsv_line[start + 3] != separator) {
+  if ((separator >= '0' && separator <= '9') ||
+      epsv_line[start + 2] != separator || epsv_line[start + 3] != separator) {
     return false;
   }
 
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index b8f1eb0..c8acd09 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -33,6 +33,7 @@
 #include "net/base/auth.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
+#include "net/base/trace_constants.h"
 #include "net/base/upload_data_stream.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/x509_certificate.h"
@@ -1265,7 +1266,8 @@
 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetchComplete(
     int result) {
   TRACE_EVENT0(
-      "net", "HttpCacheTransaction::DoCacheToggleUnusedSincePrefetchComplete");
+      kNetTracingCategory,
+      "HttpCacheTransaction::DoCacheToggleUnusedSincePrefetchComplete");
   // Restore the original value for this transaction.
   response_.unused_since_prefetch = !response_.unused_since_prefetch;
   next_state_ = STATE_CACHE_DISPATCH_VALIDATION;
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index f81f488..5d2a786 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -15,6 +15,9 @@
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "base/values.h"
 #include "net/base/network_throttle_manager_impl.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -377,6 +380,29 @@
   }
 }
 
+void HttpNetworkSession::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_absolute_name) const {
+  std::string name = base::StringPrintf("net/http_network_session_%p", this);
+  base::trace_event::MemoryAllocatorDump* http_network_session_dump =
+      pmd->GetAllocatorDump(name);
+  // If memory dump already exists, it means that this is not the first
+  // DumpMemoryStats() invocation on this object and it is reached by another
+  // "parent." If that's the case, add an ownership edge and return early.
+  // This is needed because URLRequestContexts can share an HttpNetworkSession.
+  if (http_network_session_dump != nullptr) {
+    pmd->AddOwnershipEdge(pmd->GetAllocatorDump(parent_absolute_name)->guid(),
+                          http_network_session_dump->guid());
+
+    return;
+  }
+  http_network_session_dump = pmd->CreateAllocatorDump(name);
+  normal_socket_pool_manager_->DumpMemoryStats(
+      pmd, http_network_session_dump->absolute_name());
+  pmd->AddOwnershipEdge(pmd->GetAllocatorDump(parent_absolute_name)->guid(),
+                        http_network_session_dump->guid());
+}
+
 ClientSocketPoolManager* HttpNetworkSession::GetSocketPoolManager(
     SocketPoolType pool_type) {
   switch (pool_type) {
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 5f01694..cc20ea1 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -33,6 +33,9 @@
 
 namespace base {
 class Value;
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace net {
@@ -275,6 +278,11 @@
                     SSLConfig* server_config,
                     SSLConfig* proxy_config) const;
 
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_absolute_name) const;
+
  private:
   friend class HttpNetworkSessionPeer;
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 4853e90..d691aaf6 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -4784,7 +4784,7 @@
     "proxy-authenticate", "Basic realm=\"MyRealm1\""
   };
   SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError(
-      "407 Proxy Authentication Required", kExtraAuthenticationHeaders,
+      "407", kExtraAuthenticationHeaders,
       arraysize(kExtraAuthenticationHeaders) / 2, 1));
   SpdySerializedFrame body_authentication(
       spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -7873,7 +7873,7 @@
     "http://login.example.com/",
   };
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
-      "302 Redirect", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
+      "302", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
   MockRead data_reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),  // EOF
   };
@@ -7969,7 +7969,7 @@
     "http://login.example.com/",
   };
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
-      "404 Not Found", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
+      "404", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(
       1, "The host does not exist", 23, true));
   MockRead data_reads[] = {
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index d798e8e..19b0a4f 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -649,7 +649,7 @@
   };
   const int responseHeadersSize = arraysize(responseHeaders) / 2;
   SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
-      "302 Found", responseHeaders, responseHeadersSize, 1));
+      "302", responseHeaders, responseHeadersSize, 1));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 2),
   };
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 3190b91..24ddae3a 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "net/base/port_util.h"
 #include "net/base/proxy_delegate.h"
+#include "net/base/trace_constants.h"
 #include "net/cert/cert_verifier.h"
 #include "net/http/bidirectional_stream_impl.h"
 #include "net/http/http_basic_stream.h"
@@ -500,14 +501,12 @@
 }
 
 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "HttpStreamFactoryImpl::Job::OnIOComplete");
+  TRACE_EVENT0(kNetTracingCategory, "HttpStreamFactoryImpl::Job::OnIOComplete");
   RunLoop(result);
 }
 
 int HttpStreamFactoryImpl::Job::RunLoop(int result) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "HttpStreamFactoryImpl::Job::RunLoop");
+  TRACE_EVENT0(kNetTracingCategory, "HttpStreamFactoryImpl::Job::RunLoop");
   result = DoLoop(result);
 
   if (result == ERR_IO_PENDING)
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 9ea8df4..6e8bb31a 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -1848,11 +1848,15 @@
   SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
   size_t spdy_headers_frame_length;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(client_packet_maker().MakeSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize, true,
+      &header_stream_offset));
   mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket(
-      1, test::kClientDataStreamId1, /*should_include_version=*/true,
+      2, test::kClientDataStreamId1, /*should_include_version=*/true,
       /*fin=*/true, priority,
       client_packet_maker().GetRequestHeaders("GET", "https", "/"),
-      &spdy_headers_frame_length));
+      &spdy_headers_frame_length, &header_stream_offset));
   size_t spdy_response_headers_frame_length;
   mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket(
       1, test::kClientDataStreamId1, /*should_include_version=*/false,
@@ -1971,11 +1975,15 @@
   SpdyPriority priority =
       ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
   size_t spdy_headers_frame_length;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(client_packet_maker().MakeSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize, true,
+      &header_stream_offset));
   mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket(
-      1, test::kClientDataStreamId1, /*should_include_version=*/true,
+      2, test::kClientDataStreamId1, /*should_include_version=*/true,
       /*fin=*/true, priority,
       client_packet_maker().GetRequestHeaders("GET", "https", "/"),
-      &spdy_headers_frame_length));
+      &spdy_headers_frame_length, &header_stream_offset));
   size_t spdy_response_headers_frame_length;
   mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket(
       1, test::kClientDataStreamId1, /*should_include_version=*/false,
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 24066ef..26552e3 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -499,7 +499,8 @@
 }
 
 bool HttpUtil::IsLWS(char c) {
-  return strchr(HTTP_LWS, c) != NULL;
+  const base::StringPiece kWhiteSpaceCharacters(HTTP_LWS);
+  return kWhiteSpaceCharacters.find(c) != base::StringPiece::npos;
 }
 
 // static
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index 6af616c..ee38dc6 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -379,6 +379,7 @@
   }
 }
 TEST(HttpUtilTest, AssembleRawHeaders) {
+  // clang-format off
   struct {
     const char* const input;  // with '|' representing '\0'
     const char* const expected_result;  // with '\0' changed to '|'
@@ -680,7 +681,19 @@
       "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
       "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
     },
+
+    // The embedded NUL at the start of the line (before "Blah:") should not be
+    // interpreted as LWS (as that would mistake it for a header line
+    // continuation).
+    {
+      "HTTP/1.0 200 OK\n"
+      "Foo: 1\n"
+      "|Blah: 3\n"
+      "Bar: 2\n\n",
+      "HTTP/1.0 200 OK|Foo: 1|Blah: 3|Bar: 2||"
+    },
   };
+  // clang-format on
   for (size_t i = 0; i < arraysize(tests); ++i) {
     std::string input = tests[i].input;
     std::replace(input.begin(), input.end(), '|', '\0');
@@ -1473,4 +1486,17 @@
   EXPECT_FALSE(HttpUtil::IsToken("\xff"));
 }
 
+TEST(HttpUtilTest, IsLWS) {
+  EXPECT_FALSE(HttpUtil::IsLWS('\v'));
+  EXPECT_FALSE(HttpUtil::IsLWS('\0'));
+  EXPECT_FALSE(HttpUtil::IsLWS('1'));
+  EXPECT_FALSE(HttpUtil::IsLWS('a'));
+  EXPECT_FALSE(HttpUtil::IsLWS('.'));
+  EXPECT_FALSE(HttpUtil::IsLWS('\n'));
+  EXPECT_FALSE(HttpUtil::IsLWS('\r'));
+
+  EXPECT_TRUE(HttpUtil::IsLWS('\t'));
+  EXPECT_TRUE(HttpUtil::IsLWS(' '));
+}
+
 }  // namespace net
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index fcd2a16..061a0ae6 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -199,7 +199,7 @@
     const HashValueVector& known_pins) {
   std::unique_ptr<base::Value> value(base::JSONReader::Read(report));
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
 
   base::DictionaryValue* report_dict;
   ASSERT_TRUE(value->GetAsDictionary(&report_dict));
@@ -264,7 +264,7 @@
                                        const std::string& cert_status) {
   std::unique_ptr<base::Value> value(base::JSONReader::Read(report));
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
 
   base::DictionaryValue* report_dict;
   ASSERT_TRUE(value->GetAsDictionary(&report_dict));
diff --git a/net/http2/decoder/decode_buffer.cc b/net/http2/decoder/decode_buffer.cc
new file mode 100644
index 0000000..15235bae
--- /dev/null
+++ b/net/http2/decoder/decode_buffer.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_buffer.h"
+
+namespace net {
+
+bool DecodeBuffer::SlowDecodeUnsignedInt(uint32_t field_size,
+                                         uint32_t field_offset,
+                                         uint32_t* decode_offset,
+                                         uint32_t* value) {
+  DCHECK_LT(0u, field_size);
+  DCHECK_LE(field_size, 4u);
+  DCHECK(decode_offset != nullptr);
+  DCHECK_LE(field_offset, *decode_offset);
+  const uint32_t next_field_offset = field_offset + field_size;
+  if (*decode_offset == field_offset) {
+    // Starting to decode field. It is possible we will reach this point
+    // twice, once when we've just exhausted the input, and once when
+    // resuming decoding with a new input buffer.
+    // Clear the field; we do NOT assume that the caller has done so
+    // previously.
+    *value = 0;
+  } else if (*decode_offset >= next_field_offset) {
+    // We already decoded this field.
+    return true;
+  }
+  do {
+    if (Empty()) {
+      return false;  // Not done decoding.
+    }
+    *value = *value << 8 | DecodeUInt8();
+    (*decode_offset)++;
+  } while (*decode_offset < next_field_offset);
+  return true;
+}
+
+bool DecodeBuffer::SlowDecodeUInt8(uint32_t field_offset,
+                                   uint32_t* decode_offset,
+                                   uint8_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(1 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt16(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint16_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(2 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xffff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt24(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(3 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0xffffff;
+  DCHECK_EQ(tmp, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt31(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  uint32_t tmp = *value;
+  const bool done = SlowDecodeUnsignedInt(4 /* field_size */, field_offset,
+                                          decode_offset, &tmp);
+  *value = tmp & 0x7fffffff;
+  DCHECK_EQ(tmp & 0x7fffffff, *value);
+  return done;
+}
+
+bool DecodeBuffer::SlowDecodeUInt32(uint32_t field_offset,
+                                    uint32_t* decode_offset,
+                                    uint32_t* value) {
+  return SlowDecodeUnsignedInt(4 /* field_size */, field_offset, decode_offset,
+                               value);
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_buffer.h b/net/http2/decoder/decode_buffer.h
new file mode 100644
index 0000000..828e4bf5
--- /dev/null
+++ b/net/http2/decoder/decode_buffer.h
@@ -0,0 +1,289 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_BUFFER_H_
+#define NET_HTTP2_DECODER_DECODE_BUFFER_H_
+
+// DecodeBuffer provides primitives for decoding various integer types found
+// in HTTP/2 frames.
+// DecodeBuffer wraps a byte array from which we can read and decode serialized
+// HTTP/2 frames, or parts thereof. DecodeBuffer is intended only for stack
+// allocation, where the caller is typically going to use the DecodeBuffer
+// instance as part of decoding the entire buffer before returning to its own
+// caller. Only the concrete Slow* methods are defined in the cc file,
+// all other methods are defined in this header file to enable inlining.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+class DecodeBufferSubset;
+
+class NET_EXPORT_PRIVATE DecodeBuffer {
+ public:
+  DecodeBuffer(const char* buffer, size_t len)
+      : buffer_(buffer), cursor_(buffer), beyond_(buffer + len) {
+    DCHECK(buffer != nullptr);
+    DCHECK_LE(len, MaxDecodeBufferLength());
+  }
+  explicit DecodeBuffer(base::StringPiece s)
+      : DecodeBuffer(s.data(), s.size()) {}
+  // Constructor for character arrays, typically in tests. For example:
+  //    const char input[] = { 0x11 };
+  //    DecodeBuffer b(input);
+  template <size_t N>
+  explicit DecodeBuffer(const char (&buf)[N]) : DecodeBuffer(buf, N) {}
+
+  bool Empty() const { return cursor_ >= beyond_; }
+  bool HasData() const { return cursor_ < beyond_; }
+  size_t Remaining() const {
+    DCHECK_LE(cursor_, beyond_);
+    return beyond_ - cursor_;
+  }
+  size_t Offset() const { return cursor_ - buffer_; }
+  size_t FullSize() const { return beyond_ - buffer_; }
+
+  // Returns the minimum of the number of bytes remaining in this DecodeBuffer
+  // and |length|, in support of determining how much of some structure/payload
+  // is in this DecodeBuffer.
+  size_t MinLengthRemaining(size_t length) const {
+    return std::min(length, Remaining());
+  }
+
+  // For string decoding, returns a pointer to the next byte/char to be decoded.
+  const char* cursor() const { return cursor_; }
+  // Advances the cursor (pointer to the next byte/char to be decoded).
+  void AdvanceCursor(size_t amount) {
+    DCHECK_LE(amount, Remaining());  // Need at least that much remaining.
+    DCHECK_EQ(subset_, nullptr) << "Access via subset only when present.";
+    cursor_ += amount;
+  }
+
+  // Only call methods starting "Decode" when there is enough input remaining.
+  char DecodeChar() {
+    DCHECK_LE(1u, Remaining());  // Need at least one byte remaining.
+    DCHECK_EQ(subset_, nullptr) << "Access via subset only when present.";
+    return *cursor_++;
+  }
+
+  uint8_t DecodeUInt8() { return static_cast<uint8_t>(DecodeChar()); }
+
+  uint16_t DecodeUInt16() {
+    DCHECK_LE(2u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    // Note that chars are automatically promoted to ints during arithmetic,
+    // so the b1 << 8 doesn't end up as zero before being or-ed with b2.
+    // And the left-shift operator has higher precedence than the or operator.
+    return b1 << 8 | b2;
+  }
+
+  uint32_t DecodeUInt24() {
+    DCHECK_LE(3u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    return b1 << 16 | b2 << 8 | b3;
+  }
+
+  // For 31-bit unsigned integers, where the 32nd bit is reserved for future
+  // use (i.e. the high-bit of the first byte of the encoding); examples:
+  // the Stream Id in a frame header or the Window Size Increment in a
+  // WINDOW_UPDATE frame.
+  uint32_t DecodeUInt31() {
+    DCHECK_LE(4u, Remaining());
+    const uint8_t b1 = DecodeUInt8() & 0x7f;  // Mask out the high order bit.
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    const uint8_t b4 = DecodeUInt8();
+    return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+  }
+
+  uint32_t DecodeUInt32() {
+    DCHECK_LE(4u, Remaining());
+    const uint8_t b1 = DecodeUInt8();
+    const uint8_t b2 = DecodeUInt8();
+    const uint8_t b3 = DecodeUInt8();
+    const uint8_t b4 = DecodeUInt8();
+    return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+  }
+
+  // SlowDecode* routines are used for decoding a multi-field structure when
+  // there may not be enough bytes in the buffer to decode the entirety of the
+  // structure.
+
+  // Read as much of an unsigned int field of an encoded structure as possible,
+  // keeping track via decode_offset of our position in the encoded structure.
+  // Returns true if the field has been fully decoded.
+  // |field_size| is the number of bytes of the encoding of the field (usually
+  // a compile time fixed value).
+  // |field_offset| is the offset of the first byte of the encoding of the field
+  // within the encoding of that structure (usually a compile time fixed value).
+  // |*decode_offset| is the offset of the byte to be decoded next.
+  // |*value| is the storage for the decoded value, and is used for storing
+  // partially decoded values; if some, but not all, bytes of the encoding are
+  // available then this method will return having stored the decoded bytes into
+  // *value.
+  bool SlowDecodeUnsignedInt(uint32_t field_size,
+                             uint32_t field_offset,
+                             uint32_t* decode_offset,
+                             uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 8-bit unsigned integers.
+  // Obviously a byte can't be split (on our byte addressable machines), but
+  // a larger structure containing such a field might be.
+  bool SlowDecodeUInt8(uint32_t field_offset,
+                       uint32_t* decode_offset,
+                       uint8_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 16-bit unsigned integers.
+  bool SlowDecodeUInt16(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint16_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 24-bit unsigned integers.
+  bool SlowDecodeUInt24(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 31-bit unsigned integers.
+  // (same definition as for DecodeUInt31).
+  bool SlowDecodeUInt31(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Like SlowDecodeUnsignedInt, but specifically for 31-bit unsigned integers.
+  bool SlowDecodeUInt32(uint32_t field_offset,
+                        uint32_t* decode_offset,
+                        uint32_t* value);
+
+  // Decodes an enum value, where the size (in bytes) of the encoding must be
+  // stated explicitly. It is assumed that under the covers enums are really
+  // just integers, and that we can static_cast them to and from uint32.
+  template <typename E>
+  bool SlowDecodeEnum(uint32_t field_size,
+                      uint32_t field_offset,
+                      uint32_t* decode_offset,
+                      E* value) {
+    uint32_t tmp = static_cast<uint32_t>(*value);
+    const bool done =
+        SlowDecodeUnsignedInt(field_size, field_offset, decode_offset, &tmp);
+    *value = static_cast<E>(tmp);
+    DCHECK_EQ(tmp, static_cast<uint32_t>(*value));
+    return done;
+  }
+
+  // We assume the decode buffers will typically be modest in size (i.e. a few
+  // K).
+  // Let's make sure during testing that we don't go very high, with 32MB
+  // selected rather arbitrarily.
+  static constexpr size_t MaxDecodeBufferLength() { return 1 << 25; }
+
+ protected:
+#ifndef NDEBUG
+  // These are part of validating during tests that there is at most one
+  // DecodeBufferSubset instance at a time for any DecodeBuffer instance.
+  void set_subset_of_base(DecodeBuffer* base,
+                          const DecodeBufferSubset* subset) {
+    DCHECK_EQ(this, subset);
+    base->set_subset(subset);
+  }
+  void clear_subset_of_base(DecodeBuffer* base,
+                            const DecodeBufferSubset* subset) {
+    DCHECK_EQ(this, subset);
+    base->clear_subset(subset);
+  }
+#endif
+
+ private:
+#ifndef NDEBUG
+  void set_subset(const DecodeBufferSubset* subset) {
+    DCHECK(subset != nullptr);
+    DCHECK_EQ(subset_, nullptr) << "There is already a subset";
+    subset_ = subset;
+  }
+  void clear_subset(const DecodeBufferSubset* subset) {
+    DCHECK(subset != nullptr);
+    DCHECK_EQ(subset_, subset);
+    subset_ = nullptr;
+  }
+#endif
+
+  // Prevent heap allocation of DecodeBuffer.
+  static void* operator new(size_t s);
+  static void* operator new[](size_t s);
+  static void operator delete(void* p);
+  static void operator delete[](void* p);
+
+  const char* const buffer_;
+  const char* cursor_;
+  const char* const beyond_;
+  const DecodeBufferSubset* subset_ = nullptr;  // Used for DCHECKs.
+
+  DISALLOW_COPY_AND_ASSIGN(DecodeBuffer);
+};
+
+// DecodeBufferSubset is used when decoding a known sized chunk of data, which
+// starts at base->cursor(), and continues for subset_len, which may be
+// entirely in |base|, or may extend beyond it (hence the MinLengthRemaining
+// in the constructor).
+// There are two benefits to using DecodeBufferSubset: it ensures that the
+// cursor of |base| is advanced when the subset's destructor runs, and it
+// ensures that the consumer of the subset can't go beyond the subset which
+// it is intended to decode.
+// There must be only a single DecodeBufferSubset at a time for a base
+// DecodeBuffer, though they can be nested (i.e. a DecodeBufferSubset's
+// base may itself be a DecodeBufferSubset). This avoids the AdvanceCursor
+// being called erroneously.
+class DecodeBufferSubset : public DecodeBuffer {
+ public:
+  DecodeBufferSubset(DecodeBuffer* base, size_t subset_len)
+      : DecodeBuffer(base->cursor(), base->MinLengthRemaining(subset_len)),
+#ifndef NDEBUG
+        start_base_offset_(base->Offset()),
+        max_base_offset_(start_base_offset_ + FullSize()),
+#endif
+        base_buffer_(base) {
+#ifndef NDEBUG
+    DCHECK_LE(max_base_offset_, base->FullSize());
+    set_subset_of_base(base_buffer_, this);
+#endif
+  }
+
+  ~DecodeBufferSubset() {
+    size_t offset = Offset();
+#ifndef NDEBUG
+    clear_subset_of_base(base_buffer_, this);
+    DCHECK_LE(Offset(), FullSize());
+    DCHECK_EQ(start_base_offset_, base_buffer_->Offset())
+        << "The base buffer was modified";
+    DCHECK_LE(offset, FullSize());
+    DCHECK_LE(start_base_offset_ + offset, base_buffer_->FullSize());
+#endif
+    base_buffer_->AdvanceCursor(offset);
+#ifndef NDEBUG
+    DCHECK_GE(max_base_offset_, base_buffer_->Offset());
+#endif
+  }
+
+ private:
+#ifndef NDEBUG
+  const size_t start_base_offset_;  // Used for DCHECKs.
+  const size_t max_base_offset_;    // Used for DCHECKs.
+#endif
+  DecodeBuffer* const base_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecodeBufferSubset);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_BUFFER_H_
diff --git a/net/http2/decoder/decode_buffer_test.cc b/net/http2/decoder/decode_buffer_test.cc
new file mode 100644
index 0000000..35a9fbb
--- /dev/null
+++ b/net/http2/decoder/decode_buffer_test.cc
@@ -0,0 +1,406 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_buffer.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+enum class TestEnumClass32 {
+  kValue1 = 1,
+  kValue99 = 99,
+  kValue1M = 1000000,
+};
+
+enum class TestEnumClass8 {
+  kValue1 = 1,
+  kValue2 = 1,
+  kValue99 = 99,
+  kValue255 = 255,
+};
+
+enum TestEnum8 {
+  kMaskLo = 0x01,
+  kMaskHi = 0x80,
+};
+
+struct TestStruct {
+  uint8_t f1;
+  uint16_t f2;
+  uint32_t f3;  // Decoded as a uint24
+  uint32_t f4;
+  uint32_t f5;  // Decoded as if uint31
+  TestEnumClass32 f6;
+  TestEnumClass8 f7;
+  TestEnum8 f8;
+};
+
+const size_t kF1Offset = 0;
+const size_t kF2Offset = 1;
+const size_t kF3Offset = 3;
+const size_t kF4Offset = 6;
+const size_t kF5Offset = 10;
+const size_t kF6Offset = 14;
+const size_t kF7Offset = 18;
+const size_t kF8Offset = 19;
+
+class DecodeBufferTest : public ::testing::Test {
+ public:
+  DecodeBufferTest() {}
+
+ protected:
+  // Double checks the call fn(f).
+  template <typename T>
+  bool SlowDecodeField(DecodeBuffer* b,
+                       size_t field_size,
+                       size_t field_offset,
+                       const base::Callback<bool(DecodeBuffer*)>& fn,
+                       T* f) {
+    VLOG(2) << "Remaining: " << b->Remaining();
+    VLOG(2) << "field_size: " << field_size;
+    VLOG(2) << "field_offset: " << field_offset;
+    VLOG(2) << "decode_offset_: " << decode_offset_;
+    EXPECT_GE(decode_offset_, field_offset);
+    bool had_data = b->HasData();
+    VLOG(2) << "had_data: " << had_data;
+    uint32_t old = static_cast<uint32_t>(*f);
+    VLOG(2) << "old: " << old;
+    size_t old_decode_offset = decode_offset_;
+    bool done = fn.Run(b);
+    VLOG(2) << "done: " << done;
+    if (old_decode_offset == decode_offset_) {
+      // Didn't do any decoding (may have no input, or may have already
+      // decoded this field).
+      if (done) {
+        EXPECT_LE(field_offset + field_size, decode_offset_);
+        // Shouldn't have modified already decoded field.
+        EXPECT_EQ(old, static_cast<uint32_t>(*f));
+      } else {
+        EXPECT_TRUE(!had_data);
+      }
+    } else {
+      // Did some decoding.
+      EXPECT_TRUE(had_data);
+      EXPECT_LT(old_decode_offset, decode_offset_);
+      if (done) {
+        EXPECT_EQ(field_offset + field_size, decode_offset_);
+      } else {
+        EXPECT_GT(field_offset + field_size, decode_offset_);
+      }
+    }
+    VLOG(2) << "---------------------------------------";
+    return done;
+  }
+
+  bool decode_f1(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt8(kF1Offset, &decode_offset_, &p->f1);
+  }
+  bool decode_f2(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt16(kF2Offset, &decode_offset_, &p->f2);
+  }
+  bool decode_f3(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt24(kF3Offset, &decode_offset_, &p->f3);
+  }
+  bool decode_f4(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt32(kF4Offset, &decode_offset_, &p->f4);
+  }
+  bool decode_f5(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeUInt31(kF5Offset, &decode_offset_, &p->f5);
+  }
+  bool decode_f6(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(4, kF6Offset, &decode_offset_, &p->f6);
+  }
+  bool decode_f7(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(1, kF7Offset, &decode_offset_, &p->f7);
+  }
+  bool decode_f8(TestStruct* p, DecodeBuffer* db) {
+    return db->SlowDecodeEnum(1, kF8Offset, &decode_offset_, &p->f8);
+  }
+
+  void SlowDecodeTestStruct(StringPiece input, TestStruct* p) {
+    VLOG(2) << "############################################################";
+    EXPECT_LE(10u, input.size());
+    decode_offset_ = 0;
+    while (input.size() > 0) {
+      size_t size = input.size();
+      // Sometimes check that zero length input is OK.
+      auto r = random_.Next();
+      if (r % 100 == 0) {
+        size = 0;
+      } else if (size > 1) {
+        auto r = random_.Next();
+        size = (r % size) + 1;
+      }
+      VLOG(2) << "================= input size " << size;
+      DecodeBuffer b(input.data(), size);
+      size_t old_decode_offset = decode_offset_;
+      if (SlowDecodeField(&b, 1, kF1Offset,
+                          base::Bind(&DecodeBufferTest::decode_f1,
+                                     base::Unretained(this), p),
+                          &p->f1) &&
+          SlowDecodeField(&b, 2, kF2Offset,
+                          base::Bind(&DecodeBufferTest::decode_f2,
+                                     base::Unretained(this), p),
+                          &p->f2) &&
+          SlowDecodeField(&b, 3, kF3Offset,
+                          base::Bind(&DecodeBufferTest::decode_f3,
+                                     base::Unretained(this), p),
+                          &p->f3) &&
+          SlowDecodeField(&b, 4, kF4Offset,
+                          base::Bind(&DecodeBufferTest::decode_f4,
+                                     base::Unretained(this), p),
+                          &p->f4) &&
+          SlowDecodeField(&b, 4, kF5Offset,
+                          base::Bind(&DecodeBufferTest::decode_f5,
+                                     base::Unretained(this), p),
+                          &p->f5) &&
+          SlowDecodeField(&b, 4, kF6Offset,
+                          base::Bind(&DecodeBufferTest::decode_f6,
+                                     base::Unretained(this), p),
+                          &p->f6) &&
+          SlowDecodeField(&b, 1, kF7Offset,
+                          base::Bind(&DecodeBufferTest::decode_f7,
+                                     base::Unretained(this), p),
+                          &p->f7) &&
+          SlowDecodeField(&b, 1, kF8Offset,
+                          base::Bind(&DecodeBufferTest::decode_f8,
+                                     base::Unretained(this), p),
+                          &p->f8)) {
+        EXPECT_TRUE(b.Empty());
+        EXPECT_EQ(size, input.size());
+        EXPECT_EQ(input.size(), b.Offset());  // All input consumed.
+        return;
+      }
+      EXPECT_EQ(old_decode_offset + size, decode_offset_);
+      EXPECT_TRUE(b.Empty());
+      EXPECT_EQ(size, b.Offset());    // All input consumed.
+      EXPECT_LT(size, input.size());  // More remains.
+      input = StringPiece(input.data() + size, input.size() - size);
+    }
+    ADD_FAILURE() << "Ran out of input! decode_offset_ = " << decode_offset_;
+  }
+
+  Http2Random random_;
+  uint32_t decode_offset_;
+};
+
+TEST_F(DecodeBufferTest, DecodesFixedInts) {
+  const char data[] = "\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a";
+  DecodeBuffer b1(data, strlen(data));
+  EXPECT_EQ(1, b1.DecodeUInt8());
+  EXPECT_EQ(0x1223u, b1.DecodeUInt16());
+  EXPECT_EQ(0x344556u, b1.DecodeUInt24());
+  EXPECT_EQ(0x6778899Au, b1.DecodeUInt32());
+
+  DecodeBuffer b2(data, strlen(data));
+  uint8_t b;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt8(0, &decode_offset_, &b));
+  EXPECT_EQ(1, b);
+  uint16_t s;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt16(0, &decode_offset_, &s));
+  EXPECT_EQ(0x1223, s);
+  uint32_t i;
+  decode_offset_ = 0;
+  EXPECT_TRUE(b2.SlowDecodeUInt24(0, &decode_offset_, &i));
+  //  EXPECT_EQ(0x344556, b1.DecodeUInt24());
+  //  EXPECT_EQ(0x6778899a, b1.DecodeUInt32());
+}
+
+// Decode the structure many times, where we'll pass different partitions
+// into DecodeSlowly.
+TEST_F(DecodeBufferTest, SlowDecodeTestStruct) {
+  // clang-format off
+  const char data[] = {
+    0x12u,                       // f1
+    0x23u, 0x34u,                // f2
+    0x45u, 0x56u, 0x67u,         // f3
+    0x78u, 0x89u, 0x9au, 0xabu,  // f4
+    0xfeu, 0xedu, 0xdcu, 0xcbu,  // f5 (high-bit will be cleared.)
+    0x00u, 0x0fu, 0x42u, 0x40u,  // f6 (kValue1M)
+    0x63u,                       // f7 (kValue99)
+    0x81u,                       // f8 (kMaskLo | kMaskHi)
+  };
+  // clang-format on
+  StringPiece input(data, sizeof data);
+  for (int i = 0; i < 200; ++i) {
+    TestStruct ts;
+    // Init the struct to random garbage.
+    ts.f1 = random_.Rand8();
+    ts.f2 = random_.Rand16();
+    ts.f3 = random_.Rand32();
+    ts.f4 = random_.Rand32();
+    // Ensure high-bit is set.
+    ts.f5 = 0x80000000 | random_.Rand32();  // Ensure high-bit is set.
+    ts.f6 = static_cast<TestEnumClass32>(random_.Rand32());
+    ts.f7 = static_cast<TestEnumClass8>(random_.Rand8());
+    ts.f8 = static_cast<TestEnum8>(random_.Rand8());
+    SlowDecodeTestStruct(input, &ts);
+    ASSERT_EQ(0x12u, ts.f1);
+    ASSERT_EQ(0x2334u, ts.f2);
+    ASSERT_EQ(0x455667u, ts.f3);
+    ASSERT_EQ(0x78899AABu, ts.f4);
+    ASSERT_EQ(0x7EEDDCCBu, ts.f5);
+    ASSERT_EQ(TestEnumClass32::kValue1M, ts.f6);
+    ASSERT_EQ(TestEnumClass8::kValue99, ts.f7);
+    ASSERT_EQ(kMaskLo | kMaskHi, ts.f8);
+  }
+}
+
+// Make sure that DecodeBuffer is not copying input, just pointing into
+// provided input buffer.
+TEST_F(DecodeBufferTest, HasNotCopiedInput) {
+  const char data[] = "ab";
+  DecodeBuffer b1(data, 2);
+
+  EXPECT_EQ(2u, b1.Remaining());
+  EXPECT_EQ(0u, b1.Offset());
+  EXPECT_FALSE(b1.Empty());
+  EXPECT_EQ(data, b1.cursor());  // cursor points to input buffer
+  EXPECT_TRUE(b1.HasData());
+
+  b1.AdvanceCursor(1);
+
+  EXPECT_EQ(1u, b1.Remaining());
+  EXPECT_EQ(1u, b1.Offset());
+  EXPECT_FALSE(b1.Empty());
+  EXPECT_EQ(&data[1], b1.cursor());
+  EXPECT_TRUE(b1.HasData());
+
+  b1.AdvanceCursor(1);
+
+  EXPECT_EQ(0u, b1.Remaining());
+  EXPECT_EQ(2u, b1.Offset());
+  EXPECT_TRUE(b1.Empty());
+  EXPECT_EQ(&data[2], b1.cursor());
+  EXPECT_FALSE(b1.HasData());
+
+  DecodeBuffer b2(data, 0);
+
+  EXPECT_EQ(0u, b2.Remaining());
+  EXPECT_EQ(0u, b2.Offset());
+  EXPECT_TRUE(b2.Empty());
+  EXPECT_EQ(data, b2.cursor());
+  EXPECT_FALSE(b2.HasData());
+}
+
+// DecodeBufferSubset can't go beyond the end of the base buffer.
+TEST_F(DecodeBufferTest, DecodeBufferSubsetLimited) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  base.AdvanceCursor(1);
+  DecodeBufferSubset subset(&base, 100);
+  EXPECT_EQ(2u, subset.FullSize());
+}
+
+// DecodeBufferSubset advances the cursor of its base upon destruction.
+TEST_F(DecodeBufferTest, DecodeBufferSubsetAdvancesCursor) {
+  const char data[] = "abc";
+  const size_t size = sizeof(data) - 1;
+  EXPECT_EQ(3u, size);
+  DecodeBuffer base(data, size);
+  {
+    // First no change to the cursor.
+    DecodeBufferSubset subset(&base, size + 100);
+    EXPECT_EQ(size, subset.FullSize());
+    EXPECT_EQ(base.FullSize(), subset.FullSize());
+    EXPECT_EQ(0u, subset.Offset());
+  }
+  EXPECT_EQ(0u, base.Offset());
+  EXPECT_EQ(size, base.Remaining());
+}
+
+// Make sure that DecodeBuffer ctor complains about bad args.
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+TEST(DecodeBufferDeathTest, NonNullBufferRequired) {
+  EXPECT_DEBUG_DEATH({ DecodeBuffer b(nullptr, 3); }, "nullptr");
+}
+
+// Make sure that DecodeBuffer ctor complains about bad args.
+TEST(DecodeBufferDeathTest, ModestBufferSizeRequired) {
+  EXPECT_DEBUG_DEATH(
+      {
+        const char data[] = "abc";
+        size_t len = 0;
+        DecodeBuffer b(data, ~len);
+      },
+      "Max.*Length");
+}
+
+// Make sure that DecodeBuffer detects advance beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, LimitedAdvance) {
+  {
+    // Advance right up to end is OK.
+    const char data[] = "abc";
+    DecodeBuffer b(data, 3);
+    b.AdvanceCursor(3);  // OK
+    EXPECT_TRUE(b.Empty());
+  }
+  EXPECT_DEBUG_DEATH(
+      {
+        // Going beyond is not OK.
+        const char data[] = "abc";
+        DecodeBuffer b(data, 3);
+        b.AdvanceCursor(4);
+      },
+      "4 vs. 3");
+}
+
+// Make sure that DecodeBuffer detects decode beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, DecodeUInt8PastEnd) {
+  const char data[] = {0x12, 0x23};
+  DecodeBuffer b(data, sizeof data);
+  EXPECT_EQ(2u, b.FullSize());
+  EXPECT_EQ(0x1223, b.DecodeUInt16());
+  EXPECT_DEBUG_DEATH({ b.DecodeUInt8(); }, "1 vs. 0");
+}
+
+// Make sure that DecodeBuffer detects decode beyond end, in debug mode.
+TEST(DecodeBufferDeathTest, DecodeUInt16OverEnd) {
+  const char data[] = {0x12, 0x23, 0x34};
+  DecodeBuffer b(data, sizeof data);
+  EXPECT_EQ(3u, b.FullSize());
+  EXPECT_EQ(0x1223, b.DecodeUInt16());
+  EXPECT_DEBUG_DEATH({ b.DecodeUInt16(); }, "2 vs. 1");
+}
+
+// Make sure that DecodeBuffer doesn't agree with having two subsets.
+TEST(DecodeBufferSubsetDeathTest, TwoSubsets) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  DecodeBufferSubset subset1(&base, 1);
+  EXPECT_DEBUG_DEATH({ DecodeBufferSubset subset2(&base, 1); },
+                     "There is already a subset");
+}
+
+// Make sure that DecodeBufferSubset notices when the base's cursor has moved.
+TEST(DecodeBufferSubsetDeathTest, BaseCursorAdvanced) {
+  const char data[] = "abc";
+  DecodeBuffer base(data, 3);
+  base.AdvanceCursor(1);
+  EXPECT_DEBUG_DEATH(
+      {
+        DecodeBufferSubset subset1(&base, 2);
+        base.AdvanceCursor(1);
+      },
+      "Access via subset only when present");
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/decode_http2_structures.cc b/net/http2/decoder/decode_http2_structures.cc
new file mode 100644
index 0000000..cd419e3
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures.cc
@@ -0,0 +1,355 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_http2_structures.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+// Http2FrameHeader decoding:
+
+void DoDecode(Http2FrameHeader* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2FrameHeader::EncodedSize(), b->Remaining());
+  out->payload_length = b->DecodeUInt24();
+  out->type = static_cast<Http2FrameType>(b->DecodeUInt8());
+  out->flags = static_cast<Http2FrameFlag>(b->DecodeUInt8());
+  out->stream_id = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2FrameHeader* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2FrameHeader::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2FrameHeader* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2FrameHeader::EncodedSize(), *offset);
+  if (b->SlowDecodeUInt24(0 /* field_offset */, offset, &out->payload_length) &&
+      b->SlowDecodeEnum(1 /* field_size */, 3 /* field_offset */, offset,
+                        &out->type) &&
+      b->SlowDecodeEnum(1 /* field_size */, 4 /* field_offset */, offset,
+                        &out->flags) &&
+      b->SlowDecodeUInt31(5 /* field_offset */, offset, &out->stream_id)) {
+    DCHECK_EQ(Http2FrameHeader::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_GT(Http2FrameHeader::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2PriorityFields decoding:
+
+void DoDecode(Http2PriorityFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PriorityFields::EncodedSize(), b->Remaining());
+  uint32_t stream_id_and_flag = b->DecodeUInt32();
+  out->stream_dependency = stream_id_and_flag & StreamIdMask();
+  if (out->stream_dependency == stream_id_and_flag) {
+    out->is_exclusive = false;
+  } else {
+    out->is_exclusive = true;
+  }
+  // Note that chars are automatically promoted to ints during arithmetic,
+  // so 255 + 1 doesn't end up as zero.
+  out->weight = b->DecodeUInt8() + 1;
+}
+
+bool MaybeDecode(Http2PriorityFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PriorityFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PriorityFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2PriorityFields::EncodedSize(), *offset);
+  const uint32_t start_offset = *offset;
+  if (b->SlowDecodeUInt32(0 /* field_offset */, offset,
+                          &out->stream_dependency) &&
+      b->SlowDecodeUnsignedInt(1,  // field_size
+                               4,  // field_offset
+                               offset, &out->weight)) {
+    DCHECK_EQ(Http2PriorityFields::EncodedSize(), *offset);
+    if (start_offset < *offset) {
+      // First time here. Extract is_exclusive from stream_dependency.
+      const uint32_t stream_id_only = out->stream_dependency & StreamIdMask();
+      if (out->stream_dependency != stream_id_only) {
+        out->stream_dependency = stream_id_only;
+        out->is_exclusive = true;
+      } else {
+        out->is_exclusive = false;
+      }
+      // Need to add one to the weight field because the encoding is 0-255, but
+      // interpreted as 1-256.
+      ++(out->weight);
+    }
+    return true;
+  }
+  DCHECK_GT(Http2PriorityFields::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2RstStreamFields decoding:
+
+void DoDecode(Http2RstStreamFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2RstStreamFields::EncodedSize(), b->Remaining());
+  out->error_code = static_cast<Http2ErrorCode>(b->DecodeUInt32());
+}
+
+bool MaybeDecode(Http2RstStreamFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2RstStreamFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2RstStreamFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_GT(Http2RstStreamFields::EncodedSize(), *offset);
+
+  if (b->SlowDecodeEnum(4 /* field_size */, 0 /* field_offset */, offset,
+                        &out->error_code)) {
+    DCHECK_EQ(Http2RstStreamFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_GT(Http2RstStreamFields::EncodedSize(), *offset);
+  return false;
+}
+
+// Http2SettingFields decoding:
+
+void DoDecode(Http2SettingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2SettingFields::EncodedSize(), b->Remaining());
+  out->parameter = static_cast<Http2SettingsParameter>(b->DecodeUInt16());
+  out->value = b->DecodeUInt32();
+}
+
+bool MaybeDecode(Http2SettingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2SettingFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2SettingFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2SettingFields::EncodedSize());
+
+  if (b->SlowDecodeEnum(2 /* field_size */, 0 /* field_offset */, offset,
+                        &out->parameter) &&
+      b->SlowDecodeUInt32(2 /* field_offset */, offset, &out->value)) {
+    DCHECK_EQ(Http2SettingFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2SettingFields::EncodedSize());
+  return false;
+}
+
+// Http2PushPromiseFields decoding:
+
+void DoDecode(Http2PushPromiseFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PushPromiseFields::EncodedSize(), b->Remaining());
+  out->promised_stream_id = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2PushPromiseFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PushPromiseFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PushPromiseFields* out,
+                DecodeBuffer* b,
+                uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2PushPromiseFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset,
+                          &out->promised_stream_id)) {
+    DCHECK_EQ(Http2PushPromiseFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2PushPromiseFields::EncodedSize());
+  return false;
+}
+
+// Http2PingFields decoding:
+
+void DoDecode(Http2PingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2PingFields::EncodedSize(), b->Remaining());
+  memcpy(out->opaque_data, b->cursor(), Http2PingFields::EncodedSize());
+  b->AdvanceCursor(Http2PingFields::EncodedSize());
+}
+
+bool MaybeDecode(Http2PingFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2PingFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2PingFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2PingFields::EncodedSize());
+  while (*offset < Http2PingFields::EncodedSize()) {
+    if (b->Empty()) {
+      return false;
+    }
+    out->opaque_data[(*offset)++] = b->DecodeUInt8();
+  }
+  return true;
+}
+
+// Http2GoAwayFields decoding:
+
+void DoDecode(Http2GoAwayFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2GoAwayFields::EncodedSize(), b->Remaining());
+  out->last_stream_id = b->DecodeUInt31();
+  out->error_code = static_cast<Http2ErrorCode>(b->DecodeUInt32());
+}
+
+bool MaybeDecode(Http2GoAwayFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2GoAwayFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2GoAwayFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2GoAwayFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset, &out->last_stream_id) &&
+      b->SlowDecodeEnum(4 /* field_size */, 4 /* field_offset */, offset,
+                        &out->error_code)) {
+    DCHECK_EQ(Http2GoAwayFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2GoAwayFields::EncodedSize());
+  return false;
+}
+
+// Http2WindowUpdateFields decoding:
+
+void DoDecode(Http2WindowUpdateFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2WindowUpdateFields::EncodedSize(), b->Remaining());
+  out->window_size_increment = b->DecodeUInt31();
+}
+
+bool MaybeDecode(Http2WindowUpdateFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2WindowUpdateFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2WindowUpdateFields* out,
+                DecodeBuffer* b,
+                uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2WindowUpdateFields::EncodedSize());
+  if (b->SlowDecodeUInt31(0 /* field_offset */, offset,
+                          &out->window_size_increment)) {
+    DCHECK_EQ(Http2WindowUpdateFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2WindowUpdateFields::EncodedSize());
+  return false;
+}
+
+// Http2AltSvcFields decoding:
+
+void DoDecode(Http2AltSvcFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_LE(Http2AltSvcFields::EncodedSize(), b->Remaining());
+  out->origin_length = b->DecodeUInt16();
+}
+
+bool MaybeDecode(Http2AltSvcFields* out, DecodeBuffer* b) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  if (b->Remaining() >= Http2AltSvcFields::EncodedSize()) {
+    DoDecode(out, b);
+    return true;
+  }
+  return false;
+}
+
+bool SlowDecode(Http2AltSvcFields* out, DecodeBuffer* b, uint32_t* offset) {
+  DCHECK_NE(nullptr, out);
+  DCHECK_NE(nullptr, b);
+  DCHECK_NE(nullptr, offset);
+  DCHECK_LT(*offset, Http2AltSvcFields::EncodedSize());
+  if (b->SlowDecodeUInt16(0 /* field_offset */, offset, &out->origin_length)) {
+    DCHECK_EQ(Http2AltSvcFields::EncodedSize(), *offset);
+    return true;
+  }
+  DCHECK_LT(*offset, Http2AltSvcFields::EncodedSize());
+  return false;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_http2_structures.h b/net/http2/decoder/decode_http2_structures.h
new file mode 100644
index 0000000..7cee46f
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
+#define NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
+
+// Provides functions for decoding the fixed size structures in the HTTP/2 spec.
+
+// TODO(jamessynge): Consider whether the value of the SlowDecode methods is
+// worth their complexity; in particular, dropping back to buffering at most
+// 9 bytes (the largest fixed size structure) may actually be more efficient
+// than using the SlowDecode methods, or at least worth the complexity
+// reduction.
+// See http2_structure_decoder.h et al for an experiment in removing all except
+// DoDecode.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// DoDecode(STRUCTURE* out, DecodeBuffer* b) decodes the structure from start
+// to end, advancing the cursor by STRUCTURE::EncodedSize(). The decoder buffer
+// must be large enough (i.e. b->Remaining() >= STRUCTURE::EncodedSize()).
+
+NET_EXPORT_PRIVATE void DoDecode(Http2FrameHeader* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PriorityFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2RstStreamFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2SettingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PushPromiseFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2PingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2GoAwayFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2WindowUpdateFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE void DoDecode(Http2AltSvcFields* out, DecodeBuffer* b);
+
+// MaybeDecode(STRUCTURE* out, DecodeBuffer* b) decodes the structure from
+// start to end if the decoder buffer is large enough, advancing the cursor
+// by STRUCTURE::EncodedSize(), then returns true.
+// If the decode buffer isn't large enough, does nothing and returns false.
+// The buffer is large enough if b->Remaining() >= STRUCTURE::EncodedSize().
+
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2FrameHeader* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PriorityFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2RstStreamFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2SettingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PushPromiseFields* out,
+                                    DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2PingFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2GoAwayFields* out, DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2WindowUpdateFields* out,
+                                    DecodeBuffer* b);
+NET_EXPORT_PRIVATE bool MaybeDecode(Http2AltSvcFields* out, DecodeBuffer* b);
+
+// SlowDecode(STRUCTURE* out, DecodeBuffer* b, uint32_t* offset) provides
+// incremental decoding of a structure, supporting cases where the structure
+// is split across multiple input buffers. *offset represents the offset within
+// the encoding of the structure, in the range [0, STRUCTURE::EncodedSize()].
+// Returns true when it is able to completely decode the structure, false
+// before that. Updates *offset to record the progress decoding the structure;
+// if false is returned, then b->Remaining() == 0 when SlowDecode returns.
+
+NET_EXPORT_PRIVATE bool SlowDecode(Http2FrameHeader* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PriorityFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2RstStreamFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2SettingFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PushPromiseFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2PingFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2GoAwayFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2WindowUpdateFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+NET_EXPORT_PRIVATE bool SlowDecode(Http2AltSvcFields* out,
+                                   DecodeBuffer* b,
+                                   uint32_t* offset);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_HTTP2_STRUCTURES_H_
diff --git a/net/http2/decoder/decode_http2_structures_test.cc b/net/http2/decoder/decode_http2_structures_test.cc
new file mode 100644
index 0000000..4b09e7a
--- /dev/null
+++ b/net/http2/decoder/decode_http2_structures_test.cc
@@ -0,0 +1,542 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_http2_structures.h"
+
+// Tests decoding all of the fixed size HTTP/2 structures (i.e. those defined
+// in net/http2/http2_structures.h).
+
+// TODO(jamessynge): Combine tests of DoDecode, MaybeDecode, SlowDecode and
+// Http2StructureDecoder test using gUnit's support for tests parameterized
+// by type.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+template <class S>
+string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+template <class S>
+class StructureDecoderTest : public RandomDecoderTest {
+ protected:
+  typedef S Structure;
+
+  StructureDecoderTest() {
+    // IF the test adds more data after the encoded structure, stop as
+    // soon as the structure is decoded.
+    stop_decode_on_done_ = true;
+  }
+
+  // Reset the decoding to the start of the structure, and overwrite the
+  // current contents of |structure_|, in to which we'll decode the buffer.
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    decode_offset_ = 0;
+    Randomize(&structure_);
+    return ResumeDecoding(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    // If we're at the start...
+    if (decode_offset_ == 0) {
+      const uint32_t start_offset = b->Offset();
+      const char* const start_cursor = b->cursor();
+      // ... attempt to decode the entire structure.
+      if (MaybeDecode(&structure_, b)) {
+        ++fast_decode_count_;
+        EXPECT_EQ(S::EncodedSize(), b->Offset() - start_offset);
+
+        if (!HasFailure()) {
+          // Success. Confirm that SlowDecode produces the same result.
+          DecodeBuffer b2(start_cursor, b->Offset() - start_offset);
+          S second;
+          Randomize(&second);
+          uint32_t second_offset = 0;
+          EXPECT_TRUE(SlowDecode(&second, &b2, &second_offset));
+          EXPECT_EQ(S::EncodedSize(), second_offset);
+          EXPECT_EQ(structure_, second);
+        }
+
+        // Test can't easily tell if MaybeDecode or SlowDecode is used, so
+        // update decode_offset_ as if SlowDecode had been used to completely
+        // decode.
+        decode_offset_ = S::EncodedSize();
+        return DecodeStatus::kDecodeDone;
+      }
+    }
+
+    // We didn't have enough in the first buffer to decode everything, so we'll
+    // reach here multiple times until we've completely decoded the structure.
+    if (SlowDecode(&structure_, b, &decode_offset_)) {
+      ++slow_decode_count_;
+      EXPECT_EQ(S::EncodedSize(), decode_offset_);
+      return DecodeStatus::kDecodeDone;
+    }
+
+    // Drained the input buffer, but not yet done.
+    EXPECT_TRUE(b->Empty());
+    EXPECT_GT(S::EncodedSize(), decode_offset_);
+
+    return DecodeStatus::kDecodeInProgress;
+  }
+
+  // Set the fields of |*p| to random values.
+  void Randomize(S* p) { ::net::test::Randomize(p, RandomPtr()); }
+
+  AssertionResult ValidatorForDecodeLeadingStructure(const S* expected,
+                                                     const DecodeBuffer& db,
+                                                     DecodeStatus status) {
+    if (expected != nullptr && *expected != structure_) {
+      return AssertionFailure()
+             << "Expected structs to be equal\nExpected: " << *expected
+             << "\n  Actual: " << structure_;
+    }
+    return AssertionSuccess();
+  }
+
+  // Fully decodes the Structure at the start of data, and confirms it matches
+  // *expected (if provided).
+  void DecodeLeadingStructure(const S* expected, StringPiece data) {
+    ASSERT_LE(S::EncodedSize(), data.size());
+    DecodeBuffer original(data);
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that structure_ matches the expected value, if provided.
+    Validator validator =
+        base::Bind(&StructureDecoderTest::ValidatorForDecodeLeadingStructure,
+                   base::Unretained(this), expected);
+
+    // First validate that decoding is done and that we've advanced the cursor
+    // the expected amount.
+    Validator wrapped_validator =
+        ValidateDoneAndOffset(S::EncodedSize(), validator);
+
+    // Decode several times, with several segmentations of the input buffer.
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+    EXPECT_TRUE(DecodeAndValidateSeveralWays(
+        &original, false /*return_non_zero_on_first*/, wrapped_validator));
+
+    if (!HasFailure()) {
+      EXPECT_EQ(S::EncodedSize(), decode_offset_);
+      EXPECT_EQ(S::EncodedSize(), original.Offset());
+      EXPECT_LT(0u, fast_decode_count_);
+      EXPECT_LT(0u, slow_decode_count_);
+      if (expected != nullptr) {
+        DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+        DVLOG(1) << "DecodeLeadingStructure   actual: " << structure_;
+        EXPECT_EQ(*expected, structure_);
+      }
+    }
+  }
+
+  template <size_t N>
+  void DecodeLeadingStructure(const char (&data)[N]) {
+    DecodeLeadingStructure(nullptr, StringPiece(data, N));
+  }
+
+  // Encode the structure |in_s| into bytes, then decode the bytes
+  // and validate that the decoder produced the same field values.
+  void EncodeThenDecode(const S& in_s) {
+    string bytes = SerializeStructure(in_s);
+    EXPECT_EQ(S::EncodedSize(), bytes.size());
+    DecodeLeadingStructure(&in_s, bytes);
+  }
+
+  // Generate
+  void TestDecodingRandomizedStructures(size_t count) {
+    for (size_t i = 0; i < count && !HasFailure(); ++i) {
+      Structure input;
+      Randomize(&input);
+      EncodeThenDecode(input);
+    }
+  }
+
+  uint32_t decode_offset_ = 0;
+  S structure_;
+  size_t fast_decode_count_ = 0;
+  size_t slow_decode_count_ = 0;
+};
+
+class FrameHeaderDecoderTest : public StructureDecoderTest<Http2FrameHeader> {};
+
+TEST_F(FrameHeaderDecoderTest, DecodesLiteral) {
+  {
+    // Realistic input.
+    const char kData[] = {
+        0x00, 0x00, 0x05,        // Payload length: 5
+        0x01,                    // Frame type: HEADERS
+        0x08,                    // Flags: PADDED
+        0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+        0x04,                    // Padding length: 4
+        0x00, 0x00, 0x00, 0x00,  // Padding bytes
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(5u, structure_.payload_length);
+      EXPECT_EQ(Http2FrameType::HEADERS, structure_.type);
+      EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, structure_.flags);
+      EXPECT_EQ(1u, structure_.stream_id);
+    }
+  }
+  {
+    // Unlikely input.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,         // Payload length: uint24 max
+        0xffu,                       // Frame type: Unknown
+        0xffu,                       // Flags: Unknown/All
+        0xffu, 0xffu, 0xffu, 0xffu,  // Stream ID: uint31 max, plus R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ((1u << 24) - 1, structure_.payload_length);
+      EXPECT_EQ(static_cast<Http2FrameType>(255), structure_.type);
+      EXPECT_EQ(255, structure_.flags);
+      EXPECT_EQ(0x7FFFFFFFu, structure_.stream_id);
+    }
+  }
+}
+
+TEST_F(FrameHeaderDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PriorityFieldsDecoderTest
+    : public StructureDecoderTest<Http2PriorityFields> {};
+
+TEST_F(PriorityFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x80u, 0x00, 0x00, 0x05,  // Exclusive (yes) and Dependency (5)
+        0xffu,                    // Weight: 256 (after adding 1)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(5u, structure_.stream_dependency);
+      EXPECT_EQ(256u, structure_.weight);
+      EXPECT_EQ(true, structure_.is_exclusive);
+    }
+  }
+  {
+    const char kData[] = {
+        0x7f,  0xffu,
+        0xffu, 0xffu,  // Exclusive (no) and Dependency (0x7fffffff)
+        0x00,          // Weight: 1 (after adding 1)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.stream_dependency);
+      EXPECT_EQ(1u, structure_.weight);
+      EXPECT_FALSE(structure_.is_exclusive);
+    }
+  }
+}
+
+TEST_F(PriorityFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class RstStreamFieldsDecoderTest
+    : public StructureDecoderTest<Http2RstStreamFields> {};
+
+TEST_F(RstStreamFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Error: PROTOCOL_ERROR
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_FALSE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+    }
+  }
+}
+
+TEST_F(RstStreamFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class SettingFieldsDecoderTest
+    : public StructureDecoderTest<Http2SettingFields> {};
+
+TEST_F(SettingFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01,              // Setting: HEADER_TABLE_SIZE
+        0x00, 0x00, 0x40, 0x00,  // Value: 16K
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_TRUE(structure_.IsSupportedParameter());
+      EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE,
+                structure_.parameter);
+      EXPECT_EQ(1u << 14, structure_.value);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00,  0x00,                 // Setting: Unknown (0)
+        0xffu, 0xffu, 0xffu, 0xffu,  // Value: max uint32
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_FALSE(structure_.IsSupportedParameter());
+      EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_.parameter);
+    }
+  }
+}
+
+TEST_F(SettingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PushPromiseFieldsDecoderTest
+    : public StructureDecoderTest<Http2PushPromiseFields> {};
+
+TEST_F(PushPromiseFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x8au, 0x92u,  // Promised Stream ID: 101010
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(101010u, structure_.promised_stream_id);
+    }
+  }
+  {
+    // Promised stream id has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Promised Stream ID: max uint31 and R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.promised_stream_id);
+    }
+  }
+}
+
+TEST_F(PushPromiseFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class PingFieldsDecoderTest : public StructureDecoderTest<Http2PingFields> {};
+
+TEST_F(PingFieldsDecoderTest, DecodesLiteral) {
+  {
+    // Each byte is different, so can detect if order changed.
+    const char kData[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+  {
+    // All zeros, detect problems handling NULs.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+    }
+  }
+}
+
+TEST_F(PingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class GoAwayFieldsDecoderTest : public StructureDecoderTest<Http2GoAwayFields> {
+};
+
+TEST_F(GoAwayFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Last Stream ID: 0
+        0x00, 0x00, 0x00, 0x00,  // Error: NO_ERROR (0)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0u, structure_.last_stream_id);
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Last Stream ID: 1
+        0x00, 0x00, 0x00, 0x0d,  // Error: HTTP_1_1_REQUIRED
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(1u, structure_.last_stream_id);
+      EXPECT_TRUE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_.error_code);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Last Stream ID: max uint31 and R-bit
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.last_stream_id);  // No high-bit.
+      EXPECT_FALSE(structure_.IsSupportedErrorCode());
+      EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+    }
+  }
+}
+
+TEST_F(GoAwayFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class WindowUpdateFieldsDecoderTest
+    : public StructureDecoderTest<Http2WindowUpdateFields> {};
+
+TEST_F(WindowUpdateFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x00, 0x00,  // Window Size Increment: 2 ^ 16
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(1u << 16, structure_.window_size_increment);
+    }
+  }
+  {
+    // Increment must be non-zero, but we need to be able to decode the invalid
+    // zero to detect it.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Window Size Increment: 0
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0u, structure_.window_size_increment);
+    }
+  }
+  {
+    // Increment has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,
+        0xffu,  // Window Size Increment: max uint31 and R-bit
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(StreamIdMask(), structure_.window_size_increment);
+    }
+  }
+}
+
+TEST_F(WindowUpdateFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+//------------------------------------------------------------------------------
+
+class AltSvcFieldsDecoderTest : public StructureDecoderTest<Http2AltSvcFields> {
+};
+
+TEST_F(AltSvcFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00,  // Origin Length: 0
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(0, structure_.origin_length);
+    }
+  }
+  {
+    const char kData[] = {
+        0x00, 0x14,  // Origin Length: 20
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(20, structure_.origin_length);
+    }
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu,  // Origin Length: uint16 max
+    };
+    DecodeLeadingStructure(kData);
+    if (!HasFailure()) {
+      EXPECT_EQ(65535, structure_.origin_length);
+    }
+  }
+}
+
+TEST_F(AltSvcFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures(100);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/decode_status.cc b/net/http2/decoder/decode_status.cc
new file mode 100644
index 0000000..99a1de0b5
--- /dev/null
+++ b/net/http2/decoder/decode_status.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/decode_status.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out, DecodeStatus v) {
+  switch (v) {
+    case DecodeStatus::kDecodeDone:
+      return out << "DecodeDone";
+    case DecodeStatus::kDecodeInProgress:
+      return out << "DecodeInProgress";
+    case DecodeStatus::kDecodeError:
+      return out << "DecodeError";
+  }
+  // Since the value doesn't come over the wire, only a programming bug should
+  // result in reaching this point.
+  int unknown = static_cast<int>(v);
+  LOG(DFATAL) << "Unknown DecodeStatus " << unknown << std::hex << unknown;
+  return out << "UnknownDecodeStatus(" << unknown << ")";
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/decode_status.h b/net/http2/decoder/decode_status.h
new file mode 100644
index 0000000..e5fbeeb
--- /dev/null
+++ b/net/http2/decoder/decode_status.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_DECODE_STATUS_H_
+#define NET_HTTP2_DECODER_DECODE_STATUS_H_
+
+// Enum DecodeStatus is used to report the status of decoding of many
+// types of HTTP/2 and HPACK objects.
+
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+enum class DecodeStatus {
+  // Decoding is done.
+  kDecodeDone,
+
+  // Decoder needs more input to be able to make progress.
+  kDecodeInProgress,
+
+  // Decoding failed (e.g. HPACK variable length integer is too large, or
+  // an HTTP/2 frame has padding declared to be larger than the payload).
+  kDecodeError,
+};
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, DecodeStatus v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_DECODE_STATUS_H_
diff --git a/net/http2/decoder/frame_decoder_state.cc b/net/http2/decoder/frame_decoder_state.cc
new file mode 100644
index 0000000..e2b4406
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state.cc
@@ -0,0 +1,81 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+DecodeStatus FrameDecoderState::ReadPadLength(DecodeBuffer* db,
+                                              bool report_pad_length) {
+  DVLOG(2) << "ReadPadLength db->Remaining=" << db->Remaining()
+           << "; payload_length=" << frame_header().payload_length;
+  DCHECK(IsPaddable());
+  DCHECK(frame_header().IsPadded());
+
+  // Pad Length is always at the start of the frame, so remaining_payload_
+  // should equal payload_length at this point.
+  const uint32_t total_payload = frame_header().payload_length;
+  DCHECK_EQ(total_payload, remaining_payload_);
+  DCHECK_EQ(0u, remaining_padding_);
+
+  if (db->HasData()) {
+    const uint32_t pad_length = db->DecodeUInt8();
+    const uint32_t total_padding = pad_length + 1;
+    if (total_padding <= total_payload) {
+      remaining_padding_ = pad_length;
+      remaining_payload_ = total_payload - total_padding;
+      if (report_pad_length) {
+        listener()->OnPadLength(pad_length);
+      }
+      return DecodeStatus::kDecodeDone;
+    }
+    const uint32_t missing_length = total_padding - total_payload;
+    // To allow for the possibility of recovery, record the number of
+    // remaining bytes of the frame's payload (invalid though it is)
+    // in remaining_payload_.
+    remaining_payload_ = total_payload - 1;  // 1 for sizeof(Pad Length).
+    remaining_padding_ = 0;
+    listener()->OnPaddingTooLong(frame_header(), missing_length);
+    return DecodeStatus::kDecodeError;
+  }
+
+  if (total_payload == 0) {
+    remaining_payload_ = 0;
+    remaining_padding_ = 0;
+    listener()->OnPaddingTooLong(frame_header(), 1);
+    return DecodeStatus::kDecodeError;
+  }
+  // Need to wait for another buffer.
+  return DecodeStatus::kDecodeInProgress;
+}
+
+bool FrameDecoderState::SkipPadding(DecodeBuffer* db) {
+  DVLOG(2) << "SkipPadding remaining_padding_=" << remaining_padding_
+           << ", db->Remaining=" << db->Remaining()
+           << ", header: " << frame_header();
+  DCHECK_EQ(remaining_payload_, 0u);
+  DCHECK(IsPaddable()) << "header: " << frame_header();
+  DCHECK_GE(remaining_padding_, 0u);
+  DCHECK(remaining_padding_ == 0 || frame_header().IsPadded())
+      << "remaining_padding_=" << remaining_padding_
+      << ", header: " << frame_header();
+  const size_t avail = AvailablePadding(db);
+  if (avail > 0) {
+    listener()->OnPadding(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    remaining_padding_ -= avail;
+  }
+  return remaining_padding_ == 0;
+}
+
+DecodeStatus FrameDecoderState::ReportFrameSizeError() {
+  DVLOG(2) << "FrameDecoderState::ReportFrameSizeError: "
+           << " remaining_payload_=" << remaining_payload_
+           << "; remaining_padding_=" << remaining_padding_
+           << ", header: " << frame_header();
+  listener()->OnFrameSizeError(frame_header());
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/frame_decoder_state.h b/net/http2/decoder/frame_decoder_state.h
new file mode 100644
index 0000000..ddf64d1f
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state.h
@@ -0,0 +1,252 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
+#define NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
+
+// FrameDecoderState provides state and behaviors in support of decoding
+// the common frame header and the payload of all frame types.
+// It is an input to all of the payload decoders.
+
+// TODO(jamessynge): Since FrameDecoderState has far more than state in it,
+// rename to FrameDecoderHelper, or similar.
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/http2_structure_decoder.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class FrameDecoderStatePeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE FrameDecoderState {
+ public:
+  FrameDecoderState() {}
+
+  // Sets the listener which the decoders should call as they decode HTTP/2
+  // frames. The listener can be changed at any time, which allows for replacing
+  // it with a no-op listener when an error is detected, either by the payload
+  // decoder (OnPaddingTooLong or OnFrameSizeError) or by the "real" listener.
+  // That in turn allows us to define Http2FrameDecoderListener such that all
+  // methods have return type void, with no direct way to indicate whether the
+  // decoder should stop, and to eliminate from the decoder all checks of the
+  // return value. Instead the listener/caller can simply replace the current
+  // listener with a no-op listener implementation.
+  // TODO(jamessynge): Make set_listener private as only Http2FrameDecoder
+  // and tests need to set it, so it doesn't need to be public.
+  void set_listener(Http2FrameDecoderListener* listener) {
+    listener_ = listener;
+  }
+  Http2FrameDecoderListener* listener() const { return listener_; }
+
+  // The most recently decoded frame header.
+  const Http2FrameHeader& frame_header() const { return frame_header_; }
+
+  // Decode a structure in the payload, adjusting remaining_payload_ to account
+  // for the consumed portion of the payload. Returns kDecodeDone when fully
+  // decoded, kDecodeError if it ran out of payload before decoding completed,
+  // and kDecodeInProgress if the decode buffer didn't have enough of the
+  // remaining payload.
+  template <class S>
+  DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+             << "\n\tremaining_payload_=" << remaining_payload_
+             << "\n\tneed=" << S::EncodedSize();
+    DecodeStatus status =
+        structure_decoder_.Start(out, db, &remaining_payload_);
+    if (status != DecodeStatus::kDecodeError) {
+      return status;
+    }
+    DVLOG(2) << "StartDecodingStructureInPayload: detected frame size error";
+    return ReportFrameSizeError();
+  }
+
+  // Resume decoding of a structure that has been split across buffers,
+  // adjusting remaining_payload_ to account for the consumed portion of
+  // the payload. Returns values are as for StartDecodingStructureInPayload.
+  template <class S>
+  DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+             << "\n\tremaining_payload_=" << remaining_payload_;
+    if (structure_decoder_.Resume(out, db, &remaining_payload_)) {
+      return DecodeStatus::kDecodeDone;
+    } else if (remaining_payload_ > 0) {
+      return DecodeStatus::kDecodeInProgress;
+    } else {
+      DVLOG(2) << "ResumeDecodingStructureInPayload: detected frame size error";
+      return ReportFrameSizeError();
+    }
+  }
+
+  // Initializes the two remaining* fields, which is needed if the frame's
+  // payload is split across buffers, or the decoder calls ReadPadLength or
+  // StartDecodingStructureInPayload, and of course the methods below which
+  // read those fields, as their names imply.
+  void InitializeRemainders() {
+    remaining_payload_ = frame_header().payload_length;
+    // Note that remaining_total_payload() relies on remaining_padding_ being
+    // zero for frames that have no padding.
+    remaining_padding_ = 0;
+  }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, including any trailing padding. This method must only be called
+  // after the variables have been initialized, which in practice means once a
+  // payload decoder has called InitializeRemainders and/or ReadPadLength.
+  size_t remaining_total_payload() const {
+    DCHECK(IsPaddable() || remaining_padding_ == 0) << frame_header();
+    return remaining_payload_ + remaining_padding_;
+  }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, excluding any trailing padding. This method must only be called
+  // after the variable has been initialized, which in practice means once a
+  // payload decoder has called InitializeRemainders; ReadPadLength will deduct
+  // the total number of padding bytes from remaining_payload_, including the
+  // size of the Pad Length field itself (1 byte).
+  size_t remaining_payload() const { return remaining_payload_; }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, including any trailing padding. This method must only be called if
+  // the frame type allows padding, and after the variable has been initialized,
+  // which in practice means once a payload decoder has called
+  // InitializeRemainders and/or ReadPadLength.
+  size_t remaining_payload_and_padding() const {
+    DCHECK(IsPaddable()) << frame_header();
+    return remaining_payload_ + remaining_padding_;
+  }
+
+  // Returns the number of bytes of trailing padding after the payload that
+  // remain to be decoded. This method must only be called if the frame type
+  // allows padding, and after the variable has been initialized, which in
+  // practice means once a payload decoder has called InitializeRemainders,
+  // and isn't set to a non-zero value until ReadPadLength has been called.
+  uint32_t remaining_padding() const {
+    DCHECK(IsPaddable()) << frame_header();
+    return remaining_padding_;
+  }
+
+  // Returns the amount of trailing padding after the payload that remains to be
+  // decoded.
+  uint32_t remaining_padding_for_test() const { return remaining_padding_; }
+
+  // How many bytes of the remaining payload are in db?
+  size_t AvailablePayload(DecodeBuffer* db) const {
+    return db->MinLengthRemaining(remaining_payload_);
+  }
+
+  // How many bytes of the remaining payload and padding are in db?
+  // Call only for frames whose type is paddable.
+  size_t AvailablePayloadAndPadding(DecodeBuffer* db) const {
+    DCHECK(IsPaddable()) << frame_header();
+    return db->MinLengthRemaining(remaining_payload_ + remaining_padding_);
+  }
+
+  // How many bytes of the padding that have not yet been skipped are in db?
+  // Call only after remaining_padding_ has been set (for padded frames), or
+  // been cleared (for unpadded frames); and after all of the non-padding
+  // payload has been decoded.
+  size_t AvailablePadding(DecodeBuffer* db) const {
+    DCHECK(IsPaddable()) << frame_header();
+    DCHECK_EQ(remaining_payload_, 0u);
+    return db->MinLengthRemaining(remaining_padding_);
+  }
+
+  // Reduces remaining_payload_ by amount. To be called by a payload decoder
+  // after it has passed a variable length portion of the payload to the
+  // listener; remaining_payload_ will be automatically reduced when fixed
+  // size structures and padding, including the Pad Length field, are decoded.
+  void ConsumePayload(size_t amount) {
+    DCHECK_LE(amount, remaining_payload_);
+    remaining_payload_ -= amount;
+  }
+
+  // Reads the Pad Length field into remaining_padding_, and appropriately sets
+  // remaining_payload_. When present, the Pad Length field is always the first
+  // field in the payload, which this method relies on so that the caller need
+  // not set remaining_payload_ before calling this method.
+  // If report_pad_length is true, calls the listener's OnPadLength method when
+  // it decodes the Pad Length field.
+  // Returns kDecodeDone if the decode buffer was not empty (i.e. because the
+  // field is only a single byte long, it can always be decoded if the buffer is
+  // not empty).
+  // Returns kDecodeError if the buffer is empty because the frame has no
+  // payload (i.e. payload_length() == 0).
+  // Returns kDecodeInProgress if the buffer is empty but the frame has a
+  // payload.
+  DecodeStatus ReadPadLength(DecodeBuffer* db, bool report_pad_length);
+
+  // Skip the trailing padding bytes; only call once remaining_payload_==0.
+  // Returns true when the padding has been skipped.
+  // Does NOT check that the padding is all zeroes.
+  bool SkipPadding(DecodeBuffer* db);
+
+  // Calls the listener's OnFrameSizeError method and returns kDecodeError.
+  DecodeStatus ReportFrameSizeError();
+
+ private:
+  friend class Http2FrameDecoder;
+  friend class test::FrameDecoderStatePeer;
+
+  // Starts the decoding of a common frame header. Returns true if completed the
+  // decoding, false if the decode buffer didn't have enough data in it, in
+  // which case the decode buffer will have been drained and the caller should
+  // call ResumeDecodingFrameHeader when more data is available. This is called
+  // from Http2FrameDecoder, a friend class.
+  bool StartDecodingFrameHeader(DecodeBuffer* db) {
+    return structure_decoder_.Start(&frame_header_, db);
+  }
+
+  // Resumes decoding the common frame header after the preceding call to
+  // StartDecodingFrameHeader returned false, as did any subsequent calls to
+  // ResumeDecodingFrameHeader. This is called from Http2FrameDecoder,
+  // a friend class.
+  bool ResumeDecodingFrameHeader(DecodeBuffer* db) {
+    return structure_decoder_.Resume(&frame_header_, db);
+  }
+
+  // Clear any of the flags in the frame header that aren't set in valid_flags.
+  void RetainFlags(uint8_t valid_flags) {
+    frame_header_.RetainFlags(valid_flags);
+  }
+
+  // Clear all of the flags in the frame header; for use with frame types that
+  // don't define any flags, such as WINDOW_UPDATE.
+  void ClearFlags() { frame_header_.flags = Http2FrameFlag(); }
+
+  // Returns true if the type of frame being decoded can have padding.
+  bool IsPaddable() const {
+    return frame_header().type == Http2FrameType::DATA ||
+           frame_header().type == Http2FrameType::HEADERS ||
+           frame_header().type == Http2FrameType::PUSH_PROMISE;
+  }
+
+  Http2FrameDecoderListener* listener_ = nullptr;
+  Http2FrameHeader frame_header_;
+
+  // Number of bytes remaining to be decoded, if set; does not include the
+  // trailing padding once the length of padding has been determined.
+  // See ReadPadLength.
+  uint32_t remaining_payload_;
+
+  // The amount of trailing padding after the payload that remains to be
+  // decoded. See ReadPadLength.
+  uint32_t remaining_padding_;
+
+  // Generic decoder of structures, which takes care of buffering the needed
+  // bytes if the encoded structure is split across decode buffers.
+  Http2StructureDecoder structure_decoder_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_DECODER_STATE_H_
diff --git a/net/http2/decoder/frame_decoder_state_test_util.cc b/net/http2/decoder/frame_decoder_state_test_util.cc
new file mode 100644
index 0000000..5988b8c
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state_test_util.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_decoder_state_test_util.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/http2_structure_decoder_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+
+namespace net {
+namespace test {
+
+// static
+void FrameDecoderStatePeer::Randomize(FrameDecoderState* p, RandomBase* rng) {
+  VLOG(1) << "FrameDecoderStatePeer::Randomize";
+  ::net::test::Randomize(&p->frame_header_, rng);
+  p->remaining_payload_ = rng->Rand32();
+  p->remaining_padding_ = rng->Rand32();
+  Http2StructureDecoderPeer::Randomize(&p->structure_decoder_, rng);
+}
+
+// static
+void FrameDecoderStatePeer::set_frame_header(const Http2FrameHeader& header,
+                                             FrameDecoderState* p) {
+  VLOG(1) << "FrameDecoderStatePeer::set_frame_header " << header;
+  p->frame_header_ = header;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_decoder_state_test_util.h b/net/http2/decoder/frame_decoder_state_test_util.h
new file mode 100644
index 0000000..e32a401
--- /dev/null
+++ b/net/http2/decoder/frame_decoder_state_test_util.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
+
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/random_decoder_test.h"
+
+namespace net {
+namespace test {
+
+class FrameDecoderStatePeer {
+ public:
+  // Randomizes (i.e. corrupts) the fields of the FrameDecoderState.
+  // PayloadDecoderBaseTest::StartDecoding calls this before passing the first
+  // decode buffer to the payload decoder, which increases the likelihood of
+  // detecting any use of prior states of the decoder on the decoding of
+  // future payloads.
+  static void Randomize(FrameDecoderState* p, RandomBase* rng);
+
+  // Inject a frame header into the FrameDecoderState.
+  // PayloadDecoderBaseTest::StartDecoding calls this just after calling
+  // Randomize (above), to simulate a full frame decoder having just finished
+  // decoding the common frame header and then calling the appropriate payload
+  // decoder based on the frame type in that frame header.
+  static void set_frame_header(const Http2FrameHeader& header,
+                               FrameDecoderState* p);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_DECODER_STATE_TEST_UTIL_H_
diff --git a/net/http2/decoder/frame_parts.cc b/net/http2/decoder/frame_parts.cc
new file mode 100644
index 0000000..33cd7bf
--- /dev/null
+++ b/net/http2/decoder/frame_parts.cc
@@ -0,0 +1,527 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts.h"
+
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/base/escape.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::ContainerEq;
+
+namespace net {
+namespace test {
+namespace {
+
+static_assert(std::is_base_of<Http2FrameDecoderListener, FrameParts>::value &&
+                  !std::is_abstract<FrameParts>::value,
+              "FrameParts needs to implement all of the methods of "
+              "Http2FrameDecoderListener");
+
+// Compare two optional variables of the same type.
+// TODO(jamessynge): Maybe create a ::testing::Matcher for this.
+template <class T>
+AssertionResult VerifyOptionalEq(const T& opt_a, const T& opt_b) {
+  if (opt_a) {
+    if (opt_b) {
+      VERIFY_EQ(opt_a.value(), opt_b.value());
+    } else {
+      return AssertionFailure() << "opt_b is not set; opt_a.value()="
+                                << opt_a.value();
+    }
+  } else if (opt_b) {
+    return AssertionFailure() << "opt_a is not set; opt_b.value()="
+                              << opt_b.value();
+  }
+  return AssertionSuccess();
+}
+
+}  // namespace
+
+FrameParts::FrameParts(const Http2FrameHeader& header) : frame_header(header) {
+  VLOG(1) << "FrameParts, header: " << frame_header;
+}
+
+FrameParts::FrameParts(const Http2FrameHeader& header, StringPiece payload)
+    : FrameParts(header) {
+  VLOG(1) << "FrameParts with payload.size() = " << payload.size();
+  payload.AppendToString(&this->payload);
+  opt_payload_length = payload.size();
+}
+FrameParts::FrameParts(const Http2FrameHeader& header,
+                       StringPiece payload,
+                       size_t total_pad_length)
+    : FrameParts(header, payload) {
+  VLOG(1) << "FrameParts with total_pad_length=" << total_pad_length;
+  SetTotalPadLength(total_pad_length);
+}
+
+FrameParts::FrameParts(const FrameParts& other) = default;
+
+FrameParts::~FrameParts() {}
+
+AssertionResult FrameParts::VerifyEquals(const FrameParts& that) const {
+#define COMMON_MESSAGE "\n  this: " << *this << "\n  that: " << that
+
+  VERIFY_EQ(frame_header, that.frame_header) << COMMON_MESSAGE;
+  VERIFY_EQ(payload, that.payload) << COMMON_MESSAGE;
+  VERIFY_EQ(padding, that.padding) << COMMON_MESSAGE;
+  VERIFY_EQ(altsvc_origin, that.altsvc_origin) << COMMON_MESSAGE;
+  VERIFY_EQ(altsvc_value, that.altsvc_value) << COMMON_MESSAGE;
+  VERIFY_EQ(settings, that.settings) << COMMON_MESSAGE;
+
+#define VERIFY_OPTIONAL_FIELD(field_name) \
+  VERIFY_SUCCESS(VerifyOptionalEq(field_name, that.field_name))
+
+  VERIFY_OPTIONAL_FIELD(opt_altsvc_origin_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_altsvc_value_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_goaway) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_missing_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_pad_length) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_ping) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_priority) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_push_promise) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_rst_stream_error_code) << COMMON_MESSAGE;
+  VERIFY_OPTIONAL_FIELD(opt_window_update_increment) << COMMON_MESSAGE;
+
+#undef VERIFY_OPTIONAL_FIELD
+
+  return AssertionSuccess();
+}
+
+void FrameParts::SetTotalPadLength(size_t total_pad_length) {
+  opt_pad_length.reset();
+  padding.clear();
+  if (total_pad_length > 0) {
+    ASSERT_LE(total_pad_length, 256u);
+    ASSERT_TRUE(frame_header.IsPadded());
+    opt_pad_length = total_pad_length - 1;
+    char zero = 0;
+    padding.append(opt_pad_length.value(), zero);
+  }
+
+  if (opt_pad_length) {
+    VLOG(1) << "SetTotalPadLength: pad_length=" << opt_pad_length.value();
+  } else {
+    VLOG(1) << "SetTotalPadLength: has no pad length";
+  }
+}
+
+void FrameParts::SetAltSvcExpected(StringPiece origin, StringPiece value) {
+  origin.AppendToString(&altsvc_origin);
+  value.AppendToString(&altsvc_value);
+  opt_altsvc_origin_length = origin.size();
+  opt_altsvc_value_length = value.size();
+}
+
+bool FrameParts::OnFrameHeader(const Http2FrameHeader& header) {
+  ADD_FAILURE() << "OnFrameHeader: " << *this;
+  return true;
+}
+
+void FrameParts::OnDataStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::DATA)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnDataPayload(const char* data, size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len << "; frame_header: " << frame_header;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::DATA)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnDataEnd() {
+  VLOG(1) << "OnDataEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::DATA)) << *this;
+}
+
+void FrameParts::OnHeadersStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::HEADERS)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnHeadersPriority(const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: priority: " << priority
+          << "; frame_header: " << frame_header;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::HEADERS)) << *this;
+  ASSERT_FALSE(opt_priority);
+  opt_priority = priority;
+  ASSERT_TRUE(opt_payload_length);
+  opt_payload_length =
+      opt_payload_length.value() - Http2PriorityFields::EncodedSize();
+}
+
+void FrameParts::OnHpackFragment(const char* data, size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len
+          << "; frame_header: " << frame_header;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(FrameCanHaveHpackPayload(frame_header)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::HEADERS)) << *this;
+}
+
+void FrameParts::OnPriorityFrame(const Http2FrameHeader& header,
+                                 const Http2PriorityFields& priority) {
+  VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PRIORITY)) << *this;
+  ASSERT_FALSE(opt_priority);
+  opt_priority = priority;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PRIORITY)) << *this;
+}
+
+void FrameParts::OnContinuationStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::CONTINUATION)) << *this;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::CONTINUATION)) << *this;
+}
+
+void FrameParts::OnPadLength(size_t trailing_length) {
+  VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+  ASSERT_TRUE(InPaddedFrame()) << *this;
+  ASSERT_FALSE(opt_pad_length);
+  ASSERT_TRUE(opt_payload_length);
+  size_t total_padding_length = trailing_length + 1;
+  ASSERT_GE(opt_payload_length.value(), static_cast<int>(total_padding_length));
+  opt_payload_length = opt_payload_length.value() - total_padding_length;
+  opt_pad_length = trailing_length;
+}
+
+void FrameParts::OnPadding(const char* pad, size_t skipped_length) {
+  VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+  ASSERT_TRUE(InPaddedFrame()) << *this;
+  ASSERT_TRUE(opt_pad_length);
+  ASSERT_TRUE(AppendString(StringPiece(pad, skipped_length), &padding,
+                           &opt_pad_length));
+}
+
+void FrameParts::OnRstStream(const Http2FrameHeader& header,
+                             Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::RST_STREAM)) << *this;
+  ASSERT_FALSE(opt_rst_stream_error_code);
+  opt_rst_stream_error_code = error_code;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::RST_STREAM)) << *this;
+}
+
+void FrameParts::OnSettingsStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
+  ASSERT_EQ(0u, settings.size());
+  ASSERT_FALSE(header.IsAck()) << header;
+}
+
+void FrameParts::OnSetting(const Http2SettingFields& setting_fields) {
+  VLOG(1) << "OnSetting: " << setting_fields;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::SETTINGS)) << *this;
+  settings.push_back(setting_fields);
+}
+
+void FrameParts::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this;
+}
+
+void FrameParts::OnSettingsAck(const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
+  ASSERT_EQ(0u, settings.size());
+  ASSERT_TRUE(header.IsAck());
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this;
+}
+
+void FrameParts::OnPushPromiseStart(const Http2FrameHeader& header,
+                                    const Http2PushPromiseFields& promise,
+                                    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart header: " << header << "; promise: " << promise
+          << "; total_padding_length: " << total_padding_length;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PUSH_PROMISE)) << *this;
+  ASSERT_GE(header.payload_length, Http2PushPromiseFields::EncodedSize());
+  opt_payload_length =
+      header.payload_length - Http2PushPromiseFields::EncodedSize();
+  ASSERT_FALSE(opt_push_promise);
+  opt_push_promise = promise;
+  if (total_padding_length > 0) {
+    ASSERT_GE(opt_payload_length.value(),
+              static_cast<int>(total_padding_length));
+    OnPadLength(total_padding_length - 1);
+  } else {
+    ASSERT_FALSE(header.IsPadded());
+  }
+}
+
+void FrameParts::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PUSH_PROMISE)) << *this;
+}
+
+void FrameParts::OnPing(const Http2FrameHeader& header,
+                        const Http2PingFields& ping) {
+  VLOG(1) << "OnPing header: " << header << "   ping: " << ping;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
+  ASSERT_FALSE(header.IsAck());
+  ASSERT_FALSE(opt_ping);
+  opt_ping = ping;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this;
+}
+
+void FrameParts::OnPingAck(const Http2FrameHeader& header,
+                           const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck header: " << header << "   ping: " << ping;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
+  ASSERT_TRUE(header.IsAck());
+  ASSERT_FALSE(opt_ping);
+  opt_ping = ping;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::PING)) << *this;
+}
+
+void FrameParts::OnGoAwayStart(const Http2FrameHeader& header,
+                               const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart: " << goaway;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::GOAWAY)) << *this;
+  ASSERT_FALSE(opt_goaway);
+  opt_goaway = goaway;
+  opt_payload_length = header.payload_length - Http2GoAwayFields::EncodedSize();
+}
+
+void FrameParts::OnGoAwayOpaqueData(const char* data, size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::GOAWAY)) << *this;
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::GOAWAY)) << *this;
+}
+
+void FrameParts::OnWindowUpdate(const Http2FrameHeader& header,
+                                uint32_t increment) {
+  VLOG(1) << "OnWindowUpdate header: " << header
+          << "     increment=" << increment;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::WINDOW_UPDATE)) << *this;
+  ASSERT_FALSE(opt_window_update_increment);
+  opt_window_update_increment = increment;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::WINDOW_UPDATE)) << *this;
+}
+
+void FrameParts::OnAltSvcStart(const Http2FrameHeader& header,
+                               size_t origin_length,
+                               size_t value_length) {
+  VLOG(1) << "OnAltSvcStart: " << header
+          << "    origin_length: " << origin_length
+          << "    value_length: " << value_length;
+  ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::ALTSVC)) << *this;
+  ASSERT_FALSE(opt_altsvc_origin_length);
+  opt_altsvc_origin_length = origin_length;
+  ASSERT_FALSE(opt_altsvc_value_length);
+  opt_altsvc_value_length = value_length;
+}
+
+void FrameParts::OnAltSvcOriginData(const char* data, size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
+  ASSERT_TRUE(AppendString(StringPiece(data, len), &altsvc_origin,
+                           &opt_altsvc_origin_length));
+}
+
+void FrameParts::OnAltSvcValueData(const char* data, size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
+  ASSERT_TRUE(AppendString(StringPiece(data, len), &altsvc_value,
+                           &opt_altsvc_value_length));
+}
+
+void FrameParts::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd; frame_header: " << frame_header;
+  ASSERT_TRUE(EndFrameOfType(Http2FrameType::ALTSVC)) << *this;
+}
+
+void FrameParts::OnUnknownStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(header.type)) << header;
+  ASSERT_FALSE(got_start_callback);
+  ASSERT_EQ(frame_header, header);
+  got_start_callback = true;
+  opt_payload_length = header.payload_length;
+}
+
+void FrameParts::OnUnknownPayload(const char* data, size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header.type)) << *this;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(
+      AppendString(StringPiece(data, len), &payload, &opt_payload_length));
+}
+
+void FrameParts::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd; frame_header: " << frame_header;
+  ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header.type)) << *this;
+  ASSERT_TRUE(got_start_callback);
+  ASSERT_FALSE(got_end_callback);
+  got_end_callback = true;
+}
+
+void FrameParts::OnPaddingTooLong(const Http2FrameHeader& header,
+                                  size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "; missing_length: " << missing_length;
+  ASSERT_EQ(frame_header, header);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_TRUE(FrameIsPadded(header));
+  ASSERT_FALSE(opt_pad_length);
+  ASSERT_FALSE(opt_missing_length);
+  opt_missing_length = missing_length;
+  got_start_callback = true;
+  got_end_callback = true;
+}
+
+void FrameParts::OnFrameSizeError(const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  ASSERT_EQ(frame_header, header);
+  ASSERT_FALSE(got_end_callback);
+  ASSERT_FALSE(has_frame_size_error);
+  has_frame_size_error = true;
+  got_end_callback = true;
+}
+
+void FrameParts::OutputTo(std::ostream& out) const {
+  out << "FrameParts{\n  frame_header: " << frame_header << "\n";
+  if (!payload.empty()) {
+    out << "  payload=\"" << EscapeQueryParamValue(payload, false) << "\"\n";
+  }
+  if (!padding.empty()) {
+    out << "  padding=\"" << EscapeQueryParamValue(padding, false) << "\"\n";
+  }
+  if (!altsvc_origin.empty()) {
+    out << "  altsvc_origin=\"" << EscapeQueryParamValue(altsvc_origin, false)
+        << "\"\n";
+  }
+  if (!altsvc_value.empty()) {
+    out << "  altsvc_value=\"" << EscapeQueryParamValue(altsvc_value, false)
+        << "\"\n";
+  }
+  if (opt_priority) {
+    out << "  priority=" << opt_priority.value() << "\n";
+  }
+  if (opt_rst_stream_error_code) {
+    out << "  rst_stream=" << opt_rst_stream_error_code.value() << "\n";
+  }
+  if (opt_push_promise) {
+    out << "  push_promise=" << opt_push_promise.value() << "\n";
+  }
+  if (opt_ping) {
+    out << "  ping=" << opt_ping.value() << "\n";
+  }
+  if (opt_goaway) {
+    out << "  goaway=" << opt_goaway.value() << "\n";
+  }
+  if (opt_window_update_increment) {
+    out << "  window_update=" << opt_window_update_increment.value() << "\n";
+  }
+  if (opt_payload_length) {
+    out << "  payload_length=" << opt_payload_length.value() << "\n";
+  }
+  if (opt_pad_length) {
+    out << "  pad_length=" << opt_pad_length.value() << "\n";
+  }
+  if (opt_missing_length) {
+    out << "  missing_length=" << opt_missing_length.value() << "\n";
+  }
+  if (opt_altsvc_origin_length) {
+    out << "  origin_length=" << opt_altsvc_origin_length.value() << "\n";
+  }
+  if (opt_altsvc_value_length) {
+    out << "  value_length=" << opt_altsvc_value_length.value() << "\n";
+  }
+  if (has_frame_size_error) {
+    out << "  has_frame_size_error\n";
+  }
+  if (got_start_callback) {
+    out << "  got_start_callback\n";
+  }
+  if (got_end_callback) {
+    out << "  got_end_callback\n";
+  }
+  for (size_t ndx = 0; ndx < settings.size(); ++ndx) {
+    out << "  setting[" << ndx << "]=" << settings[ndx];
+  }
+  out << "}";
+}
+
+AssertionResult FrameParts::StartFrameOfType(
+    const Http2FrameHeader& header,
+    Http2FrameType expected_frame_type) {
+  VERIFY_EQ(header.type, expected_frame_type);
+  VERIFY_FALSE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_EQ(frame_header, header);
+  got_start_callback = true;
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::InFrameOfType(Http2FrameType expected_frame_type) {
+  VERIFY_TRUE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_EQ(frame_header.type, expected_frame_type);
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::EndFrameOfType(Http2FrameType expected_frame_type) {
+  VERIFY_SUCCESS(InFrameOfType(expected_frame_type));
+  got_end_callback = true;
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::InPaddedFrame() {
+  VERIFY_TRUE(got_start_callback);
+  VERIFY_FALSE(got_end_callback);
+  VERIFY_TRUE(FrameIsPadded(frame_header));
+  return AssertionSuccess();
+}
+
+AssertionResult FrameParts::AppendString(StringPiece source,
+                                         string* target,
+                                         base::Optional<int>* opt_length) {
+  source.AppendToString(target);
+  if (opt_length != nullptr) {
+    VERIFY_TRUE(*opt_length) << "Length is not set yet\n" << *this;
+    VERIFY_LE(target->size(), static_cast<size_t>(opt_length->value()))
+        << "String too large; source.size() = " << source.size() << "\n"
+        << *this;
+  }
+  return ::testing::AssertionSuccess();
+}
+
+std::ostream& operator<<(std::ostream& out, const FrameParts& v) {
+  v.OutputTo(out);
+  return out;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts.h b/net/http2/decoder/frame_parts.h
new file mode 100644
index 0000000..c64dea69
--- /dev/null
+++ b/net/http2/decoder/frame_parts.h
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_H_
+
+// FrameParts implements Http2FrameDecoderListener, recording the callbacks
+// during the decoding of a single frame. It is also used for comparing the
+// info that a test expects to be recorded during the decoding of a frame
+// with the actual recorded value (i.e. by providing a comparator).
+
+// TODO(jamessynge): Convert FrameParts to a class, hide the members, add
+// getters/setters.
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+struct FrameParts;
+
+std::ostream& operator<<(std::ostream& out, const FrameParts& v);
+
+struct FrameParts : public Http2FrameDecoderListener {
+  // The first callback for every type of frame includes the frame header; this
+  // is the only constructor used during decoding of a frame.
+  explicit FrameParts(const Http2FrameHeader& header);
+
+  // For use in tests where the expected frame has a variable size payload.
+  FrameParts(const Http2FrameHeader& header, base::StringPiece payload);
+
+  // For use in tests where the expected frame has a variable size payload
+  // and may be padded.
+  FrameParts(const Http2FrameHeader& header,
+             base::StringPiece payload,
+             size_t total_pad_length);
+
+  FrameParts(const FrameParts& other);
+
+  ~FrameParts() override;
+
+  // Returns AssertionSuccess() if they're equal, else AssertionFailure()
+  // with info about the difference.
+  ::testing::AssertionResult VerifyEquals(const FrameParts& other) const;
+
+  // Format this FrameParts object.
+  void OutputTo(std::ostream& out) const;
+
+  // Set the total padding length (0 to 256).
+  void SetTotalPadLength(size_t total_pad_length);
+
+  // Set the origin and value expected in an ALTSVC frame.
+  void SetAltSvcExpected(base::StringPiece origin, base::StringPiece value);
+
+  // Http2FrameDecoderListener methods:
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* pad, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+  // The fields are public for access by tests.
+
+  const Http2FrameHeader frame_header;
+
+  std::string payload;
+  std::string padding;
+  std::string altsvc_origin;
+  std::string altsvc_value;
+
+  base::Optional<Http2PriorityFields> opt_priority;
+  base::Optional<Http2ErrorCode> opt_rst_stream_error_code;
+  base::Optional<Http2PushPromiseFields> opt_push_promise;
+  base::Optional<Http2PingFields> opt_ping;
+  base::Optional<Http2GoAwayFields> opt_goaway;
+
+  base::Optional<int> opt_pad_length;
+  base::Optional<int> opt_payload_length;
+  base::Optional<int> opt_missing_length;
+  base::Optional<int> opt_altsvc_origin_length;
+  base::Optional<int> opt_altsvc_value_length;
+
+  base::Optional<size_t> opt_window_update_increment;
+
+  bool has_frame_size_error = false;
+
+  std::vector<Http2SettingFields> settings;
+
+  // These booleans are not checked by CompareCollectedFrames.
+  bool got_start_callback = false;
+  bool got_end_callback = false;
+
+ private:
+  // ASSERT during an On* method that we're handling a frame of type
+  // expected_frame_type, and have not already received other On* methods
+  // (i.e. got_start_callback is false).
+  ::testing::AssertionResult StartFrameOfType(
+      const Http2FrameHeader& header,
+      Http2FrameType expected_frame_type);
+
+  // ASSERT that StartFrameOfType has already been called with
+  // expected_frame_type (i.e. got_start_callback has been called), and that
+  // EndFrameOfType has not yet been called (i.e. got_end_callback is false).
+  ::testing::AssertionResult InFrameOfType(Http2FrameType expected_frame_type);
+
+  // ASSERT that we're InFrameOfType, and then sets got_end_callback=true.
+  ::testing::AssertionResult EndFrameOfType(Http2FrameType expected_frame_type);
+
+  // ASSERT that we're in the middle of processing a frame that is padded.
+  ::testing::AssertionResult InPaddedFrame();
+
+  // Append source to target. If opt_length is not nullptr, then verifies that
+  // the optional has a value (i.e. that the necessary On*Start method has been
+  // called), and that target is not longer than opt_length->value().
+  ::testing::AssertionResult AppendString(base::StringPiece source,
+                                          std::string* target,
+                                          base::Optional<int>* opt_length);
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_H_
diff --git a/net/http2/decoder/frame_parts_collector.cc b/net/http2/decoder/frame_parts_collector.cc
new file mode 100644
index 0000000..05c54880
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts_collector.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+FramePartsCollector::FramePartsCollector() {}
+FramePartsCollector::~FramePartsCollector() {}
+
+void FramePartsCollector::Reset() {
+  current_frame_.reset();
+  collected_frames_.clear();
+  expected_header_set_ = false;
+}
+
+const FrameParts* FramePartsCollector::frame(size_t n) const {
+  if (n < size()) {
+    return collected_frames_.at(n).get();
+  }
+  CHECK(n == size());
+  return current_frame();
+}
+
+void FramePartsCollector::ExpectFrameHeader(const Http2FrameHeader& header) {
+  EXPECT_FALSE(IsInProgress());
+  EXPECT_FALSE(expected_header_set_) << "expected_header_: "
+                                     << expected_header_;
+  expected_header_ = header;
+  expected_header_set_ = true;
+  // OnFrameHeader is called before the flags are scrubbed, but the other
+  // methods are called after, so scrub the invalid flags from expected_header_.
+  ScrubFlagsOfHeader(&expected_header_);
+}
+
+void FramePartsCollector::TestExpectedHeader(const Http2FrameHeader& header) {
+  if (expected_header_set_) {
+    EXPECT_EQ(header, expected_header_);
+    expected_header_set_ = false;
+  }
+}
+
+Http2FrameDecoderListener* FramePartsCollector::StartFrame(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  EXPECT_FALSE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    current_frame_.reset(new FrameParts(header));
+  }
+  return current_frame();
+}
+
+Http2FrameDecoderListener* FramePartsCollector::StartAndEndFrame(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  EXPECT_FALSE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    current_frame_.reset(new FrameParts(header));
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+Http2FrameDecoderListener* FramePartsCollector::CurrentFrame() {
+  EXPECT_TRUE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    return &failing_listener_;
+  }
+  return current_frame();
+}
+
+Http2FrameDecoderListener* FramePartsCollector::EndFrame() {
+  EXPECT_TRUE(IsInProgress());
+  if (current_frame_ == nullptr) {
+    return &failing_listener_;
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+Http2FrameDecoderListener* FramePartsCollector::FrameError(
+    const Http2FrameHeader& header) {
+  TestExpectedHeader(header);
+  if (current_frame_ == nullptr) {
+    // The decoder may detect an error before making any calls to the listener
+    // regarding the frame, in which case current_frame_==nullptr and we need
+    // to create a FrameParts instance.
+    current_frame_.reset(new FrameParts(header));
+  } else {
+    // Similarly, the decoder may have made calls to the listener regarding the
+    // frame before detecting the error; for example, the DATA payload decoder
+    // calls OnDataStart before it can detect padding errors, hence before it
+    // can call OnPaddingTooLong.
+    EXPECT_EQ(header, current_frame_->frame_header);
+  }
+  Http2FrameDecoderListener* result = current_frame();
+  collected_frames_.push_back(std::move(current_frame_));
+  return result;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts_collector.h b/net/http2/decoder/frame_parts_collector.h
new file mode 100644
index 0000000..c411e48e
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector.h
@@ -0,0 +1,116 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
+
+// FramePartsCollector is a base class for Http2FrameDecoderListener
+// implementations that create one FrameParts instance for each decoded frame.
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/http2_frame_decoder_listener_test_util.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class FramePartsCollector : public FailingHttp2FrameDecoderListener {
+ public:
+  FramePartsCollector();
+  ~FramePartsCollector() override;
+
+  // Toss out the collected data.
+  void Reset();
+
+  // Returns true if has started recording the info for a frame and has not yet
+  // finished doing so.
+  bool IsInProgress() const { return current_frame_ != nullptr; }
+
+  // Returns the FrameParts instance into which we're currently recording
+  // callback info if IsInProgress, else nullptr.
+  const FrameParts* current_frame() const { return current_frame_.get(); }
+
+  // Returns the completely collected FrameParts instances.
+  const std::vector<std::unique_ptr<FrameParts>>& collected_frames() const {
+    return collected_frames_;
+  }
+
+  // Returns the number of completely collected FrameParts instances.
+  size_t size() const { return collected_frames_.size(); }
+
+  // Returns the n'th frame, where 0 is the oldest of the collected frames,
+  // and n==size() is the frame currently being collected, if there is one.
+  // Returns nullptr if the requested index is not valid.
+  const FrameParts* frame(size_t n) const;
+
+ protected:
+  // In support of OnFrameHeader, set the header that we expect to be used in
+  // the next call.
+  // TODO(jamessynge): Remove ExpectFrameHeader et al. once done with supporting
+  // SpdyFramer's exact states.
+  void ExpectFrameHeader(const Http2FrameHeader& header);
+
+  // For use in implementing On*Start methods of Http2FrameDecoderListener,
+  // returns a FrameParts instance, which will be newly created if
+  // IsInProgress==false (which the caller should ensure), else will be the
+  // current_frame(); never returns nullptr.
+  // If called when IsInProgress==true, a test failure will be recorded.
+  Http2FrameDecoderListener* StartFrame(const Http2FrameHeader& header);
+
+  // For use in implementing On* callbacks, such as OnPingAck, that are the only
+  // call expected for the frame being decoded; not for On*Start methods.
+  // Returns a FrameParts instance, which will be newly created if
+  // IsInProgress==false (which the caller should ensure), else will be the
+  // current_frame(); never returns nullptr.
+  // If called when IsInProgress==true, a test failure will be recorded.
+  Http2FrameDecoderListener* StartAndEndFrame(const Http2FrameHeader& header);
+
+  // If IsInProgress==true, returns the FrameParts into which the current
+  // frame is being recorded; else records a test failure and returns
+  // failing_listener_, which will record a test failure when any of its
+  // On* methods is called.
+  Http2FrameDecoderListener* CurrentFrame();
+
+  // For use in implementing On*End methods, pushes the current frame onto
+  // the vector of completed frames, and returns a pointer to it for recording
+  // the info in the final call. If IsInProgress==false, records a test failure
+  // and returns failing_listener_, which will record a test failure when any
+  // of its On* methods is called.
+  Http2FrameDecoderListener* EndFrame();
+
+  // For use in implementing OnPaddingTooLong and OnFrameSizeError, is
+  // equivalent to EndFrame() if IsInProgress==true, else equivalent to
+  // StartAndEndFrame().
+  Http2FrameDecoderListener* FrameError(const Http2FrameHeader& header);
+
+ private:
+  // Returns the mutable FrameParts instance into which we're currently
+  // recording callback info if IsInProgress, else nullptr.
+  FrameParts* current_frame() { return current_frame_.get(); }
+
+  // If expected header is set, verify that it matches the header param.
+  // TODO(jamessynge): Remove TestExpectedHeader et al. once done
+  // with supporting SpdyFramer's exact states.
+  void TestExpectedHeader(const Http2FrameHeader& header);
+
+  std::unique_ptr<FrameParts> current_frame_;
+  std::vector<std::unique_ptr<FrameParts>> collected_frames_;
+  FailingHttp2FrameDecoderListener failing_listener_;
+
+  // TODO(jamessynge): Remove expected_header_ et al. once done with supporting
+  // SpdyFramer's exact states.
+  Http2FrameHeader expected_header_;
+  bool expected_header_set_ = false;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_H_
diff --git a/net/http2/decoder/frame_parts_collector_listener.cc b/net/http2/decoder/frame_parts_collector_listener.cc
new file mode 100644
index 0000000..83a1ea34
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector_listener.cc
@@ -0,0 +1,230 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/frame_parts_collector_listener.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+bool FramePartsCollectorListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameHeader: " << header;
+  ExpectFrameHeader(header);
+  return true;
+}
+
+void FramePartsCollectorListener::OnDataStart(const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  StartFrame(header)->OnDataStart(header);
+}
+
+void FramePartsCollectorListener::OnDataPayload(const char* data, size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len;
+  CurrentFrame()->OnDataPayload(data, len);
+}
+
+void FramePartsCollectorListener::OnDataEnd() {
+  VLOG(1) << "OnDataEnd";
+  EndFrame()->OnDataEnd();
+}
+
+void FramePartsCollectorListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  StartFrame(header)->OnHeadersStart(header);
+}
+
+void FramePartsCollectorListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: " << priority;
+  CurrentFrame()->OnHeadersPriority(priority);
+}
+
+void FramePartsCollectorListener::OnHpackFragment(const char* data,
+                                                  size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len;
+  CurrentFrame()->OnHpackFragment(data, len);
+}
+
+void FramePartsCollectorListener::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd";
+  EndFrame()->OnHeadersEnd();
+}
+
+void FramePartsCollectorListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority_fields) {
+  VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+  StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
+}
+
+void FramePartsCollectorListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  StartFrame(header)->OnContinuationStart(header);
+}
+
+void FramePartsCollectorListener::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd";
+  EndFrame()->OnContinuationEnd();
+}
+
+void FramePartsCollectorListener::OnPadLength(size_t pad_length) {
+  VLOG(1) << "OnPadLength: " << pad_length;
+  CurrentFrame()->OnPadLength(pad_length);
+}
+
+void FramePartsCollectorListener::OnPadding(const char* padding,
+                                            size_t skipped_length) {
+  VLOG(1) << "OnPadding: " << skipped_length;
+  CurrentFrame()->OnPadding(padding, skipped_length);
+}
+
+void FramePartsCollectorListener::OnRstStream(const Http2FrameHeader& header,
+                                              Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+  StartAndEndFrame(header)->OnRstStream(header, error_code);
+}
+
+void FramePartsCollectorListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
+  EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
+  StartFrame(header)->OnSettingsStart(header);
+}
+
+void FramePartsCollectorListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+  CurrentFrame()->OnSetting(setting_fields);
+}
+
+void FramePartsCollectorListener::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd";
+  EndFrame()->OnSettingsEnd();
+}
+
+void FramePartsCollectorListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  StartAndEndFrame(header)->OnSettingsAck(header);
+}
+
+void FramePartsCollectorListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart header: " << header << "  promise: " << promise
+          << "  total_padding_length: " << total_padding_length;
+  EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
+  StartFrame(header)->OnPushPromiseStart(header, promise, total_padding_length);
+}
+
+void FramePartsCollectorListener::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd";
+  EndFrame()->OnPushPromiseEnd();
+}
+
+void FramePartsCollectorListener::OnPing(const Http2FrameHeader& header,
+                                         const Http2PingFields& ping) {
+  VLOG(1) << "OnPing: " << header << "; " << ping;
+  StartAndEndFrame(header)->OnPing(header, ping);
+}
+
+void FramePartsCollectorListener::OnPingAck(const Http2FrameHeader& header,
+                                            const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck: " << header << "; " << ping;
+  StartAndEndFrame(header)->OnPingAck(header, ping);
+}
+
+void FramePartsCollectorListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+  StartFrame(header)->OnGoAwayStart(header, goaway);
+}
+
+void FramePartsCollectorListener::OnGoAwayOpaqueData(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  CurrentFrame()->OnGoAwayOpaqueData(data, len);
+}
+
+void FramePartsCollectorListener::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd";
+  EndFrame()->OnGoAwayEnd();
+}
+
+void FramePartsCollectorListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t window_size_increment) {
+  VLOG(1) << "OnWindowUpdate: " << header
+          << "; window_size_increment=" << window_size_increment;
+  EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
+  StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
+}
+
+void FramePartsCollectorListener::OnAltSvcStart(const Http2FrameHeader& header,
+                                                size_t origin_length,
+                                                size_t value_length) {
+  VLOG(1) << "OnAltSvcStart header: " << header
+          << "; origin_length=" << origin_length
+          << "; value_length=" << value_length;
+  StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
+}
+
+void FramePartsCollectorListener::OnAltSvcOriginData(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  CurrentFrame()->OnAltSvcOriginData(data, len);
+}
+
+void FramePartsCollectorListener::OnAltSvcValueData(const char* data,
+                                                    size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  CurrentFrame()->OnAltSvcValueData(data, len);
+}
+
+void FramePartsCollectorListener::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd";
+  EndFrame()->OnAltSvcEnd();
+}
+
+void FramePartsCollectorListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  StartFrame(header)->OnUnknownStart(header);
+}
+
+void FramePartsCollectorListener::OnUnknownPayload(const char* data,
+                                                   size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  CurrentFrame()->OnUnknownPayload(data, len);
+}
+
+void FramePartsCollectorListener::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd";
+  EndFrame()->OnUnknownEnd();
+}
+
+void FramePartsCollectorListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "    missing_length: " << missing_length;
+  EndFrame()->OnPaddingTooLong(header, missing_length);
+}
+
+void FramePartsCollectorListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  FrameError(header)->OnFrameSizeError(header);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/frame_parts_collector_listener.h b/net/http2/decoder/frame_parts_collector_listener.h
new file mode 100644
index 0000000..89b7fed
--- /dev/null
+++ b/net/http2/decoder/frame_parts_collector_listener.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
+#define NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
+
+// FramePartsCollectorListener extends FramePartsCollector with an
+// implementation of every method of Http2FrameDecoderListener; it is
+// essentially the union of all the Listener classes in the tests of the
+// payload decoders (i.e. in ./payload_decoders/*_test.cc files), with the
+// addition of the OnFrameHeader method.
+// FramePartsCollectorListener supports tests of Http2FrameDecoder.
+
+#include <stddef.h>
+
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class FramePartsCollectorListener : public FramePartsCollector {
+ public:
+  FramePartsCollectorListener() {}
+  ~FramePartsCollectorListener() override {}
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority_fields) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t pad_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t window_size_increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_FRAME_PARTS_COLLECTOR_LISTENER_H_
diff --git a/net/http2/decoder/http2_frame_decoder.cc b/net/http2/decoder/http2_frame_decoder.cc
new file mode 100644
index 0000000..fe24f91
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder.cc
@@ -0,0 +1,426 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder.h"
+
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out, Http2FrameDecoder::State v) {
+  switch (v) {
+    case Http2FrameDecoder::State::kStartDecodingHeader:
+      return out << "kStartDecodingHeader";
+    case Http2FrameDecoder::State::kResumeDecodingHeader:
+      return out << "kResumeDecodingHeader";
+    case Http2FrameDecoder::State::kResumeDecodingPayload:
+      return out << "kResumeDecodingPayload";
+    case Http2FrameDecoder::State::kDiscardPayload:
+      return out << "kDiscardPayload";
+  }
+  return out << static_cast<int>(v);
+}
+
+Http2FrameDecoder::Http2FrameDecoder(Http2FrameDecoderListener* listener)
+    : state_(State::kStartDecodingHeader),
+      maximum_payload_size_(Http2SettingsInfo::DefaultMaxFrameSize()) {
+  set_listener(listener);
+}
+
+void Http2FrameDecoder::set_listener(Http2FrameDecoderListener* listener) {
+  if (listener == nullptr) {
+    listener = &no_op_listener_;
+  }
+  frame_decoder_state_.set_listener(listener);
+}
+
+Http2FrameDecoderListener* Http2FrameDecoder::listener() const {
+  return frame_decoder_state_.listener();
+}
+
+DecodeStatus Http2FrameDecoder::DecodeFrame(DecodeBuffer* db) {
+  DVLOG(2) << "Http2FrameDecoder::DecodeFrame state=" << state_;
+  switch (state_) {
+    case State::kStartDecodingHeader:
+      if (frame_decoder_state_.StartDecodingFrameHeader(db)) {
+        return StartDecodingPayload(db);
+      }
+      state_ = State::kResumeDecodingHeader;
+      return DecodeStatus::kDecodeInProgress;
+
+    case State::kResumeDecodingHeader:
+      if (frame_decoder_state_.ResumeDecodingFrameHeader(db)) {
+        return StartDecodingPayload(db);
+      }
+      return DecodeStatus::kDecodeInProgress;
+
+    case State::kResumeDecodingPayload:
+      return ResumeDecodingPayload(db);
+
+    case State::kDiscardPayload:
+      return DiscardPayload(db);
+  }
+
+  NOTREACHED();
+  return DecodeStatus::kDecodeError;
+}
+
+size_t Http2FrameDecoder::remaining_payload() const {
+  return frame_decoder_state_.remaining_payload();
+}
+
+uint32_t Http2FrameDecoder::remaining_padding() const {
+  return frame_decoder_state_.remaining_padding();
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) {
+  const Http2FrameHeader& header = frame_header();
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  if (!listener()->OnFrameHeader(header)) {
+    DVLOG(2) << "OnFrameHeader rejected the frame, will discard; header: "
+             << header;
+    state_ = State::kDiscardPayload;
+    frame_decoder_state_.InitializeRemainders();
+    return DecodeStatus::kDecodeError;
+  }
+
+  if (header.payload_length > maximum_payload_size_) {
+    DVLOG(2) << "Payload length is greater than allowed: "
+             << header.payload_length << " > " << maximum_payload_size_
+             << "\n   header: " << header;
+    state_ = State::kDiscardPayload;
+    frame_decoder_state_.InitializeRemainders();
+    listener()->OnFrameSizeError(header);
+    return DecodeStatus::kDecodeError;
+  }
+
+  // The decode buffer can extend across many frames. Make sure that the
+  // buffer we pass to the start method that is specific to the frame type
+  // does not exend beyond this frame.
+  DecodeBufferSubset subset(db, header.payload_length);
+  DecodeStatus status;
+  switch (header.type) {
+    case Http2FrameType::DATA:
+      status = StartDecodingDataPayload(&subset);
+      break;
+
+    case Http2FrameType::HEADERS:
+      status = StartDecodingHeadersPayload(&subset);
+      break;
+
+    case Http2FrameType::PRIORITY:
+      status = StartDecodingPriorityPayload(&subset);
+      break;
+
+    case Http2FrameType::RST_STREAM:
+      status = StartDecodingRstStreamPayload(&subset);
+      break;
+
+    case Http2FrameType::SETTINGS:
+      status = StartDecodingSettingsPayload(&subset);
+      break;
+
+    case Http2FrameType::PUSH_PROMISE:
+      status = StartDecodingPushPromisePayload(&subset);
+      break;
+
+    case Http2FrameType::PING:
+      status = StartDecodingPingPayload(&subset);
+      break;
+
+    case Http2FrameType::GOAWAY:
+      status = StartDecodingGoAwayPayload(&subset);
+      break;
+
+    case Http2FrameType::WINDOW_UPDATE:
+      status = StartDecodingWindowUpdatePayload(&subset);
+      break;
+
+    case Http2FrameType::CONTINUATION:
+      status = StartDecodingContinuationPayload(&subset);
+      break;
+
+    case Http2FrameType::ALTSVC:
+      status = StartDecodingAltSvcPayload(&subset);
+      break;
+
+    default:
+      status = StartDecodingUnknownPayload(&subset);
+      break;
+  }
+
+  if (status == DecodeStatus::kDecodeDone) {
+    state_ = State::kStartDecodingHeader;
+    return status;
+  } else if (status == DecodeStatus::kDecodeInProgress) {
+    state_ = State::kResumeDecodingPayload;
+    return status;
+  } else {
+    state_ = State::kDiscardPayload;
+    return status;
+  }
+}
+
+DecodeStatus Http2FrameDecoder::ResumeDecodingPayload(DecodeBuffer* db) {
+  // The decode buffer can extend across many frames. Make sure that the
+  // buffer we pass to the start method that is specific to the frame type
+  // does not exend beyond this frame.
+  size_t remaining = frame_decoder_state_.remaining_total_payload();
+  DCHECK_LE(remaining, frame_header().payload_length);
+  DecodeBufferSubset subset(db, remaining);
+  DecodeStatus status;
+  switch (frame_header().type) {
+    case Http2FrameType::DATA:
+      status = ResumeDecodingDataPayload(&subset);
+      break;
+
+    case Http2FrameType::HEADERS:
+      status = ResumeDecodingHeadersPayload(&subset);
+      break;
+
+    case Http2FrameType::PRIORITY:
+      status = ResumeDecodingPriorityPayload(&subset);
+      break;
+
+    case Http2FrameType::RST_STREAM:
+      status = ResumeDecodingRstStreamPayload(&subset);
+      break;
+
+    case Http2FrameType::SETTINGS:
+      status = ResumeDecodingSettingsPayload(&subset);
+      break;
+
+    case Http2FrameType::PUSH_PROMISE:
+      status = ResumeDecodingPushPromisePayload(&subset);
+      break;
+
+    case Http2FrameType::PING:
+      status = ResumeDecodingPingPayload(&subset);
+      break;
+
+    case Http2FrameType::GOAWAY:
+      status = ResumeDecodingGoAwayPayload(&subset);
+      break;
+
+    case Http2FrameType::WINDOW_UPDATE:
+      status = ResumeDecodingWindowUpdatePayload(&subset);
+      break;
+
+    case Http2FrameType::CONTINUATION:
+      status = ResumeDecodingContinuationPayload(&subset);
+      break;
+
+    case Http2FrameType::ALTSVC:
+      status = ResumeDecodingAltSvcPayload(&subset);
+      break;
+
+    default:
+      status = ResumeDecodingUnknownPayload(&subset);
+      break;
+  }
+
+  if (status == DecodeStatus::kDecodeDone) {
+    state_ = State::kStartDecodingHeader;
+    return status;
+  } else if (status == DecodeStatus::kDecodeInProgress) {
+    return status;
+  } else {
+    state_ = State::kDiscardPayload;
+    return status;
+  }
+}
+
+// Clear any of the flags in the frame header that aren't set in valid_flags.
+void Http2FrameDecoder::RetainFlags(uint8_t valid_flags) {
+  frame_decoder_state_.RetainFlags(valid_flags);
+}
+
+// Clear all of the flags in the frame header; for use with frame types that
+// don't define any flags, such as WINDOW_UPDATE.
+void Http2FrameDecoder::ClearFlags() {
+  frame_decoder_state_.ClearFlags();
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingAltSvcPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return altsvc_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                      db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingAltSvcPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return altsvc_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingContinuationPayload(
+    DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_HEADERS);
+  return continuation_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingContinuationPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return continuation_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingDataPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED);
+  return data_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingDataPayload(DecodeBuffer* db) {
+  return data_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingGoAwayPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return goaway_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                      db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingGoAwayPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return goaway_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingHeadersPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_STREAM |
+              Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED |
+              Http2FrameFlag::FLAG_PRIORITY);
+  return headers_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingHeadersPayload(DecodeBuffer* db) {
+  DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
+            frame_header().payload_length);
+  return headers_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPingPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_ACK);
+  return ping_payload_decoder_.StartDecodingPayload(&frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPingPayload(DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return ping_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPriorityPayload(DecodeBuffer* db) {
+  ClearFlags();
+  return priority_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPriorityPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return priority_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                         db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingPushPromisePayload(
+    DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED);
+  return push_promise_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingPushPromisePayload(
+    DecodeBuffer* db) {
+  DCHECK_LE(frame_decoder_state_.remaining_payload_and_padding(),
+            frame_header().payload_length);
+  return push_promise_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingRstStreamPayload(
+    DecodeBuffer* db) {
+  ClearFlags();
+  return rst_stream_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                          db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingRstStreamPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return rst_stream_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingSettingsPayload(DecodeBuffer* db) {
+  RetainFlags(Http2FrameFlag::FLAG_ACK);
+  return settings_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingSettingsPayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return settings_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                         db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingUnknownPayload(DecodeBuffer* db) {
+  // We don't known what type of frame this is, so we don't know which flags
+  // are valid, so we don't touch them.
+  return unknown_payload_decoder_.StartDecodingPayload(&frame_decoder_state_,
+                                                       db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingUnknownPayload(DecodeBuffer* db) {
+  // We don't known what type of frame this is, so we treat it as not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return unknown_payload_decoder_.ResumeDecodingPayload(&frame_decoder_state_,
+                                                        db);
+}
+
+DecodeStatus Http2FrameDecoder::StartDecodingWindowUpdatePayload(
+    DecodeBuffer* db) {
+  ClearFlags();
+  return window_update_payload_decoder_.StartDecodingPayload(
+      &frame_decoder_state_, db);
+}
+DecodeStatus Http2FrameDecoder::ResumeDecodingWindowUpdatePayload(
+    DecodeBuffer* db) {
+  // The frame is not paddable.
+  DCHECK_EQ(frame_decoder_state_.remaining_total_payload(),
+            frame_decoder_state_.remaining_payload());
+  return window_update_payload_decoder_.ResumeDecodingPayload(
+      &frame_decoder_state_, db);
+}
+
+DecodeStatus Http2FrameDecoder::DiscardPayload(DecodeBuffer* db) {
+  DVLOG(2) << "remaining_payload=" << frame_decoder_state_.remaining_payload_
+           << "; remaining_padding=" << frame_decoder_state_.remaining_padding_;
+  frame_decoder_state_.remaining_payload_ +=
+      frame_decoder_state_.remaining_padding_;
+  frame_decoder_state_.remaining_padding_ = 0;
+  const size_t avail = frame_decoder_state_.AvailablePayload(db);
+  DVLOG(2) << "avail=" << avail;
+  if (avail > 0) {
+    frame_decoder_state_.ConsumePayload(avail);
+    db->AdvanceCursor(avail);
+  }
+  if (frame_decoder_state_.remaining_payload_ == 0) {
+    state_ = State::kStartDecodingHeader;
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder.h b/net/http2/decoder/http2_frame_decoder.h
new file mode 100644
index 0000000..f440993
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder.h
@@ -0,0 +1,203 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
+
+// Http2FrameDecoder decodes the available input until it reaches the end of
+// the input or it reaches the end of the first frame in the input.
+// Note that Http2FrameDecoder does only minimal validation; for example,
+// stream ids are not checked, nor is the sequence of frames such as
+// CONTINUATION frame placement.
+//
+// Http2FrameDecoder enters state kError once it has called the listener's
+// OnFrameSizeError or OnPaddingTooLong methods, and at this time has no
+// provision for leaving that state. While the HTTP/2 spec (RFC7540) allows
+// for some such errors to be considered as just stream errors in some cases,
+// this implementation treats them all as connection errors.
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class Http2FrameDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE Http2FrameDecoder {
+ public:
+  explicit Http2FrameDecoder(Http2FrameDecoderListener* listener);
+  Http2FrameDecoder() : Http2FrameDecoder(nullptr) {}
+
+  // The decoder will call the listener's methods as it decodes a frame.
+  void set_listener(Http2FrameDecoderListener* listener);
+  Http2FrameDecoderListener* listener() const;
+
+  // The decoder will reject frame's whose payload
+  // length field exceeds the maximum payload size.
+  void set_maximum_payload_size(size_t v) { maximum_payload_size_ = v; }
+  size_t maximum_payload_size() const { return maximum_payload_size_; }
+
+  // Decodes the input up to the next frame boundary (i.e. at most one frame).
+  //
+  // Returns kDecodeDone if it decodes the final byte of a frame, OR if there
+  // is no input and it is awaiting the start of a new frame (e.g. if this
+  // is the first call to DecodeFrame, or if the previous call returned
+  // kDecodeDone).
+  //
+  // Returns kDecodeInProgress if it decodes all of the decode buffer, but has
+  // not reached the end of the frame.
+  //
+  // Returns kDecodeError if the frame's padding or length wasn't valid (i.e. if
+  // the decoder called either the listener's OnPaddingTooLong or
+  // OnFrameSizeError method).
+  DecodeStatus DecodeFrame(DecodeBuffer* db);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Methods that support Http2FrameDecoderAdapter.
+
+  // Is the remainder of the frame's payload being discarded?
+  bool IsDiscardingPayload() const { return state_ == State::kDiscardPayload; }
+
+  // Returns the number of bytes of the frame's payload that remain to be
+  // decoded, excluding any trailing padding. This method must only be called
+  // after the frame header has been decoded AND DecodeFrame has returned
+  // kDecodeInProgress.
+  size_t remaining_payload() const;
+
+  // Returns the number of bytes of trailing padding after the payload that
+  // remain to be decoded. This method must only be called if the frame type
+  // allows padding, and after the frame header has been decoded AND
+  // DecodeFrame has returned. Will return 0 if the Pad Length field has not
+  // yet been decoded.
+  uint32_t remaining_padding() const;
+
+ private:
+  enum class State {
+    // Ready to start decoding a new frame's header.
+    kStartDecodingHeader,
+    // Was in state kStartDecodingHeader, but unable to read the entire frame
+    // header, so needs more input to complete decoding the header.
+    kResumeDecodingHeader,
+
+    // Have decoded the frame header, and started decoding the available bytes
+    // of the frame's payload, but need more bytes to finish the job.
+    kResumeDecodingPayload,
+
+    // Decoding of the most recently started frame resulted in an error:
+    // OnPaddingTooLong or OnFrameSizeError was called to indicate that the
+    // decoder detected a problem, or OnFrameHeader returned false, indicating
+    // that the listener detected a problem. Regardless of which, the decoder
+    // will stay in state kDiscardPayload until it has been passed the rest
+    // of the bytes of the frame's payload that it hasn't yet seen, after
+    // which it will be ready to decode another frame.
+    kDiscardPayload,
+  };
+
+  friend class test::Http2FrameDecoderPeer;
+  friend std::ostream& operator<<(std::ostream& out, State v);
+
+  DecodeStatus StartDecodingPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPayload(DecodeBuffer* db);
+  DecodeStatus DiscardPayload(DecodeBuffer* db);
+
+  const Http2FrameHeader& frame_header() const {
+    return frame_decoder_state_.frame_header();
+  }
+
+  // Clear any of the flags in the frame header that aren't set in valid_flags.
+  void RetainFlags(uint8_t valid_flags);
+
+  // Clear all of the flags in the frame header; for use with frame types that
+  // don't define any flags, such as WINDOW_UPDATE.
+  void ClearFlags();
+
+  // These methods call the StartDecodingPayload() method of the frame type's
+  // payload decoder, after first clearing invalid flags in the header. The
+  // caller must ensure that the decode buffer does not extend beyond the
+  // end of the payload (handled by Http2FrameDecoder::StartDecodingPayload).
+  DecodeStatus StartDecodingAltSvcPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingContinuationPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingDataPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingGoAwayPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingHeadersPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPingPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPriorityPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingPushPromisePayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingRstStreamPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingSettingsPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingUnknownPayload(DecodeBuffer* db);
+  DecodeStatus StartDecodingWindowUpdatePayload(DecodeBuffer* db);
+
+  // These methods call the ResumeDecodingPayload() method of the frame type's
+  // payload decoder; they are called only if the preceding call to the
+  // corresponding Start method (above) returned kDecodeInProgress, as did any
+  // subsequent calls to the resume method.
+  // Unlike the Start methods, the decode buffer may extend beyond the
+  // end of the payload, so the method will create a DecodeBufferSubset
+  // before calling the ResumeDecodingPayload method of the frame type's
+  // payload decoder.
+  DecodeStatus ResumeDecodingAltSvcPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingContinuationPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingDataPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingGoAwayPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingHeadersPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPingPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPriorityPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingPushPromisePayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingRstStreamPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingSettingsPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingUnknownPayload(DecodeBuffer* db);
+  DecodeStatus ResumeDecodingWindowUpdatePayload(DecodeBuffer* db);
+
+  FrameDecoderState frame_decoder_state_;
+
+  // We only need one payload decoder at a time, so they share the same storage.
+  union {
+    AltSvcPayloadDecoder altsvc_payload_decoder_;
+    ContinuationPayloadDecoder continuation_payload_decoder_;
+    DataPayloadDecoder data_payload_decoder_;
+    GoAwayPayloadDecoder goaway_payload_decoder_;
+    HeadersPayloadDecoder headers_payload_decoder_;
+    PingPayloadDecoder ping_payload_decoder_;
+    PriorityPayloadDecoder priority_payload_decoder_;
+    PushPromisePayloadDecoder push_promise_payload_decoder_;
+    RstStreamPayloadDecoder rst_stream_payload_decoder_;
+    SettingsPayloadDecoder settings_payload_decoder_;
+    UnknownPayloadDecoder unknown_payload_decoder_;
+    WindowUpdatePayloadDecoder window_update_payload_decoder_;
+  };
+
+  State state_;
+  size_t maximum_payload_size_;
+
+  // Listener used whenever caller passes nullptr to set_listener.
+  Http2FrameDecoderNoOpListener no_op_listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(Http2FrameDecoder);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_H_
diff --git a/net/http2/decoder/http2_frame_decoder_listener.cc b/net/http2/decoder/http2_frame_decoder_listener.cc
new file mode 100644
index 0000000..bf62ae4f
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+
+namespace net {
+
+bool Http2FrameDecoderNoOpListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  return true;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder_listener.h b/net/http2/decoder/http2_frame_decoder_listener.h
new file mode 100644
index 0000000..3345ab6
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener.h
@@ -0,0 +1,356 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
+
+// Http2FrameDecoderListener is the interface which the HTTP/2 decoder uses
+// to report the decoded frames to a listener.
+//
+// The general design is to assume that the listener will copy the data it needs
+// (e.g. frame headers) and will keep track of the implicit state of the
+// decoding process (i.e. the decoder maintains just the information it needs in
+// order to perform the decoding). Therefore, the parameters are just those with
+// (potentially) new data, not previously provided info about the current frame.
+//
+// The calls are described as if they are made in quick succession, i.e. one
+// after another, but of course the decoder needs input to decode, and the
+// decoder will only call the listener once the necessary input has been
+// provided. For example: OnDataStart can only be called once the 9 bytes of
+// of an HTTP/2 common frame header have been received. The decoder will call
+// the listener methods as soon as possible to avoid almost all buffering.
+//
+// The listener interface is designed so that it is possible to exactly
+// reconstruct the serialized frames, with the exception of reserved bits,
+// including in the frame header's flags and stream_id fields, which will have
+// been cleared before the methods below are called.
+
+#include <stddef.h>
+
+#include <type_traits>
+
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// TODO(jamessynge): Consider sorting the methods by frequency of call, if that
+// helps at all.
+class Http2FrameDecoderListener {
+ public:
+  Http2FrameDecoderListener() {}
+  virtual ~Http2FrameDecoderListener() {}
+
+  // Called once the common frame header has been decoded for any frame, and
+  // before any of the methods below, which will also be called. This method is
+  // included in this interface only for the purpose of supporting SpdyFramer
+  // semantics via an adapter. This is the only method that has a non-void
+  // return type, and this is just so that Http2FrameDecoderAdapter (called
+  // from SpdyFramer) can more readily pass existing tests that expect decoding
+  // to stop if the headers alone indicate an error. Return false to stop
+  // decoding just after decoding the header, else return true to continue
+  // decoding.
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  virtual bool OnFrameHeader(const Http2FrameHeader& header) = 0;
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  // Called once the common frame header has been decoded for a DATA frame,
+  // before examining the frame's payload, after which:
+  //   OnPadLength will be called if header.IsPadded() is true, i.e. if the
+  //     PADDED flag is set;
+  //   OnDataPayload will be called as the non-padding portion of the payload
+  //     is available until all of it has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero;
+  //   OnDataEnd will be called last. If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnDataStart.
+  virtual void OnDataStart(const Http2FrameHeader& header) = 0;
+
+  // Called when the next non-padding portion of a DATA frame's payload is
+  // received.
+  // |data| The start of |len| bytes of data.
+  // |len| The length of the data buffer. Maybe zero in some cases, which does
+  //       not mean anything special.
+  virtual void OnDataPayload(const char* data, size_t len) = 0;
+
+  // Called after an entire DATA frame has been received.
+  // If header.IsEndStream() == true, this is the last data for the stream.
+  virtual void OnDataEnd() = 0;
+
+  // Called once the common frame header has been decoded for a HEADERS frame,
+  // before examining the frame's payload, after which:
+  //   OnPadLength will be called if header.IsPadded() is true, i.e. if the
+  //     PADDED flag is set;
+  //   OnHeadersPriority will be called if header.HasPriority() is true, i.e. if
+  //     the frame has the PRIORITY flag;
+  //   OnHpackFragment as the remainder of the non-padding payload is available
+  //     until all if has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero;
+  //   OnHeadersEnd will be called last; If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnHeadersStart;
+  //     OnHeadersEnd indicates the end of the HPACK block only if the frame
+  //     header had the END_HEADERS flag set, else the END_HEADERS should be
+  //     looked for on a subsequent CONTINUATION frame.
+  virtual void OnHeadersStart(const Http2FrameHeader& header) = 0;
+
+  // Called when a HEADERS frame is received with the PRIORITY flag set and
+  // the priority fields have been decoded.
+  virtual void OnHeadersPriority(
+      const Http2PriorityFields& priority_fields) = 0;
+
+  // Called when a fragment (i.e. some or all of an HPACK Block) is received;
+  // this may be part of a HEADERS, PUSH_PROMISE or CONTINUATION frame.
+  // |data| The start of |len| bytes of data.
+  // |len| The length of the data buffer. Maybe zero in some cases, which does
+  //       not mean anything special, except that it simplified the decoder.
+  virtual void OnHpackFragment(const char* data, size_t len) = 0;
+
+  // Called after an entire HEADERS frame has been received. The frame is the
+  // end of the HEADERS if the END_HEADERS flag is set; else there should be
+  // CONTINUATION frames after this frame.
+  virtual void OnHeadersEnd() = 0;
+
+  // Called when an entire PRIORITY frame has been decoded.
+  virtual void OnPriorityFrame(const Http2FrameHeader& header,
+                               const Http2PriorityFields& priority_fields) = 0;
+
+  // Called once the common frame header has been decoded for a CONTINUATION
+  // frame, before examining the frame's payload, after which:
+  //   OnHpackFragment as the frame's payload is available until all of it
+  //     has been provided;
+  //   OnContinuationEnd will be called last; If the frame has no payload,
+  //     then this will be called immediately after OnContinuationStart;
+  //     the HPACK block is at an end if and only if the frame header passed
+  //     to OnContinuationStart had the END_HEADERS flag set.
+  virtual void OnContinuationStart(const Http2FrameHeader& header) = 0;
+
+  // Called after an entire CONTINUATION frame has been received. The frame is
+  // the end of the HEADERS if the END_HEADERS flag is set.
+  virtual void OnContinuationEnd() = 0;
+
+  // Called when Pad Length field has been read. Applies to DATA and HEADERS
+  // frames. For PUSH_PROMISE frames, the Pad Length + 1 is provided in the
+  // OnPushPromiseStart call as total_padding_length.
+  virtual void OnPadLength(size_t pad_length) = 0;
+
+  // Called when padding is skipped over.
+  virtual void OnPadding(const char* padding, size_t skipped_length) = 0;
+
+  // Called when an entire RST_STREAM frame has been decoded.
+  // This is the only callback for RST_STREAM frames.
+  virtual void OnRstStream(const Http2FrameHeader& header,
+                           Http2ErrorCode error_code) = 0;
+
+  // Called once the common frame header has been decoded for a SETTINGS frame
+  // without the ACK flag, before examining the frame's payload, after which:
+  //   OnSetting will be called in turn for each pair of settings parameter and
+  //     value found in the payload;
+  //   OnSettingsEnd will be called last; If the frame has no payload,
+  //     then this will be called immediately after OnSettingsStart.
+  // The frame header is passed so that the caller can check the stream_id,
+  // which should be zero, but that hasn't been checked by the decoder.
+  virtual void OnSettingsStart(const Http2FrameHeader& header) = 0;
+
+  // Called for each setting parameter and value within a SETTINGS frame.
+  virtual void OnSetting(const Http2SettingFields& setting_fields) = 0;
+
+  // Called after parsing the complete payload of SETTINGS frame (non-ACK).
+  virtual void OnSettingsEnd() = 0;
+
+  // Called when an entire SETTINGS frame, with the ACK flag, has been decoded.
+  virtual void OnSettingsAck(const Http2FrameHeader& header) = 0;
+
+  // Called just before starting to process the HPACK block of a PUSH_PROMISE
+  // frame. The Pad Length field has already been decoded at this point, so
+  // OnPadLength will not be called; note that total_padding_length is Pad
+  // Length + 1. After OnPushPromiseStart:
+  //   OnHpackFragment as the remainder of the non-padding payload is available
+  //     until all if has been provided;
+  //   OnPadding will be called if the frame is padded AND the Pad Length field
+  //     is greater than zero (i.e. total_padding_length > 1);
+  //   OnPushPromiseEnd will be called last; If the frame is unpadded and has no
+  //     payload, then this will be called immediately after OnPushPromiseStart.
+  virtual void OnPushPromiseStart(const Http2FrameHeader& header,
+                                  const Http2PushPromiseFields& promise,
+                                  size_t total_padding_length) = 0;
+
+  // Called after all of the HPACK block fragment and padding of a PUSH_PROMISE
+  // has been decoded and delivered to the listener. This call indicates the end
+  // of the HPACK block if and only if the frame header had the END_HEADERS flag
+  // set (i.e. header.IsEndHeaders() is true); otherwise the next block must be
+  // a CONTINUATION frame with the same stream id (not the same promised stream
+  // id).
+  virtual void OnPushPromiseEnd() = 0;
+
+  // Called when an entire PING frame, without the ACK flag, has been decoded.
+  virtual void OnPing(const Http2FrameHeader& header,
+                      const Http2PingFields& ping) = 0;
+
+  // Called when an entire PING frame, with the ACK flag, has been decoded.
+  virtual void OnPingAck(const Http2FrameHeader& header,
+                         const Http2PingFields& ping) = 0;
+
+  // Called after parsing a GOAWAY frame's header and fixed size fields, after
+  // which:
+  //   OnGoAwayOpaqueData will be called as opaque data of the payload becomes
+  //     available to the decoder, until all of it has been provided to the
+  //     listener;
+  //   OnGoAwayEnd will be called last, after all the opaque data has been
+  //     provided to the listener; if there is no opaque data, then OnGoAwayEnd
+  //     will be called immediately after OnGoAwayStart.
+  virtual void OnGoAwayStart(const Http2FrameHeader& header,
+                             const Http2GoAwayFields& goaway) = 0;
+
+  // Called when the next portion of a GOAWAY frame's payload is received.
+  // |data| The start of |len| bytes of opaque data.
+  // |len| The length of the opaque data buffer. Maybe zero in some cases,
+  //       which does not mean anything special.
+  virtual void OnGoAwayOpaqueData(const char* data, size_t len) = 0;
+
+  // Called after finishing decoding all of a GOAWAY frame.
+  virtual void OnGoAwayEnd() = 0;
+
+  // Called when an entire WINDOW_UPDATE frame has been decoded. The
+  // window_size_increment is required to be non-zero, but that has not been
+  // checked. If header.stream_id==0, the connection's flow control window is
+  // being increased, else the specified stream's flow control is being
+  // increased.
+  virtual void OnWindowUpdate(const Http2FrameHeader& header,
+                              uint32_t window_size_increment) = 0;
+
+  // Called when an ALTSVC frame header and origin length have been parsed.
+  // Either or both lengths may be zero. After OnAltSvcStart:
+  //   OnAltSvcOriginData will be called until all of the (optional) Origin
+  //     has been provided;
+  //   OnAltSvcValueData will be called until all of the Alt-Svc-Field-Value
+  //     has been provided;
+  //   OnAltSvcEnd will called last, after all of the origin and
+  //     Alt-Svc-Field-Value have been delivered to the listener.
+  virtual void OnAltSvcStart(const Http2FrameHeader& header,
+                             size_t origin_length,
+                             size_t value_length) = 0;
+
+  // Called when decoding the (optional) origin of an ALTSVC;
+  // the field is uninterpreted.
+  virtual void OnAltSvcOriginData(const char* data, size_t len) = 0;
+
+  // Called when decoding the Alt-Svc-Field-Value of an ALTSVC;
+  // the field is uninterpreted.
+  virtual void OnAltSvcValueData(const char* data, size_t len) = 0;
+
+  // Called after decoding all of a ALTSVC frame and providing to the listener
+  // via the above methods.
+  virtual void OnAltSvcEnd() = 0;
+
+  // Called when the common frame header has been decoded, but the frame type
+  // is unknown, after which:
+  //   OnUnknownPayload is called as the payload of the frame is provided to the
+  //     decoder, until all of the payload has been decoded;
+  //   OnUnknownEnd will called last, after the entire frame of the unknown type
+  //     has been decoded and provided to the listener.
+  virtual void OnUnknownStart(const Http2FrameHeader& header) = 0;
+
+  // Called when the payload of an unknown frame type is received.
+  // |data| A buffer containing the data received.
+  // |len| The length of the data buffer.
+  virtual void OnUnknownPayload(const char* data, size_t len) = 0;
+
+  // Called after decoding all of the payload of an unknown frame type.
+  virtual void OnUnknownEnd() = 0;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Below here are events indicating a problem has been detected during
+  // decoding (i.e. the received frames are malformed in some way).
+
+  // Padding field (uint8) has a value that is too large (i.e. the amount of
+  // padding is greater than the remainder of the payload that isn't required).
+  // From RFC Section 6.1, DATA:
+  //     If the length of the padding is the length of the frame payload or
+  //     greater, the recipient MUST treat this as a connection error
+  //     (Section 5.4.1) of type PROTOCOL_ERROR.
+  // The same is true for HEADERS and PUSH_PROMISE.
+  virtual void OnPaddingTooLong(const Http2FrameHeader& header,
+                                size_t missing_length) = 0;
+
+  // Frame size error. Depending upon the effected frame, this may or may not
+  // require terminating the connection, though that is probably the best thing
+  // to do.
+  // From RFC Section 4.2, Frame Size:
+  //     An endpoint MUST send an error code of FRAME_SIZE_ERROR if a frame
+  //     exceeds the size defined in SETTINGS_MAX_FRAME_SIZE, exceeds any limit
+  //     defined for the frame type, or is too small to contain mandatory frame
+  //     data. A frame size error in a frame that could alter the state of the
+  //     the entire connection MUST be treated as a connection error
+  //     (Section 5.4.1); this includes any frame carrying a header block
+  //     (Section 4.3) (that is, HEADERS, PUSH_PROMISE, and CONTINUATION),
+  //     SETTINGS, and any frame with a stream identifier of 0.
+  virtual void OnFrameSizeError(const Http2FrameHeader& header) = 0;
+};
+
+// Do nothing for each call. Useful for ignoring a frame that is invalid.
+class Http2FrameDecoderNoOpListener : public Http2FrameDecoderListener {
+ public:
+  Http2FrameDecoderNoOpListener() {}
+  ~Http2FrameDecoderNoOpListener() override {}
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+
+  void OnDataStart(const Http2FrameHeader& header) override {}
+  void OnDataPayload(const char* data, size_t len) override {}
+  void OnDataEnd() override {}
+  void OnHeadersStart(const Http2FrameHeader& header) override {}
+  void OnHeadersPriority(const Http2PriorityFields& priority) override {}
+  void OnHpackFragment(const char* data, size_t len) override {}
+  void OnHeadersEnd() override {}
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override {}
+  void OnContinuationStart(const Http2FrameHeader& header) override {}
+  void OnContinuationEnd() override {}
+  void OnPadLength(size_t trailing_length) override {}
+  void OnPadding(const char* padding, size_t skipped_length) override {}
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override {}
+  void OnSettingsStart(const Http2FrameHeader& header) override {}
+  void OnSetting(const Http2SettingFields& setting_fields) override {}
+  void OnSettingsEnd() override {}
+  void OnSettingsAck(const Http2FrameHeader& header) override {}
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override {}
+  void OnPushPromiseEnd() override {}
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override {}
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override {}
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override {}
+  void OnGoAwayOpaqueData(const char* data, size_t len) override {}
+  void OnGoAwayEnd() override {}
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override {}
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override {}
+  void OnAltSvcOriginData(const char* data, size_t len) override {}
+  void OnAltSvcValueData(const char* data, size_t len) override {}
+  void OnAltSvcEnd() override {}
+  void OnUnknownStart(const Http2FrameHeader& header) override {}
+  void OnUnknownPayload(const char* data, size_t len) override {}
+  void OnUnknownEnd() override {}
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {}
+  void OnFrameSizeError(const Http2FrameHeader& header) override {}
+};
+
+static_assert(!std::is_abstract<Http2FrameDecoderNoOpListener>(),
+              "Http2FrameDecoderNoOpListener ought to be concrete.");
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_H_
diff --git a/net/http2/decoder/http2_frame_decoder_listener_test_util.cc b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
new file mode 100644
index 0000000..9159b6cd
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener_test_util.cc
@@ -0,0 +1,485 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder_listener_test_util.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+FailingHttp2FrameDecoderListener::FailingHttp2FrameDecoderListener() {}
+FailingHttp2FrameDecoderListener::~FailingHttp2FrameDecoderListener() {}
+
+bool FailingHttp2FrameDecoderListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  ADD_FAILURE() << "OnFrameHeader: " << header;
+  return false;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnDataStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataPayload(const char* data,
+                                                     size_t len) {
+  FAIL() << "OnDataPayload: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnDataEnd() {
+  FAIL() << "OnDataEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnHeadersStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  FAIL() << "OnHeadersPriority: " << priority;
+}
+
+void FailingHttp2FrameDecoderListener::OnHpackFragment(const char* data,
+                                                       size_t len) {
+  FAIL() << "OnHpackFragment: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnHeadersEnd() {
+  FAIL() << "OnHeadersEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority) {
+  FAIL() << "OnPriorityFrame: " << header << "; priority: " << priority;
+}
+
+void FailingHttp2FrameDecoderListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnContinuationStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnContinuationEnd() {
+  FAIL() << "OnContinuationEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
+  FAIL() << "OnPadLength: trailing_length=" << trailing_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnPadding(const char* padding,
+                                                 size_t skipped_length) {
+  FAIL() << "OnPadding: skipped_length=" << skipped_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnRstStream(
+    const Http2FrameHeader& header,
+    Http2ErrorCode error_code) {
+  FAIL() << "OnRstStream: " << header << "; code=" << error_code;
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnSettingsStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  FAIL() << "OnSetting: " << setting_fields;
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsEnd() {
+  FAIL() << "OnSettingsEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnSettingsAck: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  FAIL() << "OnPushPromiseStart: " << header << "; promise: " << promise
+         << "; total_padding_length: " << total_padding_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnPushPromiseEnd() {
+  FAIL() << "OnPushPromiseEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
+                                              const Http2PingFields& ping) {
+  FAIL() << "OnPing: " << header << "; ping: " << ping;
+}
+
+void FailingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
+                                                 const Http2PingFields& ping) {
+  FAIL() << "OnPingAck: " << header << "; ping: " << ping;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  FAIL() << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayOpaqueData(const char* data,
+                                                          size_t len) {
+  FAIL() << "OnGoAwayOpaqueData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnGoAwayEnd() {
+  FAIL() << "OnGoAwayEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t increment) {
+  FAIL() << "OnWindowUpdate: " << header << "; increment=" << increment;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcStart(
+    const Http2FrameHeader& header,
+    size_t origin_length,
+    size_t value_length) {
+  FAIL() << "OnAltSvcStart: " << header << "; origin_length: " << origin_length
+         << "; value_length: " << value_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
+                                                          size_t len) {
+  FAIL() << "OnAltSvcOriginData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcValueData(const char* data,
+                                                         size_t len) {
+  FAIL() << "OnAltSvcValueData: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnAltSvcEnd() {
+  FAIL() << "OnAltSvcEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnUnknownStart: " << header;
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownPayload(const char* data,
+                                                        size_t len) {
+  FAIL() << "OnUnknownPayload: len=" << len;
+}
+
+void FailingHttp2FrameDecoderListener::OnUnknownEnd() {
+  FAIL() << "OnUnknownEnd";
+}
+
+void FailingHttp2FrameDecoderListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  FAIL() << "OnPaddingTooLong: " << header
+         << "; missing_length: " << missing_length;
+}
+
+void FailingHttp2FrameDecoderListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  FAIL() << "OnFrameSizeError: " << header;
+}
+
+LoggingHttp2FrameDecoderListener::LoggingHttp2FrameDecoderListener()
+    : wrapped_(nullptr) {}
+LoggingHttp2FrameDecoderListener::LoggingHttp2FrameDecoderListener(
+    Http2FrameDecoderListener* wrapped)
+    : wrapped_(wrapped) {}
+LoggingHttp2FrameDecoderListener::~LoggingHttp2FrameDecoderListener() {}
+
+bool LoggingHttp2FrameDecoderListener::OnFrameHeader(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameHeader: " << header;
+  if (wrapped_ != nullptr) {
+    return wrapped_->OnFrameHeader(header);
+  }
+  return true;
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnDataStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataPayload(const char* data,
+                                                     size_t len) {
+  VLOG(1) << "OnDataPayload: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataPayload(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnDataEnd() {
+  VLOG(1) << "OnDataEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnDataEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnHeadersStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersPriority(
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnHeadersPriority: " << priority;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersPriority(priority);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHpackFragment(const char* data,
+                                                       size_t len) {
+  VLOG(1) << "OnHpackFragment: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHpackFragment(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnHeadersEnd() {
+  VLOG(1) << "OnHeadersEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnHeadersEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPriorityFrame(
+    const Http2FrameHeader& header,
+    const Http2PriorityFields& priority) {
+  VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPriorityFrame(header, priority);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnContinuationStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnContinuationStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnContinuationStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnContinuationEnd() {
+  VLOG(1) << "OnContinuationEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnContinuationEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
+  VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPadLength(trailing_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPadding(const char* padding,
+                                                 size_t skipped_length) {
+  VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPadding(padding, skipped_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnRstStream(
+    const Http2FrameHeader& header,
+    Http2ErrorCode error_code) {
+  VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnRstStream(header, error_code);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSetting(
+    const Http2SettingFields& setting_fields) {
+  VLOG(1) << "OnSetting: " << setting_fields;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSetting(setting_fields);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsEnd() {
+  VLOG(1) << "OnSettingsEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnSettingsAck(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnSettingsAck: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnSettingsAck(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPushPromiseStart(
+    const Http2FrameHeader& header,
+    const Http2PushPromiseFields& promise,
+    size_t total_padding_length) {
+  VLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
+          << "; total_padding_length: " << total_padding_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPushPromiseStart(header, promise, total_padding_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPushPromiseEnd() {
+  VLOG(1) << "OnPushPromiseEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPushPromiseEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
+                                              const Http2PingFields& ping) {
+  VLOG(1) << "OnPing: " << header << "; ping: " << ping;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPing(header, ping);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
+                                                 const Http2PingFields& ping) {
+  VLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPingAck(header, ping);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayStart(
+    const Http2FrameHeader& header,
+    const Http2GoAwayFields& goaway) {
+  VLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayStart(header, goaway);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayOpaqueData(const char* data,
+                                                          size_t len) {
+  VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayOpaqueData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnGoAwayEnd() {
+  VLOG(1) << "OnGoAwayEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnGoAwayEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnWindowUpdate(
+    const Http2FrameHeader& header,
+    uint32_t increment) {
+  VLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnWindowUpdate(header, increment);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcStart(
+    const Http2FrameHeader& header,
+    size_t origin_length,
+    size_t value_length) {
+  VLOG(1) << "OnAltSvcStart: " << header << "; origin_length: " << origin_length
+          << "; value_length: " << value_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcStart(header, origin_length, value_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
+                                                          size_t len) {
+  VLOG(1) << "OnAltSvcOriginData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcOriginData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcValueData(const char* data,
+                                                         size_t len) {
+  VLOG(1) << "OnAltSvcValueData: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcValueData(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnAltSvcEnd() {
+  VLOG(1) << "OnAltSvcEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnAltSvcEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownStart(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnUnknownStart: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownStart(header);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownPayload(const char* data,
+                                                        size_t len) {
+  VLOG(1) << "OnUnknownPayload: len=" << len;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownPayload(data, len);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnUnknownEnd() {
+  VLOG(1) << "OnUnknownEnd";
+  if (wrapped_ != nullptr) {
+    wrapped_->OnUnknownEnd();
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnPaddingTooLong(
+    const Http2FrameHeader& header,
+    size_t missing_length) {
+  VLOG(1) << "OnPaddingTooLong: " << header
+          << "; missing_length: " << missing_length;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnPaddingTooLong(header, missing_length);
+  }
+}
+
+void LoggingHttp2FrameDecoderListener::OnFrameSizeError(
+    const Http2FrameHeader& header) {
+  VLOG(1) << "OnFrameSizeError: " << header;
+  if (wrapped_ != nullptr) {
+    wrapped_->OnFrameSizeError(header);
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_frame_decoder_listener_test_util.h b/net/http2/decoder/http2_frame_decoder_listener_test_util.h
new file mode 100644
index 0000000..ead1b93a
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_listener_test_util.h
@@ -0,0 +1,141 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
+
+#include <stddef.h>
+
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+// Fail if any of the methods are called. Allows a test to override only the
+// expected calls.
+class FailingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
+ public:
+  FailingHttp2FrameDecoderListener();
+  ~FailingHttp2FrameDecoderListener() override;
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+ private:
+  void EnsureNotAbstract() { FailingHttp2FrameDecoderListener instance; }
+};
+
+// VLOG's all the calls it receives, and forwards those calls to an optional
+// listener.
+class LoggingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
+ public:
+  LoggingHttp2FrameDecoderListener();
+  explicit LoggingHttp2FrameDecoderListener(Http2FrameDecoderListener* wrapped);
+  ~LoggingHttp2FrameDecoderListener() override;
+
+  // TODO(jamessynge): Remove OnFrameHeader once done with supporting
+  // SpdyFramer's exact states.
+  bool OnFrameHeader(const Http2FrameHeader& header) override;
+  void OnDataStart(const Http2FrameHeader& header) override;
+  void OnDataPayload(const char* data, size_t len) override;
+  void OnDataEnd() override;
+  void OnHeadersStart(const Http2FrameHeader& header) override;
+  void OnHeadersPriority(const Http2PriorityFields& priority) override;
+  void OnHpackFragment(const char* data, size_t len) override;
+  void OnHeadersEnd() override;
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority) override;
+  void OnContinuationStart(const Http2FrameHeader& header) override;
+  void OnContinuationEnd() override;
+  void OnPadLength(size_t trailing_length) override;
+  void OnPadding(const char* padding, size_t skipped_length) override;
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override;
+  void OnSettingsStart(const Http2FrameHeader& header) override;
+  void OnSetting(const Http2SettingFields& setting_fields) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck(const Http2FrameHeader& header) override;
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override;
+  void OnPushPromiseEnd() override;
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override;
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override;
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override;
+  void OnGoAwayOpaqueData(const char* data, size_t len) override;
+  void OnGoAwayEnd() override;
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t increment) override;
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override;
+  void OnAltSvcOriginData(const char* data, size_t len) override;
+  void OnAltSvcValueData(const char* data, size_t len) override;
+  void OnAltSvcEnd() override;
+  void OnUnknownStart(const Http2FrameHeader& header) override;
+  void OnUnknownPayload(const char* data, size_t len) override;
+  void OnUnknownEnd() override;
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override;
+  void OnFrameSizeError(const Http2FrameHeader& header) override;
+
+ private:
+  void EnsureNotAbstract() { LoggingHttp2FrameDecoderListener instance; }
+
+  Http2FrameDecoderListener* wrapped_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_FRAME_DECODER_LISTENER_TEST_UTIL_H_
diff --git a/net/http2/decoder/http2_frame_decoder_test.cc b/net/http2/decoder/http2_frame_decoder_test.cc
new file mode 100644
index 0000000..c2ec0a0d
--- /dev/null
+++ b/net/http2/decoder/http2_frame_decoder_test.cc
@@ -0,0 +1,946 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_frame_decoder.h"
+
+// Tests of Http2FrameDecoder.
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace net {
+namespace test {
+class Http2FrameDecoderPeer {
+ public:
+  static size_t remaining_total_payload(Http2FrameDecoder* decoder) {
+    return decoder->frame_decoder_state_.remaining_total_payload();
+  }
+};
+
+namespace {
+
+class Http2FrameDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForDecodePayloadExpectingError(
+      const FrameParts& expected,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeError);
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+  AssertionResult ValidatorForBeyondMaximum(const FrameParts& expected,
+                                            const DecodeBuffer& input,
+                                            DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeError);
+    // The decoder detects this error after decoding the header, and without
+    // trying to decode the payload.
+    VERIFY_EQ(input.Offset(), Http2FrameHeader::EncodedSize());
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+ protected:
+  void SetUp() override {
+    // On any one run of this suite, we'll always choose the same value for
+    // use_default_reconstruct_ because the random seed is the same for each
+    // test case, but across runs the random seed changes.
+    use_default_reconstruct_ = Random().OneIn(2);
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* db) override {
+    DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+    collector_.Reset();
+    PrepareDecoder();
+
+    DecodeStatus status = decoder_.DecodeFrame(db);
+    if (status != DecodeStatus::kDecodeInProgress) {
+      // Keep track of this so that a concrete test can verify that both fast
+      // and slow decoding paths have been tested.
+      ++fast_decode_count_;
+      if (status == DecodeStatus::kDecodeError) {
+        ConfirmDiscardsRemainingPayload();
+      }
+    }
+    return status;
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
+    DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+    DecodeStatus status = decoder_.DecodeFrame(db);
+    if (status != DecodeStatus::kDecodeInProgress) {
+      // Keep track of this so that a concrete test can verify that both fast
+      // and slow decoding paths have been tested.
+      ++slow_decode_count_;
+      if (status == DecodeStatus::kDecodeError) {
+        ConfirmDiscardsRemainingPayload();
+      }
+    }
+    return status;
+  }
+
+  // When an error is returned, the decoder is in state kDiscardPayload, and
+  // stays there until the remaining bytes of the frame's payload have been
+  // skipped over. There are no callbacks for this situation.
+  void ConfirmDiscardsRemainingPayload() {
+    ASSERT_TRUE(decoder_.IsDiscardingPayload());
+    size_t remaining =
+        Http2FrameDecoderPeer::remaining_total_payload(&decoder_);
+    // The decoder will discard the remaining bytes, but not go beyond that,
+    // which these conditions verify.
+    size_t extra = 10;
+    string junk(remaining + extra, '0');
+    DecodeBuffer tmp(junk);
+    EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.DecodeFrame(&tmp));
+    EXPECT_EQ(remaining, tmp.Offset());
+    EXPECT_EQ(extra, tmp.Remaining());
+    EXPECT_FALSE(decoder_.IsDiscardingPayload());
+  }
+
+  void PrepareDecoder() {
+    // Save and restore the maximum_payload_size when reconstructing
+    // the decoder.
+    size_t maximum_payload_size = decoder_.maximum_payload_size();
+
+    // Alternate which constructor is used.
+    if (use_default_reconstruct_) {
+      decoder_.~Http2FrameDecoder();
+      new (&decoder_) Http2FrameDecoder;
+      decoder_.set_listener(&collector_);
+    } else {
+      decoder_.~Http2FrameDecoder();
+      new (&decoder_) Http2FrameDecoder(&collector_);
+    }
+    decoder_.set_maximum_payload_size(maximum_payload_size);
+
+    use_default_reconstruct_ = !use_default_reconstruct_;
+  }
+
+  void ResetDecodeSpeedCounters() {
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+  }
+
+  AssertionResult VerifyCollected(const FrameParts& expected) {
+    VERIFY_FALSE(collector_.IsInProgress());
+    VERIFY_EQ(1u, collector_.size());
+    VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*collector_.frame(0)));
+  }
+
+  AssertionResult DecodePayloadAndValidateSeveralWays(StringPiece payload,
+                                                      Validator validator) {
+    DecodeBuffer db(payload);
+    bool start_decoding_requires_non_empty = false;
+    return DecodeAndValidateSeveralWays(&db, start_decoding_requires_non_empty,
+                                        validator);
+  }
+
+  AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
+      const FrameParts& expected,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_AND_RETURN_SUCCESS(VerifyCollected(expected));
+  }
+
+  // Decode one frame's payload and confirm that the listener recorded the
+  // expected FrameParts instance, and only one FrameParts instance. The
+  // payload will be decoded several times with different partitionings
+  // of the payload, and after each the validator will be called.
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      StringPiece payload,
+      const FrameParts& expected) {
+    Validator validator = base::Bind(
+        &Http2FrameDecoderTest::ValidatorForDecodePayloadAndValidateSeveralWays,
+        base::Unretained(this), base::ConstRef(expected));
+    ResetDecodeSpeedCounters();
+    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
+        payload, ValidateDoneAndEmpty(validator)));
+    VERIFY_GT(fast_decode_count_, 0u);
+    VERIFY_GT(slow_decode_count_, 0u);
+
+    // Repeat with more input; it should stop without reading that input.
+    string next_frame = Random().RandString(10);
+    string input;
+    payload.AppendToString(&input);
+    input += next_frame;
+
+    ResetDecodeSpeedCounters();
+    VERIFY_SUCCESS(DecodePayloadAndValidateSeveralWays(
+        payload, ValidateDoneAndOffset(payload.size(), validator)));
+    VERIFY_GT(fast_decode_count_, 0u);
+    VERIFY_GT(slow_decode_count_, 0u);
+
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      const char (&buf)[N],
+      const FrameParts& expected) {
+    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N), expected);
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      const char (&buf)[N],
+      const Http2FrameHeader& header) {
+    return DecodePayloadAndValidateSeveralWays(StringPiece(buf, N),
+                                               FrameParts(header));
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingError(const char (&buf)[N],
+                                              const FrameParts& expected) {
+    ResetDecodeSpeedCounters();
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
+        ToStringPiece(buf),
+        base::Bind(
+            &Http2FrameDecoderTest::ValidatorForDecodePayloadExpectingError,
+            base::Unretained(this), expected)));
+    EXPECT_GT(fast_decode_count_, 0u);
+    EXPECT_GT(slow_decode_count_, 0u);
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingFrameSizeError(const char (&buf)[N],
+                                                       FrameParts expected) {
+    expected.has_frame_size_error = true;
+    VERIFY_AND_RETURN_SUCCESS(DecodePayloadExpectingError(buf, expected));
+  }
+
+  template <size_t N>
+  AssertionResult DecodePayloadExpectingFrameSizeError(
+      const char (&buf)[N],
+      const Http2FrameHeader& header) {
+    return DecodePayloadExpectingFrameSizeError(buf, FrameParts(header));
+  }
+
+  // Count of payloads that are fully decoded by StartDecodingPayload or for
+  // which an error was detected by StartDecodingPayload.
+  size_t fast_decode_count_ = 0;
+
+  // Count of payloads that required calling ResumeDecodingPayload in order to
+  // decode completely, or for which an error was detected by
+  // ResumeDecodingPayload.
+  size_t slow_decode_count_ = 0;
+
+  FramePartsCollectorListener collector_;
+  Http2FrameDecoder decoder_;
+  bool use_default_reconstruct_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests that pass the minimum allowed size for the frame type, which is often
+// empty. The tests are in order by frame type value (i.e. 0 for DATA frames).
+
+TEST_F(Http2FrameDecoderTest, DataEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x00,                    // DATA
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::DATA, 0, 0);
+  FrameParts expected(header, "");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x01,                    // HEADERS
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 0  (REQUIRES ID)
+  };
+  Http2FrameHeader header(0, Http2FrameType::HEADERS, 0, 1);
+  FrameParts expected(header, "");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Priority) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+  };
+  Http2FrameHeader header(5, Http2FrameType::PRIORITY, 0, 2);
+  FrameParts expected(header);
+  expected.opt_priority = Http2PriorityFields(1, 17, true);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStream) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
+  };
+  Http2FrameHeader header(4, Http2FrameType::RST_STREAM, 0, 1);
+  FrameParts expected(header);
+  expected.opt_rst_stream_error_code = Http2ErrorCode::PROTOCOL_ERROR;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Length: 0
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::SETTINGS, 0, 1);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsAck) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        //   Length: 6
+      0x04,                    //     Type: SETTINGS
+      0x01,                    //    Flags: ACK
+      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
+  };
+  Http2FrameHeader header(0, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
+                          0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromiseMinimal) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Payload length: 4
+      0x05,                    // PUSH_PROMISE
+      0x04,                    // Flags: END_HEADERS
+      0x00, 0x00, 0x00, 0x02,  //   Stream: 2 (invalid but unchecked here)
+      0x00, 0x00, 0x00, 0x01,  // Promised: 1 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "");
+  expected.opt_push_promise = Http2PushPromiseFields{1};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Ping) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xfeu,                    //    Flags: no valid flags
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+  };
+  Http2FrameHeader header(8, Http2FrameType::PING, 0, 0);
+  FrameParts expected(header);
+  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PingAck) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xffu,                    //    Flags: ACK (plus all invalid flags)
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+  };
+  Http2FrameHeader header(8, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
+  FrameParts expected(header);
+  expected.opt_ping = Http2PingFields{{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayMinimal) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x08,         // Length: 8 (no opaque data)
+      0x07,                      //   Type: GOAWAY
+      0xffu,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,   // Stream: 1 (invalid but unchecked here)
+      0x80u, 0x00, 0x00, 0xffu,  //   Last: 255 (plus R bit)
+      0x00,  0x00, 0x00, 0x09,   //  Error: COMPRESSION_ERROR
+  };
+  Http2FrameHeader header(8, Http2FrameType::GOAWAY, 0, 1);
+  FrameParts expected(header);
+  expected.opt_goaway =
+      Http2GoAwayFields(255, Http2ErrorCode::COMPRESSION_ERROR);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdate) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x04,        // Length: 4
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
+  };
+  Http2FrameHeader header(4, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  FrameParts expected(header);
+  expected.opt_window_update_increment = 1024;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, ContinuationEmpty) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x09,                    // CONTINUATION
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(0, Http2FrameType::CONTINUATION, 0, 0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcMinimal) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x02,        // Payload length: 2
+      0x0a,                     // ALTSVC
+      0xffu,                    // Flags: none (plus 0xff)
+      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0 (invalid but unchecked here)
+      0x00,  0x00,              // Origin Length: 0
+  };
+  Http2FrameHeader header(2, Http2FrameType::ALTSVC, 0, 0);
+  FrameParts expected(header);
+  expected.opt_altsvc_origin_length = 0;
+  expected.opt_altsvc_value_length = 0;
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, UnknownEmpty) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x00,        // Payload length: 0
+      0x20,                     // 32 (unknown)
+      0xffu,                    // Flags: all
+      0x00,  0x00, 0x00, 0x00,  // Stream ID: 0
+  };
+  Http2FrameHeader header(0, static_cast<Http2FrameType>(32), 0xff, 0);
+  FrameParts expected(header);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests of longer payloads, for those frame types that allow longer payloads.
+
+TEST_F(Http2FrameDecoderTest, DataPayload) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Payload length: 7
+      0x00,                     // DATA
+      0x80u,                    // Flags: 0x80
+      0x00,  0x00, 0x02, 0x02,  // Stream ID: 514
+      'a',   'b',  'c',         // Data
+  };
+  Http2FrameHeader header(3, Http2FrameType::DATA, 0, 514);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Payload length: 3
+      0x01,                    // HEADERS
+      0x05,                    // Flags: END_STREAM | END_HEADERS
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
+  };
+  Http2FrameHeader header(
+      3, Http2FrameType::HEADERS,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPriority) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Payload length: 5
+      0x01,                     // HEADERS
+      0x20,                     // Flags: PRIORITY
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x00,  0x00, 0x00, 0x01,  // Parent: 1 (Not Exclusive)
+      0xffu,                    // Weight: 256
+  };
+  Http2FrameHeader header(5, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PRIORITY, 2);
+  FrameParts expected(header);
+  expected.opt_priority = Http2PriorityFields(1, 256, false);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, Settings) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x0c,        // Length: 12
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream: 0
+      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
+      0x0a, 0x0b, 0x0c, 0x0d,  //  Value: 168496141
+      0x00, 0x02,              //  Param: ENABLE_PUSH
+      0x00, 0x00, 0x00, 0x03,  //  Value: 3 (invalid but unchecked here)
+  };
+  Http2FrameHeader header(12, Http2FrameType::SETTINGS, 0, 0);
+  FrameParts expected(header);
+  expected.settings.push_back(Http2SettingFields(
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE, 168496141));
+  expected.settings.push_back(
+      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 7,            // Payload length: 7
+      0x05,                     // PUSH_PROMISE
+      0x04,                     // Flags: END_HEADERS
+      0x00, 0x00, 0x00, 0xffu,  // Stream ID: 255
+      0x00, 0x00, 0x01, 0x00,   // Promised: 256
+      'a',  'b',  'c',          // HPACK fragment (doesn't have to be valid)
+  };
+  Http2FrameHeader header(7, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_END_HEADERS, 255);
+  FrameParts expected(header, "abc");
+  expected.opt_push_promise = Http2PushPromiseFields{256};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayOpaqueData) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x0e,        // Length: 14
+      0x07,                     //   Type: GOAWAY
+      0xffu,                    //  Flags: 0xff (no valid flags)
+      0x80u, 0x00, 0x00, 0x00,  // Stream: 0 (plus R bit)
+      0x00,  0x00, 0x01, 0x00,  //   Last: 256
+      0x00,  0x00, 0x00, 0x03,  //  Error: FLOW_CONTROL_ERROR
+      'o',   'p',  'a',  'q',  'u', 'e',
+  };
+  Http2FrameHeader header(14, Http2FrameType::GOAWAY, 0, 0);
+  FrameParts expected(header, "opaque");
+  expected.opt_goaway =
+      Http2GoAwayFields(256, Http2ErrorCode::FLOW_CONTROL_ERROR);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, ContinuationPayload) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Payload length: 3
+      0x09,                     // CONTINUATION
+      0xffu,                    // Flags: END_HEADERS | 0xfb
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 2
+      'a',   'b',  'c',         // Data
+  };
+  Http2FrameHeader header(3, Http2FrameType::CONTINUATION,
+                          Http2FrameFlag::FLAG_END_HEADERS, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x08,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00, 0x03,              // Origin Length: 0
+      'a',  'b',  'c',         // Origin
+      'd',  'e',  'f',         // Value
+  };
+  Http2FrameHeader header(8, Http2FrameType::ALTSVC, 0, 2);
+  FrameParts expected(header);
+  expected.SetAltSvcExpected("abc", "def");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, UnknownPayload) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Payload length: 3
+      0x30,                    // 48 (unknown)
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      'a',  'b',  'c',         // Payload
+  };
+  Http2FrameHeader header(3, static_cast<Http2FrameType>(48), 0, 2);
+  FrameParts expected(header, "abc");
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests of padded payloads, for those frame types that allow padding.
+
+TEST_F(Http2FrameDecoderTest, DataPayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x00,                    // DATA
+      0x09,                    // Flags: END_STREAM | PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // Data
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      7, Http2FrameType::DATA,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED, 2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x01,                    // HEADERS
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(7, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PADDED, 2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeadersPayloadPriorityAndPadding) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x0c,        // Payload length: 12
+      0x01,                     // HEADERS
+      0xffu,                    // Flags: all, including undefined
+      0x00,  0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                     // Pad Len
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00,  0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      12, Http2FrameType::HEADERS,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS |
+          Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY,
+      2);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  expected.opt_priority = Http2PriorityFields(1, 17, true);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePayloadAndPadding) {
+  const char kFrameData[] = {
+      0x00,  0x00, 11,          // Payload length: 11
+      0x05,                     // PUSH_PROMISE
+      0xffu,                    // Flags: END_HEADERS | PADDED | 0xf3
+      0x00,  0x00, 0x00, 0x01,  // Stream ID: 1
+      0x03,                     // Pad Len
+      0x00,  0x00, 0x00, 0x02,  // Promised: 2
+      'a',   'b',  'c',         // HPACK fragment (doesn't have to be valid)
+      0x00,  0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      11, Http2FrameType::PUSH_PROMISE,
+      Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED, 1);
+  size_t total_pad_length = 4;  // Including the Pad Length field.
+  FrameParts expected(header, "abc", total_pad_length);
+  expected.opt_push_promise = Http2PushPromiseFields{2};
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Payload too short errors.
+
+TEST_F(Http2FrameDecoderTest, DataMissingPadLengthField) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x00,        // Payload length: 0
+      0x00,                    // DATA
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+  };
+  Http2FrameHeader header(0, Http2FrameType::DATA, Http2FrameFlag::FLAG_PADDED,
+                          1);
+  FrameParts expected(header);
+  expected.opt_missing_length = 1;
+  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeaderPaddingTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x02,        // Payload length: 0
+      0x01,                     // HEADERS
+      0x08,                     // Flags: PADDED
+      0x00,  0x01, 0x00, 0x00,  // Stream ID: 65536
+      0xffu,                    // Pad Len: 255
+      0x00,                     // Only one byte of padding
+  };
+  Http2FrameHeader header(2, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PADDED, 65536);
+  FrameParts expected(header);
+  expected.opt_missing_length = 254;
+  EXPECT_TRUE(DecodePayloadExpectingError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, HeaderMissingPriority) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x04,        // Payload length: 0
+      0x01,                    // HEADERS
+      0x20,                    // Flags: PRIORITY
+      0x00, 0x01, 0x00, 0x00,  // Stream ID: 65536
+      0x00, 0x00, 0x00, 0x00,  // Priority (truncated)
+  };
+  Http2FrameHeader header(4, Http2FrameType::HEADERS,
+                          Http2FrameFlag::FLAG_PRIORITY, 65536);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PriorityTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x04,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+  };
+  Http2FrameHeader header(4, Http2FrameType::PRIORITY, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStreamTooShort) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x03,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00,        //  Truncated
+  };
+  Http2FrameHeader header(3, Http2FrameType::RST_STREAM, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+// SETTINGS frames must a multiple of 6 bytes long, so an 9 byte payload is
+// invalid.
+TEST_F(Http2FrameDecoderTest, SettingsWrongSize) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x09,        // Length: 2
+      0x04,                    //   Type: SETTINGS
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x00,  // Stream: 0
+      0x00, 0x02,              //  Param: ENABLE_PUSH
+      0x00, 0x00, 0x00, 0x03,  //  Value: 1
+      0x00, 0x04,              //  Param: INITIAL_WINDOW_SIZE
+      0x00,                    //  Value: Truncated
+  };
+  Http2FrameHeader header(9, Http2FrameType::SETTINGS, 0, 0);
+  FrameParts expected(header);
+  expected.settings.push_back(
+      Http2SettingFields(Http2SettingsParameter::ENABLE_PUSH, 3));
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, expected));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromiseTooShort) {
+  const char kFrameData[] = {
+      0x00, 0x00, 3,           // Payload length: 3
+      0x05,                    // PUSH_PROMISE
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+      0x00, 0x00, 0x00,        // Truncated promise id
+  };
+  Http2FrameHeader header(3, Http2FrameType::PUSH_PROMISE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PushPromisePaddedTruncatedPromise) {
+  const char kFrameData[] = {
+      0x00, 0x00, 4,           // Payload length: 4
+      0x05,                    // PUSH_PROMISE
+      0x08,                    // Flags: PADDED
+      0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+      0x00,                    // Pad Len
+      0x00, 0x00, 0x00,        // Truncated promise id
+  };
+  Http2FrameHeader header(4, Http2FrameType::PUSH_PROMISE,
+                          Http2FrameFlag::FLAG_PADDED, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PingTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x07,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xfeu,                    //    Flags: no valid flags
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',         // Too little
+  };
+  Http2FrameHeader header(7, Http2FrameType::PING, 0, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, GoAwayTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x00,        // Length: 0
+      0x07,                     //   Type: GOAWAY
+      0xffu,                    //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x00,  // Stream: 0
+  };
+  Http2FrameHeader header(0, Http2FrameType::GOAWAY, 0, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdateTooShort) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x03,        // Length: 3
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04,        // Truncated
+  };
+  Http2FrameHeader header(3, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOriginLength) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x01,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00,                    // Origin Length: truncated
+  };
+  Http2FrameHeader header(1, Http2FrameType::ALTSVC, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, AltSvcTruncatedOrigin) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x05,        // Payload length: 3
+      0x0a,                    // ALTSVC
+      0x00,                    // Flags: none
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 2
+      0x00, 0x04,              // Origin Length: 4 (too long)
+      'a',  'b',  'c',         // Origin
+  };
+  Http2FrameHeader header(5, Http2FrameType::ALTSVC, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Payload too long errors.
+
+// The decoder calls the listener's OnFrameSizeError method if the frame's
+// payload is longer than the currently configured maximum payload size.
+TEST_F(Http2FrameDecoderTest, BeyondMaximum) {
+  decoder_.set_maximum_payload_size(2);
+  const char kFrameData[] = {
+      0x00, 0x00, 0x07,        // Payload length: 7
+      0x00,                    // DATA
+      0x09,                    // Flags: END_STREAM | PADDED
+      0x00, 0x00, 0x00, 0x02,  // Stream ID: 0  (REQUIRES ID)
+      0x03,                    // Pad Len
+      'a',  'b',  'c',         // Data
+      0x00, 0x00, 0x00,        // Padding
+  };
+  Http2FrameHeader header(
+      7, Http2FrameType::DATA,
+      Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED, 2);
+  FrameParts expected(header);
+  expected.has_frame_size_error = true;
+  ResetDecodeSpeedCounters();
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(
+      ToStringPiece(kFrameData),
+      base::Bind(&Http2FrameDecoderTest::ValidatorForBeyondMaximum,
+                 base::Unretained(this), expected)));
+  EXPECT_GT(fast_decode_count_, 0u);
+  EXPECT_GT(slow_decode_count_, 0u);
+}
+
+TEST_F(Http2FrameDecoderTest, PriorityTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x06,        // Length: 5
+      0x02,                     //   Type: PRIORITY
+      0x00,                     //  Flags: none
+      0x00,  0x00, 0x00, 0x02,  // Stream: 2
+      0x80u, 0x00, 0x00, 0x01,  // Parent: 1 (Exclusive)
+      0x10,                     // Weight: 17
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(6, Http2FrameType::PRIORITY, 0, 2);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, RstStreamTooLong) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x05,        // Length: 4
+      0x03,                    //   Type: RST_STREAM
+      0x00,                    //  Flags: none
+      0x00, 0x00, 0x00, 0x01,  // Stream: 1
+      0x00, 0x00, 0x00, 0x01,  //  Error: PROTOCOL_ERROR
+      0x00,                    // Too much
+  };
+  Http2FrameHeader header(5, Http2FrameType::RST_STREAM, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, SettingsAckTooLong) {
+  const char kFrameData[] = {
+      0x00, 0x00, 0x06,        //   Length: 6
+      0x04,                    //     Type: SETTINGS
+      0x01,                    //    Flags: ACK
+      0x00, 0x00, 0x00, 0x00,  //   Stream: 0
+      0x00, 0x00,              //   Extra
+      0x00, 0x00, 0x00, 0x00,  //   Extra
+  };
+  Http2FrameHeader header(6, Http2FrameType::SETTINGS, Http2FrameFlag::FLAG_ACK,
+                          0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, PingAckTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x09,        //   Length: 8
+      0x06,                     //     Type: PING
+      0xffu,                    //    Flags: ACK | 0xfe
+      0x00,  0x00, 0x00, 0x00,  //   Stream: 0
+      's',   'o',  'm',  'e',   // "some"
+      'd',   'a',  't',  'a',   // "data"
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(9, Http2FrameType::PING, Http2FrameFlag::FLAG_ACK, 0);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+TEST_F(Http2FrameDecoderTest, WindowUpdateTooLong) {
+  const char kFrameData[] = {
+      0x00,  0x00, 0x05,        // Length: 5
+      0x08,                     //   Type: WINDOW_UPDATE
+      0x0f,                     //  Flags: 0xff (no valid flags)
+      0x00,  0x00, 0x00, 0x01,  // Stream: 1
+      0x80u, 0x00, 0x04, 0x00,  //   Incr: 1024 (plus R bit)
+      0x00,                     // Too much
+  };
+  Http2FrameHeader header(5, Http2FrameType::WINDOW_UPDATE, 0, 1);
+  EXPECT_TRUE(DecodePayloadExpectingFrameSizeError(kFrameData, header));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder.cc b/net/http2/decoder/http2_structure_decoder.cc
new file mode 100644
index 0000000..a7b1dca4
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder.cc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+#include <algorithm>
+
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+// Below we have some defensive coding: if we somehow run off the end, don't
+// overwrite lots of memory. Note that most of this decoder is not defensive
+// against bugs in the decoder, only against malicious encoders, but since
+// we're copying memory into a buffer here, let's make sure we don't allow a
+// small mistake to grow larger. The decoder will get stuck if we hit the
+// HTTP2_BUG conditions, but shouldn't corrupt memory.
+
+uint32_t Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
+                                                uint32_t target_size) {
+  if (target_size > sizeof buffer_) {
+    HTTP2_BUG << "target_size too large for buffer: " << target_size;
+    return 0;
+  }
+  const uint32_t num_to_copy = db->MinLengthRemaining(target_size);
+  memcpy(buffer_, db->cursor(), num_to_copy);
+  offset_ = num_to_copy;
+  db->AdvanceCursor(num_to_copy);
+  return num_to_copy;
+}
+
+DecodeStatus Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
+                                                    uint32_t* remaining_payload,
+                                                    uint32_t target_size) {
+  DVLOG(1) << "IncompleteStart@" << this
+           << ": *remaining_payload=" << *remaining_payload
+           << "; target_size=" << target_size
+           << "; db->Remaining=" << db->Remaining();
+  *remaining_payload -=
+      IncompleteStart(db, std::min(target_size, *remaining_payload));
+  if (*remaining_payload > 0 && db->Empty()) {
+    return DecodeStatus::kDecodeInProgress;
+  }
+  DVLOG(1) << "IncompleteStart: kDecodeError";
+  return DecodeStatus::kDecodeError;
+}
+
+bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
+                                                uint32_t target_size) {
+  DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
+           << "; offset_=" << offset_ << "; db->Remaining=" << db->Remaining();
+  if (target_size < offset_) {
+    HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
+              << "    offset_=" << offset_;
+    return false;
+  }
+  const uint32_t needed = target_size - offset_;
+  const uint32_t num_to_copy = db->MinLengthRemaining(needed);
+  DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+  memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
+  db->AdvanceCursor(num_to_copy);
+  offset_ += num_to_copy;
+  return needed == num_to_copy;
+}
+
+bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
+                                                uint32_t* remaining_payload,
+                                                uint32_t target_size) {
+  DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
+           << "; offset_=" << offset_
+           << "; *remaining_payload=" << *remaining_payload
+           << "; db->Remaining=" << db->Remaining();
+  if (target_size < offset_) {
+    HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
+              << "    offset_=" << offset_;
+    return false;
+  }
+  const uint32_t needed = target_size - offset_;
+  const uint32_t num_to_copy =
+      db->MinLengthRemaining(std::min(needed, *remaining_payload));
+  DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+  memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
+  db->AdvanceCursor(num_to_copy);
+  offset_ += num_to_copy;
+  *remaining_payload -= num_to_copy;
+  return needed == num_to_copy;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder.h b/net/http2/decoder/http2_structure_decoder.h
new file mode 100644
index 0000000..8665e96
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder.h
@@ -0,0 +1,129 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
+#define NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
+
+// Http2StructureDecoder is a class for decoding the fixed size structures in
+// the HTTP/2 spec, defined in net/http2/http2_structures.h. This class
+// is in aid of deciding whether to keep the SlowDecode methods which I
+// (jamessynge) now think may not be worth their complexity. In particular,
+// if most transport buffers are large, so it is rare that a structure is
+// split across buffer boundaries, than the cost of buffering upon
+// those rare occurrences is small, which then simplifies the callers.
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_http2_structures.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class Http2StructureDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE Http2StructureDecoder {
+ public:
+  // The caller needs to keep track of whether to call Start or Resume.
+  //
+  // Start has an optimization for the case where the DecodeBuffer holds the
+  // entire encoded structure; in that case it decodes into *out and returns
+  // true, and does NOT touch the data members of the Http2StructureDecoder
+  // instance because the caller won't be calling Resume later.
+  //
+  // However, if the DecodeBuffer is too small to hold the entire encoded
+  // structure, Start copies the available bytes into the Http2StructureDecoder
+  // instance, and returns false to indicate that it has not been able to
+  // complete the decoding.
+  //
+  template <class S>
+  bool Start(S* out, DecodeBuffer* db) {
+    static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
+    DVLOG(2) << __func__ << "@" << this << ": db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (db->Remaining() >= S::EncodedSize()) {
+      DoDecode(out, db);
+      return true;
+    }
+    IncompleteStart(db, S::EncodedSize());
+    return false;
+  }
+
+  template <class S>
+  bool Resume(S* out, DecodeBuffer* db) {
+    DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+             << "; db->Remaining=" << db->Remaining();
+    if (ResumeFillingBuffer(db, S::EncodedSize())) {
+      // We have the whole thing now.
+      DVLOG(2) << __func__ << "@" << this << "    offset_=" << offset_
+               << "    Ready to decode from buffer_.";
+      DecodeBuffer buffer_db(buffer_, S::EncodedSize());
+      DoDecode(out, &buffer_db);
+      return true;
+    }
+    DCHECK_LT(offset_, S::EncodedSize());
+    return false;
+  }
+
+  // A second pair of Start and Resume, where the caller has a variable,
+  // |remaining_payload| that is both tested for sufficiency and updated
+  // during decoding. Note that the decode buffer may extend beyond the
+  // remaining payload because the buffer may include padding.
+  template <class S>
+  DecodeStatus Start(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
+    static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
+    DVLOG(2) << __func__ << "@" << this
+             << ": *remaining_payload=" << *remaining_payload
+             << "; db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (db->MinLengthRemaining(*remaining_payload) >= S::EncodedSize()) {
+      DoDecode(out, db);
+      *remaining_payload -= S::EncodedSize();
+      return DecodeStatus::kDecodeDone;
+    }
+    return IncompleteStart(db, remaining_payload, S::EncodedSize());
+  }
+
+  template <class S>
+  bool Resume(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
+    DVLOG(3) << __func__ << "@" << this << ": offset_=" << offset_
+             << "; *remaining_payload=" << *remaining_payload
+             << "; db->Remaining=" << db->Remaining()
+             << "; EncodedSize=" << S::EncodedSize();
+    if (ResumeFillingBuffer(db, remaining_payload, S::EncodedSize())) {
+      // We have the whole thing now.
+      DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+               << "; Ready to decode from buffer_.";
+      DecodeBuffer buffer_db(buffer_, S::EncodedSize());
+      DoDecode(out, &buffer_db);
+      return true;
+    }
+    DCHECK_LT(offset_, S::EncodedSize());
+    return false;
+  }
+
+  uint32_t offset() const { return offset_; }
+
+ private:
+  friend class test::Http2StructureDecoderPeer;
+
+  uint32_t IncompleteStart(DecodeBuffer* db, uint32_t target_size);
+  DecodeStatus IncompleteStart(DecodeBuffer* db,
+                               uint32_t* remaining_payload,
+                               uint32_t target_size);
+
+  bool ResumeFillingBuffer(DecodeBuffer* db, uint32_t target_size);
+  bool ResumeFillingBuffer(DecodeBuffer* db,
+                           uint32_t* remaining_payload,
+                           uint32_t target_size);
+
+  uint32_t offset_;
+  char buffer_[Http2FrameHeader::EncodedSize()];
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_H_
diff --git a/net/http2/decoder/http2_structure_decoder_test.cc b/net/http2/decoder/http2_structure_decoder_test.cc
new file mode 100644
index 0000000..68479a1
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder_test.cc
@@ -0,0 +1,512 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+// Tests decoding all of the fixed size HTTP/2 structures (i.e. those defined in
+// net/http2/http2_structures.h) using Http2StructureDecoder, which handles
+// buffering of structures split across input buffer boundaries, and in turn
+// uses DoDecode when it has all of a structure in a contiguous buffer.
+
+// NOTE: This tests the first pair of Start and Resume, which don't take
+// a remaining_payload parameter. The other pair are well tested via the
+// payload decoder tests, though...
+// TODO(jamessynge): Create type parameterized tests for Http2StructureDecoder
+// where the type is the type of structure, and with testing of both pairs of
+// Start and Resume methods; note that it appears that the first pair will be
+// used only for Http2FrameHeader, and the other pair only for structures in the
+// frame payload.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+const bool kMayReturnZeroOnFirst = false;
+
+template <class S>
+string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+template <class S>
+class Http2StructureDecoderTest : public RandomDecoderTest {
+ protected:
+  typedef S Structure;
+
+  Http2StructureDecoderTest() {
+    // IF the test adds more data after the encoded structure, stop as
+    // soon as the structure is decoded.
+    stop_decode_on_done_ = true;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    // Overwrite the current contents of |structure_|, in to which we'll
+    // decode the buffer, so that we can be confident that we really decoded
+    // the structure every time.
+    structure_.~S();
+    new (&structure_) S;
+    uint32_t old_remaining = b->Remaining();
+    if (structure_decoder_.Start(&structure_, b)) {
+      EXPECT_EQ(old_remaining - S::EncodedSize(), b->Remaining());
+      ++fast_decode_count_;
+      return DecodeStatus::kDecodeDone;
+    } else {
+      EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+      EXPECT_EQ(0u, b->Remaining());
+      EXPECT_EQ(old_remaining - structure_decoder_.offset(), b->Remaining());
+      ++incomplete_start_count_;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    uint32_t old_offset = structure_decoder_.offset();
+    EXPECT_LT(old_offset, S::EncodedSize());
+    uint32_t avail = b->Remaining();
+    if (structure_decoder_.Resume(&structure_, b)) {
+      EXPECT_LE(S::EncodedSize(), old_offset + avail);
+      EXPECT_EQ(b->Remaining(), avail - (S::EncodedSize() - old_offset));
+      ++slow_decode_count_;
+      return DecodeStatus::kDecodeDone;
+    } else {
+      EXPECT_LT(structure_decoder_.offset(), S::EncodedSize());
+      EXPECT_EQ(0u, b->Remaining());
+      EXPECT_GT(S::EncodedSize(), old_offset + avail);
+      ++incomplete_resume_count_;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+
+  AssertionResult ValidatorForDecodeLeadingStructure(const S* expected,
+                                                     const DecodeBuffer& db,
+                                                     DecodeStatus status) {
+    VERIFY_EQ(*expected, structure_);
+    return AssertionSuccess();
+  }
+
+  // Fully decodes the Structure at the start of data, and confirms it matches
+  // *expected (if provided).
+  AssertionResult DecodeLeadingStructure(const S* expected, StringPiece data) {
+    VERIFY_LE(S::EncodedSize(), data.size());
+    DecodeBuffer original(data);
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that structure_ matches the expected value, if provided.
+    Validator validator =
+        (expected == nullptr)
+            ? base::Bind(&SucceedingValidator)
+            : base::Bind(&Http2StructureDecoderTest::
+                             ValidatorForDecodeLeadingStructure,
+                         base::Unretained(this), expected);
+
+    // Before that, validate that decoding is done and that we've advanced
+    // the cursor the expected amount.
+    Validator wrapped_validator =
+        ValidateDoneAndOffset(S::EncodedSize(), validator);
+
+    // Decode several times, with several segmentations of the input buffer.
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+    incomplete_start_count_ = 0;
+    incomplete_resume_count_ = 0;
+    VERIFY_SUCCESS(DecodeAndValidateSeveralWays(
+        &original, kMayReturnZeroOnFirst, wrapped_validator));
+    VERIFY_FALSE(HasFailure());
+    VERIFY_EQ(S::EncodedSize(), structure_decoder_.offset());
+    VERIFY_EQ(S::EncodedSize(), original.Offset());
+    VERIFY_LT(0u, fast_decode_count_);
+    VERIFY_LT(0u, slow_decode_count_);
+    VERIFY_LT(0u, incomplete_start_count_);
+
+    // If the structure is large enough so that SelectZeroOrOne will have
+    // caused Resume to return false, check that occurred.
+    if (S::EncodedSize() >= 2) {
+      VERIFY_LE(0u, incomplete_resume_count_);
+    } else {
+      VERIFY_EQ(0u, incomplete_resume_count_);
+    }
+    if (expected != nullptr) {
+      DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+      DVLOG(1) << "DecodeLeadingStructure   actual: " << structure_;
+      VERIFY_EQ(*expected, structure_);
+    }
+    return AssertionSuccess();
+  }
+
+  template <size_t N>
+  AssertionResult DecodeLeadingStructure(const char (&data)[N]) {
+    VERIFY_AND_RETURN_SUCCESS(
+        DecodeLeadingStructure(nullptr, StringPiece(data, N)));
+  }
+
+  // Encode the structure |in_s| into bytes, then decode the bytes
+  // and validate that the decoder produced the same field values.
+  AssertionResult EncodeThenDecode(const S& in_s) {
+    string bytes = SerializeStructure(in_s);
+    VERIFY_EQ(S::EncodedSize(), bytes.size());
+    VERIFY_AND_RETURN_SUCCESS(DecodeLeadingStructure(&in_s, bytes));
+  }
+
+  // Repeatedly fill a structure with random but valid contents, encode it, then
+  // decode it, and finally validate that the decoded structure matches the
+  // random input. Lather-rinse-and-repeat.
+  AssertionResult TestDecodingRandomizedStructures(size_t count) {
+    for (size_t i = 0; i < count; ++i) {
+      Structure input;
+      Randomize(&input, RandomPtr());
+      VERIFY_SUCCESS(EncodeThenDecode(input));
+    }
+    return AssertionSuccess();
+  }
+
+  AssertionResult TestDecodingRandomizedStructures() {
+    VERIFY_SUCCESS(TestDecodingRandomizedStructures(100));
+    return AssertionSuccess();
+  }
+
+  uint32_t decode_offset_ = 0;
+  S structure_;
+  Http2StructureDecoder structure_decoder_;
+  size_t fast_decode_count_ = 0;
+  size_t slow_decode_count_ = 0;
+  size_t incomplete_start_count_ = 0;
+  size_t incomplete_resume_count_ = 0;
+};
+
+class Http2FrameHeaderDecoderTest
+    : public Http2StructureDecoderTest<Http2FrameHeader> {};
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesLiteral) {
+  {
+    // Realistic input.
+    const char kData[] = {
+        0x00, 0x00, 0x05,        // Payload length: 5
+        0x01,                    // Frame type: HEADERS
+        0x08,                    // Flags: PADDED
+        0x00, 0x00, 0x00, 0x01,  // Stream ID: 1
+        0x04,                    // Padding length: 4
+        0x00, 0x00, 0x00, 0x00,  // Padding bytes
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(5u, structure_.payload_length);
+    EXPECT_EQ(Http2FrameType::HEADERS, structure_.type);
+    EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, structure_.flags);
+    EXPECT_EQ(1u, structure_.stream_id);
+  }
+  {
+    // Unlikely input.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,         // Payload length: uint24 max
+        0xffu,                       // Frame type: Unknown
+        0xffu,                       // Flags: Unknown/All
+        0xffu, 0xffu, 0xffu, 0xffu,  // Stream ID: uint31 max, plus R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ((1u << 24) - 1u, structure_.payload_length);
+    EXPECT_EQ(static_cast<Http2FrameType>(255), structure_.type);
+    EXPECT_EQ(255, structure_.flags);
+    EXPECT_EQ(0x7FFFFFFFu, structure_.stream_id);
+  }
+}
+
+TEST_F(Http2FrameHeaderDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PriorityFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PriorityFields> {};
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x80u, 0x00, 0x00, 0x05,  // Exclusive (yes) and Dependency (5)
+        0xffu,                    // Weight: 256 (after adding 1)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(5u, structure_.stream_dependency);
+    EXPECT_EQ(256u, structure_.weight);
+    EXPECT_EQ(true, structure_.is_exclusive);
+  }
+  {
+    const char kData[] = {
+        0x7fu, 0xffu,
+        0xffu, 0xffu,  // Exclusive (no) and Dependency (0x7fffffff)
+        0x00u,         // Weight: 1 (after adding 1)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.stream_dependency);
+    EXPECT_EQ(1u, structure_.weight);
+    EXPECT_FALSE(structure_.is_exclusive);
+  }
+}
+
+TEST_F(Http2PriorityFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2RstStreamFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2RstStreamFields> {};
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Error: PROTOCOL_ERROR
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_FALSE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+  }
+}
+
+TEST_F(Http2RstStreamFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2SettingFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2SettingFields> {};
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01,              // Setting: HEADER_TABLE_SIZE
+        0x00, 0x00, 0x40, 0x00,  // Value: 16K
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_TRUE(structure_.IsSupportedParameter());
+    EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE, structure_.parameter);
+    EXPECT_EQ(1u << 14, structure_.value);
+  }
+  {
+    const char kData[] = {
+        0x00,  0x00,                 // Setting: Unknown (0)
+        0xffu, 0xffu, 0xffu, 0xffu,  // Value: max uint32
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_FALSE(structure_.IsSupportedParameter());
+    EXPECT_EQ(static_cast<Http2SettingsParameter>(0), structure_.parameter);
+  }
+}
+
+TEST_F(Http2SettingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PushPromiseFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PushPromiseFields> {};
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x8au, 0x92u,  // Promised Stream ID: 101010
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(101010u, structure_.promised_stream_id);
+  }
+  {
+    // Promised stream id has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Promised Stream ID: max uint31 and R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.promised_stream_id);
+  }
+}
+
+TEST_F(Http2PushPromiseFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2PingFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2PingFields> {};
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesLiteral) {
+  {
+    // Each byte is different, so can detect if order changed.
+    const char kData[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+  {
+    // All zeros, detect problems handling NULs.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu,
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StringPiece(kData, 8), ToStringPiece(structure_.opaque_data));
+  }
+}
+
+TEST_F(Http2PingFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2GoAwayFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2GoAwayFields> {};
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Last Stream ID: 0
+        0x00, 0x00, 0x00, 0x00,  // Error: NO_ERROR (0)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0u, structure_.last_stream_id);
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x01,  // Last Stream ID: 1
+        0x00, 0x00, 0x00, 0x0d,  // Error: HTTP_1_1_REQUIRED
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(1u, structure_.last_stream_id);
+    EXPECT_TRUE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED, structure_.error_code);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu, 0xffu,  // Last Stream ID: max uint31 and R-bit
+        0xffu, 0xffu, 0xffu, 0xffu,  // Error: max uint32 (Unknown error code)
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.last_stream_id);  // No high-bit.
+    EXPECT_FALSE(structure_.IsSupportedErrorCode());
+    EXPECT_EQ(static_cast<Http2ErrorCode>(0xffffffff), structure_.error_code);
+  }
+}
+
+TEST_F(Http2GoAwayFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2WindowUpdateFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2WindowUpdateFields> {};
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x01, 0x00, 0x00,  // Window Size Increment: 2 ^ 16
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(1u << 16, structure_.window_size_increment);
+  }
+  {
+    // Increment must be non-zero, but we need to be able to decode the invalid
+    // zero to detect it.
+    const char kData[] = {
+        0x00, 0x00, 0x00, 0x00,  // Window Size Increment: 0
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0u, structure_.window_size_increment);
+  }
+  {
+    // Increment has R-bit (reserved for future use) set, which
+    // should be cleared by the decoder.
+    const char kData[] = {
+        0xffu, 0xffu, 0xffu,
+        0xffu,  // Window Size Increment: max uint31 and R-bit
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(StreamIdMask(), structure_.window_size_increment);
+  }
+}
+
+TEST_F(Http2WindowUpdateFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+//------------------------------------------------------------------------------
+
+class Http2AltSvcFieldsDecoderTest
+    : public Http2StructureDecoderTest<Http2AltSvcFields> {};
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesLiteral) {
+  {
+    const char kData[] = {
+        0x00, 0x00,  // Origin Length: 0
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(0, structure_.origin_length);
+  }
+  {
+    const char kData[] = {
+        0x00, 0x14,  // Origin Length: 20
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(20, structure_.origin_length);
+  }
+  {
+    const char kData[] = {
+        0xffu, 0xffu,  // Origin Length: uint16 max
+    };
+    ASSERT_TRUE(DecodeLeadingStructure(kData));
+    EXPECT_EQ(65535, structure_.origin_length);
+  }
+}
+
+TEST_F(Http2AltSvcFieldsDecoderTest, DecodesRandomized) {
+  TestDecodingRandomizedStructures();
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/http2_structure_decoder_test_util.h b/net/http2/decoder/http2_structure_decoder_test_util.h
new file mode 100644
index 0000000..b45211f7
--- /dev/null
+++ b/net/http2/decoder/http2_structure_decoder_test_util.h
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
+
+#include "net/http2/decoder/http2_structure_decoder.h"
+
+#include <cstddef>
+
+#include "net/http2/tools/http2_random.h"
+
+namespace net {
+namespace test {
+
+class Http2StructureDecoderPeer {
+ public:
+  static void Randomize(Http2StructureDecoder* p, RandomBase* rng) {
+    p->offset_ = rng->Rand32();
+    for (size_t i = 0; i < sizeof p->buffer_; ++i) {
+      p->buffer_[i] = rng->Rand8();
+    }
+  }
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_HTTP2_STRUCTURE_DECODER_TEST_UTIL_H_
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
new file mode 100644
index 0000000..fe0e8c2
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
@@ -0,0 +1,143 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         AltSvcPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case AltSvcPayloadDecoder::PayloadState::kStartDecodingStruct:
+      return out << "kStartDecodingStruct";
+    case AltSvcPayloadDecoder::PayloadState::kMaybeDecodedStruct:
+      return out << "kMaybeDecodedStruct";
+    case AltSvcPayloadDecoder::PayloadState::kDecodingStrings:
+      return out << "kDecodingStrings";
+    case AltSvcPayloadDecoder::PayloadState::kResumeDecodingStruct:
+      return out << "kResumeDecodingStruct";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus AltSvcPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "AltSvcPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::ALTSVC, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  DCHECK_EQ(0, state->frame_header().flags);
+
+  state->InitializeRemainders();
+  payload_state_ = PayloadState::kStartDecodingStruct;
+
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::ALTSVC, frame_header.type);
+  DCHECK_LE(state->remaining_payload(), frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+  DCHECK_NE(PayloadState::kMaybeDecodedStruct, payload_state_);
+  // |status| has to be initialized to some value to avoid compiler error in
+  // case PayloadState::kMaybeDecodedStruct below, but value does not matter,
+  // see DCHECK_NE above.
+  DecodeStatus status = DecodeStatus::kDecodeError;
+  while (true) {
+    DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kStartDecodingStruct:
+        status = state->StartDecodingStructureInPayload(&altsvc_fields_, db);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kMaybeDecodedStruct:
+        if (status == DecodeStatus::kDecodeDone &&
+            altsvc_fields_.origin_length <= state->remaining_payload()) {
+          size_t origin_length = altsvc_fields_.origin_length;
+          size_t value_length = state->remaining_payload() - origin_length;
+          state->listener()->OnAltSvcStart(frame_header, origin_length,
+                                           value_length);
+        } else if (status != DecodeStatus::kDecodeDone) {
+          DCHECK(state->remaining_payload() > 0 ||
+                 status == DecodeStatus::kDecodeError)
+              << "\nremaining_payload: " << state->remaining_payload()
+              << "\nstatus: " << status << "\nheader: " << frame_header;
+          // Assume in progress.
+          payload_state_ = PayloadState::kResumeDecodingStruct;
+          return status;
+        } else {
+          // The origin's length is longer than the remaining payload.
+          DCHECK_GT(altsvc_fields_.origin_length, state->remaining_payload());
+          return state->ReportFrameSizeError();
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kDecodingStrings:
+        return DecodeStrings(state, db);
+
+      case PayloadState::kResumeDecodingStruct:
+        status = state->ResumeDecodingStructureInPayload(&altsvc_fields_, db);
+        payload_state_ = PayloadState::kMaybeDecodedStruct;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+DecodeStatus AltSvcPayloadDecoder::DecodeStrings(FrameDecoderState* state,
+                                                 DecodeBuffer* db) {
+  DVLOG(2) << "AltSvcPayloadDecoder::DecodeStrings remaining_payload="
+           << state->remaining_payload()
+           << ", db->Remaining=" << db->Remaining();
+  // Note that we don't explicitly keep track of exactly how far through the
+  // origin; instead we compute it from how much is left of the original
+  // payload length and the decoded total length of the origin.
+  size_t origin_length = altsvc_fields_.origin_length;
+  size_t value_length = state->frame_header().payload_length - origin_length -
+                        Http2AltSvcFields::EncodedSize();
+  if (state->remaining_payload() > value_length) {
+    size_t remaining_origin_length = state->remaining_payload() - value_length;
+    size_t avail = db->MinLengthRemaining(remaining_origin_length);
+    state->listener()->OnAltSvcOriginData(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+    if (remaining_origin_length > avail) {
+      payload_state_ = PayloadState::kDecodingStrings;
+      return DecodeStatus::kDecodeInProgress;
+    }
+  }
+  // All that is left is the value string.
+  DCHECK_LE(state->remaining_payload(), value_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+  if (db->HasData()) {
+    size_t avail = db->Remaining();
+    state->listener()->OnAltSvcValueData(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnAltSvcEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  payload_state_ = PayloadState::kDecodingStrings;
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h
new file mode 100644
index 0000000..f92fa7f9
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder.h
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a ALTSVC frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class AltSvcPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE AltSvcPayloadDecoder {
+ public:
+  // States during decoding of a ALTSVC frame.
+  enum class PayloadState {
+    // Start decoding the fixed size structure at the start of an ALTSVC
+    // frame (Http2AltSvcFields).
+    kStartDecodingStruct,
+
+    // Handle the DecodeStatus returned from starting or resuming the
+    // decoding of Http2AltSvcFields. If complete, calls OnAltSvcStart.
+    kMaybeDecodedStruct,
+
+    // Reports the value of the strings (origin and value) of an ALTSVC frame
+    // to the listener.
+    kDecodingStrings,
+
+    // The initial decode buffer wasn't large enough for the Http2AltSvcFields,
+    // so this state resumes the decoding when ResumeDecodingPayload is called
+    // later with a new DecodeBuffer.
+    kResumeDecodingStruct,
+  };
+
+  // Starts the decoding of a ALTSVC frame's payload, and completes it if the
+  // entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a ALTSVC frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::AltSvcPayloadDecoderPeer;
+
+  // Implements state kDecodingStrings.
+  DecodeStatus DecodeStrings(FrameDecoderState* state, DecodeBuffer* db);
+
+  Http2AltSvcFields altsvc_fields_;
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_ALTSVC_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
new file mode 100644
index 0000000..e10fc50
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/altsvc_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class AltSvcPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::ALTSVC; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(AltSvcPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->altsvc_fields_, rng);
+    VLOG(1) << "AltSvcPayloadDecoderPeer::Randomize altsvc_fields_="
+            << p->altsvc_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnAltSvcStart(const Http2FrameHeader& header,
+                     size_t origin_length,
+                     size_t value_length) override {
+    VLOG(1) << "OnAltSvcStart header: " << header
+            << "; origin_length=" << origin_length
+            << "; value_length=" << value_length;
+    StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
+  }
+
+  void OnAltSvcOriginData(const char* data, size_t len) override {
+    VLOG(1) << "OnAltSvcOriginData: len=" << len;
+    CurrentFrame()->OnAltSvcOriginData(data, len);
+  }
+
+  void OnAltSvcValueData(const char* data, size_t len) override {
+    VLOG(1) << "OnAltSvcValueData: len=" << len;
+    CurrentFrame()->OnAltSvcValueData(data, len);
+  }
+
+  void OnAltSvcEnd() override {
+    VLOG(1) << "OnAltSvcEnd";
+    EndFrame()->OnAltSvcEnd();
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class AltSvcPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<AltSvcPayloadDecoder,
+                                        AltSvcPayloadDecoderPeer,
+                                        Listener> {};
+
+// Confirm we get an error if the payload is not long enough to hold
+// Http2AltSvcFields and the indicated length of origin.
+TEST_F(AltSvcPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2AltSvcFields{0xffff});  // The longest possible origin length.
+  fb.Append("Too little origin!");
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&AbstractPayloadDecoderTest::SucceedingApproveSize)));
+}
+
+class AltSvcPayloadLengthTests : public AltSvcPayloadDecoderTest,
+                                 public ::testing::WithParamInterface<
+                                     ::testing::tuple<uint16_t, uint32_t>> {
+ protected:
+  AltSvcPayloadLengthTests()
+      : origin_length_(::testing::get<0>(GetParam())),
+        value_length_(::testing::get<1>(GetParam())) {
+    VLOG(1) << "################  origin_length_=" << origin_length_
+            << "   value_length_=" << value_length_ << "  ################";
+  }
+
+  const uint16_t origin_length_;
+  const uint32_t value_length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousOriginAndValueLengths,
+                        AltSvcPayloadLengthTests,
+                        ::testing::Combine(::testing::Values(0, 1, 3, 65535),
+                                           ::testing::Values(0, 1, 3, 65537)));
+
+TEST_P(AltSvcPayloadLengthTests, ValidOriginAndValueLength) {
+  string origin = Random().RandString(origin_length_);
+  string value = Random().RandString(value_length_);
+  Http2FrameBuilder fb;
+  fb.Append(Http2AltSvcFields{origin_length_});
+  fb.Append(origin);
+  fb.Append(value);
+  Http2FrameHeader header(fb.size(), Http2FrameType::ALTSVC, RandFlags(),
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header);
+  expected.SetAltSvcExpected(origin, value);
+  ASSERT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc b/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc
new file mode 100644
index 0000000..ac5371c0
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder.cc
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus ContinuationPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "ContinuationPayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+  DCHECK_EQ(Http2FrameType::CONTINUATION, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_END_HEADERS));
+
+  state->InitializeRemainders();
+  state->listener()->OnContinuationStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus ContinuationPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "ContinuationPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::CONTINUATION, state->frame_header().type);
+  DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+
+  size_t avail = db->Remaining();
+  DCHECK_LE(avail, state->remaining_payload());
+  if (avail > 0) {
+    state->listener()->OnHpackFragment(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnContinuationEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder.h b/net/http2/decoder/payload_decoders/continuation_payload_decoder.h
new file mode 100644
index 0000000..4ab58aa
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a CONTINUATION frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE ContinuationPayloadDecoder {
+ public:
+  // Starts the decoding of a CONTINUATION frame's payload, and completes
+  // it if the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a CONTINUATION frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_CONTINUATION_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
new file mode 100644
index 0000000..3244039
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/continuation_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class ContinuationPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::CONTINUATION;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(ContinuationPayloadDecoder* p, RandomBase* rng) {
+    // ContinuationPayloadDecoder has no fields,
+    // so there is nothing to randomize.
+    static_assert(std::is_empty<ContinuationPayloadDecoder>::value,
+                  "Need to randomize fields of ContinuationPayloadDecoder");
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnContinuationStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnContinuationStart: " << header;
+    StartFrame(header)->OnContinuationStart(header);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnContinuationEnd() override {
+    VLOG(1) << "OnContinuationEnd";
+    EndFrame()->OnContinuationEnd();
+  }
+};
+
+class ContinuationPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<ContinuationPayloadDecoder,
+                                        ContinuationPayloadDecoderPeer,
+                                        Listener>,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  ContinuationPayloadDecoderTest() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        ContinuationPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+
+TEST_P(ContinuationPayloadDecoderTest, ValidLength) {
+  string hpack_payload = Random().RandString(length_);
+  Http2FrameHeader frame_header(length_, Http2FrameType::CONTINUATION,
+                                RandFlags(), RandStreamId());
+  set_frame_header(frame_header);
+  FrameParts expected(frame_header, hpack_payload);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(hpack_payload, expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder.cc b/net/http2/decoder/payload_decoders/data_payload_decoder.cc
new file mode 100644
index 0000000..60b8a714
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder.cc
@@ -0,0 +1,123 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         DataPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case DataPayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case DataPayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case DataPayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
+                                                      DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "DataPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0, frame_header.flags &
+             ~(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED));
+
+  // Special case for the hoped for common case: unpadded and fits fully into
+  // the decode buffer. TO BE SEEN if that is true. It certainly requires that
+  // the transport buffers be large (e.g. >> 16KB typically).
+  // TODO(jamessynge) Add counters.
+  DVLOG(2) << "StartDecodingPayload total_length=" << total_length;
+  if (!frame_header.IsPadded()) {
+    DVLOG(2) << "StartDecodingPayload !IsPadded";
+    if (db->Remaining() == total_length) {
+      DVLOG(2) << "StartDecodingPayload all present";
+      // Note that we don't cache the listener field so that the callee can
+      // replace it if the frame is bad.
+      // If this case is common enough, consider combining the 3 callbacks
+      // into one.
+      state->listener()->OnDataStart(frame_header);
+      if (total_length > 0) {
+        state->listener()->OnDataPayload(db->cursor(), total_length);
+        db->AdvanceCursor(total_length);
+      }
+      state->listener()->OnDataEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    payload_state_ = PayloadState::kReadPayload;
+  } else {
+    payload_state_ = PayloadState::kReadPadLength;
+  }
+  state->InitializeRemainders();
+  state->listener()->OnDataStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus DataPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
+                                                       DecodeBuffer* db) {
+  DVLOG(2) << "DataPayloadDecoder::ResumeDecodingPayload payload_state_="
+           << payload_state_;
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
+  DCHECK_LE(state->remaining_payload_and_padding(),
+            frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
+  DecodeStatus status;
+  size_t avail;
+  switch (payload_state_) {
+    case PayloadState::kReadPadLength:
+      // ReadPadLength handles the OnPadLength callback, and updating the
+      // remaining_payload and remaining_padding fields. If the amount of
+      // padding is too large to fit in the frame's payload, ReadPadLength
+      // instead calls OnPaddingTooLong and returns kDecodeError.
+      status = state->ReadPadLength(db, /*report_pad_length*/ true);
+      if (status != DecodeStatus::kDecodeDone) {
+        return status;
+      }
+    // FALLTHROUGH_INTENDED
+
+    case PayloadState::kReadPayload:
+      avail = state->AvailablePayload(db);
+      if (avail > 0) {
+        state->listener()->OnDataPayload(db->cursor(), avail);
+        db->AdvanceCursor(avail);
+        state->ConsumePayload(avail);
+      }
+      if (state->remaining_payload() > 0) {
+        payload_state_ = PayloadState::kReadPayload;
+        return DecodeStatus::kDecodeInProgress;
+      }
+    // FALLTHROUGH_INTENDED
+
+    case PayloadState::kSkipPadding:
+      // SkipPadding handles the OnPadding callback.
+      if (state->SkipPadding(db)) {
+        state->listener()->OnDataEnd();
+        return DecodeStatus::kDecodeDone;
+      }
+      payload_state_ = PayloadState::kSkipPadding;
+      return DecodeStatus::kDecodeInProgress;
+  }
+  HTTP2_BUG << "PayloadState: " << payload_state_;
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder.h b/net/http2/decoder/payload_decoders/data_payload_decoder.h
new file mode 100644
index 0000000..2fe09596
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a DATA frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+namespace test {
+class DataPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE DataPayloadDecoder {
+ public:
+  // States during decoding of a DATA frame.
+  enum class PayloadState {
+    // The frame is padded and we need to read the PAD_LENGTH field (1 byte),
+    // and then call OnPadLength
+    kReadPadLength,
+
+    // Report the non-padding portion of the payload to the listener's
+    // OnDataPayload method.
+    kReadPayload,
+
+    // The decoder has finished with the non-padding portion of the payload,
+    // and is now ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+  };
+
+  // Starts decoding a DATA frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a DATA frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::DataPayloadDecoderPeer;
+
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_DATA_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc
new file mode 100644
index 0000000..bedb32f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/data_payload_decoder_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/data_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class DataPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::DATA; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED;
+  }
+
+  static void Randomize(DataPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "DataPayloadDecoderPeer::Randomize";
+    CorruptEnum(&p->payload_state_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnDataStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnDataStart: " << header;
+    StartFrame(header)->OnDataStart(header);
+  }
+
+  void OnDataPayload(const char* data, size_t len) override {
+    VLOG(1) << "OnDataPayload: len=" << len;
+    CurrentFrame()->OnDataPayload(data, len);
+  }
+
+  void OnDataEnd() override {
+    VLOG(1) << "OnDataEnd";
+    EndFrame()->OnDataEnd();
+  }
+
+  void OnPadLength(size_t pad_length) override {
+    VLOG(1) << "OnPadLength: " << pad_length;
+    CurrentFrame()->OnPadLength(pad_length);
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "    missing_length: " << missing_length;
+    EndFrame()->OnPaddingTooLong(header, missing_length);
+  }
+};
+
+class DataPayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<DataPayloadDecoder,
+                                                DataPayloadDecoderPeer,
+                                                Listener> {
+ protected:
+  AssertionResult CreateAndDecodeDataOfSize(size_t data_size) {
+    Reset();
+    uint8_t flags = RandFlags();
+
+    string data_payload = Random().RandString(data_size);
+    frame_builder_.Append(data_payload);
+    MaybeAppendTrailingPadding();
+
+    Http2FrameHeader frame_header(frame_builder_.size(), Http2FrameType::DATA,
+                                  flags, RandStreamId());
+    set_frame_header(frame_header);
+    ScrubFlagsOfHeader(&frame_header);
+    FrameParts expected(frame_header, data_payload, total_pad_length_);
+    VERIFY_AND_RETURN_SUCCESS(
+        DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(), expected));
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        DataPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+TEST_P(DataPayloadDecoderTest, VariousDataPayloadSizes) {
+  for (size_t data_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    EXPECT_TRUE(CreateAndDecodeDataOfSize(data_size));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc b/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc
new file mode 100644
index 0000000..f5b597f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder.cc
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         GoAwayPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case GoAwayPayloadDecoder::PayloadState::kStartDecodingFixedFields:
+      return out << "kStartDecodingFixedFields";
+    case GoAwayPayloadDecoder::PayloadState::kHandleFixedFieldsStatus:
+      return out << "kHandleFixedFieldsStatus";
+    case GoAwayPayloadDecoder::PayloadState::kReadOpaqueData:
+      return out << "kReadOpaqueData";
+    case GoAwayPayloadDecoder::PayloadState::kResumeDecodingFixedFields:
+      return out << "kResumeDecodingFixedFields";
+  }
+
+  NOTREACHED();
+  return out;
+}
+
+DecodeStatus GoAwayPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "GoAwayPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::GOAWAY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  DCHECK_EQ(0, state->frame_header().flags);
+
+  state->InitializeRemainders();
+  payload_state_ = PayloadState::kStartDecodingFixedFields;
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus GoAwayPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload()
+           << ", db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::GOAWAY, frame_header.type);
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+  DCHECK_NE(PayloadState::kHandleFixedFieldsStatus, payload_state_);
+
+  // |status| has to be initialized to some value to avoid compiler error in
+  // case PayloadState::kHandleFixedFieldsStatus below, but value does not
+  // matter, see DCHECK_NE above.
+  DecodeStatus status = DecodeStatus::kDecodeError;
+  size_t avail;
+  while (true) {
+    DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kStartDecodingFixedFields:
+        status = state->StartDecodingStructureInPayload(&goaway_fields_, db);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kHandleFixedFieldsStatus:
+        if (status == DecodeStatus::kDecodeDone) {
+          state->listener()->OnGoAwayStart(frame_header, goaway_fields_);
+        } else {
+          // Not done decoding the structure. Either we've got more payload
+          // to decode, or we've run out because the payload is too short,
+          // in which case OnFrameSizeError will have already been called.
+          DCHECK((status == DecodeStatus::kDecodeInProgress &&
+                  state->remaining_payload() > 0) ||
+                 (status == DecodeStatus::kDecodeError &&
+                  state->remaining_payload() == 0))
+              << "\n status=" << status
+              << "; remaining_payload=" << state->remaining_payload();
+          payload_state_ = PayloadState::kResumeDecodingFixedFields;
+          return status;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadOpaqueData:
+        // The opaque data is all the remains to be decoded, so anything left
+        // in the decode buffer is opaque data.
+        avail = db->Remaining();
+        if (avail > 0) {
+          state->listener()->OnGoAwayOpaqueData(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadOpaqueData;
+          return DecodeStatus::kDecodeInProgress;
+        }
+        state->listener()->OnGoAwayEnd();
+        return DecodeStatus::kDecodeDone;
+
+      case PayloadState::kResumeDecodingFixedFields:
+        status = state->ResumeDecodingStructureInPayload(&goaway_fields_, db);
+        payload_state_ = PayloadState::kHandleFixedFieldsStatus;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder.h b/net/http2/decoder/payload_decoders/goaway_payload_decoder.h
new file mode 100644
index 0000000..5aefc6c
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a GOAWAY frame.
+
+// TODO(jamessynge): Sweep through all payload decoders, changing the names of
+// the PayloadState enums so that they are really states, and not actions.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class GoAwayPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE GoAwayPayloadDecoder {
+ public:
+  // States during decoding of a GOAWAY frame.
+  enum class PayloadState {
+    // At the start of the GOAWAY frame payload, ready to start decoding the
+    // fixed size fields into goaway_fields_.
+    kStartDecodingFixedFields,
+
+    // Handle the DecodeStatus returned from starting or resuming the
+    // decoding of Http2GoAwayFields into goaway_fields_. If complete,
+    // calls OnGoAwayStart.
+    kHandleFixedFieldsStatus,
+
+    // Report the Opaque Data portion of the payload to the listener's
+    // OnGoAwayOpaqueData method, and call OnGoAwayEnd when the end of the
+    // payload is reached.
+    kReadOpaqueData,
+
+    // The fixed size fields weren't all available when the decoder first
+    // tried to decode them (state kStartDecodingFixedFields); this state
+    // resumes the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingFixedFields,
+  };
+
+  // Starts the decoding of a GOAWAY frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a GOAWAY frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::GoAwayPayloadDecoderPeer;
+
+  Http2GoAwayFields goaway_fields_;
+  PayloadState payload_state_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_GOAWAY_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
new file mode 100644
index 0000000..248ac0d
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/goaway_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+class GoAwayPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::GOAWAY; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(GoAwayPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->goaway_fields_, rng);
+    VLOG(1) << "GoAwayPayloadDecoderPeer::Randomize goaway_fields: "
+            << p->goaway_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnGoAwayStart(const Http2FrameHeader& header,
+                     const Http2GoAwayFields& goaway) override {
+    VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+    StartFrame(header)->OnGoAwayStart(header, goaway);
+  }
+
+  void OnGoAwayOpaqueData(const char* data, size_t len) override {
+    VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+    CurrentFrame()->OnGoAwayOpaqueData(data, len);
+  }
+
+  void OnGoAwayEnd() override {
+    VLOG(1) << "OnGoAwayEnd";
+    EndFrame()->OnGoAwayEnd();
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class GoAwayPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<GoAwayPayloadDecoder,
+                                        GoAwayPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2GoAwayFields::EncodedSize();
+  }
+};
+
+// Confirm we get an error if the payload is not long enough to hold
+// Http2GoAwayFields.
+TEST_F(GoAwayPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2GoAwayFields(123, Http2ErrorCode::ENHANCE_YOUR_CALM));
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&GoAwayPayloadDecoderTest::ApproveSizeForTruncated)));
+}
+
+class GoAwayOpaqueDataLengthTests
+    : public GoAwayPayloadDecoderTest,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  GoAwayOpaqueDataLengthTests() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        GoAwayOpaqueDataLengthTests,
+                        ::testing::Values(0, 1, 2, 3, 4, 5, 6));
+
+TEST_P(GoAwayOpaqueDataLengthTests, ValidLength) {
+  Http2GoAwayFields goaway;
+  Randomize(&goaway, RandomPtr());
+  string opaque_data = Random().RandString(length_);
+  Http2FrameBuilder fb;
+  fb.Append(goaway);
+  fb.Append(opaque_data);
+  Http2FrameHeader header(fb.size(), Http2FrameType::GOAWAY, RandFlags(),
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header, opaque_data);
+  expected.opt_goaway = goaway;
+  ASSERT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder.cc b/net/http2/decoder/payload_decoders/headers_payload_decoder.cc
new file mode 100644
index 0000000..eff1d3b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         HeadersPayloadDecoder::PayloadState v) {
+  switch (v) {
+    case HeadersPayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case HeadersPayloadDecoder::PayloadState::kStartDecodingPriorityFields:
+      return out << "kStartDecodingPriorityFields";
+    case HeadersPayloadDecoder::PayloadState::kResumeDecodingPriorityFields:
+      return out << "kResumeDecodingPriorityFields";
+    case HeadersPayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case HeadersPayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "HeadersPayloadDecoder::StartDecodingPayload: " << frame_header;
+
+  DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0,
+      frame_header.flags &
+          ~(Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_END_HEADERS |
+            Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY));
+
+  // Special case for HEADERS frames that contain only the HPACK block
+  // (fragment or whole) and that fit fully into the decode buffer.
+  // Why? Unencoded browser GET requests are typically under 1K and HPACK
+  // commonly shrinks request headers by 80%, so we can expect this to
+  // be common.
+  // TODO(jamessynge) Add counters here and to Spdy for determining how
+  // common this situation is. A possible approach is to create a
+  // Http2FrameDecoderListener that counts the callbacks and then forwards
+  // them on to another listener, which makes it easy to add and remove
+  // counting on a connection or even frame basis.
+
+  // PADDED and PRIORITY both extra steps to decode, but if neither flag is
+  // set then we can decode faster.
+  const auto payload_flags =
+      Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY;
+  if (!frame_header.HasAnyFlags(payload_flags)) {
+    DVLOG(2) << "StartDecodingPayload !IsPadded && !HasPriority";
+    if (db->Remaining() == total_length) {
+      DVLOG(2) << "StartDecodingPayload all present";
+      // Note that we don't cache the listener field so that the callee can
+      // replace it if the frame is bad.
+      // If this case is common enough, consider combining the 3 callbacks
+      // into one, especially if END_HEADERS is also set.
+      state->listener()->OnHeadersStart(frame_header);
+      if (total_length > 0) {
+        state->listener()->OnHpackFragment(db->cursor(), total_length);
+        db->AdvanceCursor(total_length);
+      }
+      state->listener()->OnHeadersEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    payload_state_ = PayloadState::kReadPayload;
+  } else if (frame_header.IsPadded()) {
+    payload_state_ = PayloadState::kReadPadLength;
+  } else {
+    DCHECK(frame_header.HasPriority()) << frame_header;
+    payload_state_ = PayloadState::kStartDecodingPriorityFields;
+  }
+  state->InitializeRemainders();
+  state->listener()->OnHeadersStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload "
+           << "remaining_payload=" << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+
+  DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
+  DCHECK_LE(state->remaining_payload_and_padding(),
+            frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload_and_padding());
+  DecodeStatus status;
+  size_t avail;
+  while (true) {
+    DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload payload_state_="
+             << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kReadPadLength:
+        // ReadPadLength handles the OnPadLength callback, and updating the
+        // remaining_payload and remaining_padding fields. If the amount of
+        // padding is too large to fit in the frame's payload, ReadPadLength
+        // instead calls OnPaddingTooLong and returns kDecodeError.
+        status = state->ReadPadLength(db, /*report_pad_length*/ true);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        if (!frame_header.HasPriority()) {
+          payload_state_ = PayloadState::kReadPayload;
+          continue;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kStartDecodingPriorityFields:
+        status = state->StartDecodingStructureInPayload(&priority_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kResumeDecodingPriorityFields;
+          return status;
+        }
+        state->listener()->OnHeadersPriority(priority_fields_);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadPayload:
+        avail = state->AvailablePayload(db);
+        if (avail > 0) {
+          state->listener()->OnHpackFragment(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadPayload;
+          return DecodeStatus::kDecodeInProgress;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kSkipPadding:
+        // SkipPadding handles the OnPadding callback.
+        if (state->SkipPadding(db)) {
+          state->listener()->OnHeadersEnd();
+          return DecodeStatus::kDecodeDone;
+        }
+        payload_state_ = PayloadState::kSkipPadding;
+        return DecodeStatus::kDecodeInProgress;
+
+      case PayloadState::kResumeDecodingPriorityFields:
+        status = state->ResumeDecodingStructureInPayload(&priority_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        state->listener()->OnHeadersPriority(priority_fields_);
+        payload_state_ = PayloadState::kReadPayload;
+        continue;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder.h b/net/http2/decoder/payload_decoders/headers_payload_decoder.h
new file mode 100644
index 0000000..76e6a082
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a HEADERS frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class HeadersPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE HeadersPayloadDecoder {
+ public:
+  // States during decoding of a HEADERS frame, unless the fast path kicks
+  // in, in which case the state machine will be bypassed.
+  enum class PayloadState {
+    // The PADDED flag is set, and we now need to read the Pad Length field
+    // (the first byte of the payload, after the common frame header).
+    kReadPadLength,
+
+    // The PRIORITY flag is set, and we now need to read the fixed size priority
+    // fields (E, Stream Dependency, Weight) into priority_fields_.  Calls on
+    // OnHeadersPriority if completely decodes those fields.
+    kStartDecodingPriorityFields,
+
+    // The decoder passes the non-padding portion of the remaining payload
+    // (i.e. the HPACK block fragment) to the listener's OnHpackFragment method.
+    kReadPayload,
+
+    // The decoder has finished with the HPACK block fragment, and is now
+    // ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+
+    // The fixed size fields weren't all available when the decoder first tried
+    // to decode them (state kStartDecodingPriorityFields); this state resumes
+    // the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingPriorityFields,
+  };
+
+  // Starts the decoding of a HEADERS frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a HEADERS frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::HeadersPayloadDecoderPeer;
+
+  PayloadState payload_state_;
+  Http2PriorityFields priority_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_HEADERS_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
new file mode 100644
index 0000000..89bd29f
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/headers_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+class HeadersPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::HEADERS;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED | Http2FrameFlag::FLAG_PRIORITY;
+  }
+
+  static void Randomize(HeadersPayloadDecoder* p, RandomBase* rng) {
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->priority_fields_, rng);
+    VLOG(1) << "HeadersPayloadDecoderPeer::Randomize priority_fields_: "
+            << p->priority_fields_;
+  }
+};
+
+namespace {
+
+// Listener handles all On* methods that are expected to be called. If any other
+// On* methods of Http2FrameDecoderListener is called then the test fails; this
+// is achieved by way of FailingHttp2FrameDecoderListener, the base class of
+// FramePartsCollector.
+// These On* methods make use of StartFrame, EndFrame, etc. of the base class
+// to create and access to FrameParts instance(s) that will record the details.
+// After decoding, the test validation code can access the FramePart instance(s)
+// via the public methods of FramePartsCollector.
+struct Listener : public FramePartsCollector {
+  void OnHeadersStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnHeadersStart: " << header;
+    StartFrame(header)->OnHeadersStart(header);
+  }
+
+  void OnHeadersPriority(const Http2PriorityFields& priority) override {
+    VLOG(1) << "OnHeadersPriority: " << priority;
+    CurrentFrame()->OnHeadersPriority(priority);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnHeadersEnd() override {
+    VLOG(1) << "OnHeadersEnd";
+    EndFrame()->OnHeadersEnd();
+  }
+
+  void OnPadLength(size_t pad_length) override {
+    VLOG(1) << "OnPadLength: " << pad_length;
+    CurrentFrame()->OnPadLength(pad_length);
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "; missing_length: " << missing_length;
+    FrameError(header)->OnPaddingTooLong(header, missing_length);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class HeadersPayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<HeadersPayloadDecoder,
+                                                HeadersPayloadDecoderPeer,
+                                                Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2PriorityFields::EncodedSize();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        HeadersPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+// Decode various sizes of (fake) HPACK payload, both with and without the
+// PRIORITY flag set.
+TEST_P(HeadersPayloadDecoderTest, VariousHpackPayloadSizes) {
+  for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    LOG(INFO) << "###########   hpack_size = " << hpack_size << "  ###########";
+    Http2PriorityFields priority(RandStreamId(), 1 + Random().Rand8(),
+                                 Random().OneIn(2));
+
+    for (bool has_priority : {false, true}) {
+      Reset();
+      ASSERT_EQ(IsPadded() ? 1u : 0u, frame_builder_.size());
+      uint8_t flags = RandFlags();
+      if (has_priority) {
+        flags |= Http2FrameFlag::FLAG_PRIORITY;
+        frame_builder_.Append(priority);
+      }
+
+      string hpack_payload = Random().RandString(hpack_size);
+      frame_builder_.Append(hpack_payload);
+
+      MaybeAppendTrailingPadding();
+      Http2FrameHeader frame_header(frame_builder_.size(),
+                                    Http2FrameType::HEADERS, flags,
+                                    RandStreamId());
+      set_frame_header(frame_header);
+      ScrubFlagsOfHeader(&frame_header);
+      FrameParts expected(frame_header, hpack_payload, total_pad_length_);
+      if (has_priority) {
+        expected.opt_priority = priority;
+      }
+      EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(),
+                                                      expected));
+    }
+  }
+}
+
+// Confirm we get an error if the PRIORITY flag is set but the payload is
+// not long enough, regardless of the amount of (valid) padding.
+TEST_P(HeadersPayloadDecoderTest, Truncated) {
+  Http2FrameBuilder fb;
+  fb.Append(Http2PriorityFields(RandStreamId(), 1 + Random().Rand8(),
+                                Random().OneIn(2)));
+  EXPECT_TRUE(VerifyDetectsMultipleFrameSizeErrors(
+      Http2FrameFlag::FLAG_PRIORITY, fb.buffer(),
+      base::Bind(&HeadersPayloadDecoderTest::ApproveSizeForTruncated),
+      total_pad_length_));
+}
+
+// Confirm we get an error if the PADDED flag is set but the payload is not
+// long enough to hold even the Pad Length amount of padding.
+TEST_P(HeadersPayloadDecoderTest, PaddingTooLong) {
+  EXPECT_TRUE(VerifyDetectsPaddingTooLong());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
new file mode 100644
index 0000000..d96d481b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+
+#include "net/http2/decoder/frame_decoder_state_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+PayloadDecoderBaseTest::PayloadDecoderBaseTest() {
+  // If the test adds more data after the frame payload,
+  // stop as soon as the payload is decoded.
+  stop_decode_on_done_ = true;
+  frame_header_is_set_ = false;
+  Randomize(&frame_header_, RandomPtr());
+}
+
+DecodeStatus PayloadDecoderBaseTest::StartDecoding(DecodeBuffer* db) {
+  DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+  // Make sure sub-class has set frame_header_ so that we can inject it
+  // into the payload decoder below.
+  if (!frame_header_is_set_) {
+    ADD_FAILURE() << "frame_header_ is not set";
+    return DecodeStatus::kDecodeError;
+  }
+  // The contract with the payload decoders is that they won't receive a
+  // decode buffer that extends beyond the end of the frame.
+  if (db->Remaining() > frame_header_.payload_length) {
+    ADD_FAILURE() << "DecodeBuffer has too much data: " << db->Remaining()
+                  << " > " << frame_header_.payload_length;
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Prepare the payload decoder.
+  PreparePayloadDecoder();
+
+  // Reconstruct the FrameDecoderState, prepare the listener, and add it to
+  // the FrameDecoderState.
+  frame_decoder_state_.~FrameDecoderState();
+  new (&frame_decoder_state_) FrameDecoderState;
+  frame_decoder_state_.set_listener(PrepareListener());
+
+  // Make sure that a listener was provided.
+  if (frame_decoder_state_.listener() == nullptr) {
+    ADD_FAILURE() << "PrepareListener must return a listener.";
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Now that nothing in the payload decoder should be valid, inject the
+  // Http2FrameHeader whose payload we're about to decode. That header is the
+  // only state that a payload decoder should expect is valid when its Start
+  // method is called.
+  FrameDecoderStatePeer::set_frame_header(frame_header_, &frame_decoder_state_);
+  DecodeStatus status = StartDecodingPayload(db);
+  if (status != DecodeStatus::kDecodeInProgress) {
+    // Keep track of this so that a concrete test can verify that both fast
+    // and slow decoding paths have been tested.
+    ++fast_decode_count_;
+  }
+  return status;
+}
+
+DecodeStatus PayloadDecoderBaseTest::ResumeDecoding(DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+  DecodeStatus status = ResumeDecodingPayload(db);
+  if (status != DecodeStatus::kDecodeInProgress) {
+    // Keep track of this so that a concrete test can verify that both fast
+    // and slow decoding paths have been tested.
+    ++slow_decode_count_;
+  }
+  return status;
+}
+
+::testing::AssertionResult
+PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+    base::StringPiece payload,
+    Validator validator) {
+  VERIFY_TRUE(frame_header_is_set_);
+  // Cap the payload to be decoded at the declared payload length. This is
+  // required by the decoders' preconditions; they are designed on the
+  // assumption that they're never passed more than they're permitted to
+  // consume.
+  // Note that it is OK if the payload is too short; the validator may be
+  // designed to check for that.
+  if (payload.size() > frame_header_.payload_length) {
+    payload = base::StringPiece(payload.data(), frame_header_.payload_length);
+  }
+  DecodeBuffer db(payload);
+  ResetDecodeSpeedCounters();
+  const bool kMayReturnZeroOnFirst = false;
+  return DecodeAndValidateSeveralWays(&db, kMayReturnZeroOnFirst, validator);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
new file mode 100644
index 0000000..b49b15ef
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
@@ -0,0 +1,486 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
+
+// Base class for testing concrete payload decoder classes.
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Base class for tests of payload decoders. Below this there is a templated
+// sub-class that adds a bunch of type specific features.
+class PayloadDecoderBaseTest : public RandomDecoderTest {
+ protected:
+  PayloadDecoderBaseTest();
+
+  // Virtual functions to be implemented by the test classes for the individual
+  // payload decoders...
+
+  // Start decoding the payload.
+  virtual DecodeStatus StartDecodingPayload(DecodeBuffer* db) = 0;
+
+  // Resume decoding the payload.
+  virtual DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) = 0;
+
+  // In support of ensuring that we're really accessing and updating the
+  // decoder, prepare the decoder by, for example, overwriting the decoder.
+  virtual void PreparePayloadDecoder() = 0;
+
+  // Get the listener to be inserted into the FrameDecoderState, ready for
+  // listening (e.g. reset if it is a FramePartsCollector).
+  virtual Http2FrameDecoderListener* PrepareListener() = 0;
+
+  // Record a frame header for use on each call to StartDecoding.
+  void set_frame_header(const Http2FrameHeader& header) {
+    EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags);
+    if (!frame_header_is_set_ || frame_header_ != header) {
+      VLOG(2) << "set_frame_header: " << frame_header_;
+    }
+    frame_header_ = header;
+    frame_header_is_set_ = true;
+  }
+
+  const Http2FrameHeader& frame_header() const {
+    CHECK(frame_header_is_set_);
+    return frame_header_;
+  }
+
+  FrameDecoderState* mutable_state() { return &frame_decoder_state_; }
+
+  // Randomize the payload decoder, sets the payload decoder's frame_header_,
+  // then start decoding the payload. Called by RandomDecoderTest. This method
+  // is final so that we can always perform certain actions when
+  // RandomDecoderTest starts the decoding of a payload, such as randomizing the
+  // the payload decoder, injecting the frame header and counting fast decoding
+  // cases. Sub-classes must implement StartDecodingPayload to perform their
+  // initial decoding of a frame's payload.
+  DecodeStatus StartDecoding(DecodeBuffer* db) final;
+
+  // Called by RandomDecoderTest. This method is final so that we can always
+  // perform certain actions when RandomDecoderTest calls it, such as counting
+  // slow decode cases. Sub-classes must implement ResumeDecodingPayload to
+  // continue decoding the frame's payload, which must not all be in one buffer.
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) final;
+
+  // Given the specified payload (without the common frame header), decode
+  // it with several partitionings of the payload.
+  ::testing::AssertionResult DecodePayloadAndValidateSeveralWays(
+      base::StringPiece payload,
+      Validator validator);
+
+  // TODO(jamessynge): Add helper method for verifying these are both non-zero,
+  // and call the new method from tests that expect successful decoding.
+  void ResetDecodeSpeedCounters() {
+    fast_decode_count_ = 0;
+    slow_decode_count_ = 0;
+  }
+
+  // Count of payloads that are full decoded by StartDecodingPayload, or that
+  // an error was detected by StartDecodingPayload.
+  size_t fast_decode_count_ = 0;
+
+  // Count of payloads that require calling ResumeDecodingPayload in order to
+  // decode them completely (or to detect an error during decoding).
+  size_t slow_decode_count_ = 0;
+
+ private:
+  bool frame_header_is_set_ = false;
+  Http2FrameHeader frame_header_;
+  FrameDecoderState frame_decoder_state_;
+};
+
+// Base class for payload decoders of type Decoder, with corresponding test
+// peer of type DecoderPeer, and using class Listener as the implementation
+// of Http2FrameDecoderListenerInterface to be used during decoding.
+// Typically Listener is a sub-class of FramePartsCollector.
+// SupportedFrameType is set to false only for UnknownPayloadDecoder.
+template <class Decoder,
+          class DecoderPeer,
+          class Listener,
+          bool SupportedFrameType = true>
+class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
+ public:
+  static bool SucceedingApproveSize(size_t size) { return true; }
+
+ protected:
+  // An ApproveSize function returns true to approve decoding the specified
+  // size of payload, else false to skip that size. Typically used for negative
+  // tests; for example, decoding a SETTINGS frame at all sizes except for
+  // multiples of 6.
+  typedef base::Callback<bool(size_t size)> ApproveSize;
+
+  AbstractPayloadDecoderTest() {}
+
+  // These tests are in setup rather than the constructor for two reasons:
+  // 1) Constructors are not allowed to fail, so gUnit documents that EXPECT_*
+  //    and ASSERT_* are not allowed in constructors, and should instead be in
+  //    SetUp if they are needed before the body of the test is executed.
+  // 2) To allow the sub-class constructor to make any desired modifications to
+  //    the DecoderPeer before these tests are executed; in particular,
+  //    UnknownPayloadDecoderPeer has not got a fixed frame type, but it is
+  //    instead set during the test's constructor.
+  void SetUp() override {
+    PayloadDecoderBaseTest::SetUp();
+
+    // Confirm that DecoderPeer et al returns sensible values. Using auto as the
+    // variable type so that no (narrowing) conversions take place that hide
+    // problems; i.e. if someone changes KnownFlagsMaskForFrameType so that it
+    // doesn't return a uint8, and has bits above the low-order 8 bits set, this
+    // bit of paranoia should detect the problem before we get too far.
+    auto frame_type = DecoderPeer::FrameType();
+    if (SupportedFrameType) {
+      EXPECT_TRUE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
+    } else {
+      EXPECT_FALSE(IsSupportedHttp2FrameType(frame_type)) << frame_type;
+    }
+
+    auto known_flags = KnownFlagsMaskForFrameType(frame_type);
+    EXPECT_EQ(known_flags, known_flags & 0xff);
+
+    auto flags_to_avoid = DecoderPeer::FlagsAffectingPayloadDecoding();
+    EXPECT_EQ(flags_to_avoid, flags_to_avoid & known_flags);
+  }
+
+  void PreparePayloadDecoder() override {
+    payload_decoder_.~Decoder();
+    new (&payload_decoder_) Decoder;
+  }
+
+  Http2FrameDecoderListener* PrepareListener() override {
+    listener_.Reset();
+    return &listener_;
+  }
+
+  // Returns random flags, but only those valid for the frame type, yet not
+  // those that the DecoderPeer says will affect the decoding of the payload
+  // (e.g. the PRIORTY flag on a HEADERS frame or PADDED on DATA frames).
+  uint8_t RandFlags() {
+    return Random().Rand8() &
+           KnownFlagsMaskForFrameType(DecoderPeer::FrameType()) &
+           ~DecoderPeer::FlagsAffectingPayloadDecoding();
+  }
+
+  // Start decoding the payload.
+  DecodeStatus StartDecodingPayload(DecodeBuffer* db) override {
+    DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining();
+    return payload_decoder_.StartDecodingPayload(mutable_state(), db);
+  }
+
+  // Resume decoding the payload.
+  DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override {
+    DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" << db->Remaining();
+    return payload_decoder_.ResumeDecodingPayload(mutable_state(), db);
+  }
+
+  // Wrap |validator| in another one which will check that we've reached the
+  // expected state of kDecodeError with OnFrameSizeError having been called by
+  AssertionResult ValidatorForDecodePayloadAndValidateSeveralWays(
+      const FrameParts& expected) {
+    VERIFY_FALSE(listener_.IsInProgress());
+    VERIFY_EQ(1u, listener_.size());
+    VERIFY_AND_RETURN_SUCCESS(expected.VerifyEquals(*listener_.frame(0)));
+  }
+
+  // Decode one frame's payload and confirm that the listener recorded the
+  // expected FrameParts instance, and only FrameParts instance. The payload
+  // will be decoded several times with different partitionings of the payload,
+  // and after each the validator will be called.
+  AssertionResult DecodePayloadAndValidateSeveralWays(
+      base::StringPiece payload,
+      const FrameParts& expected) {
+    return PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+        payload, this->ValidateDoneAndEmpty(base::Bind(
+                     &AbstractPayloadDecoderTest::
+                         ValidatorForDecodePayloadAndValidateSeveralWays,
+                     base::Unretained(this), base::ConstRef(expected))));
+  }
+
+  // Wrap |validator| in another one which will check that we've reached the
+  // expected state of kDecodeError with OnFrameSizeError having been called by
+  // the payload decoder.
+  AssertionResult ValidatorForVerifyDetectsFrameSizeError(
+      const Http2FrameHeader& header,
+      const Validator& validator,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" << status
+             << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_FALSE(listener_.IsInProgress());
+    VERIFY_EQ(1u, listener_.size());
+    const FrameParts* frame = listener_.frame(0);
+    VERIFY_EQ(header, frame->frame_header);
+    VERIFY_TRUE(frame->has_frame_size_error);
+    // Verify did not get OnPaddingTooLong, as we should only ever produce
+    // one of these two errors for a single frame.
+    VERIFY_FALSE(frame->opt_missing_length);
+    return validator.Run(input, status);
+  }
+
+  // Decode one frame's payload, expecting that the final status will be
+  // kDecodeError, and that OnFrameSizeError will have been called on the
+  // listener. The payload will be decoded several times with different
+  // partitionings of the payload. The type WrappedValidator is either
+  // RandomDecoderTest::Validator, RandomDecoderTest::NoArgValidator or
+  // std::nullptr_t (not extra validation).
+  template <typename WrappedValidator>
+  ::testing::AssertionResult VerifyDetectsFrameSizeError(
+      base::StringPiece payload,
+      const Http2FrameHeader& header,
+      WrappedValidator wrapped_validator) {
+    set_frame_header(header);
+    // If wrapped_validator is not a RandomDecoderTest::Validator, make it so.
+    Validator validator = this->ToValidator(wrapped_validator);
+    VERIFY_AND_RETURN_SUCCESS(
+        PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+            payload, base::Bind(&AbstractPayloadDecoderTest::
+                                    ValidatorForVerifyDetectsFrameSizeError,
+                                base::Unretained(this), base::ConstRef(header),
+                                base::ConstRef(validator))));
+  }
+
+  // Confirm that we get OnFrameSizeError when trying to decode unpadded_payload
+  // at all sizes from zero to unpadded_payload.size(), except those sizes not
+  // approved by approve_size.
+  // If total_pad_length is greater than zero, then that amount of padding
+  // is added to the payload (including the Pad Length field).
+  // The flags will be required_flags, PADDED if total_pad_length > 0, and some
+  // randomly selected flag bits not excluded by FlagsAffectingPayloadDecoding.
+  ::testing::AssertionResult VerifyDetectsMultipleFrameSizeErrors(
+      uint8_t required_flags,
+      base::StringPiece unpadded_payload,
+      ApproveSize approve_size,
+      int total_pad_length) {
+    // required_flags should come from those that are defined for the frame
+    // type AND are those that affect the decoding of the payload (otherwise,
+    // the flag shouldn't be required).
+    Http2FrameType frame_type = DecoderPeer::FrameType();
+    VERIFY_EQ(required_flags,
+              required_flags & KnownFlagsMaskForFrameType(frame_type));
+    VERIFY_EQ(required_flags,
+              required_flags & DecoderPeer::FlagsAffectingPayloadDecoding());
+
+    if (0 != (Http2FrameFlag::FLAG_PADDED &
+              KnownFlagsMaskForFrameType(frame_type))) {
+      // Frame type supports padding.
+      if (total_pad_length == 0) {
+        required_flags &= ~Http2FrameFlag::FLAG_PADDED;
+      } else {
+        required_flags |= Http2FrameFlag::FLAG_PADDED;
+      }
+    } else {
+      VERIFY_EQ(0, total_pad_length);
+    }
+
+    bool validated = false;
+    for (size_t real_payload_size = 0;
+         real_payload_size <= unpadded_payload.size(); ++real_payload_size) {
+      if (!approve_size.Run(real_payload_size)) {
+        continue;
+      }
+      VLOG(1) << "real_payload_size=" << real_payload_size;
+      uint8_t flags = required_flags | RandFlags();
+      Http2FrameBuilder fb;
+      if (total_pad_length > 0) {
+        // total_pad_length_ includes the size of the Pad Length field, and thus
+        // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+        fb.AppendUInt8(total_pad_length - 1);
+      }
+      // Append a subset of the unpadded_payload, which the decoder should
+      // determine is not a valid amount.
+      fb.Append(unpadded_payload.substr(0, real_payload_size));
+      if (total_pad_length > 0) {
+        fb.AppendZeroes(total_pad_length - 1);
+      }
+      // We choose a random stream id because the payload decoders aren't
+      // checking stream ids.
+      uint32_t stream_id = RandStreamId();
+      Http2FrameHeader header(fb.size(), frame_type, flags, stream_id);
+      VERIFY_SUCCESS(VerifyDetectsFrameSizeError(
+          fb.buffer(), header, base::Bind(&SucceedingValidator)));
+      validated = true;
+    }
+    VERIFY_TRUE(validated);
+    return ::testing::AssertionSuccess();
+  }
+
+  // As above, but for frames without padding.
+  ::testing::AssertionResult VerifyDetectsFrameSizeError(
+      uint8_t required_flags,
+      base::StringPiece unpadded_payload,
+      ApproveSize approve_size) {
+    Http2FrameType frame_type = DecoderPeer::FrameType();
+    uint8_t known_flags = KnownFlagsMaskForFrameType(frame_type);
+    VERIFY_EQ(0, known_flags & Http2FrameFlag::FLAG_PADDED);
+    VERIFY_EQ(0, required_flags & Http2FrameFlag::FLAG_PADDED);
+    VERIFY_AND_RETURN_SUCCESS(VerifyDetectsMultipleFrameSizeErrors(
+        required_flags, unpadded_payload, approve_size, 0));
+  }
+
+  Listener listener_;
+  union {
+    // Confirm at compile time that Decoder can be in an anonymous union,
+    // i.e. complain loudly if Decoder has members that prevent this, as it
+    // becomes annoying and possibly difficult to deal with non-anonymous
+    // unions and such union members.
+    Decoder payload_decoder_;
+  };
+};
+
+// A base class for tests parameterized by the total number of bytes of
+// padding, including the Pad Length field (i.e. a total_pad_length of 0
+// means unpadded as there is then no room for the Pad Length field).
+// The frame type must support padding.
+template <class Decoder, class DecoderPeer, class Listener>
+class AbstractPaddablePayloadDecoderTest
+    : public AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener>,
+      public ::testing::WithParamInterface<int> {
+  typedef AbstractPayloadDecoderTest<Decoder, DecoderPeer, Listener> Base;
+
+ protected:
+  using Base::Random;
+  using Base::RandStreamId;
+  using Base::set_frame_header;
+  using Base::listener_;
+  typedef typename Base::Validator Validator;
+
+  AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) {
+    LOG(INFO) << "total_pad_length_ = " << total_pad_length_;
+  }
+
+  // Note that total_pad_length_ includes the size of the Pad Length field,
+  // and thus ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+  bool IsPadded() const { return total_pad_length_ > 0; }
+
+  // Value of the Pad Length field. Only call if IsPadded.
+  size_t pad_length() const {
+    EXPECT_TRUE(IsPadded());
+    return total_pad_length_ - 1;
+  }
+
+  // Clear the frame builder and add the Pad Length field if appropriate.
+  void Reset() {
+    frame_builder_ = Http2FrameBuilder();
+    if (IsPadded()) {
+      frame_builder_.AppendUInt8(pad_length());
+    }
+  }
+
+  void MaybeAppendTrailingPadding() {
+    if (IsPadded()) {
+      frame_builder_.AppendZeroes(pad_length());
+    }
+  }
+
+  uint8_t RandFlags() {
+    uint8_t flags = Base::RandFlags();
+    if (IsPadded()) {
+      flags |= Http2FrameFlag::FLAG_PADDED;
+    } else {
+      flags &= ~Http2FrameFlag::FLAG_PADDED;
+    }
+    return flags;
+  }
+
+  static ::testing::AssertionResult ValidatorForVerifyDetectsPaddingTooLong(
+      const Http2FrameHeader& header,
+      int expected_missing_length,
+      const Listener& listener,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_FALSE(listener.IsInProgress());
+    VERIFY_EQ(1u, listener.size());
+    const FrameParts* frame = listener.frame(0);
+    VERIFY_EQ(header, frame->frame_header);
+    VERIFY_TRUE(frame->opt_missing_length);
+    VERIFY_EQ(expected_missing_length, frame->opt_missing_length.value());
+    // Verify did not get OnFrameSizeError.
+    VERIFY_FALSE(frame->has_frame_size_error);
+    return ::testing::AssertionSuccess();
+  }
+
+  // Verify that we get OnPaddingTooLong when decoding payload, and that the
+  // amount of missing padding is as specified. header.IsPadded must be true,
+  // and the payload must be empty or the PadLength field must be too large.
+  ::testing::AssertionResult VerifyDetectsPaddingTooLong(
+      base::StringPiece payload,
+      const Http2FrameHeader& header,
+      int expected_missing_length) {
+    set_frame_header(header);
+    auto& listener = listener_;
+    VERIFY_AND_RETURN_SUCCESS(
+        PayloadDecoderBaseTest::DecodePayloadAndValidateSeveralWays(
+            payload, base::Bind(&AbstractPaddablePayloadDecoderTest::
+                                    ValidatorForVerifyDetectsPaddingTooLong,
+                                header, expected_missing_length,
+                                base::ConstRef(listener))));
+  }
+
+  // Verifies that we get OnPaddingTooLong for a padded frame payload whose
+  // (randomly selected) payload length is less than total_pad_length_.
+  // Flags will be selected at random, except PADDED will be set and
+  // flags_to_avoid will not be set. The stream id is selected at random.
+  ::testing::AssertionResult VerifyDetectsPaddingTooLong() {
+    uint8_t flags = RandFlags() | Http2FrameFlag::FLAG_PADDED;
+
+    // Create an all padding payload for total_pad_length_.
+    int payload_length = 0;
+    Http2FrameBuilder fb;
+    if (IsPadded()) {
+      fb.AppendUInt8(pad_length());
+      fb.AppendZeroes(pad_length());
+      VLOG(1) << "fb.size=" << fb.size();
+      // Pick a random length for the payload that is shorter than neccesary.
+      payload_length = Random().Rand32() % fb.size();
+    }
+
+    VLOG(1) << "payload_length=" << payload_length;
+    std::string payload = fb.buffer().substr(0, payload_length);
+
+    // The missing length is the amount we cut off the end, unless
+    // payload_length is zero, in which case the decoder knows only that 1
+    // byte, the Pad Length field, is missing.
+    int missing_length = payload_length == 0 ? 1 : fb.size() - payload_length;
+    VLOG(1) << "missing_length=" << missing_length;
+
+    const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(),
+                                  flags, RandStreamId());
+    VERIFY_AND_RETURN_SUCCESS(
+        VerifyDetectsPaddingTooLong(payload, header, missing_length));
+  }
+
+  // total_pad_length_ includes the size of the Pad Length field, and thus
+  // ranges from 0 (no PADDED flag) to 256 (Pad Length == 255).
+  const size_t total_pad_length_;
+  Http2FrameBuilder frame_builder_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PAYLOAD_DECODER_BASE_TEST_UTIL_H_
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder.cc b/net/http2/decoder/payload_decoders/ping_payload_decoder.cc
new file mode 100644
index 0000000..abe43d0a
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+namespace {
+constexpr auto kOpaqueSize = Http2PingFields::EncodedSize();
+}
+
+DecodeStatus PingPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
+                                                      DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "PingPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::PING, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_ACK));
+
+  // Is the payload entirely in the decode buffer and is it the correct size?
+  // Given the size of the header and payload (17 bytes total), this is most
+  // likely the case the vast majority of the time.
+  if (db->Remaining() == kOpaqueSize && total_length == kOpaqueSize) {
+    // Special case this situation as it allows us to avoid any copying;
+    // the other path makes two copies, first into the buffer in
+    // Http2StructureDecoder as it accumulates the 8 bytes of opaque data,
+    // and a second copy into the Http2PingFields member of in this class.
+    // This supports the claim that this decoder is (mostly) non-buffering.
+    static_assert(sizeof(Http2PingFields) == kOpaqueSize,
+                  "If not, then can't enter this block!");
+    auto ping = reinterpret_cast<const Http2PingFields*>(db->cursor());
+    if (frame_header.IsAck()) {
+      state->listener()->OnPingAck(frame_header, *ping);
+    } else {
+      state->listener()->OnPing(frame_header, *ping);
+    }
+    db->AdvanceCursor(kOpaqueSize);
+    return DecodeStatus::kDecodeDone;
+  }
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&ping_fields_, db));
+}
+
+DecodeStatus PingPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
+                                                       DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload();
+  DCHECK_EQ(Http2FrameType::PING, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&ping_fields_, db));
+}
+
+DecodeStatus PingPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                              DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      const Http2FrameHeader& frame_header = state->frame_header();
+      if (frame_header.IsAck()) {
+        state->listener()->OnPingAck(frame_header, ping_fields_);
+      } else {
+        state->listener()->OnPing(frame_header, ping_fields_);
+      }
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder.h b/net/http2/decoder/payload_decoders/ping_payload_decoder.h
new file mode 100644
index 0000000..0eb10a2e
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PING frame; for the RFC, see:
+//     http://httpwg.org/specs/rfc7540.html#PING
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PingPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PingPayloadDecoder {
+ public:
+  // Starts the decoding of a PING frame's payload, and completes it if the
+  // entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PING frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PingPayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2PingFields ping_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PING_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
new file mode 100644
index 0000000..c3696f7c
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/ping_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class PingPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() { return Http2FrameType::PING; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(PingPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PingPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->ping_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnPing(const Http2FrameHeader& header,
+              const Http2PingFields& ping) override {
+    VLOG(1) << "OnPing: " << header << "; " << ping;
+    StartAndEndFrame(header)->OnPing(header, ping);
+  }
+
+  void OnPingAck(const Http2FrameHeader& header,
+                 const Http2PingFields& ping) override {
+    VLOG(1) << "OnPingAck: " << header << "; " << ping;
+    StartAndEndFrame(header)->OnPingAck(header, ping);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PingPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<PingPayloadDecoder,
+                                        PingPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2PingFields::EncodedSize();
+  }
+
+ protected:
+  Http2PingFields RandPingFields() {
+    Http2PingFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2PingFields.
+TEST_F(PingPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandPingFields());
+  fb.Append(RandPingFields());
+  fb.Append(RandPingFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&PingPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(PingPayloadDecoderTest, Ping) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PingFields fields = RandPingFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PING,
+                            RandFlags() & ~Http2FrameFlag::FLAG_ACK,
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_ping = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+TEST_F(PingPayloadDecoderTest, PingAck) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PingFields fields;
+    Randomize(&fields, RandomPtr());
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PING,
+                            RandFlags() | Http2FrameFlag::FLAG_ACK,
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_ping = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder.cc b/net/http2/decoder/payload_decoders/priority_payload_decoder.cc
new file mode 100644
index 0000000..51ae80b
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus PriorityPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "PriorityPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  // PRIORITY frames have no flags.
+  DCHECK_EQ(0, state->frame_header().flags);
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&priority_fields_, db));
+}
+
+DecodeStatus PriorityPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "PriorityPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&priority_fields_, db));
+}
+
+DecodeStatus PriorityPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                  DecodeStatus status) {
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnPriorityFrame(state->frame_header(),
+                                         priority_fields_);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder.h b/net/http2/decoder/payload_decoders/priority_payload_decoder.h
new file mode 100644
index 0000000..d41a404
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PRIORITY frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PriorityPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PriorityPayloadDecoder {
+ public:
+  // Starts the decoding of a PRIORITY frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PRIORITY frame that has been split across decode
+  // buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PriorityPayloadDecoderPeer;
+
+  // Determines whether to report the PRIORITY to the listener, wait for more
+  // input, or to report a Frame Size Error.
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2PriorityFields priority_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PRIORITY_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
new file mode 100644
index 0000000..dabcba5
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/priority_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class PriorityPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::PRIORITY;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(PriorityPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PriorityPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->priority_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnPriorityFrame(const Http2FrameHeader& header,
+                       const Http2PriorityFields& priority_fields) override {
+    VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+    StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PriorityPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<PriorityPayloadDecoder,
+                                        PriorityPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2PriorityFields::EncodedSize();
+  }
+
+ protected:
+  Http2PriorityFields RandPriorityFields() {
+    Http2PriorityFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2PriorityFields.
+TEST_F(PriorityPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandPriorityFields());
+  fb.Append(RandPriorityFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&PriorityPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(PriorityPayloadDecoderTest, VariousPayloads) {
+  for (int n = 0; n < 100; ++n) {
+    Http2PriorityFields fields = RandPriorityFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::PRIORITY, RandFlags(),
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_priority = fields;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
new file mode 100644
index 0000000..f160d1e
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
@@ -0,0 +1,172 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_bug_tracker.h"
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         PushPromisePayloadDecoder::PayloadState v) {
+  switch (v) {
+    case PushPromisePayloadDecoder::PayloadState::kReadPadLength:
+      return out << "kReadPadLength";
+    case PushPromisePayloadDecoder::PayloadState::
+        kStartDecodingPushPromiseFields:
+      return out << "kStartDecodingPushPromiseFields";
+    case PushPromisePayloadDecoder::PayloadState::kReadPayload:
+      return out << "kReadPayload";
+    case PushPromisePayloadDecoder::PayloadState::kSkipPadding:
+      return out << "kSkipPadding";
+    case PushPromisePayloadDecoder::PayloadState::
+        kResumeDecodingPushPromiseFields:
+      return out << "kResumeDecodingPushPromiseFields";
+  }
+  return out << static_cast<int>(v);
+}
+
+DecodeStatus PushPromisePayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "PushPromisePayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+
+  DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(
+      0, frame_header.flags &
+             ~(Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED));
+
+  if (!frame_header.IsPadded()) {
+    // If it turns out that PUSH_PROMISE frames without padding are sufficiently
+    // common, and that they are usually short enough that they fit entirely
+    // into one DecodeBuffer, we can detect that here and implement a special
+    // case, avoiding the state machine in ResumeDecodingPayload.
+    payload_state_ = PayloadState::kStartDecodingPushPromiseFields;
+  } else {
+    payload_state_ = PayloadState::kReadPadLength;
+  }
+  state->InitializeRemainders();
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus PushPromisePayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+
+  const Http2FrameHeader& frame_header = state->frame_header();
+  DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
+  DCHECK_LE(state->remaining_payload(), frame_header.payload_length);
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+
+  DecodeStatus status;
+  while (true) {
+    DVLOG(2)
+        << "PushPromisePayloadDecoder::ResumeDecodingPayload payload_state_="
+        << payload_state_;
+    switch (payload_state_) {
+      case PayloadState::kReadPadLength:
+        DCHECK_EQ(state->remaining_payload(), frame_header.payload_length);
+        // ReadPadLength handles the OnPadLength callback, and updating the
+        // remaining_payload and remaining_padding fields. If the amount of
+        // padding is too large to fit in the frame's payload, ReadPadLength
+        // instead calls OnPaddingTooLong and returns kDecodeError.
+        // Suppress the call to OnPadLength because we haven't yet called
+        // OnPushPromiseStart, which needs to wait until we've decoded the
+        // Promised Stream ID.
+        status = state->ReadPadLength(db, /*report_pad_length*/ false);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kReadPadLength;
+          return status;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kStartDecodingPushPromiseFields:
+        status =
+            state->StartDecodingStructureInPayload(&push_promise_fields_, db);
+        if (status != DecodeStatus::kDecodeDone) {
+          payload_state_ = PayloadState::kResumeDecodingPushPromiseFields;
+          return status;
+        }
+        // Finished decoding the Promised Stream ID. Can now tell the listener
+        // that we're starting to decode a PUSH_PROMISE frame.
+        ReportPushPromise(state);
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kReadPayload:
+        DCHECK_LT(state->remaining_payload(), frame_header.payload_length);
+        DCHECK_LE(state->remaining_payload(),
+                  frame_header.payload_length -
+                      Http2PushPromiseFields::EncodedSize());
+        DCHECK_LE(
+            state->remaining_payload(),
+            frame_header.payload_length -
+                Http2PushPromiseFields::EncodedSize() -
+                (frame_header.IsPadded() ? (1 + state->remaining_padding())
+                                         : 0));
+        {
+          size_t avail = state->AvailablePayload(db);
+          state->listener()->OnHpackFragment(db->cursor(), avail);
+          db->AdvanceCursor(avail);
+          state->ConsumePayload(avail);
+        }
+        if (state->remaining_payload() > 0) {
+          payload_state_ = PayloadState::kReadPayload;
+          return DecodeStatus::kDecodeInProgress;
+        }
+      // FALLTHROUGH_INTENDED
+
+      case PayloadState::kSkipPadding:
+        // SkipPadding handles the OnPadding callback.
+        if (state->SkipPadding(db)) {
+          state->listener()->OnPushPromiseEnd();
+          return DecodeStatus::kDecodeDone;
+        }
+        payload_state_ = PayloadState::kSkipPadding;
+        return DecodeStatus::kDecodeInProgress;
+
+      case PayloadState::kResumeDecodingPushPromiseFields:
+        status =
+            state->ResumeDecodingStructureInPayload(&push_promise_fields_, db);
+        if (status == DecodeStatus::kDecodeDone) {
+          // Finished decoding the Promised Stream ID. Can now tell the listener
+          // that we're starting to decode a PUSH_PROMISE frame.
+          ReportPushPromise(state);
+          payload_state_ = PayloadState::kReadPayload;
+          continue;
+        }
+        payload_state_ = PayloadState::kResumeDecodingPushPromiseFields;
+        return status;
+    }
+    HTTP2_BUG << "PayloadState: " << payload_state_;
+  }
+}
+
+void PushPromisePayloadDecoder::ReportPushPromise(FrameDecoderState* state) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  if (frame_header.IsPadded()) {
+    state->listener()->OnPushPromiseStart(frame_header, push_promise_fields_,
+                                          1 + state->remaining_padding());
+  } else {
+    state->listener()->OnPushPromiseStart(frame_header, push_promise_fields_,
+                                          0);
+  }
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h
new file mode 100644
index 0000000..fe6c523
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a PUSH_PROMISE frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class PushPromisePayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE PushPromisePayloadDecoder {
+ public:
+  // States during decoding of a PUSH_PROMISE frame.
+  enum class PayloadState {
+    // The frame is padded and we need to read the PAD_LENGTH field (1 byte).
+    kReadPadLength,
+
+    // Ready to start decoding the fixed size fields of the PUSH_PROMISE
+    // frame into push_promise_fields_.
+    kStartDecodingPushPromiseFields,
+
+    // The decoder has already called OnPushPromiseStart, and is now reporting
+    // the HPACK block fragment to the listener's OnHpackFragment method.
+    kReadPayload,
+
+    // The decoder has finished with the HPACK block fragment, and is now
+    // ready to skip the trailing padding, if the frame has any.
+    kSkipPadding,
+
+    // The fixed size fields weren't all available when the decoder first tried
+    // to decode them (state kStartDecodingPushPromiseFields); this state
+    // resumes the decoding when ResumeDecodingPayload is called later.
+    kResumeDecodingPushPromiseFields,
+  };
+
+  // Starts the decoding of a PUSH_PROMISE frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a PUSH_PROMISE frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::PushPromisePayloadDecoderPeer;
+
+  void ReportPushPromise(FrameDecoderState* state);
+
+  PayloadState payload_state_;
+  Http2PushPromiseFields push_promise_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_PUSH_PROMISE_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
new file mode 100644
index 0000000..ba1a815
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
@@ -0,0 +1,151 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/push_promise_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class PushPromisePayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::PUSH_PROMISE;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_PADDED;
+  }
+
+  static void Randomize(PushPromisePayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "PushPromisePayloadDecoderPeer::Randomize";
+    CorruptEnum(&p->payload_state_, rng);
+    test::Randomize(&p->push_promise_fields_, rng);
+  }
+};
+
+namespace {
+
+// Listener listens for only those methods expected by the payload decoder
+// under test, and forwards them onto the FrameParts instance for the current
+// frame.
+struct Listener : public FramePartsCollector {
+  void OnPushPromiseStart(const Http2FrameHeader& header,
+                          const Http2PushPromiseFields& promise,
+                          size_t total_padding_length) override {
+    VLOG(1) << "OnPushPromiseStart header: " << header
+            << "  promise: " << promise
+            << "  total_padding_length: " << total_padding_length;
+    EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
+    StartFrame(header)->OnPushPromiseStart(header, promise,
+                                           total_padding_length);
+  }
+
+  void OnHpackFragment(const char* data, size_t len) override {
+    VLOG(1) << "OnHpackFragment: len=" << len;
+    CurrentFrame()->OnHpackFragment(data, len);
+  }
+
+  void OnPushPromiseEnd() override {
+    VLOG(1) << "OnPushPromiseEnd";
+    EndFrame()->OnPushPromiseEnd();
+  }
+
+  void OnPadding(const char* padding, size_t skipped_length) override {
+    VLOG(1) << "OnPadding: " << skipped_length;
+    CurrentFrame()->OnPadding(padding, skipped_length);
+  }
+
+  void OnPaddingTooLong(const Http2FrameHeader& header,
+                        size_t missing_length) override {
+    VLOG(1) << "OnPaddingTooLong: " << header
+            << "; missing_length: " << missing_length;
+    FrameError(header)->OnPaddingTooLong(header, missing_length);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class PushPromisePayloadDecoderTest
+    : public AbstractPaddablePayloadDecoderTest<PushPromisePayloadDecoder,
+                                                PushPromisePayloadDecoderPeer,
+                                                Listener> {
+ public:
+  static bool ApproveSizeForTruncated(size_t size) {
+    return size != Http2PushPromiseFields::EncodedSize();
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(VariousPadLengths,
+                        PushPromisePayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
+
+// Payload contains the required Http2PushPromiseFields, followed by some
+// (fake) HPACK payload.
+TEST_P(PushPromisePayloadDecoderTest, VariousHpackPayloadSizes) {
+  for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
+    LOG(INFO) << "###########   hpack_size = " << hpack_size << "  ###########";
+    Reset();
+    string hpack_payload = Random().RandString(hpack_size);
+    Http2PushPromiseFields push_promise{RandStreamId()};
+    frame_builder_.Append(push_promise);
+    frame_builder_.Append(hpack_payload);
+    MaybeAppendTrailingPadding();
+    Http2FrameHeader frame_header(frame_builder_.size(),
+                                  Http2FrameType::PUSH_PROMISE, RandFlags(),
+                                  RandStreamId());
+    set_frame_header(frame_header);
+    FrameParts expected(frame_header, hpack_payload, total_pad_length_);
+    expected.opt_push_promise = push_promise;
+    EXPECT_TRUE(
+        DecodePayloadAndValidateSeveralWays(frame_builder_.buffer(), expected));
+  }
+}
+
+// Confirm we get an error if the payload is not long enough for the required
+// portion of the payload, regardless of the amount of (valid) padding.
+TEST_P(PushPromisePayloadDecoderTest, Truncated) {
+  Http2PushPromiseFields push_promise{RandStreamId()};
+  Http2FrameBuilder fb;
+  fb.Append(push_promise);
+  EXPECT_TRUE(VerifyDetectsMultipleFrameSizeErrors(
+      0, fb.buffer(),
+      base::Bind(&PushPromisePayloadDecoderTest::ApproveSizeForTruncated),
+      total_pad_length_));
+}
+
+// Confirm we get an error if the PADDED flag is set but the payload is not
+// long enough to hold even the Pad Length amount of padding.
+TEST_P(PushPromisePayloadDecoderTest, PaddingTooLong) {
+  EXPECT_TRUE(VerifyDetectsPaddingTooLong());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
new file mode 100644
index 0000000..e9d86fb
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus RstStreamPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "RstStreamPayloadDecoder::StartDecodingPayload: "
+           << state->frame_header();
+  DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  // RST_STREAM has no flags.
+  DCHECK_EQ(0, state->frame_header().flags);
+  state->InitializeRemainders();
+  return HandleStatus(
+      state, state->StartDecodingStructureInPayload(&rst_stream_fields_, db));
+}
+
+DecodeStatus RstStreamPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "RstStreamPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(
+      state, state->ResumeDecodingStructureInPayload(&rst_stream_fields_, db));
+}
+
+DecodeStatus RstStreamPayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                   DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnRstStream(state->frame_header(),
+                                     rst_stream_fields_.error_code);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called by the FrameDecoderState.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h
new file mode 100644
index 0000000..cdfd6587
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a RST_STREAM frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class RstStreamPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE RstStreamPayloadDecoder {
+ public:
+  // Starts the decoding of a RST_STREAM frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a RST_STREAM frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::RstStreamPayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2RstStreamFields rst_stream_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_RST_STREAM_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
new file mode 100644
index 0000000..703d98ff
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RstStreamPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::RST_STREAM;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(RstStreamPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "RstStreamPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->rst_stream_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnRstStream(const Http2FrameHeader& header,
+                   Http2ErrorCode error_code) override {
+    VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+    StartAndEndFrame(header)->OnRstStream(header, error_code);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class RstStreamPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<RstStreamPayloadDecoder,
+                                        RstStreamPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2RstStreamFields::EncodedSize();
+  }
+
+ protected:
+  Http2RstStreamFields RandRstStreamFields() {
+    Http2RstStreamFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2RstStreamFields.
+TEST_F(RstStreamPayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandRstStreamFields());
+  fb.Append(RandRstStreamFields());
+  fb.Append(RandRstStreamFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&RstStreamPayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(RstStreamPayloadDecoderTest, AllErrors) {
+  for (auto error_code : AllHttp2ErrorCodes()) {
+    Http2RstStreamFields fields{error_code};
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::RST_STREAM, RandFlags(),
+                            RandStreamId());
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_rst_stream_error_code = error_code;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder.cc b/net/http2/decoder/payload_decoders/settings_payload_decoder.cc
new file mode 100644
index 0000000..f8cc52cb
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder.cc
@@ -0,0 +1,97 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus SettingsPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "SettingsPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK_EQ(Http2FrameType::SETTINGS, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+  DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::FLAG_ACK));
+
+  if (frame_header.IsAck()) {
+    if (total_length == 0) {
+      state->listener()->OnSettingsAck(frame_header);
+      return DecodeStatus::kDecodeDone;
+    } else {
+      state->InitializeRemainders();
+      return state->ReportFrameSizeError();
+    }
+  } else {
+    state->InitializeRemainders();
+    state->listener()->OnSettingsStart(frame_header);
+    return StartDecodingSettings(state, db);
+  }
+}
+
+DecodeStatus SettingsPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "SettingsPayloadDecoder::ResumeDecodingPayload"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::SETTINGS, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+
+  DecodeStatus status =
+      state->ResumeDecodingStructureInPayload(&setting_fields_, db);
+  if (status == DecodeStatus::kDecodeDone) {
+    state->listener()->OnSetting(setting_fields_);
+    return StartDecodingSettings(state, db);
+  }
+  return HandleNotDone(state, db, status);
+}
+
+DecodeStatus SettingsPayloadDecoder::StartDecodingSettings(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "SettingsPayloadDecoder::StartDecodingSettings"
+           << "  remaining_payload=" << state->remaining_payload()
+           << "  db->Remaining=" << db->Remaining();
+  while (state->remaining_payload() > 0) {
+    DecodeStatus status =
+        state->StartDecodingStructureInPayload(&setting_fields_, db);
+    if (status == DecodeStatus::kDecodeDone) {
+      state->listener()->OnSetting(setting_fields_);
+      continue;
+    }
+    return HandleNotDone(state, db, status);
+  }
+  DVLOG(2) << "LEAVING SettingsPayloadDecoder::StartDecodingSettings"
+           << "\n\tdb->Remaining=" << db->Remaining()
+           << "\n\t remaining_payload=" << state->remaining_payload();
+  state->listener()->OnSettingsEnd();
+  return DecodeStatus::kDecodeDone;
+}
+
+DecodeStatus SettingsPayloadDecoder::HandleNotDone(FrameDecoderState* state,
+                                                   DecodeBuffer* db,
+                                                   DecodeStatus status) {
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload()
+      << "; db->Remaining=" << db->Remaining();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder.h b/net/http2/decoder/payload_decoders/settings_payload_decoder.h
new file mode 100644
index 0000000..40d3c36
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a SETTINGS frame; for the RFC, see:
+//     http://httpwg.org/specs/rfc7540.html#SETTINGS
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class SettingsPayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE SettingsPayloadDecoder {
+ public:
+  // Starts the decoding of a SETTINGS frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a SETTINGS frame that has been split across decode
+  // buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::SettingsPayloadDecoderPeer;
+
+  // Decodes as many settings as are available in the decode buffer, starting at
+  // the first byte of one setting; if a single setting is split across buffers,
+  // ResumeDecodingPayload will handle starting from where the previous call
+  // left off, and then will call StartDecodingSettings.
+  DecodeStatus StartDecodingSettings(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+  // Decoding a single SETTING returned a status other than kDecodeDone; this
+  // method just brings together the DCHECKs to reduce duplication.
+  DecodeStatus HandleNotDone(FrameDecoderState* state,
+                             DecodeBuffer* db,
+                             DecodeStatus status);
+
+  Http2SettingFields setting_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_SETTINGS_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
new file mode 100644
index 0000000..7a53797
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/settings_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class SettingsPayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::SETTINGS;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() {
+    return Http2FrameFlag::FLAG_ACK;
+  }
+
+  static void Randomize(SettingsPayloadDecoder* p, RandomBase* rng) {
+    VLOG(1) << "SettingsPayloadDecoderPeer::Randomize";
+    test::Randomize(&p->setting_fields_, rng);
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnSettingsStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnSettingsStart: " << header;
+    EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
+    EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
+    StartFrame(header)->OnSettingsStart(header);
+  }
+
+  void OnSetting(const Http2SettingFields& setting_fields) override {
+    VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+    CurrentFrame()->OnSetting(setting_fields);
+  }
+
+  void OnSettingsEnd() override {
+    VLOG(1) << "OnSettingsEnd";
+    EndFrame()->OnSettingsEnd();
+  }
+
+  void OnSettingsAck(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnSettingsAck: " << header;
+    StartAndEndFrame(header)->OnSettingsAck(header);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class SettingsPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<SettingsPayloadDecoder,
+                                        SettingsPayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForSettingsWrongSize(size_t size) {
+    // Should get an error if size is not an integral multiple of the size
+    // of one setting.
+    return 0 != (size % Http2SettingFields::EncodedSize());
+  }
+
+  static bool ApproveSizeForSettingsAkcWrongSize(size_t size) {
+    return size != 0;
+  }
+
+ protected:
+  Http2SettingFields RandSettingsFields() {
+    Http2SettingFields fields;
+    test::Randomize(&fields, RandomPtr());
+    return fields;
+  }
+};
+
+// Confirm we get an error if the SETTINGS payload is not the correct size
+// to hold exactly zero or more whole Http2SettingFields.
+TEST_F(SettingsPayloadDecoderTest, SettingsWrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(
+          &SettingsPayloadDecoderTest::ApproveSizeForSettingsWrongSize)));
+}
+
+// Confirm we get an error if the SETTINGS ACK payload is not empty.
+TEST_F(SettingsPayloadDecoderTest, SettingsAkcWrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  fb.Append(RandSettingsFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      Http2FrameFlag::FLAG_ACK, fb.buffer(),
+      base::Bind(
+          &SettingsPayloadDecoderTest::ApproveSizeForSettingsAkcWrongSize)));
+}
+
+// SETTINGS must have stream_id==0, but the payload decoder doesn't check that.
+TEST_F(SettingsPayloadDecoderTest, SettingsAck) {
+  for (int stream_id = 0; stream_id < 3; ++stream_id) {
+    Http2FrameHeader header(0, Http2FrameType::SETTINGS,
+                            RandFlags() | Http2FrameFlag::FLAG_ACK, stream_id);
+    set_frame_header(header);
+    FrameParts expected(header);
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays("", expected));
+  }
+}
+
+// Try several values of each known SETTINGS parameter.
+TEST_F(SettingsPayloadDecoderTest, OneRealSetting) {
+  std::vector<uint32_t> values = {0, 1, 0xffffffff, Random().Rand32()};
+  for (auto param : AllHttp2SettingsParameters()) {
+    for (uint32_t value : values) {
+      Http2SettingFields fields(param, value);
+      Http2FrameBuilder fb;
+      fb.Append(fields);
+      Http2FrameHeader header(fb.size(), Http2FrameType::SETTINGS, RandFlags(),
+                              RandStreamId());
+      set_frame_header(header);
+      FrameParts expected(header);
+      expected.settings.push_back(fields);
+      EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+    }
+  }
+}
+
+// Decode a SETTINGS frame with lots of fields.
+TEST_F(SettingsPayloadDecoderTest, ManySettings) {
+  const uint32_t num_settings = 100;
+  const uint32_t size = Http2SettingFields::EncodedSize() * num_settings;
+  Http2FrameHeader header(size, Http2FrameType::SETTINGS,
+                          RandFlags(),  // & ~Http2FrameFlag::FLAG_ACK,
+                          RandStreamId());
+  set_frame_header(header);
+  FrameParts expected(header);
+  Http2FrameBuilder fb;
+  for (size_t n = 0; n < num_settings; ++n) {
+    Http2SettingFields fields(static_cast<Http2SettingsParameter>(n),
+                              Random().Rand32());
+    fb.Append(fields);
+    expected.settings.push_back(fields);
+  }
+  ASSERT_EQ(size, fb.size());
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc b/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc
new file mode 100644
index 0000000..ec020c1
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder.cc
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus UnknownPayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+
+  DVLOG(2) << "UnknownPayloadDecoder::StartDecodingPayload: " << frame_header;
+  DCHECK(!IsSupportedHttp2FrameType(frame_header.type)) << frame_header;
+  DCHECK_LE(db->Remaining(), frame_header.payload_length);
+
+  state->InitializeRemainders();
+  state->listener()->OnUnknownStart(frame_header);
+  return ResumeDecodingPayload(state, db);
+}
+
+DecodeStatus UnknownPayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload "
+           << "remaining_payload=" << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+  DCHECK(!IsSupportedHttp2FrameType(state->frame_header().type))
+      << state->frame_header();
+  DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
+  DCHECK_LE(db->Remaining(), state->remaining_payload());
+
+  size_t avail = db->Remaining();
+  if (avail > 0) {
+    state->listener()->OnUnknownPayload(db->cursor(), avail);
+    db->AdvanceCursor(avail);
+    state->ConsumePayload(avail);
+  }
+  if (state->remaining_payload() == 0) {
+    state->listener()->OnUnknownEnd();
+    return DecodeStatus::kDecodeDone;
+  }
+  return DecodeStatus::kDecodeInProgress;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder.h b/net/http2/decoder/payload_decoders/unknown_payload_decoder.h
new file mode 100644
index 0000000..26a1ee2
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a frame whose type unknown.  According to the HTTP/2
+// specification (http://httpwg.org/specs/rfc7540.html#FrameHeader):
+//     Implementations MUST ignore and discard any frame that has
+//     a type that is unknown.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE UnknownPayloadDecoder {
+ public:
+  // Starts decoding a payload of unknown type; just passes it to the listener.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a payload of unknown type that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_UNKNOWN_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
new file mode 100644
index 0000000..302cec47
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/unknown_payload_decoder.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+Http2FrameType g_unknown_frame_type;
+}  // namespace
+
+// Provides friend access to an instance of the payload decoder, and also
+// provides info to aid in testing.
+class UnknownPayloadDecoderPeer {
+ public:
+  static Http2FrameType FrameType() { return g_unknown_frame_type; }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(UnknownPayloadDecoder* p, RandomBase* rng) {
+    // UnknownPayloadDecoder has no fields, so there is nothing to randomize.
+    static_assert(std::is_empty<UnknownPayloadDecoder>::value,
+                  "Need to randomize fields of UnknownPayloadDecoder");
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnUnknownStart(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnUnknownStart: " << header;
+    StartFrame(header)->OnUnknownStart(header);
+  }
+
+  void OnUnknownPayload(const char* data, size_t len) override {
+    VLOG(1) << "OnUnknownPayload: len=" << len;
+    CurrentFrame()->OnUnknownPayload(data, len);
+  }
+
+  void OnUnknownEnd() override {
+    VLOG(1) << "OnUnknownEnd";
+    EndFrame()->OnUnknownEnd();
+  }
+};
+
+constexpr bool SupportedFrameType = false;
+
+class UnknownPayloadDecoderTest
+    : public AbstractPayloadDecoderTest<UnknownPayloadDecoder,
+                                        UnknownPayloadDecoderPeer,
+                                        Listener,
+                                        SupportedFrameType>,
+      public ::testing::WithParamInterface<uint32_t> {
+ protected:
+  UnknownPayloadDecoderTest() : length_(GetParam()) {
+    VLOG(1) << "################  length_=" << length_ << "  ################";
+
+    // Each test case will choose a random frame type that isn't supported.
+    do {
+      g_unknown_frame_type = static_cast<Http2FrameType>(Random().Rand8());
+    } while (IsSupportedHttp2FrameType(g_unknown_frame_type));
+  }
+
+  const uint32_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(VariousLengths,
+                        UnknownPayloadDecoderTest,
+                        ::testing::Values(0, 1, 2, 3, 255, 256));
+
+TEST_P(UnknownPayloadDecoderTest, ValidLength) {
+  string unknown_payload = Random().RandString(length_);
+  Http2FrameHeader frame_header(length_, g_unknown_frame_type, Random().Rand8(),
+                                RandStreamId());
+  set_frame_header(frame_header);
+  FrameParts expected(frame_header, unknown_payload);
+  EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(unknown_payload, expected));
+  // TODO(jamessynge): Check here (and in other such tests) that the fast
+  // and slow decode counts are both non-zero. Perhaps also add some kind of
+  // test for the listener having been called. That could simply be a test
+  // that there is a single collected FrameParts instance, and that it matches
+  // expected.
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc b/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc
new file mode 100644
index 0000000..bc1e54ce1
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder.cc
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+
+#include "base/logging.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_http2_structures.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+
+DecodeStatus WindowUpdatePayloadDecoder::StartDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  const Http2FrameHeader& frame_header = state->frame_header();
+  const uint32_t total_length = frame_header.payload_length;
+
+  DVLOG(2) << "WindowUpdatePayloadDecoder::StartDecodingPayload: "
+           << frame_header;
+
+  DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, frame_header.type);
+  DCHECK_LE(db->Remaining(), total_length);
+
+  // WINDOW_UPDATE frames have no flags.
+  DCHECK_EQ(0, frame_header.flags);
+
+  // Special case for when the payload is the correct size and entirely in
+  // the buffer.
+  if (db->Remaining() == Http2WindowUpdateFields::EncodedSize() &&
+      total_length == Http2WindowUpdateFields::EncodedSize()) {
+    DoDecode(&window_update_fields_, db);
+    state->listener()->OnWindowUpdate(
+        frame_header, window_update_fields_.window_size_increment);
+    return DecodeStatus::kDecodeDone;
+  }
+  state->InitializeRemainders();
+  return HandleStatus(state, state->StartDecodingStructureInPayload(
+                                 &window_update_fields_, db));
+}
+
+DecodeStatus WindowUpdatePayloadDecoder::ResumeDecodingPayload(
+    FrameDecoderState* state,
+    DecodeBuffer* db) {
+  DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+           << state->remaining_payload()
+           << "; db->Remaining=" << db->Remaining();
+  DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, state->frame_header().type);
+  DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
+  return HandleStatus(state, state->ResumeDecodingStructureInPayload(
+                                 &window_update_fields_, db));
+}
+
+DecodeStatus WindowUpdatePayloadDecoder::HandleStatus(FrameDecoderState* state,
+                                                      DecodeStatus status) {
+  DVLOG(2) << "HandleStatus: status=" << status
+           << "; remaining_payload=" << state->remaining_payload();
+  if (status == DecodeStatus::kDecodeDone) {
+    if (state->remaining_payload() == 0) {
+      state->listener()->OnWindowUpdate(
+          state->frame_header(), window_update_fields_.window_size_increment);
+      return DecodeStatus::kDecodeDone;
+    }
+    // Payload is too long.
+    return state->ReportFrameSizeError();
+  }
+  // Not done decoding the structure. Either we've got more payload to decode,
+  // or we've run out because the payload is too short, in which case
+  // OnFrameSizeError will have already been called.
+  DCHECK(
+      (status == DecodeStatus::kDecodeInProgress &&
+       state->remaining_payload() > 0) ||
+      (status == DecodeStatus::kDecodeError && state->remaining_payload() == 0))
+      << "\n status=" << status
+      << "; remaining_payload=" << state->remaining_payload();
+  return status;
+}
+
+}  // namespace net
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder.h b/net/http2/decoder/payload_decoders/window_update_payload_decoder.h
new file mode 100644
index 0000000..5764361
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
+#define NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
+
+// Decodes the payload of a WINDOW_UPDATE frame.
+
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/decoder/frame_decoder_state.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+class WindowUpdatePayloadDecoderPeer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE WindowUpdatePayloadDecoder {
+ public:
+  // Starts decoding a WINDOW_UPDATE frame's payload, and completes it if
+  // the entire payload is in the provided decode buffer.
+  DecodeStatus StartDecodingPayload(FrameDecoderState* state, DecodeBuffer* db);
+
+  // Resumes decoding a WINDOW_UPDATE frame's payload that has been split across
+  // decode buffers.
+  DecodeStatus ResumeDecodingPayload(FrameDecoderState* state,
+                                     DecodeBuffer* db);
+
+ private:
+  friend class test::WindowUpdatePayloadDecoderPeer;
+
+  DecodeStatus HandleStatus(FrameDecoderState* state, DecodeStatus status);
+
+  Http2WindowUpdateFields window_update_fields_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_DECODER_PAYLOAD_DECODERS_WINDOW_UPDATE_PAYLOAD_DECODER_H_
diff --git a/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc b/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
new file mode 100644
index 0000000..5517dab
--- /dev/null
+++ b/net/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/decoder/payload_decoders/window_update_payload_decoder.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "net/http2/decoder/frame_parts.h"
+#include "net/http2/decoder/frame_parts_collector.h"
+#include "net/http2/decoder/http2_frame_decoder_listener.h"
+#include "net/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class WindowUpdatePayloadDecoderPeer {
+ public:
+  static constexpr Http2FrameType FrameType() {
+    return Http2FrameType::WINDOW_UPDATE;
+  }
+
+  // Returns the mask of flags that affect the decoding of the payload (i.e.
+  // flags that that indicate the presence of certain fields or padding).
+  static constexpr uint8_t FlagsAffectingPayloadDecoding() { return 0; }
+
+  static void Randomize(WindowUpdatePayloadDecoder* p, RandomBase* rng) {
+    test::Randomize(&p->window_update_fields_, rng);
+    VLOG(1) << "WindowUpdatePayloadDecoderPeer::Randomize "
+            << "window_update_fields_: " << p->window_update_fields_;
+  }
+};
+
+namespace {
+
+struct Listener : public FramePartsCollector {
+  void OnWindowUpdate(const Http2FrameHeader& header,
+                      uint32_t window_size_increment) override {
+    VLOG(1) << "OnWindowUpdate: " << header
+            << "; window_size_increment=" << window_size_increment;
+    EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
+    StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
+  }
+
+  void OnFrameSizeError(const Http2FrameHeader& header) override {
+    VLOG(1) << "OnFrameSizeError: " << header;
+    FrameError(header)->OnFrameSizeError(header);
+  }
+};
+
+class WindowUpdatePayloadDecoderTest
+    : public AbstractPayloadDecoderTest<WindowUpdatePayloadDecoder,
+                                        WindowUpdatePayloadDecoderPeer,
+                                        Listener> {
+ public:
+  static bool ApproveSizeForWrongSize(size_t size) {
+    return size != Http2WindowUpdateFields::EncodedSize();
+  }
+
+ protected:
+  Http2WindowUpdateFields RandWindowUpdateFields() {
+    Http2WindowUpdateFields fields;
+    test::Randomize(&fields, RandomPtr());
+    VLOG(3) << "RandWindowUpdateFields: " << fields;
+    return fields;
+  }
+};
+
+// Confirm we get an error if the payload is not the correct size to hold
+// exactly one Http2WindowUpdateFields.
+TEST_F(WindowUpdatePayloadDecoderTest, WrongSize) {
+  Http2FrameBuilder fb;
+  fb.Append(RandWindowUpdateFields());
+  fb.Append(RandWindowUpdateFields());
+  fb.Append(RandWindowUpdateFields());
+  EXPECT_TRUE(VerifyDetectsFrameSizeError(
+      0, fb.buffer(),
+      base::Bind(&WindowUpdatePayloadDecoderTest::ApproveSizeForWrongSize)));
+}
+
+TEST_F(WindowUpdatePayloadDecoderTest, VariousPayloads) {
+  for (int n = 0; n < 100; ++n) {
+    uint32_t stream_id = n == 0 ? 0 : RandStreamId();
+    Http2WindowUpdateFields fields = RandWindowUpdateFields();
+    Http2FrameBuilder fb;
+    fb.Append(fields);
+    Http2FrameHeader header(fb.size(), Http2FrameType::WINDOW_UPDATE,
+                            RandFlags(), stream_id);
+    set_frame_header(header);
+    FrameParts expected(header);
+    expected.opt_window_update_increment = fields.window_size_increment;
+    EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(fb.buffer(), expected));
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_collector.cc b/net/http2/hpack/decoder/hpack_block_collector.cc
new file mode 100644
index 0000000..94f58ee4
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_collector.cc
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_collector.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/logging.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using std::string;
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+HpackBlockCollector::HpackBlockCollector() {}
+HpackBlockCollector::HpackBlockCollector(const HpackBlockCollector& other)
+    : pending_entry_(other.pending_entry_), entries_(other.entries_) {}
+HpackBlockCollector::~HpackBlockCollector() {}
+
+void HpackBlockCollector::OnIndexedHeader(size_t index) {
+  pending_entry_.OnIndexedHeader(index);
+  PushPendingEntry();
+}
+void HpackBlockCollector::OnDynamicTableSizeUpdate(size_t size) {
+  pending_entry_.OnDynamicTableSizeUpdate(size);
+  PushPendingEntry();
+}
+void HpackBlockCollector::OnStartLiteralHeader(HpackEntryType header_type,
+                                               size_t maybe_name_index) {
+  pending_entry_.OnStartLiteralHeader(header_type, maybe_name_index);
+}
+void HpackBlockCollector::OnNameStart(bool huffman_encoded, size_t len) {
+  pending_entry_.OnNameStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnNameData(const char* data, size_t len) {
+  pending_entry_.OnNameData(data, len);
+}
+void HpackBlockCollector::OnNameEnd() {
+  pending_entry_.OnNameEnd();
+}
+void HpackBlockCollector::OnValueStart(bool huffman_encoded, size_t len) {
+  pending_entry_.OnValueStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnValueData(const char* data, size_t len) {
+  pending_entry_.OnValueData(data, len);
+}
+void HpackBlockCollector::OnValueEnd() {
+  pending_entry_.OnValueEnd();
+  PushPendingEntry();
+}
+
+void HpackBlockCollector::PushPendingEntry() {
+  EXPECT_TRUE(pending_entry_.IsComplete());
+  DVLOG(2) << "PushPendingEntry: " << pending_entry_;
+  entries_.push_back(pending_entry_);
+  EXPECT_TRUE(entries_.back().IsComplete());
+  pending_entry_.Clear();
+}
+void HpackBlockCollector::Clear() {
+  pending_entry_.Clear();
+  entries_.clear();
+}
+
+void HpackBlockCollector::ExpectIndexedHeader(size_t index) {
+  entries_.push_back(
+      HpackEntryCollector(HpackEntryType::kIndexedHeader, index));
+}
+void HpackBlockCollector::ExpectDynamicTableSizeUpdate(size_t size) {
+  entries_.push_back(
+      HpackEntryCollector(HpackEntryType::kDynamicTableSizeUpdate, size));
+}
+void HpackBlockCollector::ExpectNameIndexAndLiteralValue(HpackEntryType type,
+                                                         size_t index,
+                                                         bool value_huffman,
+                                                         const string& value) {
+  entries_.push_back(HpackEntryCollector(type, index, value_huffman, value));
+}
+void HpackBlockCollector::ExpectLiteralNameAndValue(HpackEntryType type,
+                                                    bool name_huffman,
+                                                    const string& name,
+                                                    bool value_huffman,
+                                                    const string& value) {
+  entries_.push_back(
+      HpackEntryCollector(type, name_huffman, name, value_huffman, value));
+}
+
+void HpackBlockCollector::ShuffleEntries(RandomBase* rng) {
+  std::random_shuffle(entries_.begin(), entries_.end());
+}
+
+void HpackBlockCollector::AppendToHpackBlockBuilder(
+    HpackBlockBuilder* hbb) const {
+  CHECK(IsNotPending());
+  for (const auto& entry : entries_) {
+    entry.AppendToHpackBlockBuilder(hbb);
+  }
+}
+
+AssertionResult HpackBlockCollector::ValidateSoleIndexedHeader(
+    size_t ndx) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateIndexedHeader(ndx));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralValueHeader(
+    HpackEntryType expected_type,
+    size_t expected_index,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateLiteralValueHeader(
+      expected_type, expected_index, expected_value_huffman, expected_value));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralNameValueHeader(
+    HpackEntryType expected_type,
+    bool expected_name_huffman,
+    StringPiece expected_name,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateLiteralNameValueHeader(
+      expected_type, expected_name_huffman, expected_name,
+      expected_value_huffman, expected_value));
+  return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleDynamicTableSizeUpdate(
+    size_t size) const {
+  VERIFY_TRUE(pending_entry_.IsClear());
+  VERIFY_EQ(1u, entries_.size());
+  VERIFY_TRUE(entries_.front().ValidateDynamicTableSizeUpdate(size));
+  return AssertionSuccess();
+}
+
+AssertionResult HpackBlockCollector::VerifyEq(
+    const HpackBlockCollector& that) const {
+  VERIFY_EQ(pending_entry_, that.pending_entry_);
+  VERIFY_EQ(entries_, that.entries_);
+  return AssertionSuccess();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_collector.h b/net/http2/hpack/decoder/hpack_block_collector.h
new file mode 100644
index 0000000..2b283b9
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_collector.h
@@ -0,0 +1,128 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
+
+// HpackBlockCollector implements HpackEntryDecoderListener in order to record
+// the calls using HpackEntryCollector instances (one per HPACK entry). This
+// supports testing of HpackBlockDecoder, which decodes entire HPACK blocks.
+//
+// In addition to implementing the callback methods, HpackBlockCollector also
+// supports comparing two HpackBlockCollector instances (i.e. an expected and
+// an actual), or a sole HPACK entry against an expected value.
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+namespace net {
+namespace test {
+
+class RandomBase;
+
+class HpackBlockCollector : public HpackEntryDecoderListener {
+ public:
+  // Implementations of HpackEntryDecoderListener, forwarding to pending_entry_,
+  // an HpackEntryCollector for the "in-progress" HPACK entry. OnIndexedHeader
+  // and OnDynamicTableSizeUpdate are pending only for that one call, while
+  // OnStartLiteralHeader is followed by many calls, ending with OnValueEnd.
+  // Once all the calls for one HPACK entry have been received, PushPendingEntry
+  // is used to append the pending_entry_ entry to the collected entries_.
+  HpackBlockCollector();
+  HpackBlockCollector(const HpackBlockCollector& other);
+  ~HpackBlockCollector() override;
+  void OnIndexedHeader(size_t index) override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+  void OnStartLiteralHeader(HpackEntryType header_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+
+  // Methods for creating a set of expectations (i.e. HPACK entries to compare
+  // against those collected by another instance of HpackBlockCollector).
+
+  // Add an HPACK entry for an indexed header.
+  void ExpectIndexedHeader(size_t index);
+
+  // Add an HPACK entry for a dynamic table size update.
+  void ExpectDynamicTableSizeUpdate(size_t size);
+
+  // Add an HPACK entry for a header entry with an index for the name, and a
+  // literal value.
+  void ExpectNameIndexAndLiteralValue(HpackEntryType type,
+                                      size_t index,
+                                      bool value_huffman,
+                                      const std::string& value);
+
+  // Add an HPACK entry for a header entry with a literal name and value.
+  void ExpectLiteralNameAndValue(HpackEntryType type,
+                                 bool name_huffman,
+                                 const std::string& name,
+                                 bool value_huffman,
+                                 const std::string& value);
+
+  // Shuffle the entries, in support of generating an HPACK block of entries
+  // in some random order.
+  void ShuffleEntries(RandomBase* rng);
+
+  // Serialize entries_ to the HpackBlockBuilder.
+  void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is an
+  // Indexed Header with the specified index.
+  ::testing::AssertionResult ValidateSoleIndexedHeader(size_t ndx) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a
+  // Dynamic Table Size Update with the specified size.
+  ::testing::AssertionResult ValidateSoleDynamicTableSizeUpdate(
+      size_t size) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a Header
+  // entry with an index for the name and a literal value.
+  ::testing::AssertionResult ValidateSoleLiteralValueHeader(
+      HpackEntryType expected_type,
+      size_t expected_index,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Return AssertionSuccess if there is just one entry, and it is a Header
+  // with a literal name and literal value.
+  ::testing::AssertionResult ValidateSoleLiteralNameValueHeader(
+      HpackEntryType expected_type,
+      bool expected_name_huffman,
+      base::StringPiece expected_name,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  bool IsNotPending() const { return pending_entry_.IsClear(); }
+  bool IsClear() const { return IsNotPending() && entries_.empty(); }
+  void Clear();
+
+  ::testing::AssertionResult VerifyEq(const HpackBlockCollector& that) const;
+
+ private:
+  // Push the value of pending_entry_ onto entries_, and clear pending_entry_.
+  // The pending_entry_ must be complete.
+  void PushPendingEntry();
+
+  HpackEntryCollector pending_entry_;
+  std::vector<HpackEntryCollector> entries_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_block_decoder.cc b/net/http2/hpack/decoder/hpack_block_decoder.cc
new file mode 100644
index 0000000..29b6869
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_decoder.h"
+
+#include <stdint.h>
+
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+
+namespace net {
+
+DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) {
+  if (!before_entry_) {
+    DVLOG(2) << "HpackBlockDecoder::Decode resume entry, db->Remaining="
+             << db->Remaining();
+    DecodeStatus status = entry_decoder_.Resume(db, listener_);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        before_entry_ = true;
+        break;
+      case DecodeStatus::kDecodeInProgress:
+        DCHECK_EQ(0u, db->Remaining());
+        return DecodeStatus::kDecodeInProgress;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+  DCHECK(before_entry_);
+  while (db->HasData()) {
+    DVLOG(2) << "HpackBlockDecoder::Decode start entry, db->Remaining="
+             << db->Remaining();
+    DecodeStatus status = entry_decoder_.Start(db, listener_);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        continue;
+      case DecodeStatus::kDecodeInProgress:
+        DCHECK_EQ(0u, db->Remaining());
+        before_entry_ = false;
+        return DecodeStatus::kDecodeInProgress;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+    DCHECK(false);
+  }
+  DCHECK(before_entry_);
+  return DecodeStatus::kDecodeDone;
+}
+
+std::string HpackBlockDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackBlockDecoder(" << entry_decoder_.DebugString() << ", listener@"
+     << std::hex << reinterpret_cast<intptr_t>(listener_)
+     << (before_entry_ ? ", between entries)" : ", in an entry)");
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackBlockDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_block_decoder.h b/net/http2/hpack/decoder/hpack_block_decoder.h
new file mode 100644
index 0000000..49e8ae1
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
+
+// HpackBlockDecoder decodes an entire HPACK block (or the available portion
+// thereof in the DecodeBuffer) into entries, but doesn't include HPACK static
+// or dynamic table support, so table indices remain indices at this level.
+// Reports the entries to an HpackEntryDecoderListener.
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackBlockDecoder {
+ public:
+  explicit HpackBlockDecoder(HpackEntryDecoderListener* listener)
+      : listener_(listener) {
+    DCHECK_NE(listener_, nullptr);
+  }
+  ~HpackBlockDecoder() {}
+
+  // The listener may be changed at any time. The change takes effect on the
+  // next entry into the decode loop of the Decode() method below.
+  void set_listener(HpackEntryDecoderListener* listener) {
+    DCHECK_NE(nullptr, listener);
+    listener_ = listener;
+  }
+  HpackEntryDecoderListener* listener() { return listener_; }
+
+  // Prepares the decoder to start decoding a new HPACK block. Expected
+  // to be called from an implementation of Http2FrameDecoderListener's
+  // OnHeadersStart or OnPushPromiseStart methods.
+  void Reset() {
+    DVLOG(2) << "HpackBlockDecoder::Reset";
+    before_entry_ = true;
+  }
+
+  // Decode the fragment of the HPACK block contained in the decode buffer.
+  // Expected to be called from an implementation of Http2FrameDecoderListener's
+  // OnHpackFragment method.
+  DecodeStatus Decode(DecodeBuffer* db);
+
+  std::string DebugString() const;
+
+ private:
+  HpackEntryDecoder entry_decoder_;
+  HpackEntryDecoderListener* listener_;
+  bool before_entry_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(HpackBlockDecoder);
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackBlockDecoder& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_block_decoder_test.cc b/net/http2/hpack/decoder/hpack_block_decoder_test.cc
new file mode 100644
index 0000000..421f0fc5
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_block_decoder_test.cc
@@ -0,0 +1,315 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_block_decoder.h"
+
+// Tests of HpackBlockDecoder.
+
+#include <sstream>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/hpack/decoder/hpack_block_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/hpack/tools/hpack_example.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionSuccess;
+using std::string;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackBlockDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult VerifyExpected(const HpackBlockCollector& expected) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.VerifyEq(expected));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_1() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+        "custom-header"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_2() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralValueHeader(
+        HpackEntryType::kUnindexedLiteralHeader, 4, false, "/sample/path"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_3() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleLiteralNameValueHeader(
+        HpackEntryType::kNeverIndexedLiteralHeader, false, "password", false,
+        "secret"));
+  }
+
+  AssertionResult ValidateForSpecExample_C_2_4() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateSoleIndexedHeader(2));
+  }
+
+ protected:
+  HpackBlockDecoderTest() : listener_(&collector_), decoder_(&listener_) {
+    stop_decode_on_done_ = false;
+    decoder_.Reset();
+    // Make sure logging doesn't crash. Not examining the result.
+    std::ostringstream strm;
+    strm << decoder_;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* db) override {
+    collector_.Clear();
+    decoder_.Reset();
+    return ResumeDecoding(db);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
+    DecodeStatus status = decoder_.Decode(db);
+
+    // Make sure logging doesn't crash. Not examining the result.
+    std::ostringstream strm;
+    strm << decoder_;
+
+    return status;
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+                                               Validator validator) {
+    bool return_non_zero_on_first = false;
+    return RandomDecoderTest::DecodeAndValidateSeveralWays(
+        db, return_non_zero_on_first, validator);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+                                               Validator validator) {
+    DecodeBuffer db(hbb.buffer());
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  AssertionResult DecodeHpackExampleAndValidateSeveralWays(
+      StringPiece hpack_example,
+      Validator validator) {
+    string input = HpackExampleToStringOrDie(hpack_example);
+    DecodeBuffer db(input);
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  uint8_t Rand8() { return Random().Rand8(); }
+
+  string Rand8String() { return Random().RandString(Rand8()); }
+
+  HpackBlockCollector collector_;
+  HpackEntryDecoderVLoggingListener listener_;
+  HpackBlockDecoder decoder_;
+};
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_1) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_1,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      40                                      | == Literal indexed ==
+      0a                                      |   Literal name (len = 10)
+      6375 7374 6f6d 2d6b 6579                | custom-key
+      0d                                      |   Literal value (len = 13)
+      6375 7374 6f6d 2d68 6561 6465 72        | custom-header
+                                              | -> custom-key:
+                                              |   custom-header
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.2
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_2) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_2,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      04                                      | == Literal not indexed ==
+                                              |   Indexed name (idx = 4)
+                                              |     :path
+      0c                                      |   Literal value (len = 12)
+      2f73 616d 706c 652f 7061 7468           | /sample/path
+                                              | -> :path: /sample/path
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.3
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_3) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_3,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      10                                      | == Literal never indexed ==
+      08                                      |   Literal name (len = 8)
+      7061 7373 776f 7264                     | password
+      06                                      |   Literal value (len = 6)
+      7365 6372 6574                          | secret
+                                              | -> password: secret
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.4
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) {
+  NoArgValidator do_check =
+      base::Bind(&HpackBlockDecoderTest::ValidateForSpecExample_C_2_4,
+                 base::Unretained(this));
+  EXPECT_TRUE(
+      DecodeHpackExampleAndValidateSeveralWays(R"(
+      82                                      | == Indexed - Add ==
+                                              |   idx = 2
+                                              | -> :method: GET
+      )",
+                                               ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) {
+  string example = R"(
+      82                                      | == Indexed - Add ==
+                                              |   idx = 2
+                                              | -> :method: GET
+      86                                      | == Indexed - Add ==
+                                              |   idx = 6
+                                              | -> :scheme: http
+      84                                      | == Indexed - Add ==
+                                              |   idx = 4
+                                              | -> :path: /
+      41                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 1)
+                                              |     :authority
+      0f                                      |   Literal value (len = 15)
+      7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com
+                                              | -> :authority:
+                                              |   www.example.com
+      )";
+  HpackBlockCollector expected;
+  expected.ExpectIndexedHeader(2);
+  expected.ExpectIndexedHeader(6);
+  expected.ExpectIndexedHeader(4);
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          1, false, "www.example.com");
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+      example, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) {
+  string example = R"(
+      48                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 8)
+                                              |     :status
+      03                                      |   Literal value (len = 3)
+      3330 32                                 | 302
+                                              | -> :status: 302
+      58                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 24)
+                                              |     cache-control
+      07                                      |   Literal value (len = 7)
+      7072 6976 6174 65                       | private
+                                              | -> cache-control: private
+      61                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 33)
+                                              |     date
+      1d                                      |   Literal value (len = 29)
+      4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+      2032 303a 3133 3a32 3120 474d 54        |  20:13:21 GMT
+                                              | -> date: Mon, 21 Oct 2013
+                                              |   20:13:21 GMT
+      6e                                      | == Literal indexed ==
+                                              |   Indexed name (idx = 46)
+                                              |     location
+      17                                      |   Literal value (len = 23)
+      6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
+      706c 652e 636f 6d                       | ple.com
+                                              | -> location:
+                                              |   https://www.example.com
+      )";
+  HpackBlockCollector expected;
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          8, false, "302");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          24, false, "private");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          33, false,
+                                          "Mon, 21 Oct 2013 20:13:21 GMT");
+  expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+                                          46, false, "https://www.example.com");
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+      example, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+// Generate a bunch of HPACK block entries to expect, use those expectations
+// to generate an HPACK block, then decode it and confirm it matches those
+// expectations. Some of these are invalid (such as Indexed, with index=0),
+// but well-formed, and the decoder doesn't check for validity, just
+// well-formedness. That includes the validity of the strings not being checked,
+// such as lower-case ascii for the names, and valid Huffman encodings.
+TEST_F(HpackBlockDecoderTest, Computed) {
+  HpackBlockCollector expected;
+  expected.ExpectIndexedHeader(0);
+  expected.ExpectIndexedHeader(1);
+  expected.ExpectIndexedHeader(126);
+  expected.ExpectIndexedHeader(127);
+  expected.ExpectIndexedHeader(128);
+  expected.ExpectDynamicTableSizeUpdate(0);
+  expected.ExpectDynamicTableSizeUpdate(1);
+  expected.ExpectDynamicTableSizeUpdate(14);
+  expected.ExpectDynamicTableSizeUpdate(15);
+  expected.ExpectDynamicTableSizeUpdate(30);
+  expected.ExpectDynamicTableSizeUpdate(31);
+  expected.ExpectDynamicTableSizeUpdate(4095);
+  expected.ExpectDynamicTableSizeUpdate(4096);
+  expected.ExpectDynamicTableSizeUpdate(8192);
+  for (auto type : {HpackEntryType::kIndexedLiteralHeader,
+                    HpackEntryType::kUnindexedLiteralHeader,
+                    HpackEntryType::kNeverIndexedLiteralHeader}) {
+    for (bool value_huffman : {false, true}) {
+      // An entry with an index for the name. Ensure the name index
+      // is not zero by adding one to the Rand8() result.
+      expected.ExpectNameIndexAndLiteralValue(type, Rand8() + 1, value_huffman,
+                                              Rand8String());
+      // And two entries with literal names, one plain, one huffman encoded.
+      expected.ExpectLiteralNameAndValue(type, false, Rand8String(),
+                                         value_huffman, Rand8String());
+      expected.ExpectLiteralNameAndValue(type, true, Rand8String(),
+                                         value_huffman, Rand8String());
+    }
+  }
+  // Shuffle the entries and serialize them to produce an HPACK block.
+  expected.ShuffleEntries(RandomPtr());
+  HpackBlockBuilder hbb;
+  expected.AppendToHpackBlockBuilder(&hbb);
+
+  NoArgValidator do_check = base::Bind(&HpackBlockDecoderTest::VerifyExpected,
+                                       base::Unretained(this), expected);
+  EXPECT_TRUE(
+      DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
new file mode 100644
index 0000000..90d0336
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -0,0 +1,215 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+std::ostream& operator<<(std::ostream& out,
+                         const HpackDecoderStringBuffer::State v) {
+  switch (v) {
+    case HpackDecoderStringBuffer::State::RESET:
+      return out << "RESET";
+    case HpackDecoderStringBuffer::State::COLLECTING:
+      return out << "COLLECTING";
+    case HpackDecoderStringBuffer::State::COMPLETE:
+      return out << "COMPLETE";
+    default:
+      return out << "Unknown HpackDecoderStringBuffer::State!";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const HpackDecoderStringBuffer::Backing v) {
+  switch (v) {
+    case HpackDecoderStringBuffer::Backing::RESET:
+      return out << "RESET";
+    case HpackDecoderStringBuffer::Backing::UNBUFFERED:
+      return out << "UNBUFFERED";
+    case HpackDecoderStringBuffer::Backing::BUFFERED:
+      return out << "BUFFERED";
+    case HpackDecoderStringBuffer::Backing::STATIC:
+      return out << "STATIC";
+    default:
+      return out << "Unknown HpackDecoderStringBuffer::Backing!";
+  }
+}
+
+HpackDecoderStringBuffer::HpackDecoderStringBuffer() {
+  Reset();
+}
+HpackDecoderStringBuffer::~HpackDecoderStringBuffer() {}
+
+// TODO(jamessynge): Consider eliminating most of Reset (i.e. do less); in
+// particular, if a variable won't be read again until after it is next set
+// (e.g. is_huffman_encoded_ or remaining_len_), then it doesn't need to be
+// cleared here. This will be easier when not supporting both HpackDecoder2
+// (in net/spdy/hpack) and HpackWholeEntryDecoder, so we can eliminate
+// the Set() and str() methods.
+void HpackDecoderStringBuffer::Reset() {
+  DVLOG(3) << "HpackDecoderStringBuffer::Reset";
+  buffer_.clear();
+  value_.clear();
+  remaining_len_ = 0;
+  is_huffman_encoded_ = false;
+  state_ = State::RESET;
+  backing_ = Backing::RESET;
+}
+
+void HpackDecoderStringBuffer::Set(StringPiece value, bool is_static) {
+  DVLOG(2) << "HpackDecoderStringBuffer::Set";
+  DCHECK_EQ(state_, State::RESET);
+  DCHECK_EQ(backing_, Backing::RESET);
+  value_ = value;
+  state_ = State::COMPLETE;
+  backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
+}
+
+void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
+  DCHECK_EQ(state_, State::RESET);
+  DCHECK_EQ(backing_, Backing::RESET);
+  buffer_.clear();
+  value_.clear();
+
+  remaining_len_ = len;
+  is_huffman_encoded_ = huffman_encoded;
+
+  state_ = State::COLLECTING;
+
+  if (huffman_encoded) {
+    decoder_.Reset();
+    backing_ = Backing::BUFFERED;
+
+    // Reserve space in buffer_ for the uncompressed string, assuming the
+    // maximum expansion. The shortest Huffman codes in the RFC are 5 bits long,
+    // which then expand to 8 bits during decoding (i.e. each code is for one
+    // plain text octet, aka byte), so the maximum size is 60% longer than the
+    // encoded size.
+    len = len * 8 / 5;
+    if (buffer_.capacity() < len) {
+      buffer_.reserve(len);
+    }
+  } else {
+    // Assume for now that we won't need to use buffer_, so don't reserve space
+    // in it.
+    backing_ = Backing::RESET;
+  }
+}
+
+bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
+           << ", backing=" << backing_;
+  DCHECK_EQ(state_, State::COLLECTING);
+  DCHECK_LE(len, remaining_len_);
+  remaining_len_ -= len;
+
+  if (is_huffman_encoded_) {
+    DCHECK_EQ(backing_, Backing::BUFFERED);
+    // We don't set value_ for buffered strings until OnEnd,
+    // so it should be empty.
+    DCHECK_EQ(0u, value_.size());
+    return decoder_.Decode(StringPiece(data, len), &buffer_);
+  }
+
+  if (backing_ == Backing::RESET) {
+    // This is the first call to OnData.
+    DCHECK_EQ(0u, buffer_.size());
+    DCHECK_EQ(0u, value_.size());
+    // If data contains the entire string, don't copy the string. If we later
+    // find that the HPACK entry is split across input buffers, then we'll
+    // copy the string into buffer_.
+    if (remaining_len_ == 0) {
+      value_ = StringPiece(data, len);
+      backing_ = Backing::UNBUFFERED;
+      return true;
+    }
+
+    // We need to buffer the string because it is split across input buffers.
+    backing_ = Backing::BUFFERED;
+    buffer_.assign(data, len);
+    return true;
+  }
+
+  // This is not the first call to OnData for this string, so it should be
+  // buffered.
+  DCHECK_EQ(backing_, Backing::BUFFERED);
+  // We don't set value_ for buffered strings until OnEnd, so it should be
+  // empty.
+  DCHECK_EQ(0u, value_.size());
+
+  // Append to the current contents of the buffer.
+  buffer_.append(data, len);
+  return true;
+}
+
+bool HpackDecoderStringBuffer::OnEnd() {
+  DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
+  DCHECK_EQ(state_, State::COLLECTING);
+  DCHECK_EQ(0u, remaining_len_);
+
+  if (is_huffman_encoded_) {
+    DCHECK_EQ(backing_, Backing::BUFFERED);
+    // Did the Huffman encoding of the string end properly?
+    if (!decoder_.InputProperlyTerminated()) {
+      return false;  // No, it didn't.
+    }
+  }
+  state_ = State::COMPLETE;
+  if (backing_ == Backing::BUFFERED) {
+    value_ = buffer_;
+  }
+  return true;
+}
+
+void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
+  DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
+           << state_ << ", backing=" << backing_;
+  if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
+    DVLOG(2) << "HpackDecoderStringBuffer buffering string of length "
+             << value_.size();
+    value_.CopyToString(&buffer_);
+    if (state_ == State::COMPLETE) {
+      value_ = buffer_;
+    }
+    backing_ = Backing::BUFFERED;
+  }
+}
+
+size_t HpackDecoderStringBuffer::BufferedLength() const {
+  DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
+  return backing_ == Backing::BUFFERED ? buffer_.size() : 0;
+}
+
+StringPiece HpackDecoderStringBuffer::str() const {
+  DVLOG(3) << "HpackDecoderStringBuffer::str";
+  DCHECK_EQ(state_, State::COMPLETE);
+  return value_;
+}
+
+void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
+  out << "{state=" << state_;
+  if (state_ != State::RESET) {
+    out << ", backing=" << backing_;
+    out << ", remaining_len=" << remaining_len_;
+    out << ", is_huffman_encoded=" << is_huffman_encoded_;
+    if (backing_ == Backing::BUFFERED) {
+      out << ", buffer: " << buffer_;
+    } else {
+      out << ", value: " << value_;
+    }
+  }
+  out << "}";
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
+  v.OutputDebugStringTo(out);
+  return out;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.h b/net/http2/hpack/decoder/hpack_decoder_string_buffer.h
new file mode 100644
index 0000000..a0917a2
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
+
+// HpackDecoderStringBuffer helps an HPACK decoder to avoid copies of a string
+// literal (name or value) except when necessary (e.g. when split across two
+// or more HPACK block fragments).
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackDecoderStringBuffer {
+ public:
+  enum class State : uint8_t { RESET, COLLECTING, COMPLETE };
+  enum class Backing : uint8_t { RESET, UNBUFFERED, BUFFERED, STATIC };
+
+  HpackDecoderStringBuffer();
+  ~HpackDecoderStringBuffer();
+
+  void Reset();
+  void Set(base::StringPiece value, bool is_static);
+
+  // Note that for Huffman encoded strings the length of the string after
+  // decoding may be larger (expected), the same or even smaller; the latter
+  // are unlikely, but possible if the encoder makes odd choices.
+  void OnStart(bool huffman_encoded, size_t len);
+  bool OnData(const char* data, size_t len);
+  bool OnEnd();
+  void BufferStringIfUnbuffered();
+  size_t BufferedLength() const;
+
+  base::StringPiece str() const;
+
+  State state_for_testing() const { return state_; }
+  Backing backing_for_testing() const { return backing_; }
+  void OutputDebugStringTo(std::ostream& out) const;
+
+ private:
+  // Storage for the string being buffered, if buffering is necessary
+  // (e.g. if Huffman encoded, buffer_ is storage for the decoded string).
+  std::string buffer_;
+
+  // The StringPiece to be returned by HpackDecoderStringBuffer::str(). If a
+  // string has been collected, but not buffered, value_ points to that string.
+  base::StringPiece value_;
+
+  // The decoder to use if the string is Huffman encoded.
+  HpackHuffmanDecoder decoder_;
+
+  // Count of bytes not yet passed to OnData.
+  size_t remaining_len_ = 0;
+
+  // Is the HPACK string Huffman encoded?
+  bool is_huffman_encoded_ = false;
+
+  // State of the string decoding process.
+  State state_;
+
+  // Where is the string stored?
+  Backing backing_;
+
+  DISALLOW_COPY_AND_ASSIGN(HpackDecoderStringBuffer);
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackDecoderStringBuffer& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
new file mode 100644
index 0000000..313a457
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
@@ -0,0 +1,245 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+// Tests of HpackDecoderStringBuffer.
+
+#include <initializer_list>
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/tools/failure.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::HasSubstr;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackDecoderStringBufferTest : public ::testing::Test {
+ protected:
+  typedef HpackDecoderStringBuffer::State State;
+  typedef HpackDecoderStringBuffer::Backing Backing;
+
+  State state() const { return buf_.state_for_testing(); }
+  Backing backing() const { return buf_.backing_for_testing(); }
+
+  // We want to know that LOG(x) << buf_ will work in production should that
+  // be needed, so we test that it outputs the expected values.
+  AssertionResult VerifyLogHasSubstrs(std::initializer_list<string> strs) {
+    VLOG(1) << buf_;
+    std::ostringstream ss;
+    buf_.OutputDebugStringTo(ss);
+    string dbg_str(ss.str());
+    for (const auto& expected : strs) {
+      VERIFY_THAT(dbg_str, HasSubstr(expected));
+    }
+    return AssertionSuccess();
+  }
+
+  HpackDecoderStringBuffer buf_;
+};
+
+TEST_F(HpackDecoderStringBufferTest, SetStatic) {
+  StringPiece data("static string");
+
+  EXPECT_EQ(state(), State::RESET);
+  EXPECT_TRUE(VerifyLogHasSubstrs({"state=RESET"}));
+
+  buf_.Set(data, /*is_static*/ true);
+  LOG(INFO) << buf_;
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::STATIC);
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_EQ(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+
+  // The string is static, so BufferStringIfUnbuffered won't change anything.
+  buf_.BufferStringIfUnbuffered();
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::STATIC);
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_EQ(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
+  StringPiece data("some text.");
+
+  LOG(INFO) << buf_;
+  EXPECT_EQ(state(), State::RESET);
+
+  buf_.OnStart(/*huffman_encoded*/ false, data.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::RESET);
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(data.data(), data.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::UNBUFFERED);
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::UNBUFFERED);
+  EXPECT_EQ(0u, buf_.BufferedLength());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=UNBUFFERED", "value: some text."}));
+
+  // We expect that the string buffer points to the passed in StringPiece's
+  // backing store.
+  EXPECT_EQ(data.data(), buf_.str().data());
+
+  // Now force it to buffer the string, after which it will still have the same
+  // string value, but the backing store will be different.
+  buf_.BufferStringIfUnbuffered();
+  LOG(INFO) << buf_;
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  EXPECT_EQ(data, buf_.str());
+  EXPECT_NE(data.data(), buf_.str().data());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"state=COMPLETE", "backing=BUFFERED", "buffer: some text."}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
+  StringPiece data("some text.");
+  StringPiece part1 = data.substr(0, 1);
+  StringPiece part2 = data.substr(1);
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ false, data.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::RESET);
+
+  // OnData with only a part of the data, not the whole, so buf_ will buffer
+  // the data.
+  EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), part1.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  LOG(INFO) << buf_;
+
+  StringPiece buffered = buf_.str();
+  EXPECT_EQ(data, buffered);
+  EXPECT_NE(data.data(), buffered.data());
+
+  // The string is already buffered, so BufferStringIfUnbuffered should not make
+  // any change.
+  buf_.BufferStringIfUnbuffered();
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), data.size());
+  EXPECT_EQ(buffered, buf_.str());
+  EXPECT_EQ(buffered.data(), buf_.str().data());
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) {
+  string encoded = a2b_hex("f1e3c2e5f23a6ba0ab90f4ff");
+  StringPiece decoded("www.example.com");
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  EXPECT_EQ(decoded, buf_.str());
+  EXPECT_TRUE(VerifyLogHasSubstrs(
+      {"{state=COMPLETE", "backing=BUFFERED", "buffer: www.example.com}"}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) {
+  string encoded = a2b_hex("f1e3c2e5f23a6ba0ab90f4ff");
+  string part1 = encoded.substr(0, 5);
+  string part2 = encoded.substr(5);
+  StringPiece decoded("www.example.com");
+
+  EXPECT_EQ(state(), State::RESET);
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(0u, buf_.BufferedLength());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_GT(buf_.BufferedLength(), 0u);
+  EXPECT_LT(buf_.BufferedLength(), decoded.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  LOG(INFO) << buf_;
+
+  EXPECT_TRUE(buf_.OnEnd());
+  EXPECT_EQ(state(), State::COMPLETE);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+  EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+  EXPECT_EQ(decoded, buf_.str());
+  LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
+  // Explicitly encode the End-of-String symbol, a no-no.
+  string encoded = a2b_hex("ffffffff");
+
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_FALSE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
+  // Last byte of string doesn't end with prefix of End-of-String symbol.
+  string encoded = a2b_hex("00");
+
+  buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+  EXPECT_EQ(state(), State::COLLECTING);
+
+  EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+  EXPECT_EQ(state(), State::COLLECTING);
+  EXPECT_EQ(backing(), Backing::BUFFERED);
+
+  EXPECT_FALSE(buf_.OnEnd());
+  LOG(INFO) << buf_;
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_collector.cc b/net/http2/hpack/decoder/hpack_entry_collector.cc
new file mode 100644
index 0000000..ea4feb6
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_collector.cc
@@ -0,0 +1,317 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/logging.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+const HpackEntryType kInvalidHeaderType = static_cast<HpackEntryType>(99);
+const size_t kInvalidIndex = 99999999;
+
+}  // namespace
+
+HpackEntryCollector::HpackEntryCollector() {
+  Clear();
+}
+
+HpackEntryCollector::HpackEntryCollector(const HpackEntryCollector& other)
+    : header_type_(other.header_type_),
+      index_(other.index_),
+      name_(other.name_),
+      value_(other.value_),
+      started_(other.started_),
+      ended_(other.ended_) {}
+
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         size_t index_or_size)
+    : header_type_(type), index_(index_or_size), started_(true), ended_(true) {}
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         size_t index,
+                                         bool value_huffman,
+                                         const string& value)
+    : header_type_(type),
+      index_(index),
+      value_(value, value_huffman),
+      started_(true),
+      ended_(true) {}
+HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
+                                         bool name_huffman,
+                                         const string& name,
+                                         bool value_huffman,
+                                         const string& value)
+    : header_type_(type),
+      index_(0),
+      name_(name, name_huffman),
+      value_(value, value_huffman),
+      started_(true),
+      ended_(true) {}
+
+HpackEntryCollector::~HpackEntryCollector() {}
+
+void HpackEntryCollector::OnIndexedHeader(size_t index) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(HpackEntryType::kIndexedHeader, index);
+  ended_ = true;
+}
+void HpackEntryCollector::OnStartLiteralHeader(HpackEntryType header_type,
+                                               size_t maybe_name_index) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(header_type, maybe_name_index);
+}
+void HpackEntryCollector::OnNameStart(bool huffman_encoded, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_FALSE(IsClear());
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  name_.OnStringStart(huffman_encoded, len);
+}
+void HpackEntryCollector::OnNameData(const char* data, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  ASSERT_TRUE(name_.IsInProgress());
+  name_.OnStringData(data, len);
+}
+void HpackEntryCollector::OnNameEnd() {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralNameExpected()) << ToString();
+  ASSERT_TRUE(name_.IsInProgress());
+  name_.OnStringEnd();
+}
+void HpackEntryCollector::OnValueStart(bool huffman_encoded, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  if (LiteralNameExpected()) {
+    ASSERT_TRUE(name_.HasEnded());
+  }
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsClear()) << value_.ToString();
+  value_.OnStringStart(huffman_encoded, len);
+}
+void HpackEntryCollector::OnValueData(const char* data, size_t len) {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsInProgress());
+  value_.OnStringData(data, len);
+}
+void HpackEntryCollector::OnValueEnd() {
+  ASSERT_TRUE(started_);
+  ASSERT_FALSE(ended_);
+  ASSERT_TRUE(LiteralValueExpected()) << ToString();
+  ASSERT_TRUE(value_.IsInProgress());
+  value_.OnStringEnd();
+  ended_ = true;
+}
+void HpackEntryCollector::OnDynamicTableSizeUpdate(size_t size) {
+  ASSERT_FALSE(started_);
+  ASSERT_TRUE(IsClear()) << ToString();
+  Init(HpackEntryType::kDynamicTableSizeUpdate, size);
+  ended_ = true;
+}
+
+void HpackEntryCollector::Clear() {
+  header_type_ = kInvalidHeaderType;
+  index_ = kInvalidIndex;
+  name_.Clear();
+  value_.Clear();
+  started_ = ended_ = false;
+}
+bool HpackEntryCollector::IsClear() const {
+  return header_type_ == kInvalidHeaderType && index_ == kInvalidIndex &&
+         name_.IsClear() && value_.IsClear() && !started_ && !ended_;
+}
+bool HpackEntryCollector::IsComplete() const {
+  return started_ && ended_;
+}
+bool HpackEntryCollector::LiteralNameExpected() const {
+  switch (header_type_) {
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return index_ == 0;
+    default:
+      return false;
+  }
+}
+bool HpackEntryCollector::LiteralValueExpected() const {
+  switch (header_type_) {
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return true;
+    default:
+      return false;
+  }
+}
+AssertionResult HpackEntryCollector::ValidateIndexedHeader(
+    size_t expected_index) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(HpackEntryType::kIndexedHeader, header_type_);
+  VERIFY_EQ(expected_index, index_);
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateLiteralValueHeader(
+    HpackEntryType expected_type,
+    size_t expected_index,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(expected_type, header_type_);
+  VERIFY_NE(0u, expected_index);
+  VERIFY_EQ(expected_index, index_);
+  VERIFY_TRUE(name_.IsClear());
+  VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman));
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateLiteralNameValueHeader(
+    HpackEntryType expected_type,
+    bool expected_name_huffman,
+    StringPiece expected_name,
+    bool expected_value_huffman,
+    StringPiece expected_value) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(expected_type, header_type_);
+  VERIFY_EQ(0u, index_);
+  VERIFY_SUCCESS(name_.Collected(expected_name, expected_name_huffman));
+  VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman));
+  return ::testing::AssertionSuccess();
+}
+AssertionResult HpackEntryCollector::ValidateDynamicTableSizeUpdate(
+    size_t size) const {
+  VERIFY_TRUE(started_);
+  VERIFY_TRUE(ended_);
+  VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, header_type_);
+  VERIFY_EQ(index_, size);
+  return ::testing::AssertionSuccess();
+}
+
+void HpackEntryCollector::AppendToHpackBlockBuilder(
+    HpackBlockBuilder* hbb) const {
+  ASSERT_TRUE(started_ && ended_) << *this;
+  switch (header_type_) {
+    case HpackEntryType::kIndexedHeader:
+      hbb->AppendIndexedHeader(index_);
+      return;
+
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      hbb->AppendDynamicTableSizeUpdate(index_);
+      return;
+
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      ASSERT_TRUE(value_.HasEnded()) << *this;
+      if (index_ != 0) {
+        CHECK(name_.IsClear());
+        hbb->AppendNameIndexAndLiteralValue(header_type_, index_,
+                                            value_.huffman_encoded, value_.s);
+      } else {
+        CHECK(name_.HasEnded()) << *this;
+        hbb->AppendLiteralNameAndValue(header_type_, name_.huffman_encoded,
+                                       name_.s, value_.huffman_encoded,
+                                       value_.s);
+      }
+      return;
+
+    default:
+      ADD_FAILURE() << *this;
+  }
+}
+
+string HpackEntryCollector::ToString() const {
+  string result("Type=");
+  switch (header_type_) {
+    case HpackEntryType::kIndexedHeader:
+      result += "IndexedHeader";
+      break;
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      result += "DynamicTableSizeUpdate";
+      break;
+    case HpackEntryType::kIndexedLiteralHeader:
+      result += "IndexedLiteralHeader";
+      break;
+    case HpackEntryType::kUnindexedLiteralHeader:
+      result += "UnindexedLiteralHeader";
+      break;
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      result += "NeverIndexedLiteralHeader";
+      break;
+    default:
+      if (header_type_ == kInvalidHeaderType) {
+        result += "<unset>";
+      } else {
+        std::stringstream ss;
+        ss << header_type_;
+        result.append(ss.str());
+      }
+  }
+  if (index_ != 0) {
+    result.append(" Index=");
+    std::stringstream ss;
+    ss << index_;
+    result.append(ss.str());
+  }
+  if (!name_.IsClear()) {
+    result.append(" Name");
+    result.append(name_.ToString());
+  }
+  if (!value_.IsClear()) {
+    result.append(" Value");
+    result.append(value_.ToString());
+  }
+  if (!started_) {
+    EXPECT_FALSE(ended_);
+    result.append(" !started");
+  } else if (!ended_) {
+    result.append(" !ended");
+  } else {
+    result.append(" Complete");
+  }
+  return result;
+}
+
+void HpackEntryCollector::Init(HpackEntryType type, size_t maybe_index) {
+  ASSERT_TRUE(IsClear()) << ToString();
+  header_type_ = type;
+  index_ = maybe_index;
+  started_ = true;
+}
+
+bool operator==(const HpackEntryCollector& a, const HpackEntryCollector& b) {
+  return a.name() == b.name() && a.value() == b.value() &&
+         a.index() == b.index() && a.header_type() == b.header_type() &&
+         a.started() == b.started() && a.ended() == b.ended();
+}
+bool operator!=(const HpackEntryCollector& a, const HpackEntryCollector& b) {
+  return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryCollector& v) {
+  return out << v.ToString();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_collector.h b/net/http2/hpack/decoder/hpack_entry_collector.h
new file mode 100644
index 0000000..876e0b0
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_collector.h
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
+
+// HpackEntryCollector records calls to HpackEntryDecoderListener in support
+// of tests of HpackEntryDecoder, or which use it. Can only record the callbacks
+// for the decoding of a single entry; call Clear() between decoding successive
+// entries or use a distinct HpackEntryCollector for each entry.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+namespace net {
+namespace test {
+
+class HpackEntryCollector : public HpackEntryDecoderListener {
+ public:
+  HpackEntryCollector();
+  HpackEntryCollector(const HpackEntryCollector& other);
+
+  // These next three constructors are intended for use in tests that create
+  // an HpackEntryCollector "manually", and then compare it against another
+  // that is populated via calls to the HpackEntryDecoderListener methods.
+  HpackEntryCollector(HpackEntryType type, size_t index_or_size);
+  HpackEntryCollector(HpackEntryType type,
+                      size_t index,
+                      bool value_huffman,
+                      const std::string& value);
+  HpackEntryCollector(HpackEntryType type,
+                      bool name_huffman,
+                      const std::string& name,
+                      bool value_huffman,
+                      const std::string& value);
+
+  ~HpackEntryCollector() override;
+
+  // Methods defined by HpackEntryDecoderListener.
+  void OnIndexedHeader(size_t index) override;
+  void OnStartLiteralHeader(HpackEntryType header_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+
+  // Clears the fields of the collector so that it is ready to start collecting
+  // another HPACK block entry.
+  void Clear();
+
+  // Is the collector ready to start collecting another HPACK block entry.
+  bool IsClear() const;
+
+  // Has a complete entry been collected?
+  bool IsComplete() const;
+
+  // Based on the HpackEntryType, is a literal name expected?
+  bool LiteralNameExpected() const;
+
+  // Based on the HpackEntryType, is a literal value expected?
+  bool LiteralValueExpected() const;
+
+  // Returns success if collected an Indexed Header (i.e. OnIndexedHeader was
+  // called).
+  ::testing::AssertionResult ValidateIndexedHeader(size_t expected_index) const;
+
+  // Returns success if collected a Header with an indexed name and literal
+  // value (i.e. OnStartLiteralHeader was called with a non-zero index for
+  // the name, which must match expected_index).
+  ::testing::AssertionResult ValidateLiteralValueHeader(
+      HpackEntryType expected_type,
+      size_t expected_index,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Returns success if collected a Header with an literal name and literal
+  // value.
+  ::testing::AssertionResult ValidateLiteralNameValueHeader(
+      HpackEntryType expected_type,
+      bool expected_name_huffman,
+      base::StringPiece expected_name,
+      bool expected_value_huffman,
+      base::StringPiece expected_value) const;
+
+  // Returns success if collected a Dynamic Table Size Update,
+  // with the specified size.
+  ::testing::AssertionResult ValidateDynamicTableSizeUpdate(
+      size_t expected_size) const;
+
+  void set_header_type(HpackEntryType v) { header_type_ = v; }
+  HpackEntryType header_type() const { return header_type_; }
+
+  void set_index(size_t v) { index_ = v; }
+  size_t index() const { return index_; }
+
+  void set_name(const HpackStringCollector& v) { name_ = v; }
+  const HpackStringCollector& name() const { return name_; }
+
+  void set_value(const HpackStringCollector& v) { value_ = v; }
+  const HpackStringCollector& value() const { return value_; }
+
+  void set_started(bool v) { started_ = v; }
+  bool started() const { return started_; }
+
+  void set_ended(bool v) { ended_ = v; }
+  bool ended() const { return ended_; }
+
+  void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+  // Returns a debug string.
+  std::string ToString() const;
+
+ private:
+  void Init(HpackEntryType type, size_t maybe_index);
+
+  HpackEntryType header_type_;
+  size_t index_;
+
+  HpackStringCollector name_;
+  HpackStringCollector value_;
+
+  // True if has received a call to an HpackEntryDecoderListener method
+  // indicating the start of decoding an HPACK entry; for example,
+  // OnIndexedHeader set it true, but OnNameStart does not change it.
+  bool started_ = false;
+
+  // True if has received a call to an HpackEntryDecoderListener method
+  // indicating the end of decoding an HPACK entry; for example,
+  // OnIndexedHeader and OnValueEnd both set it true, but OnNameEnd does
+  // not change it.
+  bool ended_ = false;
+};
+
+bool operator==(const HpackEntryCollector& a, const HpackEntryCollector& b);
+bool operator!=(const HpackEntryCollector& a, const HpackEntryCollector& b);
+std::ostream& operator<<(std::ostream& out, const HpackEntryCollector& v);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder.cc b/net/http2/hpack/decoder/hpack_entry_decoder.cc
new file mode 100644
index 0000000..c47ef0c
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder.cc
@@ -0,0 +1,233 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace net {
+namespace {
+// Converts calls from HpackStringDecoder when decoding a header name into the
+// appropriate HpackEntryDecoderListener::OnName* calls.
+class NameDecoderListener {
+ public:
+  explicit NameDecoderListener(HpackEntryDecoderListener* listener)
+      : listener_(listener) {}
+  bool OnStringStart(bool huffman_encoded, size_t len) {
+    listener_->OnNameStart(huffman_encoded, len);
+    return true;
+  }
+  void OnStringData(const char* data, size_t len) {
+    listener_->OnNameData(data, len);
+  }
+  void OnStringEnd() { listener_->OnNameEnd(); }
+
+ private:
+  HpackEntryDecoderListener* listener_;
+};
+
+// Converts calls from HpackStringDecoder when decoding a header value into
+// the appropriate HpackEntryDecoderListener::OnValue* calls.
+class ValueDecoderListener {
+ public:
+  explicit ValueDecoderListener(HpackEntryDecoderListener* listener)
+      : listener_(listener) {}
+  bool OnStringStart(bool huffman_encoded, size_t len) {
+    listener_->OnValueStart(huffman_encoded, len);
+    return true;
+  }
+  void OnStringData(const char* data, size_t len) {
+    listener_->OnValueData(data, len);
+  }
+  void OnStringEnd() { listener_->OnValueEnd(); }
+
+ private:
+  HpackEntryDecoderListener* listener_;
+};
+}  // namespace
+
+// Only call Resume if the previous call (Start or Resume) returned
+// kDecodeInProgress; Resume is also called from Start when it has succeeded
+// in decoding the entry type and its varint.
+DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
+                                       HpackEntryDecoderListener* listener) {
+  DCHECK(db != nullptr);
+  DCHECK(listener != nullptr);
+
+  DecodeStatus status;
+
+  do {
+    switch (state_) {
+      case EntryDecoderState::kResumeDecodingType:
+        // entry_type_decoder_ returned kDecodeInProgress when last called.
+        DVLOG(1) << "kResumeDecodingType: db->Remaining=" << db->Remaining();
+        status = entry_type_decoder_.Resume(db);
+        if (status != DecodeStatus::kDecodeDone) {
+          return status;
+        }
+        state_ = EntryDecoderState::kDecodedType;
+      // FALLTHROUGH_INTENDED
+
+      case EntryDecoderState::kDecodedType:
+        // entry_type_decoder_ returned kDecodeDone, now need to decide how
+        // to proceed.
+        DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining();
+        if (DispatchOnType(listener)) {
+          // All done.
+          return DecodeStatus::kDecodeDone;
+        }
+        continue;
+
+      case EntryDecoderState::kStartDecodingName:
+        DVLOG(1) << "kStartDecodingName: db->Remaining=" << db->Remaining();
+        {
+          NameDecoderListener ncb(listener);
+          status = string_decoder_.Start(db, &ncb);
+        }
+        if (status != DecodeStatus::kDecodeDone) {
+          // On the assumption that the status is kDecodeInProgress, set
+          // state_ accordingly; unnecessary if status is kDecodeError, but
+          // that will only happen if the varint encoding the name's length
+          // is too long.
+          state_ = EntryDecoderState::kResumeDecodingName;
+          return status;
+        }
+        state_ = EntryDecoderState::kStartDecodingValue;
+      // FALLTHROUGH_INTENDED
+
+      case EntryDecoderState::kStartDecodingValue:
+        DVLOG(1) << "kStartDecodingValue: db->Remaining=" << db->Remaining();
+        {
+          ValueDecoderListener vcb(listener);
+          status = string_decoder_.Start(db, &vcb);
+        }
+        if (status == DecodeStatus::kDecodeDone) {
+          // Done with decoding the literal value, so we've reached the
+          // end of the header entry.
+          return status;
+        }
+        // On the assumption that the status is kDecodeInProgress, set
+        // state_ accordingly; unnecessary if status is kDecodeError, but
+        // that will only happen if the varint encoding the value's length
+        // is too long.
+        state_ = EntryDecoderState::kResumeDecodingValue;
+        return status;
+
+      case EntryDecoderState::kResumeDecodingName:
+        // The literal name was split across decode buffers.
+        DVLOG(1) << "kResumeDecodingName: db->Remaining=" << db->Remaining();
+        {
+          NameDecoderListener ncb(listener);
+          status = string_decoder_.Resume(db, &ncb);
+        }
+        if (status != DecodeStatus::kDecodeDone) {
+          // On the assumption that the status is kDecodeInProgress, set
+          // state_ accordingly; unnecessary if status is kDecodeError, but
+          // that will only happen if the varint encoding the name's length
+          // is too long.
+          state_ = EntryDecoderState::kResumeDecodingName;
+          return status;
+        }
+        state_ = EntryDecoderState::kStartDecodingValue;
+        break;
+
+      case EntryDecoderState::kResumeDecodingValue:
+        // The literal value was split across decode buffers.
+        DVLOG(1) << "kResumeDecodingValue: db->Remaining=" << db->Remaining();
+        {
+          ValueDecoderListener vcb(listener);
+          status = string_decoder_.Resume(db, &vcb);
+        }
+        if (status == DecodeStatus::kDecodeDone) {
+          // Done with decoding the value, therefore the entry as a whole.
+          return status;
+        }
+        // On the assumption that the status is kDecodeInProgress, set
+        // state_ accordingly; unnecessary if status is kDecodeError, but
+        // that will only happen if the varint encoding the value's length
+        // is too long.
+        state_ = EntryDecoderState::kResumeDecodingValue;
+        return status;
+    }
+  } while (true);
+}
+
+bool HpackEntryDecoder::DispatchOnType(HpackEntryDecoderListener* listener) {
+  const HpackEntryType entry_type = entry_type_decoder_.entry_type();
+  const uint32_t varint = entry_type_decoder_.varint();
+  switch (entry_type) {
+    case HpackEntryType::kIndexedHeader:
+      // The entry consists solely of the entry type and varint. See:
+      // http://httpwg.org/specs/rfc7541.html#indexed.header.representation
+      listener->OnIndexedHeader(varint);
+      return true;
+
+    case HpackEntryType::kIndexedLiteralHeader:
+    case HpackEntryType::kUnindexedLiteralHeader:
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      // The entry has a literal value, and if the varint is zero also has a
+      // literal name preceding the value. See:
+      // http://httpwg.org/specs/rfc7541.html#literal.header.representation
+      listener->OnStartLiteralHeader(entry_type, varint);
+      if (varint == 0) {
+        state_ = EntryDecoderState::kStartDecodingName;
+      } else {
+        state_ = EntryDecoderState::kStartDecodingValue;
+      }
+      return false;
+
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      // The entry consists solely of the entry type and varint. FWIW, I've
+      // never seen this type of entry in production (primarily browser
+      // traffic) so if you're designing an HPACK successor someday, consider
+      // dropping it or giving it a much longer prefix. See:
+      // http://httpwg.org/specs/rfc7541.html#encoding.context.update
+      listener->OnDynamicTableSizeUpdate(varint);
+      return true;
+  }
+
+  NOTREACHED();
+  return true;
+}
+
+void HpackEntryDecoder::OutputDebugString(std::ostream& out) const {
+  out << "HpackEntryDecoder(state=" << state_ << ", " << entry_type_decoder_
+      << ", " << string_decoder_ << ")";
+}
+
+std::string HpackEntryDecoder::DebugString() const {
+  std::stringstream s;
+  s << *this;
+  return s.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryDecoder& v) {
+  v.OutputDebugString(out);
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         HpackEntryDecoder::EntryDecoderState state) {
+  typedef HpackEntryDecoder::EntryDecoderState EntryDecoderState;
+  switch (state) {
+    case EntryDecoderState::kResumeDecodingType:
+      return out << "kResumeDecodingType";
+    case EntryDecoderState::kDecodedType:
+      return out << "kDecodedType";
+    case EntryDecoderState::kStartDecodingName:
+      return out << "kStartDecodingName";
+    case EntryDecoderState::kResumeDecodingName:
+      return out << "kResumeDecodingName";
+    case EntryDecoderState::kStartDecodingValue:
+      return out << "kStartDecodingValue";
+    case EntryDecoderState::kResumeDecodingValue:
+      return out << "kResumeDecodingValue";
+  }
+  return out << static_cast<int>(state);
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder.h b/net/http2/hpack/decoder/hpack_entry_decoder.h
new file mode 100644
index 0000000..9b5f696
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder.h
@@ -0,0 +1,117 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
+
+// HpackEntryDecoder decodes a single HPACK entry (i.e. one header or one
+// dynamic table size update), in a resumable fashion. The first call, Start(),
+// must provide a non-empty decode buffer. Continue with calls to Resume() if
+// Start, and any subsequent calls to Resume, returns kDecodeInProgress.
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryDecoder {
+ public:
+  enum class EntryDecoderState {
+    // Have started decoding the type/varint, but didn't finish on the previous
+    // attempt.  Next state is kResumeDecodingType or kDecodedType.
+    kResumeDecodingType,
+
+    // Have just finished decoding the type/varint. Final state if the type is
+    // kIndexedHeader or kDynamicTableSizeUpdate. Otherwise, the next state is
+    // kStartDecodingName (if the varint is 0), else kStartDecodingValue.
+    kDecodedType,
+
+    // Ready to start decoding the literal name of a header entry. Next state
+    // is kResumeDecodingName (if the name is split across decode buffers),
+    // else kStartDecodingValue.
+    kStartDecodingName,
+
+    // Resume decoding the literal name of a header that is split across decode
+    // buffers.
+    kResumeDecodingName,
+
+    // Ready to start decoding the literal value of a header entry. Final state
+    // if the value string is entirely in the decode buffer, else the next state
+    // is kResumeDecodingValue.
+    kStartDecodingValue,
+
+    // Resume decoding the literal value of a header that is split across decode
+    // buffers.
+    kResumeDecodingValue,
+  };
+
+  // Only call when the decode buffer has data (i.e. HpackBlockDecoder must
+  // not call until there is data).
+  DecodeStatus Start(DecodeBuffer* db, HpackEntryDecoderListener* listener) {
+    DCHECK(db != nullptr);
+    DCHECK(listener != nullptr);
+    DCHECK(db->HasData());
+    DecodeStatus status = entry_type_decoder_.Start(db);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        // The type of the entry and its varint fit into the current decode
+        // buffer.
+        if (entry_type_decoder_.entry_type() ==
+            HpackEntryType::kIndexedHeader) {
+          // The entry consists solely of the entry type and varint. This
+          // is by far the most common case in practice.
+          listener->OnIndexedHeader(entry_type_decoder_.varint());
+          return DecodeStatus::kDecodeDone;
+        }
+        state_ = EntryDecoderState::kDecodedType;
+        return Resume(db, listener);
+      case DecodeStatus::kDecodeInProgress:
+        // Hit the end of the decode buffer before fully decoding the entry
+        // type and varint.
+        DCHECK_EQ(0u, db->Remaining());
+        state_ = EntryDecoderState::kResumeDecodingType;
+        return status;
+      case DecodeStatus::kDecodeError:
+        // The varint must have been invalid (too long).
+        return status;
+    }
+
+    NOTREACHED();
+    return DecodeStatus::kDecodeError;
+  }
+
+  // Only call Resume if the previous call (Start or Resume) returned
+  // kDecodeInProgress; Resume is also called from Start when it has succeeded
+  // in decoding the entry type and its varint.
+  DecodeStatus Resume(DecodeBuffer* db, HpackEntryDecoderListener* listener);
+
+  std::string DebugString() const;
+  void OutputDebugString(std::ostream& out) const;
+
+ private:
+  // Implements handling state kDecodedType.
+  bool DispatchOnType(HpackEntryDecoderListener* listener);
+
+  HpackEntryTypeDecoder entry_type_decoder_;
+  HpackStringDecoder string_decoder_;
+  EntryDecoderState state_ = EntryDecoderState();
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackEntryDecoder& v);
+NET_EXPORT_PRIVATE std::ostream& operator<<(
+    std::ostream& out,
+    HpackEntryDecoder::EntryDecoderState state);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc b/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc
new file mode 100644
index 0000000..c7e991f
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_listener.cc
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+void HpackEntryDecoderVLoggingListener::OnIndexedHeader(size_t index) {
+  VLOG(1) << "OnIndexedHeader, index=" << index;
+  if (wrapped_) {
+    wrapped_->OnIndexedHeader(index);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnStartLiteralHeader(
+    HpackEntryType entry_type,
+    size_t maybe_name_index) {
+  VLOG(1) << "OnStartLiteralHeader: entry_type=" << entry_type
+          << ", maybe_name_index=" << maybe_name_index;
+  if (wrapped_) {
+    wrapped_->OnStartLiteralHeader(entry_type, maybe_name_index);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameStart(bool huffman_encoded,
+                                                    size_t len) {
+  VLOG(1) << "OnNameStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnNameStart(huffman_encoded, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameData(const char* data,
+                                                   size_t len) {
+  VLOG(1) << "OnNameData: len=" << len;
+  if (wrapped_) {
+    wrapped_->OnNameData(data, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameEnd() {
+  VLOG(1) << "OnNameEnd";
+  if (wrapped_) {
+    wrapped_->OnNameEnd();
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueStart(bool huffman_encoded,
+                                                     size_t len) {
+  VLOG(1) << "OnValueStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnValueStart(huffman_encoded, len);
+  }
+  return;
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueData(const char* data,
+                                                    size_t len) {
+  VLOG(1) << "OnValueData: len=" << len;
+  if (wrapped_) {
+    wrapped_->OnValueData(data, len);
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueEnd() {
+  VLOG(1) << "OnValueEnd";
+  if (wrapped_) {
+    wrapped_->OnValueEnd();
+  }
+}
+
+void HpackEntryDecoderVLoggingListener::OnDynamicTableSizeUpdate(size_t size) {
+  VLOG(1) << "OnDynamicTableSizeUpdate: size=" << size;
+  if (wrapped_) {
+    wrapped_->OnDynamicTableSizeUpdate(size);
+  }
+  return;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_listener.h b/net/http2/hpack/decoder/hpack_entry_decoder_listener.h
new file mode 100644
index 0000000..09fbd54
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_listener.h
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
+
+// Defines HpackEntryDecoderListener, the base class of listeners that
+// HpackEntryDecoder calls. Also defines HpackEntryDecoderVLoggingListener
+// which logs before calling another HpackEntryDecoderListener implementation.
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryDecoderListener {
+ public:
+  virtual ~HpackEntryDecoderListener() {}
+
+  // Called when an indexed header (i.e. one in the static or dynamic table) has
+  // been decoded from an HPACK block. index is supposed to be non-zero, but
+  // that has not been checked by the caller.
+  virtual void OnIndexedHeader(size_t index) = 0;
+
+  // Called when the start of a header with a literal value, and maybe a literal
+  // name, has been decoded. maybe_name_index is zero if the header has a
+  // literal name, else it is a reference into the static or dynamic table, from
+  // which the name should be determined. When the name is literal, the next
+  // call will be to OnNameStart; else it will be to OnValueStart. entry_type
+  // indicates whether the peer has added the entry to its dynamic table, and
+  // whether a proxy is permitted to do so when forwarding the entry.
+  virtual void OnStartLiteralHeader(HpackEntryType entry_type,
+                                    size_t maybe_name_index) = 0;
+
+  // Called when the encoding (Huffman compressed or plain text) and the encoded
+  // length of a literal name has been decoded. OnNameData will be called next,
+  // and repeatedly until the sum of lengths passed to OnNameData is len.
+  virtual void OnNameStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when len bytes of an encoded header name have been decoded.
+  virtual void OnNameData(const char* data, size_t len) = 0;
+
+  // Called after the entire name has been passed to OnNameData.
+  // OnValueStart will be called next.
+  virtual void OnNameEnd() = 0;
+
+  // Called when the encoding (Huffman compressed or plain text) and the encoded
+  // length of a literal value has been decoded. OnValueData will be called
+  // next, and repeatedly until the sum of lengths passed to OnValueData is len.
+  virtual void OnValueStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when len bytes of an encoded header value have been decoded.
+  virtual void OnValueData(const char* data, size_t len) = 0;
+
+  // Called after the entire value has been passed to OnValueData, marking the
+  // end of a header entry with a literal value, and maybe a literal name.
+  virtual void OnValueEnd() = 0;
+
+  // Called when an update to the size of the peer's dynamic table has been
+  // decoded.
+  virtual void OnDynamicTableSizeUpdate(size_t size) = 0;
+};
+
+class NET_EXPORT_PRIVATE HpackEntryDecoderVLoggingListener
+    : public HpackEntryDecoderListener {
+ public:
+  HpackEntryDecoderVLoggingListener() : wrapped_(nullptr) {}
+  explicit HpackEntryDecoderVLoggingListener(HpackEntryDecoderListener* wrapped)
+      : wrapped_(wrapped) {}
+  ~HpackEntryDecoderVLoggingListener() override {}
+
+  void OnIndexedHeader(size_t index) override;
+  void OnStartLiteralHeader(HpackEntryType entry_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+
+ private:
+  HpackEntryDecoderListener* const wrapped_;
+};
+
+// A no-op implementation of HpackEntryDecoderListener.
+class NET_EXPORT_PRIVATE HpackEntryDecoderNoOpListener
+    : public HpackEntryDecoderListener {
+ public:
+  ~HpackEntryDecoderNoOpListener() override {}
+
+  void OnIndexedHeader(size_t index) override {}
+  void OnStartLiteralHeader(HpackEntryType entry_type,
+                            size_t maybe_name_index) override {}
+  void OnNameStart(bool huffman_encoded, size_t len) override {}
+  void OnNameData(const char* data, size_t len) override {}
+  void OnNameEnd() override {}
+  void OnValueStart(bool huffman_encoded, size_t len) override {}
+  void OnValueData(const char* data, size_t len) override {}
+  void OnValueEnd() override {}
+  void OnDynamicTableSizeUpdate(size_t size) override {}
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_decoder_test.cc b/net/http2/hpack/decoder/hpack_entry_decoder_test.cc
new file mode 100644
index 0000000..34581fa
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_decoder_test.cc
@@ -0,0 +1,244 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_decoder.h"
+
+// Tests of HpackEntryDecoder.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "net/http2/hpack/decoder/hpack_entry_collector.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackEntryDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidateIndexedHeader(uint32_t ndx) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateIndexedHeader(ndx));
+  }
+
+  AssertionResult ValidateForIndexedLiteralValue_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, 0x40, false, "custom-header"));
+  }
+
+  AssertionResult ValidateForIndexedLiteralNameValue_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralNameValueHeader(
+        HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+        "custom-header"));
+  }
+
+  AssertionResult ValidateForDynamicTableSizeUpdate_Literal() {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateDynamicTableSizeUpdate(31));
+  }
+
+ protected:
+  HpackEntryDecoderTest() : listener_(&collector_) {}
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    collector_.Clear();
+    return decoder_.Start(b, &listener_);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b, &listener_);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+                                               Validator validator) {
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+    return RandomDecoderTest::DecodeAndValidateSeveralWays(
+        db, return_non_zero_on_first, validator);
+  }
+
+  AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+                                               Validator validator) {
+    DecodeBuffer db(hbb.buffer());
+    return DecodeAndValidateSeveralWays(&db, validator);
+  }
+
+  HpackEntryDecoder decoder_;
+  HpackEntryCollector collector_;
+  HpackEntryDecoderVLoggingListener listener_;
+};
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
+  {
+    const char input[] = {0x82u};  // == Index 2 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 2);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+  collector_.Clear();
+  {
+    const char input[] = {0xfeu};  // == Index 126 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 126);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+  collector_.Clear();
+  {
+    const char input[] = {0xffu, 0x00};  // == Index 127 ==
+    DecodeBuffer b(input);
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), 127);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Various) {
+  // Indices chosen to hit encoding and table boundaries.
+  for (const uint32_t ndx : {1, 2, 61, 62, 63, 126, 127, 254, 255, 256}) {
+    HpackBlockBuilder hbb;
+    hbb.AppendIndexedHeader(ndx);
+
+    NoArgValidator do_check =
+        base::Bind(&HpackEntryDecoderTest::ValidateIndexedHeader,
+                   base::Unretained(this), ndx);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralValue_Literal) {
+  const char input[] =
+      "\x7f"            // == Literal indexed, name index 0x40 ==
+      "\x01"            // 2nd byte of name index (0x01 + 0x3f == 0x40)
+      "\x0d"            // Value length (13)
+      "custom-header";  // Value
+  DecodeBuffer b(input, sizeof input - 1);
+  NoArgValidator do_check =
+      base::Bind(&HpackEntryDecoderTest::ValidateForIndexedLiteralValue_Literal,
+                 base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralNameValue_Literal) {
+  const char input[] =
+      "\x40"            // == Literal indexed ==
+      "\x0a"            // Name length (10)
+      "custom-key"      // Name
+      "\x0d"            // Value length (13)
+      "custom-header";  // Value
+
+  DecodeBuffer b(input, sizeof input - 1);
+  NoArgValidator do_check = base::Bind(
+      &HpackEntryDecoderTest::ValidateForIndexedLiteralNameValue_Literal,
+      base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+TEST_F(HpackEntryDecoderTest, DynamicTableSizeUpdate_Literal) {
+  // Size update, length 31.
+  const char input[] = "\x3f\x00";
+  DecodeBuffer b(input, 2);
+  NoArgValidator do_check = base::Bind(
+      &HpackEntryDecoderTest::ValidateForDynamicTableSizeUpdate_Literal,
+      base::Unretained(this));
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+  EXPECT_TRUE(do_check.Run());
+}
+
+class HpackLiteralEntryDecoderTest
+    : public HpackEntryDecoderTest,
+      public ::testing::WithParamInterface<HpackEntryType> {
+ public:
+  AssertionResult ValidateForRandNameIndexAndLiteralValue(
+      uint32_t ndx,
+      bool value_is_huffman_encoded,
+      const string& value) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralValueHeader(
+        entry_type_, ndx, value_is_huffman_encoded, value));
+  }
+
+  AssertionResult ValidateForRandLiteralNameAndValue(
+      bool name_is_huffman_encoded,
+      const string& name,
+      bool value_is_huffman_encoded,
+      const string& value) {
+    VERIFY_AND_RETURN_SUCCESS(collector_.ValidateLiteralNameValueHeader(
+        entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded,
+        value));
+  }
+
+ protected:
+  HpackLiteralEntryDecoderTest() : entry_type_(GetParam()) {}
+
+  const HpackEntryType entry_type_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    AllLiteralTypes,
+    HpackLiteralEntryDecoderTest,
+    testing::Values(HpackEntryType::kIndexedLiteralHeader,
+                    HpackEntryType::kUnindexedLiteralHeader,
+                    HpackEntryType::kNeverIndexedLiteralHeader));
+
+TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) {
+  for (int n = 0; n < 10; n++) {
+    const uint32_t ndx = 1 + Random().Rand8();
+    const bool value_is_huffman_encoded = (n % 2) == 0;
+    const string value = Random().RandString(Random().Rand8());
+    HpackBlockBuilder hbb;
+    hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx,
+                                       value_is_huffman_encoded, value);
+    NoArgValidator do_check = base::Bind(
+        &HpackLiteralEntryDecoderTest::ValidateForRandNameIndexAndLiteralValue,
+        base::Unretained(this), ndx, value_is_huffman_encoded, value);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) {
+  for (int n = 0; n < 10; n++) {
+    const bool name_is_huffman_encoded = (n & 1) == 0;
+    const int name_len = 1 + Random().Rand8();
+    const string name = Random().RandString(name_len);
+    const bool value_is_huffman_encoded = (n & 2) == 0;
+    const int value_len = Random().Skewed(10);
+    const string value = Random().RandString(value_len);
+    HpackBlockBuilder hbb;
+    hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name,
+                                  value_is_huffman_encoded, value);
+    NoArgValidator do_check = base::Bind(
+        &HpackLiteralEntryDecoderTest::ValidateForRandLiteralNameAndValue,
+        base::Unretained(this), name_is_huffman_encoded, name,
+        value_is_huffman_encoded, value);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+    EXPECT_TRUE(do_check.Run());
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder.cc b/net/http2/hpack/decoder/hpack_entry_type_decoder.cc
new file mode 100644
index 0000000..a05d48b
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder.cc
@@ -0,0 +1,360 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+
+namespace net {
+
+std::string HpackEntryTypeDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackEntryTypeDecoder(varint_decoder=" << varint_decoder_.DebugString()
+     << ", entry_type=" << entry_type_ << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryTypeDecoder& v) {
+  return out << v.DebugString();
+}
+
+// This ridiculous looking function turned out to be the winner in benchmarking
+// of several very different alternative implementations. It would be even
+// faster (~7%) if inlined in the header file, but I'm not sure if that is
+// worth doing... yet.
+// TODO(jamessynge): Benchmark again at a higher level (e.g. at least at the
+// full HTTP/2 decoder level, but preferably still higher) to determine if the
+// alternatives that take less code/data space are preferable in that situation.
+DecodeStatus HpackEntryTypeDecoder::Start(DecodeBuffer* db) {
+  DCHECK(db != nullptr);
+  DCHECK(db->HasData());
+
+  // The high four bits (nibble) of first byte of the entry determine the type
+  // of the entry, and may also be the initial bits of the varint that
+  // represents an index or table size. Note the use of the word 'initial'
+  // rather than 'high'; the HPACK encoding of varints is not in network
+  // order (i.e. not big-endian, the high-order byte isn't first), nor in
+  // little-endian order. See:
+  // http://httpwg.org/specs/rfc7541.html#integer.representation
+  uint8_t byte = db->DecodeUInt8();
+  switch (byte) {
+    case 0b00000000:
+    case 0b00000001:
+    case 0b00000010:
+    case 0b00000011:
+    case 0b00000100:
+    case 0b00000101:
+    case 0b00000110:
+    case 0b00000111:
+    case 0b00001000:
+    case 0b00001001:
+    case 0b00001010:
+    case 0b00001011:
+    case 0b00001100:
+    case 0b00001101:
+    case 0b00001110:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+      varint_decoder_.set_value(byte);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00001111:
+      // The low 4 bits of |byte| are the initial bits of the varint. All 4
+      // are 1, so the varint extends into another byte.
+      entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+      return varint_decoder_.StartExtended(0x0f, db);
+
+    case 0b00010000:
+    case 0b00010001:
+    case 0b00010010:
+    case 0b00010011:
+    case 0b00010100:
+    case 0b00010101:
+    case 0b00010110:
+    case 0b00010111:
+    case 0b00011000:
+    case 0b00011001:
+    case 0b00011010:
+    case 0b00011011:
+    case 0b00011100:
+    case 0b00011101:
+    case 0b00011110:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+      varint_decoder_.set_value(byte & 0x0f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00011111:
+      // The low 4 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+      return varint_decoder_.StartExtended(0x0f, db);
+
+    case 0b00100000:
+    case 0b00100001:
+    case 0b00100010:
+    case 0b00100011:
+    case 0b00100100:
+    case 0b00100101:
+    case 0b00100110:
+    case 0b00100111:
+    case 0b00101000:
+    case 0b00101001:
+    case 0b00101010:
+    case 0b00101011:
+    case 0b00101100:
+    case 0b00101101:
+    case 0b00101110:
+    case 0b00101111:
+    case 0b00110000:
+    case 0b00110001:
+    case 0b00110010:
+    case 0b00110011:
+    case 0b00110100:
+    case 0b00110101:
+    case 0b00110110:
+    case 0b00110111:
+    case 0b00111000:
+    case 0b00111001:
+    case 0b00111010:
+    case 0b00111011:
+    case 0b00111100:
+    case 0b00111101:
+    case 0b00111110:
+      entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+      // The low 5 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x01f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b00111111:
+      entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+      // The low 5 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x1f, db);
+
+    case 0b01000000:
+    case 0b01000001:
+    case 0b01000010:
+    case 0b01000011:
+    case 0b01000100:
+    case 0b01000101:
+    case 0b01000110:
+    case 0b01000111:
+    case 0b01001000:
+    case 0b01001001:
+    case 0b01001010:
+    case 0b01001011:
+    case 0b01001100:
+    case 0b01001101:
+    case 0b01001110:
+    case 0b01001111:
+    case 0b01010000:
+    case 0b01010001:
+    case 0b01010010:
+    case 0b01010011:
+    case 0b01010100:
+    case 0b01010101:
+    case 0b01010110:
+    case 0b01010111:
+    case 0b01011000:
+    case 0b01011001:
+    case 0b01011010:
+    case 0b01011011:
+    case 0b01011100:
+    case 0b01011101:
+    case 0b01011110:
+    case 0b01011111:
+    case 0b01100000:
+    case 0b01100001:
+    case 0b01100010:
+    case 0b01100011:
+    case 0b01100100:
+    case 0b01100101:
+    case 0b01100110:
+    case 0b01100111:
+    case 0b01101000:
+    case 0b01101001:
+    case 0b01101010:
+    case 0b01101011:
+    case 0b01101100:
+    case 0b01101101:
+    case 0b01101110:
+    case 0b01101111:
+    case 0b01110000:
+    case 0b01110001:
+    case 0b01110010:
+    case 0b01110011:
+    case 0b01110100:
+    case 0b01110101:
+    case 0b01110110:
+    case 0b01110111:
+    case 0b01111000:
+    case 0b01111001:
+    case 0b01111010:
+    case 0b01111011:
+    case 0b01111100:
+    case 0b01111101:
+    case 0b01111110:
+      entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+      // The low 6 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x03f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b01111111:
+      entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+      // The low 6 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x3f, db);
+
+    case 0b10000000:
+    case 0b10000001:
+    case 0b10000010:
+    case 0b10000011:
+    case 0b10000100:
+    case 0b10000101:
+    case 0b10000110:
+    case 0b10000111:
+    case 0b10001000:
+    case 0b10001001:
+    case 0b10001010:
+    case 0b10001011:
+    case 0b10001100:
+    case 0b10001101:
+    case 0b10001110:
+    case 0b10001111:
+    case 0b10010000:
+    case 0b10010001:
+    case 0b10010010:
+    case 0b10010011:
+    case 0b10010100:
+    case 0b10010101:
+    case 0b10010110:
+    case 0b10010111:
+    case 0b10011000:
+    case 0b10011001:
+    case 0b10011010:
+    case 0b10011011:
+    case 0b10011100:
+    case 0b10011101:
+    case 0b10011110:
+    case 0b10011111:
+    case 0b10100000:
+    case 0b10100001:
+    case 0b10100010:
+    case 0b10100011:
+    case 0b10100100:
+    case 0b10100101:
+    case 0b10100110:
+    case 0b10100111:
+    case 0b10101000:
+    case 0b10101001:
+    case 0b10101010:
+    case 0b10101011:
+    case 0b10101100:
+    case 0b10101101:
+    case 0b10101110:
+    case 0b10101111:
+    case 0b10110000:
+    case 0b10110001:
+    case 0b10110010:
+    case 0b10110011:
+    case 0b10110100:
+    case 0b10110101:
+    case 0b10110110:
+    case 0b10110111:
+    case 0b10111000:
+    case 0b10111001:
+    case 0b10111010:
+    case 0b10111011:
+    case 0b10111100:
+    case 0b10111101:
+    case 0b10111110:
+    case 0b10111111:
+    case 0b11000000:
+    case 0b11000001:
+    case 0b11000010:
+    case 0b11000011:
+    case 0b11000100:
+    case 0b11000101:
+    case 0b11000110:
+    case 0b11000111:
+    case 0b11001000:
+    case 0b11001001:
+    case 0b11001010:
+    case 0b11001011:
+    case 0b11001100:
+    case 0b11001101:
+    case 0b11001110:
+    case 0b11001111:
+    case 0b11010000:
+    case 0b11010001:
+    case 0b11010010:
+    case 0b11010011:
+    case 0b11010100:
+    case 0b11010101:
+    case 0b11010110:
+    case 0b11010111:
+    case 0b11011000:
+    case 0b11011001:
+    case 0b11011010:
+    case 0b11011011:
+    case 0b11011100:
+    case 0b11011101:
+    case 0b11011110:
+    case 0b11011111:
+    case 0b11100000:
+    case 0b11100001:
+    case 0b11100010:
+    case 0b11100011:
+    case 0b11100100:
+    case 0b11100101:
+    case 0b11100110:
+    case 0b11100111:
+    case 0b11101000:
+    case 0b11101001:
+    case 0b11101010:
+    case 0b11101011:
+    case 0b11101100:
+    case 0b11101101:
+    case 0b11101110:
+    case 0b11101111:
+    case 0b11110000:
+    case 0b11110001:
+    case 0b11110010:
+    case 0b11110011:
+    case 0b11110100:
+    case 0b11110101:
+    case 0b11110110:
+    case 0b11110111:
+    case 0b11111000:
+    case 0b11111001:
+    case 0b11111010:
+    case 0b11111011:
+    case 0b11111100:
+    case 0b11111101:
+    case 0b11111110:
+      entry_type_ = HpackEntryType::kIndexedHeader;
+      // The low 7 bits of |byte| are the initial bits of the varint.
+      // One of those bits is 0, so the varint is only one byte long.
+      varint_decoder_.set_value(byte & 0x07f);
+      return DecodeStatus::kDecodeDone;
+
+    case 0b11111111:
+      entry_type_ = HpackEntryType::kIndexedHeader;
+      // The low 7 bits of |byte| are the initial bits of the varint.
+      // All of those bits are 1, so the varint extends into another byte.
+      return varint_decoder_.StartExtended(0x7f, db);
+  }
+  CHECK(false) << "Unreachable, byte=" << std::hex
+               << static_cast<uint32_t>(byte);
+  return DecodeStatus::kDecodeError;
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder.h b/net/http2/hpack/decoder/hpack_entry_type_decoder.h
new file mode 100644
index 0000000..d2c1f54
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
+
+// Decodes the type of an HPACK entry, and the variable length integer whose
+// prefix is in the low-order bits of the same byte, "below" the type bits.
+// The integer represents an index into static or dynamic table, which may be
+// zero, or is the new size limit of the dynamic table.
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HpackEntryTypeDecoder {
+ public:
+  // Only call when the decode buffer has data (i.e. HpackEntryDecoder must
+  // not call until there is data).
+  DecodeStatus Start(DecodeBuffer* db);
+
+  // Only call Resume if the previous call (Start or Resume) returned
+  // DecodeStatus::kDecodeInProgress.
+  DecodeStatus Resume(DecodeBuffer* db) { return varint_decoder_.Resume(db); }
+
+  // Returns the decoded entry type. Only call if the preceding call to Start
+  // or Resume returned kDecodeDone.
+  HpackEntryType entry_type() const { return entry_type_; }
+
+  // Returns the decoded variable length integer. Only call if the
+  // preceding call to Start or Resume returned kDecodeDone.
+  uint32_t varint() const { return varint_decoder_.value(); }
+
+  std::string DebugString() const;
+
+ private:
+  HpackVarintDecoder varint_decoder_;
+
+  // This field is initialized just to keep ASAN happy about reading it
+  // from DebugString().
+  HpackEntryType entry_type_ = HpackEntryType::kIndexedHeader;
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackEntryTypeDecoder& v);
+
+}  // namespace net
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc b/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
new file mode 100644
index 0000000..0abf0c9
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace net {
+namespace test {
+namespace {
+const bool kReturnNonZeroOnFirst = true;
+
+class HpackEntryTypeDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForDynamicTableSizeUpdate(uint32_t size) {
+    VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, decoder_.entry_type());
+    VERIFY_EQ(size, decoder_.varint());
+    return AssertionSuccess();
+  }
+
+  AssertionResult ValidatorForHeaderWithIndex(const HpackEntryType entry_type,
+                                              uint32_t index) {
+    VERIFY_EQ(entry_type, decoder_.entry_type());
+    VERIFY_EQ(index, decoder_.varint());
+    return AssertionSuccess();
+  }
+
+ protected:
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    CHECK_LT(0u, b->Remaining());
+    return decoder_.Start(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b);
+  }
+
+  HpackEntryTypeDecoder decoder_;
+};
+
+TEST_F(HpackEntryTypeDecoderTest, DynamicTableSizeUpdate) {
+  for (uint32_t size = 0; size < 1000 * 1000; size += 256) {
+    HpackBlockBuilder bb;
+    bb.AppendDynamicTableSizeUpdate(size);
+    DecodeBuffer db(bb.buffer());
+    NoArgValidator validator = base::Bind(
+        &HpackEntryTypeDecoderTest::ValidatorForDynamicTableSizeUpdate,
+        base::Unretained(this), size);
+    EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+                                             ValidateDoneAndEmpty(validator)))
+        << "\nentry_type=kDynamicTableSizeUpdate, size=" << size;
+    // Run the validator again to make sure that DecodeAndValidateSeveralWays
+    // did the right thing.
+    EXPECT_TRUE(validator.Run());
+  }
+}
+
+TEST_F(HpackEntryTypeDecoderTest, HeaderWithIndex) {
+  std::vector<HpackEntryType> entry_types = {
+      HpackEntryType::kIndexedHeader, HpackEntryType::kIndexedLiteralHeader,
+      HpackEntryType::kUnindexedLiteralHeader,
+      HpackEntryType::kNeverIndexedLiteralHeader,
+  };
+  for (const HpackEntryType entry_type : entry_types) {
+    const uint32_t first = entry_type == HpackEntryType::kIndexedHeader ? 1 : 0;
+    for (uint32_t index = first; index < 1000; ++index) {
+      HpackBlockBuilder bb;
+      bb.AppendEntryTypeAndVarint(entry_type, index);
+      DecodeBuffer db(bb.buffer());
+      NoArgValidator validator =
+          base::Bind(&HpackEntryTypeDecoderTest::ValidatorForHeaderWithIndex,
+                     base::Unretained(this), entry_type, index);
+      EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+                                               ValidateDoneAndEmpty(validator)))
+          << "\nentry_type=" << entry_type << ", index=" << index;
+      // Run the validator again to make sure that DecodeAndValidateSeveralWays
+      // did the right thing.
+      EXPECT_TRUE(validator.Run());
+    }
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_collector.cc b/net/http2/hpack/decoder/hpack_string_collector.cc
new file mode 100644
index 0000000..e7e8195
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_collector.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "net/base/escape.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+
+std::ostream& operator<<(std::ostream& out,
+                         HpackStringCollector::CollectorState v) {
+  switch (v) {
+    case HpackStringCollector::CollectorState::kGenesis:
+      return out << "kGenesis";
+    case HpackStringCollector::CollectorState::kStarted:
+      return out << "kStarted";
+    case HpackStringCollector::CollectorState::kEnded:
+      return out << "kEnded";
+  }
+  return out << "UnknownCollectorState";
+}
+
+}  // namespace
+
+HpackStringCollector::HpackStringCollector() {
+  Clear();
+}
+
+HpackStringCollector::HpackStringCollector(const std::string& str, bool huffman)
+    : s(str), len(str.size()), huffman_encoded(huffman), state(kEnded) {}
+
+void HpackStringCollector::Clear() {
+  s = "";
+  len = 0;
+  huffman_encoded = false;
+  state = kGenesis;
+}
+
+bool HpackStringCollector::IsClear() const {
+  return s == "" && len == 0 && huffman_encoded == false && state == kGenesis;
+}
+
+bool HpackStringCollector::IsInProgress() const {
+  return state == kStarted;
+}
+
+bool HpackStringCollector::HasEnded() const {
+  return state == kEnded;
+}
+
+void HpackStringCollector::OnStringStart(bool huffman, size_t length) {
+  EXPECT_TRUE(IsClear()) << ToString();
+  state = kStarted;
+  huffman_encoded = huffman;
+  len = length;
+  return;
+}
+
+void HpackStringCollector::OnStringData(const char* data, size_t length) {
+  StringPiece sp(data, length);
+  EXPECT_TRUE(IsInProgress()) << ToString();
+  EXPECT_LE(sp.size(), len) << ToString();
+  sp.AppendToString(&s);
+  EXPECT_LE(s.size(), len) << ToString();
+}
+
+void HpackStringCollector::OnStringEnd() {
+  EXPECT_TRUE(IsInProgress()) << ToString();
+  EXPECT_EQ(s.size(), len) << ToString();
+  state = kEnded;
+}
+
+::testing::AssertionResult HpackStringCollector::Collected(
+    StringPiece str,
+    bool is_huffman_encoded) const {
+  VERIFY_TRUE(HasEnded());
+  VERIFY_EQ(str.size(), len);
+  VERIFY_EQ(is_huffman_encoded, huffman_encoded);
+  VERIFY_EQ(str, s);
+  return ::testing::AssertionSuccess();
+}
+
+std::string HpackStringCollector::ToString() const {
+  std::stringstream ss;
+  ss << *this;
+  return ss.str();
+}
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b) {
+  return a.s == b.s && a.len == b.len &&
+         a.huffman_encoded == b.huffman_encoded && a.state == b.state;
+}
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b) {
+  return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v) {
+  out << "HpackStringCollector(state=" << v.state;
+  if (v.state == HpackStringCollector::kGenesis) {
+    return out << ")";
+  }
+  if (v.huffman_encoded) {
+    out << ", Huffman Encoded";
+  }
+  out << ", Length=" << v.len;
+  if (!v.s.empty() && v.len != v.s.size()) {
+    out << " (" << v.s.size() << ")";
+  }
+  return out << ", String=\"" << EscapeQueryParamValue(v.s, false) << "\")";
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_collector.h b/net/http2/hpack/decoder/hpack_string_collector.h
new file mode 100644
index 0000000..1bc5831
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_collector.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
+
+// Supports tests of decoding HPACK strings.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Records the callbacks associated with a decoding a string; must
+// call Clear() between decoding successive strings.
+struct HpackStringCollector : public HpackStringDecoderListener {
+  enum CollectorState {
+    kGenesis,
+    kStarted,
+    kEnded,
+  };
+
+  HpackStringCollector();
+  HpackStringCollector(const std::string& str, bool huffman);
+
+  void Clear();
+  bool IsClear() const;
+  bool IsInProgress() const;
+  bool HasEnded() const;
+
+  void OnStringStart(bool huffman, size_t length) override;
+  void OnStringData(const char* data, size_t length) override;
+  void OnStringEnd() override;
+
+  ::testing::AssertionResult Collected(base::StringPiece str,
+                                       bool is_huffman_encoded) const;
+
+  std::string ToString() const;
+
+  std::string s;
+  size_t len;
+  bool huffman_encoded;
+  CollectorState state;
+};
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b);
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b);
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder.cc b/net/http2/hpack/decoder/hpack_string_decoder.cc
new file mode 100644
index 0000000..4131e01
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder.cc
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackStringDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackStringDecoder(state=" << StateToString(state_)
+     << ", length=" << length_decoder_.DebugString()
+     << ", remaining=" << remaining_
+     << ", huffman=" << (huffman_encoded_ ? "true)" : "false)");
+  return ss.str();
+}
+
+// static
+std::string HpackStringDecoder::StateToString(StringDecoderState v) {
+  switch (v) {
+    case kStartDecodingLength:
+      return "kStartDecodingLength";
+    case kDecodingString:
+      return "kDecodingString";
+    case kResumeDecodingLength:
+      return "kResumeDecodingLength";
+  }
+  std::stringstream ss;
+  ss << "UNKNOWN_STATE(" << static_cast<uint32_t>(v) << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_decoder.h b/net/http2/hpack/decoder/hpack_string_decoder.h
new file mode 100644
index 0000000..baea422
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder.h
@@ -0,0 +1,236 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
+
+// HpackStringDecoder decodes strings encoded per the HPACK spec; this does
+// not mean decompressing Huffman encoded strings, just identifying the length,
+// encoding and contents for a listener.
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+namespace net {
+
+// Decodes a single string in an HPACK header entry. The high order bit of
+// the first byte of the length is the H (Huffman) bit indicating whether
+// the value is Huffman encoded, and the remainder of the byte is the first
+// 7 bits of an HPACK varint.
+//
+// Call Start() to begin decoding; if it returns kDecodeInProgress, then call
+// Resume() when more input is available, repeating until kDecodeInProgress is
+// not returned. If kDecodeDone or kDecodeError is returned, then Resume() must
+// not be called until Start() has been called to start decoding a new string.
+//
+// There are 3 variants of Start in this class, participants in a performance
+// experiment. Perflab experiments show it is generally fastest to call
+// StartSpecialCaseShort rather than StartOnly (~9% slower) or
+// StartAndDecodeLength (~10% slower).
+class NET_EXPORT_PRIVATE HpackStringDecoder {
+ public:
+  enum StringDecoderState {
+    kStartDecodingLength,
+    kDecodingString,
+    kResumeDecodingLength,
+  };
+
+  // TODO(jamessynge): Get rid of all but one of the Start and Resume methods
+  // after all of the HPACK decoder is checked in and has been perf tested.
+  template <class Listener>
+  DecodeStatus Start(DecodeBuffer* db, Listener* cb) {
+    return StartSpecialCaseShort(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus StartOnly(DecodeBuffer* db, Listener* cb) {
+    state_ = kStartDecodingLength;
+    return Resume(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus StartAndDecodeLength(DecodeBuffer* db, Listener* cb) {
+    DecodeStatus status;
+    if (StartDecodingLength(db, cb, &status)) {
+      state_ = kDecodingString;
+      return DecodeString(db, cb);
+    }
+    return status;
+  }
+
+  template <class Listener>
+  DecodeStatus StartSpecialCaseShort(DecodeBuffer* db, Listener* cb) {
+    // Fast decode path is used if the string is under 127 bytes and the
+    // entire length of the string is in the decode buffer. More than 83% of
+    // string lengths are encoded in just one byte.
+    if (db->HasData() && (*db->cursor() & 0x7f) != 0x7f) {
+      // The string is short.
+      uint8_t h_and_prefix = db->DecodeUInt8();
+      uint8_t length = h_and_prefix & 0x7f;
+      bool huffman_encoded = (h_and_prefix & 0x80) == 0x80;
+      cb->OnStringStart(huffman_encoded, length);
+      if (length <= db->Remaining()) {
+        // Yeah, we've got the whole thing in the decode buffer.
+        // Ideally this will be the common case. Note that we don't
+        // update any of the member variables in this path.
+        cb->OnStringData(db->cursor(), length);
+        db->AdvanceCursor(length);
+        cb->OnStringEnd();
+        return DecodeStatus::kDecodeDone;
+      }
+      // Not all in the buffer.
+      huffman_encoded_ = huffman_encoded;
+      remaining_ = length;
+      // Call Resume to decode the string body, which is only partially
+      // in the decode buffer (or not at all).
+      state_ = kDecodingString;
+      return Resume(db, cb);
+    }
+    // Call Resume to decode the string length, which is either not in
+    // the decode buffer, or spans multiple bytes.
+    state_ = kStartDecodingLength;
+    return Resume(db, cb);
+  }
+
+  template <class Listener>
+  DecodeStatus Resume(DecodeBuffer* db, Listener* cb) {
+    DecodeStatus status;
+    while (true) {
+      switch (state_) {
+        case kStartDecodingLength:
+          DVLOG(2) << "kStartDecodingLength: db->Remaining=" << db->Remaining();
+          if (!StartDecodingLength(db, cb, &status)) {
+            // The length is split across decode buffers.
+            return status;
+          }
+        // We've finished decoding the length, which spanned one or more
+        // bytes. Approximately 17% of strings have a length that is greater
+        // than 126 bytes, and thus the length is encoded in more than one
+        // byte, and so doesn't get the benefit of the optimization in
+        // Start() for single byte lengths. But, we still expect that most
+        // of such strings will be contained entirely in a single decode
+        // buffer, and hence this fall through skips another trip through the
+        // switch above and more importantly skips setting the state_ variable
+        // again in those cases where we don't need it.
+
+        // FALLTHROUGH_INTENDED
+
+        case kDecodingString:
+          DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
+                   << "    remaining_=" << remaining_;
+          return DecodeString(db, cb);
+
+        case kResumeDecodingLength:
+          DVLOG(2) << "kResumeDecodingLength: db->Remaining="
+                   << db->Remaining();
+          if (!ResumeDecodingLength(db, cb, &status)) {
+            return status;
+          }
+      }
+    }
+  }
+
+  std::string DebugString() const;
+
+ private:
+  static std::string StateToString(StringDecoderState v);
+
+  // Returns true if the length is fully decoded and the listener wants the
+  // decoding to continue, false otherwise; status is set to the status from
+  // the varint decoder.
+  // If the length is not fully decoded, case state_ is set appropriately
+  // for the next call to Resume.
+  template <class Listener>
+  bool StartDecodingLength(DecodeBuffer* db,
+                           Listener* cb,
+                           DecodeStatus* status) {
+    if (db->Empty()) {
+      *status = DecodeStatus::kDecodeInProgress;
+      state_ = kStartDecodingLength;
+      return false;
+    }
+    uint8_t h_and_prefix = db->DecodeUInt8();
+    huffman_encoded_ = (h_and_prefix & 0x80) == 0x80;
+    *status = length_decoder_.Start(h_and_prefix, 0x7f, db);
+    if (*status == DecodeStatus::kDecodeDone) {
+      OnStringStart(cb, status);
+      return true;
+    }
+    // Set the state to cover the DecodeStatus::kDecodeInProgress case.
+    // Won't be needed if the status is kDecodeError.
+    state_ = kResumeDecodingLength;
+    return false;
+  }
+
+  // Returns true if the length is fully decoded and the listener wants the
+  // decoding to continue, false otherwise; status is set to the status from
+  // the varint decoder; state_ is updated when fully decoded.
+  // If the length is not fully decoded, case state_ is set appropriately
+  // for the next call to Resume.
+  template <class Listener>
+  bool ResumeDecodingLength(DecodeBuffer* db,
+                            Listener* cb,
+                            DecodeStatus* status) {
+    DCHECK_EQ(state_, kResumeDecodingLength);
+    *status = length_decoder_.Resume(db);
+    if (*status == DecodeStatus::kDecodeDone) {
+      state_ = kDecodingString;
+      OnStringStart(cb, status);
+      return true;
+    }
+    return false;
+  }
+
+  // Returns true if the listener wants the decoding to continue, and
+  // false otherwise, in which case status set.
+  template <class Listener>
+  void OnStringStart(Listener* cb, DecodeStatus* status) {
+    remaining_ = length_decoder_.value();
+    // Make callback so consumer knows what is coming.
+    cb->OnStringStart(huffman_encoded_, remaining_);
+    return;
+  }
+
+  // Passes the available portion of the string to the listener, and signals
+  // the end of the string when it is reached. Returns kDecodeDone or
+  // kDecodeInProgress as appropriate.
+  template <class Listener>
+  DecodeStatus DecodeString(DecodeBuffer* db, Listener* cb) {
+    size_t len = std::min(remaining_, db->Remaining());
+    if (len > 0) {
+      cb->OnStringData(db->cursor(), len);
+      db->AdvanceCursor(len);
+      remaining_ -= len;
+    }
+    if (remaining_ == 0) {
+      cb->OnStringEnd();
+      return DecodeStatus::kDecodeDone;
+    }
+    state_ = kDecodingString;
+    return DecodeStatus::kDecodeInProgress;
+  }
+
+  HpackVarintDecoder length_decoder_;
+
+  // These fields are initialized just to keep ASAN happy about reading
+  // them from DebugString().
+  size_t remaining_ = 0;
+  StringDecoderState state_ = kStartDecodingLength;
+  bool huffman_encoded_ = false;
+};
+
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const HpackStringDecoder& v);
+
+}  // namespace net
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_listener.cc b/net/http2/hpack/decoder/hpack_string_decoder_listener.cc
new file mode 100644
index 0000000..97ec985
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_listener.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+
+#include "base/logging.h"
+
+namespace net {
+namespace test {
+
+void HpackStringDecoderVLoggingListener::OnStringStart(bool huffman_encoded,
+                                                       size_t len) {
+  VLOG(1) << "OnStringStart: H=" << huffman_encoded << ", len=" << len;
+  if (wrapped_) {
+    wrapped_->OnStringStart(huffman_encoded, len);
+  }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringData(const char* data,
+                                                      size_t len) {
+  VLOG(1) << "OnStringData: len=" << len;
+  if (wrapped_) {
+    return wrapped_->OnStringData(data, len);
+  }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringEnd() {
+  VLOG(1) << "OnStringEnd";
+  if (wrapped_) {
+    return wrapped_->OnStringEnd();
+  }
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_listener.h b/net/http2/hpack/decoder/hpack_string_decoder_listener.h
new file mode 100644
index 0000000..fbeeeb4
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_listener.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
+
+// Defines HpackStringDecoderListener which defines the methods required by an
+// HpackStringDecoder. Also defines HpackStringDecoderVLoggingListener which
+// logs before calling another HpackStringDecoderListener implementation.
+// For now these are only used by tests, so placed in the test namespace.
+
+#include <stddef.h>
+
+#include "net/base/net_export.h"
+
+namespace net {
+namespace test {
+
+// HpackStringDecoder methods require a listener that implements the methods
+// below, but it is NOT necessary to extend this class because the methods
+// are templates.
+class NET_EXPORT_PRIVATE HpackStringDecoderListener {
+ public:
+  virtual ~HpackStringDecoderListener() {}
+
+  // Called at the start of decoding an HPACK string. The encoded length of the
+  // string is |len| bytes, which may be zero. The string is Huffman encoded
+  // if huffman_encoded is true, else it is plain text (i.e. the encoded length
+  // is then the plain text length).
+  virtual void OnStringStart(bool huffman_encoded, size_t len) = 0;
+
+  // Called when some data is available, or once when the string length is zero
+  // (to simplify the decoder, it doesn't have a special case for len==0).
+  virtual void OnStringData(const char* data, size_t len) = 0;
+
+  // Called after OnStringData has provided all of the encoded bytes of the
+  // string.
+  virtual void OnStringEnd() = 0;
+};
+
+class NET_EXPORT_PRIVATE HpackStringDecoderVLoggingListener
+    : public HpackStringDecoderListener {
+ public:
+  HpackStringDecoderVLoggingListener() : wrapped_(nullptr) {}
+  explicit HpackStringDecoderVLoggingListener(
+      HpackStringDecoderListener* wrapped)
+      : wrapped_(wrapped) {}
+  ~HpackStringDecoderVLoggingListener() override {}
+
+  void OnStringStart(bool huffman_encoded, size_t len) override;
+  void OnStringData(const char* data, size_t len) override;
+  void OnStringEnd() override;
+
+ private:
+  HpackStringDecoderListener* const wrapped_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
diff --git a/net/http2/hpack/decoder/hpack_string_decoder_test.cc b/net/http2/hpack/decoder/hpack_string_decoder_test.cc
new file mode 100644
index 0000000..e4afc61
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_string_decoder_test.cc
@@ -0,0 +1,194 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_string_decoder.h"
+
+// Tests of HpackStringDecoder.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/decoder/hpack_string_collector.h"
+#include "net/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+const bool kMayReturnZeroOnFirst = false;
+const bool kCompressed = true;
+const bool kUncompressed = false;
+
+enum StartMethod {
+  kStart,
+  kStartOnly,
+  kStartAndDecodeLength,
+  kStartSpecialCaseShort,
+};
+
+class HpackStringDecoderTest
+    : public RandomDecoderTest,
+      public ::testing::WithParamInterface<StartMethod> {
+ protected:
+  HpackStringDecoderTest()
+      : start_method_(GetParam()), listener_(&collector_) {}
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    ++start_decoding_calls_;
+    collector_.Clear();
+    switch (start_method_) {
+      case kStart:
+        return decoder_.Start(b, &listener_);
+      case kStartOnly:
+        return decoder_.StartOnly(b, &listener_);
+      case kStartAndDecodeLength:
+        return decoder_.StartAndDecodeLength(b, &listener_);
+      case kStartSpecialCaseShort:
+        return decoder_.StartSpecialCaseShort(b, &listener_);
+      default:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    // Provides coverage of DebugString and StateToString.
+    // Not validating output.
+    VLOG(1) << decoder_.DebugString();
+    VLOG(2) << collector_;
+    return decoder_.Resume(b, &listener_);
+  }
+
+  AssertionResult Collected(StringPiece s, bool huffman_encoded) {
+    VLOG(1) << collector_;
+    return collector_.Collected(s, huffman_encoded);
+  }
+
+  // Note that base::Bind() makes a copy of |expected_str| even though it is
+  // taken as a constant reference, so even if MakeValidator is called with a
+  // C-style string that is cast to a temporary std::string that gets destroyed
+  // after the call to MakeValidator, |expected_str| is still valid later when
+  // the Validator is run.
+  AssertionResult StringValidator(const string& expected_str,
+                                  bool expected_huffman,
+                                  const DecodeBuffer& input,
+                                  DecodeStatus status) {
+    AssertionResult result = Collected(expected_str, expected_huffman);
+    if (result) {
+      VERIFY_EQ(collector_,
+                HpackStringCollector(expected_str, expected_huffman));
+    } else {
+      VERIFY_NE(collector_,
+                HpackStringCollector(expected_str, expected_huffman));
+    }
+    VLOG(2) << collector_.ToString();
+    collector_.Clear();
+    VLOG(2) << collector_;
+    return result;
+  }
+
+  Validator MakeValidator(const string& expected_str, bool expected_huffman) {
+    return base::Bind(&HpackStringDecoderTest::StringValidator,
+                      base::Unretained(this), expected_str, expected_huffman);
+  }
+
+  const StartMethod start_method_;
+  HpackStringDecoder decoder_;
+  HpackStringCollector collector_;
+  HpackStringDecoderVLoggingListener listener_;
+  size_t start_decoding_calls_ = 0;
+};
+
+TEST_P(HpackStringDecoderTest, DecodeEmptyString) {
+  {
+    Validator validator = ValidateDoneAndEmpty(MakeValidator("", kCompressed));
+    const char kData[] = {0x80u};
+    DecodeBuffer b(kData);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+  {
+    // Make sure it stops after decoding the empty string.
+    Validator validator =
+        ValidateDoneAndOffset(1, MakeValidator("", kUncompressed));
+    const char kData[] = {0x00, 0xffu};
+    DecodeBuffer b(kData);
+    EXPECT_EQ(2u, b.Remaining());
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+    EXPECT_EQ(1u, b.Remaining());
+  }
+}
+
+TEST_P(HpackStringDecoderTest, DecodeShortString) {
+  {
+    // Make sure it stops after decoding the non-empty string.
+    Validator validator =
+        ValidateDoneAndOffset(11, MakeValidator("start end.", kCompressed));
+    const char kData[] = "\x8astart end.Don't peek at this.";
+    DecodeBuffer b(kData);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+  {
+    Validator validator =
+        ValidateDoneAndOffset(11, MakeValidator("start end.", kUncompressed));
+    StringPiece data("\x0astart end.");
+    DecodeBuffer b(data);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+  }
+}
+
+TEST_P(HpackStringDecoderTest, DecodeLongStrings) {
+  string name = Random().RandString(1024);
+  string value = Random().RandString(65536);
+  HpackBlockBuilder hbb;
+
+  hbb.AppendString(false, name);
+  uint32_t offset_after_name = hbb.size();
+  EXPECT_EQ(3 + name.size(), offset_after_name);
+
+  hbb.AppendString(true, value);
+  uint32_t offset_after_value = hbb.size();
+  EXPECT_EQ(3 + name.size() + 4 + value.size(), offset_after_value);
+
+  DecodeBuffer b(hbb.buffer());
+
+  // Decode the name...
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(
+      &b, kMayReturnZeroOnFirst,
+      ValidateDoneAndOffset(offset_after_name,
+                            MakeValidator(name, kUncompressed))));
+  EXPECT_EQ(offset_after_name, b.Offset());
+  EXPECT_EQ(offset_after_value - offset_after_name, b.Remaining());
+
+  // Decode the value...
+  EXPECT_TRUE(DecodeAndValidateSeveralWays(
+      &b, kMayReturnZeroOnFirst,
+      ValidateDoneAndOffset(offset_after_value - offset_after_name,
+                            MakeValidator(value, kCompressed))));
+  EXPECT_EQ(offset_after_value, b.Offset());
+  EXPECT_EQ(0u, b.Remaining());
+}
+
+INSTANTIATE_TEST_CASE_P(AllStartMethods,
+                        HpackStringDecoderTest,
+                        ::testing::Values(kStart,
+                                          kStartOnly,
+                                          kStartAndDecodeLength,
+                                          kStartSpecialCaseShort));
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder.cc b/net/http2/hpack/decoder/hpack_varint_decoder.cc
new file mode 100644
index 0000000..f04bd89d
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackVarintDecoder::DebugString() const {
+  std::stringstream ss;
+  ss << "HpackVarintDecoder(value=" << value_ << ", offset=" << offset_ << ")";
+  return ss.str();
+}
+
+DecodeStatus HpackVarintDecoder::StartForTest(uint8_t prefix_value,
+                                              uint8_t prefix_mask,
+                                              DecodeBuffer* db) {
+  return Start(prefix_value, prefix_mask, db);
+}
+
+DecodeStatus HpackVarintDecoder::StartExtendedForTest(uint8_t prefix_mask,
+                                                      DecodeBuffer* db) {
+  return StartExtended(prefix_mask, db);
+}
+
+DecodeStatus HpackVarintDecoder::ResumeForTest(DecodeBuffer* db) {
+  return Resume(db);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackVarintDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder.h b/net/http2/hpack/decoder/hpack_varint_decoder.h
new file mode 100644
index 0000000..b110fad
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder.h
@@ -0,0 +1,181 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// HpackVarintDecoder decodes HPACK variable length unsigned integers. These
+// integers are used to identify static or dynamic table index entries, to
+// specify string lengths, and to update the size limit of the dynamic table.
+//
+// The caller will need to validate that the decoded value is in an acceptable
+// range.
+//
+// In order to support naive encoders (i.e. which always output 5 extension
+// bytes for a uint32 that is >= prefix_mask), the decoder supports an an
+// encoding with up to 5 extension bytes, and a maximum value of 268,435,582
+// (4 "full" extension bytes plus the maximum for a prefix, 127). It could be
+// modified to support a lower maximum value (by requiring that extensions bytes
+// be "empty"), or a larger value if valuable for some reason I can't see.
+//
+// For details of the encoding, see:
+//        http://httpwg.org/specs/rfc7541.html#integer.representation
+//
+// TODO(jamessynge): Consider dropping support for encodings of more than 4
+// bytes, including the prefix byte, as in practice we only see at most 3 bytes,
+// and 4 bytes would cover any desire to support large (but not ridiculously
+// large) header values.
+
+#ifndef NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
+#define NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+
+namespace net {
+// Decodes an HPACK variable length unsigned integer, in a resumable fashion
+// so it can handle running out of input in the DecodeBuffer. Call Start or
+// StartExtended the first time (when decoding the byte that contains the
+// prefix), then call Resume later if it is necessary to resume. When done,
+// call value() to retrieve the decoded value.
+//
+// No constructor or destructor. Holds no resources, so destruction isn't
+// needed. Start and StartExtended handles the initialization of member
+// variables. This is necessary in order for HpackVarintDecoder to be part
+// of a union.
+class NET_EXPORT_PRIVATE HpackVarintDecoder {
+ public:
+  // |prefix_value| is the first byte of the encoded varint.
+  // |prefix_mask| is the mask of the valid bits, i.e. without the top 1 to 4
+  // high-bits set, as appropriate for the item being decoded; must be a
+  // contiguous sequence of set bits, starting with the low-order bits.
+  DecodeStatus Start(uint8_t prefix_value,
+                     uint8_t prefix_mask,
+                     DecodeBuffer* db) {
+    DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
+    DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
+    // Confirm that |prefix_mask| is a contiguous sequence of bits.
+    DCHECK_EQ(0, (prefix_mask + 1) & prefix_mask) << std::hex << prefix_mask;
+
+    // Ignore the bits that aren't a part of the prefix of the varint.
+    value_ = prefix_value & prefix_mask;
+
+    if (value_ < prefix_mask) {
+      MarkDone();
+      return DecodeStatus::kDecodeDone;
+    }
+
+    offset_ = 0;
+    return Resume(db);
+  }
+
+  // The caller has already determined that the encoding requires multiple
+  // bytes, i.e. that the 4 to 7 low-order bits (the number determined by the
+  // prefix length, a value not passed into this function) of the first byte are
+  // are all 1. The caller passes in |prefix_mask|, which is 2^prefix_length-1.
+  DecodeStatus StartExtended(uint8_t prefix_mask, DecodeBuffer* db) {
+    DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
+    DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
+    // Confirm that |prefix_mask| is a contiguous sequence of bits.
+    DCHECK_EQ(0, prefix_mask & (prefix_mask + 1)) << std::hex << prefix_mask;
+
+    value_ = prefix_mask;
+    offset_ = 0;
+    return Resume(db);
+  }
+
+  // Resume decoding a variable length integer after an earlier
+  // call to Start or StartExtended returned kDecodeInProgress.
+  DecodeStatus Resume(DecodeBuffer* db) {
+    CheckNotDone();
+    do {
+      if (db->Empty()) {
+        return DecodeStatus::kDecodeInProgress;
+      }
+      uint8_t byte = db->DecodeUInt8();
+      value_ += (byte & 0x7f) << offset_;
+      if ((byte & 0x80) == 0) {
+        if (offset_ < MaxOffset() || byte == 0) {
+          MarkDone();
+          return DecodeStatus::kDecodeDone;
+        }
+        break;
+      }
+      offset_ += 7;
+    } while (offset_ <= MaxOffset());
+    DLOG(WARNING) << "Variable length int encoding is too large or too long. "
+                  << DebugString();
+    MarkDone();
+    return DecodeStatus::kDecodeError;
+  }
+
+  uint32_t value() const {
+    CheckDone();
+    return value_;
+  }
+
+  // This supports optimizations for the case of a varint with zero extension
+  // bytes, where the handling of the prefix is done by the caller.
+  void set_value(uint32_t v) {
+    MarkDone();
+    value_ = v;
+  }
+
+  // All the public methods below are for supporting assertions and tests.
+
+  std::string DebugString() const;
+
+  // For benchmarking, these methods ensure the decoder
+  // is NOT inlined into the caller.
+  DecodeStatus StartForTest(uint8_t prefix_value,
+                            uint8_t prefix_mask,
+                            DecodeBuffer* db);
+  DecodeStatus StartExtendedForTest(uint8_t prefix_mask, DecodeBuffer* db);
+  DecodeStatus ResumeForTest(DecodeBuffer* db);
+
+  static constexpr uint32_t MaxExtensionBytes() { return 5; }
+
+  // Returns the highest value with the specified number of extension bytes and
+  // the specified prefix length (bits).
+  static uint64_t constexpr HiValueOfExtensionBytes(uint32_t extension_bytes,
+                                                    uint32_t prefix_length) {
+    return (1 << prefix_length) - 2 +
+           (extension_bytes == 0 ? 0 : (1LLU << (extension_bytes * 7)));
+  }
+
+ private:
+  // Protection in case Resume is called when it shouldn't be.
+  void MarkDone() {
+#ifndef NDEBUG
+    // We support up to 5 extension bytes, so offset_ should never be > 28 when
+    // it makes sense to call Resume().
+    offset_ = MaxOffset() + 7;
+#endif
+  }
+  void CheckNotDone() const {
+#ifndef NDEBUG
+    DCHECK_LE(offset_, MaxOffset());
+#endif
+  }
+  void CheckDone() const {
+#ifndef NDEBUG
+    DCHECK_GT(offset_, MaxOffset());
+#endif
+  }
+  static constexpr uint32_t MaxOffset() {
+    return 7 * (MaxExtensionBytes() - 1);
+  }
+
+  // These fields are initialized just to keep ASAN happy about reading
+  // them from DebugString().
+  uint32_t value_ = 0;
+  uint32_t offset_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& out, const HpackVarintDecoder& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
diff --git a/net/http2/hpack/decoder/hpack_varint_decoder_test.cc b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
new file mode 100644
index 0000000..e63e9f0
--- /dev/null
+++ b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
@@ -0,0 +1,395 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
+
+// Tests of HpackVarintDecoder.
+
+#include <stddef.h>
+
+#include <ios>
+#include <iterator>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackVarintDecoderTest : public RandomDecoderTest {
+ public:
+  AssertionResult ValidatorForValueTooLarge(bool* validated,
+                                            uint32_t expected_offset,
+                                            const DecodeBuffer& db,
+                                            DecodeStatus status) {
+    *validated = true;
+    VERIFY_EQ(DecodeStatus::kDecodeError, status);
+    VERIFY_EQ(expected_offset, db.Offset());
+    return AssertionSuccess();
+  }
+
+ protected:
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    CHECK_LT(0u, b->Remaining());
+    CHECK_NE(0, prefix_mask_);
+    uint8_t prefix = b->DecodeUInt8();
+    return decoder_.Start(prefix, prefix_mask_, b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    return decoder_.Resume(b);
+  }
+
+  AssertionResult ValidatorForDecodeSeveralWays(uint32_t expected_value,
+                                                const DecodeBuffer& db,
+                                                DecodeStatus status) {
+    if (decoder_.value() != expected_value) {
+      return AssertionFailure()
+             << "Value doesn't match expected: " << decoder_.value()
+             << " != " << expected_value;
+    }
+    return AssertionSuccess();
+  }
+
+  void DecodeSeveralWays(uint32_t expected_value, uint32_t expected_offset) {
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that decoder_.value() matches the expected value.
+    Validator validator =
+        base::Bind(&HpackVarintDecoderTest::ValidatorForDecodeSeveralWays,
+                   base::Unretained(this), expected_value);
+
+    // First validate that decoding is done and that we've advanced the cursor
+    // the expected amount.
+    validator = ValidateDoneAndOffset(expected_offset, validator);
+
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+
+    DecodeBuffer b(buffer_);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+
+    EXPECT_EQ(expected_value, decoder_.value());
+    EXPECT_EQ(expected_offset, b.Offset());
+  }
+
+  void EncodeNoRandom(uint32_t value, uint8_t prefix_length) {
+    DCHECK_LE(4, prefix_length);
+    DCHECK_LE(prefix_length, 7);
+    prefix_length_ = prefix_length;
+
+    HpackBlockBuilder bb;
+    bb.AppendHighBitsAndVarint(0, prefix_length_, value);
+    buffer_ = bb.buffer();
+    ASSERT_LT(0u, buffer_.size());
+
+    // Note: setting member variable prefix_mask_ here, which will be read
+    // in StartDecoding above.
+    prefix_mask_ = (1 << prefix_length_) - 1;
+    ASSERT_EQ(buffer_[0], buffer_[0] & prefix_mask_);
+  }
+
+  void Encode(uint32_t value, uint8_t prefix_length) {
+    EncodeNoRandom(value, prefix_length);
+    // Add some random bits to the prefix (the first byte) above the mask.
+    uint8_t prefix = buffer_[0];
+    buffer_[0] = prefix | (Random().Rand8() << prefix_length);
+    ASSERT_EQ(prefix, buffer_[0] & prefix_mask_);
+  }
+
+  // This is really a test of HpackBlockBuilder, making sure that the input to
+  // HpackVarintDecoder is as expected, which also acts as confirmation that
+  // my thinking about the encodings being used by the tests, i.e. cover the
+  // range desired.
+  void ValidateEncoding(uint32_t value,
+                        uint32_t minimum,
+                        uint32_t maximum,
+                        size_t expected_bytes) {
+    ASSERT_EQ(expected_bytes, buffer_.size());
+    if (expected_bytes > 1) {
+      EXPECT_EQ(prefix_mask_, buffer_[0] & prefix_mask_);
+      size_t last = expected_bytes - 1;
+      for (size_t ndx = 1; ndx < last; ++ndx) {
+        // Before the last extension byte, we expect the high-bit set.
+        uint8_t byte = buffer_[ndx];
+        if (value == minimum) {
+          EXPECT_EQ(0x80, byte) << "ndx=" << ndx;
+        } else if (value == maximum) {
+          EXPECT_EQ(0xff, byte) << "ndx=" << ndx;
+        } else {
+          EXPECT_EQ(0x80, byte & 0x80) << "ndx=" << ndx;
+        }
+      }
+      // The last extension byte should not have the high-bit set.
+      uint8_t byte = buffer_[last];
+      if (value == minimum) {
+        if (expected_bytes == 2) {
+          EXPECT_EQ(0x00, byte);
+        } else {
+          EXPECT_EQ(0x01, byte);
+        }
+      } else if (value == maximum) {
+        EXPECT_EQ(0x7f, byte);
+      } else {
+        EXPECT_EQ(0x00, byte & 0x80);
+      }
+    } else {
+      EXPECT_EQ(value, static_cast<uint32_t>(buffer_[0] & prefix_mask_));
+      EXPECT_LT(value, static_cast<uint32_t>(prefix_mask_));
+    }
+  }
+
+  void EncodeAndDecodeValues(const std::set<uint32_t>& values,
+                             uint8_t prefix_length,
+                             size_t expected_bytes) {
+    CHECK(!values.empty());
+    const uint32_t minimum = *values.begin();
+    const uint32_t maximum = *values.rbegin();
+    for (const uint32_t value : values) {
+      Encode(value, prefix_length);  // Sets prefix_mask_ and buffer_
+
+      std::stringstream ss;
+      ss << "value=" << value << " (0x" << std::hex << value
+         << "), prefix_length=" << std::dec << prefix_length
+         << ", expected_bytes=" << expected_bytes << std::endl
+         << HexEncode(buffer_);
+      string msg(ss.str());
+
+      if (value == minimum) {
+        LOG(INFO) << "Checking minimum; " << msg;
+      } else if (value == maximum) {
+        LOG(INFO) << "Checking maximum; " << msg;
+      }
+
+      SCOPED_TRACE(msg);
+      ValidateEncoding(value, minimum, maximum, expected_bytes);
+      DecodeSeveralWays(value, expected_bytes);
+
+      // Append some random data to the end of buffer_ and repeat. That random
+      // data should be ignored.
+      buffer_.append(Random().RandString(1 + Random().Uniform(10)));
+      DecodeSeveralWays(value, expected_bytes);
+
+      // If possible, add extension bytes that don't change the value.
+      if (1 < expected_bytes) {
+        buffer_.resize(expected_bytes);
+        for (uint8_t total_bytes = expected_bytes + 1; total_bytes <= 6;
+             ++total_bytes) {
+          // Mark the current last byte as not being the last one.
+          EXPECT_EQ(0x00, 0x80 & buffer_.back());
+          buffer_.back() |= 0x80;
+          buffer_.push_back('\0');
+          DecodeSeveralWays(value, total_bytes);
+        }
+      }
+    }
+  }
+
+  void EncodeAndDecodeValuesInRange(uint32_t start,
+                                    uint32_t range,
+                                    uint8_t prefix_length,
+                                    size_t expected_bytes) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t beyond = start + range;
+
+    LOG(INFO) << "############################################################";
+    LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
+    LOG(INFO) << "prefix_mask=" << std::hex << static_cast<int>(prefix_mask);
+    LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
+    LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
+    LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
+    LOG(INFO) << "expected_bytes=" << expected_bytes;
+
+    // Confirm the claim that beyond requires more bytes.
+    Encode(beyond, prefix_length);
+    EXPECT_EQ(expected_bytes + 1, buffer_.size()) << HexEncode(buffer_);
+
+    std::set<uint32_t> values;
+    if (range < 200) {
+      // Select all values in the range.
+      for (uint32_t offset = 0; offset < range; ++offset) {
+        values.insert(start + offset);
+      }
+    } else {
+      // Select some values in this range, including the minimum and maximum
+      // values that require exactly |expected_bytes| extension bytes.
+      values.insert({start, start + 1, beyond - 2, beyond - 1});
+      while (values.size() < 100) {
+        values.insert(start + Random().Rand32() % range);
+      }
+    }
+
+    EncodeAndDecodeValues(values, prefix_length, expected_bytes);
+  }
+
+  HpackVarintDecoder decoder_;
+  string buffer_;
+  uint8_t prefix_mask_ = 0;
+  uint8_t prefix_length_ = 0;
+};
+
+// To help me and future debuggers of varint encodings, this LOGs out the
+// transition points where a new extension byte is added.
+TEST_F(HpackVarintDecoderTest, Encode) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint32_t a = (1 << prefix_length) - 1;
+    const uint32_t b = a + 128;
+    const uint32_t c = b + (127 << 7);
+    const uint32_t d = c + (127 << 14);
+    const uint32_t e = d + (127 << 21);
+
+    LOG(INFO) << "############################################################";
+    LOG(INFO) << "prefix_length=" << prefix_length << "   a=" << a
+              << "   b=" << b << "   c=" << c;
+
+    EXPECT_EQ(a - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(0, prefix_length));
+    EXPECT_EQ(b - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(1, prefix_length));
+    EXPECT_EQ(c - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(2, prefix_length));
+    EXPECT_EQ(d - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(3, prefix_length));
+    EXPECT_EQ(e - 1,
+              HpackVarintDecoder::HiValueOfExtensionBytes(4, prefix_length));
+
+    std::vector<uint32_t> values = {
+        0,     1,                       // Force line break.
+        a - 2, a - 1, a, a + 1, a + 2,  // Force line break.
+        b - 2, b - 1, b, b + 1, b + 2,  // Force line break.
+        c - 2, c - 1, c, c + 1, c + 2,  // Force line break.
+        d - 2, d - 1, d, d + 1, d + 2,  // Force line break.
+        e - 2, e - 1, e, e + 1, e + 2   // Force line break.
+    };
+
+    for (uint32_t value : values) {
+      EncodeNoRandom(value, prefix_length);
+      string dump = HexEncode(buffer_);
+      LOG(INFO) << StringPrintf("%10u %0#10x ", value, value)
+                << HexEncode(buffer_);
+    }
+  }
+}
+
+TEST_F(HpackVarintDecoderTest, FromSpec1337) {
+  DecodeBuffer b(StringPiece("\x1f\x9a\x0a"));
+  uint32_t prefix_length = 5;
+  uint32_t prefix_mask = (1 << prefix_length) - 1;
+  uint8_t p = b.DecodeUInt8();
+  EXPECT_EQ(1u, b.Offset());
+  EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.Start(p, prefix_mask, &b));
+  EXPECT_EQ(3u, b.Offset());
+  EXPECT_EQ(1337u, decoder_.value());
+
+  EncodeNoRandom(1337, prefix_length);
+  EXPECT_EQ(3u, buffer_.size());
+  EXPECT_EQ('\x1f', buffer_[0]);
+  EXPECT_EQ('\x9a', buffer_[1]);
+  EXPECT_EQ('\x0a', buffer_[2]);
+}
+
+// Test all the values that fit into the prefix (one less than the mask).
+TEST_F(HpackVarintDecoderTest, ValidatePrefixOnly) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    EncodeAndDecodeValuesInRange(0, prefix_mask, prefix_length, 1);
+  }
+}
+
+// Test all values that require exactly 1 extension byte.
+TEST_F(HpackVarintDecoderTest, ValidateOneExtensionByte) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint32_t start = (1 << prefix_length) - 1;
+    EncodeAndDecodeValuesInRange(start, 128, prefix_length, 2);
+  }
+}
+
+// Test *some* values that require exactly 2 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateTwoExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128;
+    const uint32_t range = 127 << 7;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 3);
+  }
+}
+
+// Test *some* values that require 3 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateThreeExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128 + (127 << 7);
+    const uint32_t range = 127 << 14;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 4);
+  }
+}
+
+// Test *some* values that require 4 extension bytes.
+TEST_F(HpackVarintDecoderTest, ValidateFourExtensionBytes) {
+  for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
+    const uint8_t prefix_mask = (1 << prefix_length) - 1;
+    const uint32_t start = prefix_mask + 128 + (127 << 7) + (127 << 14);
+    const uint32_t range = 127 << 21;
+
+    EncodeAndDecodeValuesInRange(start, range, prefix_length, 5);
+  }
+}
+
+// Test *some* values that require too many extension bytes.
+TEST_F(HpackVarintDecoderTest, ValueTooLarge) {
+  const uint32_t expected_offset = HpackVarintDecoder::MaxExtensionBytes() + 1;
+  for (prefix_length_ = 4; prefix_length_ <= 7; ++prefix_length_) {
+    prefix_mask_ = (1 << prefix_length_) - 1;
+    uint64_t too_large = HpackVarintDecoder::HiValueOfExtensionBytes(
+        HpackVarintDecoder::MaxExtensionBytes() + 3, prefix_length_);
+    HpackBlockBuilder bb;
+    bb.AppendHighBitsAndVarint(0, prefix_length_, too_large);
+    buffer_ = bb.buffer();
+
+    // The validator is called after each of the several times that the input
+    // DecodeBuffer is decoded, each with a different segmentation of the input.
+    // Validate that decoder_.value() matches the expected value.
+    bool validated = false;
+    Validator validator =
+        base::Bind(&HpackVarintDecoderTest::ValidatorForValueTooLarge,
+                   base::Unretained(this), &validated, expected_offset);
+
+    // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+    // can call Start with the prefix byte.
+    bool return_non_zero_on_first = true;
+    DecodeBuffer b(buffer_);
+    EXPECT_TRUE(
+        DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+    EXPECT_EQ(expected_offset, b.Offset());
+    EXPECT_TRUE(validated);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/http2_hpack_constants.cc b/net/http2/hpack/http2_hpack_constants.cc
new file mode 100644
index 0000000..bc0ee9e
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+#include <sstream>
+
+namespace net {
+
+std::string HpackEntryTypeToString(HpackEntryType v) {
+  switch (v) {
+    case HpackEntryType::kIndexedHeader:
+      return "kIndexedHeader";
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      return "kDynamicTableSizeUpdate";
+    case HpackEntryType::kIndexedLiteralHeader:
+      return "kIndexedLiteralHeader";
+    case HpackEntryType::kUnindexedLiteralHeader:
+      return "kUnindexedLiteralHeader";
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      return "kNeverIndexedLiteralHeader";
+  }
+  std::stringstream ss;
+  ss << "UnknownHpackEntryType(" << static_cast<int>(v) << ")";
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, HpackEntryType v) {
+  return out << HpackEntryTypeToString(v);
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/http2_hpack_constants.h b/net/http2/hpack/http2_hpack_constants.h
new file mode 100644
index 0000000..b9883a0
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
+#define NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
+
+// Enum HpackEntryType identifies the 5 basic types of HPACK Block Entries.
+//
+// See the spec for details:
+// https://http2.github.io/http2-spec/compression.html#rfc.section.6
+
+#include <ostream>
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+enum class HpackEntryType {
+  // Entry is an index into the static or dynamic table. Decoding it has no
+  // effect on the dynamic table.
+  kIndexedHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is added to the dynamic table after decoding.
+  kIndexedLiteralHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is not added to the dynamic table after decoding, but a proxy
+  // may choose to insert the entry into its dynamic table when forwarding
+  // to another endpoint.
+  kUnindexedLiteralHeader,
+
+  // The entry contains a literal value. The name may be either a literal or a
+  // reference to an entry in the static or dynamic table.
+  // The entry is not added to the dynamic table after decoding, and a proxy
+  // must NOT insert the entry into its dynamic table when forwarding to another
+  // endpoint.
+  kNeverIndexedLiteralHeader,
+
+  // Entry conveys the size limit of the dynamic table of the encoder to
+  // the decoder. May be used to flush the table by sending a zero and then
+  // resetting the size back up to the maximum that the encoder will use
+  // (within the limits of SETTINGS_HEADER_TABLE_SIZE sent by the
+  // decoder to the encoder, with the default of 4096 assumed).
+  kDynamicTableSizeUpdate,
+};
+
+// Returns the name of the enum member.
+NET_EXPORT_PRIVATE std::string HpackEntryTypeToString(HpackEntryType v);
+
+// Inserts the name of the enum member into |out|.
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            HpackEntryType v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
diff --git a/net/http2/hpack/http2_hpack_constants_test.cc b/net/http2/hpack/http2_hpack_constants_test.cc
new file mode 100644
index 0000000..217b64d
--- /dev/null
+++ b/net/http2/hpack/http2_hpack_constants_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/http2_hpack_constants.h"
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+class HpackEntryTypeTest : public testing::Test {};
+
+TEST(HpackEntryTypeTest, HpackEntryTypeToString) {
+  EXPECT_EQ("kIndexedHeader",
+            HpackEntryTypeToString(HpackEntryType::kIndexedHeader));
+  EXPECT_EQ("kDynamicTableSizeUpdate",
+            HpackEntryTypeToString(HpackEntryType::kDynamicTableSizeUpdate));
+  EXPECT_EQ("kIndexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kIndexedLiteralHeader));
+  EXPECT_EQ("kUnindexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kUnindexedLiteralHeader));
+  EXPECT_EQ("kNeverIndexedLiteralHeader",
+            HpackEntryTypeToString(HpackEntryType::kNeverIndexedLiteralHeader));
+  EXPECT_EQ("UnknownHpackEntryType(12321)",
+            HpackEntryTypeToString(static_cast<HpackEntryType>(12321)));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc
new file mode 100644
index 0000000..45c32c08
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.cc
@@ -0,0 +1,542 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+#include <bitset>
+#include <limits>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+using std::string;
+
+// Terminology:
+//
+// Symbol - a plain text (unencoded) character (uint8), or the End-of-String
+//          (EOS) symbol, 256.
+//
+// Code - the sequence of bits used to encode a symbol, varying in length from
+//        5 bits for the most common symbols (e.g. '0', '1', and 'a'), to
+//        30 bits for the least common (e.g. the EOS symbol).
+//        For those symbols whose codes have the same length, their code values
+//        are sorted such that the lower symbol value has a lower code value.
+//
+// Canonical - a symbol's cardinal value when sorted first by code length, and
+//             then by symbol value. For example, canonical 0 is for ASCII '0'
+//             (uint8 value 0x30), which is the first of the symbols whose code
+//             is 5 bits long, and the last canonical is EOS, which is the last
+//             of the symbols whose code is 30 bits long.
+
+// TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature.
+
+namespace net {
+namespace {
+
+// HuffmanCode is used to store the codes associated with symbols (a pattern of
+// from 5 to 30 bits).
+typedef uint32_t HuffmanCode;
+
+// HuffmanCodeBitCount is used to store a count of bits in a code.
+typedef uint16_t HuffmanCodeBitCount;
+
+// HuffmanCodeBitSet is used for producing a string version of a code because
+// std::bitset logs nicely.
+typedef std::bitset<32> HuffmanCodeBitSet;
+typedef std::bitset<64> HuffmanAccumulatorBitSet;
+
+static constexpr HuffmanCodeBitCount kMinCodeBitCount = 5;
+static constexpr HuffmanCodeBitCount kMaxCodeBitCount = 30;
+static constexpr HuffmanCodeBitCount kHuffmanCodeBitCount =
+    std::numeric_limits<HuffmanCode>::digits;
+
+static_assert(std::numeric_limits<HuffmanCode>::digits >= kMaxCodeBitCount,
+              "HuffmanCode isn't big enough.");
+
+static_assert(std::numeric_limits<HuffmanAccumulator>::digits >=
+                  kMaxCodeBitCount,
+              "HuffmanAccumulator isn't big enough.");
+
+static constexpr HuffmanAccumulatorBitCount kHuffmanAccumulatorBitCount =
+    std::numeric_limits<HuffmanAccumulator>::digits;
+static constexpr HuffmanAccumulatorBitCount kExtraAccumulatorBitCount =
+    kHuffmanAccumulatorBitCount - kHuffmanCodeBitCount;
+
+// PrefixInfo holds info about a group of codes that are all of the same length.
+struct PrefixInfo {
+  // Given the leading bits (32 in this case) of the encoded string, and that
+  // they start with a code of length |code_length|, return the corresponding
+  // canonical for that leading code.
+  uint32_t DecodeToCanonical(HuffmanCode bits) const {
+    // What is the position of the canonical symbol being decoded within
+    // the canonical symbols of |length|?
+    HuffmanCode ordinal_in_length =
+        ((bits - first_code) >> (kHuffmanCodeBitCount - code_length));
+
+    // Combined with |canonical| to produce the position of the canonical symbol
+    // being decoded within all of the canonical symbols.
+    return first_canonical + ordinal_in_length;
+  }
+
+  const HuffmanCode first_code;  // First code of this length, left justified in
+                                 // the field (i.e. the first bit of the code is
+                                 // the high-order bit).
+  const uint16_t code_length;    // Length of the prefix code |base|.
+  const uint16_t first_canonical;  // First canonical symbol of this length.
+};
+
+inline std::ostream& operator<<(std::ostream& out, const PrefixInfo& v) {
+  return out << "{first_code: " << HuffmanCodeBitSet(v.first_code)
+             << ", code_length: " << v.code_length
+             << ", first_canonical: " << v.first_canonical << "}";
+}
+
+// Given |value|, a sequence of the leading bits remaining to be decoded,
+// figure out which group of canonicals (by code length) that value starts
+// with. This function was generated.
+PrefixInfo PrefixToInfo(HuffmanCode value) {
+  if (value < 0b10111000000000000000000000000000) {
+    if (value < 0b01010000000000000000000000000000) {
+      return {0b00000000000000000000000000000000, 5, 0};
+    } else {
+      return {0b01010000000000000000000000000000, 6, 10};
+    }
+  } else {
+    if (value < 0b11111110000000000000000000000000) {
+      if (value < 0b11111000000000000000000000000000) {
+        return {0b10111000000000000000000000000000, 7, 36};
+      } else {
+        return {0b11111000000000000000000000000000, 8, 68};
+      }
+    } else {
+      if (value < 0b11111111110000000000000000000000) {
+        if (value < 0b11111111101000000000000000000000) {
+          if (value < 0b11111111010000000000000000000000) {
+            return {0b11111110000000000000000000000000, 10, 74};
+          } else {
+            return {0b11111111010000000000000000000000, 11, 79};
+          }
+        } else {
+          return {0b11111111101000000000000000000000, 12, 82};
+        }
+      } else {
+        if (value < 0b11111111111111100000000000000000) {
+          if (value < 0b11111111111110000000000000000000) {
+            if (value < 0b11111111111100000000000000000000) {
+              return {0b11111111110000000000000000000000, 13, 84};
+            } else {
+              return {0b11111111111100000000000000000000, 14, 90};
+            }
+          } else {
+            return {0b11111111111110000000000000000000, 15, 92};
+          }
+        } else {
+          if (value < 0b11111111111111110100100000000000) {
+            if (value < 0b11111111111111101110000000000000) {
+              if (value < 0b11111111111111100110000000000000) {
+                return {0b11111111111111100000000000000000, 19, 95};
+              } else {
+                return {0b11111111111111100110000000000000, 20, 98};
+              }
+            } else {
+              return {0b11111111111111101110000000000000, 21, 106};
+            }
+          } else {
+            if (value < 0b11111111111111111110101000000000) {
+              if (value < 0b11111111111111111011000000000000) {
+                return {0b11111111111111110100100000000000, 22, 119};
+              } else {
+                return {0b11111111111111111011000000000000, 23, 145};
+              }
+            } else {
+              if (value < 0b11111111111111111111101111000000) {
+                if (value < 0b11111111111111111111100000000000) {
+                  if (value < 0b11111111111111111111011000000000) {
+                    return {0b11111111111111111110101000000000, 24, 174};
+                  } else {
+                    return {0b11111111111111111111011000000000, 25, 186};
+                  }
+                } else {
+                  return {0b11111111111111111111100000000000, 26, 190};
+                }
+              } else {
+                if (value < 0b11111111111111111111111111110000) {
+                  if (value < 0b11111111111111111111111000100000) {
+                    return {0b11111111111111111111101111000000, 27, 205};
+                  } else {
+                    return {0b11111111111111111111111000100000, 28, 224};
+                  }
+                } else {
+                  return {0b11111111111111111111111111110000, 30, 253};
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// Mapping from canonical symbol (0 to 255) to actual symbol.
+// clang-format off
+constexpr unsigned char kCanonicalToSymbol[] = {
+    '0',  '1',  '2',  'a',  'c',  'e',  'i',  'o',
+    's',  't',  0x20, '%',  '-',  '.',  '/',  '3',
+    '4',  '5',  '6',  '7',  '8',  '9',  '=',  'A',
+    '_',  'b',  'd',  'f',  'g',  'h',  'l',  'm',
+    'n',  'p',  'r',  'u',  ':',  'B',  'C',  'D',
+    'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',
+    'M',  'N',  'O',  'P',  'Q',  'R',  'S',  'T',
+    'U',  'V',  'W',  'Y',  'j',  'k',  'q',  'v',
+    'w',  'x',  'y',  'z',  '&',  '*',  ',',  ';',
+    'X',  'Z',  '!',  '\"', '(',  ')',  '?',  '\'',
+    '+',  '|',  '#',  '>',  0x00, '$',  '@',  '[',
+    ']',  '~',  '^',  '}',  '<',  '`',  '{',  '\\',
+    0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2,
+    0xe0, 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1,
+    0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, 0x81,
+    0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0,
+    0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, 0xb9,
+    0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8,
+    0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+    0x8f, 0x93, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9d,
+    0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, 0xb6,
+    0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef, 0x09, 0x8e,
+    0x90, 0x91, 0x94, 0x9f, 0xab, 0xce, 0xd7, 0xe1,
+    0xec, 0xed, 0xc7, 0xcf, 0xea, 0xeb, 0xc0, 0xc1,
+    0xc8, 0xc9, 0xca, 0xcd, 0xd2, 0xd5, 0xda, 0xdb,
+    0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc, 0xd3,
+    0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, 0xf5,
+    0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b,
+    0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+    0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+    0x1e, 0x1f, 0x7f, 0xdc, 0xf9, 0x0a, 0x0d, 0x16,
+};
+// clang-format on
+
+constexpr size_t kShortCodeTableSize = 124;
+struct ShortCodeInfo {
+  uint8_t symbol;
+  uint8_t length;
+} kShortCodeTable[kShortCodeTableSize] = {
+    {0x30, 5},  // Match: 0b0000000, Symbol: 0
+    {0x30, 5},  // Match: 0b0000001, Symbol: 0
+    {0x30, 5},  // Match: 0b0000010, Symbol: 0
+    {0x30, 5},  // Match: 0b0000011, Symbol: 0
+    {0x31, 5},  // Match: 0b0000100, Symbol: 1
+    {0x31, 5},  // Match: 0b0000101, Symbol: 1
+    {0x31, 5},  // Match: 0b0000110, Symbol: 1
+    {0x31, 5},  // Match: 0b0000111, Symbol: 1
+    {0x32, 5},  // Match: 0b0001000, Symbol: 2
+    {0x32, 5},  // Match: 0b0001001, Symbol: 2
+    {0x32, 5},  // Match: 0b0001010, Symbol: 2
+    {0x32, 5},  // Match: 0b0001011, Symbol: 2
+    {0x61, 5},  // Match: 0b0001100, Symbol: a
+    {0x61, 5},  // Match: 0b0001101, Symbol: a
+    {0x61, 5},  // Match: 0b0001110, Symbol: a
+    {0x61, 5},  // Match: 0b0001111, Symbol: a
+    {0x63, 5},  // Match: 0b0010000, Symbol: c
+    {0x63, 5},  // Match: 0b0010001, Symbol: c
+    {0x63, 5},  // Match: 0b0010010, Symbol: c
+    {0x63, 5},  // Match: 0b0010011, Symbol: c
+    {0x65, 5},  // Match: 0b0010100, Symbol: e
+    {0x65, 5},  // Match: 0b0010101, Symbol: e
+    {0x65, 5},  // Match: 0b0010110, Symbol: e
+    {0x65, 5},  // Match: 0b0010111, Symbol: e
+    {0x69, 5},  // Match: 0b0011000, Symbol: i
+    {0x69, 5},  // Match: 0b0011001, Symbol: i
+    {0x69, 5},  // Match: 0b0011010, Symbol: i
+    {0x69, 5},  // Match: 0b0011011, Symbol: i
+    {0x6f, 5},  // Match: 0b0011100, Symbol: o
+    {0x6f, 5},  // Match: 0b0011101, Symbol: o
+    {0x6f, 5},  // Match: 0b0011110, Symbol: o
+    {0x6f, 5},  // Match: 0b0011111, Symbol: o
+    {0x73, 5},  // Match: 0b0100000, Symbol: s
+    {0x73, 5},  // Match: 0b0100001, Symbol: s
+    {0x73, 5},  // Match: 0b0100010, Symbol: s
+    {0x73, 5},  // Match: 0b0100011, Symbol: s
+    {0x74, 5},  // Match: 0b0100100, Symbol: t
+    {0x74, 5},  // Match: 0b0100101, Symbol: t
+    {0x74, 5},  // Match: 0b0100110, Symbol: t
+    {0x74, 5},  // Match: 0b0100111, Symbol: t
+    {0x20, 6},  // Match: 0b0101000, Symbol: (space)
+    {0x20, 6},  // Match: 0b0101001, Symbol: (space)
+    {0x25, 6},  // Match: 0b0101010, Symbol: %
+    {0x25, 6},  // Match: 0b0101011, Symbol: %
+    {0x2d, 6},  // Match: 0b0101100, Symbol: -
+    {0x2d, 6},  // Match: 0b0101101, Symbol: -
+    {0x2e, 6},  // Match: 0b0101110, Symbol: .
+    {0x2e, 6},  // Match: 0b0101111, Symbol: .
+    {0x2f, 6},  // Match: 0b0110000, Symbol: /
+    {0x2f, 6},  // Match: 0b0110001, Symbol: /
+    {0x33, 6},  // Match: 0b0110010, Symbol: 3
+    {0x33, 6},  // Match: 0b0110011, Symbol: 3
+    {0x34, 6},  // Match: 0b0110100, Symbol: 4
+    {0x34, 6},  // Match: 0b0110101, Symbol: 4
+    {0x35, 6},  // Match: 0b0110110, Symbol: 5
+    {0x35, 6},  // Match: 0b0110111, Symbol: 5
+    {0x36, 6},  // Match: 0b0111000, Symbol: 6
+    {0x36, 6},  // Match: 0b0111001, Symbol: 6
+    {0x37, 6},  // Match: 0b0111010, Symbol: 7
+    {0x37, 6},  // Match: 0b0111011, Symbol: 7
+    {0x38, 6},  // Match: 0b0111100, Symbol: 8
+    {0x38, 6},  // Match: 0b0111101, Symbol: 8
+    {0x39, 6},  // Match: 0b0111110, Symbol: 9
+    {0x39, 6},  // Match: 0b0111111, Symbol: 9
+    {0x3d, 6},  // Match: 0b1000000, Symbol: =
+    {0x3d, 6},  // Match: 0b1000001, Symbol: =
+    {0x41, 6},  // Match: 0b1000010, Symbol: A
+    {0x41, 6},  // Match: 0b1000011, Symbol: A
+    {0x5f, 6},  // Match: 0b1000100, Symbol: _
+    {0x5f, 6},  // Match: 0b1000101, Symbol: _
+    {0x62, 6},  // Match: 0b1000110, Symbol: b
+    {0x62, 6},  // Match: 0b1000111, Symbol: b
+    {0x64, 6},  // Match: 0b1001000, Symbol: d
+    {0x64, 6},  // Match: 0b1001001, Symbol: d
+    {0x66, 6},  // Match: 0b1001010, Symbol: f
+    {0x66, 6},  // Match: 0b1001011, Symbol: f
+    {0x67, 6},  // Match: 0b1001100, Symbol: g
+    {0x67, 6},  // Match: 0b1001101, Symbol: g
+    {0x68, 6},  // Match: 0b1001110, Symbol: h
+    {0x68, 6},  // Match: 0b1001111, Symbol: h
+    {0x6c, 6},  // Match: 0b1010000, Symbol: l
+    {0x6c, 6},  // Match: 0b1010001, Symbol: l
+    {0x6d, 6},  // Match: 0b1010010, Symbol: m
+    {0x6d, 6},  // Match: 0b1010011, Symbol: m
+    {0x6e, 6},  // Match: 0b1010100, Symbol: n
+    {0x6e, 6},  // Match: 0b1010101, Symbol: n
+    {0x70, 6},  // Match: 0b1010110, Symbol: p
+    {0x70, 6},  // Match: 0b1010111, Symbol: p
+    {0x72, 6},  // Match: 0b1011000, Symbol: r
+    {0x72, 6},  // Match: 0b1011001, Symbol: r
+    {0x75, 6},  // Match: 0b1011010, Symbol: u
+    {0x75, 6},  // Match: 0b1011011, Symbol: u
+    {0x3a, 7},  // Match: 0b1011100, Symbol: :
+    {0x42, 7},  // Match: 0b1011101, Symbol: B
+    {0x43, 7},  // Match: 0b1011110, Symbol: C
+    {0x44, 7},  // Match: 0b1011111, Symbol: D
+    {0x45, 7},  // Match: 0b1100000, Symbol: E
+    {0x46, 7},  // Match: 0b1100001, Symbol: F
+    {0x47, 7},  // Match: 0b1100010, Symbol: G
+    {0x48, 7},  // Match: 0b1100011, Symbol: H
+    {0x49, 7},  // Match: 0b1100100, Symbol: I
+    {0x4a, 7},  // Match: 0b1100101, Symbol: J
+    {0x4b, 7},  // Match: 0b1100110, Symbol: K
+    {0x4c, 7},  // Match: 0b1100111, Symbol: L
+    {0x4d, 7},  // Match: 0b1101000, Symbol: M
+    {0x4e, 7},  // Match: 0b1101001, Symbol: N
+    {0x4f, 7},  // Match: 0b1101010, Symbol: O
+    {0x50, 7},  // Match: 0b1101011, Symbol: P
+    {0x51, 7},  // Match: 0b1101100, Symbol: Q
+    {0x52, 7},  // Match: 0b1101101, Symbol: R
+    {0x53, 7},  // Match: 0b1101110, Symbol: S
+    {0x54, 7},  // Match: 0b1101111, Symbol: T
+    {0x55, 7},  // Match: 0b1110000, Symbol: U
+    {0x56, 7},  // Match: 0b1110001, Symbol: V
+    {0x57, 7},  // Match: 0b1110010, Symbol: W
+    {0x59, 7},  // Match: 0b1110011, Symbol: Y
+    {0x6a, 7},  // Match: 0b1110100, Symbol: j
+    {0x6b, 7},  // Match: 0b1110101, Symbol: k
+    {0x71, 7},  // Match: 0b1110110, Symbol: q
+    {0x76, 7},  // Match: 0b1110111, Symbol: v
+    {0x77, 7},  // Match: 0b1111000, Symbol: w
+    {0x78, 7},  // Match: 0b1111001, Symbol: x
+    {0x79, 7},  // Match: 0b1111010, Symbol: y
+    {0x7a, 7},  // Match: 0b1111011, Symbol: z
+};
+
+}  // namespace
+
+HuffmanBitBuffer::HuffmanBitBuffer() {
+  Reset();
+}
+
+void HuffmanBitBuffer::Reset() {
+  accumulator_ = 0;
+  count_ = 0;
+}
+
+size_t HuffmanBitBuffer::AppendBytes(StringPiece input) {
+  HuffmanAccumulatorBitCount free_cnt = free_count();
+  size_t bytes_available = input.size();
+  if (free_cnt < 8 || bytes_available == 0) {
+    return 0;
+  }
+
+  // Top up |accumulator_| until there isn't room for a whole byte.
+  size_t bytes_used = 0;
+  auto ptr = reinterpret_cast<const uint8_t*>(input.data());
+  do {
+    auto b = static_cast<HuffmanAccumulator>(*ptr++);
+    free_cnt -= 8;
+    accumulator_ |= (b << free_cnt);
+    ++bytes_used;
+  } while (free_cnt >= 8 && bytes_used < bytes_available);
+  count_ += (bytes_used * 8);
+  return bytes_used;
+}
+
+HuffmanAccumulatorBitCount HuffmanBitBuffer::free_count() const {
+  return kHuffmanAccumulatorBitCount - count_;
+}
+
+void HuffmanBitBuffer::ConsumeBits(HuffmanAccumulatorBitCount code_length) {
+  DCHECK_LE(code_length, count_);
+  accumulator_ <<= code_length;
+  count_ -= code_length;
+}
+
+bool HuffmanBitBuffer::InputProperlyTerminated() const {
+  auto cnt = count();
+  if (cnt < 8) {
+    if (cnt == 0) {
+      return true;
+    }
+    HuffmanAccumulator expected = ~(~HuffmanAccumulator() >> cnt);
+    // We expect all the bits below the high order |cnt| bits of accumulator_
+    // to be cleared as we perform left shift operations while decoding.
+    DCHECK_EQ(accumulator_ & ~expected, 0u)
+        << "\n  expected: " << HuffmanAccumulatorBitSet(expected) << "\n  "
+        << *this;
+    return accumulator_ == expected;
+  }
+  return false;
+}
+
+string HuffmanBitBuffer::DebugString() const {
+  std::stringstream ss;
+  ss << "{accumulator: " << HuffmanAccumulatorBitSet(accumulator_)
+     << "; count: " << count_ << "}";
+  return ss.str();
+}
+
+HpackHuffmanDecoder::HpackHuffmanDecoder() {}
+
+HpackHuffmanDecoder::~HpackHuffmanDecoder() {}
+
+bool HpackHuffmanDecoder::Decode(StringPiece input, string* output) {
+  return DecodeShortCodesFirst(input, output);
+}
+
+// "Legacy" decoder, used until cl/129771019 submitted, which added
+// DecodeShortCodesFirst() as primary decoder method.
+// TODO(jamessynge): Remove this once satisfied that there is no going back.
+bool HpackHuffmanDecoder::DecodeWithIfTreeAndStruct(StringPiece input,
+                                                    string* output) {
+  DVLOG(1) << "HpackHuffmanDecoder::DecodeWithIfTreeAndStruct";
+
+  // Fill bit_buffer_ from input.
+  input.remove_prefix(bit_buffer_.AppendBytes(input));
+
+  while (true) {
+    DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+
+    HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
+    DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+
+    PrefixInfo prefix_info = PrefixToInfo(code_prefix);
+    DVLOG(3) << "prefix_info: " << prefix_info;
+    DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
+    DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
+
+    if (prefix_info.code_length <= bit_buffer_.count()) {
+      // We have enough bits for one code.
+      uint32_t canonical = prefix_info.DecodeToCanonical(code_prefix);
+      if (canonical < 256) {
+        // Valid code.
+        char c = kCanonicalToSymbol[canonical];
+        output->push_back(c);
+        bit_buffer_.ConsumeBits(prefix_info.code_length);
+        continue;
+      }
+      // Encoder is not supposed to explicity encode the EOS symbol.
+      DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+                  << prefix_info;
+      return false;
+    }
+    // bit_buffer_ doesn't have enough bits in it to decode the next symbol.
+    // Append to it as many bytes as are available AND fit.
+    size_t byte_count = bit_buffer_.AppendBytes(input);
+    if (byte_count == 0) {
+      DCHECK_EQ(input.size(), 0u);
+      return true;
+    }
+    input.remove_prefix(byte_count);
+  }
+}
+
+bool HpackHuffmanDecoder::DecodeShortCodesFirst(StringPiece input,
+                                                string* output) {
+  DVLOG(1) << "HpackHuffmanDecoder::DecodeShortCodesFirst";
+
+  // Fill bit_buffer_ from input.
+  input.remove_prefix(bit_buffer_.AppendBytes(input));
+
+  while (true) {
+    DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+    if (bit_buffer_.count() >= 7) {
+      // Get high 7 bits of the bit buffer, see if that contains a complete
+      // code of 5, 6 or 7 bits.
+      uint8_t short_code =
+          bit_buffer_.value() >> (kHuffmanAccumulatorBitCount - 7);
+      DCHECK_LT(short_code, 128);
+      if (short_code < kShortCodeTableSize) {
+        ShortCodeInfo info = kShortCodeTable[short_code];
+        bit_buffer_.ConsumeBits(info.length);
+        output->push_back(static_cast<char>(info.symbol));
+        continue;
+      }
+      // The code is more than 7 bits long. Use PrefixToInfo, etc. to decode
+      // longer codes.
+    } else {
+      // We may have (mostly) drained bit_buffer_. If we can top it up, try
+      // using the table decoder above.
+      size_t byte_count = bit_buffer_.AppendBytes(input);
+      if (byte_count > 0) {
+        input.remove_prefix(byte_count);
+        continue;
+      }
+    }
+
+    HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
+    DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+
+    PrefixInfo prefix_info = PrefixToInfo(code_prefix);
+    DVLOG(3) << "prefix_info: " << prefix_info;
+    DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
+    DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
+
+    if (prefix_info.code_length <= bit_buffer_.count()) {
+      // We have enough bits for one code.
+      uint32_t canonical = prefix_info.DecodeToCanonical(code_prefix);
+      if (canonical < 256) {
+        // Valid code.
+        char c = kCanonicalToSymbol[canonical];
+        output->push_back(c);
+        bit_buffer_.ConsumeBits(prefix_info.code_length);
+        continue;
+      }
+      // Encoder is not supposed to explicity encode the EOS symbol.
+      DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+                  << prefix_info;
+      return false;
+    }
+    // bit_buffer_ doesn't have enough bits in it to decode the next symbol.
+    // Append to it as many bytes as are available AND fit.
+    size_t byte_count = bit_buffer_.AppendBytes(input);
+    if (byte_count == 0) {
+      DCHECK_EQ(input.size(), 0u);
+      return true;
+    }
+    input.remove_prefix(byte_count);
+  }
+}
+
+string HpackHuffmanDecoder::DebugString() const {
+  return bit_buffer_.DebugString();
+}
+
+}  // namespace net
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h
new file mode 100644
index 0000000..7b8edd10
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder.h
@@ -0,0 +1,149 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
+#define NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
+
+// HpackHuffmanDecoder is an incremental decoder of strings that have been
+// encoded using the Huffman table defined in the HPACK spec.
+// By incremental, we mean that the HpackHuffmanDecoder::Decode method does
+// not require the entire string to be provided, and can instead decode the
+// string as fragments of it become available (e.g. as HPACK block fragments
+// are received for decoding by HpackEntryDecoder).
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// HuffmanAccumulator is used to store bits during decoding, e.g. next N bits
+// that have not yet been decoded, but have been extracted from the encoded
+// string).  An advantage of using a uint64 for the accumulator
+// is that it has room for the bits of the longest code plus the bits of a full
+// byte; that means that when adding more bits to the accumulator, it can always
+// be done in whole bytes. For example, if we currently have 26 bits in the
+// accumulator, and need more to decode the current symbol, we can add a whole
+// byte to the accumulator, and not have to do juggling with adding 6 bits (to
+// reach 30), and then keep track of the last two bits we've not been able to
+// add to the accumulator.
+typedef uint64_t HuffmanAccumulator;
+typedef size_t HuffmanAccumulatorBitCount;
+
+// HuffmanBitBuffer stores the leading edge of bits to be decoded. The high
+// order bit of accumulator_ is the next bit to be decoded.
+class NET_EXPORT_PRIVATE HuffmanBitBuffer {
+ public:
+  HuffmanBitBuffer();
+
+  // Prepare for decoding a new Huffman encoded string.
+  void Reset();
+
+  // Add as many whole bytes to the accumulator (accumulator_) as possible,
+  // returning the number of bytes added.
+  size_t AppendBytes(base::StringPiece input);
+
+  // Get the bits of the accumulator.
+  HuffmanAccumulator value() const { return accumulator_; }
+
+  // Number of bits of the encoded string that are in the accumulator
+  // (accumulator_).
+  HuffmanAccumulatorBitCount count() const { return count_; }
+
+  // Are there no bits in the accumulator?
+  bool IsEmpty() const { return count_ == 0; }
+
+  // Number of additional bits that can be added to the accumulator.
+  HuffmanAccumulatorBitCount free_count() const;
+
+  // Consume the leading |code_length| bits of the accumulator.
+  void ConsumeBits(HuffmanAccumulatorBitCount code_length);
+
+  // Are the contents valid for the end of a Huffman encoded string? The RFC
+  // states that EOS (end-of-string) symbol must not be explicitly encoded in
+  // the bit stream, but any unused bits in the final byte must be set to the
+  // prefix of the EOS symbol, which is all 1 bits. So there can be at most 7
+  // such bits.
+  // Returns true if the bit buffer is empty, or contains at most 7 bits, all
+  // of them 1. Otherwise returns false.
+  bool InputProperlyTerminated() const;
+
+  std::string DebugString() const;
+
+ private:
+  HuffmanAccumulator accumulator_;
+  HuffmanAccumulatorBitCount count_;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const HuffmanBitBuffer& v) {
+  return out << v.DebugString();
+}
+
+class NET_EXPORT_PRIVATE HpackHuffmanDecoder {
+ public:
+  HpackHuffmanDecoder();
+  ~HpackHuffmanDecoder();
+
+  // Prepare for decoding a new Huffman encoded string.
+  void Reset() { bit_buffer_.Reset(); }
+
+  // Decode the portion of a HPACK Huffman encoded string that is in |input|,
+  // appending the decoded symbols into |*output|, stopping when more bits are
+  // needed to determine the next symbol, which/ means that the input has been
+  // drained, and also that the bit_buffer_ is empty or that the bits that are
+  // in it are not a whole symbol.
+  // If |input| is the start of a string, the caller must first call Reset.
+  // If |input| includes the end of the encoded string, the caller must call
+  // InputProperlyTerminated after Decode has returned true in order to
+  // determine if the encoded string was properly terminated.
+  // Returns false if something went wrong (e.g. the encoding contains the code
+  // EOS symbol). Otherwise returns true, in which case input has been fully
+  // decoded or buffered; in particular, if the low-order bit of the final byte
+  // of the input is not the last bit of an encoded symbol, then bit_buffer_
+  // will contain the leading bits of the code for that symbol, but not the
+  // final bits of that code.
+  // Note that output should be empty, but that it is not cleared by Decode().
+  bool Decode(base::StringPiece input, std::string* output);
+
+  // Is what remains in the bit_buffer_ valid at the end of an encoded string?
+  // Call after passing the the final portion of a Huffman string to Decode,
+  // and getting true as the result.
+  bool InputProperlyTerminated() const {
+    return bit_buffer_.InputProperlyTerminated();
+  }
+
+  bool IsEmptyForTest() const { return bit_buffer_.IsEmpty(); }
+
+  std::string DebugString() const;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Alternate implementations of Decode:
+
+  // As above, implemented using a tree of if statements to determine the code
+  // length, etc., which are returned as a tree. See PrefixToInfo. This is the
+  // original implementation, current as of 2016-8-8.
+  bool DecodeWithIfTreeAndStruct(base::StringPiece input, std::string* output);
+
+  // Based on DecodeWithIfTreeAndStruct, but adds an optimization for the common
+  // case of short codes (5, 6 or 7), which make up a large fraction of the
+  // frequency distribution on which the HPACK table was based.
+  // TODO(jamessynge): Be precise about that fraction.
+  bool DecodeShortCodesFirst(base::StringPiece input, std::string* output);
+
+ private:
+  HuffmanBitBuffer bit_buffer_;
+};
+
+inline std::ostream& operator<<(std::ostream& out,
+                                const HpackHuffmanDecoder& v) {
+  return out << v.DebugString();
+}
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_HUFFMAN_HTTP2_HPACK_HUFFMAN_DECODER_H_
diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
new file mode 100644
index 0000000..3ad47351
--- /dev/null
+++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
@@ -0,0 +1,292 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+
+// Tests of HpackHuffmanDecoder and HuffmanBitBuffer.
+
+#include <iostream>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/random_decoder_test.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(HuffmanBitBufferTest, Reset) {
+  HuffmanBitBuffer bb;
+  EXPECT_TRUE(bb.IsEmpty());
+  EXPECT_TRUE(bb.InputProperlyTerminated());
+  EXPECT_EQ(bb.count(), 0u);
+  EXPECT_EQ(bb.free_count(), 64u);
+  EXPECT_EQ(bb.value(), 0u);
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesAligned) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+  EXPECT_FALSE(bb.IsEmpty()) << bb;
+  EXPECT_FALSE(bb.InputProperlyTerminated());
+  EXPECT_EQ(bb.count(), 24u) << bb;
+  EXPECT_EQ(bb.free_count(), 40u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb;
+
+  s.clear();
+  s.push_back('\x44');
+  sp = s;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+  EXPECT_EQ(bb.count(), 32u) << bb;
+  EXPECT_EQ(bb.free_count(), 32u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb;
+
+  s.clear();
+  s.push_back('\x55');
+  s.push_back('\x66');
+  s.push_back('\x77');
+  s.push_back('\x88');
+  s.push_back('\x99');
+  sp = s;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 1u);
+  EXPECT_EQ('\x99', sp[0]);
+  EXPECT_EQ(bb.count(), 64u) << bb;
+  EXPECT_EQ(bb.free_count(), 0u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 1u);
+  EXPECT_EQ('\x99', sp[0]);
+  EXPECT_EQ(bb.count(), 64u) << bb;
+  EXPECT_EQ(bb.free_count(), 0u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+}
+
+TEST(HuffmanBitBufferTest, ConsumeBits) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_TRUE(sp.empty());
+
+  bb.ConsumeBits(1);
+  EXPECT_EQ(bb.count(), 23u) << bb;
+  EXPECT_EQ(bb.free_count(), 41u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb;
+
+  bb.ConsumeBits(20);
+  EXPECT_EQ(bb.count(), 3u) << bb;
+  EXPECT_EQ(bb.free_count(), 61u) << bb;
+  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb;
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesUnaligned) {
+  string s;
+  s.push_back('\x11');
+  s.push_back('\x22');
+  s.push_back('\x33');
+  s.push_back('\x44');
+  s.push_back('\x55');
+  s.push_back('\x66');
+  s.push_back('\x77');
+  s.push_back('\x88');
+  s.push_back('\x99');
+  s.push_back('\xaa');
+  s.push_back('\xbb');
+  s.push_back('\xcc');
+  s.push_back('\xdd');
+  StringPiece sp(s);
+
+  HuffmanBitBuffer bb;
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 5u);
+  EXPECT_FALSE(bb.InputProperlyTerminated());
+
+  bb.ConsumeBits(15);
+  EXPECT_EQ(bb.count(), 49u) << bb;
+  EXPECT_EQ(bb.free_count(), 15u) << bb;
+
+  HuffmanAccumulator expected(0x1122334455667788);
+  expected <<= 15;
+  EXPECT_EQ(bb.value(), expected);
+
+  sp.remove_prefix(bb.AppendBytes(sp));
+  EXPECT_EQ(sp.size(), 4u);
+  EXPECT_EQ(bb.count(), 57u) << bb;
+  EXPECT_EQ(bb.free_count(), 7u) << bb;
+
+  expected |= (HuffmanAccumulator(0x99) << 7);
+  EXPECT_EQ(bb.value(), expected) << bb << std::hex
+                                  << "\n   actual: " << bb.value()
+                                  << "\n expected: " << expected;
+}
+
+enum class DecoderChoice { IF_TREE, SHORT_CODE };
+
+class HpackHuffmanDecoderTest
+    : public RandomDecoderTest,
+      public ::testing::WithParamInterface<DecoderChoice> {
+ protected:
+  HpackHuffmanDecoderTest() {
+    // The decoder may return true, and its accumulator may be empty, at
+    // many boundaries while decoding, and yet the whole string hasn't
+    // been decoded.
+    stop_decode_on_done_ = false;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ = 0;
+    output_buffer_.clear();
+    decoder_.Reset();
+    return ResumeDecoding(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ += b->Remaining();
+    StringPiece sp(b->cursor(), b->Remaining());
+    if (DecodeFragment(sp)) {
+      b->AdvanceCursor(b->Remaining());
+      // Successfully decoded (or buffered) the bytes in StringPiece.
+      EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
+      // Have we reached the end of the encoded string?
+      if (input_bytes_expected_ == input_bytes_seen_) {
+        if (decoder_.InputProperlyTerminated()) {
+          return DecodeStatus::kDecodeDone;
+        } else {
+          return DecodeStatus::kDecodeError;
+        }
+      }
+      return DecodeStatus::kDecodeInProgress;
+    }
+    return DecodeStatus::kDecodeError;
+  }
+
+  bool DecodeFragment(StringPiece sp) {
+    switch (GetParam()) {
+      case DecoderChoice::IF_TREE:
+        return decoder_.DecodeWithIfTreeAndStruct(sp, &output_buffer_);
+      case DecoderChoice::SHORT_CODE:
+        return decoder_.DecodeShortCodesFirst(sp, &output_buffer_);
+    }
+
+    NOTREACHED();
+    return false;
+  }
+
+  AssertionResult ValidatorForHuffmanDecodeAndValidateSeveralWays(
+      StringPiece expected_plain) {
+    VERIFY_EQ(output_buffer_.size(), expected_plain.size());
+    VERIFY_EQ(output_buffer_, expected_plain);
+    return AssertionSuccess();
+  }
+
+  AssertionResult HuffmanDecodeAndValidateSeveralWays(
+      StringPiece encoded,
+      StringPiece expected_plain) {
+    input_bytes_expected_ = encoded.size();
+    DecodeBuffer db(encoded);
+    bool return_non_zero_on_first = false;
+    return DecodeAndValidateSeveralWays(
+        &db, return_non_zero_on_first,
+        ValidateDoneAndEmpty(
+            base::Bind(&HpackHuffmanDecoderTest::
+                           ValidatorForHuffmanDecodeAndValidateSeveralWays,
+                       base::Unretained(this), expected_plain)));
+  }
+
+  HpackHuffmanDecoder decoder_;
+  string output_buffer_;
+  size_t input_bytes_seen_;
+  size_t input_bytes_expected_;
+};
+INSTANTIATE_TEST_CASE_P(AllDecoders,
+                        HpackHuffmanDecoderTest,
+                        ::testing::Values(DecoderChoice::IF_TREE,
+                                          DecoderChoice::SHORT_CODE));
+
+TEST_P(HpackHuffmanDecoderTest, SpecRequestExamples) {
+  HpackHuffmanDecoder decoder;
+  string test_table[] = {
+      a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
+      "www.example.com",
+      a2b_hex("a8eb10649cbf"),
+      "no-cache",
+      a2b_hex("25a849e95ba97d7f"),
+      "custom-key",
+      a2b_hex("25a849e95bb8e8b4bf"),
+      "custom-value",
+  };
+  for (size_t i = 0; i != arraysize(test_table); i += 2) {
+    const string& huffman_encoded(test_table[i]);
+    const string& plain_string(test_table[i + 1]);
+    string buffer;
+    decoder.Reset();
+    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+    EXPECT_EQ(buffer, plain_string);
+  }
+}
+
+TEST_P(HpackHuffmanDecoderTest, SpecResponseExamples) {
+  HpackHuffmanDecoder decoder;
+  // clang-format off
+  string test_table[] = {
+    a2b_hex("6402"),
+    "302",
+    a2b_hex("aec3771a4b"),
+    "private",
+    a2b_hex("d07abe941054d444a8200595040b8166"
+            "e082a62d1bff"),
+    "Mon, 21 Oct 2013 20:13:21 GMT",
+    a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43"
+            "d3"),
+    "https://www.example.com",
+    a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
+            "d5af27087f3672c1ab270fb5291f9587"
+            "316065c003ed4ee5b1063d5007"),
+    "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+  };
+  // clang-format on
+  for (size_t i = 0; i != arraysize(test_table); i += 2) {
+    const string& huffman_encoded(test_table[i]);
+    const string& plain_string(test_table[i + 1]);
+    string buffer;
+    decoder.Reset();
+    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+    EXPECT_EQ(buffer, plain_string);
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_block_builder.cc b/net/http2/hpack/tools/hpack_block_builder.cc
new file mode 100644
index 0000000..49eba0d
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder.cc
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+void HpackBlockBuilder::AppendHighBitsAndVarint(uint8_t high_bits,
+                                                uint8_t prefix_length,
+                                                uint64_t varint) {
+  EXPECT_LE(4, prefix_length);
+  EXPECT_LE(prefix_length, 7);
+
+  // prefix_mask defines the sequence of low-order bits of the first byte
+  // that encode the prefix of the value. It is also the marker in those bits
+  // of the first byte indicating that at least one extension byte is needed.
+  uint8_t prefix_mask = (1 << prefix_length) - 1;
+  EXPECT_EQ(0, high_bits & prefix_mask);
+
+  if (varint < prefix_mask) {
+    uint8_t b = high_bits | static_cast<uint8_t>(varint);
+    buffer_.push_back(static_cast<char>(b));
+    return;
+  }
+
+  // We need extension bytes.
+  buffer_.push_back(static_cast<char>(high_bits | prefix_mask));
+  varint -= prefix_mask;
+  while (varint >= 128) {
+    uint8_t b = static_cast<uint8_t>((varint % 128) + 128);
+    buffer_.push_back(static_cast<char>(b));
+    varint = varint / 128;
+  }
+  char c = static_cast<char>(varint);
+  buffer_.push_back(c);
+}
+
+void HpackBlockBuilder::AppendEntryTypeAndVarint(HpackEntryType entry_type,
+                                                 uint64_t varint) {
+  uint8_t high_bits;
+  uint8_t prefix_length;  // Bits of the varint prefix in the first byte.
+  switch (entry_type) {
+    case HpackEntryType::kIndexedHeader:
+      high_bits = 0x80;
+      prefix_length = 7;
+      break;
+    case HpackEntryType::kDynamicTableSizeUpdate:
+      high_bits = 0x20;
+      prefix_length = 5;
+      break;
+    case HpackEntryType::kIndexedLiteralHeader:
+      high_bits = 0x40;
+      prefix_length = 6;
+      break;
+    case HpackEntryType::kUnindexedLiteralHeader:
+      high_bits = 0x00;
+      prefix_length = 4;
+      break;
+    case HpackEntryType::kNeverIndexedLiteralHeader:
+      high_bits = 0x10;
+      prefix_length = 4;
+      break;
+    default:
+      NOTREACHED();
+      high_bits = 0;
+      prefix_length = 0;
+  }
+  AppendHighBitsAndVarint(high_bits, prefix_length, varint);
+}
+
+void HpackBlockBuilder::AppendString(bool is_huffman_encoded,
+                                     base::StringPiece str) {
+  uint8_t high_bits = is_huffman_encoded ? 0x80 : 0;
+  uint8_t prefix_length = 7;
+  AppendHighBitsAndVarint(high_bits, prefix_length, str.size());
+  str.AppendToString(&buffer_);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_block_builder.h b/net/http2/hpack/tools/hpack_block_builder.h
new file mode 100644
index 0000000..06502b2a
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+#define NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+
+// HpackBlockBuilder builds wire-format HPACK blocks (or fragments thereof)
+// from components.
+
+// Supports very large varints to enable tests to create HPACK blocks with
+// values that the decoder should reject. For now, this is only intended for
+// use in tests, and thus has EXPECT* in the code. If desired to use it in an
+// encoder, it will need optimization work, especially w.r.t memory mgmt, and
+// the EXPECT* will need to be removed or replaced with DCHECKs. And of course
+// the support for very large varints will not be needed in production code.
+
+#include <stddef.h>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class HpackBlockBuilder {
+ public:
+  explicit HpackBlockBuilder(base::StringPiece initial_contents) {
+    initial_contents.AppendToString(&buffer_);
+  }
+  HpackBlockBuilder() {}
+  ~HpackBlockBuilder() {}
+
+  size_t size() const { return buffer_.size(); }
+  const std::string& buffer() const { return buffer_; }
+
+  //----------------------------------------------------------------------------
+  // Methods for appending a valid HPACK entry.
+
+  void AppendIndexedHeader(uint64_t index) {
+    AppendEntryTypeAndVarint(HpackEntryType::kIndexedHeader, index);
+  }
+
+  void AppendDynamicTableSizeUpdate(uint64_t size) {
+    AppendEntryTypeAndVarint(HpackEntryType::kDynamicTableSizeUpdate, size);
+  }
+
+  void AppendNameIndexAndLiteralValue(HpackEntryType entry_type,
+                                      uint64_t name_index,
+                                      bool value_is_huffman_encoded,
+                                      base::StringPiece value) {
+    // name_index==0 would indicate that the entry includes a literal name.
+    // Call AppendLiteralNameAndValue in that case.
+    EXPECT_NE(0u, name_index);
+    AppendEntryTypeAndVarint(entry_type, name_index);
+    AppendString(value_is_huffman_encoded, value);
+  }
+
+  void AppendLiteralNameAndValue(HpackEntryType entry_type,
+                                 bool name_is_huffman_encoded,
+                                 base::StringPiece name,
+                                 bool value_is_huffman_encoded,
+                                 base::StringPiece value) {
+    AppendEntryTypeAndVarint(entry_type, 0);
+    AppendString(name_is_huffman_encoded, name);
+    AppendString(value_is_huffman_encoded, value);
+  }
+
+  //----------------------------------------------------------------------------
+  // Primitive methods that are not guaranteed to write a valid HPACK entry.
+
+  // Appends a varint, with the specified high_bits above the prefix of the
+  // varint.
+  void AppendHighBitsAndVarint(uint8_t high_bits,
+                               uint8_t prefix_length,
+                               uint64_t varint);
+
+  // Append the start of an HPACK entry for the specified type, with the
+  // specified varint.
+  void AppendEntryTypeAndVarint(HpackEntryType entry_type, uint64_t varint);
+
+  // Append a header string (i.e. a header name or value) in HPACK format.
+  // Does NOT perform Huffman encoding.
+  void AppendString(bool is_huffman_encoded, base::StringPiece str);
+
+ private:
+  std::string buffer_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
diff --git a/net/http2/hpack/tools/hpack_block_builder_test.cc b/net/http2/hpack/tools/hpack_block_builder_test.cc
new file mode 100644
index 0000000..e591049
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_block_builder_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+namespace {
+const bool kUncompressed = false;
+const bool kCompressed = true;
+
+// TODO(jamessynge): Once static table code is checked in, switch to using
+// constants from there.
+const uint32_t kStaticTableMethodGET = 2;
+const uint32_t kStaticTablePathSlash = 4;
+const uint32_t kStaticTableSchemeHttp = 6;
+
+// Tests of encoding per the RFC. See:
+//   http://httpwg.org/specs/rfc7541.html#header.field.representation.examples
+// The expected values have been copied from the RFC.
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC2) {
+  {
+    HpackBlockBuilder b;
+    b.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+                                kUncompressed, "custom-key", kUncompressed,
+                                "custom-header");
+    EXPECT_EQ(26u, b.size());
+
+    const char kExpected[] =
+        "\x40"            // == Literal indexed ==
+        "\x0a"            // Name length (10)
+        "custom-key"      // Name
+        "\x0d"            // Value length (13)
+        "custom-header";  // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendNameIndexAndLiteralValue(HpackEntryType::kUnindexedLiteralHeader, 4,
+                                     kUncompressed, "/sample/path");
+    EXPECT_EQ(14u, b.size());
+
+    const char kExpected[] =
+        "\x04"           // == Literal unindexed, name index 0x04 ==
+        "\x0c"           // Value length (12)
+        "/sample/path";  // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                kUncompressed, "password", kUncompressed,
+                                "secret");
+    EXPECT_EQ(17u, b.size());
+
+    const char kExpected[] =
+        "\x10"      // == Literal never indexed ==
+        "\x08"      // Name length (8)
+        "password"  // Name
+        "\x06"      // Value length (6)
+        "secret";   // Value
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(2);
+    EXPECT_EQ(1u, b.size());
+
+    const char kExpected[] = "\x82";  // == Indexed (2) ==
+    EXPECT_EQ(kExpected, b.buffer());
+  }
+}
+
+// Tests of encoding per the RFC. See:
+//  http://httpwg.org/specs/rfc7541.html#request.examples.without.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC3) {
+  {
+    // Header block to encode:
+    //   :method: GET
+    //   :scheme: http
+    //   :path: /
+    //   :authority: www.example.com
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(2);  // :method: GET
+    b.AppendIndexedHeader(6);  // :scheme: http
+    b.AppendIndexedHeader(4);  // :path: /
+    b.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 1,
+                                     kUncompressed, "www.example.com");
+    EXPECT_EQ(20u, b.size());
+
+    // Hex dump of encoded data (copied from RFC):
+    // 0x0000:  8286 8441 0f77 7777 2e65 7861 6d70 6c65  ...A.www.example
+    // 0x0010:  2e63 6f6d                                .com
+
+    const std::string expected =
+        a2b_hex("828684410f7777772e6578616d706c652e636f6d");
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+// Tests of encoding per the RFC. See:
+//   http://httpwg.org/specs/rfc7541.html#request.examples.with.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC4) {
+  {
+    // Header block to encode:
+    //   :method: GET
+    //   :scheme: http
+    //   :path: /
+    //   :authority: www.example.com  (Huffman encoded)
+    HpackBlockBuilder b;
+    b.AppendIndexedHeader(kStaticTableMethodGET);
+    b.AppendIndexedHeader(kStaticTableSchemeHttp);
+    b.AppendIndexedHeader(kStaticTablePathSlash);
+    const char kHuffmanWwwExampleCom[] = {0xf1u, 0xe3u, 0xc2u, 0xe5u,
+                                          0xf2u, 0x3au, 0x6bu, 0xa0u,
+                                          0xabu, 0x90u, 0xf4u, 0xffu};
+    b.AppendNameIndexAndLiteralValue(
+        HpackEntryType::kIndexedLiteralHeader, 1, kCompressed,
+        StringPiece(kHuffmanWwwExampleCom, sizeof kHuffmanWwwExampleCom));
+    EXPECT_EQ(17u, b.size());
+
+    // Hex dump of encoded data (copied from RFC):
+    // 0x0000:  8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4  ...A......:k....
+    // 0x0010:  ff                                       .
+
+    const std::string expected = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+TEST(HpackBlockBuilderTest, DynamicTableSizeUpdate) {
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(0);
+    EXPECT_EQ(1u, b.size());
+
+    const char kData[] = {0x20};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(4096);  // The default size.
+    EXPECT_EQ(3u, b.size());
+
+    const char kData[] = {0x3f, 0xe1u, 0x1f};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+  {
+    HpackBlockBuilder b;
+    b.AppendDynamicTableSizeUpdate(1000000000000);  // A very large value.
+    EXPECT_EQ(7u, b.size());
+
+    const char kData[] = {0x3fu, 0xe1u, 0x9fu, 0x94u, 0xa5u, 0x8du, 0x1du};
+    StringPiece expected(kData, sizeof kData);
+    EXPECT_EQ(expected, b.buffer());
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_example.cc b/net/http2/hpack/tools/hpack_example.cc
new file mode 100644
index 0000000..72fd581
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_example.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/hpack/tools/hpack_example.h"
+
+#include <ctype.h>
+
+#include "base/logging.h"
+#include "net/spdy/spdy_test_utils.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+void HpackExampleToStringOrDie(StringPiece example, string* output) {
+  while (!example.empty()) {
+    const char c0 = example[0];
+    if (isxdigit(c0)) {
+      CHECK_GT(example.size(), 1u) << "Truncated hex byte?";
+      const char c1 = example[1];
+      CHECK(isxdigit(c1)) << "Found half a byte?";
+      *output += a2b_hex(example.substr(0, 2).as_string().c_str());
+      example.remove_prefix(2);
+      continue;
+    }
+    if (isspace(c0)) {
+      example.remove_prefix(1);
+      continue;
+    }
+    if (example.starts_with("|")) {
+      // Start of a comment. Skip to end of line or of input.
+      auto pos = example.find('\n');
+      if (pos == StringPiece::npos) {
+        // End of input.
+        break;
+      }
+      example.remove_prefix(pos + 1);
+      continue;
+    }
+    CHECK(false) << "Can't parse byte " << static_cast<int>(c0) << " (0x"
+                 << std::hex << c0 << ")"
+                 << "\nExample: " << example;
+  }
+  CHECK_LT(0u, output->size()) << "Example is empty.";
+  return;
+}
+
+}  // namespace
+
+string HpackExampleToStringOrDie(StringPiece example) {
+  string output;
+  HpackExampleToStringOrDie(example, &output);
+  return output;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/hpack/tools/hpack_example.h b/net/http2/hpack/tools/hpack_example.h
new file mode 100644
index 0000000..1567cbf
--- /dev/null
+++ b/net/http2/hpack/tools/hpack_example.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+#define NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+
+// Parses HPACK examples in the format seen in the HPACK specification,
+// RFC 7541. For example:
+//
+//       10                                      | == Literal never indexed ==
+//       08                                      |   Literal name (len = 8)
+//       7061 7373 776f 7264                     | password
+//       06                                      |   Literal value (len = 6)
+//       7365 6372 6574                          | secret
+//                                               | -> password: secret
+//
+// (excluding the leading "//").
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+
+namespace net {
+namespace test {
+
+std::string HpackExampleToStringOrDie(base::StringPiece example);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
diff --git a/net/http2/http2_constants.cc b/net/http2/http2_constants.cc
new file mode 100644
index 0000000..ca28f15e
--- /dev/null
+++ b/net/http2/http2_constants.cc
@@ -0,0 +1,161 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants.h"
+
+#include <ios>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+
+using base::StringPrintf;
+using std::string;
+
+namespace net {
+
+string Http2FrameTypeToString(Http2FrameType v) {
+  switch (v) {
+    case Http2FrameType::DATA:
+      return "DATA";
+    case Http2FrameType::HEADERS:
+      return "HEADERS";
+    case Http2FrameType::PRIORITY:
+      return "PRIORITY";
+    case Http2FrameType::RST_STREAM:
+      return "RST_STREAM";
+    case Http2FrameType::SETTINGS:
+      return "SETTINGS";
+    case Http2FrameType::PUSH_PROMISE:
+      return "PUSH_PROMISE";
+    case Http2FrameType::PING:
+      return "PING";
+    case Http2FrameType::GOAWAY:
+      return "GOAWAY";
+    case Http2FrameType::WINDOW_UPDATE:
+      return "WINDOW_UPDATE";
+    case Http2FrameType::CONTINUATION:
+      return "CONTINUATION";
+    case Http2FrameType::ALTSVC:
+      return "ALTSVC";
+  }
+  std::stringstream ss;
+  ss << "UnknownFrameType(" << static_cast<int>(v) << ")";
+  return ss.str();
+}
+string Http2FrameTypeToString(uint8_t v) {
+  return Http2FrameTypeToString(static_cast<Http2FrameType>(v));
+}
+
+string Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) {
+  string s;
+  // Closure to append flag name |v| to the string |s|, and to clear
+  // |bit| from |flags|.
+  auto append_and_clear = [&s, &flags](base::StringPiece v, uint8_t bit) {
+    if (!s.empty()) {
+      s.push_back('|');
+    }
+    v.AppendToString(&s);
+    flags ^= bit;
+  };
+  if (flags & 0x01) {
+    if (type == Http2FrameType::DATA || type == Http2FrameType::HEADERS) {
+      append_and_clear("END_STREAM", Http2FrameFlag::FLAG_END_STREAM);
+    } else if (type == Http2FrameType::SETTINGS ||
+               type == Http2FrameType::PING) {
+      append_and_clear("ACK", Http2FrameFlag::FLAG_ACK);
+    }
+  }
+  if (flags & 0x04) {
+    if (type == Http2FrameType::HEADERS ||
+        type == Http2FrameType::PUSH_PROMISE ||
+        type == Http2FrameType::CONTINUATION) {
+      append_and_clear("END_HEADERS", Http2FrameFlag::FLAG_END_HEADERS);
+    }
+  }
+  if (flags & 0x08) {
+    if (type == Http2FrameType::DATA || type == Http2FrameType::HEADERS ||
+        type == Http2FrameType::PUSH_PROMISE) {
+      append_and_clear("PADDED", Http2FrameFlag::FLAG_PADDED);
+    }
+  }
+  if (flags & 0x20) {
+    if (type == Http2FrameType::HEADERS) {
+      append_and_clear("PRIORITY", Http2FrameFlag::FLAG_PRIORITY);
+    }
+  }
+  if (flags != 0) {
+    append_and_clear(StringPrintf("0x%02x", flags), flags);
+  }
+  DCHECK_EQ(0, flags);
+  return s;
+}
+string Http2FrameFlagsToString(uint8_t type, uint8_t flags) {
+  return Http2FrameFlagsToString(static_cast<Http2FrameType>(type), flags);
+}
+
+string Http2ErrorCodeToString(uint32_t v) {
+  switch (v) {
+    case 0x0:
+      return "NO_ERROR";
+    case 0x1:
+      return "PROTOCOL_ERROR";
+    case 0x2:
+      return "INTERNAL_ERROR";
+    case 0x3:
+      return "FLOW_CONTROL_ERROR";
+    case 0x4:
+      return "SETTINGS_TIMEOUT";
+    case 0x5:
+      return "STREAM_CLOSED";
+    case 0x6:
+      return "FRAME_SIZE_ERROR";
+    case 0x7:
+      return "REFUSED_STREAM";
+    case 0x8:
+      return "CANCEL";
+    case 0x9:
+      return "COMPRESSION_ERROR";
+    case 0xa:
+      return "CONNECT_ERROR";
+    case 0xb:
+      return "ENHANCE_YOUR_CALM";
+    case 0xc:
+      return "INADEQUATE_SECURITY";
+    case 0xd:
+      return "HTTP_1_1_REQUIRED";
+  }
+  std::stringstream ss;
+  ss << "UnknownErrorCode(0x" << std::hex << v << ")";
+  return ss.str();
+}
+string Http2ErrorCodeToString(Http2ErrorCode v) {
+  return Http2ErrorCodeToString(static_cast<uint32_t>(v));
+}
+
+string Http2SettingsParameterToString(uint32_t v) {
+  switch (v) {
+    case 0x1:
+      return "HEADER_TABLE_SIZE";
+    case 0x2:
+      return "ENABLE_PUSH";
+    case 0x3:
+      return "MAX_CONCURRENT_STREAMS";
+    case 0x4:
+      return "INITIAL_WINDOW_SIZE";
+    case 0x5:
+      return "MAX_FRAME_SIZE";
+    case 0x6:
+      return "MAX_HEADER_LIST_SIZE";
+  }
+  std::stringstream ss;
+  ss << "UnknownSettingsParameter(0x" << std::hex << v << ")";
+  return ss.str();
+}
+string Http2SettingsParameterToString(Http2SettingsParameter v) {
+  return Http2SettingsParameterToString(static_cast<uint32_t>(v));
+}
+
+}  // namespace net
diff --git a/net/http2/http2_constants.h b/net/http2/http2_constants.h
new file mode 100644
index 0000000..61c6307
--- /dev/null
+++ b/net/http2/http2_constants.h
@@ -0,0 +1,265 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_CONSTANTS_H_
+#define NET_HTTP2_HTTP2_CONSTANTS_H_
+
+// Constants from the HTTP/2 spec, RFC 7540, and associated helper functions.
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// TODO(jamessynge): create http2_simple_types for types similar to
+// SpdyStreamId, but not for structures like Http2FrameHeader. Then will be
+// able to move these stream id functions there.
+constexpr uint32_t UInt31Mask() {
+  return 0x7fffffff;
+}
+constexpr uint32_t StreamIdMask() {
+  return UInt31Mask();
+}
+
+// The value used to identify types of frames. Upper case to match the RFC.
+// The comments indicate which flags are valid for that frame type.
+// ALTSVC is defined in http://httpwg.org/http-extensions/alt-svc.html
+// (not yet final standard as of March 2016, but close).
+enum class Http2FrameType : uint8_t {
+  DATA = 0,           // END_STREAM | PADDED
+  HEADERS = 1,        // END_STREAM | END_HEADERS | PADDED | PRIORITY
+  PRIORITY = 2,       //
+  RST_STREAM = 3,     //
+  SETTINGS = 4,       // ACK
+  PUSH_PROMISE = 5,   // END_HEADERS | PADDED
+  PING = 6,           // ACK
+  GOAWAY = 7,         //
+  WINDOW_UPDATE = 8,  //
+  CONTINUATION = 9,   // END_HEADERS
+  ALTSVC = 10,        //
+};
+
+// Is the frame type known/supported?
+inline bool IsSupportedHttp2FrameType(uint32_t v) {
+  return v <= static_cast<uint32_t>(Http2FrameType::ALTSVC);
+}
+inline bool IsSupportedHttp2FrameType(Http2FrameType v) {
+  return IsSupportedHttp2FrameType(static_cast<uint32_t>(v));
+}
+
+// The return type is 'string' so that they can generate a unique string for
+// each unsupported value. Since these are just used for debugging/error
+// messages, that isn't a cost to we need to worry about.
+// The same applies to the functions later in this file.
+NET_EXPORT_PRIVATE std::string Http2FrameTypeToString(Http2FrameType v);
+NET_EXPORT_PRIVATE std::string Http2FrameTypeToString(uint8_t v);
+NET_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out,
+                                                   Http2FrameType v) {
+  return out << Http2FrameTypeToString(v);
+}
+
+// Flags that appear in supported frame types. These are treated as bit masks.
+// The comments indicate for which frame types the flag is valid.
+// TODO(bnc): Remove FLAG_ prefix once enum SpdyFrameType is removed
+// (both enums have a PRIORITY member).
+enum Http2FrameFlag {
+  FLAG_END_STREAM = 0x01,   // DATA, HEADERS
+  FLAG_ACK = 0x01,          // SETTINGS, PING
+  FLAG_END_HEADERS = 0x04,  // HEADERS, PUSH_PROMISE, CONTINUATION
+  FLAG_PADDED = 0x08,       // DATA, HEADERS, PUSH_PROMISE
+  FLAG_PRIORITY = 0x20,     // HEADERS
+};
+
+// Formats zero or more flags for the specified type of frame. Returns an
+// empty string if flags==0.
+NET_EXPORT_PRIVATE std::string Http2FrameFlagsToString(Http2FrameType type,
+                                                       uint8_t flags);
+NET_EXPORT_PRIVATE std::string Http2FrameFlagsToString(uint8_t type,
+                                                       uint8_t flags);
+
+// Error codes for GOAWAY and RST_STREAM frames.
+enum class Http2ErrorCode : uint32_t {
+  // The associated condition is not a result of an error. For example, a GOAWAY
+  // might include this code to indicate graceful shutdown of a connection.
+  HTTP2_NO_ERROR = 0x0,
+
+  // The endpoint detected an unspecific protocol error. This error is for use
+  // when a more specific error code is not available.
+  PROTOCOL_ERROR = 0x1,
+
+  // The endpoint encountered an unexpected internal error.
+  INTERNAL_ERROR = 0x2,
+
+  // The endpoint detected that its peer violated the flow-control protocol.
+  FLOW_CONTROL_ERROR = 0x3,
+
+  // The endpoint sent a SETTINGS frame but did not receive a response in a
+  // timely manner. See Section 6.5.3 ("Settings Synchronization").
+  SETTINGS_TIMEOUT = 0x4,
+
+  // The endpoint received a frame after a stream was half-closed.
+  STREAM_CLOSED = 0x5,
+
+  // The endpoint received a frame with an invalid size.
+  FRAME_SIZE_ERROR = 0x6,
+
+  // The endpoint refused the stream prior to performing any application
+  // processing (see Section 8.1.4 for details).
+  REFUSED_STREAM = 0x7,
+
+  // Used by the endpoint to indicate that the stream is no longer needed.
+  CANCEL = 0x8,
+
+  // The endpoint is unable to maintain the header compression context
+  // for the connection.
+  COMPRESSION_ERROR = 0x9,
+
+  // The connection established in response to a CONNECT request (Section 8.3)
+  // was reset or abnormally closed.
+  CONNECT_ERROR = 0xa,
+
+  // The endpoint detected that its peer is exhibiting a behavior that might
+  // be generating excessive load.
+  ENHANCE_YOUR_CALM = 0xb,
+
+  // The underlying transport has properties that do not meet minimum
+  // security requirements (see Section 9.2).
+  INADEQUATE_SECURITY = 0xc,
+
+  // The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
+  HTTP_1_1_REQUIRED = 0xd,
+};
+
+// Is the error code supported? (So far that means it is in RFC 7540.)
+inline bool IsSupportedHttp2ErrorCode(uint32_t v) {
+  return v <= static_cast<uint32_t>(Http2ErrorCode::HTTP_1_1_REQUIRED);
+}
+inline bool IsSupportedHttp2ErrorCode(Http2ErrorCode v) {
+  return IsSupportedHttp2ErrorCode(static_cast<uint32_t>(v));
+}
+
+// Format the specified error code.
+NET_EXPORT_PRIVATE std::string Http2ErrorCodeToString(uint32_t v);
+NET_EXPORT_PRIVATE std::string Http2ErrorCodeToString(Http2ErrorCode v);
+NET_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out,
+                                                   Http2ErrorCode v) {
+  return out << Http2ErrorCodeToString(v);
+}
+
+// Supported parameters in SETTINGS frames; so far just those in RFC 7540.
+enum class Http2SettingsParameter : uint16_t {
+  // Allows the sender to inform the remote endpoint of the maximum size of the
+  // header compression table used to decode header blocks, in octets. The
+  // encoder can select any size equal to or less than this value by using
+  // signaling specific to the header compression format inside a header block
+  // (see [COMPRESSION]). The initial value is 4,096 octets.
+  HEADER_TABLE_SIZE = 0x1,
+
+  // This setting can be used to disable server push (Section 8.2). An endpoint
+  // MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a
+  // value of 0. An endpoint that has both set this parameter to 0 and had it
+  // acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a connection
+  // error (Section 5.4.1) of type PROTOCOL_ERROR.
+  //
+  // The initial value is 1, which indicates that server push is permitted. Any
+  // value other than 0 or 1 MUST be treated as a connection error (Section
+  // 5.4.1) of type PROTOCOL_ERROR.
+  ENABLE_PUSH = 0x2,
+
+  // Indicates the maximum number of concurrent streams that the sender will
+  // allow. This limit is directional: it applies to the number of streams that
+  // the sender permits the receiver to create. Initially, there is no limit to
+  // this value. It is recommended that this value be no smaller than 100, so as
+  // to not unnecessarily limit parallelism.
+  //
+  // A value of 0 for MAX_CONCURRENT_STREAMS SHOULD NOT be treated as
+  // special by endpoints. A zero value does prevent the creation of new
+  // streams; however, this can also happen for any limit that is exhausted with
+  // active streams. Servers SHOULD only set a zero value for short durations;
+  // if a server does not wish to accept requests, closing the connection is
+  // more appropriate.
+  MAX_CONCURRENT_STREAMS = 0x3,
+
+  // Indicates the sender's initial window size (in octets) for stream-level
+  // flow control. The initial value is 2^16-1 (65,535) octets.
+  //
+  // This setting affects the window size of all streams (see Section 6.9.2).
+  //
+  // Values above the maximum flow-control window size of 2^31-1 MUST be treated
+  // as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR.
+  INITIAL_WINDOW_SIZE = 0x4,
+
+  // Indicates the size of the largest frame payload that the sender is willing
+  // to receive, in octets.
+  //
+  // The initial value is 2^14 (16,384) octets. The value advertised by an
+  // endpoint MUST be between this initial value and the maximum allowed frame
+  // size (2^24-1 or 16,777,215 octets), inclusive. Values outside this range
+  // MUST be treated as a connection error (Section 5.4.1) of type
+  // PROTOCOL_ERROR.
+  MAX_FRAME_SIZE = 0x5,
+
+  // This advisory setting informs a peer of the maximum size of header list
+  // that the sender is prepared to accept, in octets. The value is based on the
+  // uncompressed size of header fields, including the length of the name and
+  // value in octets plus an overhead of 32 octets for each header field.
+  //
+  // For any given request, a lower limit than what is advertised MAY be
+  // enforced. The initial value of this setting is unlimited.
+  MAX_HEADER_LIST_SIZE = 0x6,
+};
+
+// Is the settings parameter supported (so far that means it is in RFC 7540)?
+inline bool IsSupportedHttp2SettingsParameter(uint32_t v) {
+  return 0 < v &&
+         v <= static_cast<uint32_t>(
+                  Http2SettingsParameter::MAX_HEADER_LIST_SIZE);
+}
+inline bool IsSupportedHttp2SettingsParameter(Http2SettingsParameter v) {
+  return IsSupportedHttp2SettingsParameter(static_cast<uint32_t>(v));
+}
+
+// Format the specified settings parameter.
+NET_EXPORT_PRIVATE std::string Http2SettingsParameterToString(uint32_t v);
+NET_EXPORT_PRIVATE std::string Http2SettingsParameterToString(
+    Http2SettingsParameter v);
+inline std::ostream& operator<<(std::ostream& out, Http2SettingsParameter v) {
+  return out << Http2SettingsParameterToString(v);
+}
+
+// Information about the initial, minimum and maximum value of settings (not
+// applicable to all settings parameters).
+class Http2SettingsInfo {
+ public:
+  // Default value for HEADER_TABLE_SIZE.
+  static constexpr uint32_t DefaultHeaderTableSize() { return 4096; }
+
+  // Default value for ENABLE_PUSH.
+  static constexpr bool DefaultEnablePush() { return true; }
+
+  // Default value for INITIAL_WINDOW_SIZE.
+  static constexpr uint32_t DefaultInitialWindowSize() { return 65535; }
+
+  // Maximum value for INITIAL_WINDOW_SIZE, and for the connection flow control
+  // window, and for each stream flow control window.
+  static constexpr uint32_t MaximumWindowSize() { return UInt31Mask(); }
+
+  // Default value for MAX_FRAME_SIZE.
+  static constexpr uint32_t DefaultMaxFrameSize() { return 16384; }
+
+  // Minimum value for MAX_FRAME_SIZE.
+  static constexpr uint32_t MinimumMaxFrameSize() { return 16384; }
+
+  // Maximum value for MAX_FRAME_SIZE.
+  static constexpr uint32_t MaximumMaxFrameSize() { return (1 << 24) - 1; }
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_CONSTANTS_H_
diff --git a/net/http2/http2_constants_test.cc b/net/http2/http2_constants_test.cc
new file mode 100644
index 0000000..f7f24f7
--- /dev/null
+++ b/net/http2/http2_constants_test.cc
@@ -0,0 +1,272 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+class Http2ConstantsTest : public testing::Test {};
+
+TEST(Http2ConstantsTest, Http2FrameType) {
+  EXPECT_EQ(Http2FrameType::DATA, static_cast<Http2FrameType>(0));
+  EXPECT_EQ(Http2FrameType::HEADERS, static_cast<Http2FrameType>(1));
+  EXPECT_EQ(Http2FrameType::PRIORITY, static_cast<Http2FrameType>(2));
+  EXPECT_EQ(Http2FrameType::RST_STREAM, static_cast<Http2FrameType>(3));
+  EXPECT_EQ(Http2FrameType::SETTINGS, static_cast<Http2FrameType>(4));
+  EXPECT_EQ(Http2FrameType::PUSH_PROMISE, static_cast<Http2FrameType>(5));
+  EXPECT_EQ(Http2FrameType::PING, static_cast<Http2FrameType>(6));
+  EXPECT_EQ(Http2FrameType::GOAWAY, static_cast<Http2FrameType>(7));
+  EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, static_cast<Http2FrameType>(8));
+  EXPECT_EQ(Http2FrameType::CONTINUATION, static_cast<Http2FrameType>(9));
+  EXPECT_EQ(Http2FrameType::ALTSVC, static_cast<Http2FrameType>(10));
+}
+
+TEST(Http2ConstantsTest, Http2FrameTypeToString) {
+  EXPECT_EQ("DATA", Http2FrameTypeToString(Http2FrameType::DATA));
+  EXPECT_EQ("HEADERS", Http2FrameTypeToString(Http2FrameType::HEADERS));
+  EXPECT_EQ("PRIORITY", Http2FrameTypeToString(Http2FrameType::PRIORITY));
+  EXPECT_EQ("RST_STREAM", Http2FrameTypeToString(Http2FrameType::RST_STREAM));
+  EXPECT_EQ("SETTINGS", Http2FrameTypeToString(Http2FrameType::SETTINGS));
+  EXPECT_EQ("PUSH_PROMISE",
+            Http2FrameTypeToString(Http2FrameType::PUSH_PROMISE));
+  EXPECT_EQ("PING", Http2FrameTypeToString(Http2FrameType::PING));
+  EXPECT_EQ("GOAWAY", Http2FrameTypeToString(Http2FrameType::GOAWAY));
+  EXPECT_EQ("WINDOW_UPDATE",
+            Http2FrameTypeToString(Http2FrameType::WINDOW_UPDATE));
+  EXPECT_EQ("CONTINUATION",
+            Http2FrameTypeToString(Http2FrameType::CONTINUATION));
+  EXPECT_EQ("ALTSVC", Http2FrameTypeToString(Http2FrameType::ALTSVC));
+
+  EXPECT_EQ("DATA", Http2FrameTypeToString(0));
+  EXPECT_EQ("HEADERS", Http2FrameTypeToString(1));
+  EXPECT_EQ("PRIORITY", Http2FrameTypeToString(2));
+  EXPECT_EQ("RST_STREAM", Http2FrameTypeToString(3));
+  EXPECT_EQ("SETTINGS", Http2FrameTypeToString(4));
+  EXPECT_EQ("PUSH_PROMISE", Http2FrameTypeToString(5));
+  EXPECT_EQ("PING", Http2FrameTypeToString(6));
+  EXPECT_EQ("GOAWAY", Http2FrameTypeToString(7));
+  EXPECT_EQ("WINDOW_UPDATE", Http2FrameTypeToString(8));
+  EXPECT_EQ("CONTINUATION", Http2FrameTypeToString(9));
+  EXPECT_EQ("ALTSVC", Http2FrameTypeToString(10));
+
+  EXPECT_EQ("UnknownFrameType(99)", Http2FrameTypeToString(99));
+}
+
+TEST(Http2ConstantsTest, Http2FrameFlag) {
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_STREAM, static_cast<Http2FrameFlag>(0x01));
+  EXPECT_EQ(Http2FrameFlag::FLAG_ACK, static_cast<Http2FrameFlag>(0x01));
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_HEADERS,
+            static_cast<Http2FrameFlag>(0x04));
+  EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, static_cast<Http2FrameFlag>(0x08));
+  EXPECT_EQ(Http2FrameFlag::FLAG_PRIORITY, static_cast<Http2FrameFlag>(0x20));
+
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_STREAM, 0x01);
+  EXPECT_EQ(Http2FrameFlag::FLAG_ACK, 0x01);
+  EXPECT_EQ(Http2FrameFlag::FLAG_END_HEADERS, 0x04);
+  EXPECT_EQ(Http2FrameFlag::FLAG_PADDED, 0x08);
+  EXPECT_EQ(Http2FrameFlag::FLAG_PRIORITY, 0x20);
+}
+
+TEST(Http2ConstantsTest, Http2FrameFlagsToString) {
+  // Single flags...
+
+  // 0b00000001
+  EXPECT_EQ("END_STREAM",
+            Http2FrameFlagsToString(Http2FrameType::DATA,
+                                    Http2FrameFlag::FLAG_END_STREAM));
+  EXPECT_EQ("END_STREAM",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x01));
+  EXPECT_EQ("ACK", Http2FrameFlagsToString(Http2FrameType::SETTINGS,
+                                           Http2FrameFlag::FLAG_ACK));
+  EXPECT_EQ("ACK", Http2FrameFlagsToString(Http2FrameType::PING, 0x01));
+
+  // 0b00000010
+  EXPECT_EQ("0x02", Http2FrameFlagsToString(0xff, 0x02));
+
+  // 0b00000100
+  EXPECT_EQ("END_HEADERS",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS,
+                                    Http2FrameFlag::FLAG_END_HEADERS));
+  EXPECT_EQ("END_HEADERS",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0x04));
+  EXPECT_EQ("END_HEADERS", Http2FrameFlagsToString(0x09, 0x04));
+  EXPECT_EQ("0x04", Http2FrameFlagsToString(0xff, 0x04));
+
+  // 0b00001000
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(Http2FrameType::DATA,
+                                              Http2FrameFlag::FLAG_PADDED));
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x08));
+  EXPECT_EQ("PADDED", Http2FrameFlagsToString(0x05, 0x08));
+  EXPECT_EQ("0x08", Http2FrameFlagsToString(0xff, Http2FrameFlag::FLAG_PADDED));
+
+  // 0b00010000
+  EXPECT_EQ("0x10", Http2FrameFlagsToString(Http2FrameType::SETTINGS, 0x10));
+
+  // 0b00100000
+  EXPECT_EQ("PRIORITY", Http2FrameFlagsToString(Http2FrameType::HEADERS, 0x20));
+  EXPECT_EQ("0x20",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0x20));
+
+  // 0b01000000
+  EXPECT_EQ("0x40", Http2FrameFlagsToString(0xff, 0x40));
+
+  // 0b10000000
+  EXPECT_EQ("0x80", Http2FrameFlagsToString(0xff, 0x80));
+
+  // Combined flags...
+
+  EXPECT_EQ("END_STREAM|PADDED|0xf6",
+            Http2FrameFlagsToString(Http2FrameType::DATA, 0xff));
+  EXPECT_EQ("END_STREAM|END_HEADERS|PADDED|PRIORITY|0xd2",
+            Http2FrameFlagsToString(Http2FrameType::HEADERS, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::PRIORITY, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::RST_STREAM, 0xff));
+  EXPECT_EQ("ACK|0xfe",
+            Http2FrameFlagsToString(Http2FrameType::SETTINGS, 0xff));
+  EXPECT_EQ("END_HEADERS|PADDED|0xf3",
+            Http2FrameFlagsToString(Http2FrameType::PUSH_PROMISE, 0xff));
+  EXPECT_EQ("ACK|0xfe", Http2FrameFlagsToString(Http2FrameType::PING, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::GOAWAY, 0xff));
+  EXPECT_EQ("0xff",
+            Http2FrameFlagsToString(Http2FrameType::WINDOW_UPDATE, 0xff));
+  EXPECT_EQ("END_HEADERS|0xfb",
+            Http2FrameFlagsToString(Http2FrameType::CONTINUATION, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(Http2FrameType::ALTSVC, 0xff));
+  EXPECT_EQ("0xff", Http2FrameFlagsToString(0xff, 0xff));
+}
+
+TEST(Http2ConstantsTest, Http2ErrorCode) {
+  EXPECT_EQ(Http2ErrorCode::HTTP2_NO_ERROR, static_cast<Http2ErrorCode>(0x0));
+  EXPECT_EQ(Http2ErrorCode::PROTOCOL_ERROR, static_cast<Http2ErrorCode>(0x1));
+  EXPECT_EQ(Http2ErrorCode::INTERNAL_ERROR, static_cast<Http2ErrorCode>(0x2));
+  EXPECT_EQ(Http2ErrorCode::FLOW_CONTROL_ERROR,
+            static_cast<Http2ErrorCode>(0x3));
+  EXPECT_EQ(Http2ErrorCode::SETTINGS_TIMEOUT, static_cast<Http2ErrorCode>(0x4));
+  EXPECT_EQ(Http2ErrorCode::STREAM_CLOSED, static_cast<Http2ErrorCode>(0x5));
+  EXPECT_EQ(Http2ErrorCode::FRAME_SIZE_ERROR, static_cast<Http2ErrorCode>(0x6));
+  EXPECT_EQ(Http2ErrorCode::REFUSED_STREAM, static_cast<Http2ErrorCode>(0x7));
+  EXPECT_EQ(Http2ErrorCode::CANCEL, static_cast<Http2ErrorCode>(0x8));
+  EXPECT_EQ(Http2ErrorCode::COMPRESSION_ERROR,
+            static_cast<Http2ErrorCode>(0x9));
+  EXPECT_EQ(Http2ErrorCode::CONNECT_ERROR, static_cast<Http2ErrorCode>(0xa));
+  EXPECT_EQ(Http2ErrorCode::ENHANCE_YOUR_CALM,
+            static_cast<Http2ErrorCode>(0xb));
+  EXPECT_EQ(Http2ErrorCode::INADEQUATE_SECURITY,
+            static_cast<Http2ErrorCode>(0xc));
+  EXPECT_EQ(Http2ErrorCode::HTTP_1_1_REQUIRED,
+            static_cast<Http2ErrorCode>(0xd));
+}
+
+TEST(Http2ConstantsTest, Http2ErrorCodeToString) {
+  EXPECT_EQ("NO_ERROR", Http2ErrorCodeToString(Http2ErrorCode::HTTP2_NO_ERROR));
+  EXPECT_EQ("NO_ERROR", Http2ErrorCodeToString(0x0));
+  EXPECT_EQ("PROTOCOL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::PROTOCOL_ERROR));
+  EXPECT_EQ("PROTOCOL_ERROR", Http2ErrorCodeToString(0x1));
+  EXPECT_EQ("INTERNAL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::INTERNAL_ERROR));
+  EXPECT_EQ("INTERNAL_ERROR", Http2ErrorCodeToString(0x2));
+  EXPECT_EQ("FLOW_CONTROL_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::FLOW_CONTROL_ERROR));
+  EXPECT_EQ("FLOW_CONTROL_ERROR", Http2ErrorCodeToString(0x3));
+  EXPECT_EQ("SETTINGS_TIMEOUT",
+            Http2ErrorCodeToString(Http2ErrorCode::SETTINGS_TIMEOUT));
+  EXPECT_EQ("SETTINGS_TIMEOUT", Http2ErrorCodeToString(0x4));
+  EXPECT_EQ("STREAM_CLOSED",
+            Http2ErrorCodeToString(Http2ErrorCode::STREAM_CLOSED));
+  EXPECT_EQ("STREAM_CLOSED", Http2ErrorCodeToString(0x5));
+  EXPECT_EQ("FRAME_SIZE_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::FRAME_SIZE_ERROR));
+  EXPECT_EQ("FRAME_SIZE_ERROR", Http2ErrorCodeToString(0x6));
+  EXPECT_EQ("REFUSED_STREAM",
+            Http2ErrorCodeToString(Http2ErrorCode::REFUSED_STREAM));
+  EXPECT_EQ("REFUSED_STREAM", Http2ErrorCodeToString(0x7));
+  EXPECT_EQ("CANCEL", Http2ErrorCodeToString(Http2ErrorCode::CANCEL));
+  EXPECT_EQ("CANCEL", Http2ErrorCodeToString(0x8));
+  EXPECT_EQ("COMPRESSION_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::COMPRESSION_ERROR));
+  EXPECT_EQ("COMPRESSION_ERROR", Http2ErrorCodeToString(0x9));
+  EXPECT_EQ("CONNECT_ERROR",
+            Http2ErrorCodeToString(Http2ErrorCode::CONNECT_ERROR));
+  EXPECT_EQ("CONNECT_ERROR", Http2ErrorCodeToString(0xa));
+  EXPECT_EQ("ENHANCE_YOUR_CALM",
+            Http2ErrorCodeToString(Http2ErrorCode::ENHANCE_YOUR_CALM));
+  EXPECT_EQ("ENHANCE_YOUR_CALM", Http2ErrorCodeToString(0xb));
+  EXPECT_EQ("INADEQUATE_SECURITY",
+            Http2ErrorCodeToString(Http2ErrorCode::INADEQUATE_SECURITY));
+  EXPECT_EQ("INADEQUATE_SECURITY", Http2ErrorCodeToString(0xc));
+  EXPECT_EQ("HTTP_1_1_REQUIRED",
+            Http2ErrorCodeToString(Http2ErrorCode::HTTP_1_1_REQUIRED));
+  EXPECT_EQ("HTTP_1_1_REQUIRED", Http2ErrorCodeToString(0xd));
+
+  EXPECT_EQ("UnknownErrorCode(0x123)", Http2ErrorCodeToString(0x123));
+}
+
+TEST(Http2ConstantsTest, Http2SettingsParameter) {
+  EXPECT_EQ(Http2SettingsParameter::HEADER_TABLE_SIZE,
+            static_cast<Http2SettingsParameter>(0x1));
+  EXPECT_EQ(Http2SettingsParameter::ENABLE_PUSH,
+            static_cast<Http2SettingsParameter>(0x2));
+  EXPECT_EQ(Http2SettingsParameter::MAX_CONCURRENT_STREAMS,
+            static_cast<Http2SettingsParameter>(0x3));
+  EXPECT_EQ(Http2SettingsParameter::INITIAL_WINDOW_SIZE,
+            static_cast<Http2SettingsParameter>(0x4));
+  EXPECT_EQ(Http2SettingsParameter::MAX_FRAME_SIZE,
+            static_cast<Http2SettingsParameter>(0x5));
+  EXPECT_EQ(Http2SettingsParameter::MAX_HEADER_LIST_SIZE,
+            static_cast<Http2SettingsParameter>(0x6));
+
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::HEADER_TABLE_SIZE));
+  EXPECT_TRUE(
+      IsSupportedHttp2SettingsParameter(Http2SettingsParameter::ENABLE_PUSH));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_CONCURRENT_STREAMS));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_FRAME_SIZE));
+  EXPECT_TRUE(IsSupportedHttp2SettingsParameter(
+      Http2SettingsParameter::MAX_HEADER_LIST_SIZE));
+
+  EXPECT_FALSE(IsSupportedHttp2SettingsParameter(
+      static_cast<Http2SettingsParameter>(0)));
+  EXPECT_FALSE(IsSupportedHttp2SettingsParameter(
+      static_cast<Http2SettingsParameter>(7)));
+}
+
+TEST(Http2ConstantsTest, Http2SettingsParameterToString) {
+  EXPECT_EQ("HEADER_TABLE_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::HEADER_TABLE_SIZE));
+  EXPECT_EQ("HEADER_TABLE_SIZE", Http2SettingsParameterToString(0x1));
+  EXPECT_EQ("ENABLE_PUSH", Http2SettingsParameterToString(
+                               Http2SettingsParameter::ENABLE_PUSH));
+  EXPECT_EQ("ENABLE_PUSH", Http2SettingsParameterToString(0x2));
+  EXPECT_EQ("MAX_CONCURRENT_STREAMS",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::MAX_CONCURRENT_STREAMS));
+  EXPECT_EQ("MAX_CONCURRENT_STREAMS", Http2SettingsParameterToString(0x3));
+  EXPECT_EQ("INITIAL_WINDOW_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::INITIAL_WINDOW_SIZE));
+  EXPECT_EQ("INITIAL_WINDOW_SIZE", Http2SettingsParameterToString(0x4));
+  EXPECT_EQ("MAX_FRAME_SIZE", Http2SettingsParameterToString(
+                                  Http2SettingsParameter::MAX_FRAME_SIZE));
+  EXPECT_EQ("MAX_FRAME_SIZE", Http2SettingsParameterToString(0x5));
+  EXPECT_EQ("MAX_HEADER_LIST_SIZE",
+            Http2SettingsParameterToString(
+                Http2SettingsParameter::MAX_HEADER_LIST_SIZE));
+  EXPECT_EQ("MAX_HEADER_LIST_SIZE", Http2SettingsParameterToString(0x6));
+
+  EXPECT_EQ("UnknownSettingsParameter(0x123)",
+            Http2SettingsParameterToString(0x123));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_constants_test_util.cc b/net/http2/http2_constants_test_util.cc
new file mode 100644
index 0000000..2f4be40
--- /dev/null
+++ b/net/http2/http2_constants_test_util.cc
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_constants_test_util.h"
+
+namespace net {
+namespace test {
+
+std::vector<Http2FrameType> AllHttp2FrameTypes() {
+  // clang-format off
+  return {
+      Http2FrameType::DATA,
+      Http2FrameType::HEADERS,
+      Http2FrameType::PRIORITY,
+      Http2FrameType::RST_STREAM,
+      Http2FrameType::SETTINGS,
+      Http2FrameType::PUSH_PROMISE,
+      Http2FrameType::PING,
+      Http2FrameType::GOAWAY,
+      Http2FrameType::WINDOW_UPDATE,
+      Http2FrameType::CONTINUATION,
+      Http2FrameType::ALTSVC,
+  };
+  // clang-format on
+}
+
+std::vector<Http2FrameFlag> AllHttp2FrameFlagsForFrameType(
+    Http2FrameType type) {
+  // clang-format off
+  switch (type) {
+    case Http2FrameType::DATA:
+      return {
+          Http2FrameFlag::FLAG_END_STREAM,
+          Http2FrameFlag::FLAG_PADDED,
+      };
+    case Http2FrameType::HEADERS:
+      return {
+          Http2FrameFlag::FLAG_END_STREAM,
+          Http2FrameFlag::FLAG_END_HEADERS,
+          Http2FrameFlag::FLAG_PADDED,
+          Http2FrameFlag::FLAG_PRIORITY,
+      };
+    case Http2FrameType::SETTINGS:
+      return {
+          Http2FrameFlag::FLAG_ACK,
+      };
+    case Http2FrameType::PUSH_PROMISE:
+      return {
+          Http2FrameFlag::FLAG_END_HEADERS,
+          Http2FrameFlag::FLAG_PADDED,
+      };
+    case Http2FrameType::PING:
+      return {
+          Http2FrameFlag::FLAG_ACK,
+      };
+    case Http2FrameType::CONTINUATION:
+      return {
+          Http2FrameFlag::FLAG_END_HEADERS,
+      };
+    default:
+      return std::vector<Http2FrameFlag>{};
+  }
+  // clang-format on
+}
+
+std::vector<Http2ErrorCode> AllHttp2ErrorCodes() {
+  // clang-format off
+  return {
+      Http2ErrorCode::HTTP2_NO_ERROR,
+      Http2ErrorCode::PROTOCOL_ERROR,
+      Http2ErrorCode::INTERNAL_ERROR,
+      Http2ErrorCode::FLOW_CONTROL_ERROR,
+      Http2ErrorCode::SETTINGS_TIMEOUT,
+      Http2ErrorCode::STREAM_CLOSED,
+      Http2ErrorCode::FRAME_SIZE_ERROR,
+      Http2ErrorCode::REFUSED_STREAM,
+      Http2ErrorCode::CANCEL,
+      Http2ErrorCode::COMPRESSION_ERROR,
+      Http2ErrorCode::CONNECT_ERROR,
+      Http2ErrorCode::ENHANCE_YOUR_CALM,
+      Http2ErrorCode::INADEQUATE_SECURITY,
+      Http2ErrorCode::HTTP_1_1_REQUIRED,
+  };
+  // clang-format on
+}
+
+std::vector<Http2SettingsParameter> AllHttp2SettingsParameters() {
+  // clang-format off
+  return {
+      Http2SettingsParameter::HEADER_TABLE_SIZE,
+      Http2SettingsParameter::ENABLE_PUSH,
+      Http2SettingsParameter::MAX_CONCURRENT_STREAMS,
+      Http2SettingsParameter::INITIAL_WINDOW_SIZE,
+      Http2SettingsParameter::MAX_FRAME_SIZE,
+      Http2SettingsParameter::MAX_HEADER_LIST_SIZE,
+  };
+  // clang-format on
+}
+
+// Returns a mask of flags supported for the specified frame type. Returns
+// zero for unknown frame types.
+uint8_t KnownFlagsMaskForFrameType(Http2FrameType type) {
+  switch (type) {
+    case Http2FrameType::DATA:
+      return Http2FrameFlag::FLAG_END_STREAM | Http2FrameFlag::FLAG_PADDED;
+    case Http2FrameType::HEADERS:
+      return Http2FrameFlag::FLAG_END_STREAM |
+             Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED |
+             Http2FrameFlag::FLAG_PRIORITY;
+    case Http2FrameType::PRIORITY:
+      return 0x00;
+    case Http2FrameType::RST_STREAM:
+      return 0x00;
+    case Http2FrameType::SETTINGS:
+      return Http2FrameFlag::FLAG_ACK;
+    case Http2FrameType::PUSH_PROMISE:
+      return Http2FrameFlag::FLAG_END_HEADERS | Http2FrameFlag::FLAG_PADDED;
+    case Http2FrameType::PING:
+      return Http2FrameFlag::FLAG_ACK;
+    case Http2FrameType::GOAWAY:
+      return 0x00;
+    case Http2FrameType::WINDOW_UPDATE:
+      return 0x00;
+    case Http2FrameType::CONTINUATION:
+      return Http2FrameFlag::FLAG_END_HEADERS;
+    case Http2FrameType::ALTSVC:
+      return 0x00;
+    default:
+      return 0x00;
+  }
+}
+
+uint8_t InvalidFlagMaskForFrameType(Http2FrameType type) {
+  if (IsSupportedHttp2FrameType(type)) {
+    return ~KnownFlagsMaskForFrameType(type);
+  }
+  return 0x00;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_constants_test_util.h b/net/http2/http2_constants_test_util.h
new file mode 100644
index 0000000..6d20cec
--- /dev/null
+++ b/net/http2/http2_constants_test_util.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
+#define NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
+
+#include <vector>
+
+#include "net/http2/http2_constants.h"
+
+namespace net {
+namespace test {
+
+// Returns a vector of all supported frame types.
+std::vector<Http2FrameType> AllHttp2FrameTypes();
+
+// Returns a vector of all supported frame flags for the specified
+// frame type. Empty if the type is unknown.
+std::vector<Http2FrameFlag> AllHttp2FrameFlagsForFrameType(Http2FrameType type);
+
+// Returns a vector of all supported RST_STREAM and GOAWAY error codes.
+std::vector<Http2ErrorCode> AllHttp2ErrorCodes();
+
+// Returns a vector of all supported parameters in SETTINGS frames.
+std::vector<Http2SettingsParameter> AllHttp2SettingsParameters();
+
+// Returns a mask of flags supported for the specified frame type. Returns
+// zero for unknown frame types.
+uint8_t KnownFlagsMaskForFrameType(Http2FrameType type);
+
+// Returns a mask of flag bits known to be invalid for the frame type.
+// For unknown frame types, the mask is zero; i.e., we don't know that any
+// are invalid.
+uint8_t InvalidFlagMaskForFrameType(Http2FrameType type);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_CONSTANTS_TEST_UTIL_H_
diff --git a/net/http2/http2_structures.cc b/net/http2/http2_structures.cc
new file mode 100644
index 0000000..83360430
--- /dev/null
+++ b/net/http2/http2_structures.cc
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures.h"
+
+#include <cstring>  // For std::memcmp
+#include <sstream>
+#include <string>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+
+using std::string;
+using base::StringPiece;
+
+namespace net {
+
+// Http2FrameHeader:
+
+bool Http2FrameHeader::IsProbableHttpResponse() const {
+  return (payload_length == 0x485454 &&      // "HTT"
+          static_cast<char>(type) == 'P' &&  // "P"
+          flags == '/');                     // "/"
+}
+
+string Http2FrameHeader::ToString() const {
+  std::stringstream ss;
+  ss << "length=" << payload_length << ", type=" << Http2FrameTypeToString(type)
+     << ", flags=" << FlagsToString() << ", stream=" << stream_id;
+  return ss.str();
+}
+
+string Http2FrameHeader::FlagsToString() const {
+  return Http2FrameFlagsToString(type, flags);
+}
+
+bool operator==(const Http2FrameHeader& a, const Http2FrameHeader& b) {
+  return a.payload_length == b.payload_length && a.stream_id == b.stream_id &&
+         a.type == b.type && a.flags == b.flags;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2FrameHeader& v) {
+  return out << v.ToString();
+}
+
+// Http2PriorityFields:
+
+bool operator==(const Http2PriorityFields& a, const Http2PriorityFields& b) {
+  return a.stream_dependency == b.stream_dependency && a.weight == b.weight;
+}
+
+std::string Http2PriorityFields::ToString() const {
+  std::stringstream ss;
+  ss << "E=" << (is_exclusive ? "true" : "false")
+     << ", stream=" << stream_dependency
+     << ", weight=" << static_cast<uint32_t>(weight);
+  return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PriorityFields& v) {
+  return out << v.ToString();
+}
+
+// Http2RstStreamFields:
+
+bool operator==(const Http2RstStreamFields& a, const Http2RstStreamFields& b) {
+  return a.error_code == b.error_code;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2RstStreamFields& v) {
+  return out << "error_code=" << v.error_code;
+}
+
+// Http2SettingFields:
+
+bool operator==(const Http2SettingFields& a, const Http2SettingFields& b) {
+  return a.parameter == b.parameter && a.value == b.value;
+}
+std::ostream& operator<<(std::ostream& out, const Http2SettingFields& v) {
+  return out << "parameter=" << v.parameter << ", value=" << v.value;
+}
+
+// Http2PushPromiseFields:
+
+bool operator==(const Http2PushPromiseFields& a,
+                const Http2PushPromiseFields& b) {
+  return a.promised_stream_id == b.promised_stream_id;
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PushPromiseFields& v) {
+  return out << "promised_stream_id=" << v.promised_stream_id;
+}
+
+// Http2PingFields:
+
+bool operator==(const Http2PingFields& a, const Http2PingFields& b) {
+  static_assert((sizeof a.opaque_data) == Http2PingFields::EncodedSize(),
+                "Why not the same size?");
+  return 0 == std::memcmp(a.opaque_data, b.opaque_data, sizeof a.opaque_data);
+}
+
+std::ostream& operator<<(std::ostream& out, const Http2PingFields& v) {
+  string s = base::HexEncode(v.opaque_data, sizeof v.opaque_data);
+  base::CollapseWhitespaceASCII(s, /*trim_sequences_with_line_breaks=*/false);
+  return out << "opaque_data=[" << s << "]";
+}
+
+// Http2GoAwayFields:
+
+bool operator==(const Http2GoAwayFields& a, const Http2GoAwayFields& b) {
+  return a.last_stream_id == b.last_stream_id && a.error_code == b.error_code;
+}
+std::ostream& operator<<(std::ostream& out, const Http2GoAwayFields& v) {
+  return out << "last_stream_id=" << v.last_stream_id
+             << ", error_code=" << v.error_code;
+}
+
+// Http2WindowUpdateFields:
+
+bool operator==(const Http2WindowUpdateFields& a,
+                const Http2WindowUpdateFields& b) {
+  return a.window_size_increment == b.window_size_increment;
+}
+std::ostream& operator<<(std::ostream& out, const Http2WindowUpdateFields& v) {
+  return out << "window_size_increment=" << v.window_size_increment;
+}
+
+// Http2AltSvcFields:
+
+bool operator==(const Http2AltSvcFields& a, const Http2AltSvcFields& b) {
+  return a.origin_length == b.origin_length;
+}
+std::ostream& operator<<(std::ostream& out, const Http2AltSvcFields& v) {
+  return out << "origin_length=" << v.origin_length;
+}
+
+}  // namespace net
diff --git a/net/http2/http2_structures.h b/net/http2/http2_structures.h
new file mode 100644
index 0000000..95328e1
--- /dev/null
+++ b/net/http2/http2_structures.h
@@ -0,0 +1,326 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_STRUCTURES_H_
+#define NET_HTTP2_HTTP2_STRUCTURES_H_
+
+// Defines structs for various fixed sized structures in HTTP/2.
+//
+// Those structs with multiple fields have constructors that take arguments in
+// the same order as their encoding (which may be different from their order
+// in the struct). For single field structs, use aggregate initialization if
+// desired, e.g.:
+//
+//   Http2RstStreamFields var{Http2ErrorCode::ENHANCE_YOUR_CALM};
+// or:
+//   SomeFunc(Http2RstStreamFields{Http2ErrorCode::ENHANCE_YOUR_CALM});
+//
+// Each struct includes a static method EncodedSize which returns the number
+// of bytes of the encoding.
+//
+// With the exception of Http2FrameHeader, all the types are named
+// Http2<X>Fields, where X is the title-case form of the frame which always
+// includes the fields; the "always" is to cover the case of the PRIORITY frame;
+// its fields optionally appear in the HEADERS frame, but the struct is called
+// Http2PriorityFields.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/logging.h"
+#include "net/base/net_export.h"
+#include "net/http2/http2_constants.h"
+
+namespace net {
+
+struct NET_EXPORT_PRIVATE Http2FrameHeader {
+  Http2FrameHeader() {}
+  Http2FrameHeader(uint32_t payload_length,
+                   Http2FrameType type,
+                   uint8_t flags,
+                   uint32_t stream_id)
+      : payload_length(payload_length),
+        stream_id(stream_id),
+        type(type),
+        flags(static_cast<Http2FrameFlag>(flags)) {
+    DCHECK_LT(payload_length, static_cast<uint32_t>(1 << 24))
+        << "Payload Length is only a 24 bit field\n"
+        << ToString();
+  }
+
+  static constexpr size_t EncodedSize() { return 9; }
+
+  // Keep the current value of those flags that are in
+  // valid_flags, and clear all the others.
+  void RetainFlags(uint8_t valid_flags) {
+    flags = static_cast<Http2FrameFlag>(flags & valid_flags);
+  }
+
+  // Returns true if any of the flags in flag_mask are set,
+  // otherwise false.
+  bool HasAnyFlags(uint8_t flag_mask) const { return 0 != (flags & flag_mask); }
+
+  // Is the END_STREAM flag set?
+  bool IsEndStream() const {
+    DCHECK(type == Http2FrameType::DATA || type == Http2FrameType::HEADERS)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_END_STREAM) != 0;
+  }
+
+  // Is the ACK flag set?
+  bool IsAck() const {
+    DCHECK(type == Http2FrameType::SETTINGS || type == Http2FrameType::PING)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_ACK) != 0;
+  }
+
+  // Is the END_HEADERS flag set?
+  bool IsEndHeaders() const {
+    DCHECK(type == Http2FrameType::HEADERS ||
+           type == Http2FrameType::PUSH_PROMISE ||
+           type == Http2FrameType::CONTINUATION)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_END_HEADERS) != 0;
+  }
+
+  // Is the PADDED flag set?
+  bool IsPadded() const {
+    DCHECK(type == Http2FrameType::DATA || type == Http2FrameType::HEADERS ||
+           type == Http2FrameType::PUSH_PROMISE)
+        << ToString();
+    return (flags & Http2FrameFlag::FLAG_PADDED) != 0;
+  }
+
+  // Is the PRIORITY flag set?
+  bool HasPriority() const {
+    DCHECK_EQ(type, Http2FrameType::HEADERS) << ToString();
+    return (flags & Http2FrameFlag::FLAG_PRIORITY) != 0;
+  }
+
+  // Does the encoding of this header start with "HTTP/", indicating that it
+  // might be from a non-HTTP/2 server.
+  bool IsProbableHttpResponse() const;
+
+  // Produce strings useful for debugging/logging messages.
+  std::string ToString() const;
+  std::string FlagsToString() const;
+
+  // 24 bit length of the payload after the header, including any padding.
+  // First field in encoding.
+  uint32_t payload_length;  // 24 bits
+
+  // 31 bit stream id, with high bit (32nd bit) reserved (must be zero),
+  // and is cleared during decoding.
+  // Fourth field in encoding.
+  uint32_t stream_id;
+
+  // Type of the frame.
+  // Second field in encoding.
+  Http2FrameType type;
+
+  // Flag bits, with interpretations that depend upon the frame type.
+  // Flag bits not used by the frame type are cleared.
+  // Third field in encoding.
+  Http2FrameFlag flags;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2FrameHeader& a,
+                                   const Http2FrameHeader& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2FrameHeader& a,
+                                          const Http2FrameHeader& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2FrameHeader& v);
+
+// Http2PriorityFields:
+
+struct NET_EXPORT_PRIVATE Http2PriorityFields {
+  Http2PriorityFields() {}
+  Http2PriorityFields(uint32_t stream_dependency,
+                      uint32_t weight,
+                      bool is_exclusive)
+      : stream_dependency(stream_dependency),
+        weight(weight),
+        is_exclusive(is_exclusive) {
+    // Can't have the high-bit set in the stream id because we need to use
+    // that for the EXCLUSIVE flag bit.
+    DCHECK_EQ(stream_dependency, stream_dependency & StreamIdMask())
+        << "Stream Dependency is only a 31-bit field.\n"
+        << ToString();
+    DCHECK_LE(1u, weight) << "Weight is too small.";
+    DCHECK_LE(weight, 256u) << "Weight is too large.";
+  }
+  static constexpr size_t EncodedSize() { return 5; }
+
+  // Produce strings useful for debugging/logging messages.
+  std::string ToString() const;
+
+  // A 31-bit stream identifier for the stream that this stream depends on.
+  uint32_t stream_dependency;
+
+  // Weight (1 to 256) is encoded as a byte in the range 0 to 255, so we
+  // add one when decoding, and store it in a field larger than a byte.
+  uint32_t weight;
+
+  // A single-bit flag indicating that the stream dependency is exclusive;
+  // extracted from high bit of stream dependency field during decoding.
+  bool is_exclusive;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PriorityFields& a,
+                                   const Http2PriorityFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PriorityFields& a,
+                                          const Http2PriorityFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PriorityFields& v);
+
+// Http2RstStreamFields:
+
+struct Http2RstStreamFields {
+  static constexpr size_t EncodedSize() { return 4; }
+  bool IsSupportedErrorCode() const {
+    return IsSupportedHttp2ErrorCode(error_code);
+  }
+
+  Http2ErrorCode error_code;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2RstStreamFields& a,
+                                   const Http2RstStreamFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2RstStreamFields& a,
+                                          const Http2RstStreamFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2RstStreamFields& v);
+
+// Http2SettingFields:
+
+struct Http2SettingFields {
+  Http2SettingFields() {}
+  Http2SettingFields(Http2SettingsParameter parameter, uint32_t value)
+      : parameter(parameter), value(value) {}
+  static constexpr size_t EncodedSize() { return 6; }
+  bool IsSupportedParameter() const {
+    return IsSupportedHttp2SettingsParameter(parameter);
+  }
+
+  Http2SettingsParameter parameter;
+  uint32_t value;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2SettingFields& a,
+                                   const Http2SettingFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2SettingFields& a,
+                                          const Http2SettingFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2SettingFields& v);
+
+// Http2PushPromiseFields:
+
+struct Http2PushPromiseFields {
+  static constexpr size_t EncodedSize() { return 4; }
+
+  uint32_t promised_stream_id;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PushPromiseFields& a,
+                                   const Http2PushPromiseFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PushPromiseFields& a,
+                                          const Http2PushPromiseFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PushPromiseFields& v);
+
+// Http2PingFields:
+
+struct Http2PingFields {
+  static constexpr size_t EncodedSize() { return 8; }
+
+  // TODO(jamessynge): Rename opaque_data to opaque_bytes.
+  uint8_t opaque_data[8];
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2PingFields& a,
+                                   const Http2PingFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2PingFields& a,
+                                          const Http2PingFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2PingFields& v);
+
+// Http2GoAwayFields:
+
+struct Http2GoAwayFields {
+  Http2GoAwayFields() {}
+  Http2GoAwayFields(uint32_t last_stream_id, Http2ErrorCode error_code)
+      : last_stream_id(last_stream_id), error_code(error_code) {}
+  static constexpr size_t EncodedSize() { return 8; }
+  bool IsSupportedErrorCode() const {
+    return IsSupportedHttp2ErrorCode(error_code);
+  }
+
+  uint32_t last_stream_id;
+  Http2ErrorCode error_code;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2GoAwayFields& a,
+                                   const Http2GoAwayFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2GoAwayFields& a,
+                                          const Http2GoAwayFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2GoAwayFields& v);
+
+// Http2WindowUpdateFields:
+
+struct Http2WindowUpdateFields {
+  static constexpr size_t EncodedSize() { return 4; }
+
+  // 31-bit, unsigned increase in the window size (only positive values are
+  // allowed). The high-bit is reserved for the future.
+  uint32_t window_size_increment;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2WindowUpdateFields& a,
+                                   const Http2WindowUpdateFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2WindowUpdateFields& a,
+                                          const Http2WindowUpdateFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2WindowUpdateFields& v);
+
+// Http2AltSvcFields:
+
+struct Http2AltSvcFields {
+  static constexpr size_t EncodedSize() { return 2; }
+
+  // This is the one fixed size portion of the ALTSVC payload.
+  uint16_t origin_length;
+};
+
+NET_EXPORT_PRIVATE bool operator==(const Http2AltSvcFields& a,
+                                   const Http2AltSvcFields& b);
+NET_EXPORT_PRIVATE inline bool operator!=(const Http2AltSvcFields& a,
+                                          const Http2AltSvcFields& b) {
+  return !(a == b);
+}
+NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+                                            const Http2AltSvcFields& v);
+
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_STRUCTURES_H_
diff --git a/net/http2/http2_structures_test.cc b/net/http2/http2_structures_test.cc
new file mode 100644
index 0000000..96035c77
--- /dev/null
+++ b/net/http2/http2_structures_test.cc
@@ -0,0 +1,486 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures.h"
+
+// Tests are focused on Http2FrameHeader because it has by far the most
+// methods of any of the structures.
+// Note that EXPECT.*DEATH tests are slow (a fork is probably involved).
+
+// And in case you're wondering, yes, these are ridiculously thorough tests,
+// but believe it or not, I've found stupid bugs this way.
+
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+#include "base/template_util.h"
+#include "net/http2/http2_structures_test_util.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::Combine;
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::MatchesRegex;
+using ::testing::Not;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+namespace net {
+namespace test {
+namespace {
+
+template <typename E>
+E IncrementEnum(E e) {
+  typedef typename base::underlying_type<E>::type I;
+  return static_cast<E>(1 + static_cast<I>(e));
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+std::vector<Http2FrameType> ValidFrameTypes() {
+  std::vector<Http2FrameType> valid_types{Http2FrameType::DATA};
+  while (valid_types.back() != Http2FrameType::ALTSVC) {
+    valid_types.push_back(IncrementEnum(valid_types.back()));
+  }
+  return valid_types;
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+TEST(Http2FrameHeaderTest, Constructor) {
+  Http2Random random;
+  uint8_t frame_type = 0;
+  do {
+    // Only the payload length is DCHECK'd in the constructor, so we need to
+    // make sure it is a "uint24".
+    uint32_t payload_length = random.Rand32() & 0xffffff;
+    Http2FrameType type = static_cast<Http2FrameType>(frame_type);
+    uint8_t flags = random.Rand8();
+    uint32_t stream_id = random.Rand32();
+
+    Http2FrameHeader v(payload_length, type, flags, stream_id);
+
+    EXPECT_EQ(payload_length, v.payload_length);
+    EXPECT_EQ(type, v.type);
+    EXPECT_EQ(flags, v.flags);
+    EXPECT_EQ(stream_id, v.stream_id);
+  } while (frame_type++ == 255);
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+  EXPECT_DEBUG_DEATH(Http2FrameHeader(0x01000000, Http2FrameType::DATA, 0, 1),
+                     "payload_length");
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+}
+
+TEST(Http2FrameHeaderTest, Eq) {
+  Http2Random random;
+  uint32_t payload_length = random.Rand32() & 0xffffff;
+  Http2FrameType type = static_cast<Http2FrameType>(random.Rand8());
+
+  uint8_t flags = random.Rand8();
+  uint32_t stream_id = random.Rand32();
+
+  Http2FrameHeader v(payload_length, type, flags, stream_id);
+
+  EXPECT_EQ(payload_length, v.payload_length);
+  EXPECT_EQ(type, v.type);
+  EXPECT_EQ(flags, v.flags);
+  EXPECT_EQ(stream_id, v.stream_id);
+
+  Http2FrameHeader u(0, type, ~flags, stream_id);
+
+  EXPECT_NE(u, v);
+  EXPECT_NE(v, u);
+  EXPECT_FALSE(u == v);
+  EXPECT_FALSE(v == u);
+  EXPECT_TRUE(u != v);
+  EXPECT_TRUE(v != u);
+
+  u = v;
+
+  EXPECT_EQ(u, v);
+  EXPECT_EQ(v, u);
+  EXPECT_TRUE(u == v);
+  EXPECT_TRUE(v == u);
+  EXPECT_FALSE(u != v);
+  EXPECT_FALSE(v != u);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+// The tests of the valid frame types include EXPECT_DEBUG_DEATH, which is
+// quite slow, so using value parameterized tests in order to allow sharding.
+class Http2FrameHeaderTypeAndFlagTest
+    : public ::testing::TestWithParam<
+          std::tuple<Http2FrameType, Http2FrameFlag>> {
+ protected:
+  Http2FrameHeaderTypeAndFlagTest()
+      : type_(std::get<0>(GetParam())), flags_(std::get<1>(GetParam())) {
+    LOG(INFO) << "Frame type: " << type_;
+    LOG(INFO) << "Frame flags: " << Http2FrameFlagsToString(type_, flags_);
+  }
+
+  const Http2FrameType type_;
+  const Http2FrameFlag flags_;
+};
+
+class IsEndStreamTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsEndStream,
+                        IsEndStreamTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_END_STREAM,
+                                       0xff)));
+TEST_P(IsEndStreamTest, IsEndStream) {
+  const bool is_set = (flags_ & Http2FrameFlag::FLAG_END_STREAM) ==
+                      Http2FrameFlag::FLAG_END_STREAM;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+      EXPECT_EQ(is_set, v.IsEndStream()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?END_STREAM\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("END_STREAM")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_END_STREAM);
+      EXPECT_EQ(is_set, v.IsEndStream()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=END_STREAM,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsEndStream(), "DATA.*HEADERS") << v;
+  }
+}
+
+class IsACKTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsAck,
+                        IsACKTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_ACK, 0xff)));
+TEST_P(IsACKTest, IsAck) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_ACK) == Http2FrameFlag::FLAG_ACK;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::SETTINGS:
+    case Http2FrameType::PING:
+      EXPECT_EQ(is_set, v.IsAck()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?ACK\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("ACK")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_ACK);
+      EXPECT_EQ(is_set, v.IsAck()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=ACK,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsAck(), "SETTINGS.*PING") << v;
+  }
+}
+
+class IsEndHeadersTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsEndHeaders,
+                        IsEndHeadersTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_END_HEADERS,
+                                       0xff)));
+TEST_P(IsEndHeadersTest, IsEndHeaders) {
+  const bool is_set = (flags_ & Http2FrameFlag::FLAG_END_HEADERS) ==
+                      Http2FrameFlag::FLAG_END_HEADERS;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+      EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?END_HEADERS\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("END_HEADERS")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_END_HEADERS);
+      EXPECT_EQ(is_set, v.IsEndHeaders()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=END_HEADERS,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsEndHeaders(),
+                         "HEADERS.*PUSH_PROMISE.*CONTINUATION")
+          << v;
+  }
+}
+
+class IsPaddedTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(IsPadded,
+                        IsPaddedTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_PADDED, 0xff)));
+TEST_P(IsPaddedTest, IsPadded) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_PADDED) == Http2FrameFlag::FLAG_PADDED;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+      EXPECT_EQ(is_set, v.IsPadded()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?PADDED\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("PADDED")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_PADDED);
+      EXPECT_EQ(is_set, v.IsPadded()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=PADDED,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.IsPadded(), "DATA.*HEADERS.*PUSH_PROMISE") << v;
+  }
+}
+
+class HasPriorityTest : public Http2FrameHeaderTypeAndFlagTest {};
+INSTANTIATE_TEST_CASE_P(HasPriority,
+                        HasPriorityTest,
+                        Combine(ValuesIn(ValidFrameTypes()),
+                                Values(~Http2FrameFlag::FLAG_PRIORITY, 0xff)));
+TEST_P(HasPriorityTest, HasPriority) {
+  const bool is_set =
+      (flags_ & Http2FrameFlag::FLAG_PRIORITY) == Http2FrameFlag::FLAG_PRIORITY;
+  LOG(INFO) << "is_set=" << is_set;
+  Http2FrameHeader v(0, type_, flags_, 0);
+  switch (type_) {
+    case Http2FrameType::HEADERS:
+      EXPECT_EQ(is_set, v.HasPriority()) << v;
+      if (is_set) {
+        EXPECT_THAT(v.FlagsToString(), MatchesRegex(".*\\|?PRIORITY\\|.*"));
+      } else {
+        EXPECT_THAT(v.FlagsToString(), Not(HasSubstr("PRIORITY")));
+      }
+      v.RetainFlags(Http2FrameFlag::FLAG_PRIORITY);
+      EXPECT_EQ(is_set, v.HasPriority()) << v;
+      {
+        std::stringstream s;
+        s << v;
+        EXPECT_EQ(v.ToString(), s.str());
+        if (is_set) {
+          EXPECT_THAT(s.str(), HasSubstr("flags=PRIORITY,"));
+        } else {
+          EXPECT_THAT(s.str(), HasSubstr("flags=,"));
+        }
+      }
+      break;
+    default:
+      EXPECT_DEBUG_DEATH(v.HasPriority(), "HEADERS") << v;
+  }
+}
+
+TEST(Http2PriorityFieldsTest, Constructor) {
+  Http2Random random;
+  uint32_t stream_dependency = random.Rand32() & StreamIdMask();
+  uint32_t weight = 1 + random.Rand8();
+  bool is_exclusive = random.OneIn(2);
+
+  Http2PriorityFields v(stream_dependency, weight, is_exclusive);
+
+  EXPECT_EQ(stream_dependency, v.stream_dependency);
+  EXPECT_EQ(weight, v.weight);
+  EXPECT_EQ(is_exclusive, v.is_exclusive);
+
+  // The high-bit must not be set on the stream id.
+  EXPECT_DEBUG_DEATH(
+      Http2PriorityFields(stream_dependency | 0x80000000, weight, is_exclusive),
+      "31-bit");
+
+  // The weight must be in the range 1-256.
+  EXPECT_DEBUG_DEATH(Http2PriorityFields(stream_dependency, 0, is_exclusive),
+                     "too small");
+  EXPECT_DEBUG_DEATH(
+      Http2PriorityFields(stream_dependency, weight + 256, is_exclusive),
+      "too large");
+}
+#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+TEST(Http2RstStreamFieldsTest, IsSupported) {
+  Http2RstStreamFields v{Http2ErrorCode::HTTP2_NO_ERROR};
+  EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
+
+  Http2RstStreamFields u{static_cast<Http2ErrorCode>(~0)};
+  EXPECT_FALSE(u.IsSupportedErrorCode()) << v;
+}
+
+TEST(Http2SettingFieldsTest, Misc) {
+  Http2Random random;
+  Http2SettingsParameter parameter =
+      static_cast<Http2SettingsParameter>(random.Rand16());
+  uint32_t value = random.Rand32();
+
+  Http2SettingFields v(parameter, value);
+
+  EXPECT_EQ(v, v);
+  EXPECT_EQ(parameter, v.parameter);
+  EXPECT_EQ(value, v.value);
+
+  if (static_cast<uint16_t>(parameter) < 7) {
+    EXPECT_TRUE(v.IsSupportedParameter()) << v;
+  } else {
+    EXPECT_FALSE(v.IsSupportedParameter()) << v;
+  }
+
+  Http2SettingFields u(parameter, ~value);
+  EXPECT_NE(v, u);
+  EXPECT_EQ(v.parameter, u.parameter);
+  EXPECT_NE(v.value, u.value);
+
+  Http2SettingFields w(IncrementEnum(parameter), value);
+  EXPECT_NE(v, w);
+  EXPECT_NE(v.parameter, w.parameter);
+  EXPECT_EQ(v.value, w.value);
+
+  Http2SettingFields x(Http2SettingsParameter::MAX_FRAME_SIZE, 123);
+  std::stringstream s;
+  s << x;
+  EXPECT_EQ("parameter=MAX_FRAME_SIZE, value=123", s.str());
+}
+
+TEST(Http2PushPromiseTest, Misc) {
+  Http2Random random;
+  uint32_t promised_stream_id = random.Rand32() & StreamIdMask();
+
+  Http2PushPromiseFields v{promised_stream_id};
+  EXPECT_EQ(promised_stream_id, v.promised_stream_id);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "promised_stream_id=" << promised_stream_id;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  // High-bit is reserved, but not used, so we can set it.
+  promised_stream_id |= 0x80000000;
+  Http2PushPromiseFields w{promised_stream_id};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.promised_stream_id = promised_stream_id;
+  EXPECT_EQ(v, w);
+}
+
+TEST(Http2GoAwayFieldsTest, Misc) {
+  Http2Random random;
+  uint32_t last_stream_id = random.Rand32() & StreamIdMask();
+  Http2ErrorCode error_code = static_cast<Http2ErrorCode>(random.Rand32());
+
+  Http2GoAwayFields v(last_stream_id, error_code);
+  EXPECT_EQ(v, v);
+  EXPECT_EQ(last_stream_id, v.last_stream_id);
+  EXPECT_EQ(error_code, v.error_code);
+
+  if (static_cast<uint32_t>(error_code) < 14) {
+    EXPECT_TRUE(v.IsSupportedErrorCode()) << v;
+  } else {
+    EXPECT_FALSE(v.IsSupportedErrorCode()) << v;
+  }
+
+  Http2GoAwayFields u(~last_stream_id, error_code);
+  EXPECT_NE(v, u);
+  EXPECT_NE(v.last_stream_id, u.last_stream_id);
+  EXPECT_EQ(v.error_code, u.error_code);
+}
+
+TEST(Http2WindowUpdateTest, Misc) {
+  Http2Random random;
+  uint32_t window_size_increment = random.Rand32() & UInt31Mask();
+
+  Http2WindowUpdateFields v{window_size_increment};
+  EXPECT_EQ(window_size_increment, v.window_size_increment);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "window_size_increment=" << window_size_increment;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  // High-bit is reserved, but not used, so we can set it.
+  window_size_increment |= 0x80000000;
+  Http2WindowUpdateFields w{window_size_increment};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.window_size_increment = window_size_increment;
+  EXPECT_EQ(v, w);
+}
+
+TEST(Http2AltSvcTest, Misc) {
+  Http2Random random;
+  uint16_t origin_length = random.Rand16();
+
+  Http2AltSvcFields v{origin_length};
+  EXPECT_EQ(origin_length, v.origin_length);
+  EXPECT_EQ(v, v);
+
+  std::stringstream s1;
+  s1 << "origin_length=" << origin_length;
+  std::stringstream s2;
+  s2 << v;
+  EXPECT_EQ(s1.str(), s2.str());
+
+  Http2AltSvcFields w{++origin_length};
+  EXPECT_EQ(w, w);
+  EXPECT_NE(v, w);
+
+  v.origin_length = w.origin_length;
+  EXPECT_EQ(v, w);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_structures_test_util.cc b/net/http2/http2_structures_test_util.cc
new file mode 100644
index 0000000..1e6f429
--- /dev/null
+++ b/net/http2/http2_structures_test_util.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/http2_structures_test_util.h"
+
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_constants_test_util.h"
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_random.h"
+
+namespace net {
+namespace test {
+
+void Randomize(Http2FrameHeader* p, RandomBase* rng) {
+  p->payload_length = rng->Rand32() & 0xffffff;
+  p->type = static_cast<Http2FrameType>(rng->Rand8());
+  p->flags = static_cast<Http2FrameFlag>(rng->Rand8());
+  p->stream_id = rng->Rand32() & StreamIdMask();
+}
+void Randomize(Http2PriorityFields* p, RandomBase* rng) {
+  p->stream_dependency = rng->Rand32() & StreamIdMask();
+  p->weight = rng->Rand8() + 1;
+  p->is_exclusive = rng->OneIn(2);
+}
+void Randomize(Http2RstStreamFields* p, RandomBase* rng) {
+  p->error_code = static_cast<Http2ErrorCode>(rng->Rand32());
+}
+void Randomize(Http2SettingFields* p, RandomBase* rng) {
+  p->parameter = static_cast<Http2SettingsParameter>(rng->Rand16());
+  p->value = rng->Rand32();
+}
+void Randomize(Http2PushPromiseFields* p, RandomBase* rng) {
+  p->promised_stream_id = rng->Rand32() & StreamIdMask();
+}
+void Randomize(Http2PingFields* p, RandomBase* rng) {
+  for (int ndx = 0; ndx < 8; ++ndx) {
+    p->opaque_data[ndx] = rng->Rand8();
+  }
+}
+void Randomize(Http2GoAwayFields* p, RandomBase* rng) {
+  p->last_stream_id = rng->Rand32() & StreamIdMask();
+  p->error_code = static_cast<Http2ErrorCode>(rng->Rand32());
+}
+void Randomize(Http2WindowUpdateFields* p, RandomBase* rng) {
+  p->window_size_increment = rng->Rand32() & 0x7fffffff;
+}
+void Randomize(Http2AltSvcFields* p, RandomBase* rng) {
+  p->origin_length = rng->Rand16();
+}
+
+void ScrubFlagsOfHeader(Http2FrameHeader* header) {
+  uint8_t invalid_mask = InvalidFlagMaskForFrameType(header->type);
+  uint8_t keep_mask = ~invalid_mask;
+  header->RetainFlags(keep_mask);
+}
+
+bool FrameIsPadded(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+      return header.IsPadded();
+    default:
+      return false;
+  }
+}
+
+bool FrameHasPriority(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::HEADERS:
+      return header.HasPriority();
+    case Http2FrameType::PRIORITY:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool FrameCanHavePayload(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::DATA:
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+    case Http2FrameType::PING:
+    case Http2FrameType::GOAWAY:
+    case Http2FrameType::ALTSVC:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool FrameCanHaveHpackPayload(const Http2FrameHeader& header) {
+  switch (header.type) {
+    case Http2FrameType::HEADERS:
+    case Http2FrameType::PUSH_PROMISE:
+    case Http2FrameType::CONTINUATION:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/http2_structures_test_util.h b/net/http2/http2_structures_test_util.h
new file mode 100644
index 0000000..710a530
--- /dev/null
+++ b/net/http2/http2_structures_test_util.h
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
+#define NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
+
+#include <string>
+
+#include "net/http2/http2_structures.h"
+#include "net/http2/tools/http2_frame_builder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RandomBase;
+
+template <class S>
+std::string SerializeStructure(const S& s) {
+  Http2FrameBuilder fb;
+  fb.Append(s);
+  EXPECT_EQ(S::EncodedSize(), fb.size());
+  return fb.buffer();
+}
+
+// Randomize the members of out, in a manner that yields encodeable contents
+// (e.g. a "uint24" field has only the low 24 bits set).
+void Randomize(Http2FrameHeader* out, RandomBase* rng);
+void Randomize(Http2PriorityFields* out, RandomBase* rng);
+void Randomize(Http2RstStreamFields* out, RandomBase* rng);
+void Randomize(Http2SettingFields* out, RandomBase* rng);
+void Randomize(Http2PushPromiseFields* out, RandomBase* rng);
+void Randomize(Http2PingFields* out, RandomBase* rng);
+void Randomize(Http2GoAwayFields* out, RandomBase* rng);
+void Randomize(Http2WindowUpdateFields* out, RandomBase* rng);
+void Randomize(Http2AltSvcFields* out, RandomBase* rng);
+
+// Clear bits of header->flags that are known to be invalid for the
+// type. For unknown frame types, no change is made.
+void ScrubFlagsOfHeader(Http2FrameHeader* header);
+
+// Is the frame with this header padded? Only true for known/supported frame
+// types.
+bool FrameIsPadded(const Http2FrameHeader& header);
+
+// Does the frame with this header have Http2PriorityFields?
+bool FrameHasPriority(const Http2FrameHeader& header);
+
+// Does the frame with this header have a variable length payload (including
+// empty) payload (e.g. DATA or HEADERS)? Really a test of the frame type.
+bool FrameCanHavePayload(const Http2FrameHeader& header);
+
+// Does the frame with this header have a variable length HPACK payload
+// (including empty) payload (e.g. HEADERS)? Really a test of the frame type.
+bool FrameCanHaveHpackPayload(const Http2FrameHeader& header);
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_HTTP2_STRUCTURES_TEST_UTIL_H_
diff --git a/net/http2/tools/failure.cc b/net/http2/tools/failure.cc
new file mode 100644
index 0000000..b354be1e
--- /dev/null
+++ b/net/http2/tools/failure.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/failure.h"
+
+namespace net {
+namespace test {
+
+// This is a copy of the same named method in ::testing::internal.
+// TODO(jamessynge): See about getting something like VERIFY_* adopted by
+// gUnit (probably a very difficult task!).
+std::string GetBoolAssertionFailureMessage(
+    const ::testing::AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value) {
+  const char* actual_message = assertion_result.message();
+  ::testing::Message msg;
+  msg << "Value of: " << expression_text
+      << "\n  Actual: " << actual_predicate_value;
+  if (actual_message[0] != '\0')
+    msg << " (" << actual_message << ")";
+  msg << "\nExpected: " << expected_predicate_value;
+  return msg.GetString();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/failure.h b/net/http2/tools/failure.h
new file mode 100644
index 0000000..aa36d79
--- /dev/null
+++ b/net/http2/tools/failure.h
@@ -0,0 +1,154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_FAILURE_H_
+#define NET_HTTP2_TOOLS_FAILURE_H_
+
+// Defines VERIFY_* macros, analogous to gUnit's EXPECT_* and ASSERT_* macros,
+// but these return an appropriate AssertionResult if the condition is not
+// satisfied. This enables one to create a function for verifying expectations
+// that are needed by multiple callers or that rely on arguments not accessible
+// to the main test method. Using VERIFY_SUCCESS allows one to annotate the
+// a failing AssertionResult with more context.
+
+#include <iosfwd>
+#include <sstream>
+#include <string>
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+template <typename T>
+class VerifyThatHelper {
+ public:
+  VerifyThatHelper(const T& value, ::testing::Matcher<T> matcher) {
+    matches_ = matcher.Matches(value);
+    if (!matches_) {
+      printed_value_ = ::testing::PrintToString(value);
+
+      std::ostringstream os;
+      matcher.DescribeTo(&os);
+      matcher_description_ = os.str();
+    }
+  }
+
+  operator bool() const { return matches_; }
+
+  const std::string& printed_value() const { return printed_value_; }
+  const std::string& matcher_description() const {
+    return matcher_description_;
+  }
+
+ private:
+  bool matches_;
+  std::string printed_value_;
+  std::string matcher_description_;
+
+  DISALLOW_COPY_AND_ASSIGN(VerifyThatHelper);
+};
+
+// Constructs a failure message for Boolean assertions such as VERIFY_TRUE.
+std::string GetBoolAssertionFailureMessage(
+    const ::testing::AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value);
+
+}  // namespace test
+}  // namespace net
+
+// Macro for adding verification location to output stream or AssertionResult.
+// Starts with a new-line because of the way that gUnit displays failures for
+//    EXPECT_TRUE(CallToFunctionThatFailsToVerify()).
+#define VERIFY_FAILED_LOCATION_                   \
+  "\n"                                            \
+      << "(VERIFY failed in " << __func__ << "\n" \
+      << "               at " __FILE__ " : " << __LINE__ << ")\n"
+
+// Implements Boolean test verifications VERIFY_TRUE and VERIFY_FALSE.
+// text is a textual represenation of expression as it was passed into
+// VERIFY_TRUE or VERIFY_FALSE.
+// clang-format off
+#define VERIFY_TEST_BOOLEAN_(condition, text, actual, expected)        \
+  if (const ::testing::AssertionResult __assertion_result =            \
+          ::testing::AssertionResult((condition) ? expected : actual)) \
+    ;                                                                  \
+  else                                                                 \
+    return ::testing::AssertionFailure()                               \
+         << VERIFY_FAILED_LOCATION_                                    \
+         << ::net::test::GetBoolAssertionFailureMessage(              \
+                __assertion_result, text, #actual, #expected)
+// clang-format on
+
+// Boolean assertions. condition can be either a Boolean expression or an
+// expression convertable to a boolean (such as a ::gtl::labs::optional).
+#define VERIFY_TRUE(condition) \
+  VERIFY_TEST_BOOLEAN_(condition, #condition, false, true)
+
+#define VERIFY_FALSE(condition) \
+  VERIFY_TEST_BOOLEAN_(condition, #condition, true, false)
+
+// Convenient helper macro for writing methods that return an AssertionFailure
+// that includes the tested condition in the message (in the manner of
+// ASSERT_THAT and EXPECT_THAT).
+//
+// This macro avoids the do {} while(false) trick and putting braces around
+// the if so you can do things like:
+// VERIFY_THAT(foo, Lt(4)) << "foo too big in iteration " << i;
+// (This parallels the implementation of CHECK().)
+//
+// We use an if statement with an empty true branch so that this doesn't eat
+// a neighboring else when used in an unbraced if statement like:
+// if (condition)
+//   VERIFY_THAT(foo, Eq(bar));
+// else
+//   FAIL();
+#define VERIFY_THAT(value, matcher)                                       \
+  if (const auto& _verify_that_helper =                                   \
+          ::net::test::VerifyThatHelper<decltype(value)>(value, matcher)) \
+    ;                                                                     \
+  else                                                                    \
+    return ::testing::AssertionFailure()                                  \
+           << "Failed to verify that '" #value "' ("                      \
+           << _verify_that_helper.printed_value() << ") "                 \
+           << _verify_that_helper.matcher_description()                   \
+           << " (on " __FILE__ ":" << __LINE__ << "). "
+
+// Useful variants of VERIFY_THAT, similar to the corresponding EXPECT_X or
+// ASSERT_X defined by gUnit.
+#define VERIFY_EQ(val1, val2) VERIFY_THAT(val1, ::testing::Eq(val2))
+#define VERIFY_NE(val1, val2) VERIFY_THAT(val1, ::testing::Ne(val2))
+#define VERIFY_GT(val1, val2) VERIFY_THAT(val1, ::testing::Gt(val2))
+#define VERIFY_LT(val1, val2) VERIFY_THAT(val1, ::testing::Lt(val2))
+#define VERIFY_GE(val1, val2) VERIFY_THAT(val1, ::testing::Ge(val2))
+#define VERIFY_LE(val1, val2) VERIFY_THAT(val1, ::testing::Le(val2))
+
+// Convenience macro matching EXPECT_OK
+#define VERIFY_OK(statement) VERIFY_EQ(::util::Status::OK, (statement))
+
+// This version verifies that an expression of type AssertionResult is
+// AssertionSuccess. If instead the value is an AssertionFailure, it appends
+// info about the current code location to the failure's message and returns
+// the failure to the caller of the current method. It permits the code site
+// to append further messages to the failure message. For example:
+//    VERIFY_SUCCESS(SomeCall()) << "Some more context about SomeCall";
+// clang-format off
+#define VERIFY_SUCCESS(expr)                                  \
+  if (::testing::AssertionResult __assertion_result = (expr)) \
+    ;                                                         \
+  else                                                        \
+    return __assertion_result << VERIFY_FAILED_LOCATION_
+// clang-format on
+
+#define VERIFY_AND_RETURN_SUCCESS(expression) \
+  {                                           \
+    VERIFY_SUCCESS(expression);               \
+    return ::testing::AssertionSuccess();     \
+  }
+
+#endif  // NET_HTTP2_TOOLS_FAILURE_H_
diff --git a/net/http2/tools/http2_bug_tracker.h b/net/http2/tools/http2_bug_tracker.h
new file mode 100644
index 0000000..0e31bc2
--- /dev/null
+++ b/net/http2/tools/http2_bug_tracker.h
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
+#define NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
+
+#include "base/logging.h"
+
+#define HTTP2_BUG LOG(DFATAL)
+#define HTTP2_BUG_IF LOG_IF(DFATAL, (condition))
+#define FLAGS_http2_always_log_bugs_for_tests (true)
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_BUG_TRACKER_H_
diff --git a/net/http2/tools/http2_frame_builder.cc b/net/http2/tools/http2_frame_builder.cc
new file mode 100644
index 0000000..c072525
--- /dev/null
+++ b/net/http2/tools/http2_frame_builder.cc
@@ -0,0 +1,182 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/http2_frame_builder.h"
+
+#ifdef WIN32
+#include <winsock2.h>  // for htonl() functions
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>  // for htonl, htons
+#endif
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+Http2FrameBuilder::Http2FrameBuilder(Http2FrameType type,
+                                     uint8_t flags,
+                                     uint32_t stream_id) {
+  AppendUInt24(0);  // Frame payload length, unknown so far.
+  Append(type);
+  AppendUInt8(flags);
+  AppendUInt31(stream_id);
+}
+
+Http2FrameBuilder::Http2FrameBuilder(const Http2FrameHeader& v) {
+  Append(v);
+}
+
+void Http2FrameBuilder::Append(StringPiece s) {
+  s.AppendToString(&buffer_);
+}
+
+void Http2FrameBuilder::AppendBytes(const void* data, uint32_t num_bytes) {
+  Append(StringPiece(static_cast<const char*>(data), num_bytes));
+}
+
+void Http2FrameBuilder::AppendZeroes(size_t num_zero_bytes) {
+  char zero = 0;
+  buffer_.append(num_zero_bytes, zero);
+}
+
+void Http2FrameBuilder::AppendUInt8(uint8_t value) {
+  AppendBytes(&value, 1);
+}
+
+void Http2FrameBuilder::AppendUInt16(uint16_t value) {
+  value = htons(value);
+  AppendBytes(&value, 2);
+}
+
+void Http2FrameBuilder::AppendUInt24(uint32_t value) {
+  // Doesn't make sense to try to append a larger value, as that doesn't
+  // simulate something an encoder could do (i.e. the other 8 bits simply aren't
+  // there to be occupied).
+  EXPECT_EQ(value, value & 0xffffff);
+  value = htonl(value);
+  AppendBytes(reinterpret_cast<char*>(&value) + 1, 3);
+}
+
+void Http2FrameBuilder::AppendUInt31(uint32_t value) {
+  // If you want to test the high-bit being set, call AppendUInt32 instead.
+  uint32_t tmp = value & StreamIdMask();
+  EXPECT_EQ(value, value & StreamIdMask())
+      << "High-bit of uint32 should be clear.";
+  value = htonl(tmp);
+  AppendBytes(&value, 4);
+}
+
+void Http2FrameBuilder::AppendUInt32(uint32_t value) {
+  value = htonl(value);
+  AppendBytes(&value, sizeof(value));
+}
+
+void Http2FrameBuilder::Append(Http2ErrorCode error_code) {
+  AppendUInt32(static_cast<uint32_t>(error_code));
+}
+
+void Http2FrameBuilder::Append(Http2FrameType type) {
+  AppendUInt8(static_cast<uint8_t>(type));
+}
+
+void Http2FrameBuilder::Append(Http2SettingsParameter parameter) {
+  AppendUInt16(static_cast<uint16_t>(parameter));
+}
+
+void Http2FrameBuilder::Append(const Http2FrameHeader& v) {
+  AppendUInt24(v.payload_length);
+  Append(v.type);
+  AppendUInt8(v.flags);
+  AppendUInt31(v.stream_id);
+}
+
+void Http2FrameBuilder::Append(const Http2PriorityFields& v) {
+  // The EXCLUSIVE flag is the high-bit of the 32-bit stream dependency field.
+  uint32_t tmp = v.stream_dependency & StreamIdMask();
+  EXPECT_EQ(tmp, v.stream_dependency);
+  if (v.is_exclusive) {
+    tmp |= 0x80000000;
+  }
+  AppendUInt32(tmp);
+
+  // The PRIORITY frame's weight field is logically in the range [1, 256],
+  // but is encoded as a byte in the range [0, 255].
+  ASSERT_LE(1u, v.weight);
+  ASSERT_LE(v.weight, 256u);
+  AppendUInt8(v.weight - 1);
+}
+
+void Http2FrameBuilder::Append(const Http2RstStreamFields& v) {
+  Append(v.error_code);
+}
+
+void Http2FrameBuilder::Append(const Http2SettingFields& v) {
+  Append(v.parameter);
+  AppendUInt32(v.value);
+}
+
+void Http2FrameBuilder::Append(const Http2PushPromiseFields& v) {
+  AppendUInt31(v.promised_stream_id);
+}
+
+void Http2FrameBuilder::Append(const Http2PingFields& v) {
+  AppendBytes(v.opaque_data, sizeof Http2PingFields::opaque_data);
+}
+
+void Http2FrameBuilder::Append(const Http2GoAwayFields& v) {
+  AppendUInt31(v.last_stream_id);
+  Append(v.error_code);
+}
+
+void Http2FrameBuilder::Append(const Http2WindowUpdateFields& v) {
+  EXPECT_NE(0u, v.window_size_increment) << "Increment must be non-zero.";
+  AppendUInt31(v.window_size_increment);
+}
+
+void Http2FrameBuilder::Append(const Http2AltSvcFields& v) {
+  AppendUInt16(v.origin_length);
+}
+
+// Methods for changing existing buffer contents.
+
+void Http2FrameBuilder::WriteAt(StringPiece s, size_t offset) {
+  ASSERT_LE(offset, buffer_.size());
+  size_t len = offset + s.size();
+  if (len > buffer_.size()) {
+    buffer_.resize(len);
+  }
+  for (size_t ndx = 0; ndx < s.size(); ++ndx) {
+    buffer_[offset + ndx] = s[ndx];
+  }
+}
+
+void Http2FrameBuilder::WriteBytesAt(const void* data,
+                                     uint32_t num_bytes,
+                                     size_t offset) {
+  WriteAt(StringPiece(static_cast<const char*>(data), num_bytes), offset);
+}
+
+void Http2FrameBuilder::WriteUInt24At(uint32_t value, size_t offset) {
+  ASSERT_LT(value, static_cast<uint32_t>(1 << 24));
+  value = htonl(value);
+  WriteBytesAt(reinterpret_cast<char*>(&value) + 1, sizeof(value) - 1, offset);
+}
+
+void Http2FrameBuilder::SetPayloadLength(uint32_t payload_length) {
+  WriteUInt24At(payload_length, 0);
+}
+
+size_t Http2FrameBuilder::SetPayloadLength() {
+  EXPECT_GE(size(), Http2FrameHeader::EncodedSize());
+  uint32_t payload_length = size() - Http2FrameHeader::EncodedSize();
+  SetPayloadLength(payload_length);
+  return payload_length;
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/http2_frame_builder.h b/net/http2/tools/http2_frame_builder.h
new file mode 100644
index 0000000..c665c759
--- /dev/null
+++ b/net/http2/tools/http2_frame_builder.h
@@ -0,0 +1,100 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
+#define NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
+
+// Http2FrameBuilder builds wire-format HTTP/2 frames (or fragments thereof)
+// from components.
+//
+// For now, this is only intended for use in tests, and thus has EXPECT* in the
+// code. If desired to use it in an encoder, it will need optimization work,
+// especially w.r.t memory mgmt, and the EXPECT* will need to be removed or
+// replaced with DCHECKs.
+
+#include <stddef.h>  // for size_t
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/http2_structures.h"
+
+namespace net {
+namespace test {
+
+class Http2FrameBuilder {
+ public:
+  Http2FrameBuilder(Http2FrameType type, uint8_t flags, uint32_t stream_id);
+  explicit Http2FrameBuilder(const Http2FrameHeader& v);
+  Http2FrameBuilder() {}
+  ~Http2FrameBuilder() {}
+
+  size_t size() const { return buffer_.size(); }
+  const std::string& buffer() const { return buffer_; }
+
+  //----------------------------------------------------------------------------
+  // Methods for appending to the end of the buffer.
+
+  // Append a sequence of bytes from various sources.
+  void Append(base::StringPiece s);
+  void AppendBytes(const void* data, uint32_t num_bytes);
+
+  // Append an array of type T[N] to the string. Intended for tests with arrays
+  // initialized from literals, such as:
+  //    const char kData[] = {0x12, 0x23, ...};
+  //    builder.AppendBytes(kData);
+  template <typename T, size_t N>
+  void AppendBytes(T (&buf)[N]) {
+    AppendBytes(buf, N * sizeof(buf[0]));
+  }
+
+  // Support for appending padding. Does not read or write the Pad Length field.
+  void AppendZeroes(size_t num_zero_bytes);
+
+  // Append various sizes of unsigned integers.
+  void AppendUInt8(uint8_t value);
+  void AppendUInt16(uint16_t value);
+  void AppendUInt24(uint32_t value);
+  void AppendUInt31(uint32_t value);
+  void AppendUInt32(uint32_t value);
+
+  // Append various enums.
+  void Append(Http2ErrorCode error_code);
+  void Append(Http2FrameType type);
+  void Append(Http2SettingsParameter parameter);
+
+  // Append various structures.
+  void Append(const Http2FrameHeader& v);
+  void Append(const Http2PriorityFields& v);
+  void Append(const Http2RstStreamFields& v);
+  void Append(const Http2SettingFields& v);
+  void Append(const Http2PushPromiseFields& v);
+  void Append(const Http2PingFields& v);
+  void Append(const Http2GoAwayFields& v);
+  void Append(const Http2WindowUpdateFields& v);
+  void Append(const Http2AltSvcFields& v);
+
+  // Methods for changing existing buffer contents (mostly focused on updating
+  // the payload length).
+
+  void WriteAt(base::StringPiece s, size_t offset);
+  void WriteBytesAt(const void* data, uint32_t num_bytes, size_t offset);
+  void WriteUInt24At(uint32_t value, size_t offset);
+
+  // Set the payload length to the specified size.
+  void SetPayloadLength(uint32_t payload_length);
+
+  // Sets the payload length to the size of the buffer minus the size of
+  // the frame header.
+  size_t SetPayloadLength();
+
+ private:
+  std::string buffer_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_FRAME_BUILDER_H_
diff --git a/net/http2/tools/http2_random.cc b/net/http2/tools/http2_random.cc
new file mode 100644
index 0000000..8732d6a8
--- /dev/null
+++ b/net/http2/tools/http2_random.cc
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/http2_random.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/rand_util.h"
+
+namespace net {
+namespace test {
+
+bool Http2Random::OneIn(int n) {
+  return base::RandGenerator(n) == 0;
+}
+
+int32_t Http2Random::Uniform(int32_t n) {
+  return base::RandGenerator(n);
+}
+
+uint8_t Http2Random::Rand8() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint8_t>::max()) + 1);
+}
+
+uint16_t Http2Random::Rand16() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint16_t>::max()) + 1);
+}
+
+uint32_t Http2Random::Rand32() {
+  return base::RandGenerator(
+      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1);
+}
+
+uint64_t Http2Random::Rand64() {
+  return base::RandUint64();
+}
+
+int32_t Http2Random::Next() {
+  return Rand32();
+}
+
+int32_t Http2Random::Skewed(int max_log) {
+  const uint32_t base = Rand32() % (max_log + 1);
+  const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
+  return Rand32() & mask;
+}
+
+std::string Http2Random::RandString(int length) {
+  std::unique_ptr<char[]> buffer(new char[length]);
+  base::RandBytes(buffer.get(), length);
+  return std::string(buffer.get(), length);
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/http2_random.h b/net/http2/tools/http2_random.h
new file mode 100644
index 0000000..f7956cb8
--- /dev/null
+++ b/net/http2/tools/http2_random.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
+#define NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace net {
+namespace test {
+
+class RandomBase {
+ public:
+  virtual ~RandomBase() {}
+  virtual bool OneIn(int n) = 0;
+  virtual int32_t Uniform(int32_t n) = 0;
+  virtual uint8_t Rand8() = 0;
+  virtual uint16_t Rand16() = 0;
+  virtual uint32_t Rand32() = 0;
+  virtual uint64_t Rand64() = 0;
+  virtual int32_t Next() = 0;
+  virtual int32_t Skewed(int max_log) = 0;
+  virtual std::string RandString(int length) = 0;
+};
+
+class Http2Random : public RandomBase {
+ public:
+  ~Http2Random() override {}
+  bool OneIn(int n) override;
+  int32_t Uniform(int32_t n) override;
+  uint8_t Rand8() override;
+  uint16_t Rand16() override;
+  uint32_t Rand32() override;
+  uint64_t Rand64() override;
+  int32_t Next() override;
+  int32_t Skewed(int max_log) override;
+  std::string RandString(int length) override;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_HTTP2_RANDOM_H_
diff --git a/net/http2/tools/random_decoder_test.cc b/net/http2/tools/random_decoder_test.cc
new file mode 100644
index 0000000..8fd36743
--- /dev/null
+++ b/net/http2/tools/random_decoder_test.cc
@@ -0,0 +1,179 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http2/tools/random_decoder_test.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/http2_constants.h"
+#include "net/http2/tools/failure.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// It's rather time consuming to decode large buffers one at a time,
+// especially with the log level cranked up. So, by default we don't do
+// that unless explicitly requested.
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+std::string HexEncode(StringPiece s) {
+  return base::HexEncode(s.data(), s.size());
+}
+
+RandomDecoderTest::RandomDecoderTest() {}
+
+bool RandomDecoderTest::StopDecodeOnDone() {
+  return stop_decode_on_done_;
+}
+
+DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
+                                               const SelectSize& select_size) {
+  DecodeStatus status = DecodeStatus::kDecodeInProgress;
+  bool first = true;
+  VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
+  while (first || original->HasData()) {
+    size_t remaining = original->Remaining();
+    size_t size = std::min(
+        remaining, select_size.Run(first, original->Offset(), remaining));
+    DecodeBuffer db(original->cursor(), size);
+    VLOG(2) << "Decoding " << size << " bytes of " << remaining << " remaining";
+    if (first) {
+      first = false;
+      status = StartDecoding(&db);
+    } else {
+      status = ResumeDecoding(&db);
+    }
+    // A decoder MUST consume some input (if any is available), else we could
+    // get stuck in infinite loops.
+    if (db.Offset() == 0 && db.HasData() &&
+        status != DecodeStatus::kDecodeError) {
+      ADD_FAILURE() << "Decoder didn't make any progress; db.FullSize="
+                    << db.FullSize()
+                    << "   original.Offset=" << original->Offset();
+      return DecodeStatus::kDecodeError;
+    }
+    original->AdvanceCursor(db.Offset());
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        if (original->Empty() || StopDecodeOnDone()) {
+          return DecodeStatus::kDecodeDone;
+        }
+        continue;
+      case DecodeStatus::kDecodeInProgress:
+        continue;
+      case DecodeStatus::kDecodeError:
+        return DecodeStatus::kDecodeError;
+    }
+  }
+  return status;
+}
+
+// Decode |original| multiple times, with different segmentations, validating
+// after each decode, returning on the first failure.
+AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
+    DecodeBuffer* original,
+    bool return_non_zero_on_first,
+    const Validator& validator) {
+  const uint32_t original_remaining = original->Remaining();
+  VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
+          << original_remaining;
+  uint32_t first_consumed;
+  {
+    // Fast decode (no stopping unless decoder does so).
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&SelectRemaining), validator))
+        << "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    first_consumed = input.Offset();
+  }
+  if (original_remaining <= 30) {
+    // Decode again, one byte at a time.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
+    VERIFY_SUCCESS(
+        DecodeSegmentsAndValidate(&input, base::Bind(&SelectOne), validator))
+        << "\nFailed with SelectOne; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectOne";
+  }
+  if (original_remaining <= 20) {
+    // Decode again, one or zero bytes at a time.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
+    bool zero_next = !return_non_zero_on_first;
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&SelectZeroAndOne, &zero_next), validator))
+        << "\nFailed with SelectZeroAndOne";
+    VERIFY_EQ(first_consumed, input.Offset())
+        << "\nFailed with SelectZeroAndOne; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+  }
+  {
+    // Decode again, with randomly selected segment sizes.
+    DecodeBuffer input(original->cursor(), original_remaining);
+    VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
+    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
+        &input, base::Bind(&RandomDecoderTest::SelectRandom,
+                           base::Unretained(this), return_non_zero_on_first),
+        validator))
+        << "\nFailed with SelectRandom; input.Offset=" << input.Offset()
+        << "; input.Remaining=" << input.Remaining();
+    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectRandom";
+  }
+  VERIFY_EQ(original_remaining, original->Remaining());
+  original->AdvanceCursor(first_consumed);
+  VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
+  return ::testing::AssertionSuccess();
+}
+
+// static
+size_t RandomDecoderTest::SelectZeroAndOne(bool* zero_next,
+                                           bool first,
+                                           size_t offset,
+                                           size_t remaining) {
+  if (*zero_next) {
+    *zero_next = false;
+    return 0;
+  } else {
+    *zero_next = true;
+    return 1;
+  }
+}
+
+size_t RandomDecoderTest::SelectRandom(bool return_non_zero_on_first,
+                                       bool first,
+                                       size_t offset,
+                                       size_t remaining) {
+  uint32_t r = random_.Rand32();
+  if (first && return_non_zero_on_first) {
+    CHECK_LT(0u, remaining);
+    if (remaining == 1) {
+      return 1;
+    }
+    return 1 + (r % remaining);  // size in range [1, remaining).
+  }
+  return r % (remaining + 1);  // size in range [0, remaining].
+}
+
+uint32_t RandomDecoderTest::RandStreamId() {
+  return random_.Rand32() & StreamIdMask();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/http2/tools/random_decoder_test.h b/net/http2/tools/random_decoder_test.h
new file mode 100644
index 0000000..f3ab829
--- /dev/null
+++ b/net/http2/tools/random_decoder_test.h
@@ -0,0 +1,264 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
+#define NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
+
+// RandomDecoderTest is a base class for tests of decoding various kinds
+// of HTTP/2 and HPACK encodings.
+
+// TODO(jamessynge): Move more methods into .cc file.
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/template_util.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/http2/tools/failure.h"
+#include "net/http2/tools/http2_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Some helpers.
+
+template <typename T, size_t N>
+base::StringPiece ToStringPiece(T (&data)[N]) {
+  return base::StringPiece(reinterpret_cast<const char*>(data), N * sizeof(T));
+}
+
+// strings/hex_ascii_dump.h doesn't support StringPiece args for this case.
+std::string HexEncode(base::StringPiece s);
+
+// Overwrite the enum with some random value, probably not a valid value for
+// the enum type, but which fits into its storage.
+template <typename T,
+          typename E = typename std::enable_if<std::is_enum<T>::value>::type>
+void CorruptEnum(T* out, RandomBase* rng) {
+  // Per cppreference.com, if the destination type of a static_cast is
+  // smaller than the source type (i.e. type of r and uint32 below), the
+  // resulting value is the smallest unsigned value equal to the source value
+  // modulo 2^n, where n is the number of bits used to represent the
+  // destination type unsigned U.
+  typedef typename base::underlying_type<T>::type underlying_type_T;
+  typedef typename std::make_unsigned<underlying_type_T>::type
+      unsigned_underlying_type_T;
+  auto r = static_cast<unsigned_underlying_type_T>(rng->Rand32());
+  *out = static_cast<T>(r);
+}
+
+// Base class for tests of the ability to decode a sequence of bytes with
+// various boundaries between the DecodeBuffers provided to the decoder.
+class RandomDecoderTest : public ::testing::Test {
+ public:
+  // SelectSize returns the size of the next DecodeBuffer to be passed to the
+  // decoder. Note that RandomDecoderTest allows that size to be zero, though
+  // some decoders can't deal with that on the first byte, hence the |first|
+  // parameter.
+  typedef base::Callback<size_t(bool first, size_t offset, size_t remaining)>
+      SelectSize;
+
+  // Validator returns an AssertionResult so test can do:
+  // EXPECT_THAT(DecodeAndValidate(..., validator));
+  typedef ::testing::AssertionResult AssertionResult;
+  typedef base::Callback<AssertionResult(const DecodeBuffer& input,
+                                         DecodeStatus status)>
+      Validator;
+  typedef base::Callback<AssertionResult()> NoArgValidator;
+
+  RandomDecoderTest();
+
+ protected:
+  // TODO(jamessynge): Modify StartDecoding, etc. to (somehow) return
+  // AssertionResult so that the VERIFY_* methods exported from
+  // gunit_helpers.h can be widely used.
+
+  // Start decoding; call allows sub-class to Reset the decoder, or deal with
+  // the first byte if that is done in a unique fashion.  Might be called with
+  // a zero byte buffer.
+  virtual DecodeStatus StartDecoding(DecodeBuffer* db) = 0;
+
+  // Resume decoding of the input after a prior call to StartDecoding, and
+  // possibly many calls to ResumeDecoding.
+  virtual DecodeStatus ResumeDecoding(DecodeBuffer* db) = 0;
+
+  // Return true if a decode status of kDecodeDone indicates that
+  // decoding should stop.
+  virtual bool StopDecodeOnDone();
+
+  // Decode buffer |original| until we run out of input, or kDecodeDone is
+  // returned by the decoder AND StopDecodeOnDone() returns true. Segments
+  // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
+  // by calling |select_size| to decide how large each buffer should be.
+  // We do this to test the ability to deal with arbitrary boundaries, as might
+  // happen in transport.
+  // Returns the final DecodeStatus.
+  DecodeStatus DecodeSegments(DecodeBuffer* original,
+                              const SelectSize& select_size);
+
+  // Decode buffer |original| until we run out of input, or kDecodeDone is
+  // returned by the decoder AND StopDecodeOnDone() returns true. Segments
+  // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
+  // by calling |select_size| to decide how large each buffer should be.
+  // We do this to test the ability to deal with arbitrary boundaries, as might
+  // happen in transport.
+  // Invokes |validator| with the final decode status and the original decode
+  // buffer, with the cursor advanced as far as has been consumed by the decoder
+  // and returns validator's result.
+  ::testing::AssertionResult DecodeSegmentsAndValidate(
+      DecodeBuffer* original,
+      const SelectSize& select_size,
+      const Validator& validator) {
+    DecodeStatus status = DecodeSegments(original, select_size);
+    VERIFY_AND_RETURN_SUCCESS(validator.Run(*original, status));
+  }
+
+  // Returns a size for fast decoding, i.e. passing all that
+  // is available to the decoder.
+  static size_t SelectRemaining(bool first, size_t offset, size_t remaining) {
+    return remaining;
+  }
+
+  // Returns a size for decoding a single byte at a time.
+  static size_t SelectOne(bool first, size_t offset, size_t remaining) {
+    return 1;
+  }
+
+  // Returns a size for decoding a single byte at a time, where
+  // zero byte buffers are also allowed. Alternates between zero and one.
+  static size_t SelectZeroAndOne(bool* zero_next,
+                                 bool first,
+                                 size_t offset,
+                                 size_t remaining);
+
+  // Returns a size for decoding random sized segments.
+  size_t SelectRandom(bool return_non_zero_on_first,
+                      bool first,
+                      size_t offset,
+                      size_t remaining);
+
+  // Decode |original| multiple times, with different segmentations of the
+  // decode buffer, validating after each decode, and confirming that they
+  // each decode the same amount. Returns on the first failure, else returns
+  // success.
+  AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* original,
+                                               bool return_non_zero_on_first,
+                                               const Validator& validator);
+
+  static AssertionResult SucceedingValidator(const DecodeBuffer& input,
+                                             DecodeStatus status) {
+    return ::testing::AssertionSuccess();
+  }
+
+  static Validator ToValidator(const Validator& validator) { return validator; }
+
+  static AssertionResult RunNoArgValidator(const NoArgValidator& validator,
+                                           const DecodeBuffer& input,
+                                           DecodeStatus status) {
+    return validator.Run();
+  }
+
+  static Validator ToValidator(const NoArgValidator& validator) {
+    return base::Bind(&RunNoArgValidator, validator);
+  }
+
+  // Wraps a validator with another validator
+  // that first checks that the DecodeStatus is kDecodeDone and
+  // that the DecodeBuffer is empty.
+  // TODO(jamessynge): Replace this overload with the next, as using this method
+  // usually means that the wrapped function doesn't need to be passed the
+  // DecodeBuffer nor the DecodeStatus.
+  static AssertionResult ValidateDoneAndEmptyImpl(const Validator& wrapped,
+                                                  const DecodeBuffer& input,
+                                                  DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
+    return wrapped.Run(input, status);
+  }
+  static Validator ValidateDoneAndEmpty(const Validator& wrapped) {
+    return base::Bind(&ValidateDoneAndEmptyImpl, wrapped);
+  }
+  static AssertionResult ValidateDoneAndEmptyNoArgImpl(
+      const NoArgValidator& wrapped,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
+    return wrapped.Run();
+  }
+  static Validator ValidateDoneAndEmpty(const NoArgValidator& wrapped) {
+    return base::Bind(&ValidateDoneAndEmptyNoArgImpl, wrapped);
+  }
+  static Validator ValidateDoneAndEmpty() {
+    return ValidateDoneAndEmpty(base::Bind(&SucceedingValidator));
+  }
+
+  // Wraps a validator with another validator
+  // that first checks that the DecodeStatus is kDecodeDone and
+  // that the DecodeBuffer has the expected offset.
+  // TODO(jamessynge): Replace this overload with the next, as using this method
+  // usually means that the wrapped function doesn't need to be passed the
+  // DecodeBuffer nor the DecodeStatus.
+  static AssertionResult ValidateDoneAndOffsetImpl(uint32_t offset,
+                                                   const Validator& wrapped,
+                                                   const DecodeBuffer& input,
+                                                   DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
+    return wrapped.Run(input, status);
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset,
+                                         const Validator& wrapped) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return base::Bind(&ValidateDoneAndOffsetImpl, offset, wrapped);
+  }
+  static AssertionResult ValidateDoneAndOffsetNoArgImpl(
+      uint32_t offset,
+      const NoArgValidator& wrapped,
+      const DecodeBuffer& input,
+      DecodeStatus status) {
+    VERIFY_EQ(status, DecodeStatus::kDecodeDone);
+    VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
+    return wrapped.Run();
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset,
+                                         const NoArgValidator& wrapped) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return base::Bind(&ValidateDoneAndOffsetNoArgImpl, offset, wrapped);
+  }
+  static Validator ValidateDoneAndOffset(uint32_t offset) {
+    // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
+    // issues if this method is called with a temporary Validator.
+    return ValidateDoneAndOffset(offset, base::Bind(&SucceedingValidator));
+  }
+
+  // Expose |random_| as RandomBase so callers do not have to care about which
+  // sub-class of RandomBase is used, nor can they rely on the specific
+  // sub-class that RandomDecoderTest uses.
+  RandomBase& Random() { return random_; }
+  RandomBase* RandomPtr() { return &random_; }
+
+  uint32_t RandStreamId();
+
+  bool stop_decode_on_done_ = true;
+
+ private:
+  Http2Random random_;
+};
+
+}  // namespace test
+}  // namespace net
+
+#endif  // NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
diff --git a/net/net.gypi b/net/net.gypi
index e1ff808..419f532 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -784,6 +784,69 @@
       'http/url_security_manager.h',
       'http/url_security_manager_posix.cc',
       'http/url_security_manager_win.cc',
+      'http2/decoder/decode_buffer.cc',
+      'http2/decoder/decode_buffer.h',
+      'http2/decoder/decode_http2_structures.cc',
+      'http2/decoder/decode_http2_structures.h',
+      'http2/decoder/decode_status.cc',
+      'http2/decoder/decode_status.h',
+      'http2/decoder/frame_decoder_state.cc',
+      'http2/decoder/frame_decoder_state.h',
+      'http2/decoder/http2_frame_decoder.cc',
+      'http2/decoder/http2_frame_decoder.h',
+      'http2/decoder/http2_frame_decoder_listener.cc',
+      'http2/decoder/http2_frame_decoder_listener.h',
+      'http2/decoder/http2_structure_decoder.cc',
+      'http2/decoder/http2_structure_decoder.h',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder.cc',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder.h',
+      'http2/decoder/payload_decoders/continuation_payload_decoder.cc',
+      'http2/decoder/payload_decoders/continuation_payload_decoder.h',
+      'http2/decoder/payload_decoders/data_payload_decoder.cc',
+      'http2/decoder/payload_decoders/data_payload_decoder.h',
+      'http2/decoder/payload_decoders/goaway_payload_decoder.cc',
+      'http2/decoder/payload_decoders/goaway_payload_decoder.h',
+      'http2/decoder/payload_decoders/headers_payload_decoder.cc',
+      'http2/decoder/payload_decoders/headers_payload_decoder.h',
+      'http2/decoder/payload_decoders/ping_payload_decoder.cc',
+      'http2/decoder/payload_decoders/ping_payload_decoder.h',
+      'http2/decoder/payload_decoders/priority_payload_decoder.cc',
+      'http2/decoder/payload_decoders/priority_payload_decoder.h',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder.cc',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder.h',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder.cc',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder.h',
+      'http2/decoder/payload_decoders/settings_payload_decoder.cc',
+      'http2/decoder/payload_decoders/settings_payload_decoder.h',
+      'http2/decoder/payload_decoders/unknown_payload_decoder.cc',
+      'http2/decoder/payload_decoders/unknown_payload_decoder.h',
+      'http2/decoder/payload_decoders/window_update_payload_decoder.cc',
+      'http2/decoder/payload_decoders/window_update_payload_decoder.h',
+      'http2/hpack/decoder/hpack_block_decoder.cc',
+      'http2/hpack/decoder/hpack_block_decoder.h',
+      'http2/hpack/decoder/hpack_decoder_string_buffer.cc',
+      'http2/hpack/decoder/hpack_decoder_string_buffer.h',
+      'http2/hpack/decoder/hpack_entry_decoder.cc',
+      'http2/hpack/decoder/hpack_entry_decoder.h',
+      'http2/hpack/decoder/hpack_entry_decoder_listener.cc',
+      'http2/hpack/decoder/hpack_entry_decoder_listener.h',
+      'http2/hpack/decoder/hpack_entry_type_decoder.cc',
+      'http2/hpack/decoder/hpack_entry_type_decoder.h',
+      'http2/hpack/decoder/hpack_string_decoder.cc',
+      'http2/hpack/decoder/hpack_string_decoder.h',
+      'http2/hpack/decoder/hpack_string_decoder_listener.cc',
+      'http2/hpack/decoder/hpack_string_decoder_listener.h',
+      'http2/hpack/decoder/hpack_varint_decoder.cc',
+      'http2/hpack/decoder/hpack_varint_decoder.h',
+      'http2/hpack/http2_hpack_constants.cc',
+      'http2/hpack/http2_hpack_constants.h',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder.cc',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder.h',
+      'http2/http2_constants.cc',
+      'http2/http2_constants.h',
+      'http2/http2_structures.cc',
+      'http2/http2_structures.h',
+      'http2/tools/http2_bug_tracker.h',
       'nqe/cached_network_quality.cc',
       'nqe/cached_network_quality.h',
       'nqe/effective_connection_type.cc',
@@ -1256,6 +1319,8 @@
       'spdy/hpack/hpack_decoder.cc',
       'spdy/hpack/hpack_decoder.h',
       'spdy/hpack/hpack_decoder_interface.h',
+      'spdy/hpack/hpack_decoder2.cc',
+      'spdy/hpack/hpack_decoder2.h',
       'spdy/hpack/hpack_encoder.cc',
       'spdy/hpack/hpack_encoder.h',
       'spdy/hpack/hpack_entry.cc',
@@ -1680,6 +1745,68 @@
       'http/transport_security_persister_unittest.cc',
       'http/transport_security_state_unittest.cc',
       'http/url_security_manager_unittest.cc',
+      'http2/decoder/decode_buffer_test.cc',
+      'http2/decoder/decode_http2_structures_test.cc',
+      'http2/decoder/frame_decoder_state_test_util.cc',
+      'http2/decoder/frame_decoder_state_test_util.h',
+      'http2/decoder/frame_parts.cc',
+      'http2/decoder/frame_parts.h',
+      'http2/decoder/frame_parts_collector.cc',
+      'http2/decoder/frame_parts_collector.h',
+      'http2/decoder/frame_parts_collector_listener.cc',
+      'http2/decoder/frame_parts_collector_listener.h',
+      'http2/decoder/http2_frame_decoder_listener_test_util.cc',
+      'http2/decoder/http2_frame_decoder_listener_test_util.h',
+      'http2/decoder/http2_frame_decoder_test.cc',
+      'http2/decoder/http2_structure_decoder_test.cc',
+      'http2/decoder/http2_structure_decoder_test_util.h',
+      'http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/continuation_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/data_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/goaway_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/headers_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/payload_decoder_base_test_util.cc',
+      'http2/decoder/payload_decoders/payload_decoder_base_test_util.h',
+      'http2/decoder/payload_decoders/ping_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/priority_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/settings_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/unknown_payload_decoder_test.cc',
+      'http2/decoder/payload_decoders/window_update_payload_decoder_test.cc',
+      'http2/hpack/decoder/hpack_block_collector.cc',
+      'http2/hpack/decoder/hpack_block_collector.h',
+      'http2/hpack/decoder/hpack_block_decoder_test.cc',
+      'http2/hpack/decoder/hpack_decoder_string_buffer_test.cc',
+      'http2/hpack/decoder/hpack_entry_collector.cc',
+      'http2/hpack/decoder/hpack_entry_collector.h',
+      'http2/hpack/decoder/hpack_entry_decoder_test.cc',
+      'http2/hpack/decoder/hpack_entry_type_decoder_test.cc',
+      'http2/hpack/decoder/hpack_string_collector.cc',
+      'http2/hpack/decoder/hpack_string_collector.h',
+      'http2/hpack/decoder/hpack_string_decoder_test.cc',
+      'http2/hpack/decoder/hpack_varint_decoder_test.cc',
+      'http2/hpack/http2_hpack_constants_test.cc',
+      'http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc',
+      'http2/hpack/tools/hpack_block_builder.cc',
+      'http2/hpack/tools/hpack_block_builder.h',
+      'http2/hpack/tools/hpack_block_builder_test.cc',
+      'http2/hpack/tools/hpack_example.cc',
+      'http2/hpack/tools/hpack_example.h',
+      'http2/http2_constants_test.cc',
+      'http2/http2_constants_test_util.cc',
+      'http2/http2_constants_test_util.h',
+      'http2/http2_structures_test.cc',
+      'http2/http2_structures_test_util.cc',
+      'http2/http2_structures_test_util.h',
+      'http2/tools/failure.cc',
+      'http2/tools/failure.h',
+      'http2/tools/http2_frame_builder.cc',
+      'http2/tools/http2_frame_builder.h',
+      'http2/tools/http2_random.cc',
+      'http2/tools/http2_random.h',
+      'http2/tools/random_decoder_test.cc',
+      'http2/tools/random_decoder_test.h',
       'log/bounded_file_net_log_observer_unittest.cc',
       'log/net_log_capture_mode_unittest.cc',
       'log/net_log_unittest.cc',
@@ -1746,6 +1873,7 @@
       'quic/core/congestion_control/pacing_sender_test.cc',
       'quic/core/congestion_control/prr_sender_test.cc',
       'quic/core/congestion_control/rtt_stats_test.cc',
+      'quic/core/congestion_control/send_algorithm_test.cc',
       'quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc',
       'quic/core/congestion_control/tcp_cubic_sender_packets_test.cc',
       'quic/core/congestion_control/windowed_filter_test.cc',
@@ -1951,6 +2079,7 @@
       'spdy/fuzzing/hpack_fuzz_util_test.cc',
       'spdy/header_coalescer_test.cc',
       'spdy/hpack/hpack_decoder_test.cc',
+      'spdy/hpack/hpack_decoder2_test.cc',
       'spdy/hpack/hpack_encoder_test.cc',
       'spdy/hpack/hpack_entry_test.cc',
       'spdy/hpack/hpack_header_table_test.cc',
@@ -2055,6 +2184,7 @@
     'net_linux_test_sources': [
       'tools/quic/chlo_extractor_test.cc',
       'tools/quic/platform/impl/quic_epoll_clock_test.cc',
+      "tools/quic/platform/impl/quic_socket_utils_test.cc",
       'tools/quic/end_to_end_test.cc',
       'tools/quic/quic_client_session_test.cc',
       'tools/quic/quic_client_test.cc',
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 7e29eee..39cdda92 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -28,6 +28,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/network_interfaces.h"
+#include "net/base/trace_constants.h"
 #include "net/base/url_util.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -367,7 +368,7 @@
 }
 
 void NetworkQualityEstimator::NotifyHeadersReceived(const URLRequest& request) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
+  TRACE_EVENT0(kNetTracingCategory,
                "NetworkQualityEstimator::NotifyHeadersReceived");
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -534,7 +535,7 @@
 
 void NetworkQualityEstimator::NotifyRequestCompleted(const URLRequest& request,
                                                      int net_error) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
+  TRACE_EVENT0(kNetTracingCategory,
                "NetworkQualityEstimator::NotifyRequestCompleted");
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/net/proxy/proxy_resolver_v8_tracing.cc b/net/proxy/proxy_resolver_v8_tracing.cc
index 57cbfb0..1019e84 100644
--- a/net/proxy/proxy_resolver_v8_tracing.cc
+++ b/net/proxy/proxy_resolver_v8_tracing.cc
@@ -23,6 +23,7 @@
 #include "net/base/address_list.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_interfaces.h"
+#include "net/base/trace_constants.h"
 #include "net/dns/host_resolver.h"
 #include "net/log/net_log_with_source.h"
 #include "net/proxy/proxy_info.h"
@@ -562,7 +563,7 @@
 }
 
 int Job::ExecuteProxyResolver() {
-  TRACE_EVENT0("net", "Job::ExecuteProxyResolver");
+  TRACE_EVENT0(kNetTracingCategory, "Job::ExecuteProxyResolver");
   int result = ERR_UNEXPECTED;  // Initialized to silence warnings.
 
   switch (operation_) {
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 4175907c..a567ebc7 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -537,6 +537,7 @@
       QuicPacketNumber packet_number,
       bool fin,
       RequestPriority request_priority,
+      QuicStreamOffset* header_stream_offset,
       size_t* spdy_headers_frame_length,
       const std::vector<std::string>& data) {
     SpdyPriority priority =
@@ -544,7 +545,8 @@
     std::unique_ptr<QuicReceivedPacket> packet(
         client_maker_.MakeRequestHeadersAndMultipleDataFramesPacket(
             packet_number, stream_id_, kIncludeVersion, fin, priority,
-            std::move(request_headers_), spdy_headers_frame_length, data));
+            std::move(request_headers_), header_stream_offset,
+            spdy_headers_frame_length, data));
     DVLOG(2) << "packet(" << packet_number << "): " << std::endl
              << QuicUtils::HexDump(packet->AsStringPiece());
     return packet;
@@ -652,6 +654,15 @@
                                        !kIncludeCongestionFeedback);
   }
 
+  std::unique_ptr<QuicReceivedPacket> ConstructSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      QuicStreamOffset* offset) {
+    return client_maker_.MakeSettingsPacket(packet_number, id, value,
+                                            kIncludeVersion, offset);
+  }
+
   void ExpectLoadTimingValid(const LoadTimingInfo& load_timing_info,
                              bool session_reused) {
     EXPECT_EQ(session_reused, load_timing_info.socket_reused);
@@ -705,10 +716,15 @@
 TEST_P(BidirectionalStreamQuicImplTest, GetRequest) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));
 
-  AddWrite(ConstructClientAckPacket(2, 3, 1));
   Initialize();
 
   BidirectionalStreamRequestInfo request;
@@ -806,7 +822,9 @@
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   AddWrite(ConstructRequestHeadersPacketInner(
       2, kClientDataStreamId2, kFin, DEFAULT_PRIORITY, nullptr, &offset));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  AddWrite(ConstructSettingsPacket(3, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize, &offset));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
   Initialize();
 
   BidirectionalStreamRequestInfo request;
@@ -866,22 +884,26 @@
 TEST_P(BidirectionalStreamQuicImplTest, CoalesceDataBuffersNotHeadersFrame) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   const char kBody1[] = "here are some data";
   const char kBody2[] = "data keep coming";
   std::vector<std::string> two_writes = {kBody1, kBody2};
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientMultipleDataFramesPacket(2, kIncludeVersion, !kFin, 0,
+  AddWrite(ConstructRequestHeadersPacketInner(
+      2, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientMultipleDataFramesPacket(3, kIncludeVersion, !kFin, 0,
                                                    {kBody1, kBody2}));
   // Ack server's data packet.
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
   const char kBody3[] = "hello there";
   const char kBody4[] = "another piece of small data";
   const char kBody5[] = "really small";
   QuicStreamOffset data_offset = strlen(kBody1) + strlen(kBody2);
   AddWrite(ConstructClientMultipleDataFramesPacket(
-      4, !kIncludeVersion, kFin, data_offset, {kBody3, kBody4, kBody5}));
+      5, !kIncludeVersion, kFin, data_offset, {kBody3, kBody4, kBody5}));
 
   Initialize();
 
@@ -976,16 +998,19 @@
        SendDataCoalesceDataBufferAndHeaderFrame) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   const char kBody1[] = "here are some data";
   AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket(
-      1, !kFin, DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      {kBody1}));
+      2, !kFin, DEFAULT_PRIORITY, &header_stream_offset,
+      &spdy_request_headers_frame_length, {kBody1}));
   // Ack server's data packet.
-  AddWrite(ConstructClientAckPacket(2, 3, 1));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));
   const char kBody2[] = "really small";
   QuicStreamOffset data_offset = strlen(kBody1);
-  AddWrite(ConstructClientMultipleDataFramesPacket(3, !kIncludeVersion, kFin,
+  AddWrite(ConstructClientMultipleDataFramesPacket(4, !kIncludeVersion, kFin,
                                                    data_offset, {kBody2}));
 
   Initialize();
@@ -1070,21 +1095,24 @@
        SendvDataCoalesceDataBuffersAndHeaderFrame) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   const char kBody1[] = "here are some data";
   const char kBody2[] = "data keep coming";
   std::vector<std::string> two_writes = {kBody1, kBody2};
   AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket(
-      1, !kFin, DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
-      two_writes));
+      2, !kFin, DEFAULT_PRIORITY, &header_stream_offset,
+      &spdy_request_headers_frame_length, two_writes));
   // Ack server's data packet.
-  AddWrite(ConstructClientAckPacket(2, 3, 1));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));
   const char kBody3[] = "hello there";
   const char kBody4[] = "another piece of small data";
   const char kBody5[] = "really small";
   QuicStreamOffset data_offset = strlen(kBody1) + strlen(kBody2);
   AddWrite(ConstructClientMultipleDataFramesPacket(
-      3, !kIncludeVersion, kFin, data_offset, {kBody3, kBody4, kBody5}));
+      4, !kIncludeVersion, kFin, data_offset, {kBody3, kBody4, kBody5}));
 
   Initialize();
 
@@ -1172,11 +1200,16 @@
 TEST_P(BidirectionalStreamQuicImplTest, PostRequest) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData,
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, 0, kUploadData,
                                &client_maker_));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
 
   Initialize();
 
@@ -1327,11 +1360,17 @@
 TEST_P(BidirectionalStreamQuicImplTest, InterleaveReadDataAndSendData) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructAckAndDataPacket(2, !kIncludeVersion, 2, 1, !kFin, 0,
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructAckAndDataPacket(3, !kIncludeVersion, 2, 1, !kFin, 0,
                                      kUploadData, &client_maker_));
-  AddWrite(ConstructAckAndDataPacket(3, !kIncludeVersion, 3, 3, kFin,
+  AddWrite(ConstructAckAndDataPacket(4, !kIncludeVersion, 3, 3, kFin,
                                      strlen(kUploadData), kUploadData,
                                      &client_maker_));
   Initialize();
@@ -1412,8 +1451,13 @@
 TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterHeaders) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   Initialize();
 
   BidirectionalStreamRequestInfo request;
@@ -1450,10 +1494,15 @@
 TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterReadData) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   // Why does QUIC ack Rst? Is this expected?
-  AddWrite(ConstructClientAckPacket(2, 3, 1));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));
 
   Initialize();
 
@@ -1508,8 +1557,13 @@
 TEST_P(BidirectionalStreamQuicImplTest, SessionClosedBeforeReadData) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   Initialize();
 
   BidirectionalStreamRequestInfo request;
@@ -1565,9 +1619,14 @@
 TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamAfterReadData) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientAckAndRstStreamPacket(2, 2, 1, 1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientAckAndRstStreamPacket(3, 2, 1, 1));
 
   Initialize();
 
@@ -1616,9 +1675,14 @@
 TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnHeadersReceived) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientAckAndRstStreamPacket(2, 2, 1, 1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientAckAndRstStreamPacket(3, 2, 1, 1));
 
   Initialize();
 
@@ -1659,10 +1723,15 @@
 TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnDataRead) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientAckPacket(2, 3, 1));
-  AddWrite(ConstructClientRstStreamPacket(3));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructRequestHeadersPacketInner(
+      1, kClientDataStreamId1, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  AddWrite(ConstructClientRstStreamPacket(4));
 
   Initialize();
 
diff --git a/net/quic/chromium/crypto/proof_source_chromium.cc b/net/quic/chromium/crypto/proof_source_chromium.cc
index daf2c74..8e2c46a 100644
--- a/net/quic/chromium/crypto/proof_source_chromium.cc
+++ b/net/quic/chromium/crypto/proof_source_chromium.cc
@@ -77,7 +77,7 @@
 }
 
 bool ProofSourceChromium::GetProof(
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_addr,
     const string& hostname,
     const string& server_config,
     QuicVersion quic_version,
@@ -133,7 +133,7 @@
   return true;
 }
 
-void ProofSourceChromium::GetProof(const QuicIpAddress& server_ip,
+void ProofSourceChromium::GetProof(const QuicSocketAddress& server_addr,
                                    const std::string& hostname,
                                    const std::string& server_config,
                                    QuicVersion quic_version,
@@ -146,7 +146,7 @@
   string signature;
   string leaf_cert_sct;
   QuicCryptoProof out_proof;
-  const bool ok = GetProof(server_ip, hostname, server_config, quic_version,
+  const bool ok = GetProof(server_addr, hostname, server_config, quic_version,
                            chlo_hash, connection_options, &chain, &out_proof);
   callback->Run(ok, chain, out_proof, nullptr /* details */);
 }
diff --git a/net/quic/chromium/crypto/proof_source_chromium.h b/net/quic/chromium/crypto/proof_source_chromium.h
index f03000cf..341e45e2 100644
--- a/net/quic/chromium/crypto/proof_source_chromium.h
+++ b/net/quic/chromium/crypto/proof_source_chromium.h
@@ -33,7 +33,7 @@
                   const base::FilePath& sct_path);
 
   // ProofSource interface
-  bool GetProof(const QuicIpAddress& server_ip,
+  bool GetProof(const QuicSocketAddress& server_ip,
                 const std::string& hostname,
                 const std::string& server_config,
                 QuicVersion quic_version,
@@ -42,7 +42,7 @@
                 scoped_refptr<ProofSource::Chain>* out_chain,
                 QuicCryptoProof* proof) override;
 
-  void GetProof(const QuicIpAddress& server_ip,
+  void GetProof(const QuicSocketAddress& server_ip,
                 const std::string& hostname,
                 const std::string& server_config,
                 QuicVersion quic_version,
diff --git a/net/quic/chromium/crypto/proof_test_chromium.cc b/net/quic/chromium/crypto/proof_test_chromium.cc
index 78b4f2a..4d15e13a 100644
--- a/net/quic/chromium/crypto/proof_test_chromium.cc
+++ b/net/quic/chromium/crypto/proof_test_chromium.cc
@@ -139,14 +139,14 @@
   scoped_refptr<ProofSource::Chain> first_chain;
   string error_details;
   QuicCryptoProof proof, first_proof;
-  QuicIpAddress server_ip;
+  QuicSocketAddress server_addr;
 
-  ASSERT_TRUE(source->GetProof(server_ip, hostname, server_config, quic_version,
-                               first_chlo_hash, QuicTagVector(), &first_chain,
-                               &first_proof));
-  ASSERT_TRUE(source->GetProof(server_ip, hostname, server_config, quic_version,
-                               second_chlo_hash, QuicTagVector(), &chain,
-                               &proof));
+  ASSERT_TRUE(source->GetProof(server_addr, hostname, server_config,
+                               quic_version, first_chlo_hash, QuicTagVector(),
+                               &first_chain, &first_proof));
+  ASSERT_TRUE(source->GetProof(server_addr, hostname, server_config,
+                               quic_version, second_chlo_hash, QuicTagVector(),
+                               &chain, &proof));
 
   // Check that the proof source is caching correctly:
   ASSERT_EQ(first_chain->certs, chain->certs);
@@ -184,13 +184,13 @@
   const string first_chlo_hash = "first chlo hash bytes";
   const string second_chlo_hash = "first chlo hash bytes";
   const QuicVersion quic_version = GetParam();
-  QuicIpAddress server_ip;
+  QuicSocketAddress server_addr;
 
   // Call synchronous version
   scoped_refptr<ProofSource::Chain> expected_chain;
   QuicCryptoProof expected_proof;
-  ASSERT_TRUE(source->GetProof(server_ip, hostname, server_config, quic_version,
-                               first_chlo_hash, QuicTagVector(),
+  ASSERT_TRUE(source->GetProof(server_addr, hostname, server_config,
+                               quic_version, first_chlo_hash, QuicTagVector(),
                                &expected_chain, &expected_proof));
 
   // Call asynchronous version and compare results
@@ -200,7 +200,7 @@
   QuicCryptoProof proof;
   std::unique_ptr<ProofSource::Callback> cb(
       new TestCallback(&called, &ok, &chain, &proof));
-  source->GetProof(server_ip, hostname, server_config, quic_version,
+  source->GetProof(server_addr, hostname, server_config, quic_version,
                    first_chlo_hash, QuicTagVector(), std::move(cb));
   // TODO(gredner): whan GetProof really invokes the callback asynchronously,
   // figure out what to do here.
@@ -219,9 +219,9 @@
   scoped_refptr<ProofSource::Chain> chain;
   string error_details;
   QuicCryptoProof proof;
-  QuicIpAddress server_ip;
+  QuicSocketAddress server_addr;
 
-  ASSERT_TRUE(source->GetProof(server_ip, hostname, server_config, GetParam(),
+  ASSERT_TRUE(source->GetProof(server_addr, hostname, server_config, GetParam(),
                                chlo_hash, QuicTagVector(), &chain, &proof));
 
   // Make sure we can safely access results after deleting where they came from.
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc
index 0e73a00..d767cee7 100644
--- a/net/quic/chromium/quic_chromium_client_session_test.cc
+++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -181,16 +181,30 @@
                         ::testing::ValuesIn(AllSupportedVersions()));
 
 TEST_P(QuicChromiumClientSessionTest, CryptoConnect) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
   CompleteCryptoHandshake();
 }
 
 TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) {
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
 
@@ -223,10 +237,15 @@
 TEST_P(QuicChromiumClientSessionTest, PushStreamTimedOutNoResponse) {
   base::HistogramTester histogram_tester;
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kServerDataStreamId1, QUIC_PUSH_STREAM_TIMED_OUT));
+      2, true, kServerDataStreamId1, QUIC_PUSH_STREAM_TIMED_OUT));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
@@ -269,10 +288,15 @@
 TEST_P(QuicChromiumClientSessionTest, PushStreamTimedOutWithResponse) {
   base::HistogramTester histogram_tester;
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kServerDataStreamId1, QUIC_PUSH_STREAM_TIMED_OUT));
+      2, true, kServerDataStreamId1, QUIC_PUSH_STREAM_TIMED_OUT));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
@@ -317,11 +341,16 @@
 
 TEST_P(QuicChromiumClientSessionTest, CancelPushWhenPendingValidation) {
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
 
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
@@ -370,10 +399,15 @@
 TEST_P(QuicChromiumClientSessionTest, CancelPushBeforeReceivingResponse) {
   base::HistogramTester histogram_tester;
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
@@ -418,10 +452,15 @@
 TEST_P(QuicChromiumClientSessionTest, CancelPushAfterReceivingResponse) {
   base::HistogramTester histogram_tester;
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
   Initialize();
@@ -470,10 +509,15 @@
 
 TEST_P(QuicChromiumClientSessionTest, Priority) {
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
 
@@ -496,10 +540,15 @@
 
 TEST_P(QuicChromiumClientSessionTest, MaxNumStreamsViaRequest) {
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_rst(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
   MockWrite writes[] = {
-      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1)};
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1),
+      MockWrite(ASYNC, client_rst->data(), client_rst->length(), 2)};
   socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
                                              arraysize(writes)));
 
@@ -533,6 +582,15 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, GoAwayReceived) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
   CompleteCryptoHandshake();
 
@@ -544,6 +602,15 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, CanPool) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
   // Load a cert that is valid for:
   //   www.example.org
@@ -566,6 +633,15 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithTlsChannelId) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
   // Load a cert that is valid for:
   //   www.example.org
@@ -589,6 +665,15 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionNotPooledWithDifferentPin) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
 
   uint8_t primary_pin = 1;
@@ -615,6 +700,15 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithMatchingPin) {
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(reads, arraysize(reads), writes,
+                                             arraysize(writes)));
   Initialize();
 
   uint8_t primary_pin = 1;
@@ -640,16 +734,25 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, MigrateToSocket) {
+  MockRead old_reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite old_writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(
+      old_reads, arraysize(old_reads), old_writes, arraysize(old_writes)));
   Initialize();
   CompleteCryptoHandshake();
 
   char data[] = "ABCD";
   std::unique_ptr<QuicEncryptedPacket> client_ping(
-      client_maker_.MakePingPacket(1, /*include_version=*/false));
+      client_maker_.MakePingPacket(2, /*include_version=*/false));
   std::unique_ptr<QuicEncryptedPacket> server_ping(
       server_maker_.MakePingPacket(1, /*include_version=*/false));
   std::unique_ptr<QuicEncryptedPacket> ack_and_data_out(
-      client_maker_.MakeAckAndDataPacket(2, false, 5, 1, 1, false, 0,
+      client_maker_.MakeAckAndDataPacket(3, false, 5, 1, 1, false, 0,
                                          StringPiece(data)));
   MockRead reads[] = {
       MockRead(SYNCHRONOUS, server_ping->data(), server_ping->length(), 0),
@@ -661,7 +764,6 @@
   StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
                                        arraysize(writes));
   socket_factory_.AddSocketDataProvider(&socket_data);
-
   // Create connected socket.
   std::unique_ptr<DatagramClientSocket> new_socket =
       socket_factory_.CreateDatagramClientSocket(DatagramSocket::DEFAULT_BIND,
@@ -699,13 +801,22 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, MigrateToSocketMaxReaders) {
+  MockRead old_reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite old_writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  socket_data_.reset(new SequencedSocketData(
+      old_reads, arraysize(old_reads), old_writes, arraysize(old_writes)));
   Initialize();
   CompleteCryptoHandshake();
 
   for (size_t i = 0; i < kMaxReadersPerQuicSession; ++i) {
     MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 1)};
     std::unique_ptr<QuicEncryptedPacket> ping_out(
-        client_maker_.MakePingPacket(i + 1, /*include_version=*/true));
+        client_maker_.MakePingPacket(i + 2, /*include_version=*/true));
     MockWrite writes[] = {
         MockWrite(SYNCHRONOUS, ping_out->data(), ping_out->length(), i + 2)};
     StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
@@ -748,19 +859,23 @@
 }
 
 TEST_P(QuicChromiumClientSessionTest, MigrateToSocketReadError) {
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
   std::unique_ptr<QuicEncryptedPacket> client_ping(
-      client_maker_.MakePingPacket(1, /*include_version=*/false));
+      client_maker_.MakePingPacket(2, /*include_version=*/false));
   std::unique_ptr<QuicEncryptedPacket> server_ping(
       server_maker_.MakePingPacket(1, /*include_version=*/false));
+  MockWrite old_writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 0)};
   MockRead old_reads[] = {
-      MockRead(SYNCHRONOUS, client_ping->data(), client_ping->length(), 0),
       MockRead(ASYNC, ERR_IO_PENDING, 1),  // causes reading to pause.
       MockRead(ASYNC, ERR_NETWORK_CHANGED, 2)};
-  socket_data_.reset(
-      new SequencedSocketData(old_reads, arraysize(old_reads), nullptr, 0));
+  socket_data_.reset(new SequencedSocketData(
+      old_reads, arraysize(old_reads), old_writes, arraysize(old_writes)));
   Initialize();
   CompleteCryptoHandshake();
-
   MockWrite writes[] = {
       MockWrite(SYNCHRONOUS, client_ping->data(), client_ping->length(), 1)};
   MockRead new_reads[] = {
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc
index 3b940c5..d2ae8f8 100644
--- a/net/quic/chromium/quic_http_stream_test.cc
+++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -525,6 +525,15 @@
                                        !kIncludeCongestionFeedback);
   }
 
+  std::unique_ptr<QuicReceivedPacket> ConstructSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      QuicStreamOffset* offset) {
+    return client_maker_.MakeSettingsPacket(packet_number, id, value,
+                                            kIncludeVersion, offset);
+  }
+
   void ReceivePromise(QuicStreamId id) {
     auto headers = AsHeaderList(push_promise_);
     QuicChromiumClientStream* stream =
@@ -621,8 +630,14 @@
 TEST_P(QuicHttpStreamTest, GetRequest) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_header_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_header_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_header_frame_length, &header_stream_offset));
+
   Initialize();
 
   request_.method = "GET";
@@ -680,16 +695,18 @@
   size_t spdy_request_header_frame_length;
 
   QuicStreamOffset offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize, &offset));
   AddWrite(InnerConstructRequestHeadersPacket(
-      1, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
       &spdy_request_header_frame_length, &offset));
 
   // SetRequest() again for second request as |request_headers_| was moved.
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   AddWrite(InnerConstructRequestHeadersPacket(
-      2, kClientDataStreamId2, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      3, kClientDataStreamId2, kIncludeVersion, kFin, DEFAULT_PRIORITY,
       &spdy_request_header_frame_length, &offset));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));  // Ack the responses.
+  AddWrite(ConstructClientAckPacket(4, 3, 1));  // Ack the responses.
 
   Initialize();
 
@@ -761,9 +778,14 @@
 TEST_P(QuicHttpStreamTest, GetRequestWithTrailers) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_header_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_header_frame_length));
-  AddWrite(ConstructClientAckPacket(2, 3, 1));  // Ack the data packet.
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_header_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientAckPacket(3, 3, 1));  // Ack the data packet.
 
   Initialize();
 
@@ -852,8 +874,13 @@
 TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   Initialize();
 
   request_.method = "GET";
@@ -951,9 +978,14 @@
 TEST_P(QuicHttpStreamTest, LogGranularQuicConnectionError) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructAckAndRstStreamPacket(2));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructAckAndRstStreamPacket(3));
   use_closing_stream_ = true;
   Initialize();
 
@@ -986,9 +1018,14 @@
 TEST_P(QuicHttpStreamTest, DoNotLogGranularQuicErrorIfHandshakeNotConfirmed) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructAckAndRstStreamPacket(2));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructAckAndRstStreamPacket(3));
   use_closing_stream_ = true;
   Initialize();
 
@@ -1026,8 +1063,13 @@
 TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   Initialize();
 
   request_.method = "GET";
@@ -1055,10 +1097,15 @@
 TEST_P(QuicHttpStreamTest, SendPostRequest) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientDataPacket(2, kIncludeVersion, kFin, 0, kUploadData));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, kUploadData));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
 
   Initialize();
 
@@ -1121,13 +1168,18 @@
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t chunk_size = strlen(kUploadData);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   AddWrite(
-      ConstructClientDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
-  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size,
+      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
+  AddWrite(ConstructClientDataPacket(4, kIncludeVersion, kFin, chunk_size,
                                      kUploadData));
-  AddWrite(ConstructClientAckPacket(4, 3, 1));
+  AddWrite(ConstructClientAckPacket(5, 3, 1));
   Initialize();
 
   ChunkedUploadDataStream upload_data_stream(0);
@@ -1192,12 +1244,17 @@
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t chunk_size = strlen(kUploadData);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   AddWrite(
-      ConstructClientDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
-  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, chunk_size, ""));
-  AddWrite(ConstructClientAckPacket(4, 3, 1));
+      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
+  AddWrite(ConstructClientDataPacket(4, kIncludeVersion, kFin, chunk_size, ""));
+  AddWrite(ConstructClientAckPacket(5, 3, 1));
   Initialize();
 
   ChunkedUploadDataStream upload_data_stream(0);
@@ -1259,10 +1316,15 @@
 TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithOneEmptyDataPacket) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientDataPacket(2, kIncludeVersion, kFin, 0, ""));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientDataPacket(3, kIncludeVersion, kFin, 0, ""));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
   Initialize();
 
   ChunkedUploadDataStream upload_data_stream(0);
@@ -1323,9 +1385,14 @@
 TEST_P(QuicHttpStreamTest, DestroyedEarly) {
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructAckAndRstStreamPacket(2));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructAckAndRstStreamPacket(3));
   use_closing_stream_ = true;
   Initialize();
 
@@ -1363,9 +1430,14 @@
 TEST_P(QuicHttpStreamTest, Priority) {
   SetRequest("GET", "/", MEDIUM);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, kFin, MEDIUM,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructAckAndRstStreamPacket(2));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, kFin, MEDIUM,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructAckAndRstStreamPacket(3));
   use_closing_stream_ = true;
   Initialize();
 
@@ -1414,8 +1486,11 @@
 TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
   SetRequest("GET", "/", MEDIUM);
   use_closing_stream_ = true;
-
-  AddWrite(ConstructClientRstStreamPacket(1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientRstStreamPacket(2));
 
   Initialize();
 
@@ -1446,10 +1521,15 @@
 TEST_P(QuicHttpStreamTest, SessionClosedDuringDoLoop) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   AddWrite(
-      ConstructClientDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
+      ConstructClientDataPacket(3, kIncludeVersion, !kFin, 0, kUploadData));
   // Second data write will result in a synchronous failure which will close
   // the session.
   AddWrite(SYNCHRONOUS, ERR_FAILED);
@@ -1480,6 +1560,10 @@
 
 TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendHeadersComplete) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
   AddWrite(SYNCHRONOUS, ERR_FAILED);
   Initialize();
 
@@ -1501,8 +1585,13 @@
 TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendBodyComplete) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
   AddWrite(SYNCHRONOUS, ERR_FAILED);
   Initialize();
 
@@ -1889,12 +1978,16 @@
   request_headers_["accept-encoding"] = "sdch";
 
   size_t spdy_request_header_frame_length;
-  AddWrite(ConstructClientRstStreamVaryMismatchPacket(1));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(ConstructClientRstStreamVaryMismatchPacket(2));
   AddWrite(InnerConstructRequestHeadersPacket(
-      2, stream_id_ + 2, !kIncludeVersion, kFin, DEFAULT_PRIORITY,
-      &spdy_request_header_frame_length, /*offset=*/nullptr));
-  AddWrite(ConstructClientAckPacket(3, 3, 1));
-  AddWrite(ConstructClientRstStreamCancelledPacket(4));
+      3, stream_id_ + 2, !kIncludeVersion, kFin, DEFAULT_PRIORITY,
+      &spdy_request_header_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientAckPacket(4, 3, 1));
+  AddWrite(ConstructClientRstStreamCancelledPacket(5));
   Initialize();
 
   // Initialize the first stream, for receiving the promise on.
@@ -2000,9 +2093,14 @@
 TEST_P(QuicHttpStreamTest, DataReadErrorSynchronous) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientRstStreamErrorPacket(2, kIncludeVersion));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientRstStreamErrorPacket(3, kIncludeVersion));
 
   Initialize();
 
@@ -2032,9 +2130,14 @@
 TEST_P(QuicHttpStreamTest, DataReadErrorAsynchronous) {
   SetRequest("POST", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
-  AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
-                                         &spdy_request_headers_frame_length));
-  AddWrite(ConstructClientRstStreamErrorPacket(2, !kIncludeVersion));
+  QuicStreamOffset header_stream_offset = 0;
+  AddWrite(ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                   kDefaultMaxUncompressedHeaderSize,
+                                   &header_stream_offset));
+  AddWrite(InnerConstructRequestHeadersPacket(
+      2, kClientDataStreamId1, kIncludeVersion, !kFin, DEFAULT_PRIORITY,
+      &spdy_request_headers_frame_length, &header_stream_offset));
+  AddWrite(ConstructClientRstStreamErrorPacket(3, !kIncludeVersion));
 
   Initialize();
 
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index a072765..7f6a544 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -273,6 +273,14 @@
   }
 
   std::unique_ptr<QuicEncryptedPacket> ConstructClientAckPacket(
+      QuicPacketNumber packet_number,
+      QuicPacketNumber largest_received,
+      QuicPacketNumber least_unacked) {
+    return client_maker_.MakeAckPacket(packet_number, largest_received,
+                                       least_unacked, least_unacked, true);
+  }
+
+  std::unique_ptr<QuicEncryptedPacket> ConstructClientAckPacket(
       QuicPacketNumber largest_received,
       QuicPacketNumber least_unacked) {
     return client_maker_.MakeAckPacket(2, largest_received, least_unacked,
@@ -334,6 +342,23 @@
                                        error_code);
   }
 
+  std::unique_ptr<QuicReceivedPacket> ConstructSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      QuicStreamOffset* offset) {
+    return client_maker_.MakeSettingsPacket(packet_number, id, value,
+                                            kIncludeVersion, offset);
+  }
+
+  std::unique_ptr<QuicReceivedPacket> ConstructServerAckPacket(
+      QuicPacketNumber packet_number,
+      QuicPacketNumber largest_received,
+      QuicPacketNumber least_unacked) {
+    return server_maker_.MakeAckPacket(packet_number, largest_received,
+                                       least_unacked, false);
+  }
+
   // Uses default QuicTestPacketMaker.
   SpdyHeaderBlock GetRequestHeaders(const std::string& method,
                                     const std::string& scheme,
@@ -735,14 +760,18 @@
       HostPortPair::FromString("mail.example.org:443"));
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -800,14 +829,18 @@
   AddQuicAlternateProtocolMapping(MockCryptoClientStream::CONFIRM_HANDSHAKE);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -825,14 +858,18 @@
       ProxyService::CreateFixedFromPacResult("QUIC mail.example.org:70");
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "http", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "http", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -864,14 +901,18 @@
 
   client_maker_.set_hostname(origin_host);
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "http", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "http", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -917,14 +958,18 @@
 
   client_maker_.set_hostname(origin.host());
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -943,9 +988,16 @@
       HostPortPair::FromString("mail.example.org:443"));
 
   MockQuicData mock_quic_data1;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data1.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data1.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
-
   MockQuicData mock_quic_data2;
+  header_stream_offset = 0;
+  mock_quic_data2.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_);
   mock_quic_data2.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_);
@@ -1000,14 +1052,18 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1035,14 +1091,18 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1136,14 +1196,18 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1158,9 +1222,13 @@
 
 TEST_P(QuicNetworkTransactionTest, GoAwayWithConnectionMigrationOnPortsOnly) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   // Read a GoAway packet with
@@ -1168,11 +1236,11 @@
   mock_quic_data.AddRead(ConstructServerGoAwayPacket(
       2, QUIC_ERROR_MIGRATING_PORT,
       "connection migration with port change only"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ConstructServerDataPacket(3, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
   mock_quic_data.AddWrite(ConstructClientAckAndRstPacket(
-      3, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 3, 3, 1));
+      4, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 3, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1263,8 +1331,11 @@
   // Open a session to foo.example.org:443 using the first entry of the
   // alternative service list.
   MockQuicData mock_quic_data;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
+      2, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
 
   std::string alt_svc_list =
@@ -1275,13 +1346,13 @@
       GetResponseHeaders("200 OK", alt_svc_list), &response_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
 
   // Second QUIC request data.
   // Connection pooling, using existing session, no need to include version
   // as version negotiation has been completed.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, true,
+      4, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kClientDataStreamId2, false, false, GetResponseHeaders("200 OK"),
@@ -1289,7 +1360,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(4, kClientDataStreamId2,
                                                    false, true, 0, "hello!"));
   mock_quic_data.AddWrite(
-      ConstructClientAckAndConnectionClosePacket(4, 4, 3, 1));
+      ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1315,8 +1386,11 @@
   // Open a session to foo.example.org:443 using the first entry of the
   // alternative service list.
   MockQuicData mock_quic_data;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
+      2, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "http", "/"), &request_header_offset));
 
   std::string alt_svc_list;
@@ -1325,13 +1399,13 @@
       GetResponseHeaders("200 OK", alt_svc_list), &response_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
 
   // Second QUIC request data.
   // Connection pooling, using existing session, no need to include version
   // as version negotiation has been completed.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, true,
+      4, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "http", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kClientDataStreamId2, false, false, GetResponseHeaders("200 OK"),
@@ -1339,7 +1413,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(4, kClientDataStreamId2,
                                                    false, true, 0, "hello!"));
   mock_quic_data.AddWrite(
-      ConstructClientAckAndConnectionClosePacket(4, 4, 3, 1));
+      ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1379,20 +1453,23 @@
   QuicStreamOffset request_header_offset(0);
   QuicStreamOffset response_header_offset(0);
 
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset));
   // First request.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
+      2, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"),
       &response_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
 
   // Second request.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, true,
+      4, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kClientDataStreamId2, false, false, GetResponseHeaders("200 OK"),
@@ -1400,7 +1477,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(4, kClientDataStreamId2,
                                                    false, true, 0, "hello!"));
   mock_quic_data.AddWrite(
-      ConstructClientAckAndConnectionClosePacket(4, 4, 3, 1));
+      ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1445,16 +1522,20 @@
   QuicStreamOffset request_header_offset(0);
   QuicStreamOffset response_header_offset(0);
 
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset));
+
   // First request.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
+      2, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"),
       &response_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
 
   // Second request.
   QuicTestPacketMaker client_maker2(version_, 0, clock_, origin2.host(),
@@ -1462,7 +1543,7 @@
   QuicTestPacketMaker server_maker2(version_, 0, clock_, origin2.host(),
                                     Perspective::IS_SERVER);
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, true,
+      4, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "https", "/", &client_maker2),
       &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
@@ -1471,7 +1552,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(4, kClientDataStreamId2,
                                                    false, true, 0, "hello!"));
   mock_quic_data.AddWrite(
-      ConstructClientAckAndConnectionClosePacket(4, 4, 3, 1));
+      ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1556,10 +1637,12 @@
   server_maker_.set_hostname("www.example.org");
   client_maker_.set_hostname("www.example.org");
   MockQuicData mock_quic_data;
-
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset));
   // First QUIC request data.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
+      2, kClientDataStreamId1, true, true,
       GetRequestHeaders("GET", "https", "/"), &request_header_offset));
 
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
@@ -1567,10 +1650,10 @@
       &response_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(
       2, kClientDataStreamId1, false, true, 0, "hello from mail QUIC!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   // Second QUIC request data.
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, true,
+      4, kClientDataStreamId2, false, true,
       GetRequestHeaders("GET", "https", "/", &client_maker),
       &request_header_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
@@ -1579,7 +1662,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(
       4, kClientDataStreamId2, false, true, 0, "hello from mail QUIC!"));
   mock_quic_data.AddWrite(
-      ConstructClientAckAndConnectionClosePacket(4, 4, 3, 1));
+      ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1645,14 +1728,18 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1690,14 +1777,18 @@
   socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, 0);  // EOF
 
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -1717,14 +1808,18 @@
       ProxyService::CreateFixedFromPacResult("HTTPS mail.example.org:443");
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "http", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "http", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -1813,9 +1908,10 @@
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
       1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
@@ -1905,14 +2001,18 @@
 
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -1954,9 +2054,13 @@
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorLocal) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   // Read a close connection packet with
   // QuicErrorCode: QUIC_CRYPTO_VERSION_NOT_SUPPORTED from the peer.
   mock_quic_data.AddRead(ConstructServerConnectionClosePacket(1));
@@ -2004,16 +2108,20 @@
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorRemote) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   // Peer sending data from an non-existing stream causes this end to raise
   // error and close connection.
   mock_quic_data.AddRead(
       ConstructServerRstPacket(1, false, 99, QUIC_STREAM_LAST_ERROR));
   std::string quic_error_details = "Data for nonexistent stream";
   mock_quic_data.AddWrite(ConstructClientAckAndConnectionClosePacket(
-      2, QuicTime::Delta::Infinite(), 0, 1, QUIC_INVALID_STREAM_ID,
+      3, QuicTime::Delta::Infinite(), 0, 1, QUIC_INVALID_STREAM_ID,
       quic_error_details));
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -2056,15 +2164,19 @@
 
 TEST_P(QuicNetworkTransactionTest, RstSteamErrorHandling) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   // Read the response headers, then a RST_STREAM frame.
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerRstPacket(
       2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more read data.
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -2115,9 +2227,13 @@
 
 TEST_P(QuicNetworkTransactionTest, RstSteamBeforeHeaders) {
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerRstPacket(
       1, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more read data.
@@ -2455,14 +2571,18 @@
   EXPECT_FALSE(
       test_socket_performance_watcher_factory_.rtt_notification_received());
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
   mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more read data.
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -2565,8 +2685,11 @@
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   QuicStreamOffset offset = 0;
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, &offset));
   socket_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, false,
+      2, kClientDataStreamId1, true, false,
       GetRequestHeaders("POST", "https", "/"), &offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(&socket_factory_);
@@ -2606,9 +2729,13 @@
       HostPortPair::FromString("mail.example.org:443"));
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true,
-      GetRequestHeaders("GET", "https", "/")));
+      2, kClientDataStreamId1, true, true,
+      GetRequestHeaders("GET", "https", "/"), &header_stream_offset));
   QuicStreamOffset server_header_offset = 0;
   mock_quic_data.AddRead(ConstructServerPushPromisePacket(
       1, kClientDataStreamId1, kServerDataStreamId1, false,
@@ -2617,17 +2744,17 @@
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       2, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK"),
       &server_header_offset));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 2, 1, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kServerDataStreamId1, false, false, GetResponseHeaders("200 OK"),
       &server_header_offset));
   mock_quic_data.AddRead(ConstructServerDataPacket(4, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 4, 3, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(4, 4, 3, 1));
   mock_quic_data.AddRead(ConstructServerDataPacket(
       5, kServerDataStreamId1, false, true, 0, "and hello!"));
   mock_quic_data.AddWrite(ConstructClientAckAndRstPacket(
-      4, kServerDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT, 5, 5, 1));
+      5, kServerDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT, 5, 5, 1));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
   mock_quic_data.AddSocketDataToFactory(&socket_factory_);
@@ -2665,17 +2792,20 @@
   MockQuicData mock_quic_data;
 
   QuicStreamOffset offset = 0;
+  mock_quic_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, &offset));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, false,
+      2, kClientDataStreamId1, true, false,
       GetRequestHeaders("POST", "https", "/"), &offset));
 
   std::unique_ptr<QuicEncryptedPacket> packet;
   if (version_ > QUIC_VERSION_35) {
-    packet = ConstructClientForceHolDataPacket(2, kClientDataStreamId1, true,
+    packet = ConstructClientForceHolDataPacket(3, kClientDataStreamId1, true,
                                                true, &offset, "1");
   } else {
     packet =
-        ConstructClientDataPacket(2, kClientDataStreamId1, true, true, 0, "1");
+        ConstructClientDataPacket(3, kClientDataStreamId1, true, true, 0, "1");
   }
   mock_quic_data.AddWrite(std::move(packet));
 
@@ -2685,7 +2815,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
                                                    false, true, 0, "hello!"));
 
-  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(4, 2, 1, 1));
 
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
@@ -2743,11 +2873,16 @@
       HostPortPair::FromString("mail.example.org:443"));
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   SpdyHeaderBlock headers(GetRequestHeaders("GET", "https", "/"));
   headers["user-agent"] = "";
   headers["accept-encoding"] = "gzip, deflate";
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true, std::move(headers)));
+      2, kClientDataStreamId1, true, true, std::move(headers),
+      &header_stream_offset));
 
   QuicStreamOffset expected_raw_header_response_size = 0;
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
@@ -2756,7 +2891,7 @@
 
   mock_quic_data.AddRead(ConstructServerDataPacket(
       2, kClientDataStreamId1, false, true, 0, "Main Resource Data"));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1));
 
   mock_quic_data.AddRead(ASYNC, 0);  // EOF
 
@@ -2796,11 +2931,16 @@
       HostPortPair::FromString("mail.example.org:443"));
 
   MockQuicData mock_quic_data;
+  QuicStreamOffset header_stream_offset = 0;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   SpdyHeaderBlock headers(GetRequestHeaders("GET", "https", "/"));
   headers["user-agent"] = "";
   headers["accept-encoding"] = "gzip, deflate";
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, true, std::move(headers)));
+      2, kClientDataStreamId1, true, true, std::move(headers),
+      &header_stream_offset));
 
   QuicStreamOffset server_header_offset = 0;
   QuicStreamOffset expected_raw_header_response_size = 0;
@@ -2817,7 +2957,7 @@
   expected_raw_header_response_size =
       server_header_offset - expected_raw_header_response_size;
 
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 2, 1, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1));
 
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kServerDataStreamId1, false, false, GetResponseHeaders("200 OK"),
@@ -2825,7 +2965,7 @@
   mock_quic_data.AddRead(ConstructServerDataPacket(
       4, kServerDataStreamId1, false, true, 0, "Pushed Resource Data"));
 
-  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 4, 3, 1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(4, 4, 3, 1));
   mock_quic_data.AddRead(ConstructServerDataPacket(
       5, kClientDataStreamId1, false, true, 0, "Main Resource Data"));
 
@@ -3005,6 +3145,16 @@
                                 ack_least_unacked, stop_least_unacked, true);
   }
 
+  std::unique_ptr<QuicReceivedPacket> ConstructSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      QuicStreamOffset* offset,
+      QuicTestPacketMaker* maker) {
+    return maker->MakeSettingsPacket(packet_number, id, value, kIncludeVersion,
+                                     offset);
+  }
+
   void AddRefusedSocketData() {
     std::unique_ptr<StaticSocketDataProvider> refused_data(
         new StaticSocketDataProvider());
@@ -3166,13 +3316,16 @@
   QuicStreamOffset response_header_offset(0);
 
   MockQuicData mock_quic_data;
+  mock_quic_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &request_header_offset, &client_maker1));
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, &request_header_offset, &client_maker1));
+      2, kClientDataStreamId1, true, &request_header_offset, &client_maker1));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, &response_header_offset, &server_maker1));
   mock_quic_data.AddRead(
       ConstructServerDataPacket(2, kClientDataStreamId1, &server_maker1));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(2, 2, 1, 1, &client_maker1));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(3, 2, 1, 1, &client_maker1));
 
   QuicTestPacketMaker client_maker2(version_, 0, clock_, origin2_,
                                     Perspective::IS_CLIENT);
@@ -3180,12 +3333,12 @@
                                     Perspective::IS_SERVER);
 
   mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
-      3, kClientDataStreamId2, false, &request_header_offset, &client_maker2));
+      4, kClientDataStreamId2, false, &request_header_offset, &client_maker2));
   mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
       3, kClientDataStreamId2, &response_header_offset, &server_maker2));
   mock_quic_data.AddRead(
       ConstructServerDataPacket(4, kClientDataStreamId2, &server_maker2));
-  mock_quic_data.AddWrite(ConstructClientAckPacket(4, 4, 3, 1, &client_maker2));
+  mock_quic_data.AddWrite(ConstructClientAckPacket(5, 4, 3, 1, &client_maker2));
   mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data.AddRead(ASYNC, 0);               // EOF
 
@@ -3240,14 +3393,18 @@
                                     Perspective::IS_SERVER);
 
   MockQuicData mock_quic_data1;
+  QuicStreamOffset header_stream_offset1 = 0;
+  mock_quic_data1.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset1, &client_maker1));
   mock_quic_data1.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, &client_maker1));
+      2, kClientDataStreamId1, true, &header_stream_offset1, &client_maker1));
   mock_quic_data1.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, &server_maker1));
   mock_quic_data1.AddRead(
       ConstructServerDataPacket(2, kClientDataStreamId1, &server_maker1));
   mock_quic_data1.AddWrite(
-      ConstructClientAckPacket(2, 2, 1, 1, &client_maker1));
+      ConstructClientAckPacket(3, 2, 1, 1, &client_maker1));
   mock_quic_data1.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data1.AddRead(ASYNC, 0);               // EOF
 
@@ -3261,14 +3418,18 @@
                                     Perspective::IS_SERVER);
 
   MockQuicData mock_quic_data2;
+  QuicStreamOffset header_stream_offset2 = 0;
+  mock_quic_data2.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset2, &client_maker2));
   mock_quic_data2.AddWrite(ConstructClientRequestHeadersPacket(
-      1, kClientDataStreamId1, true, &client_maker2));
+      2, kClientDataStreamId1, true, &header_stream_offset2, &client_maker2));
   mock_quic_data2.AddRead(ConstructServerResponseHeadersPacket(
       1, kClientDataStreamId1, &server_maker2));
   mock_quic_data2.AddRead(
       ConstructServerDataPacket(2, kClientDataStreamId1, &server_maker2));
   mock_quic_data2.AddWrite(
-      ConstructClientAckPacket(2, 2, 1, 1, &client_maker2));
+      ConstructClientAckPacket(3, 2, 1, 1, &client_maker2));
   mock_quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // No more data to read
   mock_quic_data2.AddRead(ASYNC, 0);               // EOF
 
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index 0563a537..e9e1598 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -25,6 +25,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_delegate.h"
+#include "net/base/trace_constants.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_verifier.h"
 #include "net/dns/host_resolver.h"
@@ -400,7 +401,7 @@
 }
 
 int QuicStreamFactory::Job::DoLoop(int rv) {
-  TRACE_EVENT0("net", "QuicStreamFactory::Job::DoLoop");
+  TRACE_EVENT0(kNetTracingCategory, "QuicStreamFactory::Job::DoLoop");
   do {
     IoState state = io_state_;
     io_state_ = STATE_NONE;
@@ -1612,7 +1613,7 @@
 
     need_to_evaluate_consecutive_disabled_count_ = false;
   }
-  TRACE_EVENT0("net", "QuicStreamFactory::CreateSession");
+  TRACE_EVENT0(kNetTracingCategory, "QuicStreamFactory::CreateSession");
   IPEndPoint addr = *address_list.begin();
   const QuicServerId& server_id = key.server_id();
   DatagramSocket::BindType bind_type = DatagramSocket::DEFAULT_BIND;
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index b88bc9e..c8c651ce 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -352,6 +352,9 @@
 
     MockQuicData socket_data;
     socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+    socket_data.AddWrite(
+        ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                kDefaultMaxUncompressedHeaderSize, nullptr));
     socket_data.AddSocketDataToFactory(&socket_factory_);
 
     QuicStreamRequest request(factory_.get());
@@ -396,6 +399,13 @@
                                        QUIC_RST_ACKNOWLEDGEMENT);
   }
 
+  std::unique_ptr<QuicEncryptedPacket> ConstructClientRstPacket(
+      QuicPacketNumber packet_number) {
+    QuicStreamId stream_id = kClientDataStreamId1;
+    return client_maker_.MakeRstPacket(packet_number, true, stream_id,
+                                       QUIC_RST_ACKNOWLEDGEMENT);
+  }
+
   static ProofVerifyDetailsChromium DefaultProofVerifyDetails() {
     // Load a certificate that is valid for *.example.org
     scoped_refptr<X509Certificate> test_cert(
@@ -428,6 +438,22 @@
         std::move(headers), &spdy_headers_frame_len);
   }
 
+  std::unique_ptr<QuicEncryptedPacket> ConstructGetRequestPacket(
+      QuicPacketNumber packet_number,
+      QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      QuicStreamOffset* offset) {
+    SpdyHeaderBlock headers =
+        client_maker_.GetRequestHeaders("GET", "https", "/");
+    SpdyPriority priority =
+        ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
+    size_t spdy_headers_frame_len;
+    return client_maker_.MakeRequestHeadersPacket(
+        packet_number, stream_id, should_include_version, fin, priority,
+        std::move(headers), &spdy_headers_frame_len, offset);
+  }
+
   std::unique_ptr<QuicEncryptedPacket> ConstructOkResponsePacket(
       QuicPacketNumber packet_number,
       QuicStreamId stream_id,
@@ -440,6 +466,15 @@
         std::move(headers), &spdy_headers_frame_len);
   }
 
+  std::unique_ptr<QuicReceivedPacket> ConstructSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      QuicStreamOffset* offset) {
+    return client_maker_.MakeSettingsPacket(packet_number, id, value,
+                                            kIncludeVersion, offset);
+  }
+
   // Helper method for server migration tests.
   void VerifyServerMigration(QuicConfig& config, IPEndPoint expected_address) {
     allow_server_migration_ = true;
@@ -459,9 +494,12 @@
     MockQuicData socket_data2;
     socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
     socket_data2.AddWrite(
-        client_maker_.MakePingPacket(1, /*include_version=*/true));
+        ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                kDefaultMaxUncompressedHeaderSize, nullptr));
+    socket_data2.AddWrite(
+        client_maker_.MakePingPacket(2, /*include_version=*/true));
     socket_data2.AddWrite(client_maker_.MakeRstPacket(
-        2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+        3, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
     socket_data2.AddSocketDataToFactory(&socket_factory_);
 
     // Create request and QuicHttpStream.
@@ -773,6 +811,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -861,6 +902,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -890,6 +934,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -928,6 +975,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server2(kServer2HostName, kDefaultServerPort);
@@ -977,7 +1027,15 @@
   host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
 
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
-  SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+
+  SequencedSocketData socket_data(reads, arraysize(reads), writes,
+                                  arraysize(writes));
   socket_factory_.AddSocketDataProvider(&socket_data);
 
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -1010,9 +1068,15 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server2(kServer2HostName, kDefaultServerPort);
@@ -1052,9 +1116,15 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server2(kServer2HostName, kDefaultServerPort);
@@ -1103,6 +1173,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server1(kDefaultServerHostName, 443);
@@ -1142,9 +1215,15 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server1(kDefaultServerHostName, 443);
@@ -1185,6 +1264,9 @@
   Initialize();
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server1(kDefaultServerHostName, 443);
@@ -1230,9 +1312,15 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server1(kDefaultServerHostName, 443);
@@ -1280,9 +1368,15 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server1(kDefaultServerHostName, 443);
@@ -1338,9 +1432,15 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -1394,7 +1494,10 @@
   QuicStreamId stream_id = kClientDataStreamId1;
   MockQuicData socket_data;
   socket_data.AddWrite(
-      client_maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED));
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
+  socket_data.AddWrite(
+      client_maker_.MakeRstPacket(2, true, stream_id, QUIC_STREAM_CANCELLED));
   socket_data.AddRead(
       server_maker_.MakeRstPacket(1, false, stream_id, QUIC_STREAM_CANCELLED));
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
@@ -1491,6 +1594,9 @@
   Initialize();
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
   {
     QuicStreamRequest request(factory_.get());
@@ -1518,11 +1624,17 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructClientRstPacket());
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
+  socket_data.AddWrite(ConstructClientRstPacket(2));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -1570,11 +1682,17 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructClientRstPacket());
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
+  socket_data.AddWrite(ConstructClientRstPacket(2));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -1631,9 +1749,14 @@
 
   int packet_number = 1;
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructGetRequestPacket(
-      packet_number++, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      packet_number++, SETTINGS_MAX_HEADER_LIST_SIZE,
+      kDefaultMaxUncompressedHeaderSize, &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(packet_number++,
+                                                 kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   if (async_write_before) {
     socket_data.AddWrite(ASYNC, OK);
     packet_number++;
@@ -1705,6 +1828,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -1756,9 +1882,14 @@
 
   int packet_number = 1;
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructGetRequestPacket(
-      packet_number++, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      packet_number++, SETTINGS_MAX_HEADER_LIST_SIZE,
+      kDefaultMaxUncompressedHeaderSize, &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(packet_number++,
+                                                 kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   if (async_write_before) {
     socket_data.AddWrite(ASYNC, OK);
     packet_number++;
@@ -1825,6 +1956,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -1871,6 +2005,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -1926,8 +2063,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -1974,8 +2114,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2025,8 +2168,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2072,8 +2218,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
+      2, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2121,6 +2270,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2158,6 +2310,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2194,9 +2349,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2242,12 +2401,12 @@
   // The response to the earlier request is read on this new socket.
   MockQuicData socket_data1;
   socket_data1.AddWrite(
-      client_maker_.MakePingPacket(2, /*include_version=*/true));
+      client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      4, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Add a new network and notify the stream factory of a new connected network.
@@ -2273,6 +2432,9 @@
   // Create a new request and verify that a new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
   QuicStreamRequest request2(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
@@ -2302,11 +2464,16 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddWrite(ASYNC, OK);
   socket_data1.AddSocketDataToFactory(&socket_factory_);
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddWrite(ASYNC, OK);
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
@@ -2405,9 +2572,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2440,12 +2611,12 @@
 
   MockQuicData socket_data1;
   socket_data1.AddWrite(
-      client_maker_.MakePingPacket(2, /*include_version=*/true));
+      client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      4, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Trigger early connection migration. This should cause a PING frame
@@ -2471,6 +2642,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -2525,9 +2699,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2562,12 +2740,12 @@
   // The response to the earlier request is read on this new socket.
   MockQuicData socket_data1;
   socket_data1.AddWrite(
-      client_maker_.MakePingPacket(2, /*include_version=*/true));
+      client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      4, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Trigger early connection migration. This should cause a PING frame
@@ -2593,6 +2771,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -2642,8 +2823,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2693,8 +2877,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2744,8 +2931,11 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -2799,7 +2989,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -2829,13 +3023,13 @@
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
   MockQuicData socket_data1;
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Send GET request on stream. This should cause a write error, which triggers
@@ -2887,6 +3081,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -2975,6 +3172,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3038,6 +3238,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3110,6 +3313,11 @@
     // The last socket is created but never used.
     if (i < kMaxReadersPerQuicSession) {
       socket_data[i].AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+      if (i == 0) {
+        socket_data[i].AddWrite(ConstructSettingsPacket(
+            1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+            nullptr));
+      }
       socket_data[i].AddWrite(
           (i % 2 == 0) ? first_write_error_mode : second_write_error_mode,
           ERR_FAILED);
@@ -3190,7 +3398,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3220,13 +3432,13 @@
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
   MockQuicData socket_data1;
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // First queue a network change notification in the message loop.
@@ -3284,7 +3496,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3314,13 +3530,13 @@
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
   MockQuicData socket_data1;
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Send GET request on stream. This should cause a write error,
@@ -3379,7 +3595,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3431,13 +3651,13 @@
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
   MockQuicData socket_data1;
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
@@ -3461,6 +3681,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -3507,7 +3730,11 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
   socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED);
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
@@ -3555,13 +3782,13 @@
   // migration. The request is rewritten to this new socket, and the
   // response to the request is read on this new socket.
   MockQuicData socket_data1;
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket(
-      2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   scoped_mock_network_change_notifier_->mock_network_change_notifier()
@@ -3597,6 +3824,9 @@
   // new session is created.
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request2(factory_.get());
@@ -3655,9 +3885,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
+  socket_data.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                 true, &header_stream_offset));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -3715,9 +3949,13 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   MockQuicData socket_data1;
+  QuicStreamOffset header_stream_offset = 0;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data1.AddWrite(
-      ConstructGetRequestPacket(1, kClientDataStreamId1, true, true));
+  socket_data1.AddWrite(ConstructSettingsPacket(
+      1, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize,
+      &header_stream_offset));
+  socket_data1.AddWrite(ConstructGetRequestPacket(2, kClientDataStreamId1, true,
+                                                  true, &header_stream_offset));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -3758,12 +3996,12 @@
   // response to the request is read on this new socket.
   MockQuicData socket_data2;
   socket_data2.AddWrite(
-      client_maker_.MakePingPacket(2, /*include_version=*/true));
+      client_maker_.MakePingPacket(3, /*include_version=*/true));
   socket_data2.AddRead(
       ConstructOkResponsePacket(1, kClientDataStreamId1, false, false));
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   socket_data2.AddWrite(client_maker_.MakeAckAndRstPacket(
-      3, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
+      4, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   const uint8_t kTestIpAddress[] = {1, 2, 3, 4};
@@ -3856,8 +4094,11 @@
   // Set up only socket data provider.
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   // Create request and QuicHttpStream.
@@ -3906,11 +4147,17 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructClientRstPacket());
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
+  socket_data.AddWrite(ConstructClientRstPacket(2));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -3957,11 +4204,17 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  socket_data.AddWrite(ConstructClientRstPacket());
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
+  socket_data.AddWrite(ConstructClientRstPacket(2));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -4173,10 +4426,16 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   HostPortPair server2(kServer2HostName, kDefaultServerPort);
@@ -4264,6 +4523,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   crypto_client_stream_factory_.set_handshake_mode(
@@ -4324,10 +4586,16 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   crypto_client_stream_factory_.set_handshake_mode(
@@ -4418,10 +4686,16 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   crypto_client_stream_factory_.set_handshake_mode(
@@ -4508,6 +4782,9 @@
   QuicStreamFactoryPeer::SetDelayTcpRace(factory_.get(), false);
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   ServerNetworkStats stats1;
@@ -4568,6 +4845,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   // Save current state of |race_cert_verification|.
@@ -4731,6 +5011,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -4769,12 +5052,18 @@
 
   MockQuicData socket_data1;
   socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data1.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data1.AddWrite(client_maker_.MakeRstPacket(
-      1, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
+      2, true, kServerDataStreamId1, QUIC_STREAM_CANCELLED));
   socket_data1.AddSocketDataToFactory(&socket_factory_);
 
   MockQuicData socket_data2;
   socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data2.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data2.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -4834,6 +5123,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request1(factory_.get());
@@ -4876,6 +5168,9 @@
 
   MockQuicData socket_data;
   socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(
+      ConstructSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                              kDefaultMaxUncompressedHeaderSize, nullptr));
   socket_data.AddSocketDataToFactory(&socket_factory_);
 
   QuicStreamRequest request(factory_.get());
@@ -5010,7 +5305,17 @@
   verify_details.cert_verify_result.is_issued_by_known_root = true;
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
-  AddHangingSocketData();
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  std::unique_ptr<SequencedSocketData> sequenced_socket_data(
+      new SequencedSocketData(reads, 1, writes, arraysize(writes)));
+  socket_factory_.AddSocketDataProvider(sequenced_socket_data.get());
+  sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data));
 
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
@@ -5018,6 +5323,7 @@
                              /*cert_verify_flags=*/0, url1, "GET", net_log_,
                              callback_.callback()));
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
+
   std::unique_ptr<QuicHttpStream> stream1 = request1.CreateStream();
   EXPECT_TRUE(stream1.get());
   EXPECT_TRUE(HasActiveSession(origin1_));
@@ -5070,8 +5376,21 @@
   verify_details2.cert_verify_result.is_issued_by_known_root = true;
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2);
 
-  AddHangingSocketData();
-  AddHangingSocketData();
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  std::unique_ptr<SequencedSocketData> sequenced_socket_data(
+      new SequencedSocketData(reads, 1, writes, arraysize(writes)));
+  socket_factory_.AddSocketDataProvider(sequenced_socket_data.get());
+  sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data));
+  std::unique_ptr<SequencedSocketData> sequenced_socket_data1(
+      new SequencedSocketData(reads, 1, writes, arraysize(writes)));
+  socket_factory_.AddSocketDataProvider(sequenced_socket_data1.get());
+  sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data1));
 
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
@@ -5143,8 +5462,21 @@
   verify_details2.cert_verify_result.is_issued_by_known_root = true;
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2);
 
-  AddHangingSocketData();
-  AddHangingSocketData();
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  std::unique_ptr<QuicEncryptedPacket> settings_packet(
+      client_maker_.MakeSettingsPacket(1, SETTINGS_MAX_HEADER_LIST_SIZE,
+                                       kDefaultMaxUncompressedHeaderSize, true,
+                                       nullptr));
+  MockWrite writes[] = {
+      MockWrite(ASYNC, settings_packet->data(), settings_packet->length(), 1)};
+  std::unique_ptr<SequencedSocketData> sequenced_socket_data(
+      new SequencedSocketData(reads, 1, writes, arraysize(writes)));
+  socket_factory_.AddSocketDataProvider(sequenced_socket_data.get());
+  sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data));
+  std::unique_ptr<SequencedSocketData> sequenced_socket_data1(
+      new SequencedSocketData(reads, 1, writes, arraysize(writes)));
+  socket_factory_.AddSocketDataProvider(sequenced_socket_data1.get());
+  sequenced_socket_data_vector_.push_back(std::move(sequenced_socket_data1));
 
   QuicStreamRequest request1(factory_.get());
   EXPECT_EQ(ERR_IO_PENDING,
diff --git a/net/quic/core/congestion_control/send_algorithm_test.cc b/net/quic/core/congestion_control/send_algorithm_test.cc
new file mode 100644
index 0000000..75bacfb
--- /dev/null
+++ b/net/quic/core/congestion_control/send_algorithm_test.cc
@@ -0,0 +1,359 @@
+// Copyright 2013 The Chromium 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 <algorithm>
+#include <map>
+#include <memory>
+
+#include "base/logging.h"
+#include "net/quic/core/congestion_control/rtt_stats.h"
+#include "net/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/quic/core/quic_types.h"
+#include "net/quic/core/quic_utils.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simulator/quic_endpoint.h"
+#include "net/quic/test_tools/simulator/simulator.h"
+#include "net/quic/test_tools/simulator/switch.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+// Use the initial CWND of 10, as 32 is too much for the test network.
+const uint32_t kInitialCongestionWindowPackets = 10;
+
+// Test network parameters.  Here, the topology of the network is:
+//
+//           QUIC Sender
+//               |
+//               |  <-- local link
+//               |
+//        Network switch
+//               *  <-- the bottleneck queue in the direction
+//               |          of the receiver
+//               |
+//               |  <-- test link
+//               |
+//               |
+//           Receiver
+//
+// When setting the bandwidth of the local link and test link, choose
+// a bandwidth lower than 20Mbps, as the clock-granularity of the
+// simulator can only handle a granularity of 1us.
+
+// Default settings between the switch and the sender.
+const QuicBandwidth kLocalLinkBandwidth =
+    QuicBandwidth::FromKBitsPerSecond(10000);
+const QuicTime::Delta kLocalPropagationDelay =
+    QuicTime::Delta::FromMilliseconds(2);
+
+// Wired network settings.  A typical desktop network setup, a
+// high-bandwidth, 30ms test link to the receiver.
+const QuicBandwidth kTestLinkWiredBandwidth =
+    QuicBandwidth::FromKBitsPerSecond(4000);
+const QuicTime::Delta kTestLinkWiredPropagationDelay =
+    QuicTime::Delta::FromMilliseconds(30);
+const QuicTime::Delta kTestWiredTransferTime =
+    kTestLinkWiredBandwidth.TransferTime(kMaxPacketSize) +
+    kLocalLinkBandwidth.TransferTime(kMaxPacketSize);
+const QuicTime::Delta kTestWiredRtt =
+    (kTestLinkWiredPropagationDelay + kLocalPropagationDelay +
+     kTestWiredTransferTime) *
+    2;
+const QuicByteCount kTestWiredBdp = kTestWiredRtt * kTestLinkWiredBandwidth;
+
+// Small BDP, Bandwidth-policed network settings.  In this scenario,
+// the receiver has a low-bandwidth, short propagation-delay link,
+// resulting in a small BDP.  We model the policer by setting the
+// queue size to only one packet.
+const QuicBandwidth kTestLinkLowBdpBandwidth =
+    QuicBandwidth::FromKBitsPerSecond(200);
+const QuicTime::Delta kTestLinkLowBdpPropagationDelay =
+    QuicTime::Delta::FromMilliseconds(30);
+const QuicByteCount kTestPolicerQueue = kMaxPacketSize;
+
+// Satellite network settings.  In a satellite network, the bottleneck
+// buffer is typically sized for non-satellite links , but the
+// propagation delay of the test link to the receiver is as much as a
+// quarter second.
+const QuicTime::Delta kTestSatellitePropagationDelay =
+    QuicTime::Delta::FromMilliseconds(250);
+
+// Cellular scenarios.  In a cellular network, the bottleneck queue at
+// the edge of the network can be as great as 3MB.
+const QuicBandwidth kTestLink2GBandwidth =
+    QuicBandwidth::FromKBitsPerSecond(100);
+const QuicBandwidth kTestLink3GBandwidth =
+    QuicBandwidth::FromKBitsPerSecond(1500);
+const QuicByteCount kCellularQueue = 3 * 1024 * 1024;
+const QuicTime::Delta kTestCellularPropagationDelay =
+    QuicTime::Delta::FromMilliseconds(40);
+
+const char* CongestionControlTypeToString(CongestionControlType cc_type) {
+  switch (cc_type) {
+    case kCubic:
+      return "CUBIC_PACKETS";
+    case kCubicBytes:
+      return "CUBIC_BYTES";
+    case kReno:
+      return "RENO_PACKETS";
+    case kRenoBytes:
+      return "RENO_BYTES";
+    case kBBR:
+      return "BBR";
+    default:
+      DLOG(FATAL) << "Unexpected CongestionControlType";
+      return nullptr;
+  }
+}
+
+struct TestParams {
+  explicit TestParams(CongestionControlType congestion_control_type)
+      : congestion_control_type(congestion_control_type) {}
+
+  friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+    os << "{ congestion_control_type: "
+       << CongestionControlTypeToString(p.congestion_control_type);
+    os << " }";
+    return os;
+  }
+
+  CongestionControlType congestion_control_type;
+};
+
+string TestParamToString(const testing::TestParamInfo<TestParams>& params) {
+  return CongestionControlTypeToString(params.param.congestion_control_type);
+}
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+  std::vector<TestParams> params;
+  for (const CongestionControlType congestion_control_type :
+       {kBBR, kCubic, kCubicBytes, kReno, kRenoBytes}) {
+    params.push_back(TestParams(congestion_control_type));
+  }
+  return params;
+}
+
+}  // namespace
+
+class SendAlgorithmTest : public ::testing::TestWithParam<TestParams> {
+ protected:
+  SendAlgorithmTest()
+      : simulator_(),
+        quic_sender_(&simulator_,
+                     "QUIC sender",
+                     "Receiver",
+                     Perspective::IS_CLIENT,
+                     42),
+        receiver_(&simulator_,
+                  "Receiver",
+                  "QUIC sender",
+                  Perspective::IS_SERVER,
+                  42) {
+    rtt_stats_ = quic_sender_.connection()->sent_packet_manager().GetRttStats();
+    sender_ = SendAlgorithmInterface::Create(
+        simulator_.GetClock(), rtt_stats_,
+        QuicSentPacketManagerPeer::GetUnackedPacketMap(
+            QuicConnectionPeer::GetSentPacketManager(quic_sender_.connection(),
+                                                     kDefaultPathId)),
+        GetParam().congestion_control_type, &random_, &stats_,
+        kInitialCongestionWindowPackets);
+
+    if (FLAGS_quic_fix_cubic_convex_mode) {
+      QuicConfig client_config;
+      QuicTagVector options;
+      options.push_back(kCCVX);
+      client_config.SetInitialReceivedConnectionOptions(options);
+      sender_->SetFromConfig(client_config, Perspective::IS_SERVER);
+    }
+
+    QuicConnectionPeer::SetSendAlgorithm(quic_sender_.connection(),
+                                         kDefaultPathId, sender_);
+
+    clock_ = simulator_.GetClock();
+    simulator_.set_random_generator(&random_);
+
+    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+    random_.set_seed(seed);
+    VLOG(1) << "SendAlgorithmTest simulator set up.  Seed: " << seed;
+  }
+
+  // Creates a simulated network, with default settings between the
+  // sender and the switch and the given settings from the switch to
+  // the receiver.
+  void CreateSetup(const QuicBandwidth& test_bandwidth,
+                   const QuicTime::Delta& test_link_delay,
+                   QuicByteCount bottleneck_queue_length) {
+    switch_.reset(new simulator::Switch(&simulator_, "Switch", 8,
+                                        bottleneck_queue_length));
+    quic_sender_link_.reset(new simulator::SymmetricLink(
+        &quic_sender_, switch_->port(1), kLocalLinkBandwidth,
+        kLocalPropagationDelay));
+    receiver_link_.reset(new simulator::SymmetricLink(
+        &receiver_, switch_->port(2), test_bandwidth, test_link_delay));
+  }
+
+  void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) {
+    quic_sender_.AddBytesToTransfer(transfer_size);
+    bool simulator_result = simulator_.RunUntilOrTimeout(
+        [this]() { return quic_sender_.bytes_to_transfer() == 0; }, deadline);
+    EXPECT_TRUE(simulator_result)
+        << "Simple transfer failed.  Bytes remaining: "
+        << quic_sender_.bytes_to_transfer();
+  }
+
+  void SendBursts(size_t number_of_bursts,
+                  QuicByteCount bytes,
+                  QuicTime::Delta rtt,
+                  QuicTime::Delta wait_time) {
+    ASSERT_EQ(0u, quic_sender_.bytes_to_transfer());
+    for (size_t i = 0; i < number_of_bursts; i++) {
+      quic_sender_.AddBytesToTransfer(bytes);
+
+      // Transfer data and wait for three seconds between each transfer.
+      simulator_.RunFor(wait_time);
+
+      // Ensure the connection did not time out.
+      ASSERT_TRUE(quic_sender_.connection()->connected());
+      ASSERT_TRUE(receiver_.connection()->connected());
+    }
+
+    simulator_.RunFor(wait_time + rtt);
+    EXPECT_EQ(0u, quic_sender_.bytes_to_transfer());
+  }
+
+  // Estimates the elapsed time for a given transfer size, given the
+  // bottleneck bandwidth and link propagation delay.
+  QuicTime::Delta EstimatedElapsedTime(
+      QuicByteCount transfer_size_bytes,
+      QuicBandwidth test_link_bandwidth,
+      const QuicTime::Delta& test_link_delay) const {
+    return test_link_bandwidth.TransferTime(transfer_size_bytes) +
+           2 * test_link_delay;
+  }
+
+  QuicTime QuicSenderStartTime() {
+    return quic_sender_.connection()->GetStats().connection_creation_time;
+  }
+
+  void PrintTransferStats() {
+    VLOG(1) << "Summary for scenario " << GetParam();
+    VLOG(1) << "Sender stats is " << quic_sender_.connection()->GetStats();
+    VLOG(1) << "Connection elapsed time: "
+            << (clock_->Now() - QuicSenderStartTime()).ToMilliseconds()
+            << " (ms)";
+  }
+
+  simulator::Simulator simulator_;
+  simulator::QuicEndpoint quic_sender_;
+  simulator::QuicEndpoint receiver_;
+  std::unique_ptr<simulator::Switch> switch_;
+  std::unique_ptr<simulator::SymmetricLink> quic_sender_link_;
+  std::unique_ptr<simulator::SymmetricLink> receiver_link_;
+  QuicConnectionStats stats_;
+
+  SimpleRandom random_;
+
+  // Owned by different components of the connection.
+  const QuicClock* clock_;
+  const RttStats* rtt_stats_;
+  SendAlgorithmInterface* sender_;
+};
+
+INSTANTIATE_TEST_CASE_P(SendAlgorithmTests,
+                        SendAlgorithmTest,
+                        ::testing::ValuesIn(GetTestParams()),
+                        TestParamToString);
+
+// Test a simple long data transfer in the default setup.
+TEST_P(SendAlgorithmTest, SimpleWiredNetworkTransfer) {
+  CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay,
+              kTestWiredBdp);
+  const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024;
+  const QuicTime::Delta maximum_elapsed_time =
+      EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth,
+                           kTestLinkWiredPropagationDelay) *
+      1.2;
+  DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+  PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, LowBdpPolicedNetworkTransfer) {
+  CreateSetup(kTestLinkLowBdpBandwidth, kTestLinkLowBdpPropagationDelay,
+              kTestPolicerQueue);
+  const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024;
+  const QuicTime::Delta maximum_elapsed_time =
+      EstimatedElapsedTime(kTransferSizeBytes, kTestLinkLowBdpBandwidth,
+                           kTestLinkLowBdpPropagationDelay) *
+      1.2;
+  DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+  PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, AppLimitedBurstsOverWiredNetwork) {
+  CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay,
+              kTestWiredBdp);
+  const QuicByteCount kBurstSizeBytes = 512;
+  const int kNumBursts = 20;
+  const QuicTime::Delta kWaitTime = QuicTime::Delta::FromSeconds(3);
+  SendBursts(kNumBursts, kBurstSizeBytes, kTestWiredRtt, kWaitTime);
+  PrintTransferStats();
+
+  const QuicTime::Delta estimated_burst_time =
+      EstimatedElapsedTime(kBurstSizeBytes, kTestLinkWiredBandwidth,
+                           kTestLinkWiredPropagationDelay) +
+      kWaitTime;
+  const QuicTime::Delta max_elapsed_time =
+      kNumBursts * estimated_burst_time + kWaitTime;
+  const QuicTime::Delta actual_elapsed_time =
+      clock_->Now() - QuicSenderStartTime();
+  EXPECT_GE(max_elapsed_time, actual_elapsed_time);
+}
+
+TEST_P(SendAlgorithmTest, SatelliteNetworkTransfer) {
+  CreateSetup(kTestLinkWiredBandwidth, kTestSatellitePropagationDelay,
+              kTestWiredBdp);
+  const QuicByteCount kTransferSizeBytes = 20 * 1024 * 1024;
+  const QuicTime::Delta maximum_elapsed_time =
+      EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth,
+                           kTestSatellitePropagationDelay) *
+      1.25;
+  DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+  PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, 2GNetworkTransfer) {
+  CreateSetup(kTestLink2GBandwidth, kTestCellularPropagationDelay,
+              kCellularQueue);
+  const QuicByteCount kTransferSizeBytes = 1024 * 1024;
+  const QuicTime::Delta maximum_elapsed_time =
+      EstimatedElapsedTime(kTransferSizeBytes, kTestLink2GBandwidth,
+                           kTestCellularPropagationDelay) *
+      1.2;
+  DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+  PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, 3GNetworkTransfer) {
+  CreateSetup(kTestLink3GBandwidth, kTestCellularPropagationDelay,
+              kCellularQueue);
+  const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024;
+  const QuicTime::Delta maximum_elapsed_time =
+      EstimatedElapsedTime(kTransferSizeBytes, kTestLink3GBandwidth,
+                           kTestCellularPropagationDelay) *
+      1.2;
+  DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+  PrintTransferStats();
+}
+
+}  // namespace test
+}  // namespace net
diff --git a/net/quic/core/crypto/crypto_protocol.h b/net/quic/core/crypto/crypto_protocol.h
index 9772f171..f9e8640 100644
--- a/net/quic/core/crypto/crypto_protocol.h
+++ b/net/quic/core/crypto/crypto_protocol.h
@@ -107,6 +107,12 @@
                                                  // Retransmissions.
 const QuicTag kLFAK = TAG('L', 'F', 'A', 'K');   // Don't invoke FACK on the
                                                  // first ack.
+
+// TODO(fayang): Remove this connection option in QUIC_VERSION_37, in which
+// MAX_HEADER_LIST_SIZE settings frame should be supported.
+const QuicTag kSMHL = TAG('S', 'M', 'H', 'L');   // Support MAX_HEADER_LIST_SIZE
+                                                 // settings frame.
+
 const QuicTag kCCVX = TAG('C', 'C', 'V', 'X');   // Fix Cubic convex bug.
 
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
diff --git a/net/quic/core/crypto/crypto_server_test.cc b/net/quic/core/crypto/crypto_server_test.cc
index 4f88458..8c1c688 100644
--- a/net/quic/core/crypto/crypto_server_test.cc
+++ b/net/quic/core/crypto/crypto_server_test.cc
@@ -234,10 +234,10 @@
 
   void ShouldSucceed(const CryptoHandshakeMessage& message) {
     bool called = false;
-    QuicIpAddress server_ip;
+    QuicSocketAddress server_address;
     config_.ValidateClientHello(
-        message, client_address_.host(), server_ip, supported_versions_.front(),
-        &clock_, signed_config_,
+        message, client_address_.host(), server_address,
+        supported_versions_.front(), &clock_, signed_config_,
         std::unique_ptr<ValidateCallback>(
             new ValidateCallback(this, true, "", &called)));
     EXPECT_TRUE(called);
@@ -253,10 +253,10 @@
   void ShouldFailMentioning(const char* error_substr,
                             const CryptoHandshakeMessage& message,
                             bool* called) {
-    QuicIpAddress server_ip;
+    QuicSocketAddress server_address;
     config_.ValidateClientHello(
-        message, client_address_.host(), server_ip, supported_versions_.front(),
-        &clock_, signed_config_,
+        message, client_address_.host(), server_address,
+        supported_versions_.front(), &clock_, signed_config_,
         std::unique_ptr<ValidateCallback>(
             new ValidateCallback(this, false, error_substr, called)));
   }
@@ -310,12 +310,12 @@
   void ProcessValidationResult(scoped_refptr<ValidateCallback::Result> result,
                                bool should_succeed,
                                const char* error_substr) {
-    QuicIpAddress server_ip;
+    QuicSocketAddress server_address;
     QuicConnectionId server_designated_connection_id =
         rand_for_id_generation_.RandUint64();
     bool called;
     config_.ProcessClientHello(
-        result, /*reject_only=*/false, /*connection_id=*/1, server_ip,
+        result, /*reject_only=*/false, /*connection_id=*/1, server_address,
         client_address_, supported_versions_.front(), supported_versions_,
         use_stateless_rejects_, server_designated_connection_id, &clock_, rand_,
         &compressed_certs_cache_, params_, signed_config_,
diff --git a/net/quic/core/crypto/proof_source.h b/net/quic/core/crypto/proof_source.h
index 0fce4f4..c5990ba5 100644
--- a/net/quic/core/crypto/proof_source.h
+++ b/net/quic/core/crypto/proof_source.h
@@ -103,7 +103,7 @@
   // cert.
   //
   // This function may be called concurrently.
-  virtual bool GetProof(const QuicIpAddress& server_ip,
+  virtual bool GetProof(const QuicSocketAddress& server_address,
                         const std::string& hostname,
                         const std::string& server_config,
                         QuicVersion quic_version,
@@ -116,7 +116,7 @@
   // are delivered to |callback|.  Callers should expect that |callback| might
   // be invoked synchronously.  The ProofSource takes ownership of |callback| in
   // any case.
-  virtual void GetProof(const QuicIpAddress& server_ip,
+  virtual void GetProof(const QuicSocketAddress& server_address,
                         const std::string& hostname,
                         const std::string& server_config,
                         QuicVersion quic_version,
diff --git a/net/quic/core/crypto/quic_crypto_server_config.cc b/net/quic/core/crypto/quic_crypto_server_config.cc
index ef3e459..fc4c8ad 100644
--- a/net/quic/core/crypto/quic_crypto_server_config.cc
+++ b/net/quic/core/crypto/quic_crypto_server_config.cc
@@ -419,7 +419,7 @@
 void QuicCryptoServerConfig::ValidateClientHello(
     const CryptoHandshakeMessage& client_hello,
     const QuicIpAddress& client_ip,
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     QuicVersion version,
     const QuicClock* clock,
     scoped_refptr<QuicSignedServerConfig> signed_config,
@@ -460,8 +460,9 @@
     signed_config->chain = nullptr;
     signed_config->proof.signature = "";
     signed_config->proof.leaf_cert_scts = "";
-    EvaluateClientHello(server_ip, version, requested_config, primary_config,
-                        signed_config, result, std::move(done_cb));
+    EvaluateClientHello(server_address, version, requested_config,
+                        primary_config, signed_config, result,
+                        std::move(done_cb));
   } else {
     done_cb->Run(result, /* details = */ nullptr);
   }
@@ -590,7 +591,7 @@
         validate_chlo_result,
     bool reject_only,
     QuicConnectionId connection_id,
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     const QuicSocketAddress& client_address,
     QuicVersion version,
     const QuicVersionVector& supported_versions,
@@ -682,7 +683,7 @@
               rand, compressed_certs_cache, params, signed_config,
               total_framing_overhead, chlo_packet_size, requested_config,
               primary_config, std::move(done_cb)));
-      proof_source_->GetProof(server_ip, info.sni.as_string(),
+      proof_source_->GetProof(server_address, info.sni.as_string(),
                               primary_config->serialized, version, chlo_hash,
                               connection_options, std::move(cb));
       helper.DetachCallback();
@@ -690,7 +691,7 @@
     }
 
     QuicCryptoProof proof;
-    if (!proof_source_->GetProof(server_ip, info.sni.as_string(),
+    if (!proof_source_->GetProof(server_address, info.sni.as_string(),
                                  primary_config->serialized, version, chlo_hash,
                                  connection_options, &signed_config->chain,
                                  &proof)) {
@@ -1163,7 +1164,7 @@
 };
 
 void QuicCryptoServerConfig::EvaluateClientHello(
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     QuicVersion version,
     scoped_refptr<Config> requested_config,
     scoped_refptr<Config> primary_config,
@@ -1252,10 +1253,10 @@
       // back into EvaluateClientHelloAfterGetProof
       std::unique_ptr<EvaluateClientHelloCallback> cb(
           new EvaluateClientHelloCallback(
-              *this, found_error, server_ip, version, requested_config,
-              primary_config, signed_config, client_hello_state,
-              std::move(done_cb)));
-      proof_source_->GetProof(server_ip, info->sni.as_string(),
+              *this, found_error, server_address.host(), version,
+              requested_config, primary_config, signed_config,
+              client_hello_state, std::move(done_cb)));
+      proof_source_->GetProof(server_address, info->sni.as_string(),
                               serialized_config, version, chlo_hash,
                               connection_options, std::move(cb));
       helper.DetachCallback();
@@ -1268,7 +1269,7 @@
     QuicCryptoProof proof;
 
     if (proof_source_->GetProof(
-            server_ip, info->sni.as_string(), serialized_config, version,
+            server_address, info->sni.as_string(), serialized_config, version,
             chlo_hash, connection_options, &signed_config->chain, &proof)) {
       signed_config->proof = proof;
     } else {
@@ -1279,9 +1280,9 @@
   // Details are null because the synchronous version of GetProof does not
   // return any stats.  Eventually the synchronous codepath will be eliminated.
   EvaluateClientHelloAfterGetProof(
-      found_error, server_ip, version, requested_config, primary_config,
-      signed_config, nullptr /* proof_source_details */, get_proof_failed,
-      client_hello_state, std::move(done_cb));
+      found_error, server_address.host(), version, requested_config,
+      primary_config, signed_config, nullptr /* proof_source_details */,
+      get_proof_failed, client_hello_state, std::move(done_cb));
   helper.DetachCallback();
 }
 
@@ -1331,7 +1332,7 @@
     QuicVersion version,
     StringPiece chlo_hash,
     const SourceAddressTokens& previous_source_address_tokens,
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     const QuicIpAddress& client_ip,
     const QuicClock* clock,
     QuicRandom* rand,
@@ -1362,7 +1363,7 @@
 
   scoped_refptr<ProofSource::Chain> chain;
   QuicCryptoProof proof;
-  if (!proof_source_->GetProof(server_ip, params.sni, serialized, version,
+  if (!proof_source_->GetProof(server_address, params.sni, serialized, version,
                                chlo_hash, connection_options, &chain, &proof)) {
     DVLOG(1) << "Server: failed to get proof.";
     return false;
@@ -1377,7 +1378,7 @@
   if (params.sct_supported_by_client && enable_serving_sct_) {
     if (proof.leaf_cert_scts.empty()) {
       DLOG(WARNING) << "SCT is expected but it is empty. sni: " << params.sni
-                    << " server_ip: " << server_ip.ToString();
+                    << " server_address: " << server_address.ToString();
     } else {
       out->SetStringPiece(kCertificateSCTTag, proof.leaf_cert_scts);
     }
@@ -1389,7 +1390,7 @@
     QuicVersion version,
     StringPiece chlo_hash,
     const SourceAddressTokens& previous_source_address_tokens,
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     const QuicIpAddress& client_ip,
     const QuicClock* clock,
     QuicRandom* rand,
@@ -1426,8 +1427,9 @@
   // and so should not have much impact on the experiments associated with that
   // tag (plus it would be a chore to plumb information about the tag down to
   // here).
-  proof_source_->GetProof(server_ip, params.sni, serialized, version, chlo_hash,
-                          connection_options, std::move(proof_source_cb));
+  proof_source_->GetProof(server_address, params.sni, serialized, version,
+                          chlo_hash, connection_options,
+                          std::move(proof_source_cb));
 }
 
 QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
diff --git a/net/quic/core/crypto/quic_crypto_server_config.h b/net/quic/core/crypto/quic_crypto_server_config.h
index 444102e7..0da88346 100644
--- a/net/quic/core/crypto/quic_crypto_server_config.h
+++ b/net/quic/core/crypto/quic_crypto_server_config.h
@@ -261,8 +261,8 @@
   // client_hello: the incoming client hello message.
   // client_ip: the IP address of the client, which is used to generate and
   //     validate source-address tokens.
-  // server_ip: the IP address of the server. The IP address may be used for
-  //     certificate selection.
+  // server_address: the IP address and port of the server. The IP address and
+  //     port may be used for certificate selection.
   // version: protocol version used for this connection.
   // clock: used to validate client nonces and ephemeral keys.
   // crypto_proof: in/out parameter to which will be written the crypto proof
@@ -276,7 +276,7 @@
   void ValidateClientHello(
       const CryptoHandshakeMessage& client_hello,
       const QuicIpAddress& client_ip,
-      const QuicIpAddress& server_ip,
+      const QuicSocketAddress& server_address,
       QuicVersion version,
       const QuicClock* clock,
       scoped_refptr<QuicSignedServerConfig> crypto_proof,
@@ -316,7 +316,7 @@
           validate_chlo_result,
       bool reject_only,
       QuicConnectionId connection_id,
-      const QuicIpAddress& server_ip,
+      const QuicSocketAddress& server_address,
       const QuicSocketAddress& client_address,
       QuicVersion version,
       const QuicVersionVector& supported_versions,
@@ -343,7 +343,7 @@
       QuicVersion version,
       base::StringPiece chlo_hash,
       const SourceAddressTokens& previous_source_address_tokens,
-      const QuicIpAddress& server_ip,
+      const QuicSocketAddress& server_address,
       const QuicIpAddress& client_ip,
       const QuicClock* clock,
       QuicRandom* rand,
@@ -367,7 +367,7 @@
       QuicVersion version,
       base::StringPiece chlo_hash,
       const SourceAddressTokens& previous_source_address_tokens,
-      const QuicIpAddress& server_ip,
+      const QuicSocketAddress& server_address,
       const QuicIpAddress& client_ip,
       const QuicClock* clock,
       QuicRandom* rand,
@@ -511,7 +511,7 @@
   // whether it can be shown to be fresh (i.e. not a replay). The results are
   // written to |info|.
   void EvaluateClientHello(
-      const QuicIpAddress& server_ip,
+      const QuicSocketAddress& server_address,
       QuicVersion version,
       scoped_refptr<Config> requested_config,
       scoped_refptr<Config> primary_config,
diff --git a/net/quic/core/quic_client_session_base.cc b/net/quic/core/quic_client_session_base.cc
index 2abe509..d0e6c4d7 100644
--- a/net/quic/core/quic_client_session_base.cc
+++ b/net/quic/core/quic_client_session_base.cc
@@ -35,7 +35,7 @@
 }
 
 void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
-  QuicSession::OnCryptoHandshakeEvent(event);
+  QuicSpdySession::OnCryptoHandshakeEvent(event);
 }
 
 void QuicClientSessionBase::OnInitialHeadersComplete(
diff --git a/net/quic/core/quic_config.cc b/net/quic/core/quic_config.cc
index 8f147218..c0b313b 100644
--- a/net/quic/core/quic_config.cc
+++ b/net/quic/core/quic_config.cc
@@ -10,6 +10,7 @@
 #include "net/quic/core/crypto/crypto_handshake_message.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
 #include "net/quic/core/quic_bug_tracker.h"
+#include "net/quic/core/quic_flags.h"
 #include "net/quic/core/quic_socket_address_coder.h"
 #include "net/quic/core/quic_utils.h"
 
@@ -338,7 +339,8 @@
       multipath_enabled_(kMPTH, PRESENCE_OPTIONAL),
       connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL),
       alternate_server_address_(kASAD, PRESENCE_OPTIONAL),
-      force_hol_blocking_(kFHL2, PRESENCE_OPTIONAL) {
+      force_hol_blocking_(kFHL2, PRESENCE_OPTIONAL),
+      support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL) {
   SetDefaults();
 }
 
@@ -593,6 +595,14 @@
   }
 }
 
+void QuicConfig::SetSupportMaxHeaderListSize() {
+  support_max_header_list_size_.SetSendValue(1);
+}
+
+bool QuicConfig::SupportMaxHeaderListSize() const {
+  return support_max_header_list_size_.HasReceivedValue();
+}
+
 bool QuicConfig::negotiated() const {
   // TODO(ianswett): Add the negotiated parameters once and iterate over all
   // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and
@@ -616,6 +626,9 @@
 
   SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow);
   SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow);
+  if (FLAGS_quic_send_max_header_list_size) {
+    SetSupportMaxHeaderListSize();
+  }
 }
 
 void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
@@ -632,6 +645,7 @@
   connection_options_.ToHandshakeMessage(out);
   alternate_server_address_.ToHandshakeMessage(out);
   force_hol_blocking_.ToHandshakeMessage(out);
+  support_max_header_list_size_.ToHandshakeMessage(out);
 }
 
 QuicErrorCode QuicConfig::ProcessPeerHello(
@@ -693,6 +707,10 @@
     error = force_hol_blocking_.ProcessPeerHello(peer_hello, hello_type,
                                                  error_details);
   }
+  if (error == QUIC_NO_ERROR) {
+    error = support_max_header_list_size_.ProcessPeerHello(
+        peer_hello, hello_type, error_details);
+  }
   return error;
 }
 
diff --git a/net/quic/core/quic_config.h b/net/quic/core/quic_config.h
index 2d88127..5e19bef 100644
--- a/net/quic/core/quic_config.h
+++ b/net/quic/core/quic_config.h
@@ -371,6 +371,10 @@
 
   bool ForceHolBlocking(Perspective perspective) const;
 
+  void SetSupportMaxHeaderListSize();
+
+  bool SupportMaxHeaderListSize() const;
+
   bool negotiated() const;
 
   // ToHandshakeMessage serialises the settings in this object as a series of
@@ -436,6 +440,9 @@
 
   // Force HOL blocking for measurement purposes.
   QuicFixedUint32 force_hol_blocking_;
+
+  // Whether support HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame.
+  QuicFixedUint32 support_max_header_list_size_;
 };
 
 }  // namespace net
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index 6802d1f..06c05f678 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -253,14 +253,13 @@
       time_of_last_sent_new_packet_(clock_->ApproximateNow()),
       last_send_for_timeout_(clock_->ApproximateNow()),
       packet_number_of_last_sent_packet_(0),
-      sent_packet_manager_(new QuicSentPacketManager(
-          perspective,
-          kDefaultPathId,
-          clock_,
-          &stats_,
-          FLAGS_quic_default_enable_cubic_bytes ? kCubicBytes : kCubic,
-          kNack,
-          /*delegate=*/nullptr)),
+      sent_packet_manager_(new QuicSentPacketManager(perspective,
+                                                     kDefaultPathId,
+                                                     clock_,
+                                                     &stats_,
+                                                     kCubicBytes,
+                                                     kNack,
+                                                     /*delegate=*/nullptr)),
       version_negotiation_state_(START_NEGOTIATION),
       perspective_(perspective),
       connected_(true),
diff --git a/net/quic/core/quic_crypto_client_stream_test.cc b/net/quic/core/quic_crypto_client_stream_test.cc
index b839536d..fa031bad 100644
--- a/net/quic/core/quic_crypto_client_stream_test.cc
+++ b/net/quic/core/quic_crypto_client_stream_test.cc
@@ -252,10 +252,10 @@
   CryptoHandshakeMessage server_config_update;
   EXPECT_TRUE(crypto_config.BuildServerConfigUpdateMessage(
       session_->connection()->version(), stream()->chlo_hash(), tokens,
-      QuicIpAddress::Loopback6(), QuicIpAddress::Loopback6(),
-      connection_->clock(), QuicRandom::GetInstance(), &cache,
-      stream()->crypto_negotiated_params(), &network_params, QuicTagVector(),
-      &server_config_update));
+      QuicSocketAddress(QuicIpAddress::Loopback6(), 1234),
+      QuicIpAddress::Loopback6(), connection_->clock(),
+      QuicRandom::GetInstance(), &cache, stream()->crypto_negotiated_params(),
+      &network_params, QuicTagVector(), &server_config_update));
 
   std::unique_ptr<QuicData> data(
       CryptoFramer::ConstructHandshakeMessage(server_config_update));
diff --git a/net/quic/core/quic_crypto_server_stream.cc b/net/quic/core/quic_crypto_server_stream.cc
index 0a300def..576d693 100644
--- a/net/quic/core/quic_crypto_server_stream.cc
+++ b/net/quic/core/quic_crypto_server_stream.cc
@@ -162,7 +162,7 @@
   validate_client_hello_cb_ = cb.get();
   crypto_config_->ValidateClientHello(
       message, session()->connection()->peer_address().host(),
-      session()->connection()->self_address().host(), version(),
+      session()->connection()->self_address(), version(),
       session()->connection()->clock(), signed_config_, std::move(cb));
 }
 
@@ -294,7 +294,7 @@
     crypto_config_->BuildServerConfigUpdateMessage(
         session()->connection()->version(), chlo_hash_,
         previous_source_address_tokens_,
-        session()->connection()->self_address().host(),
+        session()->connection()->self_address(),
         session()->connection()->peer_address().host(),
         session()->connection()->clock(),
         session()->connection()->random_generator(), compressed_certs_cache_,
@@ -310,7 +310,7 @@
   if (!crypto_config_->BuildServerConfigUpdateMessage(
           session()->connection()->version(), chlo_hash_,
           previous_source_address_tokens_,
-          session()->connection()->self_address().host(),
+          session()->connection()->self_address(),
           session()->connection()->peer_address().host(),
           session()->connection()->clock(),
           session()->connection()->random_generator(), compressed_certs_cache_,
@@ -368,10 +368,6 @@
   ++num_server_config_update_messages_sent_;
 }
 
-void QuicCryptoServerStream::OnServerHelloAcked() {
-  session()->connection()->OnHandshakeComplete();
-}
-
 uint8_t QuicCryptoServerStream::NumHandshakeMessages() const {
   return num_handshake_messages_;
 }
@@ -469,7 +465,7 @@
       GenerateConnectionIdForReject(use_stateless_rejects_in_crypto_config);
   crypto_config_->ProcessClientHello(
       result, /*reject_only=*/false, connection->connection_id(),
-      connection->self_address().host(), connection->peer_address(), version(),
+      connection->self_address(), connection->peer_address(), version(),
       connection->supported_versions(), use_stateless_rejects_in_crypto_config,
       server_designated_connection_id, connection->clock(),
       connection->random_generator(), compressed_certs_cache_,
diff --git a/net/quic/core/quic_crypto_server_stream.h b/net/quic/core/quic_crypto_server_stream.h
index 27735755..71e779b8 100644
--- a/net/quic/core/quic_crypto_server_stream.h
+++ b/net/quic/core/quic_crypto_server_stream.h
@@ -53,10 +53,6 @@
   virtual void SendServerConfigUpdate(
       const CachedNetworkParameters* cached_network_params) = 0;
 
-  // Called by the ServerHello AckNotifier once the SHLO has been ACKed by the
-  // client.
-  virtual void OnServerHelloAcked() = 0;
-
   // These are all accessors and setters to their respective counters.
   virtual uint8_t NumHandshakeMessages() const = 0;
   virtual uint8_t NumHandshakeMessagesWithServerNonces() const = 0;
@@ -111,7 +107,6 @@
   bool GetBase64SHA256ClientChannelID(std::string* output) const override;
   void SendServerConfigUpdate(
       const CachedNetworkParameters* cached_network_params) override;
-  void OnServerHelloAcked() override;
   uint8_t NumHandshakeMessages() const override;
   uint8_t NumHandshakeMessagesWithServerNonces() const override;
   int NumServerConfigUpdateMessagesSent() const override;
diff --git a/net/quic/core/quic_crypto_server_stream_test.cc b/net/quic/core/quic_crypto_server_stream_test.cc
index a437a378..90ff14f0 100644
--- a/net/quic/core/quic_crypto_server_stream_test.cc
+++ b/net/quic/core/quic_crypto_server_stream_test.cc
@@ -416,6 +416,12 @@
 }
 
 TEST_P(QuicCryptoServerStreamTest, SendSCUPAfterHandshakeComplete) {
+  // Do not send MAX_HEADER_LIST_SIZE SETTING frame.
+  // TODO(fayang): This SETTING frame cannot be decrypted and
+  // CryptoTestUtils::MovePackets stops processing parsing following packets.
+  // Actually, crypto stream test should use QuicSession instead of
+  // QuicSpdySession (b/32366134).
+  FLAGS_quic_send_max_header_list_size = false;
   Initialize();
 
   InitializeFakeClient(/* supports_stateless_rejects= */ false);
@@ -479,7 +485,7 @@
 
 class FailingProofSource : public ProofSource {
  public:
-  bool GetProof(const QuicIpAddress& server_ip,
+  bool GetProof(const QuicSocketAddress& server_address,
                 const string& hostname,
                 const string& server_config,
                 QuicVersion quic_version,
@@ -490,7 +496,7 @@
     return false;
   }
 
-  void GetProof(const QuicIpAddress& server_ip,
+  void GetProof(const QuicSocketAddress& server_address,
                 const string& hostname,
                 const string& server_config,
                 QuicVersion quic_version,
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index baaf6f7..c1202a95 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -122,10 +122,6 @@
 // stream.
 QUIC_FLAG(bool, FLAGS_quic_headers_stream_release_sequencer_buffer, false)
 
-// Default enable QUIC's Cubic in bytes implementation instead of
-// Cubic in packets.
-QUIC_FLAG(bool, FLAGS_quic_default_enable_cubic_bytes, true)
-
 // Set the retransmission alarm only when there are unacked
 // retransmittable packets.
 QUIC_FLAG(bool, FLAGS_quic_more_conservative_retransmission_alarm, true)
@@ -151,3 +147,7 @@
 
 // Ensure that BBR startup pacing rate does not drop below the initial one.
 QUIC_FLAG(bool, FLAGS_quic_bbr_faster_startup, false)
+
+// If true, GFE sends SETTINGS_MAX_HEADER_LIST_SIZE to the client at the
+// beginning of a connection.
+QUIC_FLAG(bool, FLAGS_quic_send_max_header_list_size, true)
diff --git a/net/quic/core/quic_headers_stream.cc b/net/quic/core/quic_headers_stream.cc
index 383d44f..7f890f6 100644
--- a/net/quic/core/quic_headers_stream.cc
+++ b/net/quic/core/quic_headers_stream.cc
@@ -192,6 +192,10 @@
         break;
       // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when
       // clients are actually sending it.
+      case SETTINGS_MAX_HEADER_LIST_SIZE:
+        if (FLAGS_quic_send_max_header_list_size) {
+          break;
+        }
       default:
         CloseConnection("Unsupported field of HTTP/2 SETTINGS frame: " +
                         base::IntToString(id));
@@ -580,6 +584,15 @@
   }
 }
 
+size_t QuicHeadersStream::SendMaxHeaderListSize(size_t value) {
+  SpdySettingsIR settings_frame;
+  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false, value);
+
+  SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame));
+  WriteOrBufferData(StringPiece(frame.data(), frame.size()), false, nullptr);
+  return frame.size();
+}
+
 bool QuicHeadersStream::OnDataFrameHeader(QuicStreamId stream_id,
                                           size_t length,
                                           bool fin) {
diff --git a/net/quic/core/quic_headers_stream.h b/net/quic/core/quic_headers_stream.h
index e5ffc6c..97907666 100644
--- a/net/quic/core/quic_headers_stream.h
+++ b/net/quic/core/quic_headers_stream.h
@@ -98,6 +98,9 @@
   // Release underlying buffer if allowed.
   void MaybeReleaseSequencerBuffer();
 
+  // Sends SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame.
+  size_t SendMaxHeaderListSize(size_t value);
+
   // Sets how much encoded data the hpack decoder of spdy_framer_ is willing to
   // buffer.
   void set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes) {
diff --git a/net/quic/core/quic_headers_stream_test.cc b/net/quic/core/quic_headers_stream_test.cc
index 6a6112e8..3651973 100644
--- a/net/quic/core/quic_headers_stream_test.cc
+++ b/net/quic/core/quic_headers_stream_test.cc
@@ -745,10 +745,13 @@
 
 TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameSupportedFields) {
   FLAGS_quic_respect_http2_settings_frame = true;
+  FLAGS_quic_send_max_header_list_size = true;
   const uint32_t kTestHeaderTableSize = 1000;
   SpdySettingsIR data;
-  // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE.
+  // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE,
+  // SETTINGS_MAX_HEADER_LIST_SIZE.
   data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, true, true, kTestHeaderTableSize);
+  data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, true, true, 2000);
   SpdySerializedFrame frame(framer_->SerializeFrame(data));
   stream_frame_.data_buffer = frame.data();
   stream_frame_.data_length = frame.size();
@@ -760,11 +763,11 @@
 
 TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) {
   FLAGS_quic_respect_http2_settings_frame = true;
+  FLAGS_quic_send_max_header_list_size = true;
   SpdySettingsIR data;
-  // Does not support SETTINGS_MAX_HEADER_LIST_SIZE,
-  // SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE,
-  // SETTINGS_ENABLE_PUSH and SETTINGS_MAX_FRAME_SIZE.
-  data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, true, true, 2000);
+  // Does not support SETTINGS_MAX_CONCURRENT_STREAMS,
+  // SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and
+  // SETTINGS_MAX_FRAME_SIZE.
   data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, true, true, 100);
   data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, true, true, 100);
   data.AddSetting(SETTINGS_ENABLE_PUSH, true, true, 1);
@@ -774,12 +777,6 @@
       *connection_,
       CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
                       "Unsupported field of HTTP/2 SETTINGS frame: " +
-                          base::IntToString(SETTINGS_MAX_HEADER_LIST_SIZE),
-                      _));
-  EXPECT_CALL(
-      *connection_,
-      CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
-                      "Unsupported field of HTTP/2 SETTINGS frame: " +
                           base::IntToString(SETTINGS_MAX_CONCURRENT_STREAMS),
                       _));
   EXPECT_CALL(
diff --git a/net/quic/core/quic_sent_packet_manager.cc b/net/quic/core/quic_sent_packet_manager.cc
index 43ac400..022ce3ec 100644
--- a/net/quic/core/quic_sent_packet_manager.cc
+++ b/net/quic/core/quic_sent_packet_manager.cc
@@ -73,7 +73,6 @@
       loss_algorithm_(&general_loss_algorithm_),
       general_loss_algorithm_(loss_type),
       n_connection_simulation_(false),
-      receive_buffer_bytes_(kDefaultSocketReceiveBuffer),
       least_packet_awaited_by_peer_(1),
       first_rto_transmission_(0),
       consecutive_rto_count_(0),
@@ -124,11 +123,7 @@
       }
     } else if (config.HasClientRequestedIndependentOption(kBYTE,
                                                           perspective_)) {
-      if (FLAGS_quic_default_enable_cubic_bytes) {
-        SetSendAlgorithm(kCubic);
-      } else {
-        SetSendAlgorithm(kCubicBytes);
-      }
+      SetSendAlgorithm(kCubic);
     }
   } else {
     if (FLAGS_quic_allow_new_bbr && config.HasReceivedConnectionOptions() &&
@@ -144,11 +139,7 @@
       }
     } else if (config.HasReceivedConnectionOptions() &&
                ContainsQuicTag(config.ReceivedConnectionOptions(), kBYTE)) {
-      if (FLAGS_quic_default_enable_cubic_bytes) {
-        SetSendAlgorithm(kCubic);
-      } else {
-        SetSendAlgorithm(kCubicBytes);
-      }
+      SetSendAlgorithm(kCubic);
     }
   }
   using_pacing_ = !FLAGS_quic_disable_pacing_for_perf_tests;
@@ -498,29 +489,6 @@
   return packet_number;
 }
 
-void QuicSentPacketManager::MarkPacketNotRetransmittable(
-    QuicPacketNumber packet_number,
-    QuicTime::Delta ack_delay_time) {
-  if (!unacked_packets_.IsUnacked(packet_number)) {
-    return;
-  }
-
-  const QuicTransmissionInfo& transmission_info =
-      unacked_packets_.GetTransmissionInfo(packet_number);
-  QuicPacketNumber newest_transmission =
-      GetNewestRetransmission(packet_number, transmission_info);
-  // We do not need to retransmit this packet anymore.
-  if (delegate_ != nullptr) {
-    delegate_->OnPacketMarkedNotRetransmittable(path_id_, newest_transmission,
-                                                ack_delay_time);
-  } else {
-    pending_retransmissions_.erase(newest_transmission);
-  }
-
-  unacked_packets_.NotifyAndClearListeners(newest_transmission, ack_delay_time);
-  unacked_packets_.RemoveRetransmittability(packet_number);
-}
-
 void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number,
                                               QuicTransmissionInfo* info,
                                               QuicTime::Delta ack_delay_time) {
@@ -1046,15 +1014,6 @@
   return consecutive_tlp_count_;
 }
 
-QuicTransmissionInfo* QuicSentPacketManager::GetMutableTransmissionInfo(
-    QuicPacketNumber packet_number) {
-  return unacked_packets_.GetMutableTransmissionInfo(packet_number);
-}
-
-void QuicSentPacketManager::RemoveObsoletePackets() {
-  unacked_packets_.RemoveObsoletePackets();
-}
-
 void QuicSentPacketManager::OnApplicationLimited() {
   send_algorithm_->OnApplicationLimited(unacked_packets_.bytes_in_flight());
 }
diff --git a/net/quic/core/quic_sent_packet_manager.h b/net/quic/core/quic_sent_packet_manager.h
index fdd7d34..cb1f6078 100644
--- a/net/quic/core/quic_sent_packet_manager.h
+++ b/net/quic/core/quic_sent_packet_manager.h
@@ -57,11 +57,6 @@
     virtual void OnRetransmissionMarked(QuicPathId path_id,
                                         QuicPacketNumber packet_number,
                                         TransmissionType transmission_type) = 0;
-    // Called when |packet_number| is marked as not retransmittable.
-    virtual void OnPacketMarkedNotRetransmittable(
-        QuicPathId path_id,
-        QuicPacketNumber packet_number,
-        QuicTime::Delta delta_largest_observed) = 0;
     // Called when any transmission of |packet_number| is handled.
     virtual void OnPacketMarkedHandled(
         QuicPathId path_id,
@@ -279,17 +274,6 @@
                                   QuicByteCount prior_in_flight,
                                   QuicTime event_time);
 
-  // Called when frames of |packet_number| has been received but the packet
-  // itself has not been received by the peer. Currently, this method is not
-  // used.
-  // TODO(fayang): Update the comment when multipath sent packet manager is
-  // landed.
-  // The packet needs no longer to be retransmitted, but the packet remains
-  // pending if it is and the congestion control does not consider the packet
-  // acked.
-  void MarkPacketNotRetransmittable(QuicPacketNumber packet_number,
-                                    QuicTime::Delta ack_delay_time);
-
   // Removes the retransmittability and in flight properties from the packet at
   // |info| due to receipt by the peer.
   void MarkPacketHandled(QuicPacketNumber packet_number,
@@ -313,15 +297,6 @@
   void RecordSpuriousRetransmissions(const QuicTransmissionInfo& info,
                                      QuicPacketNumber acked_packet_number);
 
-  // Returns mutable QuicTransmissionInfo associated with |packet_number|, which
-  // must be unacked.
-  QuicTransmissionInfo* GetMutableTransmissionInfo(
-      QuicPacketNumber packet_number);
-
-  // Remove any packets no longer needed for retransmission, congestion, or
-  // RTT measurement purposes.
-  void RemoveObsoletePackets();
-
   // Sets the send algorithm to the given congestion control type and points the
   // pacing sender at |send_algorithm_|. Can be called any number of times.
   void SetSendAlgorithm(CongestionControlType congestion_control_type);
@@ -365,9 +340,6 @@
   GeneralLossAlgorithm general_loss_algorithm_;
   bool n_connection_simulation_;
 
-  // Receiver side buffer in bytes.
-  QuicByteCount receive_buffer_bytes_;
-
   // Least packet number which the peer is still waiting for.
   QuicPacketNumber least_packet_awaited_by_peer_;
 
diff --git a/net/quic/core/quic_sent_packet_manager_test.cc b/net/quic/core/quic_sent_packet_manager_test.cc
index 4cc638b..bc6d0e1 100644
--- a/net/quic/core/quic_sent_packet_manager_test.cc
+++ b/net/quic/core/quic_sent_packet_manager_test.cc
@@ -62,7 +62,7 @@
                  kDefaultPathId,
                  &clock_,
                  &stats_,
-                 FLAGS_quic_default_enable_cubic_bytes ? kCubicBytes : kCubic,
+                 kCubicBytes,
                  kNack,
                  /*delegate=*/nullptr),
         send_algorithm_(new StrictMock<MockSendAlgorithm>),
@@ -1412,13 +1412,8 @@
   QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
   EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
   manager_.SetFromConfig(config);
-  if (FLAGS_quic_default_enable_cubic_bytes) {
-    EXPECT_EQ(kCubic, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
-                          ->GetCongestionControlType());
-  } else {
-    EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
-                               ->GetCongestionControlType());
-  }
+  EXPECT_EQ(kCubic, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+                        ->GetCongestionControlType());
 
   options.clear();
   options.push_back(kRENO);
@@ -1466,13 +1461,8 @@
   config.SetClientConnectionOptions(options);
   EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
   manager_.SetFromConfig(config);
-  if (FLAGS_quic_default_enable_cubic_bytes) {
-    EXPECT_EQ(kCubic, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
-                          ->GetCongestionControlType());
-  } else {
-    EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
-                               ->GetCongestionControlType());
-  }
+  EXPECT_EQ(kCubic, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+                        ->GetCongestionControlType());
 
   options.clear();
   options.push_back(kRENO);
diff --git a/net/quic/core/quic_session.h b/net/quic/core/quic_session.h
index aae47eef..50518ca1 100644
--- a/net/quic/core/quic_session.h
+++ b/net/quic/core/quic_session.h
@@ -185,19 +185,19 @@
 
   // Returns the number of currently open streams, excluding the reserved
   // headers and crypto streams, and never counting unfinished streams.
-  virtual size_t GetNumActiveStreams() const;
+  size_t GetNumActiveStreams() const;
 
   // Returns the number of currently open peer initiated streams, excluding the
   // reserved headers and crypto streams.
-  virtual size_t GetNumOpenIncomingStreams() const;
+  size_t GetNumOpenIncomingStreams() const;
 
   // Returns the number of currently open self initiated streams, excluding the
   // reserved headers and crypto streams.
-  virtual size_t GetNumOpenOutgoingStreams() const;
+  size_t GetNumOpenOutgoingStreams() const;
 
   // Returns the number of "available" streams, the stream ids less than
   // largest_peer_created_stream_id_ that have not yet been opened.
-  virtual size_t GetNumAvailableStreams() const;
+  size_t GetNumAvailableStreams() const;
 
   // Add the stream to the session's write-blocked list because it is blocked by
   // connection-level flow control but not by its own stream-level flow control.
@@ -355,10 +355,6 @@
   virtual void HandleRstOnValidNonexistentStream(
       const QuicRstStreamFrame& frame);
 
-  Visitor* visitor() { return visitor_; }
-
-  const Visitor* visitor() const { return visitor_; }
-
  private:
   friend class test::QuicSessionPeer;
 
diff --git a/net/quic/core/quic_session_test.cc b/net/quic/core/quic_session_test.cc
index 0bb974c..43e303a4 100644
--- a/net/quic/core/quic_session_test.cc
+++ b/net/quic/core/quic_session_test.cc
@@ -69,6 +69,8 @@
         session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
     EXPECT_EQ(QUIC_NO_ERROR, error);
     session()->OnConfigNegotiated();
+    session()->connection()->SetDefaultEncryptionLevel(
+        ENCRYPTION_FORWARD_SECURE);
     session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
   }
 
@@ -516,6 +518,12 @@
 TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) {
   // Encryption needs to be established before data can be sent.
   CryptoHandshakeMessage msg;
+  MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+      QuicConnectionPeer::GetWriter(session_.connection()));
+  if (FLAGS_quic_send_max_header_list_size) {
+    EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+        .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+  }
   session_.GetCryptoStream()->OnHandshakeMessage(msg);
 
   // Drive congestion control manually.
@@ -551,8 +559,6 @@
 
   // Expect that we only send one packet, the writes from different streams
   // should be bundled together.
-  MockPacketWriter* writer = static_cast<MockPacketWriter*>(
-      QuicConnectionPeer::GetWriter(session_.connection()));
   EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
       .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
   EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _));
diff --git a/net/quic/core/quic_spdy_session.cc b/net/quic/core/quic_spdy_session.cc
index 99af124..d081b65 100644
--- a/net/quic/core/quic_spdy_session.cc
+++ b/net/quic/core/quic_spdy_session.cc
@@ -102,6 +102,14 @@
   return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
 }
 
+void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+  QuicSession::OnCryptoHandshakeEvent(event);
+  if (FLAGS_quic_send_max_header_list_size && event == HANDSHAKE_CONFIRMED &&
+      config()->SupportMaxHeaderListSize()) {
+    headers_stream()->SendMaxHeaderListSize(kDefaultMaxUncompressedHeaderSize);
+  }
+}
+
 void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
                                           QuicStreamId promised_stream_id,
                                           size_t frame_len,
diff --git a/net/quic/core/quic_spdy_session.h b/net/quic/core/quic_spdy_session.h
index 670142eb..eb58af6 100644
--- a/net/quic/core/quic_spdy_session.h
+++ b/net/quic/core/quic_spdy_session.h
@@ -117,6 +117,8 @@
   // If an outgoing stream can be created, return true.
   virtual bool ShouldCreateOutgoingDynamicStream() = 0;
 
+  void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
  private:
   friend class test::QuicSpdySessionPeer;
 
diff --git a/net/quic/core/quic_spdy_stream.cc b/net/quic/core/quic_spdy_stream.cc
index 61b6deb7..6c9335b76 100644
--- a/net/quic/core/quic_spdy_stream.cc
+++ b/net/quic/core/quic_spdy_stream.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "net/base/parse_number.h"
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_spdy_session.h"
 #include "net/quic/core/quic_utils.h"
@@ -290,15 +291,19 @@
   if (status.size() != 3) {
     return false;
   }
-  // First character must be an integer in range [1,5].
-  if (status[0] < '1' || status[0] > '5') {
+
+  unsigned int result;
+  if (!ParseUint32(status, &result, nullptr)) {
     return false;
   }
-  // The remaining two characters must be integers.
-  if (!isdigit(status[1]) || !isdigit(status[2])) {
+
+  // Valid status codes are only in the range [100, 599].
+  if (result < 100 || result >= 600) {
     return false;
   }
-  return StringToInt(status, status_code);
+
+  *status_code = static_cast<int>(result);
+  return true;
 }
 
 bool QuicSpdyStream::FinishedReadingTrailers() const {
diff --git a/net/quic/core/quic_spdy_stream_test.cc b/net/quic/core/quic_spdy_stream_test.cc
index 8a1fb60..386f4b2 100644
--- a/net/quic/core/quic_spdy_stream_test.cc
+++ b/net/quic/core/quic_spdy_stream_test.cc
@@ -179,11 +179,19 @@
   Initialize(kShouldProcessData);
   int status_code = 0;
 
-  // Valid status code.
+  // Valid status codes.
   headers_[":status"] = "404";
   EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
   EXPECT_EQ(404, status_code);
 
+  headers_[":status"] = "100";
+  EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+  EXPECT_EQ(100, status_code);
+
+  headers_[":status"] = "599";
+  EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+  EXPECT_EQ(599, status_code);
+
   // Invalid status codes.
   headers_[":status"] = "010";
   EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
@@ -203,6 +211,12 @@
   headers_[":status"] = "+20";
   EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
 
+  headers_[":status"] = "-10";
+  EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+  headers_[":status"] = "-100";
+  EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
   // Leading or trailing spaces are also invalid.
   headers_[":status"] = " 200";
   EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
diff --git a/net/quic/quartc/quartc_session.cc b/net/quic/quartc/quartc_session.cc
index 5a8eb532..5f9ca349 100644
--- a/net/quic/quartc/quartc_session.cc
+++ b/net/quic/quartc/quartc_session.cc
@@ -28,7 +28,7 @@
   ~DummyProofSource() override {}
 
   // ProofSource override.
-  bool GetProof(const net::QuicIpAddress& server_ip,
+  bool GetProof(const net::QuicSocketAddress& server_addr,
                 const std::string& hostname,
                 const std::string& server_config,
                 net::QuicVersion quic_version,
@@ -44,7 +44,7 @@
     return true;
   }
 
-  void GetProof(const net::QuicIpAddress& server_ip,
+  void GetProof(const net::QuicSocketAddress& server_addr,
                 const std::string& hostname,
                 const std::string& server_config,
                 net::QuicVersion quic_version,
diff --git a/net/quic/quartc/quartc_session_test.cc b/net/quic/quartc/quartc_session_test.cc
index 6617ad4..a9eb65d 100644
--- a/net/quic/quartc/quartc_session_test.cc
+++ b/net/quic/quartc/quartc_session_test.cc
@@ -52,7 +52,7 @@
   explicit FakeProofSource(bool success) : success_(success) {}
 
   // ProofSource override.
-  bool GetProof(const QuicIpAddress& server_ip,
+  bool GetProof(const QuicSocketAddress& server_ip,
                 const std::string& hostname,
                 const std::string& server_config,
                 net::QuicVersion quic_version,
@@ -70,7 +70,7 @@
     return success_;
   }
 
-  void GetProof(const QuicIpAddress& server_ip,
+  void GetProof(const QuicSocketAddress& server_ip,
                 const std::string& hostname,
                 const std::string& server_config,
                 net::QuicVersion quic_version,
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index 3934ba76..0f756f0e 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -275,14 +275,14 @@
 class FullChloGenerator {
  public:
   FullChloGenerator(QuicCryptoServerConfig* crypto_config,
-                    QuicIpAddress server_ip,
+                    QuicSocketAddress server_addr,
                     QuicSocketAddress client_addr,
                     const QuicClock* clock,
                     scoped_refptr<QuicSignedServerConfig> signed_config,
                     QuicCompressedCertsCache* compressed_certs_cache,
                     CryptoHandshakeMessage* out)
       : crypto_config_(crypto_config),
-        server_ip_(server_ip),
+        server_addr_(server_addr),
         client_addr_(client_addr),
         clock_(clock),
         signed_config_(signed_config),
@@ -314,7 +314,7 @@
       scoped_refptr<ValidateClientHelloResultCallback::Result> result) {
     result_ = result;
     crypto_config_->ProcessClientHello(
-        result_, /*reject_only=*/false, /*connection_id=*/1, server_ip_,
+        result_, /*reject_only=*/false, /*connection_id=*/1, server_addr_,
         client_addr_, AllSupportedVersions().front(), AllSupportedVersions(),
         /*use_stateless_rejects=*/true, /*server_designated_connection_id=*/0,
         clock_, QuicRandom::GetInstance(), compressed_certs_cache_, params_,
@@ -370,7 +370,7 @@
 
  protected:
   QuicCryptoServerConfig* crypto_config_;
-  QuicIpAddress server_ip_;
+  QuicSocketAddress server_addr_;
   QuicSocketAddress client_addr_;
   const QuicClock* clock_;
   scoped_refptr<QuicSignedServerConfig> signed_config_;
@@ -566,12 +566,13 @@
 
 uint64_t CryptoTestUtils::LeafCertHashForTesting() {
   scoped_refptr<ProofSource::Chain> chain;
-  QuicIpAddress server_ip;
+  QuicSocketAddress server_address;
   QuicCryptoProof proof;
   std::unique_ptr<ProofSource> proof_source(
       CryptoTestUtils::ProofSourceForTesting());
-  if (!proof_source->GetProof(server_ip, "", "", AllSupportedVersions().front(),
-                              "", QuicTagVector(), &chain, &proof) ||
+  if (!proof_source->GetProof(server_address, "", "",
+                              AllSupportedVersions().front(), "",
+                              QuicTagVector(), &chain, &proof) ||
       chain->certs.empty()) {
     DCHECK(false) << "Proof generation failed";
     return 0;
@@ -1001,7 +1002,7 @@
 void CryptoTestUtils::GenerateFullCHLO(
     const CryptoHandshakeMessage& inchoate_chlo,
     QuicCryptoServerConfig* crypto_config,
-    QuicIpAddress server_ip,
+    QuicSocketAddress server_addr,
     QuicSocketAddress client_addr,
     QuicVersion version,
     const QuicClock* clock,
@@ -1009,10 +1010,10 @@
     QuicCompressedCertsCache* compressed_certs_cache,
     CryptoHandshakeMessage* out) {
   // Pass a inchoate CHLO.
-  FullChloGenerator generator(crypto_config, server_ip, client_addr, clock,
+  FullChloGenerator generator(crypto_config, server_addr, client_addr, clock,
                               proof, compressed_certs_cache, out);
   crypto_config->ValidateClientHello(
-      inchoate_chlo, client_addr.host(), server_ip, version, clock, proof,
+      inchoate_chlo, client_addr.host(), server_addr, version, clock, proof,
       generator.GetValidateClientHelloCallback());
 }
 
diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h
index 9eef2c0..cf82a0d8 100644
--- a/net/quic/test_tools/crypto_test_utils.h
+++ b/net/quic/test_tools/crypto_test_utils.h
@@ -221,7 +221,7 @@
   static void GenerateFullCHLO(
       const CryptoHandshakeMessage& inchoate_chlo,
       QuicCryptoServerConfig* crypto_config,
-      QuicIpAddress server_ip,
+      QuicSocketAddress server_addr,
       QuicSocketAddress client_addr,
       QuicVersion version,
       const QuicClock* clock,
diff --git a/net/quic/test_tools/crypto_test_utils_test.cc b/net/quic/test_tools/crypto_test_utils_test.cc
index ce0ebe8..b8f662d5 100644
--- a/net/quic/test_tools/crypto_test_utils_test.cc
+++ b/net/quic/test_tools/crypto_test_utils_test.cc
@@ -19,13 +19,13 @@
 class ShloVerifier {
  public:
   ShloVerifier(QuicCryptoServerConfig* crypto_config,
-               QuicIpAddress server_ip,
+               QuicSocketAddress server_addr,
                QuicSocketAddress client_addr,
                const QuicClock* clock,
                scoped_refptr<QuicSignedServerConfig> signed_config,
                QuicCompressedCertsCache* compressed_certs_cache)
       : crypto_config_(crypto_config),
-        server_ip_(server_ip),
+        server_addr_(server_addr),
         client_addr_(client_addr),
         clock_(clock),
         signed_config_(signed_config),
@@ -56,7 +56,7 @@
       const scoped_refptr<ValidateClientHelloResultCallback::Result>& result) {
     result_ = result;
     crypto_config_->ProcessClientHello(
-        result_, /*reject_only=*/false, /*connection_id=*/1, server_ip_,
+        result_, /*reject_only=*/false, /*connection_id=*/1, server_addr_,
         client_addr_, AllSupportedVersions().front(), AllSupportedVersions(),
         /*use_stateless_rejects=*/true, /*server_designated_connection_id=*/0,
         clock_, QuicRandom::GetInstance(), compressed_certs_cache_, params_,
@@ -93,7 +93,7 @@
   }
 
   QuicCryptoServerConfig* crypto_config_;
-  QuicIpAddress server_ip_;
+  QuicSocketAddress server_addr_;
   QuicSocketAddress client_addr_;
   const QuicClock* clock_;
   scoped_refptr<QuicSignedServerConfig> signed_config_;
@@ -108,7 +108,7 @@
   QuicCryptoServerConfig crypto_config(
       QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
       CryptoTestUtils::ProofSourceForTesting());
-  QuicIpAddress server_ip;
+  QuicSocketAddress server_addr;
   QuicSocketAddress client_addr(QuicIpAddress::Loopback4(), 1);
   scoped_refptr<QuicSignedServerConfig> signed_config(
       new QuicSignedServerConfig);
@@ -157,15 +157,15 @@
     nullptr);
   // clang-format on
 
-  CryptoTestUtils::GenerateFullCHLO(inchoate_chlo, &crypto_config, server_ip,
+  CryptoTestUtils::GenerateFullCHLO(inchoate_chlo, &crypto_config, server_addr,
                                     client_addr, version, &clock, signed_config,
                                     &compressed_certs_cache, &full_chlo);
   // Verify that full_chlo can pass crypto_config's verification.
-  ShloVerifier shlo_verifier(&crypto_config, server_ip, client_addr, &clock,
+  ShloVerifier shlo_verifier(&crypto_config, server_addr, client_addr, &clock,
                              signed_config, &compressed_certs_cache);
   crypto_config.ValidateClientHello(
-      full_chlo, client_addr.host(), server_ip, version, &clock, signed_config,
-      shlo_verifier.GetValidateClientHelloCallback());
+      full_chlo, client_addr.host(), server_addr, version, &clock,
+      signed_config, shlo_verifier.GetValidateClientHelloCallback());
 }
 
 }  // namespace test
diff --git a/net/quic/test_tools/fake_proof_source.cc b/net/quic/test_tools/fake_proof_source.cc
index 3e60491..8f64222 100644
--- a/net/quic/test_tools/fake_proof_source.cc
+++ b/net/quic/test_tools/fake_proof_source.cc
@@ -15,14 +15,14 @@
 
 FakeProofSource::~FakeProofSource() {}
 
-FakeProofSource::Params::Params(const QuicIpAddress& server_ip,
+FakeProofSource::Params::Params(const QuicSocketAddress& server_addr,
                                 std::string hostname,
                                 std::string server_config,
                                 QuicVersion quic_version,
                                 std::string chlo_hash,
                                 const QuicTagVector& connection_options,
                                 std::unique_ptr<ProofSource::Callback> callback)
-    : server_ip(server_ip),
+    : server_address(server_addr),
       hostname(hostname),
       server_config(server_config),
       quic_version(quic_version),
@@ -41,7 +41,7 @@
   active_ = true;
 }
 
-bool FakeProofSource::GetProof(const QuicIpAddress& server_ip,
+bool FakeProofSource::GetProof(const QuicSocketAddress& server_address,
                                const string& hostname,
                                const string& server_config,
                                QuicVersion quic_version,
@@ -50,13 +50,13 @@
                                scoped_refptr<ProofSource::Chain>* out_chain,
                                QuicCryptoProof* out_proof) {
   LOG(WARNING) << "Synchronous GetProof called";
-  return delegate_->GetProof(server_ip, hostname, server_config, quic_version,
-                             chlo_hash, connection_options, out_chain,
-                             out_proof);
+  return delegate_->GetProof(server_address, hostname, server_config,
+                             quic_version, chlo_hash, connection_options,
+                             out_chain, out_proof);
 }
 
 void FakeProofSource::GetProof(
-    const QuicIpAddress& server_ip,
+    const QuicSocketAddress& server_address,
     const string& hostname,
     const string& server_config,
     QuicVersion quic_version,
@@ -66,16 +66,17 @@
   if (!active_) {
     scoped_refptr<Chain> chain;
     QuicCryptoProof proof;
-    const bool ok = GetProof(server_ip, hostname, server_config, quic_version,
-                             chlo_hash, connection_options, &chain, &proof);
+    const bool ok =
+        GetProof(server_address, hostname, server_config, quic_version,
+                 chlo_hash, connection_options, &chain, &proof);
     callback->Run(ok, chain, proof, /* details = */ nullptr);
     return;
   }
 
   LOG(WARNING) << "Asynchronous GetProof called";
-  params_.push_back(Params{server_ip, hostname, server_config, quic_version,
-                           chlo_hash.as_string(), connection_options,
-                           std::move(callback)});
+  params_.push_back(Params{server_address, hostname, server_config,
+                           quic_version, chlo_hash.as_string(),
+                           connection_options, std::move(callback)});
 }
 
 int FakeProofSource::NumPendingCallbacks() const {
@@ -90,7 +91,7 @@
   scoped_refptr<ProofSource::Chain> chain;
   QuicCryptoProof proof;
   const bool ok = delegate_->GetProof(
-      params.server_ip, params.hostname, params.server_config,
+      params.server_address, params.hostname, params.server_config,
       params.quic_version, params.chlo_hash, params.connection_options, &chain,
       &proof);
 
diff --git a/net/quic/test_tools/fake_proof_source.h b/net/quic/test_tools/fake_proof_source.h
index 93ee287..fb1c3a3a 100644
--- a/net/quic/test_tools/fake_proof_source.h
+++ b/net/quic/test_tools/fake_proof_source.h
@@ -30,7 +30,7 @@
   void Activate();
 
   // ProofSource interface
-  bool GetProof(const QuicIpAddress& server_ip,
+  bool GetProof(const QuicSocketAddress& server_address,
                 const std::string& hostname,
                 const std::string& server_config,
                 QuicVersion quic_version,
@@ -38,7 +38,7 @@
                 const QuicTagVector& connection_options,
                 scoped_refptr<ProofSource::Chain>* out_chain,
                 QuicCryptoProof* out_proof) override;
-  void GetProof(const QuicIpAddress& server_ip,
+  void GetProof(const QuicSocketAddress& server_address,
                 const std::string& hostname,
                 const std::string& server_config,
                 QuicVersion quic_version,
@@ -58,7 +58,7 @@
   bool active_ = false;
 
   struct Params {
-    Params(const QuicIpAddress& server_ip,
+    Params(const QuicSocketAddress& server_addr,
            std::string hostname,
            std::string server_config,
            QuicVersion quic_version,
@@ -69,7 +69,7 @@
     Params(Params&& other);
     Params& operator=(Params&& other);
 
-    QuicIpAddress server_ip;
+    QuicSocketAddress server_address;
     std::string hostname;
     std::string server_config;
     QuicVersion quic_version;
diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc
index 1453a5b..7a6dbff 100644
--- a/net/quic/test_tools/mock_crypto_client_stream.cc
+++ b/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -108,6 +108,12 @@
   if (event == QuicSession::HANDSHAKE_CONFIRMED) {
     handshake_confirmed_ = true;
     SetConfigNegotiated();
+    session()->connection()->SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+                                          QuicDecrypter::Create(kNULL));
+    session()->connection()->SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+                                          QuicEncrypter::Create(kNULL));
+    session()->connection()->SetDefaultEncryptionLevel(
+        ENCRYPTION_FORWARD_SECURE);
   }
   session()->OnCryptoHandshakeEvent(event);
 }
diff --git a/net/quic/test_tools/quic_sent_packet_manager_peer.cc b/net/quic/test_tools/quic_sent_packet_manager_peer.cc
index 7c4e7935..eb225aa8 100644
--- a/net/quic/test_tools/quic_sent_packet_manager_peer.cc
+++ b/net/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -46,12 +46,6 @@
 }
 
 // static
-QuicByteCount QuicSentPacketManagerPeer::GetReceiveWindow(
-    QuicSentPacketManager* sent_packet_manager) {
-  return sent_packet_manager->receive_buffer_bytes_;
-}
-
-// static
 void QuicSentPacketManagerPeer::SetPerspective(
     QuicSentPacketManager* sent_packet_manager,
     Perspective perspective) {
diff --git a/net/quic/test_tools/quic_sent_packet_manager_peer.h b/net/quic/test_tools/quic_sent_packet_manager_peer.h
index db6250bb..210669c 100644
--- a/net/quic/test_tools/quic_sent_packet_manager_peer.h
+++ b/net/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -32,9 +32,6 @@
 
   static bool GetUndoRetransmits(QuicSentPacketManager* sent_packet_manager);
 
-  static QuicByteCount GetReceiveWindow(
-      QuicSentPacketManager* sent_packet_manager);
-
   static void SetPerspective(QuicSentPacketManager* sent_packet_manager,
                              Perspective perspective);
 
diff --git a/net/quic/test_tools/quic_test_packet_maker.cc b/net/quic/test_tools/quic_test_packet_maker.cc
index 4cbbc1f..f865171 100644
--- a/net/quic/test_tools/quic_test_packet_maker.cc
+++ b/net/quic/test_tools/quic_test_packet_maker.cc
@@ -347,6 +347,7 @@
     bool fin,
     SpdyPriority priority,
     SpdyHeaderBlock headers,
+    QuicStreamOffset* header_stream_offset,
     size_t* spdy_headers_frame_length,
     const std::vector<std::string>& data_writes) {
   InitializeHeader(packet_number, should_include_version);
@@ -360,12 +361,16 @@
   if (spdy_headers_frame_length) {
     *spdy_headers_frame_length = spdy_frame.size();
   }
-  QuicStreamFrame frame(
-      kHeadersStreamId, false, 0,
-      base::StringPiece(spdy_frame.data(), spdy_frame.size()));
-
   QuicFrames frames;
+  QuicStreamOffset header_offset =
+      header_stream_offset == nullptr ? 0 : *header_stream_offset;
+  QuicStreamFrame frame(
+      kHeadersStreamId, false, header_offset,
+      base::StringPiece(spdy_frame.data(), spdy_frame.size()));
   frames.push_back(QuicFrame(&frame));
+  if (header_stream_offset != nullptr) {
+    *header_stream_offset += spdy_frame.size();
+  }
 
   QuicStreamOffset offset = 0;
   // QuicFrame takes a raw pointer. Use a std::vector here so we keep
@@ -628,5 +633,31 @@
   header_.packet_number = packet_number;
 }
 
+std::unique_ptr<QuicReceivedPacket> QuicTestPacketMaker::MakeSettingsPacket(
+    QuicPacketNumber packet_number,
+    SpdySettingsIds id,
+    size_t value,
+    bool should_include_version,
+    QuicStreamOffset* offset) {
+  SpdySettingsIR settings_frame;
+  settings_frame.AddSetting(id, false, false, value);
+  SpdySerializedFrame spdy_frame(
+      spdy_request_framer_.SerializeFrame(settings_frame));
+  InitializeHeader(packet_number, should_include_version);
+  if (offset != nullptr) {
+    QuicStreamFrame quic_frame(
+        kHeadersStreamId, false, *offset,
+        StringPiece(spdy_frame.data(), spdy_frame.size()));
+    *offset += spdy_frame.size();
+    return MakePacket(header_, QuicFrame(&quic_frame));
+  } else {
+    QuicStreamFrame quic_frame(
+        kHeadersStreamId, false, 0,
+        StringPiece(spdy_frame.data(), spdy_frame.size()));
+    LOG(INFO) << "quic_frame: " << quic_frame;
+    return MakePacket(header_, QuicFrame(&quic_frame));
+  }
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/test_tools/quic_test_packet_maker.h b/net/quic/test_tools/quic_test_packet_maker.h
index 944e1461d..9c86edb 100644
--- a/net/quic/test_tools/quic_test_packet_maker.h
+++ b/net/quic/test_tools/quic_test_packet_maker.h
@@ -122,6 +122,7 @@
       bool fin,
       SpdyPriority priority,
       SpdyHeaderBlock headers,
+      QuicStreamOffset* header_stream_offset,
       size_t* spdy_headers_frame_length,
       const std::vector<std::string>& data_writes);
 
@@ -198,6 +199,13 @@
                                               SpdyHeaderBlock headers,
                                               QuicStreamOffset* offset);
 
+  std::unique_ptr<QuicReceivedPacket> MakeSettingsPacket(
+      QuicPacketNumber packet_number,
+      SpdySettingsIds id,
+      size_t value,
+      bool should_include_version,
+      QuicStreamOffset* offset);
+
   SpdyHeaderBlock GetRequestHeaders(const std::string& method,
                                     const std::string& scheme,
                                     const std::string& path);
diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc
index 0fc4875..5d338bcb 100644
--- a/net/socket/client_socket_handle.cc
+++ b/net/socket/client_socket_handle.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/log/net_log_event_type.h"
 #include "net/socket/client_socket_pool.h"
 
@@ -137,7 +138,7 @@
 }
 
 void ClientSocketHandle::OnIOComplete(int result) {
-  TRACE_EVENT0("net", "ClientSocketHandle::OnIOComplete");
+  TRACE_EVENT0(kNetTracingCategory, "ClientSocketHandle::OnIOComplete");
   CompletionCallback callback = user_callback_;
   user_callback_.Reset();
   HandleInitCompletion(result);
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index c4307fb..252ca29 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -14,11 +14,15 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source.h"
@@ -128,7 +132,7 @@
 }
 
 void ConnectJob::NotifyDelegateOfCompletion(int rv) {
-  TRACE_EVENT0("net", "ConnectJob::NotifyDelegateOfCompletion");
+  TRACE_EVENT0(kNetTracingCategory, "ConnectJob::NotifyDelegateOfCompletion");
   // The delegate will own |this|.
   Delegate* delegate = delegate_;
   delegate_ = NULL;
@@ -697,6 +701,29 @@
   return dict;
 }
 
+void ClientSocketPoolBaseHelper::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {
+  base::trace_event::MemoryAllocatorDump* socket_pool_dump = nullptr;
+  size_t socket_count = 0;
+  for (const auto& kv : group_map_) {
+    for (const auto& socket : kv.second->idle_sockets()) {
+      // Only create a MemoryAllocatorDump if there is at least one idle socket
+      if (!socket_pool_dump) {
+        socket_pool_dump = pmd->CreateAllocatorDump(base::StringPrintf(
+            "%s/socket_pool", parent_dump_absolute_name.c_str()));
+      }
+      socket.socket->DumpMemoryStats(pmd, socket_pool_dump->absolute_name());
+      ++socket_count;
+    }
+  }
+  if (socket_pool_dump) {
+    socket_pool_dump->AddScalar(
+        base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+        base::trace_event::MemoryAllocatorDump::kUnitsObjects, socket_count);
+  }
+}
+
 bool ClientSocketPoolBaseHelper::IdleSocket::IsUsable() const {
   if (socket->WasEverUsed())
     return socket->IsConnectedAndIdle();
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index 1926021..f221f8e3 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -56,6 +56,9 @@
 
 namespace base {
 class DictionaryValue;
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace net {
@@ -338,6 +341,11 @@
       const std::string& name,
       const std::string& type) const;
 
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_dump_absolute_name) const;
+
   base::TimeDelta ConnectionTimeout() const {
     return connect_job_factory_->ConnectionTimeout();
   }
@@ -793,6 +801,11 @@
     return helper_.GetLoadState(group_name, handle);
   }
 
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_dump_absolute_name) const {
+    return helper_.DumpMemoryStats(pmd, parent_dump_absolute_name);
+  }
+
   virtual void OnConnectJobComplete(int result, ConnectJob* job) {
     return helper_.OnConnectJobComplete(result, job);
   }
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index 8482dc2..f0b4b6f 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -4,8 +4,6 @@
 
 #include "net/socket/client_socket_pool_manager.h"
 
-#include <string>
-
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/load_flags.h"
diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h
index 553a348..9c0dae8 100644
--- a/net/socket/client_socket_pool_manager.h
+++ b/net/socket/client_socket_pool_manager.h
@@ -9,6 +9,8 @@
 #ifndef NET_SOCKET_CLIENT_SOCKET_POOL_MANAGER_H_
 #define NET_SOCKET_CLIENT_SOCKET_POOL_MANAGER_H_
 
+#include <string>
+
 #include "net/base/completion_callback.h"
 #include "net/base/net_export.h"
 #include "net/base/request_priority.h"
@@ -16,6 +18,9 @@
 
 namespace base {
 class Value;
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace net {
@@ -83,6 +88,12 @@
       const HostPortPair& proxy_server) = 0;
   // Creates a Value summary of the state of the socket pools.
   virtual std::unique_ptr<base::Value> SocketPoolInfoToValue() const = 0;
+
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  virtual void DumpMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd,
+      const std::string& parent_dump_absolute_name) const = 0;
 };
 
 // A helper method that uses the passed in proxy information to initialize a
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index 5135248e..26348cbb7 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -360,4 +360,10 @@
   FlushSocketPoolsWithError(ERR_NETWORK_CHANGED);
 }
 
+void ClientSocketPoolManagerImpl::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {
+  return ssl_socket_pool_->DumpMemoryStats(pmd, parent_dump_absolute_name);
+}
+
 }  // namespace net
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index c378ed9..f2f4e7e 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <string>
 #include <type_traits>
 
 #include "base/compiler_specific.h"
@@ -17,6 +18,12 @@
 #include "net/http/http_network_session.h"
 #include "net/socket/client_socket_pool_manager.h"
 
+namespace base {
+namespace trace_event {
+class ProcessMemoryDump;
+}
+}
+
 namespace net {
 
 class CertVerifier;
@@ -74,6 +81,10 @@
   // CertDatabase::Observer methods:
   void OnCertDBChanged(const X509Certificate* cert) override;
 
+  void DumpMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd,
+      const std::string& parent_dump_absolute_name) const override;
+
  private:
   using TransportSocketPoolMap =
       std::map<HostPortPair, std::unique_ptr<TransportClientSocketPool>>;
diff --git a/net/socket/mock_client_socket_pool_manager.cc b/net/socket/mock_client_socket_pool_manager.cc
index 3f33b645..3c80ac6 100644
--- a/net/socket/mock_client_socket_pool_manager.cc
+++ b/net/socket/mock_client_socket_pool_manager.cc
@@ -93,4 +93,8 @@
   return std::unique_ptr<base::Value>(nullptr);
 }
 
+void MockClientSocketPoolManager::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {}
+
 }  // namespace net
diff --git a/net/socket/mock_client_socket_pool_manager.h b/net/socket/mock_client_socket_pool_manager.h
index 379065a..1800437 100644
--- a/net/socket/mock_client_socket_pool_manager.h
+++ b/net/socket/mock_client_socket_pool_manager.h
@@ -5,6 +5,8 @@
 #ifndef NET_SOCKET_MOCK_CLIENT_SOCKET_POOL_MANAGER_H_
 #define NET_SOCKET_MOCK_CLIENT_SOCKET_POOL_MANAGER_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "net/socket/client_socket_pool_manager_impl.h"
@@ -39,6 +41,9 @@
   SSLClientSocketPool* GetSocketPoolForSSLWithProxy(
       const HostPortPair& proxy_server) override;
   std::unique_ptr<base::Value> SocketPoolInfoToValue() const override;
+  void DumpMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd,
+      const std::string& parent_dump_absolute_name) const override;
 
  private:
   using TransportSocketPoolMap =
diff --git a/net/socket/socket_bio_adapter.cc b/net/socket/socket_bio_adapter.cc
index 1241bae..0d75e68 100644
--- a/net/socket/socket_bio_adapter.cc
+++ b/net/socket/socket_bio_adapter.cc
@@ -53,6 +53,16 @@
   return read_result_ > 0;
 }
 
+size_t SocketBIOAdapter::GetAllocationSize() const {
+  size_t buffer_size = 0;
+  if (read_buffer_)
+    buffer_size += read_buffer_capacity_;
+
+  if (write_buffer_)
+    buffer_size += write_buffer_capacity_;
+  return buffer_size;
+}
+
 int SocketBIOAdapter::BIORead(char* out, int len) {
   if (len <= 0)
     return len;
diff --git a/net/socket/socket_bio_adapter.h b/net/socket/socket_bio_adapter.h
index d718721..235917a 100644
--- a/net/socket/socket_bio_adapter.h
+++ b/net/socket/socket_bio_adapter.h
@@ -78,6 +78,9 @@
   // but not yet consumed by the BIO.
   bool HasPendingReadData();
 
+  // Returns the allocation size estimate in bytes.
+  size_t GetAllocationSize() const;
+
  private:
   int BIORead(char* out, int len);
   void HandleSocketReadResult(int result);
diff --git a/net/socket/socket_posix.cc b/net/socket/socket_posix.cc
index a86bf855..09d9baec 100644
--- a/net/socket/socket_posix.cc
+++ b/net/socket/socket_posix.cc
@@ -19,6 +19,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/sockaddr_storage.h"
+#include "net/base/trace_constants.h"
 
 namespace net {
 
@@ -369,7 +370,8 @@
 }
 
 void SocketPosix::OnFileCanReadWithoutBlocking(int fd) {
-  TRACE_EVENT0("net", "SocketPosix::OnFileCanReadWithoutBlocking");
+  TRACE_EVENT0(kNetTracingCategory,
+               "SocketPosix::OnFileCanReadWithoutBlocking");
   DCHECK(!accept_callback_.is_null() || !read_callback_.is_null());
   if (!accept_callback_.is_null()) {
     AcceptCompleted();
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index 7c91d2f..2ace6e76 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -4,7 +4,6 @@
 
 #include "net/socket/ssl_client_socket.h"
 
-#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/strings/string_util.h"
@@ -17,13 +16,6 @@
 
 namespace net {
 
-namespace {
-#if !defined(OS_NACL)
-const base::Feature kPostQuantumExperiment{"SSLPostQuantumExperiment",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-}  // namespace
-
 SSLClientSocket::SSLClientSocket()
     : signed_cert_timestamps_received_(false),
       stapled_ocsp_response_received_(false) {}
@@ -47,15 +39,6 @@
 }
 
 // static
-bool SSLClientSocket::IsPostQuantumExperimentEnabled() {
-#if !defined(OS_NACL)
-  return base::FeatureList::IsEnabled(kPostQuantumExperiment);
-#else
-  return false;
-#endif
-}
-
-// static
 std::vector<uint8_t> SSLClientSocket::SerializeNextProtos(
     const NextProtoVector& next_protos) {
   std::vector<uint8_t> wire_protos;
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index e90c733..89ac601 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/strings/string_piece.h"
@@ -115,11 +116,6 @@
   // establishing the connection (or NULL if no channel ID was used).
   virtual crypto::ECPrivateKey* GetChannelIDKey() const = 0;
 
-  // Returns true if the CECPQ1 (experimental post-quantum) experiment is
-  // enabled.  This should be removed after the experiment is ended, around
-  // 2017-18.
-  static bool IsPostQuantumExperimentEnabled();
-
  protected:
   void set_signed_cert_timestamps_received(
       bool signed_cert_timestamps_received) {
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index d20d81e2..7832819 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <string.h>
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -21,14 +22,18 @@
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_local.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/openssl_util.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_ev_whitelist.h"
 #include "net/cert/ct_policy_enforcer.h"
@@ -802,6 +807,50 @@
   return transport_->socket()->GetTotalReceivedBytes();
 }
 
+void SSLClientSocketImpl::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {
+  size_t total_size = 0;
+  base::trace_event::MemoryAllocatorDump* socket_dump =
+      pmd->CreateAllocatorDump(base::StringPrintf(
+          "%s/ssl_socket_%p", parent_dump_absolute_name.c_str(), this));
+  if (transport_adapter_) {
+    size_t bio_buffers_size = transport_adapter_->GetAllocationSize();
+    socket_dump->AddScalar("buffer_size",
+                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                           bio_buffers_size);
+    total_size += bio_buffers_size;
+  }
+  size_t total_cert_size = 0;
+  size_t certs_count = 0;
+  if (server_cert_chain_) {
+    certs_count = server_cert_chain_->size();
+    for (size_t i = 0; i < certs_count; ++i) {
+      X509* cert = server_cert_chain_->Get(i);
+      total_cert_size += i2d_X509(cert, nullptr);
+    }
+  }
+  total_size += total_cert_size;
+  // This measures the lower bound of the serialized certificate. It doesn't
+  // measure the actual memory used, which is 4x this amount (see
+  // crbug.com/671420 for more details).
+  socket_dump->AddScalar("serialized_cert_size",
+                         base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                         total_cert_size);
+  socket_dump->AddScalar("cert_count",
+                         base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                         certs_count);
+  socket_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                         base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                         total_size);
+}
+
+// static
+void SSLClientSocketImpl::DumpSSLClientSessionMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd) {
+  SSLContext::GetInstance()->session_cache()->DumpMemoryStats(pmd);
+}
+
 int SSLClientSocketImpl::Read(IOBuffer* buf,
                               int buf_len,
                               const CompletionCallback& callback) {
@@ -937,27 +986,6 @@
   SSL_set_mode(ssl_.get(), mode.set_mask);
   SSL_clear_mode(ssl_.get(), mode.clear_mask);
 
-  std::string command;
-  if (SSLClientSocket::IsPostQuantumExperimentEnabled()) {
-    // These are experimental, non-standard ciphersuites.  They are part of an
-    // experiment in post-quantum cryptography.  They're not intended to
-    // represent a de-facto standard, and will be removed from BoringSSL in
-    // ~2018.
-    if (EVP_has_aes_hardware()) {
-      command.append(
-          "CECPQ1-RSA-AES256-GCM-SHA384:"
-          "CECPQ1-ECDSA-AES256-GCM-SHA384:");
-    }
-    command.append(
-        "CECPQ1-RSA-CHACHA20-POLY1305-SHA256:"
-        "CECPQ1-ECDSA-CHACHA20-POLY1305-SHA256:");
-    if (!EVP_has_aes_hardware()) {
-      command.append(
-          "CECPQ1-RSA-AES256-GCM-SHA384:"
-          "CECPQ1-ECDSA-AES256-GCM-SHA384:");
-    }
-  }
-
   // Use BoringSSL defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
   // (note that SHA256 and SHA384 only select legacy CBC ciphers). Also disable
   // DHE_RSA_WITH_AES_256_GCM_SHA384. Historically, AES_256_GCM was not
@@ -966,7 +994,8 @@
   //
   // TODO(davidben): Remove the DHE_RSA_WITH_AES_256_GCM_SHA384 exclusion when
   // the DHEEnabled administrative policy expires.
-  command.append("ALL:!SHA256:!SHA384:!DHE-RSA-AES256-GCM-SHA384:!aPSK:!RC4");
+  std::string command(
+      "ALL:!SHA256:!SHA384:!DHE-RSA-AES256-GCM-SHA384:!aPSK:!RC4");
 
   if (ssl_config_.require_ecdhe)
     command.append(":!kRSA:!kDHE");
@@ -1330,7 +1359,7 @@
 }
 
 int SSLClientSocketImpl::DoHandshakeLoop(int last_io_result) {
-  TRACE_EVENT0("net", "SSLClientSocketImpl::DoHandshakeLoop");
+  TRACE_EVENT0(kNetTracingCategory, "SSLClientSocketImpl::DoHandshakeLoop");
   int rv = last_io_result;
   do {
     // Default to STATE_NONE for next state.
diff --git a/net/socket/ssl_client_socket_impl.h b/net/socket/ssl_client_socket_impl.h
index de677ba..3070daf 100644
--- a/net/socket/ssl_client_socket_impl.h
+++ b/net/socket/ssl_client_socket_impl.h
@@ -37,6 +37,9 @@
 namespace base {
 class FilePath;
 class SequencedTaskRunner;
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace crypto {
@@ -114,6 +117,13 @@
   void ClearConnectionAttempts() override {}
   void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
   int64_t GetTotalReceivedBytes() const override;
+  void DumpMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd,
+      const std::string& parent_dump_absolute_name) const override;
+
+  // Dumps memory allocation stats. |pmd| is the browser process memory dump.
+  static void DumpSSLClientSessionMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd);
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index b8ca341..d98f6b00 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -16,6 +16,7 @@
 #include "base/values.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/http/http_proxy_client_socket.h"
 #include "net/http/http_proxy_client_socket_pool.h"
 #include "net/log/net_log_source_type.h"
@@ -176,7 +177,7 @@
 }
 
 int SSLConnectJob::DoLoop(int result) {
-  TRACE_EVENT0("net", "SSLConnectJob::DoLoop");
+  TRACE_EVENT0(kNetTracingCategory, "SSLConnectJob::DoLoop");
   DCHECK_NE(next_state_, STATE_NONE);
 
   int rv = result;
@@ -294,7 +295,7 @@
 }
 
 int SSLConnectJob::DoSSLConnect() {
-  TRACE_EVENT0("net", "SSLConnectJob::DoSSLConnect");
+  TRACE_EVENT0(kNetTracingCategory, "SSLConnectJob::DoSSLConnect");
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/462815 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION("462815 SSLConnectJob::DoSSLConnect"));
@@ -376,9 +377,6 @@
         SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
     UMA_HISTOGRAM_SPARSE_SLOWLY("Net.SSL_CipherSuite", cipher_suite);
 
-    const SSL_CIPHER* cipher = SSL_get_cipher_by_value(cipher_suite);
-    bool is_cecpq1 = cipher && SSL_CIPHER_is_CECPQ1(cipher);
-
     if (ssl_info.key_exchange_group != 0) {
       UMA_HISTOGRAM_SPARSE_SLOWLY("Net.SSL_KeyExchange.ECDHE",
                                   ssl_info.key_exchange_group);
@@ -422,27 +420,6 @@
                                    base::TimeDelta::FromMilliseconds(1),
                                    base::TimeDelta::FromMinutes(1),
                                    100);
-
-        // These are hosts that we expect to always offer CECPQ1.  Connections
-        // to them, whether or not this browser is in the experiment group, form
-        // the basis of our comparisons.
-        bool cecpq1_expected_to_be_offered =
-            ssl_info.is_issued_by_known_root &&
-            (host == "play.google.com" || host == "checkout.google.com" ||
-             host == "wallet.google.com");
-        if (cecpq1_expected_to_be_offered) {
-          UMA_HISTOGRAM_CUSTOM_TIMES(
-              "Net.SSL_Connection_Latency_PostQuantumSupported_Full_Handshake",
-              connect_duration, base::TimeDelta::FromMilliseconds(1),
-              base::TimeDelta::FromMinutes(1), 100);
-          if (SSLClientSocket::IsPostQuantumExperimentEnabled()) {
-            // But don't trust that these hosts offer CECPQ1: make sure.  If
-            // we're doing everything right on the server side, |is_cecpq1|
-            // should always be true if we get here, modulo MITM.
-            UMA_HISTOGRAM_BOOLEAN("Net.SSL_Connection_PostQuantum_Negotiated",
-                                  is_cecpq1);
-          }
-        }
       }
     }
   }
@@ -637,6 +614,12 @@
   return base_.GetLoadState(group_name, handle);
 }
 
+void SSLClientSocketPool::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_dump_absolute_name) const {
+  base_.DumpMemoryStats(pmd, parent_dump_absolute_name);
+}
+
 std::unique_ptr<base::DictionaryValue> SSLClientSocketPool::GetInfoAsValue(
     const std::string& name,
     const std::string& type,
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
index e7b7e8c..c02fe98 100644
--- a/net/socket/ssl_client_socket_pool.h
+++ b/net/socket/ssl_client_socket_pool.h
@@ -233,6 +233,11 @@
   LoadState GetLoadState(const std::string& group_name,
                          const ClientSocketHandle* handle) const override;
 
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_dump_absolute_name) const;
+
   std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
       const std::string& name,
       const std::string& type,
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index b80aa4d..d8c48e00 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -18,6 +18,10 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
 #include "net/base/address_list.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
@@ -3674,4 +3678,64 @@
   EXPECT_THAT(rv, IsError(ERR_BAD_SSL_CLIENT_AUTH_CERT));
 }
 
+// Basic test for dumping memory stats.
+TEST_F(SSLClientSocketTest, DumpMemoryStats) {
+  ASSERT_TRUE(StartTestServer(SpawnedTestServer::SSLOptions()));
+
+  int rv;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(SSLConfig(), &rv));
+  EXPECT_THAT(rv, IsOk());
+
+  base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
+      new base::trace_event::ProcessMemoryDump(nullptr, dump_args));
+  base::trace_event::MemoryAllocatorDump* parent_dump1 =
+      process_memory_dump->CreateAllocatorDump("parent1");
+  sock_->DumpMemoryStats(process_memory_dump.get(),
+                         parent_dump1->absolute_name());
+
+  // Read the response without writing a request, so the read will be pending.
+  TestCompletionCallback read_callback;
+  scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
+  rv = sock_->Read(buf.get(), 4096, read_callback.callback());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Dump memory again and check that |buffer_size| contain the read buffer.
+  base::trace_event::MemoryAllocatorDump* parent_dump2 =
+      process_memory_dump->CreateAllocatorDump("parent2");
+  sock_->DumpMemoryStats(process_memory_dump.get(),
+                         parent_dump2->absolute_name());
+
+  const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
+      allocator_dumps = process_memory_dump->allocator_dumps();
+  bool did_dump[] = {false, false};
+  // Checks that there are two dumps because DumpMemoryStats() is invoked twice.
+  for (const auto& pair : allocator_dumps) {
+    const std::string& dump_name = pair.first;
+    if (dump_name.find("ssl_socket") == std::string::npos)
+      return;
+    std::unique_ptr<base::Value> raw_attrs =
+        pair.second->attributes_for_testing()->ToBaseValue();
+    base::DictionaryValue* attrs;
+    ASSERT_TRUE(raw_attrs->GetAsDictionary(&attrs));
+    base::DictionaryValue* buffer_size_attrs;
+    ASSERT_TRUE(attrs->GetDictionary("buffer_size", &buffer_size_attrs));
+    std::string buffer_size;
+    ASSERT_TRUE(buffer_size_attrs->GetString("value", &buffer_size));
+    ASSERT_TRUE(attrs->HasKey("serialized_cert_size"));
+    ASSERT_TRUE(attrs->HasKey("cert_count"));
+    if (dump_name.find("parent1/ssl_socket") != std::string::npos) {
+      did_dump[0] = true;
+      ASSERT_EQ("0", buffer_size);
+    }
+    if (dump_name.find("parent2/ssl_socket") != std::string::npos) {
+      did_dump[1] = true;
+      // The read buffer is not released, so |buffer_size| can't be 0.
+      ASSERT_NE("0", buffer_size);
+    }
+  }
+  EXPECT_THAT(did_dump, testing::ElementsAre(true, true));
+}
+
 }  // namespace net
diff --git a/net/socket/stream_socket.h b/net/socket/stream_socket.h
index b038d9b..232cb449 100644
--- a/net/socket/stream_socket.h
+++ b/net/socket/stream_socket.h
@@ -13,6 +13,12 @@
 #include "net/socket/next_proto.h"
 #include "net/socket/socket.h"
 
+namespace base {
+namespace trace_event {
+class ProcessMemoryDump;
+}
+}
+
 namespace net {
 
 class IPEndPoint;
@@ -110,6 +116,13 @@
   // Disconnect() is called.
   virtual int64_t GetTotalReceivedBytes() const = 0;
 
+  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
+  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
+  // Default implementation does nothing.
+  virtual void DumpMemoryStats(
+      base::trace_event::ProcessMemoryDump* pmd,
+      const std::string& parent_dump_absolute_name) const {};
+
  protected:
   // The following class is only used to gather statistics about the history of
   // a socket.  It is only instantiated and used in basic sockets, such as
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 9df352a..3cbbd61 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -19,6 +19,7 @@
 #include "base/values.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source_type.h"
@@ -268,7 +269,8 @@
 }
 
 int TransportConnectJob::DoResolveHostComplete(int result) {
-  TRACE_EVENT0("net", "TransportConnectJob::DoResolveHostComplete");
+  TRACE_EVENT0(kNetTracingCategory,
+               "TransportConnectJob::DoResolveHostComplete");
   connect_timing_.dns_end = base::TimeTicks::Now();
   // Overwrite connection start time, since for connections that do not go
   // through proxies, |connect_start| should not include dns lookup time.
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index 8b8c227..10b6762 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -27,6 +27,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/network_activity_monitor.h"
 #include "net/base/sockaddr_storage.h"
+#include "net/base/trace_constants.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source.h"
@@ -574,7 +575,7 @@
 }
 
 void UDPSocketPosix::ReadWatcher::OnFileCanReadWithoutBlocking(int) {
-  TRACE_EVENT0("net",
+  TRACE_EVENT0(kNetTracingCategory,
                "UDPSocketPosix::ReadWatcher::OnFileCanReadWithoutBlocking");
   if (!socket_->read_callback_.is_null())
     socket_->DidCompleteRead();
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
index 32ba290..0239226 100644
--- a/net/socket/websocket_transport_client_socket_pool.cc
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_source_type.h"
@@ -124,7 +125,8 @@
 }
 
 int WebSocketTransportConnectJob::DoResolveHostComplete(int result) {
-  TRACE_EVENT0("net", "WebSocketTransportConnectJob::DoResolveHostComplete");
+  TRACE_EVENT0(kNetTracingCategory,
+               "WebSocketTransportConnectJob::DoResolveHostComplete");
   connect_timing_.dns_end = base::TimeTicks::Now();
   // Overwrite connection start time, since for connections that do not go
   // through proxies, |connect_start| should not include dns lookup time.
diff --git a/net/spdy/hpack/hpack_decoder2.cc b/net/spdy/hpack/hpack_decoder2.cc
new file mode 100644
index 0000000..03b1c2e
--- /dev/null
+++ b/net/spdy/hpack/hpack_decoder2.cc
@@ -0,0 +1,330 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack/hpack_decoder2.h"
+
+#include <list>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/spdy/hpack/hpack_entry.h"
+
+using base::StringPiece;
+
+namespace net {
+
+HpackDecoder2::HpackDecoder2() : hpack_block_decoder_(this) {
+  Reset();
+}
+
+HpackDecoder2::~HpackDecoder2() {}
+
+void HpackDecoder2::Reset() {
+  DVLOG(2) << "HpackDecoder2::Reset";
+  handler_ = nullptr;
+
+  hpack_block_decoder_.Reset();
+  hpack_block_decoder_.set_listener(this);
+
+  total_hpack_bytes_ = 0;
+  total_header_bytes_ = 0;
+  size_update_count_ = 0;
+  header_seen_ = false;
+  in_progress_ = false;
+  error_detected_ = false;
+  header_block_started_ = false;
+
+  name_.Reset();
+  value_.Reset();
+}
+
+void HpackDecoder2::SetErrorDetected() {
+  if (!error_detected_) {
+    DVLOG(2) << "HpackDecoder2::SetErrorDetected";
+    hpack_block_decoder_.set_listener(&no_op_listener_);
+    error_detected_ = true;
+  }
+}
+
+void HpackDecoder2::ApplyHeaderTableSizeSetting(size_t size_setting) {
+  DVLOG(2) << "HpackDecoder2::ApplyHeaderTableSizeSetting";
+  header_table_.SetSettingsHeaderTableSize(size_setting);
+}
+
+// If a SpdyHeadersHandlerInterface is provided, the decoder will emit
+// headers to it rather than accumulating them in a SpdyHeaderBlock.
+void HpackDecoder2::HandleControlFrameHeadersStart(
+    SpdyHeadersHandlerInterface* handler) {
+  DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersStart";
+  DCHECK(!header_block_started_);
+  handler_ = handler;
+}
+
+// Called as HPACK block fragments arrive. Returns false
+// if an error occurred while decoding the block.
+bool HpackDecoder2::HandleControlFrameHeadersData(const char* headers_data,
+                                                  size_t headers_data_length) {
+  DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersData: len="
+           << headers_data_length;
+  if (!header_block_started_) {
+    DCHECK_EQ(total_hpack_bytes_, 0u);
+    // Clear the SpdyHeaderBlock here rather than in Reset so that it is NOT
+    // cleared in HandleControlFrameHeadersComplete, which would be before it
+    // could be used.
+    decoded_block_.clear();
+    header_block_started_ = true;
+    if (handler_ != nullptr) {
+      handler_->OnHeaderBlockStart();
+    }
+  }
+
+  // Sometimes we get a call with headers_data==nullptr and
+  // headers_data_length==0, in which case we need to avoid creating
+  // a DecodeBuffer, which would otherwise complain.
+  if (headers_data_length > 0) {
+    DCHECK_NE(headers_data, nullptr);
+    total_hpack_bytes_ += headers_data_length;
+    DecodeBuffer db(headers_data, headers_data_length);
+    DecodeStatus status = hpack_block_decoder_.Decode(&db);
+    switch (status) {
+      case DecodeStatus::kDecodeDone:
+        // We've completed the decoding of headers_data, and it ended at the
+        // boundary between two HPACK block entries, so name_ and value_ are
+        // currently reset.
+        DCHECK_EQ(0u, db.Remaining());
+        in_progress_ = false;
+        break;
+
+      case DecodeStatus::kDecodeInProgress:
+        DCHECK_EQ(0u, db.Remaining());
+        in_progress_ = true;
+        if (!error_detected_) {
+          name_.BufferStringIfUnbuffered();
+          value_.BufferStringIfUnbuffered();
+          EnforceMaxDecodeBufferSize();
+        }
+        break;
+
+      case DecodeStatus::kDecodeError:
+        SetErrorDetected();
+        break;
+    }
+  }
+  return !error_detected_;
+}
+
+// Called after a HPACK block has been completely delivered via
+// HandleControlFrameHeadersData(). Returns false if an error occurred.
+// |compressed_len| if non-null will be set to the size of the encoded
+// buffered block that was accumulated in HandleControlFrameHeadersData(),
+// to support subsequent calculation of compression percentage.
+// Discards the handler supplied at the start of decoding the block.
+// TODO(jamessynge): Determine if compressed_len is needed; it is used to
+// produce UUMA stat Net.SpdyHpackDecompressionPercentage, but only for
+// SPDY3, not HTTP2.
+bool HpackDecoder2::HandleControlFrameHeadersComplete(size_t* compressed_len) {
+  DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersComplete";
+  if (error_detected_ || in_progress_) {
+    DVLOG(2) << "error_detected_=" << error_detected_
+             << ", in_progress_=" << in_progress_;
+    return false;
+  }
+  if (compressed_len != nullptr) {
+    *compressed_len = total_hpack_bytes_;
+  }
+  if (handler_ != nullptr) {
+    handler_->OnHeaderBlockEnd(total_header_bytes_);
+  }
+  Reset();
+  return true;
+}
+
+const SpdyHeaderBlock& HpackDecoder2::decoded_block() const {
+  return decoded_block_;
+}
+
+void HpackDecoder2::SetHeaderTableDebugVisitor(
+    std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
+  DVLOG(2) << "HpackDecoder2::SetHeaderTableDebugVisitor";
+  header_table_.set_debug_visitor(std::move(visitor));
+}
+
+void HpackDecoder2::set_max_decode_buffer_size_bytes(
+    size_t max_decode_buffer_size_bytes) {
+  DVLOG(2) << "HpackDecoder2::set_max_decode_buffer_size_bytes";
+  max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
+}
+
+void HpackDecoder2::OnIndexedHeader(size_t index) {
+  DVLOG(2) << "HpackDecoder2::OnIndexedHeader: index=" << index;
+  DCHECK(!error_detected_);
+  const HpackEntry* entry = header_table_.GetByIndex(index);
+  if (entry == nullptr) {
+    SetErrorDetected();
+    return;
+  }
+  HandleHeaderRepresentation(entry->name(), entry->value());
+}
+
+void HpackDecoder2::OnStartLiteralHeader(HpackEntryType entry_type,
+                                         size_t maybe_name_index) {
+  DVLOG(2) << "HpackDecoder2::OnStartLiteralHeader: entry_type=" << entry_type
+           << ",  maybe_name_index=" << maybe_name_index;
+  DCHECK(!error_detected_);
+  entry_type_ = entry_type;
+  if (maybe_name_index > 0) {
+    const HpackEntry* entry = header_table_.GetByIndex(maybe_name_index);
+    if (entry == nullptr) {
+      SetErrorDetected();
+      return;
+    } else {
+      // Non-static entries could be evicted, leaving us with a dangling
+      // pointer, so we preemptively copy. This could be avoided if
+      // TryAddEntry would copy the strings prior to performing eviction.
+      name_.Set(entry->name(), entry->IsStatic());
+      name_.BufferStringIfUnbuffered();
+    }
+  }
+}
+
+void HpackDecoder2::OnNameStart(bool huffman_encoded, size_t len) {
+  DVLOG(2) << "HpackDecoder2::OnNameStart: huffman_encoded="
+           << (huffman_encoded ? "true" : "false") << ",  len=" << len;
+  if (len > max_decode_buffer_size_bytes_) {
+    DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
+             << max_decode_buffer_size_bytes_ << ")";
+    SetErrorDetected();
+    return;
+  }
+  name_.OnStart(huffman_encoded, len);
+}
+
+void HpackDecoder2::OnNameData(const char* data, size_t len) {
+  DVLOG(2) << "HpackDecoder2::OnNameData: len=" << len
+           << "\n data: " << StringPiece(data, len);
+  if (error_detected_) {
+    return;
+  }
+  if (!name_.OnData(data, len)) {
+    SetErrorDetected();
+  }
+}
+
+void HpackDecoder2::OnNameEnd() {
+  DVLOG(2) << "HpackDecoder2::OnNameEnd";
+  if (error_detected_) {
+    return;
+  }
+  if (!name_.OnEnd()) {
+    SetErrorDetected();
+  }
+}
+
+void HpackDecoder2::OnValueStart(bool huffman_encoded, size_t len) {
+  DVLOG(2) << "HpackDecoder2::OnValueStart: huffman_encoded="
+           << (huffman_encoded ? "true" : "false") << ",  len=" << len;
+  if (len > max_decode_buffer_size_bytes_) {
+    DVLOG(1) << "Value length (" << len << ") is longer than permitted ("
+             << max_decode_buffer_size_bytes_ << ")";
+    SetErrorDetected();
+    return;
+  }
+  value_.OnStart(huffman_encoded, len);
+}
+
+void HpackDecoder2::OnValueData(const char* data, size_t len) {
+  DVLOG(2) << "HpackDecoder2::OnValueData: len=" << len
+           << "\n data: " << StringPiece(data, len);
+  if (error_detected_) {
+    return;
+  }
+  if (!value_.OnData(data, len)) {
+    SetErrorDetected();
+  }
+}
+
+void HpackDecoder2::OnValueEnd() {
+  DVLOG(2) << "HpackDecoder2::OnValueEnd";
+  if (error_detected_) {
+    return;
+  }
+  if (!value_.OnEnd()) {
+    SetErrorDetected();
+    return;
+  }
+  if (EnforceMaxDecodeBufferSize()) {
+    // All is well.
+    HandleHeaderRepresentation(name_.str(), value_.str());
+    if (entry_type_ == HpackEntryType::kIndexedLiteralHeader) {
+      header_table_.TryAddEntry(name_.str(), value_.str());
+    }
+    name_.Reset();
+    value_.Reset();
+  }
+}
+
+void HpackDecoder2::OnDynamicTableSizeUpdate(size_t size) {
+  DVLOG(2) << "HpackDecoder2::OnDynamicTableSizeUpdate: size=" << size;
+  if (error_detected_) {
+    return;
+  }
+  if (size > header_table_.settings_size_bound()) {
+    DVLOG(1) << "Dynamic Table Size Update with too large a size: " << size
+             << " > " << header_table_.settings_size_bound();
+    SetErrorDetected();
+    return;
+  }
+  if (header_seen_) {
+    DVLOG(1) << "Dynamic Table Size Update seen after a Header";
+    SetErrorDetected();
+    return;
+  }
+  ++size_update_count_;
+  if (size_update_count_ > 2) {
+    DVLOG(1) << "Too many (" << size_update_count_
+             << ") Dynamic Table Size Updates";
+    SetErrorDetected();
+    return;
+  }
+  header_table_.SetMaxSize(size);
+  return;
+}
+
+bool HpackDecoder2::EnforceMaxDecodeBufferSize() {
+  if (!error_detected_) {
+    size_t buffered_length = name_.BufferedLength() + value_.BufferedLength();
+    DVLOG(2) << "buffered_length=" << buffered_length
+             << "; max=" << max_decode_buffer_size_bytes_;
+    if (buffered_length > max_decode_buffer_size_bytes_) {
+      DVLOG(1) << "Header length (" << buffered_length
+               << ") is longer than permitted ("
+               << max_decode_buffer_size_bytes_ << ")";
+      SetErrorDetected();
+    }
+  }
+  return !error_detected_;
+}
+
+void HpackDecoder2::HandleHeaderRepresentation(StringPiece name,
+                                               StringPiece value) {
+  DVLOG(2) << "HpackDecoder2::HandleHeaderRepresentation:\n name: " << name
+           << "\n value: " << value;
+  total_header_bytes_ += name.size() + value.size();
+  header_seen_ = true;
+  if (handler_ == nullptr) {
+    DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
+             << "adding to decoded_block";
+    decoded_block_.AppendValueOrAddHeader(name, value);
+  } else {
+    DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
+             << "passing to handler";
+    DCHECK(decoded_block_.empty());
+    handler_->OnHeader(name, value);
+  }
+}
+
+}  // namespace net
diff --git a/net/spdy/hpack/hpack_decoder2.h b/net/spdy/hpack/hpack_decoder2.h
new file mode 100644
index 0000000..6dd207c
--- /dev/null
+++ b/net/spdy/hpack/hpack_decoder2.h
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SPDY_HPACK_HPACK_DECODER2_H_
+#define NET_SPDY_HPACK_HPACK_DECODER2_H_
+
+// HpackDecoder2
+
+// An HpackDecoder decodes header sets as outlined in
+// http://tools.ietf.org/html/rfc7541. This implementation uses the
+// new HpackBlockDecoder in //net/http2/hpack/
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/http2/hpack/decoder/hpack_block_decoder.h"
+#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+#include "net/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "net/http2/hpack/http2_hpack_constants.h"
+#include "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
+#include "net/spdy/hpack/hpack_constants.h"
+#include "net/spdy/hpack/hpack_decoder_interface.h"
+#include "net/spdy/hpack/hpack_header_table.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/spdy/spdy_headers_handler_interface.h"
+
+namespace net {
+namespace test {
+class HpackDecoder2Peer;
+}  // namespace test
+
+class NET_EXPORT_PRIVATE HpackDecoder2 : public HpackDecoderInterface,
+                                         HpackEntryDecoderListener {
+ public:
+  friend test::HpackDecoder2Peer;
+  HpackDecoder2();
+  ~HpackDecoder2() override;
+
+  // Override the interface methods:
+
+  void ApplyHeaderTableSizeSetting(size_t size_setting) override;
+  void HandleControlFrameHeadersStart(
+      SpdyHeadersHandlerInterface* handler) override;
+  bool HandleControlFrameHeadersData(const char* headers_data,
+                                     size_t headers_data_length) override;
+  bool HandleControlFrameHeadersComplete(size_t* compressed_len) override;
+  const SpdyHeaderBlock& decoded_block() const override;
+  void SetHeaderTableDebugVisitor(
+      std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor)
+      override;
+  void set_max_decode_buffer_size_bytes(
+      size_t max_decode_buffer_size_bytes) override;
+
+ protected:
+  // Override the HpackEntryDecoderListener methods:
+
+  void OnIndexedHeader(size_t index) override;
+  void OnStartLiteralHeader(HpackEntryType entry_type,
+                            size_t maybe_name_index) override;
+  void OnNameStart(bool huffman_encoded, size_t len) override;
+  void OnNameData(const char* data, size_t len) override;
+  void OnNameEnd() override;
+  void OnValueStart(bool huffman_encoded, size_t len) override;
+  void OnValueData(const char* data, size_t len) override;
+  void OnValueEnd() override;
+  void OnDynamicTableSizeUpdate(size_t size) override;
+
+ private:
+  // Called when a complete header entry has been decoded, with the name and
+  // value of the entry. If check_header_order_ is true, confirms that
+  // pseudo-headers don't appear after normal headers, else it treats the
+  // headers as malformed, as per sections 8.1.2.3. of the HTTP2 specification.
+  // Calls handler_->OnHeader() if there is a handler, else adds the header
+  // to decoded_block_.
+  void HandleHeaderRepresentation(base::StringPiece name,
+                                  base::StringPiece value);
+
+  // Reset state in preparation for decoding a new HPACK block. Does not reset
+  // the dynamic table.
+  void Reset();
+
+  // Called when an error is detected while decoding. Replaces the listener
+  // in the HpackBlockDecoder with the no-op listener.
+  void SetErrorDetected();
+
+  // Enforce the limit on the maximum size of strings that can be buffered.
+  // It happens that this test is made after the strings have been buffered,
+  // but that isn't a problem because we don't pass enormous buffers into
+  // HandleControlFrameHeadersData.
+  bool EnforceMaxDecodeBufferSize();
+
+  HpackHeaderTable header_table_;
+  SpdyHeaderBlock decoded_block_;
+
+  // Scratch space for storing decoded literals.
+  HpackDecoderStringBuffer name_, value_;
+
+  // If non-NULL, handles decoded headers.
+  SpdyHeadersHandlerInterface* handler_;
+
+  HpackEntryDecoderNoOpListener no_op_listener_;
+
+  // Total bytes that have been received as input (i.e. HPACK encoded).
+  size_t total_hpack_bytes_;
+
+  // Total bytes of the name and value strings in the current HPACK block.
+  size_t total_header_bytes_;
+
+  // How much encoded data this decoder is willing to buffer.
+  size_t max_decode_buffer_size_bytes_ = 32 * 1024;  // 32 KB
+
+  HpackBlockDecoder hpack_block_decoder_;
+
+  // Count of Dynamic Table Size Updates seen in the current HPACK block.
+  uint32_t size_update_count_;
+
+  // The type of the current header entry (with literals) that is being decoded.
+  HpackEntryType entry_type_;
+
+  // Has a header been seen in the current HPACK block?
+  bool header_seen_;
+
+  // Did the HpackBlockDecoder stop in the middle of an entry?
+  bool in_progress_;
+
+  // Has an error been detected while decoding the HPACK block?
+  bool error_detected_;
+
+  // Flag to keep track of having seen the header block start. Needed at the
+  // moment because HandleControlFrameHeadersStart won't be called if a handler
+  // is not being provided by the caller.
+  // TODO(jamessynge): Consider collapsing several of these bools into a single
+  // enum representing the state of the decoding process.
+  bool header_block_started_;
+
+  DISALLOW_COPY_AND_ASSIGN(HpackDecoder2);
+};
+
+}  // namespace net
+#endif  // NET_SPDY_HPACK_HPACK_DECODER2_H_
diff --git a/net/spdy/hpack/hpack_decoder2_test.cc b/net/spdy/hpack/hpack_decoder2_test.cc
new file mode 100644
index 0000000..efea044
--- /dev/null
+++ b/net/spdy/hpack/hpack_decoder2_test.cc
@@ -0,0 +1,954 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack/hpack_decoder2.h"
+
+// Tests of HpackDecoder2.
+
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/hpack/tools/hpack_block_builder.h"
+#include "net/http2/tools/http2_random.h"
+#include "net/spdy/hpack/hpack_encoder.h"
+#include "net/spdy/hpack/hpack_entry.h"
+#include "net/spdy/hpack/hpack_huffman_table.h"
+#include "net/spdy/hpack/hpack_output_stream.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+class HpackDecoder2Peer {
+ public:
+  explicit HpackDecoder2Peer(HpackDecoder2* decoder) : decoder_(decoder) {}
+
+  void HandleHeaderRepresentation(StringPiece name, StringPiece value) {
+    decoder_->HandleHeaderRepresentation(name, value);
+  }
+  HpackHeaderTable* header_table() { return &decoder_->header_table_; }
+
+ private:
+  HpackDecoder2* decoder_;
+};
+
+namespace {
+
+using testing::ElementsAre;
+using testing::Pair;
+
+// Is HandleControlFrameHeadersStart to be called, and with what value?
+enum StartChoice { START_WITH_HANDLER, START_WITHOUT_HANDLER, NO_START };
+
+class HpackDecoder2Test
+    : public ::testing::TestWithParam<std::tuple<StartChoice, bool>> {
+ protected:
+  HpackDecoder2Test() : decoder_(), decoder_peer_(&decoder_) {}
+
+  void SetUp() override {
+    std::tie(start_choice_, randomly_split_input_buffer_) = GetParam();
+  }
+
+  void HandleControlFrameHeadersStart() {
+    switch (start_choice_) {
+      case START_WITH_HANDLER:
+        decoder_.HandleControlFrameHeadersStart(&handler_);
+        break;
+      case START_WITHOUT_HANDLER:
+        decoder_.HandleControlFrameHeadersStart(nullptr);
+        break;
+      case NO_START:
+        break;
+    }
+  }
+
+  bool HandleControlFrameHeadersData(StringPiece str) {
+    return decoder_.HandleControlFrameHeadersData(str.data(), str.size());
+  }
+
+  bool HandleControlFrameHeadersComplete(size_t* size) {
+    return decoder_.HandleControlFrameHeadersComplete(size);
+  }
+
+  bool DecodeHeaderBlock(StringPiece str) {
+    // Don't call this again if HandleControlFrameHeadersData failed previously.
+    EXPECT_FALSE(decode_has_failed_);
+    HandleControlFrameHeadersStart();
+    if (randomly_split_input_buffer_) {
+      do {
+        // Decode some fragment of the remaining bytes.
+        size_t bytes = str.length();
+        if (!str.empty()) {
+          bytes = (random_.Rand8() % str.length()) + 1;
+        }
+        EXPECT_LE(bytes, str.length());
+        if (!HandleControlFrameHeadersData(str.substr(0, bytes))) {
+          decode_has_failed_ = true;
+          return false;
+        }
+        str.remove_prefix(bytes);
+      } while (!str.empty());
+    } else if (!HandleControlFrameHeadersData(str)) {
+      decode_has_failed_ = true;
+      return false;
+    }
+    if (!HandleControlFrameHeadersComplete(nullptr)) {
+      decode_has_failed_ = true;
+      return false;
+    }
+    return true;
+  }
+
+  const SpdyHeaderBlock& decoded_block() const {
+    if (start_choice_ == START_WITH_HANDLER) {
+      return handler_.decoded_block();
+    } else {
+      return decoder_.decoded_block();
+    }
+  }
+
+  const SpdyHeaderBlock& DecodeBlockExpectingSuccess(StringPiece str) {
+    EXPECT_TRUE(DecodeHeaderBlock(str));
+    return decoded_block();
+  }
+
+  void expectEntry(size_t index,
+                   size_t size,
+                   const string& name,
+                   const string& value) {
+    const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index);
+    EXPECT_EQ(name, entry->name()) << "index " << index;
+    EXPECT_EQ(value, entry->value());
+    EXPECT_EQ(size, entry->Size());
+    EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry));
+  }
+
+  SpdyHeaderBlock MakeHeaderBlock(
+      const std::vector<std::pair<string, string>>& headers) {
+    SpdyHeaderBlock result;
+    for (const auto& kv : headers) {
+      result.AppendValueOrAddHeader(kv.first, kv.second);
+    }
+    return result;
+  }
+
+  Http2Random random_;
+  HpackDecoder2 decoder_;
+  test::HpackDecoder2Peer decoder_peer_;
+  TestHeadersHandler handler_;
+  StartChoice start_choice_;
+  bool randomly_split_input_buffer_;
+  bool decode_has_failed_ = false;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    StartChoiceAndRandomlySplitChoice,
+    HpackDecoder2Test,
+    ::testing::Combine(
+        ::testing::Values(START_WITH_HANDLER, START_WITHOUT_HANDLER, NO_START),
+        ::testing::Bool()));
+
+TEST_P(HpackDecoder2Test, AddHeaderDataWithHandleControlFrameHeadersData) {
+  // The hpack decode buffer size is limited in size. This test verifies that
+  // adding encoded data under that limit is accepted, and data that exceeds the
+  // limit is rejected.
+  HandleControlFrameHeadersStart();
+  const size_t kMaxBufferSizeBytes = 50;
+  const string a_value = string(49, 'x');
+  decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
+  {
+    HpackBlockBuilder hbb;
+    hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                  false, "a", false, a_value);
+    const auto& s = hbb.buffer();
+    EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(s.data(), s.size()));
+  }
+  {
+    HpackBlockBuilder hbb;
+    hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                  false, "b", false, string(51, 'x'));
+    const auto& s = hbb.buffer();
+    EXPECT_FALSE(decoder_.HandleControlFrameHeadersData(s.data(), s.size()));
+  }
+
+  SpdyHeaderBlock expected_block = MakeHeaderBlock({{"a", a_value}});
+  EXPECT_EQ(expected_block, decoded_block());
+}
+
+TEST_P(HpackDecoder2Test, NameTooLong) {
+  // Verify that a name longer than the allowed size generates an error.
+  const size_t kMaxBufferSizeBytes = 50;
+  const string name = string(2 * kMaxBufferSizeBytes, 'x');
+  const string value = "abc";
+
+  decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
+
+  HpackBlockBuilder hbb;
+  hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                false, name, false, value);
+
+  const size_t fragment_size = (3 * kMaxBufferSizeBytes) / 2;
+  const string fragment = hbb.buffer().substr(0, fragment_size);
+
+  HandleControlFrameHeadersStart();
+  EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
+}
+
+TEST_P(HpackDecoder2Test, HeaderTooLongToBuffer) {
+  // Verify that a header longer than the allowed size generates an error if
+  // it isn't all in one input buffer.
+  const string name = "some-key";
+  const string value = "some-value";
+  const size_t kMaxBufferSizeBytes = name.size() + value.size() - 2;
+  decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
+
+  HpackBlockBuilder hbb;
+  hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+                                false, name, false, value);
+  const size_t fragment_size = hbb.size() - 1;
+  const string fragment = hbb.buffer().substr(0, fragment_size);
+
+  HandleControlFrameHeadersStart();
+  EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
+}
+
+// Decode with incomplete data in buffer.
+TEST_P(HpackDecoder2Test, DecodeWithIncompleteData) {
+  HandleControlFrameHeadersStart();
+
+  // No need to wait for more data.
+  EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82"));
+  std::vector<std::pair<string, string>> expected_headers = {
+      {":method", "GET"}, {":path", "/index.html"}, {":method", "GET"}};
+
+  SpdyHeaderBlock expected_block1 = MakeHeaderBlock(expected_headers);
+  EXPECT_EQ(expected_block1, decoded_block());
+
+  // Full and partial headers, won't add partial to the headers.
+  EXPECT_TRUE(
+      HandleControlFrameHeadersData("\x40\x03goo"
+                                    "\x03gar\xbe\x40\x04spam"));
+  expected_headers.push_back({"goo", "gar"});
+  expected_headers.push_back({"goo", "gar"});
+
+  SpdyHeaderBlock expected_block2 = MakeHeaderBlock(expected_headers);
+  EXPECT_EQ(expected_block2, decoded_block());
+
+  // Add the needed data.
+  EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs"));
+
+  size_t size = 0;
+  EXPECT_TRUE(HandleControlFrameHeadersComplete(&size));
+  EXPECT_EQ(24u, size);
+
+  expected_headers.push_back({"spam", "gggs"});
+
+  SpdyHeaderBlock expected_block3 = MakeHeaderBlock(expected_headers);
+  EXPECT_EQ(expected_block3, decoded_block());
+}
+
+TEST_P(HpackDecoder2Test, HandleHeaderRepresentation) {
+  // Make sure the decoder is properly initialized.
+  HandleControlFrameHeadersStart();
+  HandleControlFrameHeadersData("");
+
+  // All cookie crumbs are joined.
+  decoder_peer_.HandleHeaderRepresentation("cookie", " part 1");
+  decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 ");
+  decoder_peer_.HandleHeaderRepresentation("cookie", "part3");
+
+  // Already-delimited headers are passed through.
+  decoder_peer_.HandleHeaderRepresentation("passed-through",
+                                           string("foo\0baz", 7));
+
+  // Other headers are joined on \0. Case matters.
+  decoder_peer_.HandleHeaderRepresentation("joined", "not joined");
+  decoder_peer_.HandleHeaderRepresentation("joineD", "value 1");
+  decoder_peer_.HandleHeaderRepresentation("joineD", "value 2");
+
+  // Empty headers remain empty.
+  decoder_peer_.HandleHeaderRepresentation("empty", "");
+
+  // Joined empty headers work as expected.
+  decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+  decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo");
+  decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+  decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
+
+  // Non-contiguous cookie crumb.
+  decoder_peer_.HandleHeaderRepresentation("cookie", " fin!");
+
+  // Finish and emit all headers.
+  decoder_.HandleControlFrameHeadersComplete(nullptr);
+
+  // Resulting decoded headers are in the same order as the inputs.
+  EXPECT_THAT(decoded_block(),
+              ElementsAre(Pair("cookie", " part 1; part 2 ; part3;  fin!"),
+                          Pair("passed-through", StringPiece("foo\0baz", 7)),
+                          Pair("joined", "not joined"),
+                          Pair("joineD", StringPiece("value 1\0value 2", 15)),
+                          Pair("empty", ""),
+                          Pair("empty-joined", StringPiece("\0foo\0\0", 6))));
+}
+
+// Decoding indexed static table field should work.
+TEST_P(HpackDecoder2Test, IndexedHeaderStatic) {
+  // Reference static table entries #2 and #5.
+  const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess("\x82\x85");
+  SpdyHeaderBlock expected_header_set1;
+  expected_header_set1[":method"] = "GET";
+  expected_header_set1[":path"] = "/index.html";
+  EXPECT_EQ(expected_header_set1, header_set1);
+
+  // Reference static table entry #2.
+  const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess("\x82");
+  SpdyHeaderBlock expected_header_set2;
+  expected_header_set2[":method"] = "GET";
+  EXPECT_EQ(expected_header_set2, header_set2);
+}
+
+TEST_P(HpackDecoder2Test, IndexedHeaderDynamic) {
+  // First header block: add an entry to header table.
+  const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess(
+      "\x40\x03"
+      "foo"
+      "\x03"
+      "bar");
+  SpdyHeaderBlock expected_header_set1;
+  expected_header_set1["foo"] = "bar";
+  EXPECT_EQ(expected_header_set1, header_set1);
+
+  // Second header block: add another entry to header table.
+  const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess(
+      "\xbe\x40\x04"
+      "spam"
+      "\x04"
+      "eggs");
+  SpdyHeaderBlock expected_header_set2;
+  expected_header_set2["foo"] = "bar";
+  expected_header_set2["spam"] = "eggs";
+  EXPECT_EQ(expected_header_set2, header_set2);
+
+  // Third header block: refer to most recently added entry.
+  const SpdyHeaderBlock& header_set3 = DecodeBlockExpectingSuccess("\xbe");
+  SpdyHeaderBlock expected_header_set3;
+  expected_header_set3["spam"] = "eggs";
+  EXPECT_EQ(expected_header_set3, header_set3);
+}
+
+// Test a too-large indexed header.
+TEST_P(HpackDecoder2Test, InvalidIndexedHeader) {
+  // High-bit set, and a prefix of one more than the number of static entries.
+  EXPECT_FALSE(DecodeHeaderBlock("\xbe"));
+}
+
+TEST_P(HpackDecoder2Test, ContextUpdateMaximumSize) {
+  EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+            decoder_peer_.header_table()->max_size());
+  string input;
+  {
+    // Maximum-size update with size 126. Succeeds.
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(126);
+
+    output_stream.TakeString(&input);
+    EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(126u, decoder_peer_.header_table()->max_size());
+  }
+  {
+    // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds.
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(kDefaultHeaderTableSizeSetting);
+
+    output_stream.TakeString(&input);
+    EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+              decoder_peer_.header_table()->max_size());
+  }
+  {
+    // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails.
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1);
+
+    output_stream.TakeString(&input);
+    EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+              decoder_peer_.header_table()->max_size());
+  }
+}
+
+// Two HeaderTableSizeUpdates may appear at the beginning of the block
+TEST_P(HpackDecoder2Test, TwoTableSizeUpdates) {
+  string input;
+  {
+    // Should accept two table size updates, update to second one
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(0);
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(122);
+
+    output_stream.TakeString(&input);
+    EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(122u, decoder_peer_.header_table()->max_size());
+  }
+}
+
+// Three HeaderTableSizeUpdates should result in an error
+TEST_P(HpackDecoder2Test, ThreeTableSizeUpdatesError) {
+  string input;
+  {
+    // Should reject three table size updates, update to second one
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(5);
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(10);
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(15);
+
+    output_stream.TakeString(&input);
+
+    EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(10u, decoder_peer_.header_table()->max_size());
+  }
+}
+
+// HeaderTableSizeUpdates may only appear at the beginning of the block
+// Any other updates should result in an error
+TEST_P(HpackDecoder2Test, TableSizeUpdateSecondError) {
+  string input;
+  {
+    // Should reject a table size update appearing after a different entry
+    // The table size should remain as the default
+    HpackOutputStream output_stream;
+    output_stream.AppendBytes("\x82\x85");
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(123);
+
+    output_stream.TakeString(&input);
+
+    EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(kDefaultHeaderTableSizeSetting,
+              decoder_peer_.header_table()->max_size());
+  }
+}
+
+// HeaderTableSizeUpdates may only appear at the beginning of the block
+// Any other updates should result in an error
+TEST_P(HpackDecoder2Test, TableSizeUpdateFirstThirdError) {
+  string input;
+  {
+    // Should reject the second table size update
+    // if a different entry appears after the first update
+    // The table size should update to the first but not the second
+    HpackOutputStream output_stream;
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(60);
+    output_stream.AppendBytes("\x82\x85");
+    output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
+    output_stream.AppendUint32(125);
+
+    output_stream.TakeString(&input);
+
+    EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input)));
+    EXPECT_EQ(60u, decoder_peer_.header_table()->max_size());
+  }
+}
+
+// Decoding two valid encoded literal headers with no indexing should
+// work.
+TEST_P(HpackDecoder2Test, LiteralHeaderNoIndexing) {
+  // First header with indexed name, second header with string literal
+  // name.
+  const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2";
+  const SpdyHeaderBlock& header_set =
+      DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1));
+
+  SpdyHeaderBlock expected_header_set;
+  expected_header_set[":path"] = "/sample/path";
+  expected_header_set[":path2"] = "/sample/path/2";
+  EXPECT_EQ(expected_header_set, header_set);
+}
+
+// Decoding two valid encoded literal headers with incremental
+// indexing and string literal names should work.
+TEST_P(HpackDecoder2Test, LiteralHeaderIncrementalIndexing) {
+  const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2";
+  const SpdyHeaderBlock& header_set =
+      DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1));
+
+  SpdyHeaderBlock expected_header_set;
+  expected_header_set[":path"] = "/sample/path";
+  expected_header_set[":path2"] = "/sample/path/2";
+  EXPECT_EQ(expected_header_set, header_set);
+}
+
+TEST_P(HpackDecoder2Test, LiteralHeaderWithIndexingInvalidNameIndex) {
+  decoder_.ApplyHeaderTableSizeSetting(0);
+
+  // Name is the last static index. Works.
+  EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x7d\x03ooo")));
+  // Name is one beyond the last static index. Fails.
+  EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x7e\x03ooo")));
+}
+
+TEST_P(HpackDecoder2Test, LiteralHeaderNoIndexingInvalidNameIndex) {
+  // Name is the last static index. Works.
+  EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x0f\x2e\x03ooo")));
+  // Name is one beyond the last static index. Fails.
+  EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x0f\x2f\x03ooo")));
+}
+
+TEST_P(HpackDecoder2Test, LiteralHeaderNeverIndexedInvalidNameIndex) {
+  // Name is the last static index. Works.
+  EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x1f\x2e\x03ooo")));
+  // Name is one beyond the last static index. Fails.
+  EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x1f\x2f\x03ooo")));
+}
+
+TEST_P(HpackDecoder2Test, TruncatedIndex) {
+  // Indexed Header, varint for index requires multiple bytes,
+  // but only one provided.
+  EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\xff", 1)));
+}
+
+TEST_P(HpackDecoder2Test, TruncatedHuffmanLiteral) {
+  // Literal value, Huffman encoded, but with the last byte missing (i.e.
+  // drop the final ff shown below).
+  //
+  // 41                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 1)
+  //                                         |     :authority
+  // 8c                                      |   Literal value (len = 12)
+  //                                         |     Huffman encoded:
+  // f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k.....
+  //                                         |     Decoded:
+  //                                         | www.example.com
+  //                                         | -> :authority: www.example.com
+
+  string first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
+  EXPECT_TRUE(DecodeHeaderBlock(first));
+  first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4");
+  EXPECT_FALSE(DecodeHeaderBlock(first));
+}
+
+TEST_P(HpackDecoder2Test, HuffmanEOSError) {
+  // Literal value, Huffman encoded, but with an additional ff byte at the end
+  // of the string, i.e. an EOS that is longer than permitted.
+  //
+  // 41                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 1)
+  //                                         |     :authority
+  // 8d                                      |   Literal value (len = 13)
+  //                                         |     Huffman encoded:
+  // f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k.....
+  //                                         |     Decoded:
+  //                                         | www.example.com
+  //                                         | -> :authority: www.example.com
+
+  string first = a2b_hex("418cf1e3c2e5f23a6ba0ab90f4ff");
+  EXPECT_TRUE(DecodeHeaderBlock(first));
+  first = a2b_hex("418df1e3c2e5f23a6ba0ab90f4ffff");
+  EXPECT_FALSE(DecodeHeaderBlock(first));
+}
+
+// Round-tripping the header set from E.2.1 should work.
+TEST_P(HpackDecoder2Test, BasicE21) {
+  HpackEncoder encoder(ObtainHpackHuffmanTable());
+
+  SpdyHeaderBlock expected_header_set;
+  expected_header_set[":method"] = "GET";
+  expected_header_set[":scheme"] = "http";
+  expected_header_set[":path"] = "/";
+  expected_header_set[":authority"] = "www.example.com";
+
+  string encoded_header_set;
+  EXPECT_TRUE(
+      encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set));
+
+  EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set));
+  EXPECT_EQ(expected_header_set, decoded_block());
+}
+
+TEST_P(HpackDecoder2Test, SectionD4RequestHuffmanExamples) {
+  // TODO(jamessynge): Use net/http2/hpack/tools/hpack_example.h to parse the
+  // example directly, instead of having it as a comment.
+  // 82                                      | == Indexed - Add ==
+  //                                         |   idx = 2
+  //                                         | -> :method: GET
+  // 86                                      | == Indexed - Add ==
+  //                                         |   idx = 6
+  //                                         | -> :scheme: http
+  // 84                                      | == Indexed - Add ==
+  //                                         |   idx = 4
+  //                                         | -> :path: /
+  // 41                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 1)
+  //                                         |     :authority
+  // 8c                                      |   Literal value (len = 12)
+  //                                         |     Huffman encoded:
+  // f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k.....
+  //                                         |     Decoded:
+  //                                         | www.example.com
+  //                                         | -> :authority: www.example.com
+  string first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+  const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
+
+  EXPECT_THAT(first_header_set,
+              ElementsAre(
+                  // clang-format off
+      Pair(":method", "GET"),
+      Pair(":scheme", "http"),
+      Pair(":path", "/"),
+      Pair(":authority", "www.example.com")));
+  // clang-format on
+
+  expectEntry(62, 57, ":authority", "www.example.com");
+  EXPECT_EQ(57u, decoder_peer_.header_table()->size());
+
+  // 82                                      | == Indexed - Add ==
+  //                                         |   idx = 2
+  //                                         | -> :method: GET
+  // 86                                      | == Indexed - Add ==
+  //                                         |   idx = 6
+  //                                         | -> :scheme: http
+  // 84                                      | == Indexed - Add ==
+  //                                         |   idx = 4
+  //                                         | -> :path: /
+  // be                                      | == Indexed - Add ==
+  //                                         |   idx = 62
+  //                                         | -> :authority: www.example.com
+  // 58                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 24)
+  //                                         |     cache-control
+  // 86                                      |   Literal value (len = 8)
+  //                                         |     Huffman encoded:
+  // a8eb 1064 9cbf                          | ...d..
+  //                                         |     Decoded:
+  //                                         | no-cache
+  //                                         | -> cache-control: no-cache
+
+  string second = a2b_hex("828684be5886a8eb10649cbf");
+  const SpdyHeaderBlock& second_header_set =
+      DecodeBlockExpectingSuccess(second);
+
+  EXPECT_THAT(second_header_set,
+              ElementsAre(
+                  // clang-format off
+      Pair(":method", "GET"),
+      Pair(":scheme", "http"),
+      Pair(":path", "/"),
+      Pair(":authority", "www.example.com"),
+      Pair("cache-control", "no-cache")));
+  // clang-format on
+
+  expectEntry(62, 53, "cache-control", "no-cache");
+  expectEntry(63, 57, ":authority", "www.example.com");
+  EXPECT_EQ(110u, decoder_peer_.header_table()->size());
+
+  // 82                                      | == Indexed - Add ==
+  //                                         |   idx = 2
+  //                                         | -> :method: GET
+  // 87                                      | == Indexed - Add ==
+  //                                         |   idx = 7
+  //                                         | -> :scheme: https
+  // 85                                      | == Indexed - Add ==
+  //                                         |   idx = 5
+  //                                         | -> :path: /index.html
+  // bf                                      | == Indexed - Add ==
+  //                                         |   idx = 63
+  //                                         | -> :authority: www.example.com
+  // 40                                      | == Literal indexed ==
+  // 88                                      |   Literal name (len = 10)
+  //                                         |     Huffman encoded:
+  // 25a8 49e9 5ba9 7d7f                     | %.I.[.}.
+  //                                         |     Decoded:
+  //                                         | custom-key
+  // 89                                      |   Literal value (len = 12)
+  //                                         |     Huffman encoded:
+  // 25a8 49e9 5bb8 e8b4 bf                  | %.I.[....
+  //                                         |     Decoded:
+  //                                         | custom-value
+  //                                         | -> custom-key: custom-value
+  string third = a2b_hex("828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf");
+  const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
+
+  EXPECT_THAT(
+      third_header_set,
+      ElementsAre(
+          // clang-format off
+      Pair(":method", "GET"),
+      Pair(":scheme", "https"),
+      Pair(":path", "/index.html"),
+      Pair(":authority", "www.example.com"),
+      Pair("custom-key", "custom-value")));
+  // clang-format on
+
+  expectEntry(62, 54, "custom-key", "custom-value");
+  expectEntry(63, 53, "cache-control", "no-cache");
+  expectEntry(64, 57, ":authority", "www.example.com");
+  EXPECT_EQ(164u, decoder_peer_.header_table()->size());
+}
+
+TEST_P(HpackDecoder2Test, SectionD6ResponseHuffmanExamples) {
+  decoder_.ApplyHeaderTableSizeSetting(256);
+
+  // 48                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 8)
+  //                                         |     :status
+  // 82                                      |   Literal value (len = 3)
+  //                                         |     Huffman encoded:
+  // 6402                                    | d.
+  //                                         |     Decoded:
+  //                                         | 302
+  //                                         | -> :status: 302
+  // 58                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 24)
+  //                                         |     cache-control
+  // 85                                      |   Literal value (len = 7)
+  //                                         |     Huffman encoded:
+  // aec3 771a 4b                            | ..w.K
+  //                                         |     Decoded:
+  //                                         | private
+  //                                         | -> cache-control: private
+  // 61                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 33)
+  //                                         |     date
+  // 96                                      |   Literal value (len = 29)
+  //                                         |     Huffman encoded:
+  // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
+  // e082 a62d 1bff                          | ...-..
+  //                                         |     Decoded:
+  //                                         | Mon, 21 Oct 2013 20:13:21
+  //                                         | GMT
+  //                                         | -> date: Mon, 21 Oct 2013
+  //                                         |   20:13:21 GMT
+  // 6e                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 46)
+  //                                         |     location
+  // 91                                      |   Literal value (len = 23)
+  //                                         |     Huffman encoded:
+  // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
+  // d3                                      | .
+  //                                         |     Decoded:
+  //                                         | https://www.example.com
+  //                                         | -> location: https://www.e
+  //                                         |    xample.com
+
+  string first = a2b_hex(
+      "488264025885aec3771a4b6196d07abe"
+      "941054d444a8200595040b8166e082a6"
+      "2d1bff6e919d29ad171863c78f0b97c8"
+      "e9ae82ae43d3");
+  const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
+
+  EXPECT_THAT(first_header_set,
+              ElementsAre(
+                  // clang-format off
+      Pair(":status", "302"),
+      Pair("cache-control", "private"),
+      Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+      Pair("location", "https://www.example.com")));
+  // clang-format on
+
+  expectEntry(62, 63, "location", "https://www.example.com");
+  expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
+  expectEntry(64, 52, "cache-control", "private");
+  expectEntry(65, 42, ":status", "302");
+  EXPECT_EQ(222u, decoder_peer_.header_table()->size());
+
+  // 48                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 8)
+  //                                         |     :status
+  // 83                                      |   Literal value (len = 3)
+  //                                         |     Huffman encoded:
+  // 640e ff                                 | d..
+  //                                         |     Decoded:
+  //                                         | 307
+  //                                         | - evict: :status: 302
+  //                                         | -> :status: 307
+  // c1                                      | == Indexed - Add ==
+  //                                         |   idx = 65
+  //                                         | -> cache-control: private
+  // c0                                      | == Indexed - Add ==
+  //                                         |   idx = 64
+  //                                         | -> date: Mon, 21 Oct 2013
+  //                                         |   20:13:21 GMT
+  // bf                                      | == Indexed - Add ==
+  //                                         |   idx = 63
+  //                                         | -> location:
+  //                                         |   https://www.example.com
+  string second = a2b_hex("4883640effc1c0bf");
+  const SpdyHeaderBlock& second_header_set =
+      DecodeBlockExpectingSuccess(second);
+
+  EXPECT_THAT(second_header_set,
+              ElementsAre(
+                  // clang-format off
+      Pair(":status", "307"),
+      Pair("cache-control", "private"),
+      Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+      Pair("location", "https://www.example.com")));
+  // clang-format on
+
+  expectEntry(62, 42, ":status", "307");
+  expectEntry(63, 63, "location", "https://www.example.com");
+  expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
+  expectEntry(65, 52, "cache-control", "private");
+  EXPECT_EQ(222u, decoder_peer_.header_table()->size());
+
+  // 88                                      | == Indexed - Add ==
+  //                                         |   idx = 8
+  //                                         | -> :status: 200
+  // c1                                      | == Indexed - Add ==
+  //                                         |   idx = 65
+  //                                         | -> cache-control: private
+  // 61                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 33)
+  //                                         |     date
+  // 96                                      |   Literal value (len = 22)
+  //                                         |     Huffman encoded:
+  // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
+  // e084 a62d 1bff                          | ...-..
+  //                                         |     Decoded:
+  //                                         | Mon, 21 Oct 2013 20:13:22
+  //                                         | GMT
+  //                                         | - evict: cache-control:
+  //                                         |   private
+  //                                         | -> date: Mon, 21 Oct 2013
+  //                                         |   20:13:22 GMT
+  // c0                                      | == Indexed - Add ==
+  //                                         |   idx = 64
+  //                                         | -> location:
+  //                                         |    https://www.example.com
+  // 5a                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 26)
+  //                                         |     content-encoding
+  // 83                                      |   Literal value (len = 3)
+  //                                         |     Huffman encoded:
+  // 9bd9 ab                                 | ...
+  //                                         |     Decoded:
+  //                                         | gzip
+  //                                         | - evict: date: Mon, 21 Oct
+  //                                         |    2013 20:13:21 GMT
+  //                                         | -> content-encoding: gzip
+  // 77                                      | == Literal indexed ==
+  //                                         |   Indexed name (idx = 55)
+  //                                         |     set-cookie
+  // ad                                      |   Literal value (len = 45)
+  //                                         |     Huffman encoded:
+  // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
+  // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
+  // 3160 65c0 03ed 4ee5 b106 3d50 07        | 1`e...N...=P.
+  //                                         |     Decoded:
+  //                                         | foo=ASDJKHQKBZXOQWEOPIUAXQ
+  //                                         | WEOIU; max-age=3600; versi
+  //                                         | on=1
+  //                                         | - evict: location:
+  //                                         |   https://www.example.com
+  //                                         | - evict: :status: 307
+  //                                         | -> set-cookie: foo=ASDJKHQ
+  //                                         |   KBZXOQWEOPIUAXQWEOIU;
+  //                                         |   max-age=3600; version=1
+  string third = a2b_hex(
+      "88c16196d07abe941054d444a8200595"
+      "040b8166e084a62d1bffc05a839bd9ab"
+      "77ad94e7821dd7f2e6c7b335dfdfcd5b"
+      "3960d5af27087f3672c1ab270fb5291f"
+      "9587316065c003ed4ee5b1063d5007");
+  const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
+
+  EXPECT_THAT(third_header_set,
+              ElementsAre(
+                  // clang-format off
+      Pair(":status", "200"),
+      Pair("cache-control", "private"),
+      Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+      Pair("location", "https://www.example.com"),
+      Pair("content-encoding", "gzip"),
+      Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
+           " max-age=3600; version=1")));
+  // clang-format on
+
+  expectEntry(62, 98, "set-cookie",
+              "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
+              " max-age=3600; version=1");
+  expectEntry(63, 52, "content-encoding", "gzip");
+  expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
+  EXPECT_EQ(215u, decoder_peer_.header_table()->size());
+}
+
+// Regression test: Found that entries with dynamic indexed names and literal
+// values caused "use after free" MSAN failures if the name was evicted as it
+// was being re-used.
+TEST_P(HpackDecoder2Test, ReuseNameOfEvictedEntry) {
+  // Each entry is measured as 32 bytes plus the sum of the lengths of the name
+  // and the value. Set the size big enough for at most one entry, and a fairly
+  // small one at that (31 ASCII characters).
+  decoder_.ApplyHeaderTableSizeSetting(63);
+
+  HpackBlockBuilder hbb;
+
+  const StringPiece name("some-name");
+  const StringPiece value1("some-value");
+  const StringPiece value2("another-value");
+  const StringPiece value3("yet-another-value");
+
+  // Add an entry that will become the first in the dynamic table, entry 62.
+  hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
+                                name, false, value1);
+
+  // Confirm that entry has been added by re-using it.
+  hbb.AppendIndexedHeader(62);
+
+  // Add another entry referring to the name of the first. This will evict the
+  // first.
+  hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
+                                     false, value2);
+
+  // Confirm that entry has been added by re-using it.
+  hbb.AppendIndexedHeader(62);
+
+  // Add another entry referring to the name of the second. This will evict the
+  // second.
+  hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
+                                     false, value3);
+
+  // Confirm that entry has been added by re-using it.
+  hbb.AppendIndexedHeader(62);
+
+  EXPECT_TRUE(DecodeHeaderBlock(hbb.buffer()));
+
+  SpdyHeaderBlock expected_header_set;
+  expected_header_set.AppendValueOrAddHeader(name, value1);
+  expected_header_set.AppendValueOrAddHeader(name, value1);
+  expected_header_set.AppendValueOrAddHeader(name, value2);
+  expected_header_set.AppendValueOrAddHeader(name, value2);
+  expected_header_set.AppendValueOrAddHeader(name, value3);
+  expected_header_set.AppendValueOrAddHeader(name, value3);
+
+  // SpdyHeaderBlock stores these 6 strings as '\0' separated values.
+  // Make sure that is what happened.
+  string joined_values = expected_header_set[name].as_string();
+  EXPECT_EQ(joined_values.size(),
+            2 * value1.size() + 2 * value2.size() + 2 * value3.size() + 5);
+
+  EXPECT_EQ(expected_header_set, decoded_block());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/spdy/hpack/hpack_huffman_decoder.cc b/net/spdy/hpack/hpack_huffman_decoder.cc
index f305a66..dcb512c 100644
--- a/net/spdy/hpack/hpack_huffman_decoder.cc
+++ b/net/spdy/hpack/hpack_huffman_decoder.cc
@@ -158,7 +158,7 @@
 };
 // clang-format on
 
-#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#if DCHECK_IS_ON()
 
 // Only used in DLOG.
 bool IsEOSPrefix(HuffmanWord bits, HuffmanCodeLength bits_available) {
@@ -171,7 +171,7 @@
   return bits == expected;
 }
 
-#endif  // NDEBUG && !defined(DCHECK_ALWAYS_ON)
+#endif  // DCHECK_IS_ON()
 
 }  // namespace
 
diff --git a/net/spdy/spdy_alt_svc_wire_format.cc b/net/spdy/spdy_alt_svc_wire_format.cc
index e94b4fa7..df4e84d 100644
--- a/net/spdy/spdy_alt_svc_wire_format.cc
+++ b/net/spdy/spdy_alt_svc_wire_format.cc
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/logging.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 
 namespace net {
@@ -22,7 +23,8 @@
                               StringPiece::const_iterator end,
                               T* value) {
   *value = 0;
-  for (; c != end && isdigit(*c); ++c) {
+  // TODO(mmenke):  This really should be using methods in parse_number.h.
+  for (; c != end && '0' <= *c && *c <= '9'; ++c) {
     if (*value > std::numeric_limits<T>::max() / 10) {
       return false;
     }
@@ -277,22 +279,20 @@
     }
     DCHECK_EQ('%', *c);
     ++c;
-    if (c == end || !isxdigit(*c)) {
+    if (c == end || !base::IsHexDigit(*c)) {
       return false;
     }
-    char decoded = tolower(*c);
-    // '0' is 0, 'a' is 10.
-    decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
     // Network byte order is big-endian.
-    decoded <<= 4;
+    int decoded = base::HexDigitToInt(*c) << 4;
+
     ++c;
-    if (c == end || !isxdigit(*c)) {
+    if (c == end || !base::IsHexDigit(*c)) {
       return false;
     }
-    decoded += tolower(*c);
-    // '0' is 0, 'a' is 10.
-    decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
-    output->push_back(decoded);
+    // Network byte order is big-endian.
+    decoded += base::HexDigitToInt(*c);
+
+    output->push_back(static_cast<char>(decoded));
   }
   return true;
 }
diff --git a/net/spdy/spdy_flags.cc b/net/spdy/spdy_flags.cc
index 3bfa0527..e8c495bf 100644
--- a/net/spdy/spdy_flags.cc
+++ b/net/spdy/spdy_flags.cc
@@ -9,6 +9,9 @@
 // Log compressed size of HTTP/2 requests.
 bool FLAGS_chromium_http2_flag_log_compressed_size = true;
 
+// Use //net/http2/hpack/decoder as HPACK decoder.
+bool FLAGS_chromium_http2_flag_spdy_use_hpack_decoder2 = false;
+
 // If true, increase HPACK table size up to optimal size kOptTableSize if
 // clients allow it.
 bool FLAGS_chromium_reloadable_flag_increase_hpack_table_size = false;
diff --git a/net/spdy/spdy_flags.h b/net/spdy/spdy_flags.h
index cf0db83..efd1f24 100644
--- a/net/spdy/spdy_flags.h
+++ b/net/spdy/spdy_flags.h
@@ -11,6 +11,8 @@
 
 NET_EXPORT_PRIVATE extern bool FLAGS_chromium_http2_flag_log_compressed_size;
 NET_EXPORT_PRIVATE extern bool
+    FLAGS_chromium_http2_flag_spdy_use_hpack_decoder2;
+NET_EXPORT_PRIVATE extern bool
     FLAGS_chromium_reloadable_flag_increase_hpack_table_size;
 NET_EXPORT_PRIVATE extern bool FLAGS_use_nested_spdy_framer_decoder;
 
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index 6bbadf4..0ec0236 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -23,6 +23,8 @@
 #include "base/strings/string_util.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/spdy/hpack/hpack_constants.h"
+#include "net/spdy/hpack/hpack_decoder.h"
+#include "net/spdy/hpack/hpack_decoder2.h"
 #include "net/spdy/spdy_bitmasks.h"
 #include "net/spdy/spdy_bug_tracker.h"
 #include "net/spdy/spdy_flags.h"
@@ -2359,7 +2361,11 @@
 
 HpackDecoderInterface* SpdyFramer::GetHpackDecoder() {
   if (hpack_decoder_.get() == nullptr) {
-    hpack_decoder_.reset(new HpackDecoder());
+    if (FLAGS_chromium_http2_flag_spdy_use_hpack_decoder2) {
+      hpack_decoder_.reset(new HpackDecoder2());
+    } else {
+      hpack_decoder_.reset(new HpackDecoder());
+    }
   }
   return hpack_decoder_.get();
 }
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index 691826f6..223fdf85 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -16,7 +16,6 @@
 #include "base/strings/string_piece.h"
 #include "base/sys_byteorder.h"
 #include "net/base/net_export.h"
-#include "net/spdy/hpack/hpack_decoder.h"
 #include "net/spdy/hpack/hpack_decoder_interface.h"
 #include "net/spdy/hpack/hpack_encoder.h"
 #include "net/spdy/spdy_alt_svc_wire_format.h"
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
index 14a4980..03ebf8c 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_test.cc
@@ -11,6 +11,7 @@
 #include <limits>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -27,6 +28,7 @@
 #include "net/spdy/spdy_protocol.h"
 #include "net/spdy/spdy_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
 using base::StringPiece;
@@ -611,8 +613,32 @@
                      frame.size() - framer.GetHeadersMinimumSize());
 }
 
-class SpdyFramerTest : public ::testing::Test {
+enum DecoderChoice { DECODER_SELF, DECODER_NESTED };
+enum HpackChoice { HPACK_DECODER_1, HPACK_DECODER_2 };
+
+class SpdyFramerTest
+    : public ::testing::TestWithParam<std::tuple<DecoderChoice, HpackChoice>> {
  protected:
+  void SetUp() override {
+    auto param = GetParam();
+    switch (std::get<0>(param)) {
+      case DECODER_SELF:
+        FLAGS_use_nested_spdy_framer_decoder = false;
+        break;
+      case DECODER_NESTED:
+        FLAGS_use_nested_spdy_framer_decoder = true;
+        break;
+    }
+    switch (std::get<1>(param)) {
+      case HPACK_DECODER_1:
+        FLAGS_chromium_http2_flag_spdy_use_hpack_decoder2 = false;
+        break;
+      case HPACK_DECODER_2:
+        FLAGS_chromium_http2_flag_spdy_use_hpack_decoder2 = true;
+        break;
+    }
+  }
+
   void CompareFrame(const string& description,
                     const SpdySerializedFrame& actual_frame,
                     const unsigned char* expected,
@@ -635,8 +661,14 @@
   }
 };
 
+INSTANTIATE_TEST_CASE_P(
+    SpdyFramerTests,
+    SpdyFramerTest,
+    ::testing::Combine(::testing::Values(DECODER_SELF, DECODER_NESTED),
+                       ::testing::Values(HPACK_DECODER_1, HPACK_DECODER_2)));
+
 // Test that we can encode and decode a SpdyHeaderBlock in serialized form.
-TEST_F(SpdyFramerTest, HeaderBlockInBuffer) {
+TEST_P(SpdyFramerTest, HeaderBlockInBuffer) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
 
@@ -657,7 +689,7 @@
 }
 
 // Test that if there's not a full frame, we fail to parse it.
-TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
+TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
 
@@ -678,7 +710,7 @@
 
 // Test that we treat incoming upper-case or mixed-case header values as
 // malformed.
-TEST_F(SpdyFramerTest, RejectUpperCaseHeaderBlockValue) {
+TEST_P(SpdyFramerTest, RejectUpperCaseHeaderBlockValue) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
 
@@ -713,7 +745,7 @@
 
 // Test that we can encode and decode stream dependency values in a header
 // frame.
-TEST_F(SpdyFramerTest, HeaderStreamDependencyValues) {
+TEST_P(SpdyFramerTest, HeaderStreamDependencyValues) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
 
@@ -742,7 +774,7 @@
 
 // Test that if we receive a frame with payload length field at the
 // advertised max size, we do not set an error in ProcessInput.
-TEST_F(SpdyFramerTest, AcceptMaxFrameSizeSetting) {
+TEST_P(SpdyFramerTest, AcceptMaxFrameSizeSetting) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -767,7 +799,7 @@
 
 // Test that if we receive a frame with payload length larger than the
 // advertised max size, we set an error of SPDY_INVALID_CONTROL_FRAME_SIZE.
-TEST_F(SpdyFramerTest, ExceedMaxFrameSizeSetting) {
+TEST_P(SpdyFramerTest, ExceedMaxFrameSizeSetting) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -793,7 +825,7 @@
 
 // Test that if we receive a DATA frame with padding length larger than the
 // payload length, we set an error of SPDY_INVALID_PADDING
-TEST_F(SpdyFramerTest, OversizedDataPaddingError) {
+TEST_P(SpdyFramerTest, OversizedDataPaddingError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -828,7 +860,7 @@
 
 // Test that if we receive a DATA frame with padding length not larger than the
 // payload length, we do not set an error of SPDY_INVALID_PADDING
-TEST_F(SpdyFramerTest, CorrectlySizedDataPaddingNoError) {
+TEST_P(SpdyFramerTest, CorrectlySizedDataPaddingNoError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -863,7 +895,7 @@
 
 // Test that if we receive a HEADERS frame with padding length larger than the
 // payload length, we set an error of SPDY_INVALID_PADDING
-TEST_F(SpdyFramerTest, OversizedHeadersPaddingError) {
+TEST_P(SpdyFramerTest, OversizedHeadersPaddingError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -895,7 +927,7 @@
 
 // Test that if we receive a HEADERS frame with padding length not larger
 // than the payload length, we do not set an error of SPDY_INVALID_PADDING
-TEST_F(SpdyFramerTest, CorrectlySizedHeadersPaddingNoError) {
+TEST_P(SpdyFramerTest, CorrectlySizedHeadersPaddingNoError) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -922,7 +954,7 @@
 
 // Test that if we receive a DATA with stream ID zero, we signal an error
 // (but don't crash).
-TEST_F(SpdyFramerTest, DataWithStreamIdZero) {
+TEST_P(SpdyFramerTest, DataWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -941,7 +973,7 @@
 
 // Test that if we receive a HEADERS with stream ID zero, we signal an error
 // (but don't crash).
-TEST_F(SpdyFramerTest, HeadersWithStreamIdZero) {
+TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -960,7 +992,7 @@
 
 // Test that if we receive a PRIORITY with stream ID zero, we signal an error
 // (but don't crash).
-TEST_F(SpdyFramerTest, PriorityWithStreamIdZero) {
+TEST_P(SpdyFramerTest, PriorityWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -978,7 +1010,7 @@
 
 // Test that if we receive a RST_STREAM with stream ID zero, we signal an error
 // (but don't crash).
-TEST_F(SpdyFramerTest, RstStreamWithStreamIdZero) {
+TEST_P(SpdyFramerTest, RstStreamWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -996,7 +1028,7 @@
 
 // Test that if we receive a SETTINGS with stream ID other than zero,
 // we signal an error (but don't crash).
-TEST_F(SpdyFramerTest, SettingsWithStreamIdNotZero) {
+TEST_P(SpdyFramerTest, SettingsWithStreamIdNotZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -1023,7 +1055,7 @@
 
 // Test that if we receive a GOAWAY with stream ID other than zero,
 // we signal an error (but don't crash).
-TEST_F(SpdyFramerTest, GoawayWithStreamIdNotZero) {
+TEST_P(SpdyFramerTest, GoawayWithStreamIdNotZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -1051,7 +1083,7 @@
 
 // Test that if we receive a CONTINUATION with stream ID zero, we signal an
 // SPDY_INVALID_STREAM_ID.
-TEST_F(SpdyFramerTest, ContinuationWithStreamIdZero) {
+TEST_P(SpdyFramerTest, ContinuationWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -1073,7 +1105,7 @@
 
 // Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an
 // SPDY_INVALID_STREAM_ID.
-TEST_F(SpdyFramerTest, PushPromiseWithStreamIdZero) {
+TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -1092,7 +1124,7 @@
 
 // Test that if we receive a PUSH_PROMISE with promised stream ID zero, we
 // signal SPDY_INVALID_STREAM_ID.
-TEST_F(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
+TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -1109,7 +1141,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, DuplicateHeader) {
+TEST_P(SpdyFramerTest, DuplicateHeader) {
   SpdyFramer framer;
   // Frame builder with plentiful buffer size.
   SpdyFrameBuilder frame(1024);
@@ -1132,7 +1164,7 @@
       serialized_headers.data(), serialized_headers.size(), &new_headers));
 }
 
-TEST_F(SpdyFramerTest, MultiValueHeader) {
+TEST_P(SpdyFramerTest, MultiValueHeader) {
   SpdyFramer framer;
   // Frame builder with plentiful buffer size.
   SpdyFrameBuilder frame(1024);
@@ -1165,7 +1197,7 @@
               testing::ElementsAre(testing::Pair("name", StringPiece(value))));
 }
 
-TEST_F(SpdyFramerTest, CompressEmptyHeaders) {
+TEST_P(SpdyFramerTest, CompressEmptyHeaders) {
   // See crbug.com/172383
   SpdyHeadersIR headers(1);
   headers.SetHeader("server", "SpdyServer 1.0");
@@ -1182,7 +1214,7 @@
       SpdyFramerPeer::SerializeHeaders(&framer, headers));
 }
 
-TEST_F(SpdyFramerTest, Basic) {
+TEST_P(SpdyFramerTest, Basic) {
   // Send HEADERS frames with PRIORITY and END_HEADERS set.
   // frame-format off
   const unsigned char kH2Input[] = {
@@ -1263,7 +1295,7 @@
 }
 
 // Test that the FIN flag on a data frame signifies EOF.
-TEST_F(SpdyFramerTest, FinOnDataFrame) {
+TEST_P(SpdyFramerTest, FinOnDataFrame) {
   // Send HEADERS frames with END_HEADERS set.
   // frame-format off
   const unsigned char kH2Input[] = {
@@ -1308,7 +1340,7 @@
   EXPECT_EQ(2, visitor.data_frame_count_);
 }
 
-TEST_F(SpdyFramerTest, FinOnHeadersFrame) {
+TEST_P(SpdyFramerTest, FinOnHeadersFrame) {
   // Send HEADERS frames with END_HEADERS set.
   // frame-format off
   const unsigned char kH2Input[] = {
@@ -1341,7 +1373,7 @@
 
 // Verify we can decompress the stream even if handed over to the
 // framer 1 byte at a time.
-TEST_F(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
+TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) {
   SpdyFramer framer;
 
   framer.set_enable_compression(true);
@@ -1386,7 +1418,7 @@
   EXPECT_EQ(1, visitor.data_frame_count_);
 }
 
-TEST_F(SpdyFramerTest, WindowUpdateFrame) {
+TEST_P(SpdyFramerTest, WindowUpdateFrame) {
   SpdyFramer framer;
   SpdySerializedFrame frame(
       framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 0x12345678)));
@@ -1403,7 +1435,7 @@
   CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData));
 }
 
-TEST_F(SpdyFramerTest, CreateDataFrame) {
+TEST_P(SpdyFramerTest, CreateDataFrame) {
   SpdyFramer framer;
 
   {
@@ -1605,7 +1637,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreateRstStream) {
+TEST_P(SpdyFramerTest, CreateRstStream) {
   SpdyFramer framer;
 
   {
@@ -1651,7 +1683,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreateSettings) {
+TEST_P(SpdyFramerTest, CreateSettings) {
   SpdyFramer framer;
 
   {
@@ -1735,7 +1767,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreatePingFrame) {
+TEST_P(SpdyFramerTest, CreatePingFrame) {
   SpdyFramer framer;
 
   {
@@ -1772,7 +1804,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreateGoAway) {
+TEST_P(SpdyFramerTest, CreateGoAway) {
   SpdyFramer framer;
 
   {
@@ -1808,7 +1840,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
+TEST_P(SpdyFramerTest, CreateHeadersUncompressed) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
 
@@ -2060,7 +2092,7 @@
 // TODO(phajdan.jr): Clean up after we no longer need
 // to workaround http://crbug.com/139744.
 #if !defined(USE_SYSTEM_ZLIB)
-TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
+TEST_P(SpdyFramerTest, CreateHeadersCompressed) {
   SpdyFramer framer;
   framer.set_enable_compression(true);
 
@@ -2075,7 +2107,7 @@
 }
 #endif  // !defined(USE_SYSTEM_ZLIB)
 
-TEST_F(SpdyFramerTest, CreateWindowUpdate) {
+TEST_P(SpdyFramerTest, CreateWindowUpdate) {
   SpdyFramer framer;
 
   {
@@ -2121,7 +2153,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, SerializeBlocked) {
+TEST_P(SpdyFramerTest, SerializeBlocked) {
   SpdyFramer framer;
 
   const char kDescription[] = "BLOCKED frame";
@@ -2138,7 +2170,7 @@
   CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData));
 }
 
-TEST_F(SpdyFramerTest, CreateBlocked) {
+TEST_P(SpdyFramerTest, CreateBlocked) {
   SpdyFramer framer;
 
   const char kDescription[] = "BLOCKED frame";
@@ -2152,7 +2184,7 @@
   CompareFrames(kDescription, frame_serialized, frame_created);
 }
 
-TEST_F(SpdyFramerTest, CreatePushPromiseUncompressed) {
+TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) {
   {
     // Test framing PUSH_PROMISE without padding.
     SpdyFramer framer;
@@ -2284,7 +2316,7 @@
 }
 
 // Regression test for https://crbug.com/464748.
-TEST_F(SpdyFramerTest, GetNumberRequiredContinuationFrames) {
+TEST_P(SpdyFramerTest, GetNumberRequiredContinuationFrames) {
   SpdyFramer framer;
   EXPECT_EQ(1u, SpdyFramerPeer::GetNumberRequiredContinuationFrames(
                     &framer, 16383 + 16374));
@@ -2296,7 +2328,7 @@
                     &framer, 16383 + 2 * 16374 + 1));
 }
 
-TEST_F(SpdyFramerTest, CreateContinuationUncompressed) {
+TEST_P(SpdyFramerTest, CreateContinuationUncompressed) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
   const char kDescription[] = "CONTINUATION frame";
@@ -2339,7 +2371,7 @@
 
 // Test that if we send an unexpected CONTINUATION
 // we signal an error (but don't crash).
-TEST_F(SpdyFramerTest, SendUnexpectedContinuation) {
+TEST_P(SpdyFramerTest, SendUnexpectedContinuation) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -2375,7 +2407,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) {
+TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) {
   {
     // Test framing in a case such that a PUSH_PROMISE frame, with one byte of
     // padding, cannot hold all the data payload, which is overflowed to the
@@ -2476,7 +2508,7 @@
   }
 }
 
-TEST_F(SpdyFramerTest, CreateAltSvc) {
+TEST_P(SpdyFramerTest, CreateAltSvc) {
   SpdyFramer framer;
 
   const char kDescription[] = "ALTSVC frame";
@@ -2501,7 +2533,7 @@
   CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData));
 }
 
-TEST_F(SpdyFramerTest, CreatePriority) {
+TEST_P(SpdyFramerTest, CreatePriority) {
   SpdyFramer framer;
 
   const char kDescription[] = "PRIORITY frame";
@@ -2524,7 +2556,7 @@
   CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData));
 }
 
-TEST_F(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
+TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) {
   SpdyFramer framer;
   SpdyHeadersIR headers_ir(1);
   headers_ir.SetHeader("alpha", "beta");
@@ -2543,7 +2575,7 @@
   EXPECT_EQ(headers_ir.header_block(), visitor.headers_);
 }
 
-TEST_F(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
+TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) {
   SpdyFramer framer;
   SpdyHeadersIR headers_ir(1);
   headers_ir.set_fin(true);
@@ -2563,7 +2595,7 @@
   EXPECT_EQ(headers_ir.header_block(), visitor.headers_);
 }
 
-TEST_F(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
+TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
   SpdyHeadersIR headers(1);
@@ -2590,7 +2622,7 @@
   EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_);
 }
 
-TEST_F(SpdyFramerTest, MultipleContinuationFramesWithIterator) {
+TEST_P(SpdyFramerTest, MultipleContinuationFramesWithIterator) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
   auto headers = base::MakeUnique<SpdyHeadersIR>(1);
@@ -2652,7 +2684,7 @@
   EXPECT_FALSE(frame_it.HasNextFrame());
 }
 
-TEST_F(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
+TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) {
   SpdyFramer framer;
   framer.set_enable_compression(false);
   SpdyPushPromiseIR push_promise(1, 2);
@@ -2681,7 +2713,7 @@
 // Check that the framer stops delivering header data chunks once the visitor
 // declares it doesn't want any more. This is important to guard against
 // "zip bomb" types of attacks.
-TEST_F(SpdyFramerTest, ControlFrameMuchTooLarge) {
+TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) {
   const size_t kHeaderBufferChunks = 4;
   const size_t kHeaderBufferSize =
       TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks;
@@ -2705,7 +2737,7 @@
   EXPECT_EQ(1, visitor.end_of_stream_count_);
 }
 
-TEST_F(SpdyFramerTest, ControlFrameSizesAreValidated) {
+TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) {
   SpdyFramer framer;
   // Create a GoAway frame that has a few extra bytes at the end.
   // We create enough overhead to overflow the framer's control frame buffer.
@@ -2743,7 +2775,7 @@
   EXPECT_EQ(0, visitor.goaway_count_);  // Frame not parsed.
 }
 
-TEST_F(SpdyFramerTest, ReadZeroLenSettingsFrame) {
+TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) {
   SpdyFramer framer;
   SpdySettingsIR settings_ir;
   SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir));
@@ -2758,7 +2790,7 @@
 }
 
 // Tests handling of SETTINGS frames with invalid length.
-TEST_F(SpdyFramerTest, ReadBogusLenSettingsFrame) {
+TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) {
   SpdyFramer framer;
   SpdySettingsIR settings_ir;
 
@@ -2783,7 +2815,7 @@
 }
 
 // Tests handling of SETTINGS frames larger than the frame buffer size.
-TEST_F(SpdyFramerTest, ReadLargeSettingsFrame) {
+TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) {
   SpdyFramer framer;
   SpdySettingsIR settings_ir;
   settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE,
@@ -2830,7 +2862,7 @@
 }
 
 // Tests handling of SETTINGS frame with duplicate entries.
-TEST_F(SpdyFramerTest, ReadDuplicateSettings) {
+TEST_P(SpdyFramerTest, ReadDuplicateSettings) {
   SpdyFramer framer;
 
   const unsigned char kH2FrameData[] = {
@@ -2858,7 +2890,7 @@
 }
 
 // Tests handling of SETTINGS frame with a setting we don't recognize.
-TEST_F(SpdyFramerTest, ReadUnknownSettingsId) {
+TEST_P(SpdyFramerTest, ReadUnknownSettingsId) {
   SpdyFramer framer;
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x06,        // Length: 6
@@ -2879,7 +2911,7 @@
 }
 
 // Tests handling of SETTINGS frame with entries out of order.
-TEST_F(SpdyFramerTest, ReadOutOfOrderSettings) {
+TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) {
   SpdyFramer framer;
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x12,        // Length: 18
@@ -2903,7 +2935,7 @@
   EXPECT_EQ(0, visitor.error_count_);
 }
 
-TEST_F(SpdyFramerTest, ProcessSettingsAckFrame) {
+TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) {
   SpdyFramer framer;
 
   const unsigned char kFrameData[] = {
@@ -2922,7 +2954,7 @@
   EXPECT_EQ(1, visitor.settings_ack_received_);
 }
 
-TEST_F(SpdyFramerTest, ProcessDataFrameWithPadding) {
+TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) {
   const int kPaddingLen = 119;
   const char data_payload[] = "hello";
 
@@ -2980,7 +3012,7 @@
   CHECK_EQ(framer.error_code(), SpdyFramer::SPDY_NO_ERROR);
 }
 
-TEST_F(SpdyFramerTest, ReadWindowUpdate) {
+TEST_P(SpdyFramerTest, ReadWindowUpdate) {
   SpdyFramer framer;
   SpdySerializedFrame control_frame(
       framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2)));
@@ -2992,7 +3024,7 @@
   EXPECT_EQ(2, visitor.last_window_update_delta_);
 }
 
-TEST_F(SpdyFramerTest, ReadCompressedPushPromise) {
+TEST_P(SpdyFramerTest, ReadCompressedPushPromise) {
   SpdyFramer framer;
   SpdyPushPromiseIR push_promise(42, 57);
   push_promise.SetHeader("foo", "bar");
@@ -3007,7 +3039,7 @@
   EXPECT_EQ(push_promise.header_block(), visitor.headers_);
 }
 
-TEST_F(SpdyFramerTest, ReadHeadersWithContinuation) {
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) {
   // frame-format off
   const unsigned char kInput[] = {
       0x00, 0x00, 0x14,                       // Length: 20
@@ -3065,7 +3097,7 @@
                            testing::Pair("name", "value")));
 }
 
-TEST_F(SpdyFramerTest, ReadHeadersWithContinuationAndFin) {
+TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) {
   // frame-format off
   const unsigned char kInput[] = {
       0x00, 0x00, 0x10,                       // Length: 20
@@ -3122,7 +3154,7 @@
                            testing::Pair("name", "value")));
 }
 
-TEST_F(SpdyFramerTest, ReadPushPromiseWithContinuation) {
+TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) {
   // frame-format off
   const unsigned char kInput[] = {
     0x00, 0x00, 0x17, 0x05,  // PUSH_PROMISE
@@ -3172,7 +3204,7 @@
 
 // Receiving an unknown frame when a continuation is expected should
 // result in a SPDY_UNEXPECTED_FRAME error
-TEST_F(SpdyFramerTest, ReceiveUnknownMidContinuation) {
+TEST_P(SpdyFramerTest, ReceiveUnknownMidContinuation) {
   const unsigned char kInput[] = {
       0x00, 0x00, 0x10,        // Length: 16
       0x01,                    //   Type: HEADERS
@@ -3209,7 +3241,7 @@
   EXPECT_EQ(0u, visitor.header_buffer_length_);
 }
 
-TEST_F(SpdyFramerTest, ReceiveContinuationOnWrongStream) {
+TEST_P(SpdyFramerTest, ReceiveContinuationOnWrongStream) {
   const unsigned char kInput[] = {
       0x00, 0x00, 0x10,        // Length: 16
       0x01,                    //   Type: HEADERS
@@ -3244,7 +3276,7 @@
   EXPECT_EQ(0u, visitor.header_buffer_length_);
 }
 
-TEST_F(SpdyFramerTest, ReadContinuationOutOfOrder) {
+TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) {
   const unsigned char kInput[] = {
       0x00, 0x00, 0x18,        // Length: 24
       0x09,                    //   Type: CONTINUATION
@@ -3268,7 +3300,7 @@
   EXPECT_EQ(0u, visitor.header_buffer_length_);
 }
 
-TEST_F(SpdyFramerTest, ExpectContinuationReceiveData) {
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) {
   const unsigned char kInput[] = {
       0x00, 0x00, 0x10,        // Length: 16
       0x01,                    //   Type: HEADERS
@@ -3301,7 +3333,7 @@
   EXPECT_EQ(0, visitor.data_frame_count_);
 }
 
-TEST_F(SpdyFramerTest, ExpectContinuationReceiveControlFrame) {
+TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) {
   const unsigned char kInput[] = {
       0x00, 0x00, 0x10,        // Length: 16
       0x01,                    //   Type: HEADERS
@@ -3336,7 +3368,7 @@
   EXPECT_EQ(0, visitor.data_frame_count_);
 }
 
-TEST_F(SpdyFramerTest, ReadGarbage) {
+TEST_P(SpdyFramerTest, ReadGarbage) {
   SpdyFramer framer;
   unsigned char garbage_frame[256];
   memset(garbage_frame, ~0, sizeof(garbage_frame));
@@ -3346,7 +3378,7 @@
   EXPECT_EQ(1, visitor.error_count_);
 }
 
-TEST_F(SpdyFramerTest, ReadUnknownExtensionFrame) {
+TEST_P(SpdyFramerTest, ReadUnknownExtensionFrame) {
   SpdyFramer framer;
 
   // The unrecognized frame type should still have a valid length.
@@ -3382,7 +3414,7 @@
   EXPECT_EQ(1u, static_cast<unsigned>(visitor.settings_ack_sent_));
 }
 
-TEST_F(SpdyFramerTest, ReadGarbageWithValidLength) {
+TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) {
   SpdyFramer framer;
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x08,        // Length: 8
@@ -3398,7 +3430,7 @@
   EXPECT_EQ(1, visitor.error_count_);
 }
 
-TEST_F(SpdyFramerTest, ReadGarbageHPACKEncoding) {
+TEST_P(SpdyFramerTest, ReadGarbageHPACKEncoding) {
   const unsigned char kInput[] = {
       0x00, 0x12, 0x01,        // Length: 4609
       0x04,                    //   Type: SETTINGS
@@ -3417,7 +3449,7 @@
   EXPECT_EQ(1, visitor.error_count_);
 }
 
-TEST_F(SpdyFramerTest, SizesTest) {
+TEST_P(SpdyFramerTest, SizesTest) {
   SpdyFramer framer;
   EXPECT_EQ(9u, framer.GetDataFrameMinimumSize());
   EXPECT_EQ(9u, framer.GetFrameHeaderSize());
@@ -3435,7 +3467,7 @@
   EXPECT_EQ(16384u, framer.GetDataFrameMaximumPayload());
 }
 
-TEST_F(SpdyFramerTest, StateToStringTest) {
+TEST_P(SpdyFramerTest, StateToStringTest) {
   EXPECT_STREQ("ERROR", SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
   EXPECT_STREQ("FRAME_COMPLETE",
                SpdyFramer::StateToString(SpdyFramer::SPDY_FRAME_COMPLETE));
@@ -3469,7 +3501,7 @@
                                     SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD + 1));
 }
 
-TEST_F(SpdyFramerTest, ErrorCodeToStringTest) {
+TEST_P(SpdyFramerTest, ErrorCodeToStringTest) {
   EXPECT_STREQ("NO_ERROR",
                SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
   EXPECT_STREQ("INVALID_STREAM_ID", SpdyFramer::ErrorCodeToString(
@@ -3504,7 +3536,7 @@
                SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
 }
 
-TEST_F(SpdyFramerTest, StatusCodeToStringTest) {
+TEST_P(SpdyFramerTest, StatusCodeToStringTest) {
   EXPECT_STREQ("NO_ERROR", SpdyFramer::StatusCodeToString(RST_STREAM_NO_ERROR));
   EXPECT_STREQ("PROTOCOL_ERROR",
                SpdyFramer::StatusCodeToString(RST_STREAM_PROTOCOL_ERROR));
@@ -3522,7 +3554,7 @@
   EXPECT_STREQ("UNKNOWN_STATUS", SpdyFramer::StatusCodeToString(-1));
 }
 
-TEST_F(SpdyFramerTest, FrameTypeToStringTest) {
+TEST_P(SpdyFramerTest, FrameTypeToStringTest) {
   EXPECT_STREQ("DATA", SpdyFramer::FrameTypeToString(DATA));
   EXPECT_STREQ("RST_STREAM", SpdyFramer::FrameTypeToString(RST_STREAM));
   EXPECT_STREQ("SETTINGS", SpdyFramer::FrameTypeToString(SETTINGS));
@@ -3534,7 +3566,7 @@
   EXPECT_STREQ("CONTINUATION", SpdyFramer::FrameTypeToString(CONTINUATION));
 }
 
-TEST_F(SpdyFramerTest, DataFrameFlagsV4) {
+TEST_P(SpdyFramerTest, DataFrameFlagsV4) {
   uint8_t valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_PADDED;
 
   uint8_t flags = 0;
@@ -3585,7 +3617,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, RstStreamFrameFlags) {
+TEST_P(SpdyFramerTest, RstStreamFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3609,7 +3641,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, SettingsFrameFlags) {
+TEST_P(SpdyFramerTest, SettingsFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3647,7 +3679,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, GoawayFrameFlags) {
+TEST_P(SpdyFramerTest, GoawayFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3670,7 +3702,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, HeadersFrameFlags) {
+TEST_P(SpdyFramerTest, HeadersFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3727,7 +3759,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, PingFrameFlags) {
+TEST_P(SpdyFramerTest, PingFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3753,7 +3785,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, WindowUpdateFrameFlags) {
+TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3776,7 +3808,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, PushPromiseFrameFlags) {
+TEST_P(SpdyFramerTest, PushPromiseFrameFlags) {
   const SpdyStreamId client_id = 123;   // Must be odd.
   const SpdyStreamId promised_id = 22;  // Must be even.
   uint8_t flags = 0;
@@ -3816,7 +3848,7 @@
   } while (++flags != 0);
 }
 
-TEST_F(SpdyFramerTest, ContinuationFrameFlags) {
+TEST_P(SpdyFramerTest, ContinuationFrameFlags) {
   uint8_t flags = 0;
   do {
     SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex
@@ -3862,7 +3894,7 @@
 
 // TODO(hkhalil): Add TEST_F(SpdyFramerTest, BlockedFrameFlags)
 
-TEST_F(SpdyFramerTest, SettingsFlagsAndId) {
+TEST_P(SpdyFramerTest, SettingsFlagsAndId) {
   const uint32_t kId = 0x020304;
   const uint32_t kFlags = 0x01;
   const uint32_t kWireFormat = base::HostToNet32(0x01020304);
@@ -3875,7 +3907,7 @@
 }
 
 // Test handling of a RST_STREAM with out-of-bounds status codes.
-TEST_F(SpdyFramerTest, RstStreamStatusBounds) {
+TEST_P(SpdyFramerTest, RstStreamStatusBounds) {
   const unsigned char kH2RstStreamInvalid[] = {
       0x00, 0x00, 0x04,        // Length: 4
       0x03,                    //   Type: RST_STREAM
@@ -3913,7 +3945,7 @@
 }
 
 // Test handling of GOAWAY frames with out-of-bounds status code.
-TEST_F(SpdyFramerTest, GoAwayStatusBounds) {
+TEST_P(SpdyFramerTest, GoAwayStatusBounds) {
   SpdyFramer framer;
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x0a,        // Length: 10
@@ -3936,7 +3968,7 @@
 }
 
 // Tests handling of a GOAWAY frame with out-of-bounds stream ID.
-TEST_F(SpdyFramerTest, GoAwayStreamIdBounds) {
+TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) {
   const unsigned char kH2FrameData[] = {
       0x00, 0x00, 0x08,        // Length: 8
       0x07,                    //   Type: GOAWAY
@@ -3958,7 +3990,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, OnBlocked) {
+TEST_P(SpdyFramerTest, OnBlocked) {
   const SpdyStreamId kStreamId = 0;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
@@ -3976,7 +4008,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, OnAltSvc) {
+TEST_P(SpdyFramerTest, OnAltSvc) {
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
@@ -4005,7 +4037,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, OnAltSvcNoOrigin) {
+TEST_P(SpdyFramerTest, OnAltSvcNoOrigin) {
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
@@ -4032,7 +4064,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, OnAltSvcEmptyProtocolId) {
+TEST_P(SpdyFramerTest, OnAltSvcEmptyProtocolId) {
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
   SpdyFramer framer;
   framer.set_visitor(&visitor);
@@ -4053,7 +4085,7 @@
       << SpdyFramer::ErrorCodeToString(framer.error_code());
 }
 
-TEST_F(SpdyFramerTest, OnAltSvcBadLengths) {
+TEST_P(SpdyFramerTest, OnAltSvcBadLengths) {
   const SpdyStreamId kStreamId = 1;
 
   testing::StrictMock<test::MockSpdyFramerVisitor> visitor;
@@ -4078,7 +4110,7 @@
 }
 
 // Tests handling of ALTSVC frames delivered in small chunks.
-TEST_F(SpdyFramerTest, ReadChunkedAltSvcFrame) {
+TEST_P(SpdyFramerTest, ReadChunkedAltSvcFrame) {
   SpdyFramer framer;
   SpdyAltSvcIR altsvc_ir(1);
   SpdyAltSvcWireFormat::AlternativeService altsvc1(
@@ -4112,7 +4144,7 @@
 }
 
 // Tests handling of PRIORITY frames.
-TEST_F(SpdyFramerTest, ReadPriority) {
+TEST_P(SpdyFramerTest, ReadPriority) {
   SpdyFramer framer;
   SpdyPriorityIR priority(3, 1, 256, false);
   SpdySerializedFrame frame(framer.SerializePriority(priority));
@@ -4129,7 +4161,7 @@
 }
 
 // Tests handling of PRIORITY frame with incorrect size.
-TEST_F(SpdyFramerTest, ReadIncorrectlySizedPriority) {
+TEST_P(SpdyFramerTest, ReadIncorrectlySizedPriority) {
   // PRIORITY frame of size 4, which isn't correct.
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x04,        // Length: 4
@@ -4149,7 +4181,7 @@
 }
 
 // Tests handling of PING frame with incorrect size.
-TEST_F(SpdyFramerTest, ReadIncorrectlySizedPing) {
+TEST_P(SpdyFramerTest, ReadIncorrectlySizedPing) {
   // PING frame of size 4, which isn't correct.
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x04,        // Length: 4
@@ -4169,7 +4201,7 @@
 }
 
 // Tests handling of WINDOW_UPDATE frame with incorrect size.
-TEST_F(SpdyFramerTest, ReadIncorrectlySizedWindowUpdate) {
+TEST_P(SpdyFramerTest, ReadIncorrectlySizedWindowUpdate) {
   // WINDOW_UPDATE frame of size 3, which isn't correct.
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x03,        // Length: 3
@@ -4189,7 +4221,7 @@
 }
 
 // Tests handling of RST_STREAM frame with incorrect size.
-TEST_F(SpdyFramerTest, ReadIncorrectlySizedRstStream) {
+TEST_P(SpdyFramerTest, ReadIncorrectlySizedRstStream) {
   // RST_STREAM frame of size 3, which isn't correct.
   const unsigned char kFrameData[] = {
       0x00, 0x00, 0x03,        // Length: 3
@@ -4210,7 +4242,7 @@
 
 // Test that SpdyFramer processes, by default, all passed input in one call
 // to ProcessInput (i.e. will not be calling set_process_single_input_frame()).
-TEST_F(SpdyFramerTest, ProcessAllInput) {
+TEST_P(SpdyFramerTest, ProcessAllInput) {
   SpdyFramer framer;
   std::unique_ptr<TestSpdyVisitor> visitor(new TestSpdyVisitor);
   framer.set_visitor(visitor.get());
@@ -4259,7 +4291,7 @@
 // process_single_input_frame is set. Input to ProcessInput has two frames, but
 // only processes the first when we give it the first frame split at any point,
 // or give it more than one frame in the input buffer.
-TEST_F(SpdyFramerTest, ProcessAtMostOneFrame) {
+TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) {
   SpdyFramer framer;
   framer.set_process_single_input_frame(true);
   std::unique_ptr<TestSpdyVisitor> visitor;
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 7b8a859..459d015 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -4362,7 +4362,7 @@
     "Basic realm=\"MyRealm\""
   };
   SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError(
-      "401 Authentication Required", kExtraAuthenticationHeaders,
+      "401", kExtraAuthenticationHeaders,
       arraysize(kExtraAuthenticationHeaders) / 2, 1));
   SpdySerializedFrame body_authentication(
       spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -6162,6 +6162,32 @@
   EXPECT_EQ("hello!", out.response_data);
 }
 
+TEST_F(SpdyNetworkTransactionTest, 100Continue) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  MockWrite writes[] = {CreateMockWrite(req, 0)};
+
+  SpdyHeaderBlock informational_headers;
+  informational_headers[spdy_util_.GetStatusKey()] = "100";
+  SpdySerializedFrame informational_response(
+      spdy_util_.ConstructSpdyReply(1, std::move(informational_headers)));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
+  MockRead reads[] = {
+      CreateMockRead(informational_response, 1), CreateMockRead(resp, 2),
+      CreateMockRead(body, 3), MockRead(ASYNC, 0, 4)  // EOF
+  };
+
+  SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(), DEFAULT_PRIORITY,
+                                     NetLogWithSource(), nullptr);
+  helper.RunToCompletion(&data);
+  TransactionHelperResult out = helper.output();
+  EXPECT_THAT(out.rv, IsOk());
+  EXPECT_EQ("HTTP/1.1 200", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
 class SpdyNetworkTransactionTLSUsageCheckTest
     : public SpdyNetworkTransactionTest {
  protected:
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 40c66156..4688c63 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -13,6 +13,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "net/base/address_list.h"
+#include "net/base/trace_constants.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_server_properties.h"
 #include "net/log/net_log_event_type.h"
@@ -81,7 +82,8 @@
     std::unique_ptr<ClientSocketHandle> connection,
     const NetLogWithSource& net_log,
     bool is_secure) {
-  TRACE_EVENT0("net", "SpdySessionPool::CreateAvailableSessionFromSocket");
+  TRACE_EVENT0(kNetTracingCategory,
+               "SpdySessionPool::CreateAvailableSessionFromSocket");
 
   UMA_HISTOGRAM_ENUMERATION(
       "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX);
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index eb6176f9..c22928e 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -29,6 +30,38 @@
 
 namespace {
 
+enum StatusHeader {
+  STATUS_HEADER_NOT_INCLUDED = 0,
+  STATUS_HEADER_DOES_NOT_START_WITH_NUMBER = 1,
+  STATUS_HEADER_IS_NUMBER = 2,
+  STATUS_HEADER_HAS_STATUS_TEXT = 3,
+  STATUS_HEADER_MAX = STATUS_HEADER_HAS_STATUS_TEXT
+};
+
+StatusHeader ParseStatusHeaderImpl(const SpdyHeaderBlock& response_headers,
+                                   int* status) {
+  SpdyHeaderBlock::const_iterator it = response_headers.find(":status");
+  if (it == response_headers.end())
+    return STATUS_HEADER_NOT_INCLUDED;
+
+  // Save status in |*status| even if some text follows the status code.
+  base::StringPiece status_string = it->second;
+  base::StringPiece::size_type end = status_string.find(' ');
+  if (!StringToInt(status_string.substr(0, end), status))
+    return STATUS_HEADER_DOES_NOT_START_WITH_NUMBER;
+
+  return end == base::StringPiece::npos ? STATUS_HEADER_IS_NUMBER
+                                        : STATUS_HEADER_HAS_STATUS_TEXT;
+}
+
+StatusHeader ParseStatusHeader(const SpdyHeaderBlock& response_headers,
+                               int* status) {
+  StatusHeader status_header = ParseStatusHeaderImpl(response_headers, status);
+  UMA_HISTOGRAM_ENUMERATION("Net.Http2ResponseStatusHeader", status_header,
+                            STATUS_HEADER_MAX + 1);
+  return status_header;
+}
+
 std::unique_ptr<base::Value> NetLogSpdyStreamErrorCallback(
     SpdyStreamId stream_id,
     int status,
@@ -375,15 +408,34 @@
                                    base::Time response_time,
                                    base::TimeTicks recv_first_byte_time) {
   switch (response_state_) {
-    case READY_FOR_HEADERS:
+    case READY_FOR_HEADERS: {
       // No header block has been received yet.
       DCHECK(response_headers_.empty());
-
-      if (response_headers.find(":status") == response_headers.end()) {
-        const std::string error("Response headers do not include :status.");
-        LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
-        session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR, error);
-        return;
+      int status;
+      switch (ParseStatusHeader(response_headers, &status)) {
+        case STATUS_HEADER_NOT_INCLUDED: {
+          const std::string error("Response headers do not include :status.");
+          LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+          session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR, error);
+          return;
+        }
+        case STATUS_HEADER_DOES_NOT_START_WITH_NUMBER: {
+          const std::string error("Cannot parse :status.");
+          LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
+          session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR, error);
+          return;
+        }
+        // Intentional fallthrough for the following two cases,
+        // to maintain compatibility with broken servers that include
+        // status text in the response.
+        case STATUS_HEADER_IS_NUMBER:
+        case STATUS_HEADER_HAS_STATUS_TEXT:
+          // Ignore informational headers.
+          // TODO(bnc): Add support for 103 Early Hints,
+          // https://crbug.com/671310.
+          if (status / 100 == 1) {
+            return;
+          }
       }
 
       response_state_ = READY_FOR_DATA_OR_TRAILERS;
@@ -421,7 +473,7 @@
       SaveResponseHeaders(response_headers);
 
       break;
-
+    }
     case READY_FOR_DATA_OR_TRAILERS:
       // Second header block is trailers.
       if (type_ == SPDY_PUSH_STREAM) {
@@ -440,7 +492,7 @@
       const std::string error("Header block received after trailers.");
       LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
       session_->ResetStream(stream_id_, RST_STREAM_PROTOCOL_ERROR, error);
-      return;
+      break;
   }
 }
 
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index 9e67e6a..0a72167 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -396,7 +396,6 @@
   // * zero or one header block ("trailers").
   // Each header block must have a ":status" header field.  SpdyStream enforces
   // these requirements, and resets the stream if they are not met.
-  // TODO(bnc) https://crbug.com/662197 Add support for informational headers.
   enum ResponseState {
     READY_FOR_HEADERS,
     READY_FOR_DATA_OR_TRAILERS,
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index ce3ec46..c41a975 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -994,6 +994,159 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
 }
 
+TEST_F(SpdyStreamTest, InformationalHeaders) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  AddWrite(req);
+
+  SpdyHeaderBlock informational_headers;
+  informational_headers[":status"] = "100";
+  SpdySerializedFrame informational_response(
+      spdy_util_.ConstructSpdyResponseHeaders(
+          1, std::move(informational_headers), false));
+  AddRead(informational_response);
+
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  AddRead(reply);
+
+  SpdySerializedFrame body(
+      spdy_util_.ConstructSpdyDataFrame(1, kPostBody, kPostBodyLength, true));
+  AddRead(body);
+
+  AddReadEOF();
+
+  SequencedSocketData data(GetReads(), GetNumReads(), GetWrites(),
+                           GetNumWrites());
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  data.set_connect_data(connect_data);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  AddSSLSocketData();
+
+  base::WeakPtr<SpdySession> session(CreateDefaultSpdySession());
+
+  base::WeakPtr<SpdyStream> stream = CreateStreamSynchronously(
+      SPDY_REQUEST_RESPONSE_STREAM, session, url_, LOWEST, NetLogWithSource());
+  ASSERT_TRUE(stream);
+
+  StreamDelegateDoNothing delegate(stream);
+  stream->SetDelegate(&delegate);
+
+  EXPECT_TRUE(stream->GetUrlFromHeaders().is_empty());
+
+  SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  EXPECT_EQ(ERR_IO_PENDING, stream->SendRequestHeaders(std::move(headers),
+                                                       NO_MORE_DATA_TO_SEND));
+  EXPECT_EQ(kDefaultUrl, stream->GetUrlFromHeaders().spec());
+
+  EXPECT_THAT(delegate.WaitForClose(), IsOk());
+  EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
+  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+            delegate.TakeReceivedData());
+
+  EXPECT_TRUE(data.AllWriteDataConsumed());
+  EXPECT_TRUE(data.AllReadDataConsumed());
+}
+
+TEST_F(SpdyStreamTest, StatusMustStartWithNumber) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  AddWrite(req);
+
+  SpdyHeaderBlock incorrect_headers;
+  incorrect_headers[":status"] = "nan";
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyResponseHeaders(
+      1, std::move(incorrect_headers), false));
+  AddRead(reply);
+
+  SpdySerializedFrame rst(
+      spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_PROTOCOL_ERROR));
+  AddWrite(rst);
+
+  AddReadEOF();
+
+  SequencedSocketData data(GetReads(), GetNumReads(), GetWrites(),
+                           GetNumWrites());
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  data.set_connect_data(connect_data);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  AddSSLSocketData();
+
+  base::WeakPtr<SpdySession> session(CreateDefaultSpdySession());
+
+  base::WeakPtr<SpdyStream> stream = CreateStreamSynchronously(
+      SPDY_REQUEST_RESPONSE_STREAM, session, url_, LOWEST, NetLogWithSource());
+  ASSERT_TRUE(stream);
+
+  StreamDelegateDoNothing delegate(stream);
+  stream->SetDelegate(&delegate);
+
+  EXPECT_TRUE(stream->GetUrlFromHeaders().is_empty());
+
+  SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  EXPECT_EQ(ERR_IO_PENDING, stream->SendRequestHeaders(std::move(headers),
+                                                       NO_MORE_DATA_TO_SEND));
+  EXPECT_EQ(kDefaultUrl, stream->GetUrlFromHeaders().spec());
+
+  EXPECT_THAT(delegate.WaitForClose(), IsError(ERR_SPDY_PROTOCOL_ERROR));
+
+  EXPECT_TRUE(data.AllWriteDataConsumed());
+  EXPECT_TRUE(data.AllReadDataConsumed());
+}
+
+TEST_F(SpdyStreamTest, StatusCanHaveExtraText) {
+  SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
+  AddWrite(req);
+
+  SpdyHeaderBlock headers_with_status_text;
+  headers_with_status_text[":status"] =
+      "200 Some random extra text describing status";
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyResponseHeaders(
+      1, std::move(headers_with_status_text), false));
+  AddRead(reply);
+
+  SpdySerializedFrame body(
+      spdy_util_.ConstructSpdyDataFrame(1, kPostBody, kPostBodyLength, true));
+  AddRead(body);
+
+  AddReadEOF();
+
+  SequencedSocketData data(GetReads(), GetNumReads(), GetWrites(),
+                           GetNumWrites());
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  data.set_connect_data(connect_data);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  AddSSLSocketData();
+
+  base::WeakPtr<SpdySession> session(CreateDefaultSpdySession());
+
+  base::WeakPtr<SpdyStream> stream = CreateStreamSynchronously(
+      SPDY_REQUEST_RESPONSE_STREAM, session, url_, LOWEST, NetLogWithSource());
+  ASSERT_TRUE(stream);
+
+  StreamDelegateDoNothing delegate(stream);
+  stream->SetDelegate(&delegate);
+
+  EXPECT_TRUE(stream->GetUrlFromHeaders().is_empty());
+
+  SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
+  EXPECT_EQ(ERR_IO_PENDING, stream->SendRequestHeaders(std::move(headers),
+                                                       NO_MORE_DATA_TO_SEND));
+  EXPECT_EQ(kDefaultUrl, stream->GetUrlFromHeaders().spec());
+
+  EXPECT_THAT(delegate.WaitForClose(), IsOk());
+  EXPECT_EQ("200 Some random extra text describing status",
+            delegate.GetResponseHeaderValue(spdy_util_.GetStatusKey()));
+  EXPECT_EQ(std::string(kPostBody, kPostBodyLength),
+            delegate.TakeReceivedData());
+
+  EXPECT_TRUE(data.AllWriteDataConsumed());
+  EXPECT_TRUE(data.AllReadDataConsumed());
+}
+
 // Call IncreaseSendWindowSize on a stream with a large enough delta to overflow
 // an int32_t. The SpdyStream should handle that case gracefully.
 TEST_F(SpdyStreamTest, IncreaseSendWindowSizeOverflow) {
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 15d61736..3f1a873 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -1001,12 +1001,12 @@
   static const char* const kExtraHeaders[] = {
     "location", "http://www.foo.com/index.php",
   };
-  return ConstructSpdyReplyError("301 Moved Permanently", kExtraHeaders,
+  return ConstructSpdyReplyError("301", kExtraHeaders,
                                  arraysize(kExtraHeaders) / 2, stream_id);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError(int stream_id) {
-  return ConstructSpdyReplyError("500 Internal Server Error", NULL, 0, 1);
+  return ConstructSpdyReplyError("500", NULL, 0, 1);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetReply(
diff --git a/net/ssl/ssl_client_session_cache.cc b/net/ssl/ssl_client_session_cache.cc
index fc817fdb..5fd3c48 100644
--- a/net/ssl/ssl_client_session_cache.cc
+++ b/net/ssl/ssl_client_session_cache.cc
@@ -7,9 +7,13 @@
 #include <utility>
 
 #include "base/memory/memory_coordinator_client_registry.h"
+#include "base/strings/stringprintf.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "net/cert/x509_util_openssl.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "third_party/boringssl/src/include/openssl/x509.h"
 
 namespace net {
 
@@ -82,6 +86,45 @@
              SSL_SESSION_get_time(session) + SSL_SESSION_get_timeout(session);
 }
 
+void SSLClientSessionCache::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd) {
+  std::string absolute_name = "net/ssl_session_cache";
+  base::trace_event::MemoryAllocatorDump* cache_dump =
+      pmd->GetAllocatorDump(absolute_name);
+  // This method can be reached from different URLRequestContexts. Since this is
+  // a singleton, only log memory stats once.
+  // TODO(xunjieli): Change this once crbug.com/458365 is fixed.
+  if (cache_dump)
+    return;
+  cache_dump = pmd->CreateAllocatorDump(absolute_name);
+  base::AutoLock lock(lock_);
+  for (const auto& pair : cache_) {
+    auto entry = pair.second.get();
+    auto cert_chain = entry->x509_chain;
+    size_t cert_count = sk_X509_num(cert_chain);
+    base::trace_event::MemoryAllocatorDump* entry_dump =
+        pmd->CreateAllocatorDump(
+            base::StringPrintf("%s/entry_%p", absolute_name.c_str(), entry));
+    int cert_size = 0;
+    for (size_t i = 0; i < cert_count; ++i) {
+      X509* cert = sk_X509_value(cert_chain, i);
+      cert_size += i2d_X509(cert, nullptr);
+    }
+    // This measures the lower bound of the serialized certificate. It doesn't
+    // measure the actual memory used, which is 4x this amount (see
+    // crbug.com/671420 for more details).
+    entry_dump->AddScalar("cert_size",
+                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                          cert_size);
+    entry_dump->AddScalar("serialized_cert_count",
+                          base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                          cert_count);
+    entry_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                          cert_size);
+  }
+}
+
 void SSLClientSessionCache::FlushExpiredSessions() {
   time_t now = clock_->Now().ToTimeT();
   auto iter = cache_.begin();
diff --git a/net/ssl/ssl_client_session_cache.h b/net/ssl/ssl_client_session_cache.h
index cd668a26..8133010c 100644
--- a/net/ssl/ssl_client_session_cache.h
+++ b/net/ssl/ssl_client_session_cache.h
@@ -24,6 +24,9 @@
 
 namespace base {
 class Clock;
+namespace trace_event {
+class ProcessMemoryDump;
+}
 }
 
 namespace net {
@@ -56,6 +59,10 @@
 
   void SetClockForTesting(std::unique_ptr<base::Clock> clock);
 
+  // Dumps memory allocation stats. |pmd| is the ProcessMemoryDump of the
+  // browser process.
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd);
+
  private:
   // base::MemoryCoordinatorClient implementation:
   void OnMemoryStateChange(base::MemoryState state) override;
diff --git a/net/ssl/ssl_client_session_cache_unittest.cc b/net/ssl/ssl_client_session_cache_unittest.cc
index b3e53469a..2f14ae86 100644
--- a/net/ssl/ssl_client_session_cache_unittest.cc
+++ b/net/ssl/ssl_client_session_cache_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 
@@ -299,4 +301,40 @@
   EXPECT_EQ(0u, cache.size());
 }
 
+// Basic test for dumping memory stats.
+TEST(SSLClientSessionCacheTest, TestDumpMemoryStats) {
+  SSLClientSessionCache::Config config;
+  SSLClientSessionCache cache(config);
+
+  bssl::UniquePtr<SSL_SESSION> session1(SSL_SESSION_new());
+  bssl::UniquePtr<SSL_SESSION> session2(SSL_SESSION_new());
+  bssl::UniquePtr<SSL_SESSION> session3(SSL_SESSION_new());
+
+  // Insert three entries.
+  cache.Insert("key1", session1.get());
+  cache.Insert("key2", session2.get());
+  cache.Insert("key3", session3.get());
+  EXPECT_EQ(session1.get(), cache.Lookup("key1").get());
+  EXPECT_EQ(session2.get(), cache.Lookup("key2").get());
+  EXPECT_EQ(session3.get(), cache.Lookup("key3").get());
+  EXPECT_EQ(3u, cache.size());
+
+  base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump(
+      new base::trace_event::ProcessMemoryDump(nullptr, dump_args));
+  cache.DumpMemoryStats(process_memory_dump.get());
+
+  const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
+      allocator_dumps = process_memory_dump->allocator_dumps();
+
+  size_t num_entry_dump = 0;
+  for (const auto& pair : allocator_dumps) {
+    const std::string& dump_name = pair.first;
+    if (dump_name.find("net/ssl_session_cache/entry") != std::string::npos)
+      num_entry_dump++;
+  }
+  ASSERT_EQ(3u, num_entry_dump);
+}
+
 }  // namespace net
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc
index a8f7df92..c3ffc07b 100644
--- a/net/test/spawned_test_server/base_test_server.cc
+++ b/net/test/spawned_test_server/base_test_server.cc
@@ -482,7 +482,7 @@
   VLOG(1) << "Server data: " << server_data;
   base::JSONReader json_reader;
   std::unique_ptr<base::Value> value(json_reader.ReadToValue(server_data));
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Could not parse server data: "
                << json_reader.GetErrorMessage();
     return false;
diff --git a/net/test/spawned_test_server/local_test_server.cc b/net/test/spawned_test_server/local_test_server.cc
index 30211667..70ea7a94 100644
--- a/net/test/spawned_test_server/local_test_server.cc
+++ b/net/test/spawned_test_server/local_test_server.cc
@@ -25,17 +25,17 @@
                                  base::CommandLine* command_line) {
   std::string argument_name = "--" + key;
   switch (value_node.GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       command_line->AppendArg(argument_name);
       break;
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int value;
       bool result = value_node.GetAsInteger(&value);
       DCHECK(result);
       command_line->AppendArg(argument_name + "=" + base::IntToString(value));
       break;
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string value;
       bool result = value_node.GetAsString(&value);
       if (!result || value.empty())
@@ -43,11 +43,11 @@
       command_line->AppendArg(argument_name + "=" + value);
       break;
     }
-    case base::Value::TYPE_BOOLEAN:
-    case base::Value::TYPE_DOUBLE:
-    case base::Value::TYPE_LIST:
-    case base::Value::TYPE_DICTIONARY:
-    case base::Value::TYPE_BINARY:
+    case base::Value::Type::BOOLEAN:
+    case base::Value::Type::DOUBLE:
+    case base::Value::Type::LIST:
+    case base::Value::Type::DICTIONARY:
+    case base::Value::Type::BINARY:
     default:
       NOTREACHED() << "improper json type";
       return false;
@@ -207,7 +207,7 @@
     const std::string& key = it.key();
 
     // Add arguments from a list.
-    if (value.IsType(base::Value::TYPE_LIST)) {
+    if (value.IsType(base::Value::Type::LIST)) {
       const base::ListValue* list = NULL;
       if (!value.GetAsList(&list) || !list || list->empty())
         return false;
diff --git a/net/test/spawned_test_server/spawner_communicator.cc b/net/test/spawned_test_server/spawner_communicator.cc
index 3a1566d..0f294a3 100644
--- a/net/test/spawned_test_server/spawner_communicator.cc
+++ b/net/test/spawned_test_server/spawner_communicator.cc
@@ -355,7 +355,7 @@
   // Check whether the data returned from spawner server is JSON-formatted.
   std::unique_ptr<base::Value> value =
       base::JSONReader::Read(server_return_data);
-  if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value.get() || !value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Invalid server data: " << server_return_data.c_str();
     return false;
   }
diff --git a/net/tools/epoll_server/epoll_server.h b/net/tools/epoll_server/epoll_server.h
index b553099..af15daa 100644
--- a/net/tools/epoll_server/epoll_server.h
+++ b/net/tools/epoll_server/epoll_server.h
@@ -477,13 +477,6 @@
   // Returns true when the EpollServer() is being destroyed.
   bool in_shutdown() const { return in_shutdown_; }
 
-  // Summary:
-  //   A function for implementing the ready list. It invokes OnEvent for each
-  //   of the fd in the ready list, and takes care of adding them back to the
-  //   ready list if the callback requests it (by checking that out_ready_mask
-  //   is non-zero).
-  void CallReadyListCallbacks();
-
  protected:
   virtual void SetNonblocking(int fd);
 
@@ -631,6 +624,13 @@
                                                 int events_size);
 
   // Summary:
+  //   A function for implementing the ready list. It invokes OnEvent for each
+  //   of the fd in the ready list, and takes care of adding them back to the
+  //   ready list if the callback requests it (by checking that out_ready_mask
+  //   is non-zero).
+  void CallReadyListCallbacks();
+
+  // Summary:
   //   An internal function for implementing the ready list. It adds a fd's
   //   CBAndEventMask to the ready list. If the fd is already on the ready
   //   list, it is a no-op.
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 1e4b16d..fa6c34a 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -44,12 +44,12 @@
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/test/gtest_util.h"
 #include "net/tools/epoll_server/epoll_server.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_http_response_cache.h"
 #include "net/tools/quic/quic_packet_writer_wrapper.h"
 #include "net/tools/quic/quic_server.h"
 #include "net/tools/quic/quic_simple_server_stream.h"
-#include "net/tools/quic/quic_socket_utils.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
 #include "net/tools/quic/test_tools/packet_dropping_test_writer.h"
 #include "net/tools/quic/test_tools/packet_reordering_writer.h"
@@ -1331,8 +1331,7 @@
       expected_congestion_control_type = kBBR;
       break;
     case kQBIC:
-      expected_congestion_control_type =
-          FLAGS_quic_default_enable_cubic_bytes ? kCubicBytes : kCubic;
+      expected_congestion_control_type = kCubicBytes;
       break;
     default:
       DLOG(FATAL) << "Unexpected congestion control tag";
@@ -1767,23 +1766,71 @@
       QuicServerPeer::GetDispatcher(server_thread_->server());
   auto server_session = static_cast<QuicSpdySession*>(
       dispatcher->session_map().begin()->second.get());
-
   ExpectFlowControlsSynced(client_session->flow_controller(),
                            server_session->flow_controller());
   ExpectFlowControlsSynced(
       QuicSessionPeer::GetCryptoStream(client_session)->flow_controller(),
       QuicSessionPeer::GetCryptoStream(server_session)->flow_controller());
-  ExpectFlowControlsSynced(
-      QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller(),
-      QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller());
+  SpdyFramer spdy_framer;
+  SpdySettingsIR settings_frame;
+  settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, false, false,
+                            kDefaultMaxUncompressedHeaderSize);
+  SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame));
+  QuicFlowController* client_header_stream_flow_controller =
+      QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller();
+  QuicFlowController* server_header_stream_flow_controller =
+      QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller();
+  if (FLAGS_quic_send_max_header_list_size) {
+    // Both client and server are sending this SETTINGS frame, and the send
+    // window is consumed. But because of timing issue, the server may send or
+    // not send the frame, and the client may send/ not send / receive / not
+    // receive the frame.
+    // TODO(fayang): Rewrite this part because it is hacky.
+    QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize(
+                                        server_header_stream_flow_controller) -
+                                    QuicFlowControllerPeer::SendWindowSize(
+                                        client_header_stream_flow_controller);
+    QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize(
+                                        client_header_stream_flow_controller) -
+                                    QuicFlowControllerPeer::SendWindowSize(
+                                        server_header_stream_flow_controller);
+    EXPECT_TRUE(win_difference1 == 0 || win_difference1 == frame.size());
+    EXPECT_TRUE(win_difference2 == 0 || win_difference2 == frame.size());
+  } else {
+    ExpectFlowControlsSynced(
+        QuicSpdySessionPeer::GetHeadersStream(client_session)
+            ->flow_controller(),
+        QuicSpdySessionPeer::GetHeadersStream(server_session)
+            ->flow_controller());
+  }
 
   if (!client_session->force_hol_blocking()) {
-    EXPECT_EQ(static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
-                  client_session->flow_controller())) /
-                  QuicFlowControllerPeer::ReceiveWindowSize(
-                      QuicSpdySessionPeer::GetHeadersStream(client_session)
-                          ->flow_controller()),
-              kSessionToStreamRatio);
+    if (FLAGS_quic_send_max_header_list_size) {
+      // Client *may* have received the SETTINGs frame.
+      // TODO(fayang): Rewrite this part because it is hacky.
+      float ratio1 =
+          static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
+              client_session->flow_controller())) /
+          QuicFlowControllerPeer::ReceiveWindowSize(
+              QuicSpdySessionPeer::GetHeadersStream(client_session)
+                  ->flow_controller());
+      float ratio2 =
+          static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
+              client_session->flow_controller())) /
+          (QuicFlowControllerPeer::ReceiveWindowSize(
+               QuicSpdySessionPeer::GetHeadersStream(client_session)
+                   ->flow_controller()) +
+           frame.size());
+      EXPECT_TRUE(ratio1 == kSessionToStreamRatio ||
+                  ratio2 == kSessionToStreamRatio);
+    } else {
+      EXPECT_EQ(static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
+                    client_session->flow_controller())) /
+                    QuicFlowControllerPeer::ReceiveWindowSize(
+                        QuicSpdySessionPeer::GetHeadersStream(client_session)
+                            ->flow_controller()),
+                kSessionToStreamRatio);
+    }
   }
 
   server_thread_->Resume();
diff --git a/net/tools/quic/platform/impl/quic_socket_utils.cc b/net/tools/quic/platform/impl/quic_socket_utils.cc
new file mode 100644
index 0000000..c0ee991b
--- /dev/null
+++ b/net/tools/quic/platform/impl/quic_socket_utils.cc
@@ -0,0 +1,316 @@
+// 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 "net/tools/quic/platform/impl/quic_socket_utils.h"
+
+#include <errno.h>
+#include <linux/net_tstamp.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <string>
+
+#include "base/logging.h"
+#include "net/quic/core/quic_bug_tracker.h"
+#include "net/quic/core/quic_flags.h"
+#include "net/quic/core/quic_packets.h"
+#include "net/quic/platform/api/quic_socket_address.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+using std::string;
+
+namespace net {
+
+// static
+void QuicSocketUtils::GetAddressAndTimestampFromMsghdr(
+    struct msghdr* hdr,
+    QuicIpAddress* address,
+    QuicWallTime* walltimestamp) {
+  if (hdr->msg_controllen > 0) {
+    for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
+         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+      char* addr_data = nullptr;
+      int len = 0;
+      if (cmsg->cmsg_type == IPV6_PKTINFO) {
+        in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
+        addr_data = reinterpret_cast<char*>(&info->ipi6_addr);
+        len = sizeof(in6_addr);
+        address->FromPackedString(addr_data, len);
+      } else if (cmsg->cmsg_type == IP_PKTINFO) {
+        in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
+        addr_data = reinterpret_cast<char*>(&info->ipi_addr);
+        len = sizeof(in_addr);
+        address->FromPackedString(addr_data, len);
+      } else if (cmsg->cmsg_level == SOL_SOCKET &&
+                 cmsg->cmsg_type == SO_TIMESTAMPING) {
+        LinuxTimestamping* lts =
+            reinterpret_cast<LinuxTimestamping*>(CMSG_DATA(cmsg));
+        timespec* ts = &lts->systime;
+        int64_t usec = (static_cast<int64_t>(ts->tv_sec) * 1000 * 1000) +
+                       (static_cast<int64_t>(ts->tv_nsec) / 1000);
+        *walltimestamp = QuicWallTime::FromUNIXMicroseconds(usec);
+      }
+    }
+  }
+}
+
+// static
+bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr* hdr,
+                                            QuicPacketCount* dropped_packets) {
+  if (hdr->msg_controllen > 0) {
+    struct cmsghdr* cmsg;
+    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
+         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+      if (cmsg->cmsg_type == SO_RXQ_OVFL) {
+        *dropped_packets = *(reinterpret_cast<uint32_t*> CMSG_DATA(cmsg));
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// static
+bool QuicSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) {
+  if (hdr->msg_controllen > 0) {
+    struct cmsghdr* cmsg;
+    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
+         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+      if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) ||
+          (cmsg->cmsg_level == IPPROTO_IPV6 &&
+           cmsg->cmsg_type == IPV6_HOPLIMIT)) {
+        *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// static
+int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
+  int get_local_ip = 1;
+  int rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
+                      sizeof(get_local_ip));
+  if (rc == 0 && address_family == AF_INET6) {
+    rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
+                    sizeof(get_local_ip));
+  }
+  return rc;
+}
+
+// static
+int QuicSocketUtils::SetGetSoftwareReceiveTimestamp(int fd) {
+  int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
+  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &timestamping,
+                    sizeof(timestamping));
+}
+
+// static
+bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) {
+  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) {
+    LOG(ERROR) << "Failed to set socket send size";
+    return false;
+  }
+  return true;
+}
+
+// static
+bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) {
+  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) {
+    LOG(ERROR) << "Failed to set socket recv size";
+    return false;
+  }
+  return true;
+}
+
+// static
+int QuicSocketUtils::ReadPacket(int fd,
+                                char* buffer,
+                                size_t buf_len,
+                                QuicPacketCount* dropped_packets,
+                                QuicIpAddress* self_address,
+                                QuicWallTime* walltimestamp,
+                                QuicSocketAddress* peer_address) {
+  DCHECK(peer_address != nullptr);
+  char cbuf[kSpaceForCmsg];
+  memset(cbuf, 0, arraysize(cbuf));
+
+  iovec iov = {buffer, buf_len};
+  struct sockaddr_storage raw_address;
+  msghdr hdr;
+
+  hdr.msg_name = &raw_address;
+  hdr.msg_namelen = sizeof(sockaddr_storage);
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  hdr.msg_flags = 0;
+
+  struct cmsghdr* cmsg = reinterpret_cast<struct cmsghdr*>(cbuf);
+  cmsg->cmsg_len = arraysize(cbuf);
+  hdr.msg_control = cmsg;
+  hdr.msg_controllen = arraysize(cbuf);
+
+  int bytes_read = recvmsg(fd, &hdr, 0);
+
+  // Return before setting dropped packets: if we get EAGAIN, it will
+  // be 0.
+  if (bytes_read < 0 && errno != 0) {
+    if (errno != EAGAIN) {
+      LOG(ERROR) << "Error reading " << strerror(errno);
+    }
+    return -1;
+  }
+
+  if (hdr.msg_controllen >= arraysize(cbuf)) {
+    QUIC_BUG << "Incorrectly set control length: " << hdr.msg_controllen
+             << ", expected " << arraysize(cbuf);
+    return -1;
+  }
+
+  if (dropped_packets != nullptr) {
+    GetOverflowFromMsghdr(&hdr, dropped_packets);
+  }
+
+  QuicIpAddress stack_address;
+  if (self_address == nullptr) {
+    self_address = &stack_address;
+  }
+
+  QuicWallTime stack_walltimestamp = QuicWallTime::FromUNIXMicroseconds(0);
+  if (walltimestamp == nullptr) {
+    walltimestamp = &stack_walltimestamp;
+  }
+
+  GetAddressAndTimestampFromMsghdr(&hdr, self_address, walltimestamp);
+
+  *peer_address = QuicSocketAddress(raw_address);
+  return bytes_read;
+}
+
+size_t QuicSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address,
+                                        cmsghdr* cmsg) {
+  string address_string;
+  if (self_address.IsIPv4()) {
+    cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
+    cmsg->cmsg_level = IPPROTO_IP;
+    cmsg->cmsg_type = IP_PKTINFO;
+    in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
+    memset(pktinfo, 0, sizeof(in_pktinfo));
+    pktinfo->ipi_ifindex = 0;
+    address_string = self_address.ToPackedString();
+    memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(),
+           address_string.length());
+    return sizeof(in_pktinfo);
+  } else if (self_address.IsIPv6()) {
+    cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
+    cmsg->cmsg_level = IPPROTO_IPV6;
+    cmsg->cmsg_type = IPV6_PKTINFO;
+    in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
+    memset(pktinfo, 0, sizeof(in6_pktinfo));
+    address_string = self_address.ToPackedString();
+    memcpy(&pktinfo->ipi6_addr, address_string.c_str(),
+           address_string.length());
+    return sizeof(in6_pktinfo);
+  } else {
+    NOTREACHED() << "Unrecognized IPAddress";
+    return 0;
+  }
+}
+
+// static
+WriteResult QuicSocketUtils::WritePacket(
+    int fd,
+    const char* buffer,
+    size_t buf_len,
+    const QuicIpAddress& self_address,
+    const QuicSocketAddress& peer_address) {
+  sockaddr_storage raw_address = peer_address.generic_address();
+  iovec iov = {const_cast<char*>(buffer), buf_len};
+
+  msghdr hdr;
+  hdr.msg_name = &raw_address;
+  hdr.msg_namelen = raw_address.ss_family == AF_INET ? sizeof(sockaddr_in)
+                                                     : sizeof(sockaddr_in6);
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  hdr.msg_flags = 0;
+
+  const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo));
+  const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo));
+  // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info.
+  const int kSpaceForIp =
+      (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4;
+  char cbuf[kSpaceForIp];
+  if (!self_address.IsInitialized()) {
+    hdr.msg_control = 0;
+    hdr.msg_controllen = 0;
+  } else {
+    hdr.msg_control = cbuf;
+    hdr.msg_controllen = kSpaceForIp;
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+    SetIpInfoInCmsg(self_address, cmsg);
+    hdr.msg_controllen = cmsg->cmsg_len;
+  }
+
+  int rc;
+  do {
+    rc = sendmsg(fd, &hdr, 0);
+  } while (rc < 0 && errno == EINTR);
+  if (rc >= 0) {
+    return WriteResult(WRITE_STATUS_OK, rc);
+  }
+  return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK)
+                         ? WRITE_STATUS_BLOCKED
+                         : WRITE_STATUS_ERROR,
+                     errno);
+}
+
+// static
+int QuicSocketUtils::CreateUDPSocket(const QuicSocketAddress& address,
+                                     bool* overflow_supported) {
+  int address_family = address.host().AddressFamilyToInt();
+  int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+  if (fd < 0) {
+    LOG(ERROR) << "socket() failed: " << strerror(errno);
+    return -1;
+  }
+
+  int get_overflow = 1;
+  int rc = setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
+                      sizeof(get_overflow));
+  if (rc < 0) {
+    DLOG(WARNING) << "Socket overflow detection not supported";
+  } else {
+    *overflow_supported = true;
+  }
+
+  if (!SetReceiveBufferSize(fd, kDefaultSocketReceiveBuffer)) {
+    return -1;
+  }
+
+  if (!SetSendBufferSize(fd, kDefaultSocketReceiveBuffer)) {
+    return -1;
+  }
+
+  rc = SetGetAddressInfo(fd, address_family);
+  if (rc < 0) {
+    LOG(ERROR) << "IP detection not supported" << strerror(errno);
+    return -1;
+  }
+
+  rc = SetGetSoftwareReceiveTimestamp(fd);
+  if (rc < 0) {
+    LOG(WARNING) << "SO_TIMESTAMPING not supported; using fallback: "
+                 << strerror(errno);
+  }
+
+  return fd;
+}
+
+}  // namespace net
diff --git a/net/tools/quic/platform/impl/quic_socket_utils.h b/net/tools/quic/platform/impl/quic_socket_utils.h
new file mode 100644
index 0000000..3fb28f9
--- /dev/null
+++ b/net/tools/quic/platform/impl/quic_socket_utils.h
@@ -0,0 +1,132 @@
+// 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.
+//
+// Some socket related helper methods for quic.
+
+#ifndef NET_TOOLS_QUIC_PLATFORM_IMPL_QUIC_SOCKET_UTILS_H_
+#define NET_TOOLS_QUIC_PLATFORM_IMPL_QUIC_SOCKET_UTILS_H_
+
+#include <netinet/in.h>
+#include <stddef.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/core/quic_bandwidth.h"
+#include "net/quic/core/quic_types.h"
+
+namespace net {
+class QuicIpAddress;
+class QuicSocketAddress;
+
+// This is the structure that SO_TIMESTAMPING fills into the cmsg header. It is
+// well-defined, but does not have a definition in a public header. See
+// https://www.kernel.org/doc/Documentation/networking/timestamping.txt for more
+// information.
+struct LinuxTimestamping {
+  // The converted system time of the timestamp.
+  struct timespec systime;
+  // Deprecated; serves only as padding.
+  struct timespec hwtimetrans;
+  // The raw hardware timestamp.
+  struct timespec hwtimeraw;
+};
+
+class QuicSocketUtils {
+ public:
+  // The first integer is for overflow. The in6_pktinfo is the larger of the
+  // address structures present. LinuxTimestamping is present for socket
+  // timestamping.  The subsequent int is for ttl.
+  // The final int is a sentinel so the msg_controllen feedback
+  // can be used to detect larger control messages than there is space for.
+  static const int kSpaceForCmsg =
+      CMSG_SPACE(CMSG_LEN(sizeof(int)) + CMSG_LEN(sizeof(in6_pktinfo)) +
+                 CMSG_LEN(sizeof(LinuxTimestamping)) +
+                 CMSG_LEN(sizeof(int)) +
+                 CMSG_LEN(sizeof(int)));
+
+  // Fills in |address| if |hdr| contains IP_PKTINFO or IPV6_PKTINFO. Fills in
+  // |timestamp| if |hdr| contains |SO_TIMESTAMPING|. |address| and |timestamp|
+  // must not be null.
+  static void GetAddressAndTimestampFromMsghdr(struct msghdr* hdr,
+                                               QuicIpAddress* address,
+                                               QuicWallTime* walltimestamp);
+
+  // If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets
+  // to the correct value and return true. Otherwise it will return false.
+  static bool GetOverflowFromMsghdr(struct msghdr* hdr,
+                                    QuicPacketCount* dropped_packets);
+
+  // If the msghdr contains an IP_TTL entry, this will set ttl to the correct
+  // value and return true. Otherwise it will return false.
+  static bool GetTtlFromMsghdr(struct msghdr* hdr, int* ttl);
+
+  // Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on
+  // address_family.  Returns the return code from setsockopt.
+  static int SetGetAddressInfo(int fd, int address_family);
+
+  // Sets SO_TIMESTAMPING on the socket for software receive timestamping.
+  // Returns the return code from setsockopt.
+  static int SetGetSoftwareReceiveTimestamp(int fd);
+
+  // Sets the send buffer size to |size| and returns false if it fails.
+  static bool SetSendBufferSize(int fd, size_t size);
+
+  // Sets the receive buffer size to |size| and returns false if it fails.
+  static bool SetReceiveBufferSize(int fd, size_t size);
+
+  // Reads buf_len from the socket.  If reading is successful, returns bytes
+  // read and sets peer_address to the peer address.  Otherwise returns -1.
+  //
+  // If dropped_packets is non-null, it will be set to the number of packets
+  // dropped on the socket since the socket was created, assuming the kernel
+  // supports this feature.
+  //
+  // If self_address is non-null, it will be set to the address the peer sent
+  // packets to, assuming a packet was read.
+  //
+  // If timestamp is non-null, it will be filled with the timestamp of the
+  // received packet, assuming a packet was read and the platform supports
+  // packet receipt timestamping. If the platform does not support packet
+  // receipt timestamping, timestamp will not be changed.
+  static int ReadPacket(int fd,
+                        char* buffer,
+                        size_t buf_len,
+                        QuicPacketCount* dropped_packets,
+                        QuicIpAddress* self_address,
+                        QuicWallTime* walltimestamp,
+                        QuicSocketAddress* peer_address);
+
+  // Writes buf_len to the socket. If writing is successful, sets the result's
+  // status to WRITE_STATUS_OK and sets bytes_written.  Otherwise sets the
+  // result's status to WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and sets
+  // error_code to errno.
+  static WriteResult WritePacket(int fd,
+                                 const char* buffer,
+                                 size_t buf_len,
+                                 const QuicIpAddress& self_address,
+                                 const QuicSocketAddress& peer_address);
+
+  // A helper for WritePacket which fills in the cmsg with the supplied self
+  // address.
+  // Returns the length of the packet info structure used.
+  static size_t SetIpInfoInCmsg(const QuicIpAddress& self_address,
+                                cmsghdr* cmsg);
+
+  // Creates a UDP socket and sets appropriate socket options for QUIC.
+  // Returns the created FD if successful, -1 otherwise.
+  // |overflow_supported| is set to true if the socket supports it.
+  static int CreateUDPSocket(const QuicSocketAddress& address,
+                             bool* overflow_supported);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QuicSocketUtils);
+};
+
+}  // namespace net
+
+#endif  // NET_TOOLS_QUIC_PLATFORM_IMPL_QUIC_SOCKET_UTILS_H_
diff --git a/net/tools/quic/platform/impl/quic_socket_utils_test.cc b/net/tools/quic/platform/impl/quic_socket_utils_test.cc
new file mode 100644
index 0000000..899d1b8e
--- /dev/null
+++ b/net/tools/quic/platform/impl/quic_socket_utils_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 The Chromium 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/tools/quic/platform/impl/quic_socket_utils.h"
+
+#include <fcntl.h>
+
+#include <array>
+
+#include "net/quic/platform/api/quic_socket_address.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+// A test fixture is used to ensure that all sockets are closed down gracefully
+// upon test completion.  Also provides a convenient API to Bind not presently
+// available in QuicSocketUtils.
+class QuicSocketUtilsTest : public ::testing::Test {
+ protected:
+  ~QuicSocketUtilsTest() override {
+    for (int fd : open_sockets_) {
+      close(fd);
+    }
+  }
+
+  int CreateUDPSocket(const QuicSocketAddress& address) {
+    bool overflow_supported = false;
+    int fd = QuicSocketUtils::CreateUDPSocket(address, &overflow_supported);
+    if (fd != -1) {
+      open_sockets_.push_back(fd);
+    }
+    return fd;
+  }
+
+  int CreateBoundUDPSocket(QuicSocketAddress* address) {
+    int fd = CreateUDPSocket(*address);
+    *address = BindSocket(fd, *address);
+    if (!address->IsInitialized()) {
+      close(fd);
+      fd = -1;
+    }
+    return fd;
+  }
+
+  QuicSocketAddress BindSocket(int fd, const QuicSocketAddress& address) {
+    QuicSocketAddress bound_address;
+
+    if (fd == -1) {
+      return bound_address;
+    }
+
+    sockaddr_storage bind_addr_native = address.generic_address();
+    socklen_t bind_addr_size = 0;
+
+    switch (address.host().address_family()) {
+      case IpAddressFamily::IP_V4:
+        bind_addr_size = sizeof(struct sockaddr_in);
+        break;
+      case IpAddressFamily::IP_V6:
+        bind_addr_size = sizeof(struct sockaddr_in6);
+        break;
+      case IpAddressFamily::IP_UNSPEC:
+        LOG(FATAL) << "Unspecified IP address family";
+    }
+
+    int rc = bind(fd, reinterpret_cast<sockaddr*>(&bind_addr_native),
+                  bind_addr_size);
+    if (rc != 0) {
+      LOG(ERROR) << "Failed to bind socket to " << address.ToString() << ": "
+                 << strerror(errno);
+      return bound_address;
+    }
+
+    rc = bound_address.FromSocket(fd);
+    if (rc != 0) {
+      LOG(ERROR) << "Failed to get bound socket address from fd: "
+                 << strerror(errno);
+      bound_address = QuicSocketAddress();
+    }
+    return bound_address;
+  }
+
+ private:
+  std::vector<int> open_sockets_;
+};
+
+// This test verifies that QuicSocketUtils creates a non-blocking socket
+// successfully by seeing if a read blocks.
+TEST_F(QuicSocketUtilsTest, NonBlockingSocket) {
+  std::array<char, 512> buffer;
+
+  QuicIpAddress localhost = QuicIpAddress::Loopback4();
+  QuicSocketAddress addr(localhost, 0);
+
+  int fd = CreateUDPSocket(addr);
+  ASSERT_NE(-1, fd);
+
+  int fd_flags = fcntl(fd, F_GETFL, 0);
+
+  // Assert so that the test errors out quickly rather than blocking below and
+  // relying on timeouts.
+  ASSERT_TRUE(fd_flags & O_NONBLOCK) << "Socket not reporting as non-blocking";
+
+  QuicIpAddress target_server_addr;
+  auto walltimestamp = QuicWallTime::Zero();
+  QuicSocketAddress remote_addr;
+  int bytes_read = QuicSocketUtils::ReadPacket(fd, buffer.data(), buffer.size(),
+                                               nullptr, &target_server_addr,
+                                               &walltimestamp, &remote_addr);
+  EXPECT_EQ(-1, bytes_read);
+}
+
+// This test verifies that we can successfully WritePacket/ReadPacket between
+// two localhost sockets.
+TEST_F(QuicSocketUtilsTest, PacketRoundTrip) {
+  QuicIpAddress localhost = QuicIpAddress::Loopback4();
+  QuicSocketAddress client_addr(localhost, 0);
+  QuicSocketAddress server_addr(localhost, 0);
+
+  int server_fd = CreateBoundUDPSocket(&server_addr);
+  int client_fd = CreateUDPSocket(client_addr);
+
+  ASSERT_NE(-1, server_fd);
+  ASSERT_NE(-1, client_fd);
+
+  {
+    std::array<char, 512> write_buffer;
+    for (size_t i = 0; i < write_buffer.size(); i++) {
+      write_buffer[i] = static_cast<char>(i);
+    }
+    auto res = QuicSocketUtils::WritePacket(client_fd, write_buffer.data(),
+                                            write_buffer.size(),
+                                            QuicIpAddress(), server_addr);
+    ASSERT_EQ(WRITE_STATUS_OK, res.status) << "Failed to write with error "
+                                           << res.error_code;
+    EXPECT_EQ(512, res.bytes_written);
+  }
+
+  fd_set read_fds;
+  FD_ZERO(&read_fds);
+  FD_SET(server_fd, &read_fds);
+
+  timeval select_timeout;
+  select_timeout.tv_sec = 5;
+  select_timeout.tv_usec = 0;
+
+  int select_rc =
+      select(1 + server_fd, &read_fds, nullptr, nullptr, &select_timeout);
+  EXPECT_EQ(select_rc, 1) << "server_fd didn't become read selectable: "
+                          << errno;
+
+  {
+    std::array<char, 1024> read_buffer;
+    QuicIpAddress target_server_addr;
+    auto walltimestamp = QuicWallTime::Zero();
+    QuicSocketAddress remote_addr;
+    int bytes_read = QuicSocketUtils::ReadPacket(
+        server_fd, read_buffer.data(), read_buffer.size(), nullptr,
+        &target_server_addr, &walltimestamp, &remote_addr);
+    EXPECT_EQ(512, bytes_read);
+    for (int i = 0; i < bytes_read; i++) {
+      EXPECT_EQ(static_cast<char>(i), read_buffer[i]);
+    }
+  }
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace net
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index 962835d..a7e04b02 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -23,9 +23,9 @@
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/core/quic_server_id.h"
 #include "net/quic/core/spdy_utils.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_epoll_alarm_factory.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
-#include "net/tools/quic/quic_socket_utils.h"
 
 #ifndef SO_RXQ_OVFL
 #define SO_RXQ_OVFL 40
diff --git a/net/tools/quic/quic_default_packet_writer.cc b/net/tools/quic/quic_default_packet_writer.cc
index c03f9c6b..e7aa91d 100644
--- a/net/tools/quic/quic_default_packet_writer.cc
+++ b/net/tools/quic/quic_default_packet_writer.cc
@@ -4,7 +4,7 @@
 
 #include "net/tools/quic/quic_default_packet_writer.h"
 
-#include "net/tools/quic/quic_socket_utils.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 
 namespace net {
 
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index 56b87ff..0de4d382 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -1148,7 +1148,7 @@
     chlo.SetVector(net::kCOPT, net::QuicTagVector{net::kSREJ});
     // Pass an inchoate CHLO.
     CryptoTestUtils::GenerateFullCHLO(
-        chlo, &crypto_config_, server_ip_, client_addr_, version, clock_,
+        chlo, &crypto_config_, server_addr_, client_addr_, version, clock_,
         signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
         &full_chlo_);
   }
@@ -1158,7 +1158,7 @@
   }
 
  protected:
-  QuicIpAddress server_ip_;
+  QuicSocketAddress server_addr_;
   QuicSocketAddress client_addr_;
   scoped_refptr<QuicSignedServerConfig> signed_config_;
   const QuicClock* clock_;
@@ -1597,7 +1597,7 @@
     chlo_.SetVector(net::kCOPT, net::QuicTagVector{net::kSREJ});
     // Pass an inchoate CHLO.
     CryptoTestUtils::GenerateFullCHLO(
-        chlo_, &crypto_config_, server_ip_, client_addr_, version, clock_,
+        chlo_, &crypto_config_, server_addr_, client_addr_, version, clock_,
         signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
         &full_chlo_);
 
@@ -1648,7 +1648,7 @@
 
  private:
   QuicCryptoServerConfigPeer crypto_config_peer_;
-  QuicIpAddress server_ip_;
+  QuicSocketAddress server_addr_;
   scoped_refptr<QuicSignedServerConfig> signed_config_;
   const QuicClock* clock_;
   CryptoHandshakeMessage chlo_;
diff --git a/net/tools/quic/quic_epoll_connection_helper.cc b/net/tools/quic/quic_epoll_connection_helper.cc
index 6a7f9ddc..0bf19f3 100644
--- a/net/tools/quic/quic_epoll_connection_helper.cc
+++ b/net/tools/quic/quic_epoll_connection_helper.cc
@@ -11,7 +11,7 @@
 #include "base/stl_util.h"
 #include "net/quic/core/crypto/quic_random.h"
 #include "net/tools/epoll_server/epoll_server.h"
-#include "net/tools/quic/quic_socket_utils.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 
 namespace net {
 
diff --git a/net/tools/quic/quic_packet_reader.cc b/net/tools/quic/quic_packet_reader.cc
index 26734dfd..977249b 100644
--- a/net/tools/quic/quic_packet_reader.cc
+++ b/net/tools/quic/quic_packet_reader.cc
@@ -16,9 +16,9 @@
 #include "net/quic/core/quic_bug_tracker.h"
 #include "net/quic/core/quic_flags.h"
 #include "net/quic/platform/api/quic_socket_address.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_dispatcher.h"
 #include "net/tools/quic/quic_process_packet_interface.h"
-#include "net/tools/quic/quic_socket_utils.h"
 
 #define MMSG_MORE 0
 
diff --git a/net/tools/quic/quic_packet_reader.h b/net/tools/quic/quic_packet_reader.h
index f554fdd..71e9485c 100644
--- a/net/tools/quic/quic_packet_reader.h
+++ b/net/tools/quic/quic_packet_reader.h
@@ -14,8 +14,8 @@
 #include "net/quic/core/quic_packets.h"
 #include "net/quic/platform/api/quic_clock.h"
 #include "net/quic/platform/api/quic_socket_address.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_process_packet_interface.h"
-#include "net/tools/quic/quic_socket_utils.h"
 
 #define MMSG_MORE 0
 
diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc
index d1090d3..7054dcf 100644
--- a/net/tools/quic/quic_server.cc
+++ b/net/tools/quic/quic_server.cc
@@ -21,6 +21,7 @@
 #include "net/quic/core/quic_data_reader.h"
 #include "net/quic/core/quic_packets.h"
 #include "net/tools/quic/platform/impl/quic_epoll_clock.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_dispatcher.h"
 #include "net/tools/quic/quic_epoll_alarm_factory.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
@@ -28,7 +29,6 @@
 #include "net/tools/quic/quic_packet_reader.h"
 #include "net/tools/quic/quic_simple_crypto_server_stream_helper.h"
 #include "net/tools/quic/quic_simple_dispatcher.h"
-#include "net/tools/quic/quic_socket_utils.h"
 
 #ifndef SO_RXQ_OVFL
 #define SO_RXQ_OVFL 40
diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc
deleted file mode 100644
index 65ee67b..0000000
--- a/net/tools/quic/quic_socket_utils.cc
+++ /dev/null
@@ -1,316 +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 "net/tools/quic/quic_socket_utils.h"
-
-#include <errno.h>
-#include <linux/net_tstamp.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <string>
-
-#include "base/logging.h"
-#include "net/quic/core/quic_bug_tracker.h"
-#include "net/quic/core/quic_flags.h"
-#include "net/quic/core/quic_packets.h"
-#include "net/quic/platform/api/quic_socket_address.h"
-
-#ifndef SO_RXQ_OVFL
-#define SO_RXQ_OVFL 40
-#endif
-
-using std::string;
-
-namespace net {
-
-// static
-void QuicSocketUtils::GetAddressAndTimestampFromMsghdr(
-    struct msghdr* hdr,
-    QuicIpAddress* address,
-    QuicWallTime* walltimestamp) {
-  if (hdr->msg_controllen > 0) {
-    for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
-         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
-      char* addr_data = nullptr;
-      int len = 0;
-      if (cmsg->cmsg_type == IPV6_PKTINFO) {
-        in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
-        addr_data = reinterpret_cast<char*>(&info->ipi6_addr);
-        len = sizeof(in6_addr);
-        address->FromPackedString(addr_data, len);
-      } else if (cmsg->cmsg_type == IP_PKTINFO) {
-        in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
-        addr_data = reinterpret_cast<char*>(&info->ipi_addr);
-        len = sizeof(in_addr);
-        address->FromPackedString(addr_data, len);
-      } else if (cmsg->cmsg_level == SOL_SOCKET &&
-                 cmsg->cmsg_type == SO_TIMESTAMPING) {
-        LinuxTimestamping* lts =
-            reinterpret_cast<LinuxTimestamping*>(CMSG_DATA(cmsg));
-        timespec* ts = &lts->systime;
-        int64_t usec = (static_cast<int64_t>(ts->tv_sec) * 1000 * 1000) +
-                       (static_cast<int64_t>(ts->tv_nsec) / 1000);
-        *walltimestamp = QuicWallTime::FromUNIXMicroseconds(usec);
-      }
-    }
-  }
-}
-
-// static
-bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr* hdr,
-                                            QuicPacketCount* dropped_packets) {
-  if (hdr->msg_controllen > 0) {
-    struct cmsghdr* cmsg;
-    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
-         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
-      if (cmsg->cmsg_type == SO_RXQ_OVFL) {
-        *dropped_packets = *(reinterpret_cast<uint32_t*> CMSG_DATA(cmsg));
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-// static
-bool QuicSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) {
-  if (hdr->msg_controllen > 0) {
-    struct cmsghdr* cmsg;
-    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
-         cmsg = CMSG_NXTHDR(hdr, cmsg)) {
-      if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) ||
-          (cmsg->cmsg_level == IPPROTO_IPV6 &&
-           cmsg->cmsg_type == IPV6_HOPLIMIT)) {
-        *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg)));
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-// static
-int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
-  int get_local_ip = 1;
-  int rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
-                      sizeof(get_local_ip));
-  if (rc == 0 && address_family == AF_INET6) {
-    rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
-                    sizeof(get_local_ip));
-  }
-  return rc;
-}
-
-// static
-int QuicSocketUtils::SetGetSoftwareReceiveTimestamp(int fd) {
-  int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
-  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &timestamping,
-                    sizeof(timestamping));
-}
-
-// static
-bool QuicSocketUtils::SetSendBufferSize(int fd, size_t size) {
-  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) != 0) {
-    LOG(ERROR) << "Failed to set socket send size";
-    return false;
-  }
-  return true;
-}
-
-// static
-bool QuicSocketUtils::SetReceiveBufferSize(int fd, size_t size) {
-  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) != 0) {
-    LOG(ERROR) << "Failed to set socket recv size";
-    return false;
-  }
-  return true;
-}
-
-// static
-int QuicSocketUtils::ReadPacket(int fd,
-                                char* buffer,
-                                size_t buf_len,
-                                QuicPacketCount* dropped_packets,
-                                QuicIpAddress* self_address,
-                                QuicWallTime* walltimestamp,
-                                QuicSocketAddress* peer_address) {
-  DCHECK(peer_address != nullptr);
-  char cbuf[kSpaceForCmsg];
-  memset(cbuf, 0, arraysize(cbuf));
-
-  iovec iov = {buffer, buf_len};
-  struct sockaddr_storage raw_address;
-  msghdr hdr;
-
-  hdr.msg_name = &raw_address;
-  hdr.msg_namelen = sizeof(sockaddr_storage);
-  hdr.msg_iov = &iov;
-  hdr.msg_iovlen = 1;
-  hdr.msg_flags = 0;
-
-  struct cmsghdr* cmsg = reinterpret_cast<struct cmsghdr*>(cbuf);
-  cmsg->cmsg_len = arraysize(cbuf);
-  hdr.msg_control = cmsg;
-  hdr.msg_controllen = arraysize(cbuf);
-
-  int bytes_read = recvmsg(fd, &hdr, 0);
-
-  // Return before setting dropped packets: if we get EAGAIN, it will
-  // be 0.
-  if (bytes_read < 0 && errno != 0) {
-    if (errno != EAGAIN) {
-      LOG(ERROR) << "Error reading " << strerror(errno);
-    }
-    return -1;
-  }
-
-  if (hdr.msg_controllen >= arraysize(cbuf)) {
-    QUIC_BUG << "Incorrectly set control length: " << hdr.msg_controllen
-             << ", expected " << arraysize(cbuf);
-    return -1;
-  }
-
-  if (dropped_packets != nullptr) {
-    GetOverflowFromMsghdr(&hdr, dropped_packets);
-  }
-
-  QuicIpAddress stack_address;
-  if (self_address == nullptr) {
-    self_address = &stack_address;
-  }
-
-  QuicWallTime stack_walltimestamp = QuicWallTime::FromUNIXMicroseconds(0);
-  if (walltimestamp == nullptr) {
-    walltimestamp = &stack_walltimestamp;
-  }
-
-  GetAddressAndTimestampFromMsghdr(&hdr, self_address, walltimestamp);
-
-  *peer_address = QuicSocketAddress(raw_address);
-  return bytes_read;
-}
-
-size_t QuicSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address,
-                                        cmsghdr* cmsg) {
-  string address_string;
-  if (self_address.IsIPv4()) {
-    cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
-    cmsg->cmsg_level = IPPROTO_IP;
-    cmsg->cmsg_type = IP_PKTINFO;
-    in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
-    memset(pktinfo, 0, sizeof(in_pktinfo));
-    pktinfo->ipi_ifindex = 0;
-    address_string = self_address.ToPackedString();
-    memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(),
-           address_string.length());
-    return sizeof(in_pktinfo);
-  } else if (self_address.IsIPv6()) {
-    cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
-    cmsg->cmsg_level = IPPROTO_IPV6;
-    cmsg->cmsg_type = IPV6_PKTINFO;
-    in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
-    memset(pktinfo, 0, sizeof(in6_pktinfo));
-    address_string = self_address.ToPackedString();
-    memcpy(&pktinfo->ipi6_addr, address_string.c_str(),
-           address_string.length());
-    return sizeof(in6_pktinfo);
-  } else {
-    NOTREACHED() << "Unrecognized IPAddress";
-    return 0;
-  }
-}
-
-// static
-WriteResult QuicSocketUtils::WritePacket(
-    int fd,
-    const char* buffer,
-    size_t buf_len,
-    const QuicIpAddress& self_address,
-    const QuicSocketAddress& peer_address) {
-  sockaddr_storage raw_address = peer_address.generic_address();
-  iovec iov = {const_cast<char*>(buffer), buf_len};
-
-  msghdr hdr;
-  hdr.msg_name = &raw_address;
-  hdr.msg_namelen = raw_address.ss_family == AF_INET ? sizeof(sockaddr_in)
-                                                     : sizeof(sockaddr_in6);
-  hdr.msg_iov = &iov;
-  hdr.msg_iovlen = 1;
-  hdr.msg_flags = 0;
-
-  const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo));
-  const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo));
-  // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info.
-  const int kSpaceForIp =
-      (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4;
-  char cbuf[kSpaceForIp];
-  if (!self_address.IsInitialized()) {
-    hdr.msg_control = 0;
-    hdr.msg_controllen = 0;
-  } else {
-    hdr.msg_control = cbuf;
-    hdr.msg_controllen = kSpaceForIp;
-    cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    SetIpInfoInCmsg(self_address, cmsg);
-    hdr.msg_controllen = cmsg->cmsg_len;
-  }
-
-  int rc;
-  do {
-    rc = sendmsg(fd, &hdr, 0);
-  } while (rc < 0 && errno == EINTR);
-  if (rc >= 0) {
-    return WriteResult(WRITE_STATUS_OK, rc);
-  }
-  return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK)
-                         ? WRITE_STATUS_BLOCKED
-                         : WRITE_STATUS_ERROR,
-                     errno);
-}
-
-// static
-int QuicSocketUtils::CreateUDPSocket(const QuicSocketAddress& address,
-                                     bool* overflow_supported) {
-  int address_family = address.host().AddressFamilyToInt();
-  int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
-  if (fd < 0) {
-    LOG(ERROR) << "socket() failed: " << strerror(errno);
-    return -1;
-  }
-
-  int get_overflow = 1;
-  int rc = setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
-                      sizeof(get_overflow));
-  if (rc < 0) {
-    DLOG(WARNING) << "Socket overflow detection not supported";
-  } else {
-    *overflow_supported = true;
-  }
-
-  if (!SetReceiveBufferSize(fd, kDefaultSocketReceiveBuffer)) {
-    return -1;
-  }
-
-  if (!SetSendBufferSize(fd, kDefaultSocketReceiveBuffer)) {
-    return -1;
-  }
-
-  rc = SetGetAddressInfo(fd, address_family);
-  if (rc < 0) {
-    LOG(ERROR) << "IP detection not supported" << strerror(errno);
-    return -1;
-  }
-
-  rc = SetGetSoftwareReceiveTimestamp(fd);
-  if (rc < 0) {
-    LOG(WARNING) << "SO_TIMESTAMPING not supported; using fallback: "
-                 << strerror(errno);
-  }
-
-  return fd;
-}
-
-}  // namespace net
diff --git a/net/tools/quic/quic_socket_utils.h b/net/tools/quic/quic_socket_utils.h
deleted file mode 100644
index d82a86f..0000000
--- a/net/tools/quic/quic_socket_utils.h
+++ /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.
-//
-// Some socket related helper methods for quic.
-
-#ifndef NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
-#define NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
-
-#include <netinet/in.h>
-#include <stddef.h>
-#include <sys/socket.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "net/base/ip_address.h"
-#include "net/base/ip_endpoint.h"
-#include "net/quic/core/quic_bandwidth.h"
-#include "net/quic/core/quic_types.h"
-
-namespace net {
-class QuicIpAddress;
-class QuicSocketAddress;
-
-// This is the structure that SO_TIMESTAMPING fills into the cmsg header. It is
-// well-defined, but does not have a definition in a public header. See
-// https://www.kernel.org/doc/Documentation/networking/timestamping.txt for more
-// information.
-struct LinuxTimestamping {
-  // The converted system time of the timestamp.
-  struct timespec systime;
-  // Deprecated; serves only as padding.
-  struct timespec hwtimetrans;
-  // The raw hardware timestamp.
-  struct timespec hwtimeraw;
-};
-
-class QuicSocketUtils {
- public:
-  // The first integer is for overflow. The in6_pktinfo is the larger of the
-  // address structures present. LinuxTimestamping is present for socket
-  // timestamping.  The subsequent int is for ttl.
-  // The final int is a sentinel so the msg_controllen feedback
-  // can be used to detect larger control messages than there is space for.
-  static const int kSpaceForCmsg =
-      CMSG_SPACE(CMSG_LEN(sizeof(int)) + CMSG_LEN(sizeof(in6_pktinfo)) +
-                 CMSG_LEN(sizeof(LinuxTimestamping)) +
-                 CMSG_LEN(sizeof(int)) +
-                 CMSG_LEN(sizeof(int)));
-
-  // Fills in |address| if |hdr| contains IP_PKTINFO or IPV6_PKTINFO. Fills in
-  // |timestamp| if |hdr| contains |SO_TIMESTAMPING|. |address| and |timestamp|
-  // must not be null.
-  static void GetAddressAndTimestampFromMsghdr(struct msghdr* hdr,
-                                               QuicIpAddress* address,
-                                               QuicWallTime* walltimestamp);
-
-  // If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets
-  // to the correct value and return true. Otherwise it will return false.
-  static bool GetOverflowFromMsghdr(struct msghdr* hdr,
-                                    QuicPacketCount* dropped_packets);
-
-  // If the msghdr contains an IP_TTL entry, this will set ttl to the correct
-  // value and return true. Otherwise it will return false.
-  static bool GetTtlFromMsghdr(struct msghdr* hdr, int* ttl);
-
-  // Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on
-  // address_family.  Returns the return code from setsockopt.
-  static int SetGetAddressInfo(int fd, int address_family);
-
-  // Sets SO_TIMESTAMPING on the socket for software receive timestamping.
-  // Returns the return code from setsockopt.
-  static int SetGetSoftwareReceiveTimestamp(int fd);
-
-  // Sets the send buffer size to |size| and returns false if it fails.
-  static bool SetSendBufferSize(int fd, size_t size);
-
-  // Sets the receive buffer size to |size| and returns false if it fails.
-  static bool SetReceiveBufferSize(int fd, size_t size);
-
-  // Reads buf_len from the socket.  If reading is successful, returns bytes
-  // read and sets peer_address to the peer address.  Otherwise returns -1.
-  //
-  // If dropped_packets is non-null, it will be set to the number of packets
-  // dropped on the socket since the socket was created, assuming the kernel
-  // supports this feature.
-  //
-  // If self_address is non-null, it will be set to the address the peer sent
-  // packets to, assuming a packet was read.
-  //
-  // If timestamp is non-null, it will be filled with the timestamp of the
-  // received packet, assuming a packet was read and the platform supports
-  // packet receipt timestamping. If the platform does not support packet
-  // receipt timestamping, timestamp will not be changed.
-  static int ReadPacket(int fd,
-                        char* buffer,
-                        size_t buf_len,
-                        QuicPacketCount* dropped_packets,
-                        QuicIpAddress* self_address,
-                        QuicWallTime* walltimestamp,
-                        QuicSocketAddress* peer_address);
-
-  // Writes buf_len to the socket. If writing is successful, sets the result's
-  // status to WRITE_STATUS_OK and sets bytes_written.  Otherwise sets the
-  // result's status to WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and sets
-  // error_code to errno.
-  static WriteResult WritePacket(int fd,
-                                 const char* buffer,
-                                 size_t buf_len,
-                                 const QuicIpAddress& self_address,
-                                 const QuicSocketAddress& peer_address);
-
-  // A helper for WritePacket which fills in the cmsg with the supplied self
-  // address.
-  // Returns the length of the packet info structure used.
-  static size_t SetIpInfoInCmsg(const QuicIpAddress& self_address,
-                                cmsghdr* cmsg);
-
-  // Creates a UDP socket and sets appropriate socket options for QUIC.
-  // Returns the created FD if successful, -1 otherwise.
-  // |overflow_supported| is set to true if the socket supports it.
-  static int CreateUDPSocket(const QuicSocketAddress& address,
-                             bool* overflow_supported);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(QuicSocketUtils);
-};
-
-}  // namespace net
-
-#endif  // NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index 2ac6fd2..6de90df 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -133,6 +133,8 @@
 size_t QuicSpdyClientStream::SendRequest(SpdyHeaderBlock headers,
                                          StringPiece body,
                                          bool fin) {
+  QuicConnection::ScopedPacketBundler bundler(
+      session_->connection(), QuicConnection::SEND_ACK_IF_QUEUED);
   bool send_fin_with_headers = fin && body.empty();
   size_t bytes_sent = body.size();
   header_bytes_written_ =
diff --git a/net/tools/quic/stateless_rejector.cc b/net/tools/quic/stateless_rejector.cc
index 2736b4e..e16d2ce 100644
--- a/net/tools/quic/stateless_rejector.cc
+++ b/net/tools/quic/stateless_rejector.cc
@@ -88,7 +88,7 @@
   StatelessRejector* rejector_ptr = rejector.get();
   rejector_ptr->crypto_config_->ValidateClientHello(
       rejector_ptr->chlo_, rejector_ptr->client_address_.host(),
-      rejector_ptr->server_address_.host(), rejector_ptr->version_,
+      rejector_ptr->server_address_, rejector_ptr->version_,
       rejector_ptr->clock_, rejector_ptr->signed_config_,
       std::unique_ptr<ValidateCallback>(
           new ValidateCallback(std::move(rejector), std::move(done_cb))));
@@ -127,8 +127,8 @@
       new ProcessClientHelloCallback(std::move(rejector), std::move(done_cb)));
   crypto_config_->ProcessClientHello(
       result,
-      /*reject_only=*/true, connection_id_, server_address_.host(),
-      client_address_, version_, versions_,
+      /*reject_only=*/true, connection_id_, server_address_, client_address_,
+      version_, versions_,
       /*use_stateless_rejects=*/true, server_designated_connection_id_, clock_,
       random_, compressed_certs_cache_, params_, signed_config_,
       QuicCryptoStream::CryptoMessageFramingOverhead(version_),
diff --git a/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
index 5ac0d4b0..46c2a1a 100644
--- a/net/tools/quic/test_tools/packet_dropping_test_writer.cc
+++ b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
@@ -7,8 +7,8 @@
 #include <limits>
 
 #include "base/rand_util.h"
+#include "net/tools/quic/platform/impl/quic_socket_utils.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
-#include "net/tools/quic/quic_socket_utils.h"
 
 namespace net {
 namespace test {
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index eb4be23..75eb422 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -762,14 +762,14 @@
   }
 
   int rv = job_->Read(dest, dest_size);
-  if (rv == ERR_IO_PENDING)
+  if (rv == ERR_IO_PENDING) {
     set_status(URLRequestStatus::FromError(ERR_IO_PENDING));
+  } else if (rv <= 0) {
+    NotifyRequestCompleted();
+  }
 
   // If rv is not 0 or actual bytes read, the status cannot be success.
   DCHECK(rv >= 0 || status_.status() != URLRequestStatus::SUCCESS);
-
-  if (rv == 0 && status_.is_success())
-    NotifyRequestCompleted();
   return rv;
 }
 
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 14d6f548..42bcc899 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -15,6 +15,7 @@
 #include "net/cookies/cookie_store.h"
 #include "net/dns/host_resolver.h"
 #include "net/http/http_transaction_factory.h"
+#include "net/socket/ssl_client_socket_impl.h"
 #include "net/url_request/http_user_agent_settings.h"
 #include "net/url_request/url_request.h"
 
@@ -127,6 +128,13 @@
   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
                   base::trace_event::MemoryAllocatorDump::kUnitsObjects,
                   url_requests_->size());
+  HttpTransactionFactory* transaction_factory = http_transaction_factory();
+  if (transaction_factory) {
+    HttpNetworkSession* network_session = transaction_factory->GetSession();
+    if (network_session)
+      network_session->DumpMemoryStats(pmd, dump->absolute_name());
+  }
+  SSLClientSocketImpl::DumpSSLClientSessionMemoryStats(pmd);
   return true;
 }
 
diff --git a/net/url_request/url_request_context_unittest.cc b/net/url_request/url_request_context_unittest.cc
index 627e6f9..43d67fd 100644
--- a/net/url_request/url_request_context_unittest.cc
+++ b/net/url_request/url_request_context_unittest.cc
@@ -29,16 +29,22 @@
   const base::trace_event::ProcessMemoryDump::AllocatorDumpsMap&
       allocator_dumps = process_memory_dump->allocator_dumps();
 
+  bool did_dump_http_network_session = false;
+  bool did_dump_ssl_client_session_cache = false;
   bool did_dump_url_request_context = false;
-  // bool did_dump_space_stats = false;
-  // bool did_dump_objects_stats = false;
   for (const auto& it : allocator_dumps) {
     const std::string& dump_name = it.first;
+    if (dump_name.find("net/http_network_session") != std::string::npos)
+      did_dump_http_network_session = true;
+    if (dump_name.find("net/ssl_session_cache") != std::string::npos)
+      did_dump_ssl_client_session_cache = true;
     if (dump_name.find("net/url_request_context") != std::string::npos)
       did_dump_url_request_context = true;
   }
-
+  ASSERT_TRUE(did_dump_http_network_session);
+  ASSERT_TRUE(did_dump_ssl_client_session_cache);
   ASSERT_TRUE(did_dump_url_request_context);
 }
 
+// TODO(xunjieli): Add more granular tests on the MemoryDumpProvider.
 }  // namespace net
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 7520a48..68e708f 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -31,6 +31,7 @@
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/sdch_manager.h"
 #include "net/base/sdch_problem_codes.h"
+#include "net/base/trace_constants.h"
 #include "net/base/url_util.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cookies/cookie_store.h"
@@ -853,8 +854,7 @@
 }
 
 void URLRequestHttpJob::OnStartCompleted(int result) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "URLRequestHttpJob::OnStartCompleted");
+  TRACE_EVENT0(kNetTracingCategory, "URLRequestHttpJob::OnStartCompleted");
   RecordTimer();
 
   // If the job is done (due to cancellation), can just ignore this
@@ -936,8 +936,7 @@
 }
 
 void URLRequestHttpJob::OnReadCompleted(int result) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
-               "URLRequestHttpJob::OnReadCompleted");
+  TRACE_EVENT0(kNetTracingCategory, "URLRequestHttpJob::OnReadCompleted");
   read_in_progress_ = false;
 
   DCHECK_NE(ERR_IO_PENDING, result);
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index b315664..33fb9af 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -495,8 +495,9 @@
     source_stream_ = SetUpSourceStream();
 
     if (!source_stream_) {
-      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
-                                  ERR_CONTENT_DECODING_INIT_FAILED));
+      OnDone(URLRequestStatus(URLRequestStatus::FAILED,
+                              ERR_CONTENT_DECODING_INIT_FAILED),
+             true);
       return;
     }
     if (source_stream_->type() == SourceStream::TYPE_NONE) {
@@ -563,7 +564,7 @@
   // |this| may have been deleted here.
 }
 
-void URLRequestJob::NotifyDone(const URLRequestStatus &status) {
+void URLRequestJob::OnDone(const URLRequestStatus& status, bool notify_done) {
   DCHECK(!done_) << "Job sending done notification twice";
   if (done_)
     return;
@@ -589,15 +590,18 @@
 
   MaybeNotifyNetworkBytes();
 
-  // Complete this notification later.  This prevents us from re-entering the
-  // delegate if we're done because of a synchronous call.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&URLRequestJob::CompleteNotifyDone,
-                            weak_factory_.GetWeakPtr()));
+  if (notify_done) {
+    // Complete this notification later.  This prevents us from re-entering the
+    // delegate if we're done because of a synchronous call.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&URLRequestJob::NotifyDone, weak_factory_.GetWeakPtr()));
+  }
 }
 
-void URLRequestJob::CompleteNotifyDone() {
-  // Check if we should notify the delegate that we're done because of an error.
+void URLRequestJob::NotifyDone() {
+  // Check if we should notify the URLRequest that we're done because of an
+  // error.
   if (!request_->status().is_success()) {
     // We report the error differently depending on whether we've called
     // OnResponseStarted yet.
@@ -613,7 +617,7 @@
 
 void URLRequestJob::NotifyCanceled() {
   if (!done_) {
-    NotifyDone(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED));
+    OnDone(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED), true);
   }
 }
 
@@ -669,20 +673,21 @@
   pending_read_buffer_ = nullptr;
 
   if (result < 0) {
-    NotifyDone(URLRequestStatus::FromError(result));
+    OnDone(URLRequestStatus::FromError(result), !synchronous);
     return;
   }
 
   if (result > 0) {
     postfilter_bytes_read_ += result;
-    if (!synchronous)
-      request_->NotifyReadCompleted(result);
-    return;
+  } else {
+    DCHECK_EQ(0, result);
+    DoneReading();
+    // In the synchronous case, the caller will notify the URLRequest of
+    // completion. In the async case, the NotifyReadCompleted call will.
+    // TODO(mmenke): Can this be combined with the error case?
+    OnDone(URLRequestStatus(), false);
   }
 
-  DCHECK_EQ(0, result);
-  DoneReading();
-  NotifyDone(URLRequestStatus());
   if (!synchronous)
     request_->NotifyReadCompleted(result);
 }
@@ -713,7 +718,7 @@
 void URLRequestJob::FollowRedirect(const RedirectInfo& redirect_info) {
   int rv = request_->Redirect(redirect_info);
   if (rv != OK)
-    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+    OnDone(URLRequestStatus(URLRequestStatus::FAILED, rv), true);
 }
 
 void URLRequestJob::GatherRawReadStats(int bytes_read) {
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 610ce332..2341999 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -360,15 +360,19 @@
   // |bytes_read| unfiltered bytes have been read for this job.
   void RecordBytesRead(int bytes_read);
 
-  // NotifyDone marks that request is done. It is really a glorified
+  // OnDone marks that request is done. It is really a glorified
   // set_status, but also does internal state checking and job tracking. It
   // should be called once per request, when the job is finished doing all IO.
-  void NotifyDone(const URLRequestStatus& status);
+  //
+  // If |notify_done| is true, will notify the URLRequest if there was an error
+  // asynchronously.  Otherwise, the caller will need to do this itself,
+  // possibly through a synchronous return value.
+  // TODO(mmenke):  Remove |notify_done|, and make caller handle notification.
+  void OnDone(const URLRequestStatus& status, bool notify_done);
 
-  // Some work performed by NotifyDone must be completed asynchronously so
-  // as to avoid re-entering URLRequest::Delegate. This method performs that
-  // work.
-  void CompleteNotifyDone();
+  // Takes care of the notification initiated by OnDone() to avoid re-entering
+  // the URLRequest::Delegate.
+  void NotifyDone();
 
   // Subclasses may implement this method to record packet arrival times.
   // The default implementation does nothing.  Only invoked when bytes have been
diff --git a/net/url_request/url_request_test_job.cc b/net/url_request/url_request_test_job.cc
index 1f5824bab..42e8c68f 100644
--- a/net/url_request/url_request_test_job.cc
+++ b/net/url_request/url_request_test_job.cc
@@ -56,6 +56,9 @@
 GURL URLRequestTestJob::test_url_error() {
   return GURL("test:error");
 }
+GURL URLRequestTestJob::test_url_redirect_to_url_1() {
+  return GURL("test:redirect_to_1");
+}
 GURL URLRequestTestJob::test_url_redirect_to_url_2() {
   return GURL("test:redirect_to_2");
 }
@@ -93,6 +96,17 @@
 }
 
 // static getter for redirect response headers
+std::string URLRequestTestJob::test_redirect_to_url_1_headers() {
+  std::string headers = "HTTP/1.1 302 MOVED";
+  headers.push_back('\n');
+  headers += "Location: ";
+  headers += test_url_1().spec();
+  headers.push_back('\n');
+  headers.push_back('\n');
+  return headers;
+}
+
+// static getter for redirect response headers
 std::string URLRequestTestJob::test_redirect_to_url_2_headers() {
   std::string headers = "HTTP/1.1 302 MOVED";
   headers.push_back('\n');
@@ -199,6 +213,8 @@
       response_data_ = test_data_3();
     } else if (request_->url().spec() == test_url_4().spec()) {
       response_data_ = test_data_4();
+    } else if (request_->url().spec() == test_url_redirect_to_url_1().spec()) {
+      SetResponseHeaders(test_redirect_to_url_1_headers());
     } else if (request_->url().spec() == test_url_redirect_to_url_2().spec()) {
       SetResponseHeaders(test_redirect_to_url_2_headers());
     } else {
diff --git a/net/url_request/url_request_test_job.h b/net/url_request/url_request_test_job.h
index 162d5094..0f32723 100644
--- a/net/url_request/url_request_test_job.h
+++ b/net/url_request/url_request_test_job.h
@@ -69,6 +69,7 @@
   static GURL test_url_3();
   static GURL test_url_4();
   static GURL test_url_error();
+  static GURL test_url_redirect_to_url_1();
   static GURL test_url_redirect_to_url_2();
 
   // The data that corresponds to each of the URLs above
@@ -83,6 +84,9 @@
   // The headers for a redirect response
   static std::string test_redirect_headers();
 
+  // The headers for a redirect response to the first test url.
+  static std::string test_redirect_to_url_1_headers();
+
   // The headers for a redirect response to the second test url.
   static std::string test_redirect_to_url_2_headers();
 
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index cc56ef3..7ce435a 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -290,15 +290,22 @@
   if (response_started_count_ == 0)
     received_data_before_response_ = true;
 
-  if (cancel_in_rd_)
-    request_status_ = request->Cancel();
-
   if (bytes_read >= 0) {
     // There is data to read.
     received_bytes_count_ += bytes_read;
 
-    // consume the data
+    // Consume the data.
     data_received_.append(buf_->data(), bytes_read);
+
+    if (cancel_in_rd_) {
+      request_status_ = request->Cancel();
+      // If bytes_read is 0, won't get a notification on cancelation.
+      if (bytes_read == 0 && quit_on_complete_) {
+        base::ThreadTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+      }
+      return;
+    }
   }
 
   // If it was not end of stream, request to read more.
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 6a023ed2..3a90f5f7 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -165,6 +165,7 @@
   const std::string& data_received() const { return data_received_; }
   int bytes_received() const { return static_cast<int>(data_received_.size()); }
   int response_started_count() const { return response_started_count_; }
+  int received_bytes_count() const { return received_bytes_count_; }
   int received_redirect_count() const { return received_redirect_count_; }
   bool received_data_before_response() const {
     return received_data_before_response_;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index e4177b1..60920ef3 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -5549,7 +5549,7 @@
   EXPECT_EQ("None", d.data_received());
 }
 
-TEST_F(URLRequestTestHTTP, CancelTest) {
+TEST_F(URLRequestTestHTTP, CancelAfterStart) {
   TestDelegate d;
   {
     std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
@@ -5570,7 +5570,7 @@
   }
 }
 
-TEST_F(URLRequestTestHTTP, CancelTest2) {
+TEST_F(URLRequestTestHTTP, CancelInResponseStarted) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -5592,12 +5592,35 @@
   }
 }
 
-TEST_F(URLRequestTestHTTP, CancelTest3) {
+TEST_F(URLRequestTestHTTP, CancelOnDataReceived) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
   {
     std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
+        http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d));
+
+    d.set_cancel_in_received_data(true);
+
+    r->Start();
+    EXPECT_TRUE(r->is_pending());
+
+    base::RunLoop().Run();
+
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_NE(0, d.received_bytes_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_EQ(ERR_ABORTED, d.request_status());
+  }
+}
+
+TEST_F(URLRequestTestHTTP, CancelDuringEofRead) {
+  ASSERT_TRUE(http_test_server()->Start());
+
+  TestDelegate d;
+  {
+    // This returns an empty response (With headers).
+    std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
         http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d));
 
     d.set_cancel_in_received_data(true);
@@ -5608,16 +5631,13 @@
     base::RunLoop().Run();
 
     EXPECT_EQ(1, d.response_started_count());
-    // There is no guarantee about how much data was received
-    // before the cancel was issued.  It could have been 0 bytes,
-    // or it could have been all the bytes.
-    // EXPECT_EQ(0, d.bytes_received());
+    EXPECT_EQ(0, d.received_bytes_count());
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_EQ(ERR_ABORTED, d.request_status());
   }
 }
 
-TEST_F(URLRequestTestHTTP, CancelTest4) {
+TEST_F(URLRequestTestHTTP, CancelByDestroyingAfterStart) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -5643,7 +5663,7 @@
   EXPECT_EQ(0, d.bytes_received());
 }
 
-TEST_F(URLRequestTestHTTP, CancelTest5) {
+TEST_F(URLRequestTestHTTP, CancelWhileReadingFromCache) {
   ASSERT_TRUE(http_test_server()->Start());
 
   // populate cache
@@ -6084,7 +6104,7 @@
   std::unique_ptr<base::Value> value(
       base::JSONReader::Read(mock_report_sender.latest_report()));
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
   base::DictionaryValue* report_dict;
   ASSERT_TRUE(value->GetAsDictionary(&report_dict));
   std::string report_hostname;
@@ -6149,7 +6169,7 @@
   std::unique_ptr<base::Value> value(
       base::JSONReader::Read(mock_report_sender.latest_report()));
   ASSERT_TRUE(value);
-  ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY));
+  ASSERT_TRUE(value->IsType(base::Value::Type::DICTIONARY));
   base::DictionaryValue* report_dict;
   ASSERT_TRUE(value->GetAsDictionary(&report_dict));
   std::string report_hostname;
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index 64cd7595..4e4d94655 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -18,10 +18,10 @@
     "//ppapi/cpp:objects",
     "//ppapi/cpp/private:internal_module",
     "//ui/base",
-    "//ui/gfx/range",
   ]
 
   sources = [
+    "chunk_stream.cc",
     "chunk_stream.h",
     "document_loader.cc",
     "document_loader.h",
@@ -38,13 +38,6 @@
     "pdf_engine.h",
     "preview_mode_client.cc",
     "preview_mode_client.h",
-    "range_set.cc",
-    "range_set.h",
-    "timer.cc",
-    "timer.h",
-    "url_loader_wrapper.h",
-    "url_loader_wrapper_impl.cc",
-    "url_loader_wrapper_impl.h",
   ]
 
   if (pdf_engine == 0) {
@@ -79,8 +72,6 @@
 test("pdf_unittests") {
   sources = [
     "chunk_stream_unittest.cc",
-    "document_loader_unittest.cc",
-    "range_set_unittest.cc",
     "run_all_unittests.cc",
   ]
 
@@ -92,6 +83,5 @@
     "//ppapi/cpp",
     "//testing/gmock",
     "//testing/gtest",
-    "//ui/gfx/range",
   ]
 }
diff --git a/pdf/DEPS b/pdf/DEPS
index d088805..b7a8a17 100644
--- a/pdf/DEPS
+++ b/pdf/DEPS
@@ -4,6 +4,5 @@
   "+ppapi",
   "+ui/base/window_open_disposition.h",
   "+ui/events/keycodes/keyboard_codes.h",
-  "+ui/gfx/range/range.h",
   "+v8/include/v8.h"
 ]
diff --git a/pdf/chunk_stream.cc b/pdf/chunk_stream.cc
new file mode 100644
index 0000000..adb3cb6
--- /dev/null
+++ b/pdf/chunk_stream.cc
@@ -0,0 +1,175 @@
+// 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 "pdf/chunk_stream.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#define __STDC_LIMIT_MACROS
+#ifdef _WIN32
+#include <limits.h>
+#else
+#include <stdint.h>
+#endif
+
+#include <algorithm>
+
+namespace chrome_pdf {
+
+ChunkStream::ChunkStream() : stream_size_(0) {
+}
+
+ChunkStream::~ChunkStream() {
+}
+
+void ChunkStream::Clear() {
+  chunks_.clear();
+  data_.clear();
+  stream_size_ = 0;
+}
+
+void ChunkStream::Preallocate(size_t stream_size) {
+  data_.reserve(stream_size);
+  stream_size_ = stream_size;
+}
+
+size_t ChunkStream::GetSize() const {
+  return data_.size();
+}
+
+bool ChunkStream::WriteData(size_t offset, void* buffer, size_t size) {
+  if (SIZE_MAX - size < offset)
+    return false;
+
+  if (data_.size() < offset + size)
+    data_.resize(offset + size);
+
+  memcpy(&data_[offset], buffer, size);
+
+  if (chunks_.empty()) {
+    chunks_[offset] = size;
+    return true;
+  }
+
+  std::map<size_t, size_t>::iterator start = chunks_.upper_bound(offset);
+  if (start != chunks_.begin())
+    --start;  // start now points to the key equal or lower than offset.
+  if (start->first + start->second < offset)
+    ++start;  // start element is entirely before current chunk, skip it.
+
+  std::map<size_t, size_t>::iterator end = chunks_.upper_bound(offset + size);
+  if (start == end) {  // No chunks to merge.
+    chunks_[offset] = size;
+    return true;
+  }
+
+  --end;
+
+  size_t new_offset = std::min<size_t>(start->first, offset);
+  size_t new_size =
+      std::max<size_t>(end->first + end->second, offset + size) - new_offset;
+
+  chunks_.erase(start, ++end);
+
+  chunks_[new_offset] = new_size;
+
+  return true;
+}
+
+bool ChunkStream::ReadData(size_t offset, size_t size, void* buffer) const {
+  if (!IsRangeAvailable(offset, size))
+    return false;
+
+  memcpy(buffer, &data_[offset], size);
+  return true;
+}
+
+bool ChunkStream::GetMissedRanges(
+    size_t offset, size_t size,
+    std::vector<std::pair<size_t, size_t> >* ranges) const {
+  if (IsRangeAvailable(offset, size))
+    return false;
+
+  ranges->clear();
+  if (chunks_.empty()) {
+    ranges->push_back(std::pair<size_t, size_t>(offset, size));
+    return true;
+  }
+
+  std::map<size_t, size_t>::const_iterator start = chunks_.upper_bound(offset);
+  if (start != chunks_.begin())
+    --start;  // start now points to the key equal or lower than offset.
+  if (start->first + start->second < offset)
+    ++start;  // start element is entirely before current chunk, skip it.
+
+  std::map<size_t, size_t>::const_iterator end =
+      chunks_.upper_bound(offset + size);
+  if (start == end) {  // No data in the current range available.
+    ranges->push_back(std::pair<size_t, size_t>(offset, size));
+    return true;
+  }
+
+  size_t cur_offset = offset;
+  std::map<size_t, size_t>::const_iterator it;
+  for (it = start; it != end; ++it) {
+    if (cur_offset < it->first) {
+      size_t new_size = it->first - cur_offset;
+      ranges->push_back(std::pair<size_t, size_t>(cur_offset, new_size));
+      cur_offset = it->first + it->second;
+    } else if (cur_offset < it->first + it->second) {
+      cur_offset = it->first + it->second;
+    }
+  }
+
+  // Add last chunk.
+  if (cur_offset < offset + size)
+    ranges->push_back(std::pair<size_t, size_t>(cur_offset,
+        offset + size - cur_offset));
+
+  return true;
+}
+
+bool ChunkStream::IsRangeAvailable(size_t offset, size_t size) const {
+  if (chunks_.empty())
+    return false;
+
+  if (SIZE_MAX - size < offset)
+    return false;
+
+  std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
+  if (it == chunks_.begin())
+    return false;  // No chunks includes offset byte.
+
+  --it;  // Now it starts equal or before offset.
+  return (it->first + it->second) >= (offset + size);
+}
+
+size_t ChunkStream::GetFirstMissingByte() const {
+  if (chunks_.empty())
+    return 0;
+  std::map<size_t, size_t>::const_iterator begin = chunks_.begin();
+  return begin->first > 0 ? 0 : begin->second;
+}
+
+size_t ChunkStream::GetFirstMissingByteInInterval(size_t offset) const {
+  if (chunks_.empty())
+    return 0;
+  std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
+  if (it == chunks_.begin())
+    return 0;
+  --it;
+  return it->first + it->second;
+}
+
+size_t ChunkStream::GetLastMissingByteInInterval(size_t offset) const {
+  if (chunks_.empty())
+    return stream_size_ - 1;
+  std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
+  if (it == chunks_.end())
+    return stream_size_ - 1;
+  return it->first - 1;
+}
+
+}  // namespace chrome_pdf
diff --git a/pdf/chunk_stream.h b/pdf/chunk_stream.h
index b8b3c83..d2d8d2a1 100644
--- a/pdf/chunk_stream.h
+++ b/pdf/chunk_stream.h
@@ -6,103 +6,46 @@
 #define PDF_CHUNK_STREAM_H_
 
 #include <stddef.h>
-#include <string.h>
 
-#include <algorithm>
-#include <array>
-#include <memory>
+#include <map>
+#include <utility>
 #include <vector>
 
-#include "pdf/range_set.h"
-
 namespace chrome_pdf {
 
 // This class collects a chunks of data into one data stream. Client can check
 // if data in certain range is available, and get missing chunks of data.
-template <uint32_t N>
 class ChunkStream {
  public:
-  static constexpr uint32_t kChunkSize = N;
-  using ChunkData = typename std::array<unsigned char, N>;
+  ChunkStream();
+  ~ChunkStream();
 
-  ChunkStream() {}
-  ~ChunkStream() {}
+  void Clear();
 
-  void SetChunkData(uint32_t chunk_index, std::unique_ptr<ChunkData> data) {
-    if (!data)
-      return;
-    if (chunk_index >= data_.size()) {
-      data_.resize(chunk_index + 1);
-    }
-    if (!data_[chunk_index]) {
-      ++filled_chunks_count_;
-    }
-    data_[chunk_index] = std::move(data);
-    filled_chunks_.Union(gfx::Range(chunk_index, chunk_index + 1));
-  }
+  void Preallocate(size_t stream_size);
+  size_t GetSize() const;
 
-  bool ReadData(const gfx::Range& range, void* buffer) const {
-    if (!IsRangeAvailable(range)) {
-      return false;
-    }
-    unsigned char* data_buffer = static_cast<unsigned char*>(buffer);
-    uint32_t start = range.start();
-    while (start != range.end()) {
-      const uint32_t chunk_index = GetChunkIndex(start);
-      const uint32_t chunk_start = start % kChunkSize;
-      const uint32_t len =
-          std::min(kChunkSize - chunk_start, range.end() - start);
-      memcpy(data_buffer, data_[chunk_index]->data() + chunk_start, len);
-      data_buffer += len;
-      start += len;
-    }
-    return true;
-  }
+  bool WriteData(size_t offset, void* buffer, size_t size);
+  bool ReadData(size_t offset, size_t size, void* buffer) const;
 
-  uint32_t GetChunkIndex(uint32_t offset) const { return offset / kChunkSize; }
+  // Returns vector of pairs where first is an offset, second is a size.
+  bool GetMissedRanges(size_t offset, size_t size,
+      std::vector<std::pair<size_t, size_t> >* ranges) const;
+  bool IsRangeAvailable(size_t offset, size_t size) const;
+  size_t GetFirstMissingByte() const;
 
-  gfx::Range GetChunksRange(uint32_t offset, uint32_t size) const {
-    return gfx::Range(GetChunkIndex(offset),
-                      GetChunkIndex(offset + size + kChunkSize - 1));
-  }
-
-  bool IsRangeAvailable(const gfx::Range& range) const {
-    if (!range.IsValid() || range.is_reversed() ||
-        (eof_pos_ > 0 && eof_pos_ < range.end()))
-      return false;
-    if (range.is_empty())
-      return true;
-    const gfx::Range chunks_range(GetChunkIndex(range.start()),
-                                  GetChunkIndex(range.end() + kChunkSize - 1));
-    return filled_chunks_.Contains(chunks_range);
-  }
-
-  void set_eof_pos(uint32_t eof_pos) { eof_pos_ = eof_pos; }
-  uint32_t eof_pos() const { return eof_pos_; }
-
-  const RangeSet& filled_chunks() const { return filled_chunks_; }
-
-  bool IsComplete() const {
-    return eof_pos_ > 0 && IsRangeAvailable(gfx::Range(0, eof_pos_));
-  }
-
-  void Clear() {
-    data_.clear();
-    eof_pos_ = 0;
-    filled_chunks_.Clear();
-    filled_chunks_count_ = 0;
-  }
-
-  uint32_t filled_chunks_count() const { return filled_chunks_count_; }
-  uint32_t total_chunks_count() const {
-    return GetChunkIndex(eof_pos_ + kChunkSize - 1);
-  }
+  // Finds the first byte of the missing byte interval that offset belongs to.
+  size_t GetFirstMissingByteInInterval(size_t offset) const;
+  // Returns the last byte of the missing byte interval that offset belongs to.
+  size_t GetLastMissingByteInInterval(size_t offset) const;
 
  private:
-  std::vector<std::unique_ptr<ChunkData>> data_;
-  uint32_t eof_pos_ = 0;
-  RangeSet filled_chunks_;
-  uint32_t filled_chunks_count_ = 0;
+  std::vector<unsigned char> data_;
+
+  // Pair, first - begining of the chunk, second - size of the chunk.
+  std::map<size_t, size_t> chunks_;
+
+  size_t stream_size_;
 };
 
 };  // namespace chrome_pdf
diff --git a/pdf/chunk_stream_unittest.cc b/pdf/chunk_stream_unittest.cc
index f6b3f3c2..af768502 100644
--- a/pdf/chunk_stream_unittest.cc
+++ b/pdf/chunk_stream_unittest.cc
@@ -4,96 +4,14 @@
 
 #include "pdf/chunk_stream.h"
 
-#include <array>
-#include <memory>
-
-#include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome_pdf {
-namespace {
-typedef ChunkStream<10> TestChunkStream;
 
-std::unique_ptr<TestChunkStream::ChunkData> CreateChunkData() {
-  return base::MakeUnique<TestChunkStream::ChunkData>();
-}
+TEST(ChunkStreamTest, Simple) {
+  ChunkStream stream;
+  stream.Preallocate(1000);
+  EXPECT_FALSE(stream.IsRangeAvailable(100, 500));
 }
 
-TEST(ChunkStreamTest, InRow) {
-  TestChunkStream stream;
-  EXPECT_FALSE(stream.IsComplete());
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 10)));
-  stream.SetChunkData(0, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 10)));
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 20)));
-  stream.SetChunkData(1, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 20)));
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 30)));
-  stream.SetChunkData(2, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 30)));
-  stream.set_eof_pos(25);
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 30)));
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 25)));
-  EXPECT_TRUE(stream.IsComplete());
-}
-
-TEST(ChunkStreamTest, InBackRow) {
-  TestChunkStream stream;
-  stream.set_eof_pos(25);
-  EXPECT_FALSE(stream.IsComplete());
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(20, 25)));
-  stream.SetChunkData(2, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(20, 25)));
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(10, 20)));
-  stream.SetChunkData(1, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(10, 20)));
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 10)));
-  stream.SetChunkData(0, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 10)));
-  EXPECT_TRUE(stream.IsComplete());
-}
-
-TEST(ChunkStreamTest, FillGap) {
-  TestChunkStream stream;
-  stream.set_eof_pos(25);
-  EXPECT_FALSE(stream.IsComplete());
-  stream.SetChunkData(0, CreateChunkData());
-  stream.SetChunkData(2, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 10)));
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(20, 25)));
-  EXPECT_FALSE(stream.IsRangeAvailable(gfx::Range(0, 25)));
-  stream.SetChunkData(1, CreateChunkData());
-  EXPECT_TRUE(stream.IsRangeAvailable(gfx::Range(0, 25)));
-  EXPECT_TRUE(stream.IsComplete());
-}
-
-TEST(ChunkStreamTest, Read) {
-  TestChunkStream stream;
-  stream.set_eof_pos(25);
-  const unsigned char start_value = 33;
-  unsigned char value = start_value;
-  auto chunk_0 = CreateChunkData();
-  for (auto& it : *chunk_0) {
-    it = ++value;
-  }
-  auto chunk_1 = CreateChunkData();
-  for (auto& it : *chunk_1) {
-    it = ++value;
-  }
-  auto chunk_2 = CreateChunkData();
-  for (auto& it : *chunk_2) {
-    it = ++value;
-  }
-  stream.SetChunkData(0, std::move(chunk_0));
-  stream.SetChunkData(2, std::move(chunk_2));
-  stream.SetChunkData(1, std::move(chunk_1));
-
-  std::array<unsigned char, 25> result_data;
-  EXPECT_TRUE(stream.ReadData(gfx::Range(0, 25), result_data.data()));
-
-  value = start_value;
-  for (const auto& it : result_data) {
-    EXPECT_EQ(++value, it);
-  }
-}
 }  // namespace chrome_pdf
diff --git a/pdf/document_loader.cc b/pdf/document_loader.cc
index 661e4ca..59c9017f 100644
--- a/pdf/document_loader.cc
+++ b/pdf/document_loader.cc
@@ -7,25 +7,66 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
-
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/numerics/safe_math.h"
 #include "base/strings/string_util.h"
-#include "pdf/url_loader_wrapper.h"
+#include "net/http/http_util.h"
 #include "ppapi/c/pp_errors.h"
-#include "ui/gfx/range/range.h"
+#include "ppapi/cpp/url_loader.h"
+#include "ppapi/cpp/url_request_info.h"
+#include "ppapi/cpp/url_response_info.h"
 
 namespace chrome_pdf {
 
 namespace {
 
-// The distance from last received chunk, when we wait requesting data, using
-// current connection (like playing a cassette tape) and do not send new range
-// request (like rewind a cassette tape, and continue playing after).
-// Experimentally chosen value.
-const int kChunkCloseDistance = 10;
+// If the headers have a byte-range response, writes the start and end
+// positions and returns true if at least the start position was parsed.
+// The end position will be set to 0 if it was not found or parsed from the
+// response.
+// Returns false if not even a start position could be parsed.
+bool GetByteRange(const std::string& headers, uint32_t* start, uint32_t* end) {
+  net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
+  while (it.GetNext()) {
+    if (base::LowerCaseEqualsASCII(it.name(), "content-range")) {
+      std::string range = it.values().c_str();
+      if (base::StartsWith(range, "bytes",
+                           base::CompareCase::INSENSITIVE_ASCII)) {
+        range = range.substr(strlen("bytes"));
+        std::string::size_type pos = range.find('-');
+        std::string range_end;
+        if (pos != std::string::npos)
+          range_end = range.substr(pos + 1);
+        base::TrimWhitespaceASCII(range, base::TRIM_LEADING, &range);
+        base::TrimWhitespaceASCII(range_end, base::TRIM_LEADING, &range_end);
+        *start = atoi(range.c_str());
+        *end = atoi(range_end.c_str());
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// If the headers have a multi-part response, returns the boundary name.
+// Otherwise returns an empty string.
+std::string GetMultiPartBoundary(const std::string& headers) {
+  net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
+  while (it.GetNext()) {
+    if (base::LowerCaseEqualsASCII(it.name(), "content-type")) {
+      std::string type = base::ToLowerASCII(it.values());
+      if (base::StartsWith(type, "multipart/", base::CompareCase::SENSITIVE)) {
+        const char* boundary = strstr(type.c_str(), "boundary=");
+        if (!boundary) {
+          NOTREACHED();
+          break;
+        }
+
+        return std::string(boundary + 9);
+      }
+    }
+  }
+  return std::string();
+}
 
 // Return true if the HTTP response of |loader| is a successful one and loading
 // should continue. 4xx error indicate subsequent requests will fail too.
@@ -33,8 +74,8 @@
 // indicates a redirect was returned which won't be successful because we
 // disable following redirects for PDF loading (we assume they are already
 // resolved by the browser.
-bool ResponseStatusSuccess(const URLLoaderWrapper* loader) {
-  int32_t http_code = loader->GetStatusCode();
+bool ResponseStatusSuccess(const pp::URLLoader& loader) {
+  int32_t http_code = loader.GetResponseInfo().GetStatusCode();
   return (http_code < 400 && http_code != 301) || http_code >= 500;
 }
 
@@ -55,35 +96,48 @@
 DocumentLoader::Client::~Client() {
 }
 
-DocumentLoader::Chunk::Chunk() {}
-
-DocumentLoader::Chunk::~Chunk() {}
-
-void DocumentLoader::Chunk::Clear() {
-  chunk_index = 0;
-  data_size = 0;
-  chunk_data.reset();
-}
-
 DocumentLoader::DocumentLoader(Client* client)
-    : client_(client), loader_factory_(this) {}
+    : client_(client), partial_document_(false), request_pending_(false),
+      current_pos_(0), current_chunk_size_(0), current_chunk_read_(0),
+      document_size_(0), header_request_(true), is_multipart_(false) {
+  loader_factory_.Initialize(this);
+}
 
 DocumentLoader::~DocumentLoader() {
 }
 
-bool DocumentLoader::Init(std::unique_ptr<URLLoaderWrapper> loader,
-                          const std::string& url) {
+bool DocumentLoader::Init(const pp::URLLoader& loader,
+                          const std::string& url,
+                          const std::string& headers) {
   DCHECK(url_.empty());
-  DCHECK(!loader_);
 
   // Check that the initial response status is a valid one.
-  if (!ResponseStatusSuccess(loader.get()))
+  if (!ResponseStatusSuccess(loader))
     return false;
 
-  std::string type = loader->GetContentType();
+  url_ = url;
+  loader_ = loader;
+
+  std::string response_headers;
+  if (!headers.empty()) {
+    response_headers = headers;
+  } else {
+    pp::URLResponseInfo response = loader_.GetResponseInfo();
+    pp::Var headers_var = response.GetHeaders();
+
+    if (headers_var.is_string()) {
+      response_headers = headers_var.AsString();
+    }
+  }
+
+  bool accept_ranges_bytes = false;
+  bool content_encoded = false;
+  uint32_t content_length = 0;
+  std::string type;
+  std::string disposition;
 
   // This happens for PDFs not loaded from http(s) sources.
-  if (type == "text/plain") {
+  if (response_headers == "Content-Type: text/plain") {
     if (!base::StartsWith(url, "http://",
                           base::CompareCase::INSENSITIVE_ASCII) &&
         !base::StartsWith(url, "https://",
@@ -91,85 +145,109 @@
       type = "application/pdf";
     }
   }
+  if (type.empty() && !response_headers.empty()) {
+    net::HttpUtil::HeadersIterator it(response_headers.begin(),
+                                      response_headers.end(), "\n");
+    while (it.GetNext()) {
+      if (base::LowerCaseEqualsASCII(it.name(), "content-length")) {
+        content_length = atoi(it.values().c_str());
+      } else if (base::LowerCaseEqualsASCII(it.name(), "accept-ranges")) {
+        accept_ranges_bytes = base::LowerCaseEqualsASCII(it.values(), "bytes");
+      } else if (base::LowerCaseEqualsASCII(it.name(), "content-encoding")) {
+        content_encoded = true;
+      } else if (base::LowerCaseEqualsASCII(it.name(), "content-type")) {
+        type = it.values();
+        size_t semi_colon_pos = type.find(';');
+        if (semi_colon_pos != std::string::npos) {
+          type = type.substr(0, semi_colon_pos);
+        }
+        TrimWhitespaceASCII(type, base::TRIM_ALL, &type);
+      } else if (base::LowerCaseEqualsASCII(it.name(), "content-disposition")) {
+        disposition = it.values();
+      }
+    }
+  }
   if (!type.empty() && !IsValidContentType(type))
     return false;
-
-  if (base::StartsWith(loader->GetContentDisposition(), "attachment",
+  if (base::StartsWith(disposition, "attachment",
                        base::CompareCase::INSENSITIVE_ASCII))
     return false;
 
-  url_ = url;
-  loader_ = std::move(loader);
+  if (content_length > 0)
+    chunk_stream_.Preallocate(content_length);
 
-  if (!loader_->IsContentEncoded()) {
-    chunk_stream_.set_eof_pos(std::max(0, loader_->GetContentLength()));
+  document_size_ = content_length;
+  requests_count_ = 0;
+
+  // Enable partial loading only if file size is above the threshold.
+  // It will allow avoiding latency for multiple requests.
+  if (content_length > kMinFileSize &&
+      accept_ranges_bytes &&
+      !content_encoded) {
+    LoadPartialDocument();
+  } else {
+    LoadFullDocument();
   }
-  int64_t bytes_received = 0;
-  int64_t total_bytes_to_be_received = 0;
-  if (!chunk_stream_.eof_pos() &&
-      loader_->GetDownloadProgress(&bytes_received,
-                                   &total_bytes_to_be_received)) {
-    chunk_stream_.set_eof_pos(
-        std::max(0, static_cast<int>(total_bytes_to_be_received)));
-  }
-
-  SetPartialLoadingEnabled(
-      partial_loading_enabled_ &&
-      !base::StartsWith(url, "file://", base::CompareCase::INSENSITIVE_ASCII) &&
-      loader_->IsAcceptRangesBytes() && !loader_->IsContentEncoded() &&
-      GetDocumentSize());
-
-  ReadMore();
   return true;
 }
 
-bool DocumentLoader::IsDocumentComplete() const {
-  return chunk_stream_.IsComplete();
+void DocumentLoader::LoadPartialDocument() {
+  // The current request is a full request (not a range request) so it starts at
+  // 0 and ends at |document_size_|.
+  current_chunk_size_ = document_size_;
+  current_pos_ = 0;
+  current_request_offset_ = 0;
+  current_request_size_ = 0;
+  current_request_extended_size_ = document_size_;
+  request_pending_ = true;
+
+  partial_document_ = true;
+  header_request_ = true;
+  ReadMore();
 }
 
-uint32_t DocumentLoader::GetDocumentSize() const {
-  return chunk_stream_.eof_pos();
+void DocumentLoader::LoadFullDocument() {
+  partial_document_ = false;
+  chunk_buffer_.clear();
+  ReadMore();
+}
+
+bool DocumentLoader::IsDocumentComplete() const {
+  if (document_size_ == 0)  // Document size unknown.
+    return false;
+  return IsDataAvailable(0, document_size_);
+}
+
+uint32_t DocumentLoader::GetAvailableData() const {
+  if (document_size_ == 0) {  // If document size is unknown.
+    return current_pos_;
+  }
+
+  std::vector<std::pair<size_t, size_t> > ranges;
+  chunk_stream_.GetMissedRanges(0, document_size_, &ranges);
+  uint32_t available = document_size_;
+  for (const auto& range : ranges)
+    available -= range.second;
+  return available;
 }
 
 void DocumentLoader::ClearPendingRequests() {
-  pending_requests_.Clear();
+  pending_requests_.erase(pending_requests_.begin(),
+                          pending_requests_.end());
 }
 
 bool DocumentLoader::GetBlock(uint32_t position,
                               uint32_t size,
                               void* buf) const {
-  base::CheckedNumeric<uint32_t> addition_result = position;
-  addition_result += size;
-  if (!addition_result.IsValid())
-    return false;
-  return chunk_stream_.ReadData(
-      gfx::Range(position, addition_result.ValueOrDie()), buf);
+  return chunk_stream_.ReadData(position, size, buf);
 }
 
 bool DocumentLoader::IsDataAvailable(uint32_t position, uint32_t size) const {
-  base::CheckedNumeric<uint32_t> addition_result = position;
-  addition_result += size;
-  if (!addition_result.IsValid())
-    return false;
-  return chunk_stream_.IsRangeAvailable(
-      gfx::Range(position, addition_result.ValueOrDie()));
+  return chunk_stream_.IsRangeAvailable(position, size);
 }
 
 void DocumentLoader::RequestData(uint32_t position, uint32_t size) {
-  if (!size || IsDataAvailable(position, size)) {
-    return;
-  }
-  {
-    // Check integer overflow.
-    base::CheckedNumeric<uint32_t> addition_result = position;
-    addition_result += size;
-    if (!addition_result.IsValid())
-      return;
-  }
-
-  if (GetDocumentSize() && (position + size > GetDocumentSize())) {
-    return;
-  }
+  DCHECK(partial_document_);
 
   // We have some artefact request from
   // PDFiumEngine::OnDocumentComplete() -> FPDFAvail_IsPageAvail after
@@ -178,225 +256,304 @@
   // Bug: http://code.google.com/p/chromium/issues/detail?id=79996
   // Test url:
   // http://www.icann.org/en/correspondence/holtzman-to-jeffrey-02mar11-en.pdf
-  if (!loader_)
+  if (IsDocumentComplete())
     return;
 
-  RangeSet requested_chunks(chunk_stream_.GetChunksRange(position, size));
-  requested_chunks.Subtract(chunk_stream_.filled_chunks());
-  if (requested_chunks.IsEmpty()) {
-    NOTREACHED();
+  pending_requests_.push_back(std::pair<size_t, size_t>(position, size));
+  DownloadPendingRequests();
+}
+
+void DocumentLoader::RemoveCompletedRanges() {
+  // Split every request that has been partially downloaded already into smaller
+  // requests.
+  std::vector<std::pair<size_t, size_t> > ranges;
+  auto it = pending_requests_.begin();
+  while (it != pending_requests_.end()) {
+    chunk_stream_.GetMissedRanges(it->first, it->second, &ranges);
+    pending_requests_.insert(it, ranges.begin(), ranges.end());
+    ranges.clear();
+    pending_requests_.erase(it++);
+  }
+}
+
+void DocumentLoader::DownloadPendingRequests() {
+  if (request_pending_)
     return;
-  }
-  pending_requests_.Union(requested_chunks);
-}
 
-void DocumentLoader::SetPartialLoadingEnabled(bool enabled) {
-  partial_loading_enabled_ = enabled;
-  if (!enabled) {
-    is_partial_loader_active_ = false;
-  }
-}
+  uint32_t pos;
+  uint32_t size;
+  if (pending_requests_.empty()) {
+    // If the document is not complete and we have no outstanding requests,
+    // download what's left for as long as no other request gets added to
+    // |pending_requests_|.
+    pos = chunk_stream_.GetFirstMissingByte();
+    if (pos >= document_size_) {
+      // We're done downloading the document.
+      return;
+    }
+    // Start with size 0, we'll set |current_request_extended_size_| to > 0.
+    // This way this request will get cancelled as soon as the renderer wants
+    // another portion of the document.
+    size = 0;
+  } else {
+    RemoveCompletedRanges();
 
-bool DocumentLoader::ShouldCancelLoading() const {
-  if (!loader_)
-    return true;
-  if (!partial_loading_enabled_ || pending_requests_.IsEmpty())
-    return false;
-  const gfx::Range current_range(chunk_.chunk_index,
-                                 chunk_.chunk_index + kChunkCloseDistance);
-  return !pending_requests_.Intersects(current_range);
-}
-
-void DocumentLoader::ContinueDownload() {
-  if (!ShouldCancelLoading())
-    return ReadMore();
-  DCHECK(partial_loading_enabled_);
-  DCHECK(!IsDocumentComplete());
-  DCHECK(GetDocumentSize());
-
-  const uint32_t range_start =
-      pending_requests_.IsEmpty() ? 0 : pending_requests_.First().start();
-  RangeSet candidates_for_request(
-      gfx::Range(range_start, chunk_stream_.total_chunks_count()));
-  candidates_for_request.Subtract(chunk_stream_.filled_chunks());
-  DCHECK(!candidates_for_request.IsEmpty());
-  gfx::Range next_request = candidates_for_request.First();
-  if (candidates_for_request.Size() == 1 &&
-      next_request.length() < kChunkCloseDistance) {
-    // We have only request at the end, try to enlarge it to improve back order
-    // reading.
-    const int additional_chunks_count =
-        kChunkCloseDistance - next_request.length();
-    int new_start = std::max(
-        0, static_cast<int>(next_request.start()) - additional_chunks_count);
-    candidates_for_request =
-        RangeSet(gfx::Range(new_start, next_request.end()));
-    candidates_for_request.Subtract(chunk_stream_.filled_chunks());
-    next_request = candidates_for_request.Last();
+    pos = pending_requests_.front().first;
+    size = pending_requests_.front().second;
+    if (IsDataAvailable(pos, size)) {
+      ReadComplete();
+      return;
+    }
   }
 
-  loader_.reset();
-  chunk_.Clear();
-  if (!is_partial_loader_active_) {
-    client_->CancelBrowserDownload();
-    is_partial_loader_active_ = true;
+  size_t last_byte_before = chunk_stream_.GetFirstMissingByteInInterval(pos);
+  if (size < kDefaultRequestSize) {
+    // Try to extend before pos, up to size |kDefaultRequestSize|.
+    if (pos + size - last_byte_before > kDefaultRequestSize) {
+      pos += size - kDefaultRequestSize;
+      size = kDefaultRequestSize;
+    } else {
+      size += pos - last_byte_before;
+      pos = last_byte_before;
+    }
+  }
+  if (pos - last_byte_before < kDefaultRequestSize) {
+    // Don't leave a gap smaller than |kDefaultRequestSize|.
+    size += pos - last_byte_before;
+    pos = last_byte_before;
   }
 
-  const uint32_t start = next_request.start() * DataStream::kChunkSize;
-  const uint32_t length =
-      std::min(chunk_stream_.eof_pos() - start,
-               next_request.length() * DataStream::kChunkSize);
+  current_request_offset_ = pos;
+  current_request_size_ = size;
 
+  // Extend the request until the next downloaded byte or the end of the
+  // document.
+  size_t last_missing_byte =
+      chunk_stream_.GetLastMissingByteInInterval(pos + size - 1);
+  current_request_extended_size_ = last_missing_byte - pos + 1;
+
+  request_pending_ = true;
+
+  // Start downloading first pending request.
+  loader_.Close();
   loader_ = client_->CreateURLLoader();
-
-  loader_->OpenRange(
-      url_, url_, start, length,
-      loader_factory_.NewCallback(&DocumentLoader::DidOpenPartial));
+  pp::CompletionCallback callback =
+      loader_factory_.NewCallback(&DocumentLoader::DidOpen);
+  pp::URLRequestInfo request = GetRequest(pos, current_request_extended_size_);
+  requests_count_++;
+  int rv = loader_.Open(request, callback);
+  if (rv != PP_OK_COMPLETIONPENDING)
+    callback.Run(rv);
 }
 
-void DocumentLoader::DidOpenPartial(int32_t result) {
+pp::URLRequestInfo DocumentLoader::GetRequest(uint32_t position,
+                                              uint32_t size) const {
+  pp::URLRequestInfo request(client_->GetPluginInstance());
+  request.SetURL(url_);
+  request.SetMethod("GET");
+  request.SetFollowRedirects(false);
+  request.SetCustomReferrerURL(url_);
+
+  const size_t kBufSize = 100;
+  char buf[kBufSize];
+  // According to rfc2616, byte range specifies position of the first and last
+  // bytes in the requested range inclusively. Therefore we should subtract 1
+  // from the position + size, to get index of the last byte that needs to be
+  // downloaded.
+  base::snprintf(buf, kBufSize, "Range: bytes=%d-%d", position,
+                 position + size - 1);
+  pp::Var header(buf);
+  request.SetHeaders(header);
+
+  return request;
+}
+
+void DocumentLoader::DidOpen(int32_t result) {
   if (result != PP_OK) {
-    return ReadComplete();
+    NOTREACHED();
+    client_->OnDocumentFailed();
+    return;
   }
 
-  if (!ResponseStatusSuccess(loader_.get()))
-    return ReadComplete();
+  if (!ResponseStatusSuccess(loader_)) {
+    client_->OnDocumentFailed();
+    return;
+  }
 
-  // Leave position untouched for multiparted responce for now, when we read the
-  // data we'll get it.
-  if (!loader_->IsMultipart()) {
+  is_multipart_ = false;
+  current_chunk_size_ = 0;
+  current_chunk_read_ = 0;
+
+  pp::Var headers_var = loader_.GetResponseInfo().GetHeaders();
+  std::string headers;
+  if (headers_var.is_string())
+    headers = headers_var.AsString();
+
+  std::string boundary = GetMultiPartBoundary(headers);
+  if (!boundary.empty()) {
+    // Leave position untouched for now, when we read the data we'll get it.
+    is_multipart_ = true;
+    multipart_boundary_ = boundary;
+  } else {
     // Need to make sure that the server returned a byte-range, since it's
     // possible for a server to just ignore our byte-range request and just
     // return the entire document even if it supports byte-range requests.
     // i.e. sniff response to
     // http://www.act.org/compass/sample/pdf/geometry.pdf
-    int start_pos = 0;
-    int end_pos = 0;
-    if (loader_->GetByteRange(&start_pos, &end_pos)) {
-      if (start_pos % DataStream::kChunkSize != 0) {
-        return ReadComplete();
-      }
-      DCHECK(!chunk_.chunk_data);
-      chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
+    current_pos_ = 0;
+    uint32_t start_pos, end_pos;
+    if (GetByteRange(headers, &start_pos, &end_pos)) {
+      current_pos_ = start_pos;
+      if (end_pos && end_pos > start_pos)
+        current_chunk_size_ = end_pos - start_pos + 1;
     } else {
-      SetPartialLoadingEnabled(false);
+      partial_document_ = false;
     }
-    return ContinueDownload();
   }
-  // Needs more data to calc chunk index.
-  return ReadMore();
+
+  ReadMore();
 }
 
 void DocumentLoader::ReadMore() {
-  loader_->ReadResponseBody(
-      buffer_, sizeof(buffer_),
-      loader_factory_.NewCallback(&DocumentLoader::DidRead));
+  pp::CompletionCallback callback =
+        loader_factory_.NewCallback(&DocumentLoader::DidRead);
+  int rv = loader_.ReadResponseBody(buffer_, sizeof(buffer_), callback);
+  if (rv != PP_OK_COMPLETIONPENDING)
+    callback.Run(rv);
 }
 
 void DocumentLoader::DidRead(int32_t result) {
-  if (result < 0) {
-    // An error occurred.
-    // The renderer will detect that we're missing data and will display a
-    // message.
-    return ReadComplete();
+  if (result <= 0) {
+    // If |result| == PP_OK, the document was loaded, otherwise an error was
+    // encountered. Either way we want to stop processing the response. In the
+    // case where an error occurred, the renderer will detect that we're missing
+    // data and will display a message.
+    ReadComplete();
+    return;
   }
-  if (result == 0) {
-    loader_.reset();
-    if (!is_partial_loader_active_)
-      return ReadComplete();
-    return ContinueDownload();
-  }
-  if (loader_->IsMultipart()) {
-    int start_pos = 0;
-    int end_pos = 0;
-    if (!loader_->GetByteRange(&start_pos, &end_pos)) {
-      return ReadComplete();
+
+  char* start = buffer_;
+  size_t length = result;
+  if (is_multipart_ && result > 2) {
+    for (int i = 2; i < result; ++i) {
+      if ((buffer_[i - 1] == '\n' && buffer_[i - 2] == '\n') ||
+          (i >= 4 && buffer_[i - 1] == '\n' && buffer_[i - 2] == '\r' &&
+           buffer_[i - 3] == '\n' && buffer_[i - 4] == '\r')) {
+        uint32_t start_pos, end_pos;
+        if (GetByteRange(std::string(buffer_, i), &start_pos, &end_pos)) {
+          current_pos_ = start_pos;
+          start += i;
+          length -= i;
+          if (end_pos && end_pos > start_pos)
+            current_chunk_size_ = end_pos - start_pos + 1;
+        }
+        break;
+      }
     }
-    DCHECK(!chunk_.chunk_data);
-    chunk_.chunk_index = chunk_stream_.GetChunkIndex(start_pos);
+
+    // Reset this flag so we don't look inside the buffer in future calls of
+    // DidRead for this response.  Note that this code DOES NOT handle multi-
+    // part responses with more than one part (we don't issue them at the
+    // moment, so they shouldn't arrive).
+    is_multipart_ = false;
   }
-  if (!SaveChunkData(buffer_, result)) {
-    return ReadMore();
+
+  if (current_chunk_size_ && current_chunk_read_ + length > current_chunk_size_)
+    length = current_chunk_size_ - current_chunk_read_;
+
+  if (length) {
+    if (document_size_ > 0) {
+      chunk_stream_.WriteData(current_pos_, start, length);
+    } else {
+      // If we did not get content-length in the response, we can't
+      // preallocate buffer for the entire document. Resizing array causing
+      // memory fragmentation issues on the large files and OOM exceptions.
+      // To fix this, we collect all chunks of the file to the list and
+      // concatenate them together after request is complete.
+      std::vector<unsigned char> buf(length);
+      memcpy(buf.data(), start, length);
+      chunk_buffer_.push_back(std::move(buf));
+    }
+    current_pos_ += length;
+    current_chunk_read_ += length;
+    client_->OnNewDataAvailable();
   }
-  if (IsDocumentComplete()) {
-    return ReadComplete();
+
+  // Only call the renderer if we allow partial loading.
+  if (!partial_document_) {
+    ReadMore();
+    return;
   }
-  return ContinueDownload();
+
+  UpdateRendering();
+  RemoveCompletedRanges();
+
+  if (!pending_requests_.empty()) {
+    // If there are pending requests and the current content we're downloading
+    // doesn't satisfy any of these requests, cancel the current request to
+    // fullfill those more important requests.
+    bool satisfying_pending_request =
+        SatisfyingRequest(current_request_offset_, current_request_size_);
+    for (const auto& pending_request : pending_requests_) {
+      if (SatisfyingRequest(pending_request.first, pending_request.second)) {
+        satisfying_pending_request = true;
+        break;
+      }
+    }
+    // Cancel the request as it's not satisfying any request from the
+    // renderer, unless the current request is finished in which case we let
+    // it finish cleanly.
+    if (!satisfying_pending_request &&
+        current_pos_ <
+            current_request_offset_ + current_request_extended_size_) {
+      loader_.Close();
+    }
+  }
+
+  ReadMore();
 }
 
-bool DocumentLoader::SaveChunkData(char* input, uint32_t input_size) {
-  count_of_bytes_received_ += input_size;
-  bool chunk_saved = false;
-  bool loading_pending_request = pending_requests_.Contains(chunk_.chunk_index);
-  while (input_size > 0) {
-    if (chunk_.data_size == 0) {
-      chunk_.chunk_data = base::MakeUnique<DataStream::ChunkData>();
-    }
-    const uint32_t new_chunk_data_len =
-        std::min(DataStream::kChunkSize - chunk_.data_size, input_size);
-    memcpy(chunk_.chunk_data->data() + chunk_.data_size, input,
-           new_chunk_data_len);
-    chunk_.data_size += new_chunk_data_len;
-    if (chunk_.data_size == DataStream::kChunkSize ||
-        chunk_stream_.eof_pos() ==
-            chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size) {
-      chunk_stream_.SetChunkData(chunk_.chunk_index,
-                                 std::move(chunk_.chunk_data));
-      pending_requests_.Subtract(
-          gfx::Range(chunk_.chunk_index, chunk_.chunk_index + 1));
-      chunk_.data_size = 0;
-      ++(chunk_.chunk_index);
-      chunk_saved = true;
-    }
-
-    input += new_chunk_data_len;
-    input_size -= new_chunk_data_len;
-  }
-
-  if (IsDocumentComplete())
-    return true;
-
-  if (!chunk_saved)
-    return false;
-
-  if (loading_pending_request &&
-      !pending_requests_.Contains(chunk_.chunk_index)) {
-    client_->OnPendingRequestComplete();
-  }
-  client_->OnNewDataAvailable();
-  return true;
+bool DocumentLoader::SatisfyingRequest(size_t offset, size_t size) const {
+  return offset <= current_pos_ + kDefaultRequestSize &&
+      current_pos_ < offset + size;
 }
 
 void DocumentLoader::ReadComplete() {
-  if (!GetDocumentSize()) {
-    uint32_t eof =
-        chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size;
-    if (!chunk_stream_.filled_chunks().IsEmpty()) {
-      eof = std::max(
-          chunk_stream_.filled_chunks().Last().end() * DataStream::kChunkSize,
-          eof);
+  if (!partial_document_) {
+    if (document_size_ == 0) {
+      // For the document with no 'content-length" specified we've collected all
+      // the chunks already. Let's allocate final document buffer and copy them
+      // over.
+      chunk_stream_.Preallocate(current_pos_);
+      uint32_t pos = 0;
+      for (auto& chunk : chunk_buffer_) {
+        chunk_stream_.WriteData(pos, chunk.data(), chunk.size());
+        pos += chunk.size();
+      }
+      chunk_buffer_.clear();
     }
-    chunk_stream_.set_eof_pos(eof);
-    if (eof == chunk_.chunk_index * DataStream::kChunkSize + chunk_.data_size) {
-      chunk_stream_.SetChunkData(chunk_.chunk_index,
-                                 std::move(chunk_.chunk_data));
-    }
+    document_size_ = current_pos_;
+    client_->OnDocumentComplete();
+    return;
   }
-  loader_.reset();
+
+  request_pending_ = false;
+
   if (IsDocumentComplete()) {
     client_->OnDocumentComplete();
-  } else {
-    client_->OnDocumentCanceled();
+    return;
   }
+
+  UpdateRendering();
+  DownloadPendingRequests();
 }
 
-float DocumentLoader::GetProgress() const {
-  if (!GetDocumentSize())
-    return -1;
-  if (IsDocumentComplete())
-    return 1;
-  return static_cast<float>(chunk_stream_.filled_chunks_count()) /
-         chunk_stream_.total_chunks_count();
+void DocumentLoader::UpdateRendering() {
+  if (header_request_)
+    client_->OnPartialDocumentLoaded();
+  else
+    client_->OnPendingRequestComplete();
+  header_request_ = false;
 }
 
 }  // namespace chrome_pdf
diff --git a/pdf/document_loader.h b/pdf/document_loader.h
index 54bce41..95457fc 100644
--- a/pdf/document_loader.h
+++ b/pdf/document_loader.h
@@ -9,23 +9,18 @@
 #include <stdint.h>
 
 #include <list>
-#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include "pdf/chunk_stream.h"
+#include "ppapi/cpp/url_loader.h"
 #include "ppapi/utility/completion_callback_factory.h"
 
 namespace chrome_pdf {
 
-class URLLoaderWrapper;
-
 class DocumentLoader {
  public:
-  // Number was chosen in crbug.com/78264#c8
-  static constexpr uint32_t kDefaultRequestSize = 65536;
-
   class Client {
    public:
     virtual ~Client();
@@ -33,23 +28,27 @@
     // Gets the pp::Instance object.
     virtual pp::Instance* GetPluginInstance() = 0;
     // Creates new URLLoader based on client settings.
-    virtual std::unique_ptr<URLLoaderWrapper> CreateURLLoader() = 0;
+    virtual pp::URLLoader CreateURLLoader() = 0;
+    // Notification called when partial information about document is available.
+    // Only called for urls that returns full content size and supports byte
+    // range requests.
+    virtual void OnPartialDocumentLoaded() = 0;
     // Notification called when all outstanding pending requests are complete.
     virtual void OnPendingRequestComplete() = 0;
     // Notification called when new data is available.
     virtual void OnNewDataAvailable() = 0;
+    // Notification called if document failed to load.
+    virtual void OnDocumentFailed() = 0;
     // Notification called when document is fully loaded.
     virtual void OnDocumentComplete() = 0;
-    // Notification called when document loading is canceled.
-    virtual void OnDocumentCanceled() = 0;
-    // Called when initial loader was closed.
-    virtual void CancelBrowserDownload() = 0;
   };
 
   explicit DocumentLoader(Client* client);
   ~DocumentLoader();
 
-  bool Init(std::unique_ptr<URLLoaderWrapper> loader, const std::string& url);
+  bool Init(const pp::URLLoader& loader,
+            const std::string& url,
+            const std::string& headers);
 
   // Data access interface. Return true is successful.
   bool GetBlock(uint32_t position, uint32_t size, void* buf) const;
@@ -61,60 +60,77 @@
   void RequestData(uint32_t position, uint32_t size);
 
   bool IsDocumentComplete() const;
-  uint32_t GetDocumentSize() const;
-  uint32_t count_of_bytes_received() const { return count_of_bytes_received_; }
-  float GetProgress() const;
+  uint32_t document_size() const { return document_size_; }
+
+  // Return number of bytes available.
+  uint32_t GetAvailableData() const;
 
   // Clear pending requests from the queue.
   void ClearPendingRequests();
 
-  void SetPartialLoadingEnabled(bool enabled);
-
-  bool is_partial_loader_active() const { return is_partial_loader_active_; }
+  bool is_partial_document() const { return partial_document_; }
 
  private:
-  using DataStream = ChunkStream<kDefaultRequestSize>;
-  struct Chunk {
-    Chunk();
-    ~Chunk();
-
-    void Clear();
-
-    uint32_t chunk_index = 0;
-    uint32_t data_size = 0;
-    std::unique_ptr<DataStream::ChunkData> chunk_data;
-  };
-
   // Called by the completion callback of the document's URLLoader.
-  void DidOpenPartial(int32_t result);
+  void DidOpen(int32_t result);
   // Call to read data from the document's URLLoader.
   void ReadMore();
   // Called by the completion callback of the document's URLLoader.
   void DidRead(int32_t result);
 
-  bool ShouldCancelLoading() const;
-  void ContinueDownload();
-  // Called when we complete server request.
+  // Called when we detect that partial document load is possible.
+  void LoadPartialDocument();
+  // Called when we have to load full document.
+  void LoadFullDocument();
+  // Download pending requests.
+  void DownloadPendingRequests();
+  // Remove completed ranges.
+  void RemoveCompletedRanges();
+  // Returns true if we are already in progress satisfying the request, or just
+  // about ready to start. This helps us avoid expensive jumping around, and
+  // even worse leaving tiny gaps in the byte stream that might have to be
+  // filled later.
+  bool SatisfyingRequest(size_t pos, size_t size) const;
+  // Called when we complete server request and read all data from it.
   void ReadComplete();
+  // Creates request to download size byte of data data starting from position.
+  pp::URLRequestInfo GetRequest(uint32_t position, uint32_t size) const;
+  // Updates the rendering by the Client.
+  void UpdateRendering();
 
-  bool SaveChunkData(char* input, uint32_t input_size);
+  // Document below size will be downloaded in one chunk.
+  static const uint32_t kMinFileSize = 64 * 1024;
+  // Number was chosen in crbug.com/78264#c8
+  enum { kDefaultRequestSize = 65536 };
 
   Client* const client_;
   std::string url_;
-  std::unique_ptr<URLLoaderWrapper> loader_;
-
+  pp::URLLoader loader_;
   pp::CompletionCallbackFactory<DocumentLoader> loader_factory_;
-
-  DataStream chunk_stream_;
-  bool partial_loading_enabled_ = true;
-  bool is_partial_loader_active_ = false;
-
-  static constexpr uint32_t kReadBufferSize = 256 * 1024;
-  char buffer_[kReadBufferSize];
-
-  Chunk chunk_;
-  RangeSet pending_requests_;
-  uint32_t count_of_bytes_received_ = 0;
+  ChunkStream chunk_stream_;
+  bool partial_document_;
+  bool request_pending_;
+  typedef std::list<std::pair<size_t, size_t> > PendingRequests;
+  PendingRequests pending_requests_;
+  // The starting position of the HTTP request currently being processed.
+  size_t current_request_offset_;
+  // The size of the byte range the current HTTP request must download before
+  // being cancelled.
+  size_t current_request_size_;
+  // The actual byte range size of the current HTTP request. This may be larger
+  // than |current_request_size_| and the request may be cancelled before
+  // reaching |current_request_offset_| + |current_request_extended_size_|.
+  size_t current_request_extended_size_;
+  char buffer_[kDefaultRequestSize];
+  uint32_t current_pos_;
+  uint32_t current_chunk_size_;
+  uint32_t current_chunk_read_;
+  uint32_t document_size_;
+  bool header_request_;
+  bool is_multipart_;
+  std::string multipart_boundary_;
+  uint32_t requests_count_;
+  std::vector<std::vector<unsigned char> > chunk_buffer_;
 };
 
 }  // namespace chrome_pdf
diff --git a/pdf/document_loader_unittest.cc b/pdf/document_loader_unittest.cc
deleted file mode 100644
index 5d3df8d..0000000
--- a/pdf/document_loader_unittest.cc
+++ /dev/null
@@ -1,1179 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "pdf/document_loader.h"
-
-#include <memory>
-#include <string>
-
-#include "base/logging.h"
-#include "pdf/url_loader_wrapper.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/range/range.h"
-
-using ::testing::_;
-using ::testing::Mock;
-using ::testing::Sequence;
-using ::testing::NiceMock;
-using ::testing::Return;
-
-namespace chrome_pdf {
-
-namespace {
-
-class TestURLLoader : public URLLoaderWrapper {
- public:
-  class LoaderData {
-   public:
-    LoaderData() {}
-    ~LoaderData() {
-      // We should call callbacks to prevent memory leaks.
-      // The callbacks don't do anything, because the objects that created the
-      // callbacks have been destroyed.
-      if (IsWaitRead())
-        CallReadCallback(-1);
-      if (IsWaitOpen())
-        CallOpenCallback(-1);
-    }
-
-    int content_length() const { return content_length_; }
-    void set_content_length(int content_length) {
-      content_length_ = content_length;
-    }
-    bool accept_ranges_bytes() const { return accept_ranges_bytes_; }
-    void set_accept_ranges_bytes(bool accept_ranges_bytes) {
-      accept_ranges_bytes_ = accept_ranges_bytes;
-    }
-    bool content_encoded() const { return content_encoded_; }
-    void set_content_encoded(bool content_encoded) {
-      content_encoded_ = content_encoded;
-    }
-    const std::string& content_type() const { return content_type_; }
-    void set_content_type(const std::string& content_type) {
-      content_type_ = content_type;
-    }
-    const std::string& content_disposition() const {
-      return content_disposition_;
-    }
-    void set_content_disposition(const std::string& content_disposition) {
-      content_disposition_ = content_disposition;
-    }
-    const std::string& multipart_boundary() const {
-      return multipart_boundary_;
-    }
-    void set_multipart_boundary(const std::string& multipart_boundary) {
-      multipart_boundary_ = multipart_boundary;
-    }
-    const gfx::Range& byte_range() const { return byte_range_; }
-    void set_byte_range(const gfx::Range& byte_range) {
-      byte_range_ = byte_range;
-    }
-    bool is_multipart() const { return is_multipart_; }
-    void set_is_multipart(bool is_multipart) { is_multipart_ = is_multipart; }
-    int status_code() const { return status_code_; }
-    void set_status_code(int status_code) { status_code_ = status_code; }
-    bool closed() const { return closed_; }
-    void set_closed(bool closed) { closed_ = closed; }
-    const gfx::Range& open_byte_range() const { return open_byte_range_; }
-    void set_open_byte_range(const gfx::Range& open_byte_range) {
-      open_byte_range_ = open_byte_range;
-    }
-
-    bool IsWaitRead() const { return !did_read_callback_.IsOptional(); }
-    bool IsWaitOpen() const { return !did_open_callback_.IsOptional(); }
-    char* buffer() const { return buffer_; }
-    int buffer_size() const { return buffer_size_; }
-
-    void SetReadCallback(const pp::CompletionCallback& read_callback,
-                         char* buffer,
-                         int buffer_size) {
-      did_read_callback_ = read_callback;
-      buffer_ = buffer;
-      buffer_size_ = buffer_size;
-    }
-
-    void SetOpenCallback(const pp::CompletionCallback& open_callback,
-                         gfx::Range req_byte_range) {
-      did_open_callback_ = open_callback;
-      set_open_byte_range(req_byte_range);
-    }
-
-    void CallOpenCallback(int result) {
-      DCHECK(IsWaitOpen());
-      did_open_callback_.RunAndClear(result);
-    }
-
-    void CallReadCallback(int result) {
-      DCHECK(IsWaitRead());
-      did_read_callback_.RunAndClear(result);
-    }
-
-   private:
-    pp::CompletionCallback did_open_callback_;
-    pp::CompletionCallback did_read_callback_;
-    char* buffer_ = nullptr;
-    int buffer_size_ = 0;
-
-    int content_length_ = -1;
-    bool accept_ranges_bytes_ = false;
-    bool content_encoded_ = false;
-    std::string content_type_;
-    std::string content_disposition_;
-    std::string multipart_boundary_;
-    gfx::Range byte_range_ = gfx::Range::InvalidRange();
-    bool is_multipart_ = false;
-    int status_code_ = 0;
-    bool closed_ = true;
-    gfx::Range open_byte_range_ = gfx::Range::InvalidRange();
-
-    DISALLOW_COPY_AND_ASSIGN(LoaderData);
-  };
-
-  explicit TestURLLoader(LoaderData* data) : data_(data) {
-    data_->set_closed(false);
-  }
-
-  ~TestURLLoader() override { Close(); }
-
-  int GetContentLength() const override { return data_->content_length(); }
-
-  bool IsAcceptRangesBytes() const override {
-    return data_->accept_ranges_bytes();
-  }
-
-  bool IsContentEncoded() const override { return data_->content_encoded(); }
-
-  std::string GetContentType() const override { return data_->content_type(); }
-
-  std::string GetContentDisposition() const override {
-    return data_->content_disposition();
-  }
-
-  int GetStatusCode() const override { return data_->status_code(); }
-
-  bool IsMultipart() const override { return data_->is_multipart(); }
-
-  bool GetByteRange(int* start, int* end) const override {
-    *start = data_->byte_range().start();
-    *end = data_->byte_range().end();
-    return data_->byte_range().IsValid();
-  }
-
-  void Close() override { data_->set_closed(true); }
-
-  void OpenRange(const std::string& url,
-                 const std::string& referrer_url,
-                 uint32_t position,
-                 uint32_t size,
-                 const pp::CompletionCallback& cc) override {
-    data_->SetOpenCallback(cc, gfx::Range(position, position + size));
-  }
-
-  void ReadResponseBody(char* buffer,
-                        int buffer_size,
-                        const pp::CompletionCallback& cc) override {
-    data_->SetReadCallback(cc, buffer, buffer_size);
-  }
-
-  bool GetDownloadProgress(int64_t* bytes_received,
-                           int64_t* total_bytes_to_be_received) const override {
-    return false;
-  }
-
- private:
-  LoaderData* data_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestURLLoader);
-};
-
-class TestClient : public DocumentLoader::Client {
- public:
-  TestClient() { full_page_loader_data()->set_content_type("application/pdf"); }
-  ~TestClient() override {}
-
-  // DocumentLoader::Client overrides:
-  pp::Instance* GetPluginInstance() override { return nullptr; }
-  std::unique_ptr<URLLoaderWrapper> CreateURLLoader() override {
-    return std::unique_ptr<URLLoaderWrapper>(
-        new TestURLLoader(partial_loader_data()));
-  }
-  void OnPendingRequestComplete() override {}
-  void OnNewDataAvailable() override {}
-  void OnDocumentComplete() override {}
-  void OnDocumentCanceled() override {}
-  void CancelBrowserDownload() override {}
-
-  std::unique_ptr<URLLoaderWrapper> CreateFullPageLoader() {
-    return std::unique_ptr<URLLoaderWrapper>(
-        new TestURLLoader(full_page_loader_data()));
-  }
-
-  TestURLLoader::LoaderData* full_page_loader_data() {
-    return &full_page_loader_data_;
-  }
-  TestURLLoader::LoaderData* partial_loader_data() {
-    return &partial_loader_data_;
-  }
-
-  void SetCanUsePartialLoading() {
-    full_page_loader_data()->set_content_length(10 * 1024 * 1024);
-    full_page_loader_data()->set_content_encoded(false);
-    full_page_loader_data()->set_accept_ranges_bytes(true);
-  }
-
-  void SendAllPartialData() {
-    partial_loader_data_.set_byte_range(partial_loader_data_.open_byte_range());
-    partial_loader_data_.CallOpenCallback(0);
-    uint32_t length = partial_loader_data_.byte_range().length();
-    while (length > 0) {
-      const uint32_t max_part_len = DocumentLoader::kDefaultRequestSize;
-      const uint32_t part_len = std::min(length, max_part_len);
-      partial_loader_data_.CallReadCallback(part_len);
-      length -= part_len;
-    }
-    if (partial_loader_data_.IsWaitRead()) {
-      partial_loader_data_.CallReadCallback(0);
-    }
-  }
-
- private:
-  TestURLLoader::LoaderData full_page_loader_data_;
-  TestURLLoader::LoaderData partial_loader_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestClient);
-};
-
-class MockClient : public TestClient {
- public:
-  MockClient() {}
-
-  MOCK_METHOD0(OnPendingRequestComplete, void());
-  MOCK_METHOD0(OnNewDataAvailable, void());
-  MOCK_METHOD0(OnDocumentComplete, void());
-  MOCK_METHOD0(OnDocumentCanceled, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockClient);
-};
-
-}  // namespace
-
-using DocumentLoaderTest = ::testing::Test;
-
-TEST_F(DocumentLoaderTest, PartialLoadingEnabled) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(1000000, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingDisabledOnSmallFiles) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 2);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(1000000, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingDisabledIfContentEncoded) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_encoded(true);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(1000000, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingDisabledNoAcceptRangeBytes) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_accept_ranges_bytes(false);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(1000000, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingReallyDisabledRequestFromBegin) {
-  TestClient client;
-  DocumentLoader loader(&client);
-  client.SetCanUsePartialLoading();
-  loader.SetPartialLoadingEnabled(false);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  // We should not start partial loading if requested data is beside full page
-  // loading position.
-  loader.RequestData(DocumentLoader::kDefaultRequestSize, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingReallyDisabledRequestFromMiddle) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.SetPartialLoadingEnabled(false);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(1000000, 1);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingSimple) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  // While we have no requests, we should not start partial loading.
-  EXPECT_FALSE(loader.is_partial_loader_active());
-
-  loader.RequestData(5000000, 1);
-
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_FALSE(loader.is_partial_loader_active());
-
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  // Partial loader should request headers.
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_TRUE(loader.is_partial_loader_active());
-  // Loader should be stopped.
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-
-  EXPECT_EQ("{4980736,10485760}",
-            client.partial_loader_data()->open_byte_range().ToString());
-}
-
-TEST_F(DocumentLoaderTest, PartialLoadingBackOrder) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  // While we have no requests, we should not start partial loading.
-  EXPECT_FALSE(loader.is_partial_loader_active());
-
-  loader.RequestData(client.full_page_loader_data()->content_length() - 1, 1);
-
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_FALSE(loader.is_partial_loader_active());
-
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  // Partial loader should request headers.
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_TRUE(loader.is_partial_loader_active());
-  // Loader should be stopped.
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-
-  // Requested range should be enlarged.
-  EXPECT_GT(client.partial_loader_data()->open_byte_range().length(), 1u);
-  EXPECT_EQ("{9830400,10485760}",
-            client.partial_loader_data()->open_byte_range().ToString());
-}
-
-TEST_F(DocumentLoaderTest, CompleteWithoutPartial) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_FALSE(client.full_page_loader_data()->closed());
-  while (client.full_page_loader_data()->IsWaitRead()) {
-    client.full_page_loader_data()->CallReadCallback(1000);
-  }
-  EXPECT_TRUE(loader.IsDocumentComplete());
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, ErrorDownloadFullDocument) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
-  EXPECT_FALSE(client.full_page_loader_data()->closed());
-  client.full_page_loader_data()->CallReadCallback(-3);
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-  EXPECT_FALSE(loader.IsDocumentComplete());
-}
-
-TEST_F(DocumentLoaderTest, CompleteNoContentLength) {
-  TestClient client;
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_FALSE(client.full_page_loader_data()->closed());
-  for (int i = 0; i < 10; ++i) {
-    EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
-    client.full_page_loader_data()->CallReadCallback(1000);
-  }
-  EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
-  client.full_page_loader_data()->CallReadCallback(0);
-  EXPECT_EQ(10000ul, loader.GetDocumentSize());
-  EXPECT_TRUE(loader.IsDocumentComplete());
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, CompleteWithPartial) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(19 * DocumentLoader::kDefaultRequestSize,
-                     DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(client.full_page_loader_data()->closed());
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitRead());
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-  EXPECT_FALSE(client.partial_loader_data()->closed());
-
-  client.SendAllPartialData();
-  // Now we should send other document data.
-  client.SendAllPartialData();
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, PartialRequestLastChunk) {
-  const uint32_t kLastChunkSize = 300;
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20 + kLastChunkSize);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(20 * DocumentLoader::kDefaultRequestSize, 1);
-
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_EQ(
-      static_cast<int>(client.partial_loader_data()->open_byte_range().end()),
-      client.full_page_loader_data()->content_length());
-  client.partial_loader_data()->set_byte_range(
-      client.partial_loader_data()->open_byte_range());
-  client.partial_loader_data()->CallOpenCallback(0);
-  uint32_t data_length = client.partial_loader_data()->byte_range().length();
-  while (data_length > DocumentLoader::kDefaultRequestSize) {
-    client.partial_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-    data_length -= DocumentLoader::kDefaultRequestSize;
-  }
-  EXPECT_EQ(kLastChunkSize, data_length);
-  client.partial_loader_data()->CallReadCallback(kLastChunkSize);
-  EXPECT_TRUE(loader.IsDataAvailable(DocumentLoader::kDefaultRequestSize * 20,
-                                     kLastChunkSize));
-}
-
-TEST_F(DocumentLoaderTest, DocumentSize) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(123456789);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_EQ(static_cast<int>(loader.GetDocumentSize()),
-            client.full_page_loader_data()->content_length());
-}
-
-TEST_F(DocumentLoaderTest, DocumentSizeNoContentLength) {
-  TestClient client;
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_EQ(0ul, loader.GetDocumentSize());
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  client.full_page_loader_data()->CallReadCallback(1000);
-  client.full_page_loader_data()->CallReadCallback(500);
-  client.full_page_loader_data()->CallReadCallback(0);
-  EXPECT_EQ(DocumentLoader::kDefaultRequestSize + 1000ul + 500ul,
-            loader.GetDocumentSize());
-  EXPECT_TRUE(loader.IsDocumentComplete());
-}
-
-TEST_F(DocumentLoaderTest, ClearPendingRequests) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 100 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 100, 10);
-  loader.ClearPendingRequests();
-  loader.RequestData(15 * DocumentLoader::kDefaultRequestSize + 200, 20);
-  // pending requests are accumulating, and will be processed after initial data
-  // load.
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  {
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    const gfx::Range range_requested(15 * DocumentLoader::kDefaultRequestSize,
-                                     16 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  // clear requests before Open callback.
-  loader.ClearPendingRequests();
-  // Current request should continue loading.
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->CallOpenCallback(0);
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(client.partial_loader_data()->closed());
-  // Current request should continue loading, because no other request queued.
-
-  loader.RequestData(18 * DocumentLoader::kDefaultRequestSize + 200, 20);
-  // Requests queue is processed only on receiving data.
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  // New request within close distance from the one currently loading. Loading
-  // isn't restarted.
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-
-  loader.ClearPendingRequests();
-  // request again two.
-  loader.RequestData(60 * DocumentLoader::kDefaultRequestSize + 100, 10);
-  loader.RequestData(35 * DocumentLoader::kDefaultRequestSize + 200, 20);
-  // Requests queue is processed only on receiving data.
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  {
-    // new requset not with in close distance from current loading.
-    // Loading should be restarted.
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    // The first requested chunk should be processed.
-    const gfx::Range range_requested(35 * DocumentLoader::kDefaultRequestSize,
-                                     36 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->CallOpenCallback(0);
-  // Override pending requests.
-  loader.ClearPendingRequests();
-  loader.RequestData(70 * DocumentLoader::kDefaultRequestSize + 100, 10);
-
-  // Requests queue is processed only on receiving data.
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  {
-    // New requset not with in close distance from current loading.
-    // Loading should be restarted .
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    // The first requested chunk should be processed.
-    const gfx::Range range_requested(70 * DocumentLoader::kDefaultRequestSize,
-                                     71 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-}
-
-TEST_F(DocumentLoaderTest, GetBlock) {
-  std::vector<char> buffer(DocumentLoader::kDefaultRequestSize);
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_FALSE(loader.GetBlock(0, 1000, buffer.data()));
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(loader.GetBlock(0, 1000, buffer.data()));
-  EXPECT_FALSE(loader.GetBlock(DocumentLoader::kDefaultRequestSize, 1500,
-                               buffer.data()));
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(loader.GetBlock(DocumentLoader::kDefaultRequestSize, 1500,
-                              buffer.data()));
-
-  EXPECT_FALSE(loader.GetBlock(17 * DocumentLoader::kDefaultRequestSize, 3000,
-                               buffer.data()));
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 100, 10);
-  EXPECT_FALSE(loader.GetBlock(17 * DocumentLoader::kDefaultRequestSize, 3000,
-                               buffer.data()));
-
-  // Requests queue is processed only on receiving data.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  client.SendAllPartialData();
-  EXPECT_TRUE(loader.GetBlock(17 * DocumentLoader::kDefaultRequestSize, 3000,
-                              buffer.data()));
-}
-
-TEST_F(DocumentLoaderTest, IsDataAvailable) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_FALSE(loader.IsDataAvailable(0, 1000));
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(loader.IsDataAvailable(0, 1000));
-  EXPECT_FALSE(
-      loader.IsDataAvailable(DocumentLoader::kDefaultRequestSize, 1500));
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(
-      loader.IsDataAvailable(DocumentLoader::kDefaultRequestSize, 1500));
-
-  EXPECT_FALSE(
-      loader.IsDataAvailable(17 * DocumentLoader::kDefaultRequestSize, 3000));
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 100, 10);
-  EXPECT_FALSE(
-      loader.IsDataAvailable(17 * DocumentLoader::kDefaultRequestSize, 3000));
-
-  // Requests queue is processed only on receiving data.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  client.SendAllPartialData();
-  EXPECT_TRUE(
-      loader.IsDataAvailable(17 * DocumentLoader::kDefaultRequestSize, 3000));
-}
-
-TEST_F(DocumentLoaderTest, RequestData) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 100 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(37 * DocumentLoader::kDefaultRequestSize + 200, 10);
-  loader.RequestData(25 * DocumentLoader::kDefaultRequestSize + 600, 100);
-  loader.RequestData(13 * DocumentLoader::kDefaultRequestSize + 900, 500);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  {
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    const gfx::Range range_requested(13 * DocumentLoader::kDefaultRequestSize,
-                                     14 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  client.partial_loader_data()->CallOpenCallback(0);
-  // Override pending requests.
-  loader.ClearPendingRequests();
-  loader.RequestData(38 * DocumentLoader::kDefaultRequestSize + 200, 10);
-  loader.RequestData(26 * DocumentLoader::kDefaultRequestSize + 600, 100);
-  // Requests queue is processed only on receiving data.
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  {
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    const gfx::Range range_requested(26 * DocumentLoader::kDefaultRequestSize,
-                                     27 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  client.partial_loader_data()->CallOpenCallback(0);
-  // Override pending requests.
-  loader.ClearPendingRequests();
-  loader.RequestData(39 * DocumentLoader::kDefaultRequestSize + 200, 10);
-  // Requests queue is processed only on receiving data.
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  {
-    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-    const gfx::Range range_requested(39 * DocumentLoader::kDefaultRequestSize,
-                                     40 * DocumentLoader::kDefaultRequestSize);
-    EXPECT_EQ(range_requested.start(),
-              client.partial_loader_data()->open_byte_range().start());
-    EXPECT_LE(range_requested.end(),
-              client.partial_loader_data()->open_byte_range().end());
-    client.partial_loader_data()->set_byte_range(
-        client.partial_loader_data()->open_byte_range());
-  }
-  // Fill all gaps.
-  while (!loader.IsDocumentComplete()) {
-    client.SendAllPartialData();
-  }
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, DoNotLoadAvailablePartialData) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  // Send more data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  loader.RequestData(2 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send more data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  // Partial loading should not have started for already available data.
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, DoNotLoadDataAfterComplete) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  for (int i = 0; i < 20; ++i) {
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-
-  EXPECT_TRUE(loader.IsDocumentComplete());
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-  EXPECT_TRUE(client.full_page_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, DoNotLoadPartialDataAboveDocumentSize) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(20 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, MergePendingRequests) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 50 + 58383);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-  loader.RequestData(16 * DocumentLoader::kDefaultRequestSize + 600, 100);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  const gfx::Range range_requested(16 * DocumentLoader::kDefaultRequestSize,
-                                   18 * DocumentLoader::kDefaultRequestSize);
-  EXPECT_EQ(range_requested.start(),
-            client.partial_loader_data()->open_byte_range().start());
-  EXPECT_LE(range_requested.end(),
-            client.partial_loader_data()->open_byte_range().end());
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-
-  // Fill all gaps.
-  while (!loader.IsDocumentComplete()) {
-    client.SendAllPartialData();
-  }
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, PartialStopOnStatusCodeError) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->set_status_code(404);
-  client.partial_loader_data()->CallOpenCallback(0);
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest,
-       PartialAsFullDocumentLoadingRangeRequestNoRangeField) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->set_byte_range(gfx::Range::InvalidRange());
-  client.partial_loader_data()->CallOpenCallback(0);
-  EXPECT_FALSE(client.partial_loader_data()->closed());
-  // Partial loader is used to load the whole page, like full page loader.
-  EXPECT_FALSE(loader.is_partial_loader_active());
-}
-
-TEST_F(DocumentLoaderTest, PartialMultiPart) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->set_is_multipart(true);
-  client.partial_loader_data()->CallOpenCallback(0);
-  client.partial_loader_data()->set_byte_range(
-      gfx::Range(17 * DocumentLoader::kDefaultRequestSize,
-                 18 * DocumentLoader::kDefaultRequestSize));
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_TRUE(loader.IsDataAvailable(17 * DocumentLoader::kDefaultRequestSize,
-                                     DocumentLoader::kDefaultRequestSize));
-}
-
-TEST_F(DocumentLoaderTest, PartialMultiPartRangeError) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->set_is_multipart(true);
-  client.partial_loader_data()->CallOpenCallback(0);
-  client.partial_loader_data()->set_byte_range(gfx::Range::InvalidRange());
-  client.partial_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  EXPECT_FALSE(loader.IsDataAvailable(17 * DocumentLoader::kDefaultRequestSize,
-                                      DocumentLoader::kDefaultRequestSize));
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, PartialConnectionErrorOnOpen) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->CallOpenCallback(-3);
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-
-  // Partial loading should not restart after any error.
-  loader.RequestData(18 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, PartialConnectionErrorOnRead) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(17 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  // Send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
-  client.partial_loader_data()->set_byte_range(
-      gfx::Range(17 * DocumentLoader::kDefaultRequestSize,
-                 18 * DocumentLoader::kDefaultRequestSize));
-  client.partial_loader_data()->CallOpenCallback(0);
-  EXPECT_TRUE(client.partial_loader_data()->IsWaitRead());
-  client.partial_loader_data()->CallReadCallback(-3);
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-
-  // Partial loading should not restart after any error.
-  loader.RequestData(18 * DocumentLoader::kDefaultRequestSize + 200, 10);
-
-  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
-  EXPECT_TRUE(client.partial_loader_data()->closed());
-}
-
-TEST_F(DocumentLoaderTest, GetProgress) {
-  TestClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_EQ(0., loader.GetProgress());
-
-  for (int i = 0; i < 20; ++i) {
-    EXPECT_EQ(i * 100 / 20, static_cast<int>(loader.GetProgress() * 100));
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-  EXPECT_EQ(1., loader.GetProgress());
-}
-
-TEST_F(DocumentLoaderTest, GetProgressNoContentLength) {
-  TestClient client;
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-  EXPECT_EQ(-1., loader.GetProgress());
-
-  for (int i = 0; i < 20; ++i) {
-    EXPECT_EQ(-1., loader.GetProgress());
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-  client.full_page_loader_data()->CallReadCallback(0);
-  EXPECT_EQ(1., loader.GetProgress());
-}
-
-TEST_F(DocumentLoaderTest, ClientCompleteCallbacks) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
-  for (int i = 0; i < 19; ++i) {
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnDocumentComplete()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, ClientCompleteCallbacksNoContentLength) {
-  MockClient client;
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
-  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
-  for (int i = 0; i < 20; ++i) {
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
-  EXPECT_CALL(client, OnDocumentComplete()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(0);
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, ClientCancelCallback) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
-  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
-  for (int i = 0; i < 10; ++i) {
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
-  EXPECT_CALL(client, OnDocumentCanceled()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(-3);
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, NewDataAvailable) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  client.full_page_loader_data()->set_content_length(
-      DocumentLoader::kDefaultRequestSize * 20);
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnNewDataAvailable()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnNewDataAvailable()).Times(0);
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize - 100);
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnNewDataAvailable()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(100);
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, ClientPendingRequestCompleteFullLoader) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  loader.RequestData(1000, 4000);
-
-  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, ClientPendingRequestCompletePartialLoader) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
-  loader.RequestData(15 * DocumentLoader::kDefaultRequestSize + 4000, 4000);
-
-  // Always send initial data from FullPageLoader.
-  client.full_page_loader_data()->CallReadCallback(
-      DocumentLoader::kDefaultRequestSize);
-
-  client.SendAllPartialData();
-  Mock::VerifyAndClear(&client);
-}
-
-TEST_F(DocumentLoaderTest, ClientPendingRequestCompletePartialAndFullLoader) {
-  MockClient client;
-  client.SetCanUsePartialLoading();
-  DocumentLoader loader(&client);
-  loader.Init(client.CreateFullPageLoader(), "http://url.com");
-
-  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
-  loader.RequestData(16 * DocumentLoader::kDefaultRequestSize + 4000, 4000);
-  loader.RequestData(4 * DocumentLoader::kDefaultRequestSize + 4000, 4000);
-
-  for (int i = 0; i < 5; ++i) {
-    client.full_page_loader_data()->CallReadCallback(
-        DocumentLoader::kDefaultRequestSize);
-  }
-
-  Mock::VerifyAndClear(&client);
-
-  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
-  client.SendAllPartialData();
-  Mock::VerifyAndClear(&client);
-}
-
-}  // namespace chrome_pdf
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 727e671..bc0fb5b 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -428,7 +428,13 @@
       return;
     }
 
-    if (pinch_phase == PINCH_UPDATE_ZOOM_IN) {
+    // When zooming in, we set a layer transform to avoid unneeded rerasters.
+    // Also, if we're zooming out and the last time we rerastered was when
+    // we were even further zoomed out (i.e. we pinch zoomed in and are now
+    // pinch zooming back out in the same gesture), we update the layer
+    // transform instead of rerastering.
+    if (pinch_phase == PINCH_UPDATE_ZOOM_IN ||
+        (pinch_phase == PINCH_UPDATE_ZOOM_OUT && zoom_ratio > 1.0)) {
       if (!(dict.Get(pp::Var(kJSPinchX)).is_number() &&
             dict.Get(pp::Var(kJSPinchY)).is_number() &&
             dict.Get(pp::Var(kJSPinchVectorX)).is_number() &&
@@ -943,8 +949,24 @@
 }
 
 void OutOfProcessInstance::DidOpen(int32_t result) {
-  if (result != PP_OK || !engine_->HandleDocumentLoad(embed_loader_))
+  if (result == PP_OK) {
+    if (!engine_->HandleDocumentLoad(embed_loader_)) {
+      document_load_state_ = LOAD_STATE_LOADING;
+      DocumentLoadFailed();
+    }
+  } else if (result != PP_ERROR_ABORTED) { // Can happen in tests.
+    NOTREACHED();
     DocumentLoadFailed();
+  }
+
+  // If it's a progressive load, cancel the stream URL request so that requests
+  // can be made on the original URL.
+  // TODO(raymes): Make this clearer once the in-process plugin is deleted.
+  if (engine_->IsProgressiveLoad()) {
+    pp::VarDictionary message;
+    message.Set(kType, kJSCancelStreamUrlType);
+    PostMessage(message);
+  }
 }
 
 void OutOfProcessInstance::DidOpenPreview(int32_t result) {
@@ -1574,12 +1596,6 @@
   return background_color_;
 }
 
-void OutOfProcessInstance::CancelBrowserDownload() {
-  pp::VarDictionary message;
-  message.Set(kType, kJSCancelStreamUrlType);
-  PostMessage(message);
-}
-
 void OutOfProcessInstance::IsSelectingChanged(bool is_selecting) {
   pp::VarDictionary message;
   message.Set(kType, kJSSetIsSelectingType);
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 023b43f2..d4c9c97 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -135,7 +135,6 @@
   void FormTextFieldFocusChange(bool in_focus) override;
   bool IsPrintPreview() override;
   uint32_t GetBackgroundColor() override;
-  void CancelBrowserDownload() override;
   void IsSelectingChanged(bool is_selecting) override;
 
   // PreviewModeClient::Client implementation.
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 4bb62b3..54e9f2f 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -42,8 +42,6 @@
 
 namespace chrome_pdf {
 
-class Stream;
-
 // Do one time initialization of the SDK.
 bool InitializeSDK();
 // Tells the SDK that we're shutting down.
@@ -186,9 +184,6 @@
     // Get the background color of the PDF.
     virtual uint32_t GetBackgroundColor() = 0;
 
-    // Cancel browser initiated document download.
-    virtual void CancelBrowserDownload() = 0;
-
     // Sets selection status.
     virtual void IsSelectingChanged(bool is_selecting) {}
   };
@@ -301,6 +296,8 @@
   virtual void SetScrollPosition(const pp::Point& position) = 0;
 #endif
 
+  virtual bool IsProgressiveLoad() = 0;
+
   virtual std::string GetMetadata(const std::string& key) = 0;
 };
 
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index f0ba089..35c2896 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -32,7 +32,6 @@
 #include "pdf/pdfium/pdfium_api_string_buffer_adapter.h"
 #include "pdf/pdfium/pdfium_mem_buffer_file_read.h"
 #include "pdf/pdfium/pdfium_mem_buffer_file_write.h"
-#include "pdf/url_loader_wrapper_impl.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/pp_input_event.h"
 #include "ppapi/c/ppb_core.h"
@@ -669,6 +668,7 @@
     : client_(client),
       current_zoom_(1.0),
       current_rotation_(0),
+      doc_loader_(this),
       password_tries_remaining_(0),
       doc_(nullptr),
       form_(nullptr),
@@ -695,15 +695,15 @@
 
   file_access_.m_FileLen = 0;
   file_access_.m_GetBlock = &GetBlock;
-  file_access_.m_Param = this;
+  file_access_.m_Param = &doc_loader_;
 
   file_availability_.version = 1;
   file_availability_.IsDataAvail = &IsDataAvail;
-  file_availability_.engine = this;
+  file_availability_.loader = &doc_loader_;
 
   download_hints_.version = 1;
   download_hints_.AddSegment = &AddSegment;
-  download_hints_.engine = this;
+  download_hints_.loader = &doc_loader_;
 
   // Initialize FPDF_FORMFILLINFO member variables.  Deriving from this struct
   // allows the static callbacks to be able to cast the FPDF_FORMFILLINFO in
@@ -973,22 +973,22 @@
 
 int PDFiumEngine::GetBlock(void* param, unsigned long position,
                            unsigned char* buffer, unsigned long size) {
-  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
-  return engine->doc_loader_->GetBlock(position, size, buffer);
+  DocumentLoader* loader = static_cast<DocumentLoader*>(param);
+  return loader->GetBlock(position, size, buffer);
 }
 
 FPDF_BOOL PDFiumEngine::IsDataAvail(FX_FILEAVAIL* param,
                                     size_t offset, size_t size) {
   PDFiumEngine::FileAvail* file_avail =
       static_cast<PDFiumEngine::FileAvail*>(param);
-  return file_avail->engine->doc_loader_->IsDataAvailable(offset, size);
+  return file_avail->loader->IsDataAvailable(offset, size);
 }
 
 void PDFiumEngine::AddSegment(FX_DOWNLOADHINTS* param,
                               size_t offset, size_t size) {
   PDFiumEngine::DownloadHints* download_hints =
       static_cast<PDFiumEngine::DownloadHints*>(param);
-  return download_hints->engine->doc_loader_->RequestData(offset, size);
+  return download_hints->loader->RequestData(offset, size);
 }
 
 bool PDFiumEngine::New(const char* url,
@@ -1123,27 +1123,15 @@
 
 bool PDFiumEngine::HandleDocumentLoad(const pp::URLLoader& loader) {
   password_tries_remaining_ = kMaxPasswordTries;
-  process_when_pending_request_complete_ = true;
-  auto loader_wrapper =
-      base::MakeUnique<URLLoaderWrapperImpl>(GetPluginInstance(), loader);
-  loader_wrapper->SetResponseHeaders(headers_);
-
-  doc_loader_ = base::MakeUnique<DocumentLoader>(this);
-  if (doc_loader_->Init(std::move(loader_wrapper), url_)) {
-    // request initial data.
-    doc_loader_->RequestData(0, 1);
-    return true;
-  }
-  return false;
+  return doc_loader_.Init(loader, url_, headers_);
 }
 
 pp::Instance* PDFiumEngine::GetPluginInstance() {
   return client_->GetPluginInstance();
 }
 
-std::unique_ptr<URLLoaderWrapper> PDFiumEngine::CreateURLLoader() {
-  return base::MakeUnique<URLLoaderWrapperImpl>(GetPluginInstance(),
-                                                client_->CreateURLLoader());
+pp::URLLoader PDFiumEngine::CreateURLLoader() {
+  return client_->CreateURLLoader();
 }
 
 void PDFiumEngine::AppendPage(PDFEngine* engine, int index) {
@@ -1168,32 +1156,35 @@
 }
 #endif
 
+bool PDFiumEngine::IsProgressiveLoad() {
+  return doc_loader_.is_partial_document();
+}
+
 std::string PDFiumEngine::GetMetadata(const std::string& key) {
   return GetDocumentMetadata(doc(), key);
 }
 
-void PDFiumEngine::OnPendingRequestComplete() {
-  if (!process_when_pending_request_complete_)
-    return;
+void PDFiumEngine::OnPartialDocumentLoaded() {
+  file_access_.m_FileLen = doc_loader_.document_size();
   if (!fpdf_availability_) {
-    file_access_.m_FileLen = doc_loader_->GetDocumentSize();
     fpdf_availability_ = FPDFAvail_Create(&file_availability_, &file_access_);
     DCHECK(fpdf_availability_);
-    // Currently engine does not deal efficiently with some non-linearized
-    // files.
-    // See http://code.google.com/p/chromium/issues/detail?id=59400
-    // To improve user experience we download entire file for non-linearized
-    // PDF.
-    if (FPDFAvail_IsLinearized(fpdf_availability_) != PDF_LINEARIZED) {
-      // Wait complete document.
-      process_when_pending_request_complete_ = false;
-      FPDFAvail_Destroy(fpdf_availability_);
-      fpdf_availability_ = nullptr;
-      return;
-    }
   }
 
-  if (!doc_) {
+  // Currently engine does not deal efficiently with some non-linearized files.
+  // See http://code.google.com/p/chromium/issues/detail?id=59400
+  // To improve user experience we download entire file for non-linearized PDF.
+  if (!FPDFAvail_IsLinearized(fpdf_availability_)) {
+    doc_loader_.RequestData(0, doc_loader_.document_size());
+    return;
+  }
+
+  LoadDocument();
+}
+
+void PDFiumEngine::OnPendingRequestComplete() {
+  if (!doc_ || !form_) {
+    DCHECK(fpdf_availability_);
     LoadDocument();
     return;
   }
@@ -1215,57 +1206,30 @@
 }
 
 void PDFiumEngine::OnNewDataAvailable() {
-  if (!doc_loader_->GetDocumentSize()) {
-    client_->DocumentLoadProgress(doc_loader_->count_of_bytes_received(), 0);
-    return;
-  }
+  client_->DocumentLoadProgress(doc_loader_.GetAvailableData(),
+                                doc_loader_.document_size());
+}
 
-  const float progress = doc_loader_->GetProgress();
-  DCHECK_GE(progress, 0.0);
-  DCHECK_LE(progress, 1.0);
-  client_->DocumentLoadProgress(progress * 10000, 10000);
+void PDFiumEngine::OnDocumentFailed() {
+  client_->DocumentLoadFailed();
 }
 
 void PDFiumEngine::OnDocumentComplete() {
-  if (doc_) {
-    return FinishLoadingDocument();
+  if (!doc_ || !form_) {
+    file_access_.m_FileLen = doc_loader_.document_size();
+    if (!fpdf_availability_) {
+      fpdf_availability_ = FPDFAvail_Create(&file_availability_, &file_access_);
+      DCHECK(fpdf_availability_);
+    }
+    LoadDocument();
+    return;
   }
-  file_access_.m_FileLen = doc_loader_->GetDocumentSize();
-  if (!fpdf_availability_) {
-    fpdf_availability_ = FPDFAvail_Create(&file_availability_, &file_access_);
-    DCHECK(fpdf_availability_);
-  }
-  LoadDocument();
-}
 
-void PDFiumEngine::OnDocumentCanceled() {
-  if (visible_pages_.empty())
-    client_->DocumentLoadFailed();
-  else
-    OnDocumentComplete();
-}
-
-void PDFiumEngine::CancelBrowserDownload() {
-  client_->CancelBrowserDownload();
+  FinishLoadingDocument();
 }
 
 void PDFiumEngine::FinishLoadingDocument() {
-  DCHECK(doc_loader_->IsDocumentComplete() && doc_);
-
-  if (!form_) {
-    int form_status =
-        FPDFAvail_IsFormAvail(fpdf_availability_, &download_hints_);
-    if (form_status != PDF_FORM_NOTAVAIL) {
-      form_ = FPDFDOC_InitFormFillEnvironment(
-          doc_, static_cast<FPDF_FORMFILLINFO*>(this));
-#if defined(PDF_ENABLE_XFA)
-      FPDF_LoadXFA(doc_);
-#endif
-
-      FPDF_SetFormFieldHighlightColor(form_, 0, kFormHighlightColor);
-      FPDF_SetFormFieldHighlightAlpha(form_, kFormHighlightAlpha);
-    }
-  }
+  DCHECK(doc_loader_.IsDocumentComplete() && doc_);
 
   bool need_update = false;
   for (size_t i = 0; i < pages_.size(); ++i) {
@@ -1477,7 +1441,7 @@
     return pp::Buffer_Dev();
 
   // If document is not downloaded yet, disable printing.
-  if (doc_ && !doc_loader_->IsDocumentComplete())
+  if (doc_ && !doc_loader_.IsDocumentComplete())
     return pp::Buffer_Dev();
 
   FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
@@ -2620,7 +2584,7 @@
 void PDFiumEngine::LoadDocument() {
   // Check if the document is ready for loading. If it isn't just bail for now,
   // we will call LoadDocument() again later.
-  if (!doc_ && !doc_loader_->IsDocumentComplete() &&
+  if (!doc_ && !doc_loader_.IsDocumentComplete() &&
       !FPDFAvail_IsDocAvail(fpdf_availability_, &download_hints_)) {
     return;
   }
@@ -2659,12 +2623,13 @@
     password_cstr = password.c_str();
     password_tries_remaining_--;
   }
-  if (doc_loader_->IsDocumentComplete() &&
+  if (doc_loader_.IsDocumentComplete() &&
       !FPDFAvail_IsLinearized(fpdf_availability_)) {
     doc_ = FPDF_LoadCustomDocument(&file_access_, password_cstr);
   } else {
     doc_ = FPDFAvail_GetDocument(fpdf_availability_, password_cstr);
   }
+
   if (!doc_) {
     if (FPDF_GetLastError() == FPDF_ERR_PASSWORD)
       *needs_password = true;
@@ -2705,7 +2670,6 @@
     GetPasswordAndLoad();
     return;
   }
-
   if (!doc_) {
     client_->DocumentLoadFailed();
     return;
@@ -2717,7 +2681,26 @@
   permissions_ = FPDF_GetDocPermissions(doc_);
   permissions_handler_revision_ = FPDF_GetSecurityHandlerRevision(doc_);
 
-  if (!doc_loader_->IsDocumentComplete()) {
+  if (!form_) {
+    int form_status =
+        FPDFAvail_IsFormAvail(fpdf_availability_, &download_hints_);
+    bool doc_complete = doc_loader_.IsDocumentComplete();
+    // Try again if the data is not available and the document hasn't finished
+    // downloading.
+    if (form_status == PDF_FORM_NOTAVAIL && !doc_complete)
+      return;
+
+    form_ = FPDFDOC_InitFormFillEnvironment(
+        doc_, static_cast<FPDF_FORMFILLINFO*>(this));
+#if defined(PDF_ENABLE_XFA)
+    FPDF_LoadXFA(doc_);
+#endif
+
+    FPDF_SetFormFieldHighlightColor(form_, 0, kFormHighlightColor);
+    FPDF_SetFormFieldHighlightAlpha(form_, kFormHighlightAlpha);
+  }
+
+  if (!doc_loader_.IsDocumentComplete()) {
     // Check if the first page is available.  In a linearized PDF, that is not
     // always page 0.  Doing this gives us the default page size, since when the
     // document is available, the first page is available as well.
@@ -2726,19 +2709,17 @@
 
   LoadPageInfo(false);
 
-  if (doc_loader_->IsDocumentComplete())
+  if (doc_loader_.IsDocumentComplete())
     FinishLoadingDocument();
 }
 
 void PDFiumEngine::LoadPageInfo(bool reload) {
-  if (!doc_loader_)
-    return;
   pending_pages_.clear();
   pp::Size old_document_size = document_size_;
   document_size_ = pp::Size();
   std::vector<pp::Rect> page_rects;
   int page_count = FPDF_GetPageCount(doc_);
-  bool doc_complete = doc_loader_->IsDocumentComplete();
+  bool doc_complete = doc_loader_.IsDocumentComplete();
   bool is_linear = FPDFAvail_IsLinearized(fpdf_availability_) == PDF_LINEARIZED;
   for (int i = 0; i < page_count; ++i) {
     if (i != 0) {
@@ -2795,12 +2776,10 @@
 }
 
 void PDFiumEngine::CalculateVisiblePages() {
-  if (!doc_loader_)
-    return;
   // Clear pending requests queue, since it may contain requests to the pages
   // that are already invisible (after scrolling for example).
   pending_pages_.clear();
-  doc_loader_->ClearPendingRequests();
+  doc_loader_.ClearPendingRequests();
 
   visible_pages_.clear();
   pp::Rect visible_rect(plugin_size_);
@@ -2861,7 +2840,7 @@
 }
 
 bool PDFiumEngine::CheckPageAvailable(int index, std::vector<int>* pending) {
-  if (!doc_)
+  if (!doc_ || !form_)
     return false;
 
   const int num_pages = static_cast<int>(pages_.size());
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index 0334a602..e6ff4fcb2 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -34,7 +34,6 @@
 namespace pp {
 class KeyboardInputEvent;
 class MouseInputEvent;
-class VarDictionary;
 }
 
 namespace chrome_pdf {
@@ -108,16 +107,17 @@
 #if defined(PDF_ENABLE_XFA)
   void SetScrollPosition(const pp::Point& position) override;
 #endif
+  bool IsProgressiveLoad() override;
   std::string GetMetadata(const std::string& key) override;
 
   // DocumentLoader::Client implementation.
   pp::Instance* GetPluginInstance() override;
-  std::unique_ptr<URLLoaderWrapper> CreateURLLoader() override;
+  pp::URLLoader CreateURLLoader() override;
+  void OnPartialDocumentLoaded() override;
   void OnPendingRequestComplete() override;
   void OnNewDataAvailable() override;
+  void OnDocumentFailed() override;
   void OnDocumentComplete() override;
-  void OnDocumentCanceled() override;
-  void CancelBrowserDownload() override;
 
   void UnsupportedFeature(int type);
   void FontSubstituted();
@@ -191,11 +191,11 @@
   friend class SelectionChangeInvalidator;
 
   struct FileAvail : public FX_FILEAVAIL {
-    PDFiumEngine* engine;
+    DocumentLoader* loader;
   };
 
   struct DownloadHints : public FX_DOWNLOADHINTS {
-    PDFiumEngine* engine;
+    DocumentLoader* loader;
   };
 
   // PDFium interface to get block of data.
@@ -602,7 +602,7 @@
   double current_zoom_;
   unsigned int current_rotation_;
 
-  std::unique_ptr<DocumentLoader> doc_loader_;  // Main document's loader.
+  DocumentLoader doc_loader_;  // Main document's loader.
   std::string url_;
   std::string headers_;
   pp::CompletionCallbackFactory<PDFiumEngine> find_factory_;
@@ -731,11 +731,6 @@
   // to false after the user finishes getting their password.
   bool getting_password_;
 
-  // While true, the document try to be opened and parsed after download each
-  // part. Else the document will be opened and parsed only on finish of
-  // downloading.
-  bool process_when_pending_request_complete_ = true;
-
   DISALLOW_COPY_AND_ASSIGN(PDFiumEngine);
 };
 
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc
index ec139d3..b3744c9 100644
--- a/pdf/preview_mode_client.cc
+++ b/pdf/preview_mode_client.cc
@@ -162,8 +162,6 @@
   return false;
 }
 
-void PreviewModeClient::CancelBrowserDownload() {}
-
 uint32_t PreviewModeClient::GetBackgroundColor() {
   NOTREACHED();
   return 0;
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h
index 0874325..676d718 100644
--- a/pdf/preview_mode_client.h
+++ b/pdf/preview_mode_client.h
@@ -71,7 +71,6 @@
   void DocumentLoadProgress(uint32_t available, uint32_t doc_size) override;
   void FormTextFieldFocusChange(bool in_focus) override;
   bool IsPrintPreview() override;
-  void CancelBrowserDownload() override;
   uint32_t GetBackgroundColor() override;
 
  private:
diff --git a/pdf/range_set.cc b/pdf/range_set.cc
deleted file mode 100644
index df53d660..0000000
--- a/pdf/range_set.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "pdf/range_set.h"
-
-#include <algorithm>
-#include <sstream>
-#include <vector>
-
-namespace chrome_pdf {
-
-namespace {
-
-gfx::Range FixDirection(const gfx::Range& range) {
-  if (!range.IsValid() || !range.is_reversed())
-    return range;
-  return gfx::Range(range.end() + 1, range.start() + 1);
-}
-
-}  // namespace
-
-RangeSet::RangeSet() {}
-
-RangeSet::RangeSet(const gfx::Range& range) {
-  Union(range);
-}
-
-RangeSet::RangeSet(const RangeSet& range_set) : ranges_(range_set.ranges_) {}
-
-RangeSet::RangeSet(RangeSet&& range_set)
-    : ranges_(std::move(range_set.ranges_)) {}
-
-RangeSet& RangeSet::operator=(const RangeSet& other) {
-  ranges_ = other.ranges_;
-  return *this;
-}
-
-RangeSet::~RangeSet() {}
-
-bool RangeSet::operator==(const RangeSet& other) const {
-  return other.ranges_ == ranges_;
-}
-
-bool RangeSet::operator!=(const RangeSet& other) const {
-  return other.ranges_ != ranges_;
-}
-
-void RangeSet::Union(const gfx::Range& range) {
-  if (range.is_empty())
-    return;
-  gfx::Range fixed_range = FixDirection(range);
-  if (IsEmpty()) {
-    ranges_.insert(fixed_range);
-    return;
-  }
-
-  auto start = ranges_.upper_bound(fixed_range);
-  if (start != ranges_.begin())
-    --start;  // start now points to the key equal or lower than offset.
-  if (start->end() < fixed_range.start())
-    ++start;  // start element is entirely before current range, skip it.
-
-  auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
-  if (start == end) {  // No ranges to merge.
-    ranges_.insert(fixed_range);
-    return;
-  }
-
-  --end;
-
-  int new_start = std::min<size_t>(start->start(), fixed_range.start());
-  int new_end = std::max(end->end(), fixed_range.end());
-
-  ranges_.erase(start, ++end);
-  ranges_.insert(gfx::Range(new_start, new_end));
-}
-
-void RangeSet::Union(const RangeSet& range_set) {
-  if (&range_set == this)
-    return;
-  for (const auto& it : range_set.ranges()) {
-    Union(it);
-  }
-}
-
-bool RangeSet::Contains(uint32_t point) const {
-  return Contains(gfx::Range(point, point + 1));
-}
-
-bool RangeSet::Contains(const gfx::Range& range) const {
-  if (range.is_empty())
-    return false;
-  const gfx::Range fixed_range = FixDirection(range);
-  auto it = ranges().upper_bound(fixed_range);
-  if (it == ranges().begin())
-    return false;  // No ranges includes range.start().
-
-  --it;  // Now it starts equal or before range.start().
-  return it->end() >= fixed_range.end();
-}
-
-bool RangeSet::Contains(const RangeSet& range_set) const {
-  for (const auto& it : range_set.ranges()) {
-    if (!Contains(it))
-      return false;
-  }
-  return true;
-}
-
-bool RangeSet::Intersects(const gfx::Range& range) const {
-  if (IsEmpty() || range.is_empty())
-    return false;
-  const gfx::Range fixed_range = FixDirection(range);
-  auto start = ranges_.upper_bound(fixed_range);
-  if (start != ranges_.begin()) {
-    --start;
-  }
-  // start now points to the key equal or lower than range.start().
-  if (start->end() < range.start()) {
-    // start element is entirely before current range, skip it.
-    ++start;
-  }
-  auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
-  for (auto it = start; it != end; ++it) {
-    if (fixed_range.end() > it->start() && fixed_range.start() < it->end())
-      return true;
-  }
-  return false;
-}
-
-bool RangeSet::Intersects(const RangeSet& range_set) const {
-  for (const auto& it : range_set.ranges()) {
-    if (Intersects(it))
-      return true;
-  }
-  return false;
-}
-
-void RangeSet::Intersect(const gfx::Range& range) {
-  Intersect(RangeSet(range));
-}
-
-void RangeSet::Intersect(const RangeSet& range_set) {
-  if (IsEmpty())
-    return;
-  RangesContainer new_ranges;
-  for (const auto& range : range_set.ranges()) {
-    auto start = ranges_.upper_bound(range);
-    if (start != ranges_.begin())
-      --start;  // start now points to the key equal or lower than
-                // range.start().
-    if (start->end() < range.start())
-      ++start;  // start element is entirely before current range, skip it.
-    auto end = ranges_.upper_bound(gfx::Range(range.end()));
-    if (start == end) {  // No data in the current range available.
-      continue;
-    }
-    for (auto it = start; it != end; ++it) {
-      const gfx::Range new_range = range.Intersect(*it);
-      if (!new_range.is_empty()) {
-        new_ranges.insert(new_range);
-      }
-    }
-  }
-  new_ranges.swap(ranges_);
-}
-
-void RangeSet::Subtract(const gfx::Range& range) {
-  if (range.is_empty() || IsEmpty())
-    return;
-  const gfx::Range fixed_range = FixDirection(range);
-  auto start = ranges_.upper_bound(fixed_range);
-  if (start != ranges_.begin())
-    --start;  // start now points to the key equal or lower than
-              // range.start().
-  if (start->end() < fixed_range.start())
-    ++start;  // start element is entirely before current range, skip it.
-  auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
-  if (start == end) {  // No data in the current range available.
-    return;
-  }
-  std::vector<gfx::Range> new_ranges;
-  for (auto it = start; it != end; ++it) {
-    const gfx::Range left(it->start(),
-                          std::min(it->end(), fixed_range.start()));
-    const gfx::Range right(std::max(it->start(), fixed_range.end()), it->end());
-    if (!left.is_empty() && !left.is_reversed()) {
-      new_ranges.push_back(left);
-    }
-    if (!right.is_empty() && !right.is_reversed() && right != left) {
-      new_ranges.push_back(right);
-    }
-  }
-  ranges_.erase(start, end);
-  for (const auto& it : new_ranges) {
-    ranges_.insert(it);
-  }
-}
-
-void RangeSet::Subtract(const RangeSet& range_set) {
-  if (&range_set == this) {
-    ranges_.clear();
-    return;
-  }
-  for (const auto& range : range_set.ranges()) {
-    Subtract(range);
-  }
-}
-
-void RangeSet::Xor(const gfx::Range& range) {
-  Xor(RangeSet(range));
-}
-
-void RangeSet::Xor(const RangeSet& range_set) {
-  RangeSet tmp = *this;
-  tmp.Intersect(range_set);
-  Union(range_set);
-  Subtract(tmp);
-}
-
-bool RangeSet::IsEmpty() const {
-  return ranges().empty();
-}
-
-void RangeSet::Clear() {
-  ranges_.clear();
-}
-
-gfx::Range RangeSet::First() const {
-  return *ranges().begin();
-}
-
-gfx::Range RangeSet::Last() const {
-  return *ranges().rbegin();
-}
-
-std::string RangeSet::ToString() const {
-  std::stringstream ss;
-  ss << "{";
-  for (const auto& it : ranges()) {
-    ss << "[" << it.start() << "," << it.end() << ")";
-  }
-  ss << "}";
-  return ss.str();
-}
-
-}  // namespace chrome_pdf
-
-std::ostream& operator<<(std::ostream& os,
-                         const chrome_pdf::RangeSet& range_set) {
-  return (os << range_set.ToString());
-}
diff --git a/pdf/range_set.h b/pdf/range_set.h
deleted file mode 100644
index b615999..0000000
--- a/pdf/range_set.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Defines a set of geometric ranges, and standard operations on it.
-
-#ifndef PDF_RANGE_SET_H_
-#define PDF_RANGE_SET_H_
-
-#include <ostream>
-#include <set>
-#include <string>
-
-#include "ui/gfx/range/range.h"
-
-namespace chrome_pdf {
-
-class RangeSet {
- public:
-  RangeSet();
-  explicit RangeSet(const gfx::Range& range);
-  ~RangeSet();
-
-  RangeSet(const RangeSet& range_set);
-  RangeSet(RangeSet&& range_set);
-  RangeSet& operator=(const RangeSet& other);
-
-  bool operator==(const RangeSet& other) const;
-  bool operator!=(const RangeSet& other) const;
-
-  bool Contains(uint32_t point) const;
-  bool Contains(const gfx::Range& range) const;
-  bool Contains(const RangeSet& range_set) const;
-
-  bool Intersects(const gfx::Range& range) const;
-  bool Intersects(const RangeSet& range_set) const;
-
-  void Union(const gfx::Range& range);
-  void Union(const RangeSet& range_set);
-
-  void Intersect(const gfx::Range& range);
-  void Intersect(const RangeSet& range_set);
-
-  void Subtract(const gfx::Range& range);
-  void Subtract(const RangeSet& range_set);
-
-  void Xor(const gfx::Range& range);
-  void Xor(const RangeSet& range_set);
-
-  bool IsEmpty() const;
-  void Clear();
-
-  gfx::Range First() const;
-  gfx::Range Last() const;
-  std::string ToString() const;
-
-  struct range_compare {
-    bool operator()(const gfx::Range& lval, const gfx::Range& rval) const {
-      return lval.start() < rval.start();
-    }
-  };
-
-  using RangesContainer = std::set<gfx::Range, range_compare>;
-
-  const RangesContainer& ranges() const { return ranges_; }
-  size_t Size() const { return ranges_.size(); }
-
- private:
-  RangesContainer ranges_;
-};
-
-}  // namespace chrome_pdf
-
-std::ostream& operator<<(std::ostream& os,
-                         const chrome_pdf::RangeSet& range_set);
-
-#endif  // PDF_RANGE_SET_H_
diff --git a/pdf/range_set_unittest.cc b/pdf/range_set_unittest.cc
deleted file mode 100644
index 75047dd..0000000
--- a/pdf/range_set_unittest.cc
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "pdf/range_set.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chrome_pdf {
-
-TEST(RangeSetTest, Union) {
-  {
-    RangeSet range_set;
-    EXPECT_EQ("{}", range_set.ToString());
-    range_set.Union(gfx::Range(50, 100));
-    EXPECT_EQ("{[50,100)}", range_set.ToString());
-    range_set.Union(gfx::Range(80, 150));
-    EXPECT_EQ("{[50,150)}", range_set.ToString());
-    range_set.Union(gfx::Range(0, 70));
-    EXPECT_EQ("{[0,150)}", range_set.ToString());
-    range_set.Union(gfx::Range(70, 120));
-    EXPECT_EQ("{[0,150)}", range_set.ToString());
-    range_set.Union(gfx::Range(200, 150));
-    EXPECT_EQ("{[0,150)[151,201)}", range_set.ToString());
-    range_set.Union(gfx::Range(150, 151));
-    EXPECT_EQ("{[0,201)}", range_set.ToString());
-    range_set.Union(gfx::Range(0, 300));
-    EXPECT_EQ("{[0,300)}", range_set.ToString());
-    range_set.Union(gfx::Range(500, 600));
-    EXPECT_EQ("{[0,300)[500,600)}", range_set.ToString());
-  }
-  {
-    RangeSet range_set_1;
-    range_set_1.Union(gfx::Range(0, 10));
-    range_set_1.Union(gfx::Range(20, 30));
-    range_set_1.Union(gfx::Range(40, 50));
-
-    EXPECT_EQ("{[0,10)[20,30)[40,50)}", range_set_1.ToString());
-    range_set_1.Union(range_set_1);
-    EXPECT_EQ("{[0,10)[20,30)[40,50)}", range_set_1.ToString());
-
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(10, 20));
-    range_set_2.Union(gfx::Range(30, 40));
-    range_set_2.Union(gfx::Range(50, 60));
-
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set_2.ToString());
-    range_set_1.Union(range_set_2);
-    EXPECT_EQ("{[0,60)}", range_set_1.ToString());
-    EXPECT_EQ(RangeSet(gfx::Range(0, 60)), range_set_1);
-  }
-}
-
-TEST(RangeSetTest, Contains) {
-  RangeSet range_set;
-  range_set.Union(gfx::Range(10, 20));
-  range_set.Union(gfx::Range(30, 40));
-  range_set.Union(gfx::Range(50, 60));
-  EXPECT_TRUE(range_set.Contains(range_set));
-
-  {
-    EXPECT_FALSE(range_set.Contains(9));
-    EXPECT_FALSE(range_set.Contains(29));
-    EXPECT_FALSE(range_set.Contains(49));
-
-    EXPECT_TRUE(range_set.Contains(10));
-    EXPECT_TRUE(range_set.Contains(30));
-    EXPECT_TRUE(range_set.Contains(50));
-
-    EXPECT_TRUE(range_set.Contains(15));
-    EXPECT_TRUE(range_set.Contains(35));
-    EXPECT_TRUE(range_set.Contains(55));
-
-    EXPECT_TRUE(range_set.Contains(19));
-    EXPECT_TRUE(range_set.Contains(39));
-    EXPECT_TRUE(range_set.Contains(59));
-
-    EXPECT_FALSE(range_set.Contains(20));
-    EXPECT_FALSE(range_set.Contains(40));
-    EXPECT_FALSE(range_set.Contains(60));
-  }
-  {
-    EXPECT_FALSE(range_set.Contains(gfx::Range(0, 10)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(20, 30)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(40, 50)));
-
-    EXPECT_FALSE(range_set.Contains(gfx::Range(5, 15)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(25, 35)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(45, 55)));
-
-    EXPECT_TRUE(range_set.Contains(gfx::Range(10, 15)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(30, 35)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(50, 55)));
-
-    EXPECT_TRUE(range_set.Contains(gfx::Range(15, 20)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(35, 40)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(55, 60)));
-
-    EXPECT_TRUE(range_set.Contains(gfx::Range(10, 20)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(30, 40)));
-    EXPECT_TRUE(range_set.Contains(gfx::Range(50, 60)));
-
-    EXPECT_FALSE(range_set.Contains(gfx::Range(15, 25)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(35, 45)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(55, 65)));
-
-    EXPECT_FALSE(range_set.Contains(gfx::Range(20, 25)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(40, 45)));
-    EXPECT_FALSE(range_set.Contains(gfx::Range(60, 65)));
-
-    EXPECT_FALSE(range_set.Contains(gfx::Range(0, 100)));
-  }
-  {
-    RangeSet range_set_2 = range_set;
-    EXPECT_TRUE(range_set_2.Contains(range_set));
-    range_set_2.Union(gfx::Range(100, 200));
-    EXPECT_TRUE(range_set_2.Contains(range_set));
-    EXPECT_FALSE(range_set.Contains(range_set_2));
-  }
-}
-
-TEST(RangeSetTest, Intersects) {
-  RangeSet range_set;
-  range_set.Union(gfx::Range(10, 20));
-  range_set.Union(gfx::Range(30, 40));
-  range_set.Union(gfx::Range(50, 60));
-  EXPECT_TRUE(range_set.Intersects(range_set));
-  {
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(0, 10)));
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(20, 30)));
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(40, 50)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(5, 15)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(25, 35)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(45, 55)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(10, 15)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(30, 35)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(50, 55)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(15, 20)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(35, 40)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(55, 60)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(10, 20)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(30, 40)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(50, 60)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(15, 25)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(35, 45)));
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(55, 65)));
-
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(20, 25)));
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(40, 45)));
-    EXPECT_FALSE(range_set.Intersects(gfx::Range(60, 65)));
-
-    EXPECT_TRUE(range_set.Intersects(gfx::Range(0, 100)));
-  }
-  {
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(5, 15));
-    range_set_2.Union(gfx::Range(25, 35));
-    range_set_2.Union(gfx::Range(45, 55));
-    EXPECT_TRUE(range_set_2.Intersects(range_set));
-  }
-  {
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(5, 10));
-    range_set_2.Union(gfx::Range(25, 30));
-    range_set_2.Union(gfx::Range(45, 50));
-    EXPECT_FALSE(range_set_2.Intersects(range_set));
-  }
-}
-
-TEST(RangeSetTest, Intersect) {
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Intersect(range_set);
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(0, 100));
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(0, 55));
-    EXPECT_EQ("{[10,20)[30,40)[50,55)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(15, 100));
-    EXPECT_EQ("{[15,20)[30,40)[50,55)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(17, 53));
-    EXPECT_EQ("{[17,20)[30,40)[50,53)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(19, 45));
-    EXPECT_EQ("{[19,20)[30,40)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(30, 45));
-    EXPECT_EQ("{[30,40)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(35, 40));
-    EXPECT_EQ("{[35,40)}", range_set.ToString());
-    range_set.Intersect(gfx::Range(35, 35));
-    EXPECT_TRUE(range_set.IsEmpty());
-  }
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(12, 17));
-    range_set_2.Union(gfx::Range(25, 35));
-    range_set_2.Union(gfx::Range(39, 55));
-    range_set_2.Union(gfx::Range(59, 100));
-
-    range_set.Intersect(range_set_2);
-    EXPECT_EQ("{[12,17)[30,35)[39,40)[50,55)[59,60)}", range_set.ToString());
-  }
-}
-
-TEST(RangeSetTest, Subtract) {
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(35, 35));
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(0, 5));
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(70, 80));
-    EXPECT_EQ("{[10,20)[30,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(35, 39));
-    EXPECT_EQ("{[10,20)[30,35)[39,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(15, 32));
-    EXPECT_EQ("{[10,15)[32,35)[39,40)[50,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(15, 55));
-    EXPECT_EQ("{[10,15)[55,60)}", range_set.ToString());
-    range_set.Subtract(gfx::Range(0, 100));
-    EXPECT_EQ("{}", range_set.ToString());
-  }
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-    range_set.Subtract(range_set);
-    EXPECT_EQ("{}", range_set.ToString());
-  }
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(12, 17));
-    range_set_2.Union(gfx::Range(25, 35));
-    range_set_2.Union(gfx::Range(39, 55));
-    range_set_2.Union(gfx::Range(59, 100));
-
-    range_set.Subtract(range_set_2);
-    EXPECT_EQ("{[10,12)[17,20)[35,39)[55,59)}", range_set.ToString());
-  }
-}
-
-TEST(RangeSetTest, Xor) {
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-    range_set.Xor(range_set);
-    EXPECT_EQ("{}", range_set.ToString());
-  }
-  {
-    RangeSet range_set;
-    range_set.Union(gfx::Range(10, 20));
-    range_set.Union(gfx::Range(30, 40));
-    range_set.Union(gfx::Range(50, 60));
-
-    RangeSet range_set_2;
-    range_set_2.Union(gfx::Range(12, 17));
-    range_set_2.Union(gfx::Range(25, 35));
-    range_set_2.Union(gfx::Range(39, 55));
-    range_set_2.Union(gfx::Range(59, 100));
-
-    range_set.Xor(range_set_2);
-    EXPECT_EQ("{[10,12)[17,20)[25,30)[35,39)[40,50)[55,59)[60,100)}",
-              range_set.ToString());
-  }
-}
-
-TEST(RangeSetTest, OperationsOnEmptySet) {
-  RangeSet range_set;
-  range_set.Intersect(gfx::Range(10, 20));
-  range_set.Intersects(gfx::Range(10, 20));
-  range_set.Subtract(gfx::Range(10, 20));
-  range_set.Xor(gfx::Range(30, 40));
-  range_set.Union(gfx::Range(10, 20));
-}
-
-}  // namespace chrome_pdf
diff --git a/pdf/timer.cc b/pdf/timer.cc
deleted file mode 100644
index a6890b24..0000000
--- a/pdf/timer.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "pdf/timer.h"
-
-#include "ppapi/cpp/core.h"
-#include "ppapi/cpp/module.h"
-
-namespace chrome_pdf {
-
-Timer::Timer(int delay_in_milliseconds)
-    : delay_(delay_in_milliseconds), callback_factory_(this) {
-  PostCallback();
-}
-
-Timer::~Timer() {
-}
-
-void Timer::PostCallback() {
-  pp::CompletionCallback callback =
-      callback_factory_.NewCallback(&Timer::TimerProc);
-  pp::Module::Get()->core()->CallOnMainThread(delay_, callback, 0);
-}
-
-void Timer::TimerProc(int32_t /*result*/) {
-  PostCallback();
-  OnTimer();
-}
-
-}  // namespace chrome_pdf
diff --git a/pdf/timer.h b/pdf/timer.h
deleted file mode 100644
index a27f78f3..0000000
--- a/pdf/timer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PDF_TIMER_H_
-#define PDF_TIMER_H_
-
-#include "base/macros.h"
-#include "ppapi/utility/completion_callback_factory.h"
-
-namespace chrome_pdf {
-
-// Timer implementation for pepper plugins, based on pp::Core::CallOnMainThread.
-// We can not use base::Timer for plugins, because they have no
-// base::MessageLoop, on which it is based.
-class Timer {
- public:
-  explicit Timer(int delay_in_milliseconds);
-  virtual ~Timer();
-
-  virtual void OnTimer() = 0;
-
- private:
-  void PostCallback();
-  void TimerProc(int32_t result);
-
-  int delay_;
-  pp::CompletionCallbackFactory<Timer> callback_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(Timer);
-};
-
-}  // namespace chrome_pdf
-
-#endif  // PDF_TIMER_H_
diff --git a/pdf/url_loader_wrapper.h b/pdf/url_loader_wrapper.h
deleted file mode 100644
index b95cfd2..0000000
--- a/pdf/url_loader_wrapper.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PDF_URL_LOADER_WRAPPER_H_
-#define PDF_URL_LOADER_WRAPPER_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "ppapi/cpp/completion_callback.h"
-
-namespace chrome_pdf {
-
-class URLLoaderWrapper {
- public:
-  virtual ~URLLoaderWrapper() {}
-
-  // Returns length of content, will be -1, if it is unknown.
-  virtual int GetContentLength() const = 0;
-  // Returns if the response headers contains "accept-ranges".
-  virtual bool IsAcceptRangesBytes() const = 0;
-  // Returns if the content encoded in response.
-  virtual bool IsContentEncoded() const = 0;
-  // Returns response content type.
-  virtual std::string GetContentType() const = 0;
-  // Returns response content disposition.
-  virtual std::string GetContentDisposition() const = 0;
-  // Returns response status code.
-  virtual int GetStatusCode() const = 0;
-  // Returns if the response contains multi parts.
-  virtual bool IsMultipart() const = 0;
-  // If true, [start,end] - is byte range contains in response (include end).
-  // If false, response contains full document, start/end will be undefined.
-  virtual bool GetByteRange(int* start, int* end) const = 0;
-
-  // Close connection.
-  virtual void Close() = 0;
-  // Open new connection and send http range request.
-  virtual void OpenRange(const std::string& url,
-                         const std::string& referrer_url,
-                         uint32_t position,
-                         uint32_t size,
-                         const pp::CompletionCallback& cc) = 0;
-  // Read the response body. The size of the buffer must be large enough to
-  // hold the specified number of bytes to read.
-  // This function might perform a partial read.
-  virtual void ReadResponseBody(char* buffer,
-                                int buffer_size,
-                                const pp::CompletionCallback& cc) = 0;
-  // Returns the current download progress.
-  // Progress only refers to the response body and does not include the headers.
-  // If false, progress is unknown, bytes_received/total_bytes_to_be_received
-  // will be undefined.
-  virtual bool GetDownloadProgress(
-      int64_t* bytes_received,
-      int64_t* total_bytes_to_be_received) const = 0;
-};
-
-}  // namespace chrome_pdf
-
-#endif  // PDF_URL_LOADER_WRAPPER_H_
diff --git a/pdf/url_loader_wrapper_impl.cc b/pdf/url_loader_wrapper_impl.cc
deleted file mode 100644
index b7bc808..0000000
--- a/pdf/url_loader_wrapper_impl.cc
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "pdf/url_loader_wrapper_impl.h"
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "net/http/http_util.h"
-#include "pdf/timer.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/cpp/logging.h"
-#include "ppapi/cpp/url_request_info.h"
-#include "ppapi/cpp/url_response_info.h"
-
-namespace chrome_pdf {
-
-namespace {
-// We should read with delay to prevent block UI thread, and reduce CPU usage.
-const int kReadDelayMs = 2;
-
-pp::URLRequestInfo MakeRangeRequest(pp::Instance* plugin_instance,
-                                    const std::string& url,
-                                    const std::string& referrer_url,
-                                    uint32_t position,
-                                    uint32_t size) {
-  pp::URLRequestInfo request(plugin_instance);
-  request.SetURL(url);
-  request.SetMethod("GET");
-  request.SetFollowRedirects(false);
-  request.SetCustomReferrerURL(referrer_url);
-
-  // According to rfc2616, byte range specifies position of the first and last
-  // bytes in the requested range inclusively. Therefore we should subtract 1
-  // from the position + size, to get index of the last byte that needs to be
-  // downloaded.
-  std::string str_header =
-      base::StringPrintf("Range: bytes=%d-%d", position, position + size - 1);
-  pp::Var header(str_header.c_str());
-  request.SetHeaders(header);
-
-  return request;
-}
-
-bool GetByteRangeFromStr(const std::string& content_range_str,
-                         int* start,
-                         int* end) {
-  std::string range = content_range_str;
-  if (!base::StartsWith(range, "bytes", base::CompareCase::INSENSITIVE_ASCII))
-    return false;
-
-  range = range.substr(strlen("bytes"));
-  std::string::size_type pos = range.find('-');
-  std::string range_end;
-  if (pos != std::string::npos)
-    range_end = range.substr(pos + 1);
-  base::TrimWhitespaceASCII(range, base::TRIM_LEADING, &range);
-  base::TrimWhitespaceASCII(range_end, base::TRIM_LEADING, &range_end);
-  *start = atoi(range.c_str());
-  *end = atoi(range_end.c_str());
-  return true;
-}
-
-// If the headers have a byte-range response, writes the start and end
-// positions and returns true if at least the start position was parsed.
-// The end position will be set to 0 if it was not found or parsed from the
-// response.
-// Returns false if not even a start position could be parsed.
-bool GetByteRangeFromHeaders(const std::string& headers, int* start, int* end) {
-  net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
-  while (it.GetNext()) {
-    if (base::LowerCaseEqualsASCII(it.name(), "content-range")) {
-      if (GetByteRangeFromStr(it.values().c_str(), start, end))
-        return true;
-    }
-  }
-  return false;
-}
-
-bool IsDoubleEndLineAtEnd(const char* buffer, int size) {
-  if (size < 2)
-    return false;
-
-  if (buffer[size - 1] == '\n' && buffer[size - 2] == '\n')
-    return true;
-
-  if (size < 4)
-    return false;
-
-  return buffer[size - 1] == '\n' && buffer[size - 2] == '\r' &&
-         buffer[size - 3] == '\n' && buffer[size - 4] == '\r';
-}
-
-}  // namespace
-
-class URLLoaderWrapperImpl::ReadStarter : public Timer {
- public:
-  explicit ReadStarter(URLLoaderWrapperImpl* owner)
-      : Timer(kReadDelayMs), owner_(owner) {}
-  ~ReadStarter() override {}
-
-  // Timer overrides:
-  void OnTimer() override { owner_->ReadResponseBodyImpl(); }
-
- private:
-  URLLoaderWrapperImpl* owner_;
-};
-
-URLLoaderWrapperImpl::URLLoaderWrapperImpl(pp::Instance* plugin_instance,
-                                           const pp::URLLoader& url_loader)
-    : plugin_instance_(plugin_instance),
-      url_loader_(url_loader),
-      callback_factory_(this) {
-  SetHeadersFromLoader();
-}
-
-URLLoaderWrapperImpl::~URLLoaderWrapperImpl() {
-  Close();
-  // We should call callbacks to prevent memory leaks.
-  // The callbacks don't do anything, because the objects that created the
-  // callbacks have been destroyed.
-  if (!did_open_callback_.IsOptional())
-    did_open_callback_.RunAndClear(-1);
-  if (!did_read_callback_.IsOptional())
-    did_read_callback_.RunAndClear(-1);
-}
-
-int URLLoaderWrapperImpl::GetContentLength() const {
-  return content_length_;
-}
-
-bool URLLoaderWrapperImpl::IsAcceptRangesBytes() const {
-  return accept_ranges_bytes_;
-}
-
-bool URLLoaderWrapperImpl::IsContentEncoded() const {
-  return content_encoded_;
-}
-
-std::string URLLoaderWrapperImpl::GetContentType() const {
-  return content_type_;
-}
-std::string URLLoaderWrapperImpl::GetContentDisposition() const {
-  return content_disposition_;
-}
-
-int URLLoaderWrapperImpl::GetStatusCode() const {
-  return url_loader_.GetResponseInfo().GetStatusCode();
-}
-
-bool URLLoaderWrapperImpl::IsMultipart() const {
-  return is_multipart_;
-}
-
-bool URLLoaderWrapperImpl::GetByteRange(int* start, int* end) const {
-  DCHECK(start);
-  DCHECK(end);
-  *start = byte_range_.start();
-  *end = byte_range_.end();
-  return byte_range_.IsValid();
-}
-
-bool URLLoaderWrapperImpl::GetDownloadProgress(
-    int64_t* bytes_received,
-    int64_t* total_bytes_to_be_received) const {
-  return url_loader_.GetDownloadProgress(bytes_received,
-                                         total_bytes_to_be_received);
-}
-
-void URLLoaderWrapperImpl::Close() {
-  url_loader_.Close();
-  read_starter_.reset();
-}
-
-void URLLoaderWrapperImpl::OpenRange(const std::string& url,
-                                     const std::string& referrer_url,
-                                     uint32_t position,
-                                     uint32_t size,
-                                     const pp::CompletionCallback& cc) {
-  did_open_callback_ = cc;
-  pp::CompletionCallback callback =
-      callback_factory_.NewCallback(&URLLoaderWrapperImpl::DidOpen);
-  int rv = url_loader_.Open(
-      MakeRangeRequest(plugin_instance_, url, referrer_url, position, size),
-      callback);
-  if (rv != PP_OK_COMPLETIONPENDING)
-    callback.Run(rv);
-}
-
-void URLLoaderWrapperImpl::ReadResponseBody(char* buffer,
-                                            int buffer_size,
-                                            const pp::CompletionCallback& cc) {
-  did_read_callback_ = cc;
-  buffer_ = buffer;
-  buffer_size_ = buffer_size;
-  read_starter_ = base::MakeUnique<ReadStarter>(this);
-}
-
-void URLLoaderWrapperImpl::ReadResponseBodyImpl() {
-  read_starter_.reset();
-  pp::CompletionCallback callback =
-      callback_factory_.NewCallback(&URLLoaderWrapperImpl::DidRead);
-  int rv = url_loader_.ReadResponseBody(buffer_, buffer_size_, callback);
-  if (rv != PP_OK_COMPLETIONPENDING) {
-    callback.Run(rv);
-  }
-}
-
-void URLLoaderWrapperImpl::SetResponseHeaders(
-    const std::string& response_headers) {
-  response_headers_ = response_headers;
-  ParseHeaders();
-}
-
-void URLLoaderWrapperImpl::ParseHeaders() {
-  content_length_ = -1;
-  accept_ranges_bytes_ = false;
-  content_encoded_ = false;
-  content_type_.clear();
-  content_disposition_.clear();
-  multipart_boundary_.clear();
-  byte_range_ = gfx::Range::InvalidRange();
-  is_multipart_ = false;
-
-  if (response_headers_.empty())
-    return;
-
-  net::HttpUtil::HeadersIterator it(response_headers_.begin(),
-                                    response_headers_.end(), "\n");
-  while (it.GetNext()) {
-    if (base::LowerCaseEqualsASCII(it.name(), "content-length")) {
-      content_length_ = atoi(it.values().c_str());
-    } else if (base::LowerCaseEqualsASCII(it.name(), "accept-ranges")) {
-      accept_ranges_bytes_ = base::LowerCaseEqualsASCII(it.values(), "bytes");
-    } else if (base::LowerCaseEqualsASCII(it.name(), "content-encoding")) {
-      content_encoded_ = true;
-    } else if (base::LowerCaseEqualsASCII(it.name(), "content-type")) {
-      content_type_ = it.values();
-      size_t semi_colon_pos = content_type_.find(';');
-      if (semi_colon_pos != std::string::npos) {
-        content_type_ = content_type_.substr(0, semi_colon_pos);
-      }
-      base::TrimWhitespaceASCII(content_type_, base::TRIM_ALL, &content_type_);
-      // multipart boundary.
-      std::string type = base::ToLowerASCII(it.values());
-      if (base::StartsWith(type, "multipart/", base::CompareCase::SENSITIVE)) {
-        const char* boundary = strstr(type.c_str(), "boundary=");
-        DCHECK(boundary);
-        if (boundary) {
-          multipart_boundary_ = std::string(boundary + 9);
-          is_multipart_ = !multipart_boundary_.empty();
-        }
-      }
-    } else if (base::LowerCaseEqualsASCII(it.name(), "content-disposition")) {
-      content_disposition_ = it.values();
-    } else if (base::LowerCaseEqualsASCII(it.name(), "content-range")) {
-      int start = 0;
-      int end = 0;
-      if (GetByteRangeFromStr(it.values().c_str(), &start, &end)) {
-        byte_range_ = gfx::Range(start, end);
-      }
-    }
-  }
-}
-
-void URLLoaderWrapperImpl::DidOpen(int32_t result) {
-  SetHeadersFromLoader();
-  did_open_callback_.RunAndClear(result);
-}
-
-void URLLoaderWrapperImpl::DidRead(int32_t result) {
-  if (multi_part_processed_) {
-    // Reset this flag so we look inside the buffer in calls of DidRead for this
-    // response only once.  Note that this code DOES NOT handle multi part
-    // responses with more than one part (we don't issue them at the moment, so
-    // they shouldn't arrive).
-    is_multipart_ = false;
-  }
-  if (result <= 0 || !is_multipart_) {
-    did_read_callback_.RunAndClear(result);
-    return;
-  }
-  if (result <= 2) {
-    // TODO(art-snake): Accumulate data for parse headers.
-    did_read_callback_.RunAndClear(result);
-    return;
-  }
-
-  char* start = buffer_;
-  size_t length = result;
-  multi_part_processed_ = true;
-  for (int i = 2; i < result; ++i) {
-    if (IsDoubleEndLineAtEnd(buffer_, i)) {
-      int start_pos = 0;
-      int end_pos = 0;
-      if (GetByteRangeFromHeaders(std::string(buffer_, i), &start_pos,
-                                  &end_pos)) {
-        byte_range_ = gfx::Range(start_pos, end_pos);
-        start += i;
-        length -= i;
-      }
-      break;
-    }
-  }
-  result = length;
-  if (result == 0) {
-    // Continue receiving.
-    return ReadResponseBodyImpl();
-  }
-  DCHECK(result > 0);
-  memmove(buffer_, start, result);
-
-  did_read_callback_.RunAndClear(result);
-}
-
-void URLLoaderWrapperImpl::SetHeadersFromLoader() {
-  pp::URLResponseInfo response = url_loader_.GetResponseInfo();
-  pp::Var headers_var = response.GetHeaders();
-
-  SetResponseHeaders(headers_var.is_string() ? headers_var.AsString() : "");
-}
-
-}  // namespace chrome_pdf
diff --git a/pdf/url_loader_wrapper_impl.h b/pdf/url_loader_wrapper_impl.h
deleted file mode 100644
index b494818..0000000
--- a/pdf/url_loader_wrapper_impl.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PDF_URL_LOADER_WRAPPER_IMPL_H_
-#define PDF_URL_LOADER_WRAPPER_IMPL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "pdf/url_loader_wrapper.h"
-#include "ppapi/cpp/url_loader.h"
-#include "ppapi/utility/completion_callback_factory.h"
-#include "ui/gfx/range/range.h"
-
-namespace pp {
-class Instance;
-};
-
-namespace chrome_pdf {
-
-class URLLoaderWrapperImpl : public URLLoaderWrapper {
- public:
-  URLLoaderWrapperImpl(pp::Instance* plugin_instance,
-                       const pp::URLLoader& url_loader);
-  ~URLLoaderWrapperImpl() override;
-
-  // URLLoaderWrapper overrides:
-  int GetContentLength() const override;
-  bool IsAcceptRangesBytes() const override;
-  bool IsContentEncoded() const override;
-  std::string GetContentType() const override;
-  std::string GetContentDisposition() const override;
-  int GetStatusCode() const override;
-  bool IsMultipart() const override;
-  bool GetByteRange(int* start, int* end) const override;
-  bool GetDownloadProgress(int64_t* bytes_received,
-                           int64_t* total_bytes_to_be_received) const override;
-  void Close() override;
-  void OpenRange(const std::string& url,
-                 const std::string& referrer_url,
-                 uint32_t position,
-                 uint32_t size,
-                 const pp::CompletionCallback& cc) override;
-  void ReadResponseBody(char* buffer,
-                        int buffer_size,
-                        const pp::CompletionCallback& cc) override;
-
-  void SetResponseHeaders(const std::string& response_headers);
-
- private:
-  class ReadStarter;
-
-  void SetHeadersFromLoader();
-  void ParseHeaders();
-  void DidOpen(int32_t result);
-  void DidRead(int32_t result);
-
-  void ReadResponseBodyImpl();
-
-  pp::Instance* const plugin_instance_;
-  pp::URLLoader url_loader_;
-  std::string response_headers_;
-
-  int content_length_ = -1;
-  bool accept_ranges_bytes_ = false;
-  bool content_encoded_ = false;
-  std::string content_type_;
-  std::string content_disposition_;
-  std::string multipart_boundary_;
-  gfx::Range byte_range_ = gfx::Range::InvalidRange();
-  bool is_multipart_ = false;
-  char* buffer_ = nullptr;
-  uint32_t buffer_size_ = 0;
-  bool multi_part_processed_ = false;
-
-  pp::CompletionCallback did_open_callback_;
-  pp::CompletionCallback did_read_callback_;
-  pp::CompletionCallbackFactory<URLLoaderWrapperImpl> callback_factory_;
-
-  std::unique_ptr<ReadStarter> read_starter_;
-
-  DISALLOW_COPY_AND_ASSIGN(URLLoaderWrapperImpl);
-};
-
-}  // namespace chrome_pdf
-
-#endif  // PDF_URL_LOADER_WRAPPER_IMPL_H_
diff --git a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
index 45283e3..4a16385 100644
--- a/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
+++ b/ppapi/shared_impl/private/ppb_x509_certificate_private_shared.cc
@@ -41,29 +41,29 @@
   }
 
   switch (value->GetType()) {
-    case base::Value::TYPE_NULL:
+    case base::Value::Type::NONE:
       return PP_MakeNull();
-    case base::Value::TYPE_BOOLEAN: {
+    case base::Value::Type::BOOLEAN: {
       bool val;
       value->GetAsBoolean(&val);
       return PP_MakeBool(PP_FromBool(val));
     }
-    case base::Value::TYPE_INTEGER: {
+    case base::Value::Type::INTEGER: {
       int val;
       value->GetAsInteger(&val);
       return PP_MakeInt32(val);
     }
-    case base::Value::TYPE_DOUBLE: {
+    case base::Value::Type::DOUBLE: {
       double val;
       value->GetAsDouble(&val);
       return PP_MakeDouble(val);
     }
-    case base::Value::TYPE_STRING: {
+    case base::Value::Type::STRING: {
       std::string val;
       value->GetAsString(&val);
       return StringVar::StringToPPVar(val);
     }
-    case base::Value::TYPE_BINARY: {
+    case base::Value::Type::BINARY: {
       const base::BinaryValue* binary =
           static_cast<const base::BinaryValue*>(value);
       uint32_t size = static_cast<uint32_t>(binary->GetSize());
@@ -73,8 +73,8 @@
                                                                      buffer);
       return array_buffer;
     }
-    case base::Value::TYPE_DICTIONARY:
-    case base::Value::TYPE_LIST:
+    case base::Value::Type::DICTIONARY:
+    case base::Value::Type::LIST:
       // Not handled.
       break;
   }
diff --git a/printing/printing_context_chromeos.h b/printing/printing_context_chromeos.h
index 79bd3fbe..efd2fb7 100644
--- a/printing/printing_context_chromeos.h
+++ b/printing/printing_context_chromeos.h
@@ -14,10 +14,6 @@
 #include "printing/backend/cups_printer.h"
 #include "printing/printing_context.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 class PRINTING_EXPORT PrintingContextChromeos : public PrintingContext {
diff --git a/printing/printing_context_linux.h b/printing/printing_context_linux.h
index 1a57d978..2ca0fe66 100644
--- a/printing/printing_context_linux.h
+++ b/printing/printing_context_linux.h
@@ -10,10 +10,6 @@
 #include "base/macros.h"
 #include "printing/printing_context.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 class MetafilePlayer;
diff --git a/printing/printing_context_no_system_dialog.h b/printing/printing_context_no_system_dialog.h
index 667b6f6..6ce0639 100644
--- a/printing/printing_context_no_system_dialog.h
+++ b/printing/printing_context_no_system_dialog.h
@@ -10,10 +10,6 @@
 #include "base/macros.h"
 #include "printing/printing_context.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace printing {
 
 class PRINTING_EXPORT PrintingContextNoSystemDialog : public PrintingContext {
diff --git a/remoting/android/client_java_tmpl.gni b/remoting/android/client_java_tmpl.gni
index e2441f9..705ce45 100644
--- a/remoting/android/client_java_tmpl.gni
+++ b/remoting/android/client_java_tmpl.gni
@@ -80,7 +80,8 @@
       "//base:base_java",
       "//remoting/android:remoting_android_client_java_resources",
       "//third_party/android_tools:android_support_annotations_java",
-      "//third_party/android_tools:android_support_v4_java",
+      "//third_party/android_tools:android_support_compat_java",
+      "//third_party/android_tools:android_support_core_ui_java",
       "//third_party/android_tools:android_support_v7_appcompat_java",
       "//third_party/android_tools:android_support_v7_mediarouter_java",
       "//ui/android:ui_java",
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index e696b8b6..481a9594 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -605,7 +605,8 @@
     configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
     if (is_win) {
-      defines = host_predefines + [ "BINARY=BINARY_REMOTING_START_HOST" ]
+      defines = host_predefines +
+                [ "REMOTING_HOST_BINARY=BINARY_REMOTING_START_HOST" ]
 
       deps += [
         "//build/win:default_exe_manifest",
diff --git a/remoting/host/host_config.cc b/remoting/host/host_config.cc
index 95b66e2..db6ec9a 100644
--- a/remoting/host/host_config.cc
+++ b/remoting/host/host_config.cc
@@ -17,7 +17,7 @@
     const std::string& json) {
   std::unique_ptr<base::Value> value =
       base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS);
-  if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value || !value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(WARNING) << "Failed to parse host config from JSON";
     return nullptr;
   }
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 3eaea24..49aa142 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -79,7 +79,8 @@
         "it2me_native_messaging_host_entry_point.cc",
       ]
 
-      defines = host_predefines + [ "BINARY=BINARY_REMOTE_ASSISTANCE_HOST" ]
+      defines = host_predefines +
+                [ "REMOTING_HOST_BINARY=BINARY_REMOTE_ASSISTANCE_HOST" ]
 
       ldflags = [
         # "/NODEFAULTLIB", TODO(zijiehe): Why IgnoreAllDefaultLibraries: true in
@@ -102,7 +103,8 @@
         "it2me_native_messaging_host_entry_point.cc",
       ]
 
-      defines = host_predefines + [ "BINARY=BINARY_HOST_IT2ME_UIACCESS" ]
+      defines = host_predefines +
+                [ "REMOTING_HOST_BINARY=BINARY_HOST_IT2ME_UIACCESS" ]
 
       ldflags = [
         # "/NODEFAULTLIB", TODO(zijiehe): Why IgnoreAllDefaultLibraries: true in
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 5d87e20..41e8f75e 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -119,7 +119,7 @@
 
   std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
   std::unique_ptr<base::Value> message_value = base::JSONReader::Read(message);
-  if (!message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!message_value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Received a message that's not a dictionary.";
     client_->CloseChannel(std::string());
     return;
diff --git a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
index 14e7ed56..072bfb6d 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_unittest.cc
@@ -360,7 +360,7 @@
     }
 
     std::unique_ptr<base::Value> message = base::JSONReader::Read(message_json);
-    if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!message || !message->IsType(base::Value::Type::DICTIONARY)) {
       LOG(ERROR) << "Malformed message:" << message_json;
       return nullptr;
     }
diff --git a/remoting/host/pairing_registry_delegate_win.cc b/remoting/host/pairing_registry_delegate_win.cc
index 66911c5..af61d6df 100644
--- a/remoting/host/pairing_registry_delegate_win.cc
+++ b/remoting/host/pairing_registry_delegate_win.cc
@@ -63,7 +63,7 @@
     return nullptr;
   }
 
-  if (!value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Failed to parse '" << value_name << "': not a dictionary.";
     return nullptr;
   }
diff --git a/remoting/host/policy_watcher_unittest.cc b/remoting/host/policy_watcher_unittest.cc
index 1147988..7df7c7d 100644
--- a/remoting/host/policy_watcher_unittest.cc
+++ b/remoting/host/policy_watcher_unittest.cc
@@ -674,14 +674,14 @@
   const policy::Schema string_schema =
       schema->GetKnownProperty("RemoteAccessHostDomain");
   EXPECT_TRUE(string_schema.valid());
-  EXPECT_EQ(string_schema.type(), base::Value::Type::TYPE_STRING);
+  EXPECT_EQ(string_schema.type(), base::Value::Type::STRING);
 
   // And check one, random "boolean" policy to see if the type propagated
   // correctly from policy_templates.json file.
   const policy::Schema boolean_schema =
       schema->GetKnownProperty("RemoteAccessHostRequireCurtain");
   EXPECT_TRUE(boolean_schema.valid());
-  EXPECT_EQ(boolean_schema.type(), base::Value::Type::TYPE_BOOLEAN);
+  EXPECT_EQ(boolean_schema.type(), base::Value::Type::BOOLEAN);
 }
 
 }  // namespace remoting
diff --git a/remoting/host/predefines_win.gni b/remoting/host/predefines_win.gni
index 5edbc74..29a031ff 100644
--- a/remoting/host/predefines_win.gni
+++ b/remoting/host/predefines_win.gni
@@ -4,7 +4,7 @@
 # GN config is always executed after defines, so if we would like to generate
 # defines ordered as
 # #define BINARY_CORE=1
-# #define BINARY=BINARY_CORE
+# #define REMOTING_HOST_BINARY=BINARY_CORE
 # we need to set these predefines as a list, and append others after it.
 
 assert(is_win)
diff --git a/remoting/host/security_key/BUILD.gn b/remoting/host/security_key/BUILD.gn
index b67c517..ff462ab 100644
--- a/remoting/host/security_key/BUILD.gn
+++ b/remoting/host/security_key/BUILD.gn
@@ -59,7 +59,8 @@
   executable("remote_security_key") {
     configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-    defines = host_predefines + [ "BINARY=BINARY_REMOTE_SECURITY_KEY" ]
+    defines =
+        host_predefines + [ "REMOTING_HOST_BINARY=BINARY_REMOTE_SECURITY_KEY" ]
 
     deps = [
       "//build/win:default_exe_manifest",
diff --git a/remoting/host/setup/me2me_native_messaging_host.cc b/remoting/host/setup/me2me_native_messaging_host.cc
index 1b054f22..9c5a5793 100644
--- a/remoting/host/setup/me2me_native_messaging_host.cc
+++ b/remoting/host/setup/me2me_native_messaging_host.cc
@@ -97,7 +97,7 @@
 
   std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
   std::unique_ptr<base::Value> message_value = base::JSONReader::Read(message);
-  if (!message_value->IsType(base::Value::TYPE_DICTIONARY)) {
+  if (!message_value->IsType(base::Value::Type::DICTIONARY)) {
     OnError("Received a message that's not a dictionary.");
     return;
   }
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index 6f3151c..ee1dfc1 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -424,7 +424,7 @@
     }
 
     std::unique_ptr<base::Value> message = base::JSONReader::Read(message_json);
-    if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) {
+    if (!message || !message->IsType(base::Value::Type::DICTIONARY)) {
       return nullptr;
     }
 
diff --git a/remoting/host/setup/service_client.cc b/remoting/host/setup/service_client.cc
index 7c107af..a4961f81a 100644
--- a/remoting/host/setup/service_client.cc
+++ b/remoting/host/setup/service_client.cc
@@ -151,7 +151,7 @@
           base::DictionaryValue *dict;
           std::string code;
           if (message_value.get() &&
-              message_value->IsType(base::Value::TYPE_DICTIONARY) &&
+              message_value->IsType(base::Value::Type::DICTIONARY) &&
               message_value->GetAsDictionary(&dict) &&
               dict->GetString("data.authorizationCode", &code)) {
             delegate_->OnHostRegistered(code);
diff --git a/remoting/host/token_validator_base.cc b/remoting/host/token_validator_base.cc
index 852879f..d461b57 100644
--- a/remoting/host/token_validator_base.cc
+++ b/remoting/host/token_validator_base.cc
@@ -127,11 +127,14 @@
   DCHECK_NE(net_result, net::ERR_IO_PENDING);
   DCHECK_EQ(request_.get(), source);
 
-  if (net_result != net::OK)
+  if (net_result != net::OK) {
+    // Process all network errors in the same manner as read errors.
+    OnReadCompleted(request_.get(), net_result);
     return;
+  }
 
   int bytes_read = request_->Read(buffer_.get(), kBufferSize);
-  if (bytes_read > 0)
+  if (bytes_read != net::ERR_IO_PENDING)
     OnReadCompleted(request_.get(), bytes_read);
 }
 
diff --git a/remoting/host/token_validator_factory_impl_unittest.cc b/remoting/host/token_validator_factory_impl_unittest.cc
index a0e4d1d..0a530e3f 100644
--- a/remoting/host/token_validator_factory_impl_unittest.cc
+++ b/remoting/host/token_validator_factory_impl_unittest.cc
@@ -13,7 +13,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/values.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
+#include "net/test/url_request/url_request_failed_job.h"
 #include "net/url_request/url_request_job_factory.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "net/url_request/url_request_status.h"
@@ -44,6 +46,9 @@
       : headers_(headers),
         response_(response) {
   }
+
+  ~FakeProtocolHandler() override {}
+
   net::URLRequestJob* MaybeCreateJob(
       net::URLRequest* request,
       net::NetworkDelegate* network_delegate) const override {
@@ -56,6 +61,29 @@
   std::string response_;
 };
 
+// Creates URLRequestJobs that fail at the specified phase.
+class FakeFailingProtocolHandler
+    : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+  FakeFailingProtocolHandler(
+      net::URLRequestFailedJob::FailurePhase failure_phase,
+      net::Error net_error)
+      : failure_phase_(failure_phase), net_error_(net_error) {}
+
+  ~FakeFailingProtocolHandler() override {}
+
+  net::URLRequestJob* MaybeCreateJob(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const override {
+    return new net::URLRequestFailedJob(request, network_delegate,
+                                        failure_phase_, net_error_);
+  }
+
+ private:
+  const net::URLRequestFailedJob::FailurePhase failure_phase_;
+  const net::Error net_error_;
+};
+
 class SetResponseURLRequestContext: public net::TestURLRequestContext {
  public:
   void SetResponse(const std::string& headers, const std::string& response) {
@@ -65,6 +93,16 @@
         "https", base::MakeUnique<FakeProtocolHandler>(headers, response));
     context_storage_.set_job_factory(std::move(factory));
   }
+
+  void SetErrorResponse(net::URLRequestFailedJob::FailurePhase failure_phase,
+                        net::Error net_error) {
+    std::unique_ptr<net::URLRequestJobFactoryImpl> factory =
+        base::MakeUnique<net::URLRequestJobFactoryImpl>();
+    factory->SetProtocolHandler(
+        "https",
+        base::MakeUnique<FakeFailingProtocolHandler>(failure_phase, net_error));
+    context_storage_.set_job_factory(std::move(factory));
+  }
 };
 
 }  // namespace
@@ -131,6 +169,14 @@
     context->SetResponse(headers, response);
   }
 
+  void SetErrorResponse(net::URLRequestFailedJob::FailurePhase failure_phase,
+                        net::Error net_error) {
+    SetResponseURLRequestContext* context =
+        static_cast<SetResponseURLRequestContext*>(
+            request_context_getter_->GetURLRequestContext());
+    context->SetErrorResponse(failure_phase, net_error);
+  }
+
   base::MessageLoop message_loop_;
   scoped_refptr<RsaKeyPair> key_pair_;
   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
@@ -189,4 +235,43 @@
   base::RunLoop().Run();
 }
 
+TEST_F(TokenValidatorFactoryImplTest, DeleteOnStartError) {
+  token_validator_ =
+      token_validator_factory_->CreateTokenValidator(kLocalJid, kRemoteJid);
+
+  SetErrorResponse(net::URLRequestFailedJob::START, net::ERR_FAILED);
+
+  token_validator_->ValidateThirdPartyToken(
+      kToken,
+      base::Bind(&TokenValidatorFactoryImplTest::DeleteOnFailureCallback,
+                 base::Unretained(this)));
+  base::RunLoop().Run();
+}
+
+TEST_F(TokenValidatorFactoryImplTest, DeleteOnSyncReadError) {
+  token_validator_ =
+      token_validator_factory_->CreateTokenValidator(kLocalJid, kRemoteJid);
+
+  SetErrorResponse(net::URLRequestFailedJob::READ_SYNC, net::ERR_FAILED);
+
+  token_validator_->ValidateThirdPartyToken(
+      kToken,
+      base::Bind(&TokenValidatorFactoryImplTest::DeleteOnFailureCallback,
+                 base::Unretained(this)));
+  base::RunLoop().Run();
+}
+
+TEST_F(TokenValidatorFactoryImplTest, DeleteOnAsyncReadError) {
+  token_validator_ =
+      token_validator_factory_->CreateTokenValidator(kLocalJid, kRemoteJid);
+
+  SetErrorResponse(net::URLRequestFailedJob::READ_ASYNC, net::ERR_FAILED);
+
+  token_validator_->ValidateThirdPartyToken(
+      kToken,
+      base::Bind(&TokenValidatorFactoryImplTest::DeleteOnFailureCallback,
+                 base::Unretained(this)));
+  base::RunLoop().Run();
+}
+
 }  // namespace remoting
diff --git a/remoting/host/win/BUILD.gn b/remoting/host/win/BUILD.gn
index cf2dbcd..cb204bf 100644
--- a/remoting/host/win/BUILD.gn
+++ b/remoting/host/win/BUILD.gn
@@ -263,7 +263,7 @@
 executable("remoting_console") {
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  defines = host_predefines + [ "BINARY=BINARY_HOST_ME2ME" ]
+  defines = host_predefines + [ "REMOTING_HOST_BINARY=BINARY_HOST_ME2ME" ]
 
   deps = [
     ":dpi_aware_exe_manifest",
@@ -289,7 +289,7 @@
     "//remoting/build/config:remoting_me2me_host",
   ]
 
-  defines = host_predefines + [ "BINARY=BINARY_HOST_ME2ME" ]
+  defines = host_predefines + [ "REMOTING_HOST_BINARY=BINARY_HOST_ME2ME" ]
 
   deps = [
     ":dpi_aware_exe_manifest",
@@ -315,7 +315,7 @@
               "_ATL_CSTRING_EXPLICIT_CONSTRUCTORS",
               "_ATL_NO_AUTOMATIC_NAMESPACE",
               "_ATL_NO_EXCEPTIONS",
-              "BINARY=BINARY_CORE",
+              "REMOTING_HOST_BINARY=BINARY_CORE",
               "DAEMON_CONTROLLER_CLSID=\"$daemon_controller_clsid\"",
               "RDP_DESKTOP_SESSION_CLSID=\"$rdp_desktop_session_clsid\"",
               "HOST_IMPLEMENTATION",
@@ -422,7 +422,7 @@
     "//build/config/win:windowed",
   ]
 
-  defines = host_predefines + [ "BINARY=BINARY_DESKTOP" ]
+  defines = host_predefines + [ "REMOTING_HOST_BINARY=BINARY_DESKTOP" ]
 
   deps = [
     ":remoting_core",
@@ -449,7 +449,8 @@
 executable("remoting_native_messaging_host") {
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  defines = host_predefines + [ "BINARY=BINARY_NATIVE_MESSAGING_HOST" ]
+  defines =
+      host_predefines + [ "REMOTING_HOST_BINARY=BINARY_NATIVE_MESSAGING_HOST" ]
 
   deps = [
     ":remoting_core",
diff --git a/remoting/host/win/core.rc.jinja2 b/remoting/host/win/core.rc.jinja2
index a4d2c32..15bb176d 100644
--- a/remoting/host/win/core.rc.jinja2
+++ b/remoting/host/win/core.rc.jinja2
@@ -14,7 +14,7 @@
 
 LANGUAGE {{ lang | GetPrimaryLanguage }}, {{ lang | GetSublanguage }}
 
-#if (BINARY == BINARY_CORE)
+#if (REMOTING_HOST_BINARY == BINARY_CORE)
 
 STRINGTABLE
 BEGIN
@@ -29,7 +29,7 @@
 
 IDI_CHROME_REMOTE_DESKTOP      ICON    "remoting/resources/chromoting.ico"
 
-#endif  // (BINARY == BINARY_CORE)
+#endif  // (REMOTING_HOST_BINARY == BINARY_CORE)
 
 IDD_DISCONNECT DIALOGEX 0, 0, 145, 24
 STYLE DS_SETFONT | WS_POPUP
diff --git a/remoting/host/win/version.rc.jinja2 b/remoting/host/win/version.rc.jinja2
index 224f966a..f9c77c7 100644
--- a/remoting/host/win/version.rc.jinja2
+++ b/remoting/host/win/version.rc.jinja2
@@ -29,7 +29,7 @@
  FILEFLAGS 0x0L
 #endif
  FILEOS 0x4L
-#if (BINARY == BINARY_CORE)
+#if (REMOTING_HOST_BINARY == BINARY_CORE)
  FILETYPE VFT_DLL
 #else
  FILETYPE VFT_APP
@@ -47,41 +47,41 @@
       VALUE "ProductVersion", "{{ MAJOR }}.{{ REMOTING_PATCH }}.{{ BUILD }}.{{ PATCH }}"
       VALUE "LastChange", "{{ LASTCHANGE }}"
       VALUE "Official Build", "{{ official_build }}"
-#if (BINARY == BINARY_CORE)
+#if (REMOTING_HOST_BINARY == BINARY_CORE)
       VALUE "FileDescription", "{% trans %}REMOTING_CORE_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remoting_core.dll"
       VALUE "OriginalFilename", "remoting_core.dll"
-#elif (BINARY == BINARY_DESKTOP)
+#elif (REMOTING_HOST_BINARY == BINARY_DESKTOP)
       VALUE "FileDescription", "{% trans %}REMOTING_DESKTOP_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remoting_desktop.exe"
       VALUE "OriginalFilename", "remoting_desktop.exe"
-#elif (BINARY == BINARY_HOST_ME2ME)
+#elif (REMOTING_HOST_BINARY == BINARY_HOST_ME2ME)
       VALUE "FileDescription", "{% trans %}REMOTING_HOST_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remoting_host.exe"
       VALUE "OriginalFilename", "remoting_host.exe"
-#elif (BINARY == BINARY_NATIVE_MESSAGING_HOST)
+#elif (REMOTING_HOST_BINARY == BINARY_NATIVE_MESSAGING_HOST)
       VALUE "FileDescription", "{% trans %}REMOTING_NATIVE_MESSAGING_HOST_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remoting_native_messaging_host.exe"
       VALUE "OriginalFilename", "remoting_native_messaging_host.exe"
-#elif (BINARY == BINARY_REMOTE_ASSISTANCE_HOST)
+#elif (REMOTING_HOST_BINARY == BINARY_REMOTE_ASSISTANCE_HOST)
       VALUE "FileDescription", "{% trans %}REMOTING_REMOTE_ASSISTANCE_HOST_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remote_assistance_host.exe"
       VALUE "OriginalFilename", "remote_assistance_host.exe"
-#elif (BINARY == BINARY_HOST_IT2ME_UIACCESS)
+#elif (REMOTING_HOST_BINARY == BINARY_HOST_IT2ME_UIACCESS)
       // Intentionally reuse the description for 'BINARY_REMOTE_ASSISTANCE_HOST'
       VALUE "FileDescription", "{% trans %}REMOTING_REMOTE_ASSISTANCE_HOST_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remote_assistance_host_uiaccess.exe"
       VALUE "OriginalFilename", "remote_assistance_host_uiaccess.exe"
-#elif (BINARY == BINARY_REMOTE_SECURITY_KEY)
+#elif (REMOTING_HOST_BINARY == BINARY_REMOTE_SECURITY_KEY)
       VALUE "FileDescription", "{% trans %}REMOTING_REMOTE_SECURITY_KEY_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remote_security_key.exe"
       VALUE "OriginalFilename", "remote_security_key.exe"
-#elif (BINARY == BINARY_REMOTING_START_HOST)
+#elif (REMOTING_HOST_BINARY == BINARY_REMOTING_START_HOST)
       VALUE "FileDescription", "{% trans %}REMOTING_START_HOST_DESCRIPTION{% endtrans %}"
       VALUE "InternalName", "remoting_start_host.exe"
       VALUE "OriginalFilename", "remoting_start_host.exe"
 #else
-#error BINARY must be set to one of BINARY_XXX values.
+#error REMOTING_HOST_BINARY must be set to one of BINARY_XXX values.
 #endif
     END
   END
diff --git a/remoting/test/host_list_fetcher.cc b/remoting/test/host_list_fetcher.cc
index 1b5dc2a..09fc3d5 100644
--- a/remoting/test/host_list_fetcher.cc
+++ b/remoting/test/host_list_fetcher.cc
@@ -62,7 +62,7 @@
   std::unique_ptr<base::Value> response_value(
       base::JSONReader::Read(response_string));
   if (!response_value ||
-      !response_value->IsType(base::Value::TYPE_DICTIONARY)) {
+      !response_value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Failed to parse response string to JSON";
     return false;
   }
diff --git a/remoting/test/remote_host_info_fetcher.cc b/remoting/test/remote_host_info_fetcher.cc
index 30ba6fa4..c7a0bc47 100644
--- a/remoting/test/remote_host_info_fetcher.cc
+++ b/remoting/test/remote_host_info_fetcher.cc
@@ -90,7 +90,7 @@
   std::unique_ptr<base::Value> response_value(
       base::JSONReader::Read(response_string));
   if (!response_value ||
-      !response_value->IsType(base::Value::TYPE_DICTIONARY)) {
+      !response_value->IsType(base::Value::Type::DICTIONARY)) {
     LOG(ERROR) << "Failed to parse response string to JSON";
     base::ResetAndReturn(&remote_host_info_callback_).Run(remote_host_info);
     return;
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.h b/rlz/chromeos/lib/rlz_value_store_chromeos.h
index 418b83e1..ec26248 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.h
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.h
@@ -17,7 +17,6 @@
 #include "rlz/lib/rlz_value_store.h"
 
 namespace base {
-class ListValue;
 class SequencedTaskRunner;
 class Value;
 }
diff --git a/sandbox/BUILD.gn b/sandbox/BUILD.gn
index 8ca3574..8c0405e 100644
--- a/sandbox/BUILD.gn
+++ b/sandbox/BUILD.gn
@@ -2,6 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//sandbox/features.gni")
+
 # Meta-target that forwards to the proper platform one.
 group("sandbox") {
   if (is_win) {
@@ -19,3 +22,8 @@
     ]
   }
 }
+
+buildflag_header("sandbox_features") {
+  header = "sandbox_features.h"
+  flags = [ "USE_SECCOMP_BPF=$use_seccomp_bpf" ]
+}
diff --git a/sandbox/features.gni b/sandbox/features.gni
new file mode 100644
index 0000000..aa18c04f1
--- /dev/null
+++ b/sandbox/features.gni
@@ -0,0 +1,16 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/nacl/config.gni")
+
+# The seccomp-bpf sandbox is only supported on five architectures
+# currently.
+# Do not disable seccomp_bpf anywhere without talking to
+# security@chromium.org!
+use_seccomp_bpf =
+    (is_linux || is_android) &&
+    (current_cpu == "x86" || current_cpu == "x64" || current_cpu == "arm" ||
+     current_cpu == "arm64" || current_cpu == "mipsel")
+
+use_seccomp_bpf = use_seccomp_bpf || is_nacl_nonsfi
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 1e6d7a1c..3e98def 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//build/config/nacl/config.gni")
+import("//sandbox/features.gni")
 import("//testing/test.gni")
 
 if (is_android) {
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
index 7f81344..6f0dd4e 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -76,6 +76,11 @@
 namespace sandbox {
 namespace bpf_dsl {
 
+template <typename T>
+class Caser;
+
+class Elser;
+
 // ResultExpr is an opaque reference to an immutable result expression tree.
 using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
 
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
index 10477c9..af1b48b 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
@@ -24,14 +24,6 @@
 using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
 using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>;
 
-template <typename T>
-class Arg;
-
-class Elser;
-
-template <typename T>
-class Caser;
-
 }  // namespace bpf_dsl
 }  // namespace sandbox
 
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
index 35ff64f4..f397321 100644
--- a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -13,7 +13,6 @@
 
 namespace sandbox {
 namespace bpf_dsl {
-class ErrorCode;
 class PolicyCompiler;
 
 namespace internal {
diff --git a/sandbox/win/src/broker_services.h b/sandbox/win/src/broker_services.h
index 6aad976b..919478c 100644
--- a/sandbox/win/src/broker_services.h
+++ b/sandbox/win/src/broker_services.h
@@ -29,8 +29,6 @@
 
 namespace sandbox {
 
-class PolicyBase;
-
 // BrokerServicesBase ---------------------------------------------------------
 // Broker implementation version 0
 //
diff --git a/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc
index 72e2780c..d163cfb 100644
--- a/sandbox/win/src/target_process.cc
+++ b/sandbox/win/src/target_process.cc
@@ -63,30 +63,17 @@
       base_address_(NULL) {}
 
 TargetProcess::~TargetProcess() {
-  DWORD exit_code = 0;
   // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
   // will take effect only when the context changes. As far as the testing went,
   // this wait was enough to switch context and kill the processes in the job.
   // If this process is already dead, the function will return without waiting.
-  // TODO(nsylvain):  If the process is still alive at the end, we should kill
-  // it. http://b/893891
   // For now, this wait is there only to do a best effort to prevent some leaks
   // from showing up in purify.
   if (sandbox_process_info_.IsValid()) {
     ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
-    // At this point, the target process should have been killed.  Check.
-    if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
-                              &exit_code) || (STILL_ACTIVE == exit_code)) {
-      // Something went wrong.  We don't know if the target is in a state where
-      // it can manage to do another IPC call.  If it can, and we've destroyed
-      // the |ipc_server_|, it will crash the broker.  So we intentionally leak
-      // that.
-      if (shared_section_.IsValid())
-        shared_section_.Take();
-      ignore_result(ipc_server_.release());
-      sandbox_process_info_.TakeProcessHandle();
-      return;
-    }
+    // Terminate the process if it's still alive, as its IPC server is going
+    // away. 1 is RESULT_CODE_KILLED.
+    ::TerminateProcess(sandbox_process_info_.process_handle(), 1);
   }
 
   // ipc_server_ references our process handle, so make sure the former is shut
diff --git a/sandbox/win/src/target_process.h b/sandbox/win/src/target_process.h
index 70b7b32..7d469dec 100644
--- a/sandbox/win/src/target_process.h
+++ b/sandbox/win/src/target_process.h
@@ -28,7 +28,6 @@
 
 namespace sandbox {
 
-class AttributeList;
 class SharedMemIPCServer;
 class ThreadProvider;
 
diff --git a/services/catalog/public/cpp/resource_loader.cc b/services/catalog/public/cpp/resource_loader.cc
index 4eb8c63..c18a709e 100644
--- a/services/catalog/public/cpp/resource_loader.cc
+++ b/services/catalog/public/cpp/resource_loader.cc
@@ -16,18 +16,6 @@
 
 namespace catalog {
 
-namespace {
-
-base::File GetFileFromHandle(mojo::ScopedHandle handle) {
-  CHECK(handle.is_valid());
-  base::PlatformFile platform_file;
-  CHECK_EQ(mojo::UnwrapPlatformFile(std::move(handle), &platform_file),
-           MOJO_RESULT_OK);
-  return base::File(platform_file);
-}
-
-}  // namespace
-
 ResourceLoader::ResourceLoader() {}
 ResourceLoader::~ResourceLoader() {}
 
@@ -50,7 +38,7 @@
 
   for (const auto& result : results) {
     resource_map_[result->path].reset(
-        new base::File(GetFileFromHandle(std::move(result->file_handle))));
+        new base::File(std::move(result->file_handle)));
   }
   return true;
 }
diff --git a/services/catalog/public/interfaces/catalog.mojom b/services/catalog/public/interfaces/catalog.mojom
index ccb9e3b..5977c249 100644
--- a/services/catalog/public/interfaces/catalog.mojom
+++ b/services/catalog/public/interfaces/catalog.mojom
@@ -4,7 +4,7 @@
 
 module catalog.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file_path.mojom";
 
 struct Entry {
   string name;
diff --git a/services/preferences/public/cpp/pref_observer_store.cc b/services/preferences/public/cpp/pref_observer_store.cc
index 5a5f9cb..7bf6f7f 100644
--- a/services/preferences/public/cpp/pref_observer_store.cc
+++ b/services/preferences/public/cpp/pref_observer_store.cc
@@ -5,7 +5,6 @@
 #include "services/preferences/public/cpp/pref_observer_store.h"
 
 #include "base/values.h"
-#include "mojo/common/common_custom_types.mojom.h"
 #include "mojo/public/cpp/bindings/array.h"
 #include "services/service_manager/public/cpp/connector.h"
 
diff --git a/services/preferences/public/cpp/pref_observer_store.h b/services/preferences/public/cpp/pref_observer_store.h
index 1e408e1..c8af343 100644
--- a/services/preferences/public/cpp/pref_observer_store.h
+++ b/services/preferences/public/cpp/pref_observer_store.h
@@ -9,7 +9,6 @@
 
 #include "base/macros.h"
 #include "components/prefs/value_map_pref_store.h"
-#include "mojo/common/common_custom_types.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/preferences/public/interfaces/preferences.mojom.h"
 
diff --git a/services/preferences/public/cpp/tests/pref_observer_store_unittest.cc b/services/preferences/public/cpp/tests/pref_observer_store_unittest.cc
index b1fcb04..d18cfbe7 100644
--- a/services/preferences/public/cpp/tests/pref_observer_store_unittest.cc
+++ b/services/preferences/public/cpp/tests/pref_observer_store_unittest.cc
@@ -267,7 +267,7 @@
   // Change an item within the nested dictionary
   base::Value* result = nullptr;
   store()->GetMutableValue(key1, &result);
-  EXPECT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, result->GetType());
   EXPECT_TRUE(result->Equals(&sub_dictionary1));
 
   base::DictionaryValue* dictionary_result = nullptr;
diff --git a/services/preferences/public/interfaces/preferences.mojom b/services/preferences/public/interfaces/preferences.mojom
index bf8d186b..0827850 100644
--- a/services/preferences/public/interfaces/preferences.mojom
+++ b/services/preferences/public/interfaces/preferences.mojom
@@ -4,7 +4,7 @@
 
 module prefs.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/values.mojom";
 
 // Used to subscribe to preference changes within PreferenceManager. After
 // requesting to observe, the current values for all requested keys are sent.
diff --git a/services/service_manager/public/interfaces/resolver.mojom b/services/service_manager/public/interfaces/resolver.mojom
index 5ff16cb..b6b0481 100644
--- a/services/service_manager/public/interfaces/resolver.mojom
+++ b/services/service_manager/public/interfaces/resolver.mojom
@@ -4,7 +4,7 @@
 
 module service_manager.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/file_path.mojom";
 import "services/service_manager/public/interfaces/interface_provider_spec.mojom";
 
 // The result of a Resolve operation via Resolver.
diff --git a/services/ui/common/BUILD.gn b/services/ui/common/BUILD.gn
index 2dea4b7f..8204e1f 100644
--- a/services/ui/common/BUILD.gn
+++ b/services/ui/common/BUILD.gn
@@ -51,6 +51,7 @@
   public_deps = [
     "//gpu/command_buffer/client",
     "//gpu/ipc/common",
+    "//gpu/ipc/host",
     "//services/ui/gpu/interfaces",
     "//ui/gfx",
   ]
diff --git a/services/ui/common/DEPS b/services/ui/common/DEPS
index f30e8bd..ce7a603 100644
--- a/services/ui/common/DEPS
+++ b/services/ui/common/DEPS
@@ -3,6 +3,7 @@
   "+gpu/command_buffer",
   "+gpu/ipc/client",
   "+gpu/ipc/common",
+  "+gpu/ipc/host",
   "+services/ui/gpu/interfaces",
 ]
 
diff --git a/services/ui/common/OWNERS b/services/ui/common/OWNERS
index a166098..12dcf13 100644
--- a/services/ui/common/OWNERS
+++ b/services/ui/common/OWNERS
@@ -1,2 +1,4 @@
 per-file *_type_converter*.*=set noparent
 per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
+per-file *gpu*.*=fsamuel@chromium.org
+per-file *gpu*.*=sadrul@chromium.org
diff --git a/services/ui/common/mus_gpu_memory_buffer_manager.cc b/services/ui/common/mus_gpu_memory_buffer_manager.cc
index eda1712..4e3b63a8 100644
--- a/services/ui/common/mus_gpu_memory_buffer_manager.cc
+++ b/services/ui/common/mus_gpu_memory_buffer_manager.cc
@@ -15,38 +15,56 @@
 MusGpuMemoryBufferManager::MusGpuMemoryBufferManager(
     mojom::GpuServiceInternal* gpu_service,
     int client_id)
-    : gpu_service_(gpu_service), client_id_(client_id), weak_factory_(this) {}
+    : gpu_service_(gpu_service),
+      client_id_(client_id),
+      native_configurations_(gpu::GetNativeGpuMemoryBufferConfigurations()),
+      weak_factory_(this) {}
 
 MusGpuMemoryBufferManager::~MusGpuMemoryBufferManager() {}
 
+gfx::GpuMemoryBufferHandle
+MusGpuMemoryBufferManager::CreateGpuMemoryBufferHandle(
+    gfx::GpuMemoryBufferId id,
+    int client_id,
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gpu::SurfaceHandle surface_handle) {
+  DCHECK(CalledOnValidThread());
+  if (gpu::GetNativeGpuMemoryBufferType() != gfx::EMPTY_BUFFER) {
+    const bool is_native = native_configurations_.find(std::make_pair(
+                               format, usage)) != native_configurations_.end();
+    if (is_native) {
+      gfx::GpuMemoryBufferHandle handle;
+      gpu_service_->CreateGpuMemoryBuffer(id, size, format, usage, client_id,
+                                          surface_handle, &handle);
+      if (!handle.is_null())
+        native_buffers_[client_id].insert(handle.id);
+      return handle;
+    }
+  }
+
+  DCHECK(gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage))
+      << static_cast<int>(usage);
+  return gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(id, size,
+                                                                     format);
+}
+
 std::unique_ptr<gfx::GpuMemoryBuffer>
 MusGpuMemoryBufferManager::CreateGpuMemoryBuffer(
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     gpu::SurfaceHandle surface_handle) {
-  DCHECK(CalledOnValidThread());
   gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
-  const bool is_native =
-      gpu::IsNativeGpuMemoryBufferConfigurationSupported(format, usage);
-  if (is_native) {
-    gfx::GpuMemoryBufferHandle handle;
-    gpu_service_->CreateGpuMemoryBuffer(id, size, format, usage, client_id_,
-                                        surface_handle, &handle);
-    if (handle.is_null())
-      return nullptr;
-    return gpu::GpuMemoryBufferImpl::CreateFromHandle(
-        handle, size, format, usage,
-        base::Bind(&MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
-                   weak_factory_.GetWeakPtr(), id, client_id_, is_native));
-  }
-
-  DCHECK(gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage))
-      << static_cast<int>(usage);
-  return gpu::GpuMemoryBufferImplSharedMemory::Create(
-      id, size, format,
+  gfx::GpuMemoryBufferHandle handle = CreateGpuMemoryBufferHandle(
+      id, client_id_, size, format, usage, surface_handle);
+  if (handle.is_null())
+    return nullptr;
+  return gpu::GpuMemoryBufferImpl::CreateFromHandle(
+      handle, size, format, usage,
       base::Bind(&MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
-                 weak_factory_.GetWeakPtr(), id, client_id_, is_native));
+                 weak_factory_.GetWeakPtr(), id, client_id_));
 }
 
 std::unique_ptr<gfx::GpuMemoryBuffer>
@@ -69,12 +87,18 @@
 void MusGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
     gfx::GpuMemoryBufferId id,
     int client_id,
-    bool is_native,
     const gpu::SyncToken& sync_token) {
   DCHECK(CalledOnValidThread());
-  if (is_native) {
+  if (native_buffers_[client_id].erase(id))
     gpu_service_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
-  }
+}
+
+void MusGpuMemoryBufferManager::DestroyAllGpuMemoryBufferForClient(
+    int client_id) {
+  DCHECK(CalledOnValidThread());
+  for (gfx::GpuMemoryBufferId id : native_buffers_[client_id])
+    gpu_service_->DestroyGpuMemoryBuffer(id, client_id, gpu::SyncToken());
+  native_buffers_.erase(client_id);
 }
 
 }  // namespace ui
diff --git a/services/ui/common/mus_gpu_memory_buffer_manager.h b/services/ui/common/mus_gpu_memory_buffer_manager.h
index 768e1c9..bf46681a 100644
--- a/services/ui/common/mus_gpu_memory_buffer_manager.h
+++ b/services/ui/common/mus_gpu_memory_buffer_manager.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
 #include "services/ui/gpu/interfaces/gpu_service_internal.mojom.h"
 
 namespace ui {
@@ -24,6 +25,20 @@
                             int client_id);
   ~MusGpuMemoryBufferManager() override;
 
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              int client_id,
+                              const gpu::SyncToken& sync_token);
+
+  void DestroyAllGpuMemoryBufferForClient(int client_id);
+
+  gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle(
+      gfx::GpuMemoryBufferId id,
+      int client_id,
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      gpu::SurfaceHandle surface_handle);
+
   // Overridden from gpu::GpuMemoryBufferManager:
   std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
       const gfx::Size& size,
@@ -38,15 +53,16 @@
                                const gpu::SyncToken& sync_token) override;
 
  private:
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                              int client_id,
-                              bool is_native,
-                              const gpu::SyncToken& sync_token);
-
   mojom::GpuServiceInternal* gpu_service_;
-
   const int client_id_;
   int next_gpu_memory_id_ = 1;
+
+  using NativeBuffers =
+      std::unordered_set<gfx::GpuMemoryBufferId,
+                         BASE_HASH_NAMESPACE::hash<gfx::GpuMemoryBufferId>>;
+  std::unordered_map<int, NativeBuffers> native_buffers_;
+
+  const gpu::GpuMemoryBufferConfigurationSet native_configurations_;
   base::WeakPtrFactory<MusGpuMemoryBufferManager> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MusGpuMemoryBufferManager);
diff --git a/services/ui/demo/manifest.json b/services/ui/demo/manifest.json
index 473dbc0..6be98f75 100644
--- a/services/ui/demo/manifest.json
+++ b/services/ui/demo/manifest.json
@@ -4,7 +4,7 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
-        "ui": [ "app", "ui:window_manager" ]
+        "ui": [ "window_manager" ]
       }
     }
   }
diff --git a/services/ui/ime/test_ime_driver/manifest.json b/services/ui/ime/test_ime_driver/manifest.json
index 7e980eb..d4dd447e 100644
--- a/services/ui/ime/test_ime_driver/manifest.json
+++ b/services/ui/ime/test_ime_driver/manifest.json
@@ -5,7 +5,7 @@
     "service_manager:connector": {
       "provides": { "ime:test_ime_driver": [] },
       "requires": {
-        "ui": [ "ui:ime_registrar" ]
+        "ui": [ "ime_registrar" ]
       }
     }
   }
diff --git a/services/ui/manifest.json b/services/ui/manifest.json
index dd579d3d..3e5b988 100644
--- a/services/ui/manifest.json
+++ b/services/ui/manifest.json
@@ -7,7 +7,7 @@
         // A collection of interfaces needed by a generic client of mus.
         // Additional interfaces may be requested a-la-carte.
         // NOTE: when adding a new interface chances are you will want to add it
-        // to ui:window_manager as well.
+        // to window_manager as well.
         "app": [
           "ui::mojom::Clipboard",
           "ui::mojom::DisplayCompositor",
@@ -25,17 +25,17 @@
         "test": [
           "ui::mojom::WindowServerTest"
         ],
-        "ui:gpu_client": [
+        "gpu_client": [
           "ui::mojom::Gpu",
           "ui::mojom::GpuService"
         ],
-        "ui:ime_registrar": [
+        "ime_registrar": [
           "ui::mojom::IMERegistrar"
         ],
-        "ui:user_access_manager": [
+        "user_access_manager": [
           "ui::mojom::UserAccessManager"
         ],
-        "ui:window_manager": [
+        "window_manager": [
           "display::mojom::DisplayController",
           "display::mojom::TestDisplayController",
           "ui::mojom::AccessibilityManager",
@@ -48,7 +48,7 @@
           "ui::mojom::InputDeviceServer",
           "ui::mojom::WindowManagerWindowTreeFactory"
         ],
-        "ui:window_tree_host_factory": [
+        "window_tree_host_factory": [
           "ui::mojom::WindowTreeHostFactory"
         ]
       },
diff --git a/services/ui/public/interfaces/BUILD.gn b/services/ui/public/interfaces/BUILD.gn
index 51c6e79..0e4e5b4 100644
--- a/services/ui/public/interfaces/BUILD.gn
+++ b/services/ui/public/interfaces/BUILD.gn
@@ -8,7 +8,6 @@
 mojom("interfaces") {
   sources = [
     "accessibility_manager.mojom",
-    "animations.mojom",
     "clipboard.mojom",
     "cursor.mojom",
     "display_manager.mojom",
diff --git a/services/ui/public/interfaces/animations.mojom b/services/ui/public/interfaces/animations.mojom
deleted file mode 100644
index eae16ea..0000000
--- a/services/ui/public/interfaces/animations.mojom
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ui.mojom;
-
-import "ui/gfx/mojo/transform.mojom";
-
-enum AnimationTweenType {
-  LINEAR,
-  EASE_IN,
-  EASE_OUT,
-  EASE_IN_OUT,
-};
-
-enum AnimationProperty {
-  // Used for pausing.
-  NONE,
-  OPACITY,
-  TRANSFORM,
-};
-
-struct AnimationValue {
-  float float_value;
-  gfx.mojom.Transform? transform;
-};
-
-// Identifies how a particular property should be animated between a start and
-// target value.
-struct AnimationElement {
-  AnimationProperty property;
-
-  // Duration is in microseconds.
-  int64 duration;
-
-  AnimationTweenType tween_type;
-
-  // If not specified the start value is taken from either the current value
-  // (for the first element) or the target_value of the previous element.
-  AnimationValue? start_value;
-
-  // target_value may be null when property is NONE.
-  AnimationValue? target_value;
-};
-
-// An AnimationSequence consists of a number of AnimationElements to animate.
-// Each element is animated serially.
-struct AnimationSequence {
-  // Number of times to run the sequence. Value of 0 means run until
-  // explicitly stopped.
-  uint32 cycle_count;
-
-  array<AnimationElement> elements;
-};
-
-// AnimationGroup identifies a window and a set of AnimationSequences to apply
-// to the window. Each sequence is run in parallel.
-struct AnimationGroup {
-  uint32 window_id;
-  array<AnimationSequence> sequences;
-};
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index f0e6b481..4268f21 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -23,65 +23,119 @@
 // with WindowTreeClient, further WindowTree requests this interface from
 // WindowTreeClient supplied at the time the WindowTreeHost is created.
 interface WindowManager {
-  // Whether the window is always on top. Type: bool.
-  const string kAlwaysOnTop_Property = "prop:always_on_top";
-  // The application icon; typically larger for shelf icons, etc. Type: SkBitmap
-  const string kAppIcon_Property = "prop:app-icon";
+  // WindowManager defines two sets of distinct properties.
+  // . Properties that are used at creation time only and not persisted.
+  // . Long lived properties. These properties may be changed at any time and
+  //   are mapped to aura::Window properties. aura::PropertyConverter defines
+  //   the mapping between the property defined here and the corresonding
+  //   aura property. For properties defined in PropertyConverter any change to
+  //   to the aura property is mapped to the property defined here and sent to
+  //   all clients that know about the window (which is generally just the owner
+  //   and the window-manager).
+
+  // Properties used only during creation time. --------------------------------
+
+  // Initial bounds to create the window at. If empty the WindowManager decides
+  // the initial bounds. Type: gfx::Rect.
+  const string kBounds_InitProperty = "init:bounds";
+
+  // The window manager will place the window in this container when the window
+  // is created. If not set a container is selected based on the window type.
+  // Type: int32_t.
+  const string kContainerId_InitProperty = "init:container_id";
+
   // Disables the window manager from handling immersive fullscreen for the
   // window. This is typically done if the client wants to handle immersive
   // themselves. Type: bool.
-  const string kDisableImmersive_Property = "prop:disable_immersive";
-  // Used to explicitly control whether a window appears in the most recently
-  // used list of windows. Type: bool.
-  const string kExcludeFromMru_Property = "prop:exclude_from_mru";
-  // Initial bounds to create the window at. If empty the WindowManager decides
-  // the initial bounds.
-  const string kInitialBounds_Property = "prop:initial_bounds";
-  // The window manager will place the window in this container when the window
-  // is created. If not set a container is selected based on the window type.
-  // Type: int
-  const string kInitialContainerId_Property = "prop:initial_container_id";
+  const string kDisableImmersive_InitProperty = "init:disable_immersive";
+
   // The id of the display (display::Display::id()) to create the window on.
   // Type: int64.
-  const string kInitialDisplayId_Property = "prop:initial_display_id";
-  // Internal window name. Useful for debugging. Type: mojom::String
+  const string kDisplayId_InitProperty = "init:display_id";
+
+  // Specifies that the system default caption and icon should not be rendered,
+  // and the client area should be equivalent to the window area. Type: bool
+  const string kRemoveStandardFrame_InitProperty = "init:remove-standard-frame";
+
+  // The window type. This maps to aura::client::kWindowTypeKey as well as
+  // Window::type(). This mapping is only done for top-level windows that are
+  // created by the window manager at the request of a client.
+  // Type: mojom::WindowType (int32_t).
+  const string kWindowType_InitProperty = "init:window-type";
+
+  // End properties used only during creation time. ----------------------------
+
+  // Long lived properties. ----------------------------------------------------
+
+  // All primitive properties are transported as
+  // aura::PropertyConverter::PrimitiveType.
+
+  // Whether the window is always on top. Maps to aura::client::kAlwaysOnTopKey.
+  // Type: bool.
+  const string kAlwaysOnTop_Property = "prop:always_on_top";
+
+  // The application icon; typically larger for shelf icons, etc. Type: SkBitmap
+  // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522?
+  const string kAppIcon_Property = "prop:app-icon";
+
+  // The application ID (eg. 'mojo:foo'). Maps to
+  // aura::client::client::kAppIdKey. Type: mojom::String
+  const string kAppID_Property = "prop:app-id";
+
+  // Used to explicitly control whether a window appears in the most recently
+  // used list of windows. Maps to aura::client::kExcludeFromMruKey. Type: bool.
+  const string kExcludeFromMru_Property = "prop:exclude_from_mru";
+
+  // Internal window name. Useful for debugging. Maps to aura::client::kNameKey.
+  // Type: mojom::String
   const string kName_Property = "prop:name";
-  // The window's preferred size as defined by its content. Type: gfx::Size.
+
+  // The window's preferred size as defined by its content. Maps to
+  // aura::client::kPreferredSize_Property. Type: gfx::Size.
   const string kPreferredSize_Property = "prop:preferred-size";
+
   // If true the window manager renders the title area (including frame
   // decorations) of the parent window in this window. This is only checked
   // at the time the window is added to its parent, which must be a top level
-  // window (created by way of WindowTree::NewTopLevelWindow()). Type: bool.
-  const string kRendererParentTitleArea_Property =
+  // window (created by way of WindowTree::NewTopLevelWindow()). This is not
+  // mapped by default, it's up to the window manager (such as ash) to decide
+  // how to handle this. Type: bool.
+  const string kRenderParentTitleArea_Property =
       "render-parent-non-client-area";
-  // The window's resize behavior. Type: ResizeBehavior.
+
+  // The window's resize behavior. Maps to aura::client::kResizeBehaviorKey.
+  // Type: ResizeBehavior.
   const string kResizeBehavior_Property = "prop:resize-behavior";
-  // Bounds the window is restored to. Type: gfx::Rect.
+
+  // Bounds the window is restored to. Maps to client::kRestoreBoundsKey.
+  // Type: gfx::Rect.
   const string kRestoreBounds_Property = "prop:restore-bounds";
-  // Shadow style for the window. Type: mojom::ShadowStyle.
-  const string kShadowStyle_Property = "prop:shadow-style";
-  // The ID of the shelf item corresponding to this window. Type: int
-  const string kShelfId_Property = "prop:shelf-id";
+
   // The type of item to be shown on the shelf for this window. Type: int
   // A few ash::ShelfItemType values are supported; TYPE_UNKNOWN means no item.
   const string kShelfItemType_Property = "prop:shelf-item-type";
-  // The window's show state. Type: ShowState.
+
+  // The window's show state. Maps to aura::client::kShowStateKey.
+  // Type: ShowState.
   const string kShowState_Property = "prop:show-state";
-  // The window bounds as set by user input. Type: gfx::Rect.
-  const string kUserSetBounds_Property = "prop:user-set-bounds";
+
   // The window icon; typically 16x16 for titlebars. Type: SkBitmap
+  // TODO(msw): map to aura::client::kAppIconKey http://crbug.com/663522?
   const string kWindowIcon_Property = "prop:window-icon";
-  // The window type. Type: mojom::WindowType
-  const string kWindowType_Property = "prop:window-type";
-  // The window's title. Type: mojom::String
-  const string kWindowTitle_Property = "prop:window-title";
+
   // A flag controlling the window's presence on the mash shelf. Type: bool
+  // TODO: convert this to a aura::client property; http://crbug.com/671729.
   const string kWindowIgnoredByShelf_Property = "prop:window-ignored-by-shelf";
-  // The application ID (eg. 'mojo:foo'). Type: mojom::String
-  const string kAppID_Property = "prop:app-id";
-  // Specifies that the system default caption and icon should not be rendered,
-  // and the client area should be equivalent to the window area. Type: bool
-  const string kRemoveStandardFrame_Property = "prop:remove-standard-frame";
+
+  // The window's title. Maps to aura::client::kTitleKey. Type: mojom::String
+  const string kWindowTitle_Property = "prop:window-title";
+
+  // End long lived properties. ------------------------------------------------
+
+  // Shadow style for the window. Type: mojom::ShadowStyle.
+  // TODO(sky): this is currently unused, either wire up or nuke:
+  // http://crbug.com/671714.
+  const string kShadowStyle_Property = "prop:shadow-style";
 
   // Called immediately when the WindowManager is obtained.
   OnConnect(uint16 client_id);
diff --git a/services/ui/surfaces/BUILD.gn b/services/ui/surfaces/BUILD.gn
index a0462c92..80a82be 100644
--- a/services/ui/surfaces/BUILD.gn
+++ b/services/ui/surfaces/BUILD.gn
@@ -19,21 +19,13 @@
     "//cc",
     "//cc/ipc:internal_interfaces",
     "//cc/surfaces",
-    "//cc/surfaces:surface_id",
     "//components/display_compositor",
     "//gpu/command_buffer/client",
-    "//gpu/command_buffer/client:gles2_cmd_helper",
-    "//gpu/command_buffer/client:gles2_implementation",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/ipc:command_buffer",
-    "//gpu/ipc/client",
-    "//gpu/ipc/common",
-    "//services/service_manager/public/cpp",
-    "//services/tracing/public/cpp",
     "//ui/display/types",
     "//ui/gfx",
     "//ui/gfx/geometry/mojo",
-    "//ui/gl",
   ]
 
   if (use_ozone) {
@@ -41,6 +33,9 @@
       "display_output_surface_ozone.cc",
       "display_output_surface_ozone.h",
     ]
-    deps += [ "//gpu/command_buffer/common" ]
+    deps += [
+      "//gpu/command_buffer/common",
+      "//ui/gl",
+    ]
   }
 }
diff --git a/services/ui/surfaces/display_compositor.cc b/services/ui/surfaces/display_compositor.cc
index 6eb46656..4c4f85a 100644
--- a/services/ui/surfaces/display_compositor.cc
+++ b/services/ui/surfaces/display_compositor.cc
@@ -39,6 +39,57 @@
       client_(std::move(client)),
       binding_(this, std::move(request)) {
   manager_.AddObserver(this);
+  if (client_)
+    client_->OnDisplayCompositorCreated(GetRootSurfaceId());
+}
+
+void DisplayCompositor::AddSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  for (auto& reference : references)
+    AddSurfaceReference(reference);
+}
+
+void DisplayCompositor::RemoveSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // TODO(kylechar): Each remove reference can trigger GC, it would be better if
+  // we GC only once if removing multiple references.
+  for (auto& reference : references)
+    RemoveSurfaceReference(reference);
+}
+
+DisplayCompositor::~DisplayCompositor() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Remove all temporary references on shutdown.
+  for (auto& map_entry : temp_references_) {
+    const cc::FrameSinkId& frame_sink_id = map_entry.first;
+    for (auto& local_frame_id : map_entry.second) {
+      reference_manager_->RemoveSurfaceReference(
+          GetRootSurfaceId(), cc::SurfaceId(frame_sink_id, local_frame_id));
+    }
+  }
+  manager_.RemoveObserver(this);
+}
+
+void DisplayCompositor::OnCompositorFrameSinkClientConnectionLost(
+    const cc::FrameSinkId& frame_sink_id,
+    bool destroy_compositor_frame_sink) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (destroy_compositor_frame_sink)
+    compositor_frame_sinks_.erase(frame_sink_id);
+  // TODO(fsamuel): Tell the display compositor host that the client connection
+  // has been lost so that it can drop its private connection and allow a new
+  // client instance to create a new CompositorFrameSink.
+}
+
+void DisplayCompositor::OnCompositorFrameSinkPrivateConnectionLost(
+    const cc::FrameSinkId& frame_sink_id,
+    bool destroy_compositor_frame_sink) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (destroy_compositor_frame_sink)
+    compositor_frame_sinks_.erase(frame_sink_id);
 }
 
 void DisplayCompositor::CreateCompositorFrameSink(
@@ -62,14 +113,10 @@
           std::move(private_request), std::move(client));
 }
 
-void DisplayCompositor::AddRootSurfaceReference(const cc::SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  AddSurfaceReference(GetRootSurfaceId(), child_id);
-}
+void DisplayCompositor::AddSurfaceReference(const cc::SurfaceReference& ref) {
+  const cc::SurfaceId& parent_id = ref.parent_id();
+  const cc::SurfaceId& child_id = ref.child_id();
 
-void DisplayCompositor::AddSurfaceReference(const cc::SurfaceId& parent_id,
-                                            const cc::SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   auto vector_iter = temp_references_.find(child_id.frame_sink_id());
 
   // If there are no temporary references for the FrameSinkId then we can just
@@ -118,50 +165,9 @@
     refs.erase(refs.begin(), ++temp_ref_iter);
 }
 
-void DisplayCompositor::RemoveRootSurfaceReference(
-    const cc::SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  RemoveSurfaceReference(GetRootSurfaceId(), child_id);
-}
-
-void DisplayCompositor::RemoveSurfaceReference(const cc::SurfaceId& parent_id,
-                                               const cc::SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  // TODO(kylechar): Each remove reference can trigger GC, it would be better if
-  // we GC only once if removing multiple references.
-  reference_manager_->RemoveSurfaceReference(parent_id, child_id);
-}
-
-DisplayCompositor::~DisplayCompositor() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  // Remove all temporary references on shutdown.
-  for (auto& map_entry : temp_references_) {
-    const cc::FrameSinkId& frame_sink_id = map_entry.first;
-    for (auto& local_frame_id : map_entry.second) {
-      reference_manager_->RemoveSurfaceReference(
-          GetRootSurfaceId(), cc::SurfaceId(frame_sink_id, local_frame_id));
-    }
-  }
-  manager_.RemoveObserver(this);
-}
-
-void DisplayCompositor::OnCompositorFrameSinkClientConnectionLost(
-    const cc::FrameSinkId& frame_sink_id,
-    bool destroy_compositor_frame_sink) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (destroy_compositor_frame_sink)
-    compositor_frame_sinks_.erase(frame_sink_id);
-  // TODO(fsamuel): Tell the display compositor host that the client connection
-  // has been lost so that it can drop its private connection and allow a new
-  // client instance to create a new CompositorFrameSink.
-}
-
-void DisplayCompositor::OnCompositorFrameSinkPrivateConnectionLost(
-    const cc::FrameSinkId& frame_sink_id,
-    bool destroy_compositor_frame_sink) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (destroy_compositor_frame_sink)
-    compositor_frame_sinks_.erase(frame_sink_id);
+void DisplayCompositor::RemoveSurfaceReference(
+    const cc::SurfaceReference& ref) {
+  reference_manager_->RemoveSurfaceReference(ref.parent_id(), ref.child_id());
 }
 
 std::unique_ptr<cc::Display> DisplayCompositor::CreateDisplay(
diff --git a/services/ui/surfaces/display_compositor.h b/services/ui/surfaces/display_compositor.h
index 3b29d71..97c89f7 100644
--- a/services/ui/surfaces/display_compositor.h
+++ b/services/ui/surfaces/display_compositor.h
@@ -60,22 +60,17 @@
       cc::mojom::DisplayCompositorClientPtr client);
   ~DisplayCompositor() override;
 
-  // cc::mojom::DisplayCompositor implementation:
-  void CreateCompositorFrameSink(
-      const cc::FrameSinkId& frame_sink_id,
-      gpu::SurfaceHandle surface_handle,
-      cc::mojom::MojoCompositorFrameSinkRequest request,
-      cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
-      cc::mojom::MojoCompositorFrameSinkClientPtr client) override;
-  void AddRootSurfaceReference(const cc::SurfaceId& child_id) override;
-  void AddSurfaceReference(const cc::SurfaceId& parent_id,
-                           const cc::SurfaceId& child_id) override;
-  void RemoveRootSurfaceReference(const cc::SurfaceId& child_id) override;
-  void RemoveSurfaceReference(const cc::SurfaceId& parent_id,
-                              const cc::SurfaceId& child_id) override;
-
   cc::SurfaceManager* manager() { return &manager_; }
 
+  // Adds surface references. For each reference added, this will remove the
+  // temporary reference to the child surface if one exists.
+  void AddSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references);
+
+  // Removes surface references.
+  void RemoveSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references);
+
   // We must avoid destroying a GpuCompositorFrameSink until both the display
   // compositor host and the client drop their connection to avoid getting into
   // a state where surfaces references are inconsistent.
@@ -86,9 +81,20 @@
       const cc::FrameSinkId& frame_sink_id,
       bool destroy_compositor_frame_sink);
 
+  // cc::mojom::DisplayCompositor implementation:
+  void CreateCompositorFrameSink(
+      const cc::FrameSinkId& frame_sink_id,
+      gpu::SurfaceHandle surface_handle,
+      cc::mojom::MojoCompositorFrameSinkRequest request,
+      cc::mojom::MojoCompositorFrameSinkPrivateRequest private_request,
+      cc::mojom::MojoCompositorFrameSinkClientPtr client) override;
+
  private:
   friend class test::DisplayCompositorTest;
 
+  void AddSurfaceReference(const cc::SurfaceReference& ref);
+  void RemoveSurfaceReference(const cc::SurfaceReference& ref);
+
   std::unique_ptr<cc::Display> CreateDisplay(
       const cc::FrameSinkId& frame_sink_id,
       gpu::SurfaceHandle surface_handle);
diff --git a/services/ui/surfaces/display_compositor_unittest.cc b/services/ui/surfaces/display_compositor_unittest.cc
index c1980f8..74a2c2ed 100644
--- a/services/ui/surfaces/display_compositor_unittest.cc
+++ b/services/ui/surfaces/display_compositor_unittest.cc
@@ -16,6 +16,7 @@
 #include "cc/ipc/display_compositor.mojom.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_observer.h"
+#include "cc/surfaces/surface_reference.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/ui/common/task_runner_test_base.h"
@@ -65,15 +66,22 @@
   }
 
   // cc::mojom::DisplayCompositorClient:
+  void OnDisplayCompositorCreated(
+      const cc::SurfaceId& root_surface_id) override {
+    got_root_surface_id_ = true;
+  }
+
   void OnSurfaceCreated(const cc::SurfaceId& surface_id,
                         const gfx::Size& frame_size,
                         float device_scale_factor) override {
+    EXPECT_TRUE(got_root_surface_id_);
     AddEvent(base::StringPrintf("OnSurfaceCreated(%s)",
                                 SurfaceIdString(surface_id).c_str()));
   }
 
   mojo::Binding<cc::mojom::DisplayCompositorClient> binding_;
   std::string events_;
+  bool got_root_surface_id_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestDisplayCompositorClient);
 };
@@ -139,6 +147,16 @@
 
   cc::SurfaceObserver* surface_observer() { return display_compositor_.get(); }
 
+  const cc::SurfaceId& GetRootSurfaceId() const {
+    return reference_manager_.GetRootSurfaceId();
+  }
+
+  void AddSurfaceReference(const cc::SurfaceId& parent_id,
+                           const cc::SurfaceId& child_id) {
+    display_compositor_->AddSurfaceReferences(std::vector<cc::SurfaceReference>{
+        cc::SurfaceReference(parent_id, child_id)});
+  }
+
   // Returns the total number of temporary references held by DisplayCompositor.
   size_t CountTempReferences() {
     size_t size = 0;
@@ -182,7 +200,7 @@
   EXPECT_EQ("Add(0:0:0-2:1:1)", reference_manager_.events());
   EXPECT_EQ(1u, CountTempReferences());
 
-  display_compositor_->AddSurfaceReference(parent_id, surface_id);
+  AddSurfaceReference(parent_id, surface_id);
   RunUntilIdle();
 
   // Real reference is added then temporary reference removed.
@@ -200,7 +218,7 @@
   EXPECT_EQ("Add(0:0:0-1:1:1)", reference_manager_.events());
   EXPECT_EQ(1u, CountTempReferences());
 
-  display_compositor_->AddRootSurfaceReference(surface_id);
+  AddSurfaceReference(GetRootSurfaceId(), surface_id);
   RunUntilIdle();
 
   // Adding real reference doesn't need to change anything in
@@ -223,7 +241,7 @@
   EXPECT_EQ("Add(0:0:0-2:1:1);Add(0:0:0-3:1:1)", reference_manager_.events());
   EXPECT_EQ(2u, CountTempReferences());
 
-  display_compositor_->AddSurfaceReference(parent_id, surface_id1);
+  AddSurfaceReference(parent_id, surface_id1);
   RunUntilIdle();
 
   // Real reference is added then temporary reference removed for 2:1:1. There
@@ -252,7 +270,7 @@
   EXPECT_EQ(2u, CountTempReferences());
 
   // Add a reference to the surface with the later LocalFrameId.
-  display_compositor_->AddSurfaceReference(parent_id, surface_id2);
+  AddSurfaceReference(parent_id, surface_id2);
   RunUntilIdle();
 
   // The real reference should be added for 2:1:2 and both temporary references
diff --git a/services/ui/surfaces/gpu_compositor_frame_sink.cc b/services/ui/surfaces/gpu_compositor_frame_sink.cc
index 22f12793..fe372ce 100644
--- a/services/ui/surfaces/gpu_compositor_frame_sink.cc
+++ b/services/ui/surfaces/gpu_compositor_frame_sink.cc
@@ -47,8 +47,28 @@
   support_.SubmitCompositorFrame(local_frame_id, std::move(frame));
 }
 
+void GpuCompositorFrameSink::AddSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  display_compositor_->AddSurfaceReferences(references);
+}
+
+void GpuCompositorFrameSink::RemoveSurfaceReferences(
+    const std::vector<cc::SurfaceReference>& references) {
+  display_compositor_->RemoveSurfaceReferences(references);
+}
+
+void GpuCompositorFrameSink::Require(const cc::LocalFrameId& local_frame_id,
+                                     const cc::SurfaceSequence& sequence) {
+  support_.Require(local_frame_id, sequence);
+}
+
+void GpuCompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) {
+  support_.Satisfy(sequence);
+}
+
 void GpuCompositorFrameSink::DidReceiveCompositorFrameAck() {
-  client_->DidReceiveCompositorFrameAck();
+  if (client_)
+    client_->DidReceiveCompositorFrameAck();
 }
 
 void GpuCompositorFrameSink::AddChildFrameSink(
diff --git a/services/ui/surfaces/gpu_compositor_frame_sink.h b/services/ui/surfaces/gpu_compositor_frame_sink.h
index 30dc6bc..5d4ac51 100644
--- a/services/ui/surfaces/gpu_compositor_frame_sink.h
+++ b/services/ui/surfaces/gpu_compositor_frame_sink.h
@@ -5,6 +5,9 @@
 #ifndef SERVICES_UI_SURFACES_GPU_COMPOSITOR_FRAME_SINK_H_
 #define SERVICES_UI_SURFACES_GPU_COMPOSITOR_FRAME_SINK_H_
 
+#include <memory>
+#include <vector>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
@@ -41,6 +44,13 @@
   void SetNeedsBeginFrame(bool needs_begin_frame) override;
   void SubmitCompositorFrame(const cc::LocalFrameId& local_frame_id,
                              cc::CompositorFrame frame) override;
+  void AddSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references) override;
+  void RemoveSurfaceReferences(
+      const std::vector<cc::SurfaceReference>& references) override;
+  void Require(const cc::LocalFrameId& local_frame_id,
+               const cc::SurfaceSequence& sequence) override;
+  void Satisfy(const cc::SurfaceSequence& sequence) override;
 
   // cc::mojom::MojoCompositorFrameSinkPrivate:
   void AddChildFrameSink(const cc::FrameSinkId& child_frame_sink_id) override;
diff --git a/services/ui/test_wm/manifest.json b/services/ui/test_wm/manifest.json
index 583733d..54e19891 100644
--- a/services/ui/test_wm/manifest.json
+++ b/services/ui/test_wm/manifest.json
@@ -4,7 +4,7 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
-        "ui": [ "ui:window_manager" ]
+        "ui": [ "window_manager" ]
       }
     }
   }
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 6671c95..40477661 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -15,9 +15,6 @@
     "access_policy_delegate.h",
     "accessibility_manager.cc",
     "accessibility_manager.h",
-    "animation_runner.cc",
-    "animation_runner.h",
-    "animation_runner_observer.h",
     "default_access_policy.cc",
     "default_access_policy.h",
     "display.cc",
@@ -57,8 +54,6 @@
     "platform_display_factory.h",
     "platform_display_init_params.cc",
     "platform_display_init_params.h",
-    "scheduled_animation_group.cc",
-    "scheduled_animation_group.h",
     "server_window.cc",
     "server_window.h",
     "server_window_compositor_frame_sink_manager.cc",
@@ -199,7 +194,6 @@
   sources = [
     # TODO(fsamuel): Move this test with DisplayCompositor. crbug.com/670454
     "../surfaces/display_compositor_unittest.cc",
-    "animation_runner_unittest.cc",
     "cursor_unittest.cc",
     "display_unittest.cc",
     "drag_controller_unittest.cc",
@@ -207,7 +201,6 @@
     "event_matcher_unittest.cc",
     "focus_controller_unittest.cc",
     "frame_generator_unittest.cc",
-    "scheduled_animation_group_unittest.cc",
     "server_window_compositor_frame_sink_manager_test_api.cc",
     "server_window_compositor_frame_sink_manager_test_api.h",
     "server_window_drawn_tracker_unittest.cc",
@@ -258,10 +251,6 @@
   # TODO(fsamuel): Remove this dep. crbug.com/670454
   deps += [ "//services/ui/surfaces" ]
 
-  if (use_x11) {
-    deps += [ "//tools/xdisplaycheck" ]
-  }
-
   data_deps = [
     ":mus_ws_unittests_app_manifest",
   ]
diff --git a/services/ui/ws/animation_runner.cc b/services/ui/ws/animation_runner.cc
deleted file mode 100644
index 5f03c74..0000000
--- a/services/ui/ws/animation_runner.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ui/ws/animation_runner.h"
-
-#include "base/memory/scoped_vector.h"
-#include "services/ui/ws/animation_runner_observer.h"
-#include "services/ui/ws/scheduled_animation_group.h"
-#include "services/ui/ws/server_window.h"
-
-namespace ui {
-namespace ws {
-namespace {
-
-bool ConvertWindowAndAnimationPairsToScheduledAnimationGroups(
-    const std::vector<AnimationRunner::WindowAndAnimationPair>& pairs,
-    AnimationRunner::AnimationId id,
-    base::TimeTicks now,
-    std::vector<std::unique_ptr<ScheduledAnimationGroup>>* groups) {
-  for (const auto& window_animation_pair : pairs) {
-    DCHECK(window_animation_pair.first);
-    DCHECK(window_animation_pair.second);
-    std::unique_ptr<ScheduledAnimationGroup> group(
-        ScheduledAnimationGroup::Create(window_animation_pair.first, now, id,
-                                        *(window_animation_pair.second)));
-    if (!group.get())
-      return false;
-    groups->push_back(std::move(group));
-  }
-  return true;
-}
-
-}  // namespace
-
-AnimationRunner::AnimationRunner(base::TimeTicks now)
-    : next_id_(1), last_tick_time_(now) {}
-
-AnimationRunner::~AnimationRunner() {}
-
-void AnimationRunner::AddObserver(AnimationRunnerObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void AnimationRunner::RemoveObserver(AnimationRunnerObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-AnimationRunner::AnimationId AnimationRunner::Schedule(
-    const std::vector<WindowAndAnimationPair>& pairs,
-    base::TimeTicks now) {
-  DCHECK_GE(now, last_tick_time_);
-
-  const AnimationId animation_id = next_id_++;
-  std::vector<std::unique_ptr<ScheduledAnimationGroup>> groups;
-  if (!ConvertWindowAndAnimationPairsToScheduledAnimationGroups(
-          pairs, animation_id, now, &groups)) {
-    return 0;
-  }
-
-  // Cancel any animations for the affected windows. If the cancelled animations
-  // also animated properties that are not animated by the new group - instantly
-  // set those to the target value.
-  for (auto& group : groups) {
-    ScheduledAnimationGroup* current_group =
-        window_to_animation_map_[group->window()].get();
-    if (current_group)
-      current_group->SetValuesToTargetValuesForPropertiesNotIn(*group.get());
-
-    CancelAnimationForWindowImpl(group->window(), CANCEL_SOURCE_SCHEDULE);
-  }
-
-  // Update internal maps
-  for (auto& group : groups) {
-    group->ObtainStartValues();
-    ServerWindow* window = group->window();
-    window_to_animation_map_[window] = std::move(group);
-    DCHECK(!id_to_windows_map_[animation_id].count(window));
-    id_to_windows_map_[animation_id].insert(window);
-  }
-
-  for (auto& observer : observers_)
-    observer.OnAnimationScheduled(animation_id);
-  return animation_id;
-}
-
-void AnimationRunner::CancelAnimation(AnimationId id) {
-  if (id_to_windows_map_.count(id) == 0)
-    return;
-
-  std::set<ServerWindow*> windows(id_to_windows_map_[id]);
-  for (ServerWindow* window : windows)
-    CancelAnimationForWindow(window);
-}
-
-void AnimationRunner::CancelAnimationForWindow(ServerWindow* window) {
-  CancelAnimationForWindowImpl(window, CANCEL_SOURCE_CANCEL);
-}
-
-void AnimationRunner::Tick(base::TimeTicks time) {
-  DCHECK(time >= last_tick_time_);
-  last_tick_time_ = time;
-  if (window_to_animation_map_.empty())
-    return;
-
-  // The animation ids of any windows whose animation completes are added here.
-  // We notify after processing all windows so that if an observer mutates us in
-  // some way we're aren't left in a weird state.
-  std::set<AnimationId> animations_completed;
-  for (WindowToAnimationMap::iterator i = window_to_animation_map_.begin();
-       i != window_to_animation_map_.end();) {
-    bool animation_done = i->second->Tick(time);
-    if (animation_done) {
-      const AnimationId animation_id = i->second->id();
-      ServerWindow* window = i->first;
-      ++i;
-      bool animation_completed = RemoveWindowFromMaps(window);
-      if (animation_completed)
-        animations_completed.insert(animation_id);
-    } else {
-      ++i;
-    }
-  }
-  for (const AnimationId& id : animations_completed) {
-    for (auto& observer : observers_)
-      observer.OnAnimationDone(id);
-  }
-}
-
-void AnimationRunner::CancelAnimationForWindowImpl(ServerWindow* window,
-                                                   CancelSource source) {
-  if (!window_to_animation_map_[window])
-    return;
-
-  const AnimationId animation_id = window_to_animation_map_[window]->id();
-  if (RemoveWindowFromMaps(window)) {
-    // This was the last window in the group.
-    if (source == CANCEL_SOURCE_CANCEL) {
-      for (auto& observer : observers_)
-        observer.OnAnimationCanceled(animation_id);
-    } else {
-      for (auto& observer : observers_)
-        observer.OnAnimationInterrupted(animation_id);
-    }
-  }
-}
-
-bool AnimationRunner::RemoveWindowFromMaps(ServerWindow* window) {
-  DCHECK(window_to_animation_map_[window]);
-
-  const AnimationId animation_id = window_to_animation_map_[window]->id();
-  window_to_animation_map_.erase(window);
-
-  DCHECK(id_to_windows_map_.count(animation_id));
-  id_to_windows_map_[animation_id].erase(window);
-  if (!id_to_windows_map_[animation_id].empty())
-    return false;
-
-  id_to_windows_map_.erase(animation_id);
-  return true;
-}
-
-}  // namespace ws
-}  // namespace ui
diff --git a/services/ui/ws/animation_runner.h b/services/ui/ws/animation_runner.h
deleted file mode 100644
index 13920f6..0000000
--- a/services/ui/ws/animation_runner.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_UI_WS_ANIMATION_RUNNER_H_
-#define SERVICES_UI_WS_ANIMATION_RUNNER_H_
-
-#include <algorithm>
-#include <map>
-#include <memory>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "base/observer_list.h"
-#include "base/time/time.h"
-
-namespace ui {
-namespace mojom {
-class AnimationGroup;
-}
-
-namespace ws {
-
-class AnimationRunnerObserver;
-class ScheduledAnimationGroup;
-class ServerWindow;
-
-// AnimationRunner is responsible for maintaining and running a set of
-// animations. The animations are represented as a set of AnimationGroups. New
-// animations are scheduled by way of Schedule(). A |window| may only have one
-// animation running at a time. Schedule()ing a new animation for a window
-// already animating implicitly cancels the current animation for the window.
-// Animations progress by way of the Tick() function.
-class AnimationRunner {
- public:
-  using AnimationId = uint32_t;
-  using WindowAndAnimationPair =
-      std::pair<ServerWindow*, const mojom::AnimationGroup*>;
-
-  explicit AnimationRunner(base::TimeTicks now);
-  ~AnimationRunner();
-
-  void AddObserver(AnimationRunnerObserver* observer);
-  void RemoveObserver(AnimationRunnerObserver* observer);
-
-  // Schedules animations. If any of the groups are not valid no animations are
-  // scheuled and 0 is returned. If there is an existing animation in progress
-  // for any of the windows it is canceled and any properties that were
-  // animating but are no longer animating are set to their target value.
-  AnimationId Schedule(const std::vector<WindowAndAnimationPair>& groups,
-                       base::TimeTicks now);
-
-  // Cancels an animation scheduled by an id that was previously returned from
-  // Schedule().
-  void CancelAnimation(AnimationId id);
-
-  // Cancels the animation scheduled for |window|. Does nothing if there is no
-  // animation scheduled for |window|. This does not change |window|. That is,
-  // any in progress animations are stopped.
-  void CancelAnimationForWindow(ServerWindow* window);
-
-  // Advance the animations updating values appropriately.
-  void Tick(base::TimeTicks time);
-
-  // Returns true if there are animations currently scheduled.
-  bool HasAnimations() const { return !window_to_animation_map_.empty(); }
-
-  // Returns true if the animation identified by |id| is valid and animating.
-  bool IsAnimating(AnimationId id) const {
-    return id_to_windows_map_.count(id) > 0;
-  }
-
-  // Returns the windows that are currently animating for |id|. Returns an empty
-  // set if |id| does not identify a valid animation.
-  std::set<ServerWindow*> GetWindowsAnimating(AnimationId id) {
-    return IsAnimating(id) ? id_to_windows_map_.find(id)->second
-                           : std::set<ServerWindow*>();
-  }
-
- private:
-  enum CancelSource {
-    // Cancel is the result of scheduling another animation for the window.
-    CANCEL_SOURCE_SCHEDULE,
-
-    // Cancel originates from an explicit call to cancel.
-    CANCEL_SOURCE_CANCEL,
-  };
-
-  using WindowToAnimationMap =
-      std::unordered_map<ServerWindow*,
-                         std::unique_ptr<ScheduledAnimationGroup>>;
-  using IdToWindowsMap = std::map<AnimationId, std::set<ServerWindow*>>;
-
-  void CancelAnimationForWindowImpl(ServerWindow* window, CancelSource source);
-
-  // Removes |window| from both |window_to_animation_map_| and
-  // |id_to_windows_map_|.
-  // Returns true if there are no more windows animating with the animation id
-  // the window is associated with.
-  bool RemoveWindowFromMaps(ServerWindow* window);
-
-  AnimationId next_id_;
-
-  base::TimeTicks last_tick_time_;
-
-  base::ObserverList<AnimationRunnerObserver> observers_;
-
-  WindowToAnimationMap window_to_animation_map_;
-
-  IdToWindowsMap id_to_windows_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(AnimationRunner);
-};
-
-}  // namespace ws
-}  // namespace ui
-
-#endif  // SERVICES_UI_WS_ANIMATION_RUNNER_H_
diff --git a/services/ui/ws/animation_runner_observer.h b/services/ui/ws/animation_runner_observer.h
deleted file mode 100644
index 4144e7f2..0000000
--- a/services/ui/ws/animation_runner_observer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_UI_WS_ANIMATION_RUNNER_OBSERVER_H_
-#define SERVICES_UI_WS_ANIMATION_RUNNER_OBSERVER_H_
-
-namespace ui {
-namespace ws {
-
-class AnimationRunnerObserver {
- public:
-  virtual void OnAnimationScheduled(uint32_t id) = 0;
-  virtual void OnAnimationDone(uint32_t id) = 0;
-  virtual void OnAnimationInterrupted(uint32_t id) = 0;
-  virtual void OnAnimationCanceled(uint32_t id) = 0;
-
- protected:
-  virtual ~AnimationRunnerObserver() {}
-};
-
-}  // namespace ws
-}  // namespace ui
-
-#endif  // SERVICES_UI_WS_ANIMATION_RUNNER_OBSERVER_H_
diff --git a/services/ui/ws/animation_runner_unittest.cc b/services/ui/ws/animation_runner_unittest.cc
deleted file mode 100644
index 039b748..0000000
--- a/services/ui/ws/animation_runner_unittest.cc
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ui/ws/animation_runner.h"
-
-#include "base/strings/stringprintf.h"
-#include "services/ui/public/interfaces/window_manager_constants.mojom.h"
-#include "services/ui/ws/animation_runner_observer.h"
-#include "services/ui/ws/scheduled_animation_group.h"
-#include "services/ui/ws/server_window.h"
-#include "services/ui/ws/test_server_window_delegate.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::TimeDelta;
-
-namespace ui {
-using mojom::AnimationProperty;
-using mojom::AnimationTweenType;
-using mojom::AnimationElement;
-using mojom::AnimationGroup;
-using mojom::AnimationProperty;
-using mojom::AnimationSequence;
-using mojom::AnimationTweenType;
-using mojom::AnimationValue;
-using mojom::AnimationValuePtr;
-
-namespace ws {
-namespace {
-
-class TestAnimationRunnerObserver : public AnimationRunnerObserver {
- public:
-  TestAnimationRunnerObserver() {}
-  ~TestAnimationRunnerObserver() override {}
-
-  std::vector<std::string>* changes() { return &changes_; }
-  std::vector<uint32_t>* change_ids() { return &change_ids_; }
-
-  void clear_changes() {
-    changes_.clear();
-    change_ids_.clear();
-  }
-
-  // AnimationRunnerDelgate:
-  void OnAnimationScheduled(uint32_t id) override {
-    change_ids_.push_back(id);
-    changes_.push_back("scheduled");
-  }
-  void OnAnimationDone(uint32_t id) override {
-    change_ids_.push_back(id);
-    changes_.push_back("done");
-  }
-  void OnAnimationInterrupted(uint32_t id) override {
-    change_ids_.push_back(id);
-    changes_.push_back("interrupted");
-  }
-  void OnAnimationCanceled(uint32_t id) override {
-    change_ids_.push_back(id);
-    changes_.push_back("canceled");
-  }
-
- private:
-  std::vector<uint32_t> change_ids_;
-  std::vector<std::string> changes_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAnimationRunnerObserver);
-};
-
-// Creates an AnimationValuePtr from the specified float value.
-AnimationValuePtr FloatAnimationValue(float float_value) {
-  AnimationValuePtr value(AnimationValue::New());
-  value->float_value = float_value;
-  return value;
-}
-
-// Creates an AnimationValuePtr from the specified transform.
-AnimationValuePtr TransformAnimationValue(const gfx::Transform& transform) {
-  AnimationValuePtr value(AnimationValue::New());
-  value->transform = transform;
-  return value;
-}
-
-// Adds an AnimationElement to |group|s last sequence with the specified value.
-void AddElement(AnimationGroup* group,
-                TimeDelta time,
-                AnimationValuePtr start_value,
-                AnimationValuePtr target_value,
-                AnimationProperty property,
-                AnimationTweenType tween_type) {
-  AnimationSequence& sequence =
-      *(group->sequences[group->sequences.size() - 1]);
-  sequence.elements.push_back(AnimationElement::New());
-  AnimationElement& element =
-      *(sequence.elements[sequence.elements.size() - 1]);
-  element.property = property;
-  element.duration = time.InMicroseconds();
-  element.tween_type = tween_type;
-  element.start_value = std::move(start_value);
-  element.target_value = std::move(target_value);
-}
-
-void AddOpacityElement(AnimationGroup* group,
-                       TimeDelta time,
-                       AnimationValuePtr start_value,
-                       AnimationValuePtr target_value) {
-  AddElement(group, time, std::move(start_value), std::move(target_value),
-             AnimationProperty::OPACITY, AnimationTweenType::LINEAR);
-}
-
-void AddTransformElement(AnimationGroup* group,
-                         TimeDelta time,
-                         AnimationValuePtr start_value,
-                         AnimationValuePtr target_value) {
-  AddElement(group, time, std::move(start_value), std::move(target_value),
-             AnimationProperty::TRANSFORM, AnimationTweenType::LINEAR);
-}
-
-void AddPauseElement(AnimationGroup* group, TimeDelta time) {
-  AddElement(group, time, AnimationValuePtr(), AnimationValuePtr(),
-             AnimationProperty::NONE, AnimationTweenType::LINEAR);
-}
-
-void InitGroupForWindow(AnimationGroup* group,
-                        const WindowId& id,
-                        int cycle_count) {
-  group->window_id = WindowIdToTransportId(id);
-  group->sequences.push_back(AnimationSequence::New());
-  group->sequences[group->sequences.size() - 1]->cycle_count = cycle_count;
-}
-
-}  // namespace
-
-class AnimationRunnerTest : public testing::Test {
- public:
-  AnimationRunnerTest()
-      : initial_time_(base::TimeTicks::Now()), runner_(initial_time_) {
-    runner_.AddObserver(&runner_observer_);
-  }
-  ~AnimationRunnerTest() override { runner_.RemoveObserver(&runner_observer_); }
-
- protected:
-  // Convenience to schedule an animation for a single window/group pair.
-  AnimationRunner::AnimationId ScheduleForSingleWindow(
-      ServerWindow* window,
-      const AnimationGroup* group,
-      base::TimeTicks now) {
-    std::vector<AnimationRunner::WindowAndAnimationPair> pairs;
-    pairs.push_back(std::make_pair(window, group));
-    return runner_.Schedule(pairs, now);
-  }
-
-  // If |id| is valid and there is only one window schedule against the
-  // animation it is returned; otherwise returns null.
-  ServerWindow* GetSingleWindowAnimating(AnimationRunner::AnimationId id) {
-    std::set<ServerWindow*> windows(runner_.GetWindowsAnimating(id));
-    return windows.size() == 1 ? *windows.begin() : nullptr;
-  }
-
-  const base::TimeTicks initial_time_;
-  TestAnimationRunnerObserver runner_observer_;
-  AnimationRunner runner_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AnimationRunnerTest);
-};
-
-// Opacity from 1 to .5 over 1000.
-TEST_F(AnimationRunnerTest, SingleProperty) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  const uint32_t animation_id =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-  runner_observer_.clear_changes();
-
-  EXPECT_TRUE(runner_.HasAnimations());
-
-  // Opacity should still be 1 (the initial value).
-  EXPECT_EQ(1.f, window.opacity());
-
-  // Animate half way.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  // Run well past the end. Value should progress to end and delegate should
-  // be notified.
-  runner_.Tick(initial_time_ + TimeDelta::FromSeconds(10));
-  EXPECT_EQ(.5f, window.opacity());
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-
-  EXPECT_FALSE(runner_.HasAnimations());
-}
-
-// Opacity from 1 to .5, followed by transform from identity to 2x,3x.
-TEST_F(AnimationRunnerTest, TwoPropertiesInSequence) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5f));
-
-  gfx::Transform done_transform;
-  done_transform.Scale(2, 4);
-  AddTransformElement(&group, TimeDelta::FromMicroseconds(2000),
-                      AnimationValuePtr(),
-                      TransformAnimationValue(done_transform));
-
-  const uint32_t animation_id =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-  runner_observer_.clear_changes();
-
-  // Nothing in the window should have changed yet.
-  EXPECT_EQ(1.f, window.opacity());
-  EXPECT_TRUE(window.transform().IsIdentity());
-
-  // Animate half way from through opacity animation.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_TRUE(window.transform().IsIdentity());
-
-  // Finish first element (opacity).
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
-  EXPECT_EQ(.5f, window.opacity());
-  EXPECT_TRUE(window.transform().IsIdentity());
-
-  // Half way through second (transform).
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
-  EXPECT_EQ(.5f, window.opacity());
-  gfx::Transform half_way_transform;
-  half_way_transform.Scale(1.5, 2.5);
-  EXPECT_EQ(half_way_transform, window.transform());
-
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  // To end.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
-  EXPECT_EQ(.5f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-}
-
-// Opacity from .5 to 1 over 1000, transform to 2x,4x over 500.
-TEST_F(AnimationRunnerTest, TwoPropertiesInParallel) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId(1, 1));
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    FloatAnimationValue(.5f), FloatAnimationValue(1));
-
-  group.sequences.push_back(AnimationSequence::New());
-  group.sequences[1]->cycle_count = 1;
-  gfx::Transform done_transform;
-  done_transform.Scale(2, 4);
-  AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
-                      AnimationValuePtr(),
-                      TransformAnimationValue(done_transform));
-
-  const uint32_t animation_id =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-
-  runner_observer_.clear_changes();
-
-  // Nothing in the window should have changed yet.
-  EXPECT_EQ(1.f, window.opacity());
-  EXPECT_TRUE(window.transform().IsIdentity());
-
-  // Animate to 250, which is 1/4 way through opacity and half way through
-  // transform.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
-
-  EXPECT_EQ(.625f, window.opacity());
-  gfx::Transform half_way_transform;
-  half_way_transform.Scale(1.5, 2.5);
-  EXPECT_EQ(half_way_transform, window.transform());
-
-  // Animate to 500, which is 1/2 way through opacity and transform done.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-
-  // Animate to 750, which is 3/4 way through opacity and transform done.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
-  EXPECT_EQ(.875f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  // To end.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3500));
-  EXPECT_EQ(1.f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-}
-
-// Opacity from .5 to 1 over 1000, pause for 500, 1 to .5 over 500, with a cycle
-// count of 3.
-TEST_F(AnimationRunnerTest, Cycles) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId(1, 2));
-
-  window.SetOpacity(.5f);
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 3);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(1));
-  AddPauseElement(&group, TimeDelta::FromMicroseconds(500));
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  ScheduleForSingleWindow(&window, &group, initial_time_);
-  runner_observer_.clear_changes();
-
-  // Nothing in the window should have changed yet.
-  EXPECT_EQ(.5f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-  EXPECT_EQ(.75f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1250));
-  EXPECT_EQ(1.f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
-  EXPECT_EQ(.75f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2500));
-  EXPECT_EQ(.75f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3250));
-  EXPECT_EQ(1.f, window.opacity());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(3750));
-  EXPECT_EQ(.75f, window.opacity());
-
-  // Animate to the end.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(6500));
-  EXPECT_EQ(.5f, window.opacity());
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-}
-
-// Verifies scheduling the same window twice sends an interrupt.
-TEST_F(AnimationRunnerTest, ScheduleTwice) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId(1, 2));
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  const uint32_t animation_id =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-  runner_observer_.clear_changes();
-
-  // Animate half way.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  // Schedule again. We should get an interrupt, but opacity shouldn't change.
-  const uint32_t animation2_id = ScheduleForSingleWindow(
-      &window, &group, initial_time_ + TimeDelta::FromMicroseconds(500));
-
-  // Id should have changed.
-  EXPECT_NE(animation_id, animation2_id);
-
-  EXPECT_FALSE(runner_.IsAnimating(animation_id));
-  EXPECT_EQ(&window, GetSingleWindowAnimating(animation2_id));
-
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_EQ(2u, runner_observer_.changes()->size());
-  EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-  EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
-  EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(1));
-  runner_observer_.clear_changes();
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
-  EXPECT_EQ(.625f, window.opacity());
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
-  EXPECT_EQ(.5f, window.opacity());
-  EXPECT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation2_id, runner_observer_.change_ids()->at(0));
-}
-
-// Verifies Remove() works.
-TEST_F(AnimationRunnerTest, CancelAnimationForWindow) {
-  // Create an animation and advance it part way.
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  const uint32_t animation_id =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-  runner_observer_.clear_changes();
-  EXPECT_EQ(&window, GetSingleWindowAnimating(animation_id));
-
-  EXPECT_TRUE(runner_.HasAnimations());
-
-  // Animate half way.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_TRUE(runner_observer_.changes()->empty());
-
-  // Cancel the animation.
-  runner_.CancelAnimationForWindow(&window);
-
-  EXPECT_FALSE(runner_.HasAnimations());
-  EXPECT_EQ(nullptr, GetSingleWindowAnimating(animation_id));
-
-  EXPECT_EQ(.75f, window.opacity());
-
-  EXPECT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("canceled", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-}
-
-// Verifies a tick with a very large delta and a sequence that repeats forever
-// doesn't take a long time.
-TEST_F(AnimationRunnerTest, InfiniteRepeatWithHugeGap) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId(1, 2));
-
-  window.SetOpacity(.5f);
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 0);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
-                    AnimationValuePtr(), FloatAnimationValue(1));
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(500),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  ScheduleForSingleWindow(&window, &group, initial_time_);
-  runner_observer_.clear_changes();
-
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000000000750));
-
-  EXPECT_EQ(.75f, window.opacity());
-
-  ASSERT_EQ(0u, runner_observer_.changes()->size());
-}
-
-// Verifies a second schedule sets any properties that are no longer animating
-// to their final value.
-TEST_F(AnimationRunnerTest, RescheduleSetsPropertiesToFinalValue) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  gfx::Transform done_transform;
-  done_transform.Scale(2, 4);
-  AddTransformElement(&group, TimeDelta::FromMicroseconds(500),
-                      AnimationValuePtr(),
-                      TransformAnimationValue(done_transform));
-
-  ScheduleForSingleWindow(&window, &group, initial_time_);
-
-  // Schedule() again, this time without animating opacity.
-  group.sequences[0]->elements[0]->property = AnimationProperty::NONE;
-  ScheduleForSingleWindow(&window, &group, initial_time_);
-
-  // Opacity should go to final value.
-  EXPECT_EQ(.5f, window.opacity());
-  // Transform shouldn't have changed since newly scheduled animation also has
-  // transform in it.
-  EXPECT_TRUE(window.transform().IsIdentity());
-}
-
-// Opacity from 1 to .5 over 1000 of v1 and v2 transform to 2x,4x over 500.
-TEST_F(AnimationRunnerTest, TwoWindows) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window1(&window_delegate, WindowId());
-  ServerWindow window2(&window_delegate, WindowId(1, 2));
-
-  AnimationGroup group1;
-  InitGroupForWindow(&group1, window1.id(), 1);
-  AddOpacityElement(&group1, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(.5));
-
-  AnimationGroup group2;
-  InitGroupForWindow(&group2, window2.id(), 1);
-  gfx::Transform done_transform;
-  done_transform.Scale(2, 4);
-  AddTransformElement(&group2, TimeDelta::FromMicroseconds(500),
-                      AnimationValuePtr(),
-                      TransformAnimationValue(done_transform));
-
-  std::vector<AnimationRunner::WindowAndAnimationPair> pairs;
-  pairs.push_back(std::make_pair(&window1, &group1));
-  pairs.push_back(std::make_pair(&window2, &group2));
-
-  const uint32_t animation_id = runner_.Schedule(pairs, initial_time_);
-
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("scheduled", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id, runner_observer_.change_ids()->at(0));
-  runner_observer_.clear_changes();
-
-  EXPECT_TRUE(runner_.HasAnimations());
-  EXPECT_TRUE(runner_.IsAnimating(animation_id));
-
-  // Properties should be at the initial value.
-  EXPECT_EQ(1.f, window1.opacity());
-  EXPECT_TRUE(window2.transform().IsIdentity());
-
-  // Animate 250ms in, which is quarter way for opacity and half way for
-  // transform.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(250));
-  EXPECT_EQ(.875f, window1.opacity());
-  gfx::Transform half_way_transform;
-  half_way_transform.Scale(1.5, 2.5);
-  EXPECT_EQ(half_way_transform, window2.transform());
-  std::set<ServerWindow*> windows_animating(
-      runner_.GetWindowsAnimating(animation_id));
-  EXPECT_EQ(2u, windows_animating.size());
-  EXPECT_EQ(1u, windows_animating.count(&window1));
-  EXPECT_EQ(1u, windows_animating.count(&window2));
-
-  // Animate 750ms in, window1 should be done 3/4 done, and window2 done.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(750));
-  EXPECT_EQ(.625, window1.opacity());
-  EXPECT_EQ(done_transform, window2.transform());
-  windows_animating = runner_.GetWindowsAnimating(animation_id);
-  EXPECT_EQ(1u, windows_animating.size());
-  EXPECT_EQ(1u, windows_animating.count(&window1));
-  EXPECT_TRUE(runner_.HasAnimations());
-  EXPECT_TRUE(runner_.IsAnimating(animation_id));
-
-  // Animate to end.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1750));
-  EXPECT_EQ(.5, window1.opacity());
-  EXPECT_EQ(done_transform, window2.transform());
-  windows_animating = runner_.GetWindowsAnimating(animation_id);
-  EXPECT_TRUE(windows_animating.empty());
-  EXPECT_FALSE(runner_.HasAnimations());
-  EXPECT_FALSE(runner_.IsAnimating(animation_id));
-}
-
-TEST_F(AnimationRunnerTest, Reschedule) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-
-  // Animation from 1-0 over 1ms and in parallel transform to 2x,4x over 1ms.
-  AnimationGroup group;
-  InitGroupForWindow(&group, window.id(), 1);
-  AddOpacityElement(&group, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(0));
-  group.sequences.push_back(AnimationSequence::New());
-  group.sequences[1]->cycle_count = 1;
-  gfx::Transform done_transform;
-  done_transform.Scale(2, 4);
-  AddTransformElement(&group, TimeDelta::FromMicroseconds(1000),
-                      AnimationValuePtr(),
-                      TransformAnimationValue(done_transform));
-  const uint32_t animation_id1 =
-      ScheduleForSingleWindow(&window, &group, initial_time_);
-
-  // Animate half way in.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(500));
-  EXPECT_EQ(.5f, window.opacity());
-  gfx::Transform half_way_transform;
-  half_way_transform.Scale(1.5, 2.5);
-  EXPECT_EQ(half_way_transform, window.transform());
-
-  runner_observer_.clear_changes();
-
-  // Schedule the same window animating opacity to 1.
-  AnimationGroup group2;
-  InitGroupForWindow(&group2, window.id(), 1);
-  AddOpacityElement(&group2, TimeDelta::FromMicroseconds(1000),
-                    AnimationValuePtr(), FloatAnimationValue(1));
-  const uint32_t animation_id2 = ScheduleForSingleWindow(
-      &window, &group2, initial_time_ + TimeDelta::FromMicroseconds(500));
-
-  // Opacity should remain at .5, but transform should go to end state.
-  EXPECT_EQ(.5f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-
-  ASSERT_EQ(2u, runner_observer_.changes()->size());
-  EXPECT_EQ("interrupted", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id1, runner_observer_.change_ids()->at(0));
-  EXPECT_EQ("scheduled", runner_observer_.changes()->at(1));
-  EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(1));
-  runner_observer_.clear_changes();
-
-  // Animate half way through new sequence. Opacity should be the only thing
-  // changing.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(1000));
-  EXPECT_EQ(.75f, window.opacity());
-  EXPECT_EQ(done_transform, window.transform());
-  ASSERT_EQ(0u, runner_observer_.changes()->size());
-
-  // Animate to end.
-  runner_.Tick(initial_time_ + TimeDelta::FromMicroseconds(2000));
-  ASSERT_EQ(1u, runner_observer_.changes()->size());
-  EXPECT_EQ("done", runner_observer_.changes()->at(0));
-  EXPECT_EQ(animation_id2, runner_observer_.change_ids()->at(0));
-}
-
-}  // ws
-}  // mus
diff --git a/services/ui/ws/frame_generator.cc b/services/ui/ws/frame_generator.cc
index 0c869ea..d9774da 100644
--- a/services/ui/ws/frame_generator.cc
+++ b/services/ui/ws/frame_generator.cc
@@ -4,13 +4,14 @@
 
 #include "services/ui/ws/frame_generator.h"
 
+#include <utility>
+
 #include "base/containers/adapters.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/quads/render_pass.h"
 #include "cc/quads/render_pass_draw_quad.h"
 #include "cc/quads/shared_quad_state.h"
 #include "cc/quads/surface_draw_quad.h"
-#include "cc/surfaces/surface_id.h"
 #include "services/ui/ws/frame_generator_delegate.h"
 #include "services/ui/ws/server_window.h"
 #include "services/ui/ws/server_window_compositor_frame_sink_manager.h"
@@ -24,16 +25,12 @@
                                ServerWindow* root_window)
     : delegate_(delegate),
       root_window_(root_window),
-      top_level_root_surface_id_(
-          cc::FrameSinkId(0, 0),
-          cc::LocalFrameId(0, base::UnguessableToken::Create())),
       binding_(this),
       weak_factory_(this) {
   DCHECK(delegate_);
 }
 
 FrameGenerator::~FrameGenerator() {
-  RemoveDeadSurfaceReferences();
   RemoveAllSurfaceReferences();
   // Invalidate WeakPtrs now to avoid callbacks back into the
   // FrameGenerator during destruction of |compositor_frame_sink_|.
@@ -60,36 +57,34 @@
                                       ServerWindow* window) {
   DCHECK(surface_id.is_valid());
 
-  // TODO(kylechar): Adding surface references should be synchronized with
-  // SubmitCompositorFrame().
-
   auto iter = active_references_.find(surface_id.frame_sink_id());
   if (iter == active_references_.end()) {
     AddFirstReference(surface_id, window);
     return;
   }
 
-  SurfaceReference& ref = iter->second;
-  DCHECK_EQ(surface_id.frame_sink_id(), ref.child_id.frame_sink_id());
+  cc::SurfaceReference& ref = iter->second;
 
   // This shouldn't be called multiple times for the same SurfaceId.
-  DCHECK_NE(surface_id.local_frame_id(), ref.child_id.local_frame_id());
+  DCHECK_EQ(surface_id.frame_sink_id(), ref.child_id().frame_sink_id());
+  DCHECK_NE(surface_id.local_frame_id(), ref.child_id().local_frame_id());
 
-  // Add a reference from parent to new surface first.
-  GetDisplayCompositor()->AddSurfaceReference(ref.parent_id, surface_id);
+  // The current reference will be removed after the next CompositorFrame is
+  // submitted or FrameGenerator is destroyed.
+  references_to_remove_.push_back(ref);
+  cc::SurfaceId old_surface_id = ref.child_id();
 
-  // If the display root surface has changed, update all references to embedded
-  // surfaces. For example, this would happen when the display resolution or
-  // zoom level changes.
-  if (!window->parent())
-    AddNewParentReferences(ref.child_id, surface_id);
+  // New surface reference is recorded and will be added at end of this method.
+  ref = cc::SurfaceReference(ref.parent_id(), surface_id);
+  references_to_add_.push_back(ref);
 
-  // Move the existing reference to list of references to remove after we submit
-  // the next CompositorFrame. Update local reference cache to be the new
-  // reference. If this is the display root surface then removing this reference
-  // will recursively remove any references it held.
-  dead_references_.push_back(ref);
-  ref.child_id = surface_id;
+  // If the display root surface has changed, add references from the new
+  // SurfaceId to all embedded surfaces. For example, this would happen when the
+  // display resolution or device scale factor changes.
+  if (window == root_window_)
+    AddNewParentReferences(old_surface_id, surface_id);
+
+  PerformAddSurfaceReferences();
 }
 
 void FrameGenerator::DidReceiveCompositorFrameAck() {}
@@ -114,8 +109,7 @@
     // Remove dead references after we submit a frame. This has to happen after
     // the frame is submitted otherwise we could end up deleting a surface that
     // is still embedded in the last frame.
-    // TODO(kylechar): This should be synchronized with SubmitCompositorFrame().
-    RemoveDeadSurfaceReferences();
+    PerformRemoveSurfaceReferences();
   }
 }
 
@@ -131,7 +125,7 @@
 
 cc::CompositorFrame FrameGenerator::GenerateCompositorFrame(
     const gfx::Rect& output_rect) {
-  const cc::RenderPassId render_pass_id(1, 1);
+  const int render_pass_id = 1;
   std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
   render_pass->SetNew(render_pass_id, output_rect, output_rect,
                       gfx::Transform());
@@ -142,8 +136,7 @@
   frame.render_pass_list.push_back(std::move(render_pass));
   if (delegate_->IsInHighContrastMode()) {
     std::unique_ptr<cc::RenderPass> invert_pass = cc::RenderPass::Create();
-    invert_pass->SetNew(cc::RenderPassId(2, 0), output_rect, output_rect,
-                        gfx::Transform());
+    invert_pass->SetNew(2, output_rect, output_rect, gfx::Transform());
     cc::SharedQuadState* shared_state =
         invert_pass->CreateAndAppendSharedQuadState();
     shared_state->SetAll(gfx::Transform(), output_rect.size(), output_rect,
@@ -241,7 +234,7 @@
 
 cc::SurfaceId FrameGenerator::FindParentSurfaceId(ServerWindow* window) {
   if (window == root_window_)
-    return top_level_root_surface_id_;
+    return root_window_->delegate()->GetRootSurfaceId();
 
   // The root window holds the parent SurfaceId. This SurfaceId will have an
   // invalid LocalFrameId before FrameGenerator has submitted a CompositorFrame.
@@ -252,31 +245,30 @@
 
 void FrameGenerator::AddSurfaceReference(const cc::SurfaceId& parent_id,
                                          const cc::SurfaceId& child_id) {
-  if (parent_id == top_level_root_surface_id_)
-    GetDisplayCompositor()->AddRootSurfaceReference(child_id);
-  else
-    GetDisplayCompositor()->AddSurfaceReference(parent_id, child_id);
+  DCHECK_NE(parent_id, child_id);
 
-  // Add new reference from parent to surface, plus add reference to local
-  // cache.
-  active_references_[child_id.frame_sink_id()] =
-      SurfaceReference({parent_id, child_id});
+  // Add new reference from parent to surface and record reference.
+  cc::SurfaceReference ref(parent_id, child_id);
+  active_references_[child_id.frame_sink_id()] = ref;
+  references_to_add_.push_back(ref);
 }
 
 void FrameGenerator::AddFirstReference(const cc::SurfaceId& surface_id,
                                        ServerWindow* window) {
   cc::SurfaceId parent_id = FindParentSurfaceId(window);
 
-  if (parent_id == top_level_root_surface_id_ || parent_id.is_valid()) {
+  if (parent_id.local_frame_id().is_valid()) {
     AddSurfaceReference(parent_id, surface_id);
 
     // For the first display root surface, add references to any child surfaces
-    // that were created before it.
-    if (parent_id == top_level_root_surface_id_) {
+    // that were created before it, since no reference has been added yet.
+    if (window == root_window_) {
       for (auto& child_surface_id : waiting_for_references_)
         AddSurfaceReference(surface_id, child_surface_id);
       waiting_for_references_.clear();
     }
+
+    PerformAddSurfaceReferences();
   } else {
     // This isn't the display root surface and display root surface hasn't
     // submitted a CF yet. We can't add a reference to an unknown SurfaceId.
@@ -290,30 +282,31 @@
 void FrameGenerator::AddNewParentReferences(
     const cc::SurfaceId& old_surface_id,
     const cc::SurfaceId& new_surface_id) {
-  DCHECK(old_surface_id.frame_sink_id() == new_surface_id.frame_sink_id());
+  DCHECK_EQ(old_surface_id.frame_sink_id(), new_surface_id.frame_sink_id());
 
-  cc::mojom::DisplayCompositor* display_compositor = GetDisplayCompositor();
   for (auto& map_entry : active_references_) {
-    SurfaceReference& ref = map_entry.second;
-    if (ref.parent_id == old_surface_id) {
-      display_compositor->AddSurfaceReference(new_surface_id, ref.child_id);
-      ref.parent_id = new_surface_id;
+    cc::SurfaceReference& ref = map_entry.second;
+    if (ref.parent_id() == old_surface_id) {
+      ref = cc::SurfaceReference(new_surface_id, ref.child_id());
+      references_to_add_.push_back(ref);
     }
   }
 }
 
-void FrameGenerator::RemoveDeadSurfaceReferences() {
-  if (dead_references_.empty())
+void FrameGenerator::PerformAddSurfaceReferences() {
+  if (references_to_add_.empty())
     return;
 
-  cc::mojom::DisplayCompositor* display_compositor = GetDisplayCompositor();
-  for (auto& ref : dead_references_) {
-    if (ref.parent_id == top_level_root_surface_id_)
-      display_compositor->RemoveRootSurfaceReference(ref.child_id);
-    else
-      display_compositor->RemoveSurfaceReference(ref.parent_id, ref.child_id);
-  }
-  dead_references_.clear();
+  compositor_frame_sink_->AddSurfaceReferences(references_to_add_);
+  references_to_add_.clear();
+}
+
+void FrameGenerator::PerformRemoveSurfaceReferences() {
+  if (references_to_remove_.empty())
+    return;
+
+  compositor_frame_sink_->RemoveSurfaceReferences(references_to_remove_);
+  references_to_remove_.clear();
 }
 
 void FrameGenerator::RemoveFrameSinkReference(
@@ -321,22 +314,15 @@
   auto it = active_references_.find(frame_sink_id);
   if (it == active_references_.end())
     return;
-  dead_references_.push_back(it->second);
+  references_to_remove_.push_back(it->second);
   active_references_.erase(it);
 }
 
 void FrameGenerator::RemoveAllSurfaceReferences() {
-  // TODO(kylechar): Remove multiple surfaces with one IPC call.
-  cc::mojom::DisplayCompositor* display_compositor = GetDisplayCompositor();
-  for (auto& map_entry : active_references_) {
-    const SurfaceReference& ref = map_entry.second;
-    display_compositor->RemoveSurfaceReference(ref.parent_id, ref.child_id);
-  }
+  for (auto& map_entry : active_references_)
+    references_to_remove_.push_back(map_entry.second);
   active_references_.clear();
-}
-
-cc::mojom::DisplayCompositor* FrameGenerator::GetDisplayCompositor() {
-  return root_window_->delegate()->GetDisplayCompositor();
+  PerformRemoveSurfaceReferences();
 }
 
 void FrameGenerator::OnWindowDestroying(ServerWindow* window) {
diff --git a/services/ui/ws/frame_generator.h b/services/ui/ws/frame_generator.h
index a9e822e4..0e872bf7 100644
--- a/services/ui/ws/frame_generator.h
+++ b/services/ui/ws/frame_generator.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <unordered_map>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/timer/timer.h"
@@ -14,6 +15,7 @@
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_id_allocator.h"
+#include "cc/surfaces/surface_reference.h"
 #include "services/ui/public/interfaces/window_tree_constants.mojom.h"
 #include "services/ui/ws/ids.h"
 #include "services/ui/ws/server_window_delegate.h"
@@ -22,15 +24,11 @@
 #include "ui/gfx/native_widget_types.h"
 
 namespace cc {
-class CompositorFrame;
 class RenderPass;
 class SurfaceId;
 }
 
 namespace ui {
-
-class DisplayCompositor;
-
 namespace ws {
 
 namespace test {
@@ -101,10 +99,13 @@
   void AddNewParentReferences(const cc::SurfaceId& old_surface_id,
                               const cc::SurfaceId& new_surface_id);
 
-  // Removes all references to surfaces in |dead_references_|.
-  void RemoveDeadSurfaceReferences();
+  // Sends IPC to add references in |references_to_add_|.
+  void PerformAddSurfaceReferences();
 
-  // Removes any retained references for the provided FrameSink.
+  // Sends IPC to remove all references in |references_to_remove_|.
+  void PerformRemoveSurfaceReferences();
+
+  // Removes any retained references for |frame_sink_id_|.
   void RemoveFrameSinkReference(const cc::FrameSinkId& frame_sink_id);
 
   // Removes all retained references to surfaces.
@@ -123,24 +124,18 @@
   cc::SurfaceIdAllocator id_allocator_;
   cc::mojom::MojoCompositorFrameSinkPtr compositor_frame_sink_;
 
-  // Represents the top level root surface id that should reference the display
-  // root surface. We don't know the actual value, because it's generated in
-  // another process, this is used internally as a placeholder.
-  const cc::SurfaceId top_level_root_surface_id_;
-
-  struct SurfaceReference {
-    cc::SurfaceId parent_id;
-    cc::SurfaceId child_id;
-  };
-
   // Active references held by this client to surfaces that could be embedded in
   // a CompositorFrame submitted from FrameGenerator.
-  std::unordered_map<cc::FrameSinkId, SurfaceReference, cc::FrameSinkIdHash>
+  std::unordered_map<cc::FrameSinkId, cc::SurfaceReference, cc::FrameSinkIdHash>
       active_references_;
 
   // References to surfaces that should be removed after a CompositorFrame has
   // been submitted and the surfaces are not being used.
-  std::vector<SurfaceReference> dead_references_;
+  std::vector<cc::SurfaceReference> references_to_remove_;
+
+  // References that should be added before the next CompositorFrame is
+  // submitted.
+  std::vector<cc::SurfaceReference> references_to_add_;
 
   // If a CompositorFrame for a child surface is submitted before the first
   // display root CompositorFrame, we can't add a reference from the unknown
diff --git a/services/ui/ws/gpu_service_proxy.cc b/services/ui/ws/gpu_service_proxy.cc
index 0b359fb..e956b9ac 100644
--- a/services/ui/ws/gpu_service_proxy.cc
+++ b/services/ui/ws/gpu_service_proxy.cc
@@ -10,6 +10,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connection.h"
@@ -25,6 +26,73 @@
 // The client Id 1 is reserved for the display compositor.
 const int32_t kInternalGpuChannelClientId = 2;
 
+// The implementation that relays requests from clients to the real
+// service implementation in the GPU process over mojom.GpuServiceInternal.
+class GpuServiceImpl : public mojom::GpuService {
+ public:
+  GpuServiceImpl(int client_id,
+                 gpu::GPUInfo* gpu_info,
+                 MusGpuMemoryBufferManager* gpu_memory_buffer_manager,
+                 mojom::GpuServiceInternal* gpu_service_internal)
+      : client_id_(client_id),
+        gpu_info_(gpu_info),
+        gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+        gpu_service_internal_(gpu_service_internal) {
+    DCHECK(gpu_memory_buffer_manager_);
+    DCHECK(gpu_service_internal_);
+  }
+  ~GpuServiceImpl() override {
+    gpu_memory_buffer_manager_->DestroyAllGpuMemoryBufferForClient(client_id_);
+  }
+
+ private:
+  void OnGpuChannelEstablished(const EstablishGpuChannelCallback& callback,
+                               mojo::ScopedMessagePipeHandle channel_handle) {
+    callback.Run(client_id_, std::move(channel_handle), *gpu_info_);
+  }
+
+  // mojom::GpuService overrides:
+  void EstablishGpuChannel(
+      const EstablishGpuChannelCallback& callback) override {
+    // TODO(sad): crbug.com/617415 figure out how to generate a meaningful
+    // tracing id.
+    const uint64_t client_tracing_id = 0;
+    constexpr bool is_gpu_host = false;
+    gpu_service_internal_->EstablishGpuChannel(
+        client_id_, client_tracing_id, is_gpu_host,
+        base::Bind(&GpuServiceImpl::OnGpuChannelEstablished,
+                   base::Unretained(this), callback));
+  }
+
+  void CreateGpuMemoryBuffer(
+      gfx::GpuMemoryBufferId id,
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      const mojom::GpuService::CreateGpuMemoryBufferCallback& callback)
+      override {
+    auto handle = gpu_memory_buffer_manager_->CreateGpuMemoryBufferHandle(
+        id, client_id_, size, format, usage, gpu::kNullSurfaceHandle);
+    callback.Run(handle);
+  }
+
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              const gpu::SyncToken& sync_token) override {
+    gpu_memory_buffer_manager_->DestroyGpuMemoryBuffer(id, client_id_,
+                                                       sync_token);
+  }
+
+  const int client_id_;
+
+  // The objects these pointers refer to are owned by the GpuServiceProxy
+  // object.
+  const gpu::GPUInfo* gpu_info_;
+  MusGpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  mojom::GpuServiceInternal* gpu_service_internal_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuServiceImpl);
+};
+
 }  // namespace
 
 GpuServiceProxy::GpuServiceProxy(GpuServiceProxyDelegate* delegate)
@@ -46,7 +114,11 @@
 }
 
 void GpuServiceProxy::Add(mojom::GpuServiceRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+  mojo::MakeStrongBinding(
+      base::MakeUnique<GpuServiceImpl>(next_client_id_++, &gpu_info_,
+                                       gpu_memory_buffer_manager_.get(),
+                                       gpu_service_.get()),
+      std::move(request));
 }
 
 void GpuServiceProxy::CreateDisplayCompositor(
@@ -57,51 +129,8 @@
 
 void GpuServiceProxy::OnInitialized(const gpu::GPUInfo& gpu_info) {
   gpu_info_ = gpu_info;
-
   delegate_->OnGpuServiceInitialized();
 }
 
-void GpuServiceProxy::OnGpuChannelEstablished(
-    const EstablishGpuChannelCallback& callback,
-    int32_t client_id,
-    mojo::ScopedMessagePipeHandle channel_handle) {
-  callback.Run(client_id, std::move(channel_handle), gpu_info_);
-}
-
-void GpuServiceProxy::EstablishGpuChannel(
-    const EstablishGpuChannelCallback& callback) {
-  const int client_id = next_client_id_++;
-  // TODO(sad): crbug.com/617415 figure out how to generate a meaningful tracing
-  // id.
-  const uint64_t client_tracing_id = 0;
-  constexpr bool is_gpu_host = false;
-  gpu_service_->EstablishGpuChannel(
-      client_id, client_tracing_id, is_gpu_host,
-      base::Bind(&GpuServiceProxy::OnGpuChannelEstablished,
-                 base::Unretained(this), callback, client_id));
-}
-
-void GpuServiceProxy::CreateGpuMemoryBuffer(
-    gfx::GpuMemoryBufferId id,
-    const gfx::Size& size,
-    gfx::BufferFormat format,
-    gfx::BufferUsage usage,
-    const mojom::GpuService::CreateGpuMemoryBufferCallback& callback) {
-  // TODO(sad): Check to see if native gpu memory buffer can be used first.
-  if (!gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) ||
-      !gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
-                                                                  format)) {
-    callback.Run(gfx::GpuMemoryBufferHandle());
-    return;
-  }
-  callback.Run(gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
-      id, size, format));
-}
-
-void GpuServiceProxy::DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                                             const gpu::SyncToken& sync_token) {
-  //  NOTIMPLEMENTED();
-}
-
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/gpu_service_proxy.h b/services/ui/ws/gpu_service_proxy.h
index 5687b2e3..bf562d1 100644
--- a/services/ui/ws/gpu_service_proxy.h
+++ b/services/ui/ws/gpu_service_proxy.h
@@ -18,19 +18,18 @@
 
 namespace ui {
 
-class GpuServiceInternal;
 class MusGpuMemoryBufferManager;
 
 namespace ws {
 
 class GpuServiceProxyDelegate;
 
-// The proxy implementation that relays requests from clients to the real
-// service implementation in the GPU process over mojom.GpuServiceInternal.
-class GpuServiceProxy : public mojom::GpuService {
+// Sets up connection from clients to the real service implementation in the GPU
+// process.
+class GpuServiceProxy {
  public:
   explicit GpuServiceProxy(GpuServiceProxyDelegate* delegate);
-  ~GpuServiceProxy() override;
+  ~GpuServiceProxy();
 
   void Add(mojom::GpuServiceRequest request);
 
@@ -40,28 +39,11 @@
 
  private:
   void OnInitialized(const gpu::GPUInfo& gpu_info);
-  void OnGpuChannelEstablished(const EstablishGpuChannelCallback& callback,
-                               int32_t client_id,
-                               mojo::ScopedMessagePipeHandle channel_handle);
-
-  // mojom::GpuService overrides:
-  void EstablishGpuChannel(
-      const EstablishGpuChannelCallback& callback) override;
-  void CreateGpuMemoryBuffer(
-      gfx::GpuMemoryBufferId id,
-      const gfx::Size& size,
-      gfx::BufferFormat format,
-      gfx::BufferUsage usage,
-      const mojom::GpuService::CreateGpuMemoryBufferCallback& callback)
-      override;
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                              const gpu::SyncToken& sync_token) override;
 
   GpuServiceProxyDelegate* const delegate_;
   int32_t next_client_id_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   mojom::GpuServiceInternalPtr gpu_service_;
-  mojo::BindingSet<mojom::GpuService> bindings_;
   gpu::GPUInfo gpu_info_;
   std::unique_ptr<MusGpuMemoryBufferManager> gpu_memory_buffer_manager_;
 
diff --git a/services/ui/ws/mus_ws_unittests_app_manifest.json b/services/ui/ws/mus_ws_unittests_app_manifest.json
index eeb3fefc..dde1cca1 100644
--- a/services/ui/ws/mus_ws_unittests_app_manifest.json
+++ b/services/ui/ws/mus_ws_unittests_app_manifest.json
@@ -12,8 +12,8 @@
         "*": [ "app" ],
         "mus_ws_unittests_app": [ "ui:window_tree_client" ],
         "ui": [
-          "ui:window_tree_host_factory",
-          "ui:window_manager"
+          "window_manager",
+          "window_tree_host_factory"
         ]
       }
     }
diff --git a/services/ui/ws/scheduled_animation_group.cc b/services/ui/ws/scheduled_animation_group.cc
deleted file mode 100644
index 6915850..0000000
--- a/services/ui/ws/scheduled_animation_group.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ui/ws/scheduled_animation_group.h"
-
-#include <set>
-
-#include "services/ui/ws/server_window.h"
-
-using ui::mojom::AnimationProperty;
-
-namespace ui {
-namespace ws {
-namespace {
-
-using Sequences = std::vector<ScheduledAnimationSequence>;
-
-// Gets the value of |property| from |window| into |value|.
-void GetValueFromWindow(const ServerWindow* window,
-                        AnimationProperty property,
-                        ScheduledAnimationValue* value) {
-  switch (property) {
-    case AnimationProperty::NONE:
-      NOTREACHED();
-      break;
-    case AnimationProperty::OPACITY:
-      value->float_value = window->opacity();
-      break;
-    case AnimationProperty::TRANSFORM:
-      value->transform = window->transform();
-      break;
-  }
-}
-
-// Sets the value of |property| from |value| into |window|.
-void SetWindowPropertyFromValue(ServerWindow* window,
-                                AnimationProperty property,
-                                const ScheduledAnimationValue& value) {
-  switch (property) {
-    case AnimationProperty::NONE:
-      break;
-    case AnimationProperty::OPACITY:
-      window->SetOpacity(value.float_value);
-      break;
-    case AnimationProperty::TRANSFORM:
-      window->SetTransform(value.transform);
-      break;
-  }
-}
-
-// Sets the value of |property| into |window| between two points.
-void SetWindowPropertyFromValueBetween(ServerWindow* window,
-                                       AnimationProperty property,
-                                       double value,
-                                       gfx::Tween::Type tween_type,
-                                       const ScheduledAnimationValue& start,
-                                       const ScheduledAnimationValue& target) {
-  const double tween_value = gfx::Tween::CalculateValue(tween_type, value);
-  switch (property) {
-    case AnimationProperty::NONE:
-      break;
-    case AnimationProperty::OPACITY:
-      window->SetOpacity(gfx::Tween::FloatValueBetween(
-          tween_value, start.float_value, target.float_value));
-      break;
-    case AnimationProperty::TRANSFORM:
-      window->SetTransform(gfx::Tween::TransformValueBetween(
-          tween_value, start.transform, target.transform));
-      break;
-  }
-}
-
-// TODO(mfomitchev): Use struct traits for this?
-gfx::Tween::Type AnimationTypeToTweenType(mojom::AnimationTweenType type) {
-  switch (type) {
-    case mojom::AnimationTweenType::LINEAR:
-      return gfx::Tween::LINEAR;
-    case mojom::AnimationTweenType::EASE_IN:
-      return gfx::Tween::EASE_IN;
-    case mojom::AnimationTweenType::EASE_OUT:
-      return gfx::Tween::EASE_OUT;
-    case mojom::AnimationTweenType::EASE_IN_OUT:
-      return gfx::Tween::EASE_IN_OUT;
-  }
-  return gfx::Tween::LINEAR;
-}
-
-void ConvertToScheduledValue(const mojom::AnimationValue& transport_value,
-                             ScheduledAnimationValue* value) {
-  value->float_value = transport_value.float_value;
-  value->transform =
-      transport_value.transform ? *transport_value.transform : gfx::Transform();
-}
-
-void ConvertToScheduledElement(const mojom::AnimationElement& transport_element,
-                               ScheduledAnimationElement* element) {
-  element->property = transport_element.property;
-  element->duration =
-      base::TimeDelta::FromMicroseconds(transport_element.duration);
-  element->tween_type = AnimationTypeToTweenType(transport_element.tween_type);
-  if (transport_element.property != AnimationProperty::NONE) {
-    if (transport_element.start_value.get()) {
-      element->is_start_valid = true;
-      ConvertToScheduledValue(*transport_element.start_value,
-                              &(element->start_value));
-    } else {
-      element->is_start_valid = false;
-    }
-    ConvertToScheduledValue(*transport_element.target_value,
-                            &(element->target_value));
-  }
-}
-
-bool IsAnimationValueValid(AnimationProperty property,
-                           const mojom::AnimationValue& value) {
-  bool result;
-  switch (property) {
-    case AnimationProperty::NONE:
-      NOTREACHED();
-      return false;
-    case AnimationProperty::OPACITY:
-      result = value.float_value >= 0.f && value.float_value <= 1.f;
-      DVLOG_IF(1, !result) << "Invalid AnimationValue for opacity: "
-                           << value.float_value;
-      return result;
-    case AnimationProperty::TRANSFORM:
-      return true;
-  }
-  return false;
-}
-
-bool IsAnimationElementValid(const mojom::AnimationElement& element) {
-  if (element.property == AnimationProperty::NONE)
-    return true;  // None is a pause and doesn't need any values.
-  if (element.start_value.get() &&
-      !IsAnimationValueValid(element.property, *element.start_value))
-    return false;
-  // For all other properties we require a target.
-  return element.target_value.get() &&
-         IsAnimationValueValid(element.property, *element.target_value);
-}
-
-bool IsAnimationSequenceValid(const mojom::AnimationSequence& sequence) {
-  if (sequence.elements.size() == 0) {
-    DVLOG(1) << "Empty or null AnimationSequence - invalid.";
-    return false;
-  }
-
-  for (size_t i = 0; i < sequence.elements.size(); ++i) {
-    if (!IsAnimationElementValid(*sequence.elements[i]))
-      return false;
-  }
-  return true;
-}
-
-bool IsAnimationGroupValid(const mojom::AnimationGroup& transport_group) {
-  if (transport_group.sequences.size() == 0) {
-    DVLOG(1) << "Empty or null AnimationGroup - invalid; window_id="
-             << transport_group.window_id;
-    return false;
-  }
-  for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
-    if (!IsAnimationSequenceValid(*transport_group.sequences[i]))
-      return false;
-  }
-  return true;
-}
-
-// If the start value for |element| isn't valid, the value for the property
-// is obtained from |window| and placed into |element|.
-void GetStartValueFromWindowIfNecessary(const ServerWindow* window,
-                                        ScheduledAnimationElement* element) {
-  if (element->property != AnimationProperty::NONE &&
-      !element->is_start_valid) {
-    GetValueFromWindow(window, element->property, &(element->start_value));
-  }
-}
-
-void GetScheduledAnimationProperties(const Sequences& sequences,
-                                     std::set<AnimationProperty>* properties) {
-  for (const ScheduledAnimationSequence& sequence : sequences) {
-    for (const ScheduledAnimationElement& element : sequence.elements)
-      properties->insert(element.property);
-  }
-}
-
-// Set |window|'s specified |property| to the value resulting from running all
-// the |sequences|.
-void SetPropertyToTargetProperty(ServerWindow* window,
-                                 mojom::AnimationProperty property,
-                                 const Sequences& sequences) {
-  // NOTE: this doesn't deal with |cycle_count| quite right, but I'm honestly
-  // not sure we really want to support the same property in multiple sequences
-  // animating at once so I'm not dealing.
-  base::TimeDelta max_end_duration;
-  std::unique_ptr<ScheduledAnimationValue> value;
-  for (const ScheduledAnimationSequence& sequence : sequences) {
-    base::TimeDelta duration;
-    for (const ScheduledAnimationElement& element : sequence.elements) {
-      if (element.property != property)
-        continue;
-
-      duration += element.duration;
-      if (duration > max_end_duration) {
-        max_end_duration = duration;
-        value = base::MakeUnique<ScheduledAnimationValue>(element.target_value);
-      }
-    }
-  }
-  if (value.get())
-    SetWindowPropertyFromValue(window, property, *value);
-}
-
-void ConvertSequenceToScheduled(
-    const mojom::AnimationSequence& transport_sequence,
-    base::TimeTicks now,
-    ScheduledAnimationSequence* sequence) {
-  sequence->run_until_stopped = transport_sequence.cycle_count == 0u;
-  sequence->cycle_count = transport_sequence.cycle_count;
-  DCHECK_NE(0u, transport_sequence.elements.size());
-  sequence->elements.resize(transport_sequence.elements.size());
-
-  base::TimeTicks element_start_time = now;
-  for (size_t i = 0; i < transport_sequence.elements.size(); ++i) {
-    ConvertToScheduledElement(*(transport_sequence.elements[i].get()),
-                              &(sequence->elements[i]));
-    sequence->elements[i].start_time = element_start_time;
-    sequence->duration += sequence->elements[i].duration;
-    element_start_time += sequence->elements[i].duration;
-  }
-}
-
-bool AdvanceSequence(ServerWindow* window,
-                     ScheduledAnimationSequence* sequence,
-                     base::TimeTicks now) {
-  ScheduledAnimationElement* element =
-      &(sequence->elements[sequence->current_index]);
-  while (element->start_time + element->duration < now) {
-    SetWindowPropertyFromValue(window, element->property,
-                               element->target_value);
-    if (++sequence->current_index == sequence->elements.size()) {
-      if (!sequence->run_until_stopped && --sequence->cycle_count == 0) {
-        return false;
-      }
-
-      sequence->current_index = 0;
-    }
-    sequence->elements[sequence->current_index].start_time =
-        element->start_time + element->duration;
-    element = &(sequence->elements[sequence->current_index]);
-    GetStartValueFromWindowIfNecessary(window, element);
-
-    // It's possible for the delta between now and |last_tick_time_| to be very
-    // big (could happen if machine sleeps and is woken up much later). Normally
-    // the repeat count is smallish, so we don't bother optimizing it. OTOH if
-    // a sequence repeats forever we optimize it lest we get stuck in this loop
-    // for a very long time.
-    if (sequence->run_until_stopped && sequence->current_index == 0) {
-      element->start_time =
-          now - base::TimeDelta::FromMicroseconds(
-                    (now - element->start_time).InMicroseconds() %
-                    sequence->duration.InMicroseconds());
-    }
-  }
-  return true;
-}
-
-}  // namespace
-
-ScheduledAnimationValue::ScheduledAnimationValue() : float_value(0) {}
-ScheduledAnimationValue::~ScheduledAnimationValue() {}
-
-ScheduledAnimationElement::ScheduledAnimationElement()
-    : property(AnimationProperty::OPACITY),
-      tween_type(gfx::Tween::EASE_IN),
-      is_start_valid(false) {}
-ScheduledAnimationElement::ScheduledAnimationElement(
-    const ScheduledAnimationElement& other) = default;
-ScheduledAnimationElement::~ScheduledAnimationElement() {}
-
-ScheduledAnimationSequence::ScheduledAnimationSequence()
-    : run_until_stopped(false), cycle_count(0), current_index(0u) {}
-ScheduledAnimationSequence::ScheduledAnimationSequence(
-    const ScheduledAnimationSequence& other) = default;
-ScheduledAnimationSequence::~ScheduledAnimationSequence() {}
-
-ScheduledAnimationGroup::~ScheduledAnimationGroup() {}
-
-// static
-std::unique_ptr<ScheduledAnimationGroup> ScheduledAnimationGroup::Create(
-    ServerWindow* window,
-    base::TimeTicks now,
-    uint32_t id,
-    const mojom::AnimationGroup& transport_group) {
-  if (!IsAnimationGroupValid(transport_group))
-    return nullptr;
-
-  std::unique_ptr<ScheduledAnimationGroup> group(
-      new ScheduledAnimationGroup(window, id, now));
-  group->sequences_.resize(transport_group.sequences.size());
-  for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
-    const mojom::AnimationSequence& transport_sequence(
-        *(transport_group.sequences[i]));
-    DCHECK_NE(0u, transport_sequence.elements.size());
-    ConvertSequenceToScheduled(transport_sequence, now, &group->sequences_[i]);
-  }
-  return group;
-}
-
-void ScheduledAnimationGroup::ObtainStartValues() {
-  for (ScheduledAnimationSequence& sequence : sequences_)
-    GetStartValueFromWindowIfNecessary(window_, &(sequence.elements[0]));
-}
-
-void ScheduledAnimationGroup::SetValuesToTargetValuesForPropertiesNotIn(
-    const ScheduledAnimationGroup& other) {
-  std::set<AnimationProperty> our_properties;
-  GetScheduledAnimationProperties(sequences_, &our_properties);
-
-  std::set<AnimationProperty> other_properties;
-  GetScheduledAnimationProperties(other.sequences_, &other_properties);
-
-  for (AnimationProperty property : our_properties) {
-    if (other_properties.count(property) == 0 &&
-        property != AnimationProperty::NONE) {
-      SetPropertyToTargetProperty(window_, property, sequences_);
-    }
-  }
-}
-
-bool ScheduledAnimationGroup::Tick(base::TimeTicks time) {
-  for (Sequences::iterator i = sequences_.begin(); i != sequences_.end();) {
-    if (!AdvanceSequence(window_, &(*i), time)) {
-      i = sequences_.erase(i);
-      continue;
-    }
-    const ScheduledAnimationElement& active_element(
-        i->elements[i->current_index]);
-    const double percent =
-        (time - active_element.start_time).InMillisecondsF() /
-        active_element.duration.InMillisecondsF();
-    SetWindowPropertyFromValueBetween(
-        window_, active_element.property, percent, active_element.tween_type,
-        active_element.start_value, active_element.target_value);
-    ++i;
-  }
-  return sequences_.empty();
-}
-
-ScheduledAnimationGroup::ScheduledAnimationGroup(ServerWindow* window,
-                                                 uint32_t id,
-                                                 base::TimeTicks time_scheduled)
-    : window_(window), id_(id), time_scheduled_(time_scheduled) {}
-
-}  // namespace ws
-}  // namespace ui
diff --git a/services/ui/ws/scheduled_animation_group.h b/services/ui/ws/scheduled_animation_group.h
deleted file mode 100644
index 2c5b1d2..0000000
--- a/services/ui/ws/scheduled_animation_group.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_UI_WS_SCHEDULED_ANIMATION_GROUP_H_
-#define SERVICES_UI_WS_SCHEDULED_ANIMATION_GROUP_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/time/time.h"
-#include "services/ui/public/interfaces/animations.mojom.h"
-#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/transform.h"
-
-namespace ui {
-namespace ws {
-
-class ServerWindow;
-
-struct ScheduledAnimationValue {
-  ScheduledAnimationValue();
-  ~ScheduledAnimationValue();
-
-  float float_value;
-  gfx::Transform transform;
-};
-
-struct ScheduledAnimationElement {
-  ScheduledAnimationElement();
-  // Needed because we call resize() vector of elements.
-  ScheduledAnimationElement(const ScheduledAnimationElement& other);
-  ~ScheduledAnimationElement();
-
-  mojom::AnimationProperty property;
-  base::TimeDelta duration;
-  gfx::Tween::Type tween_type;
-  bool is_start_valid;
-  ScheduledAnimationValue start_value;
-  ScheduledAnimationValue target_value;
-  // Start time is based on scheduled time and relative to any other elements
-  // in the sequence.
-  base::TimeTicks start_time;
-};
-
-struct ScheduledAnimationSequence {
-  ScheduledAnimationSequence();
-  // Needed because we call resize() and erase() on vector of sequences.
-  ScheduledAnimationSequence(const ScheduledAnimationSequence& other);
-  ~ScheduledAnimationSequence();
-
-  bool run_until_stopped;
-  std::vector<ScheduledAnimationElement> elements;
-
-  // Sum of the duration of all elements. This does not take into account
-  // |cycle_count|.
-  base::TimeDelta duration;
-
-  // The following values are updated as the animation progresses.
-
-  // Number of cycles remaining. This is only used if |run_until_stopped| is
-  // false.
-  uint32_t cycle_count;
-
-  // Index into |elements| of the element currently animating.
-  size_t current_index;
-};
-
-// Corresponds to a mojom::AnimationGroup and is responsible for running the
-// actual animation.
-class ScheduledAnimationGroup {
- public:
-  ~ScheduledAnimationGroup();
-
-  // Returns a new ScheduledAnimationGroup from the supplied parameters, or
-  // null if |transport_group| isn't valid.
-  static std::unique_ptr<ScheduledAnimationGroup> Create(
-      ServerWindow* window,
-      base::TimeTicks now,
-      uint32_t id,
-      const mojom::AnimationGroup& transport_group);
-
-  uint32_t id() const { return id_; }
-
-  // Gets the start value for any elements that don't have an explicit start.
-  // value.
-  void ObtainStartValues();
-
-  // Sets the values of any properties that are not in |other| to their final
-  // value.
-  void SetValuesToTargetValuesForPropertiesNotIn(
-      const ScheduledAnimationGroup& other);
-
-  // Advances the group. |time| is the current time. Returns true if the group
-  // is done (nothing left to animate).
-  bool Tick(base::TimeTicks time);
-
-  ServerWindow* window() { return window_; }
-
- private:
-  ScheduledAnimationGroup(ServerWindow* window,
-                          uint32_t id,
-                          base::TimeTicks time_scheduled);
-
-  ServerWindow* window_;
-  const uint32_t id_;
-  base::TimeTicks time_scheduled_;
-  std::vector<ScheduledAnimationSequence> sequences_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScheduledAnimationGroup);
-};
-
-}  // namespace ws
-}  // namespace ui
-
-#endif  // SERVICES_UI_WS_SCHEDULED_ANIMATION_GROUP_H_
diff --git a/services/ui/ws/scheduled_animation_group_unittest.cc b/services/ui/ws/scheduled_animation_group_unittest.cc
deleted file mode 100644
index 3a2ca10..0000000
--- a/services/ui/ws/scheduled_animation_group_unittest.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ui/ws/scheduled_animation_group.h"
-
-#include "services/ui/public/interfaces/animations.mojom.h"
-#include "services/ui/ws/server_window.h"
-#include "services/ui/ws/test_server_window_delegate.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ui::mojom::AnimationProperty;
-using ui::mojom::AnimationTweenType;
-using ui::mojom::AnimationGroup;
-using ui::mojom::AnimationSequence;
-using ui::mojom::AnimationElement;
-using ui::mojom::AnimationValue;
-
-namespace ui {
-namespace ws {
-namespace {
-
-bool IsAnimationGroupValid(const AnimationGroup& transport_group) {
-  TestServerWindowDelegate window_delegate;
-  ServerWindow window(&window_delegate, WindowId());
-  std::unique_ptr<ScheduledAnimationGroup> group(
-      ScheduledAnimationGroup::Create(&window, base::TimeTicks::Now(), 1,
-                                      transport_group));
-  return group.get() != nullptr;
-}
-
-}  // namespace
-
-TEST(ScheduledAnimationGroupTest, IsAnimationGroupValid) {
-  AnimationGroup group;
-
-  // AnimationGroup with no sequences is not valid.
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  group.sequences.push_back(AnimationSequence::New());
-
-  // Sequence with no elements is not valid.
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  AnimationSequence& sequence = *(group.sequences[0]);
-  sequence.elements.push_back(AnimationElement::New());
-  AnimationElement& element = *(sequence.elements[0]);
-  element.property = AnimationProperty::OPACITY;
-  element.tween_type = AnimationTweenType::LINEAR;
-
-  // Element with no target_value is not valid.
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  // Opacity must be between 0 and 1.
-  element.target_value = AnimationValue::New();
-  element.target_value->float_value = 2.5f;
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  element.target_value->float_value = .5f;
-  EXPECT_TRUE(IsAnimationGroupValid(group));
-
-  // Bogus start value.
-  element.start_value = AnimationValue::New();
-  element.start_value->float_value = 2.5f;
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  element.start_value->float_value = .5f;
-  EXPECT_TRUE(IsAnimationGroupValid(group));
-
-  // All transforms are valid.
-  element.property = AnimationProperty::TRANSFORM;
-  EXPECT_TRUE(IsAnimationGroupValid(group));
-
-  // Add another empty sequence, should be invalid again.
-  group.sequences.push_back(AnimationSequence::New());
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  AnimationSequence& sequence2 = *(group.sequences[1]);
-  sequence2.elements.push_back(AnimationElement::New());
-  AnimationElement& element2 = *(sequence2.elements[0]);
-  element2.property = AnimationProperty::OPACITY;
-  element2.tween_type = AnimationTweenType::LINEAR;
-
-  // Element with no target_value is not valid.
-  EXPECT_FALSE(IsAnimationGroupValid(group));
-
-  element2.property = AnimationProperty::NONE;
-  EXPECT_TRUE(IsAnimationGroupValid(group));
-}
-
-}  // namespace ws
-}  // namespace ui
diff --git a/services/ui/ws/server_window_delegate.h b/services/ui/ws/server_window_delegate.h
index 26784fa..035bc1e 100644
--- a/services/ui/ws/server_window_delegate.h
+++ b/services/ui/ws/server_window_delegate.h
@@ -29,6 +29,9 @@
   // DisplayCompositor running in the system.
   virtual cc::mojom::DisplayCompositor* GetDisplayCompositor() = 0;
 
+  // Returns the root surface id that was received from DisplayCompositor.
+  virtual const cc::SurfaceId& GetRootSurfaceId() const = 0;
+
   // Returns the root of the window tree to which this |window| is attached.
   // Returns null if this window is not attached up through to a root window.
   virtual ServerWindow* GetRootWindow(const ServerWindow* window) = 0;
diff --git a/services/ui/ws/test_server_window_delegate.cc b/services/ui/ws/test_server_window_delegate.cc
index a19ee6f..6465ac4 100644
--- a/services/ui/ws/test_server_window_delegate.cc
+++ b/services/ui/ws/test_server_window_delegate.cc
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ui/ws/server_window.h"
 #include "services/ui/ws/test_server_window_delegate.h"
 
-namespace ui {
+#include "services/ui/ws/server_window.h"
 
+namespace ui {
 namespace ws {
 
-TestServerWindowDelegate::TestServerWindowDelegate()
-    : root_window_(nullptr) {}
+TestServerWindowDelegate::TestServerWindowDelegate() {}
 
 TestServerWindowDelegate::~TestServerWindowDelegate() {}
 
@@ -18,11 +17,14 @@
   return nullptr;
 }
 
+const cc::SurfaceId& TestServerWindowDelegate::GetRootSurfaceId() const {
+  return root_surface_id_;
+}
+
 ServerWindow* TestServerWindowDelegate::GetRootWindow(
     const ServerWindow* window) {
   return root_window_;
 }
 
 }  // namespace ws
-
 }  // namespace ui
diff --git a/services/ui/ws/test_server_window_delegate.h b/services/ui/ws/test_server_window_delegate.h
index 4c8d851..8c7a8911 100644
--- a/services/ui/ws/test_server_window_delegate.h
+++ b/services/ui/ws/test_server_window_delegate.h
@@ -6,6 +6,7 @@
 #define SERVICES_UI_WS_TEST_SERVER_WINDOW_DELEGATE_H_
 
 #include "base/macros.h"
+#include "cc/surfaces/surface_id.h"
 #include "services/ui/ws/server_window_delegate.h"
 
 namespace cc {
@@ -15,7 +16,6 @@
 }
 
 namespace ui {
-
 namespace ws {
 
 class TestServerWindowDelegate : public ServerWindowDelegate {
@@ -28,15 +28,16 @@
  private:
   // ServerWindowDelegate:
   cc::mojom::DisplayCompositor* GetDisplayCompositor() override;
+  const cc::SurfaceId& GetRootSurfaceId() const override;
   ServerWindow* GetRootWindow(const ServerWindow* window) override;
 
-  ServerWindow* root_window_;
+  cc::SurfaceId root_surface_id_;
+  ServerWindow* root_window_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TestServerWindowDelegate);
 };
 
 }  // namespace ws
-
 }  // namespace ui
 
 #endif  // SERVICES_UI_WS_TEST_SERVER_WINDOW_DELEGATE_H_
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 381daf2..505042f 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -525,6 +525,11 @@
   return display_compositor_.get();
 }
 
+const cc::SurfaceId& WindowServer::GetRootSurfaceId() const {
+  DCHECK(root_surface_id_.local_frame_id().is_valid());
+  return root_surface_id_;
+}
+
 bool WindowServer::GetFrameDecorationsForUser(
     const UserId& user_id,
     mojom::FrameDecorationValuesPtr* values) {
@@ -804,6 +809,11 @@
   }
 }
 
+void WindowServer::OnDisplayCompositorCreated(
+    const cc::SurfaceId& root_surface_id) {
+  root_surface_id_ = root_surface_id;
+}
+
 void WindowServer::OnActiveUserIdChanged(const UserId& previously_active_id,
                                          const UserId& active_id) {
 }
diff --git a/services/ui/ws/window_server.h b/services/ui/ws/window_server.h
index 17e335d..a965f39 100644
--- a/services/ui/ws/window_server.h
+++ b/services/ui/ws/window_server.h
@@ -228,6 +228,7 @@
 
   // ServerWindowDelegate:
   cc::mojom::DisplayCompositor* GetDisplayCompositor() override;
+  const cc::SurfaceId& GetRootSurfaceId() const override;
 
   // UserDisplayManagerDelegate:
   bool GetFrameDecorationsForUser(
@@ -330,6 +331,8 @@
   void OnSurfaceCreated(const cc::SurfaceId& surface_id,
                         const gfx::Size& frame_size,
                         float device_scale_factor) override;
+  void OnDisplayCompositorCreated(
+      const cc::SurfaceId& root_surface_id) override;
 
   // UserIdTrackerObserver:
   void OnActiveUserIdChanged(const UserId& previously_active_id,
@@ -373,6 +376,8 @@
 
   WindowManagerWindowTreeFactorySet window_manager_window_tree_factory_set_;
 
+  cc::SurfaceId root_surface_id_;
+
   mojo::Binding<cc::mojom::DisplayCompositorClient>
       display_compositor_client_binding_;
   // State for rendering into a Surface.
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index a85c4a2..4b469fd 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -1330,6 +1330,10 @@
     return;
   }
 
+  DVLOG(3) << "set window bounds client window_id=" << window_id
+           << " global window_id="
+           << (window ? WindowIdToTransportId(window->id()) : 0)
+           << " bounds=" << bounds.ToString();
   // Only the owner of the window can change the bounds.
   bool success = window && access_policy_->CanSetWindowBounds(window);
   if (success) {
diff --git a/services/ui/ws/window_tree_client_unittest.cc b/services/ui/ws/window_tree_client_unittest.cc
index fe6a01a5..6cdb7ec 100644
--- a/services/ui/ws/window_tree_client_unittest.cc
+++ b/services/ui/ws/window_tree_client_unittest.cc
@@ -2164,8 +2164,7 @@
   cc::CompositorFrame compositor_frame;
   std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
   gfx::Rect frame_rect(0, 0, 100, 100);
-  render_pass->SetNew(cc::RenderPassId(1, 1), frame_rect, frame_rect,
-                      gfx::Transform());
+  render_pass->SetNew(1, frame_rect, frame_rect, gfx::Transform());
   compositor_frame.render_pass_list.push_back(std::move(render_pass));
   cc::LocalFrameId local_frame_id(1, base::UnguessableToken::Create());
   surface_ptr->SubmitCompositorFrame(local_frame_id,
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index b239fa0..be2084ee 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -19,17 +19,8 @@
 skia_support_gpu = !is_ios
 skia_support_pdf = !is_ios && (enable_basic_printing || enable_print_preview)
 
-# When building Chrome for iOS with GYP, the target cpu is not known during
-# the invocation of gyp. This cause the iOS build to use non-optimised skia.
-# Replicate this with GN to avoid introducing regression as recommended by
-# the OWNERS of skia.
-
 declare_args() {
   skia_whitelist_serialized_typefaces = false
-
-  # TODO(crbug.com/607933): Once GYP is no longer supported, port iOS to use
-  # optimised skia.
-  skia_build_no_opts = is_ios
 }
 
 # External-facing config for dependent code.
@@ -55,10 +46,6 @@
 
   defines += []
 
-  if (skia_build_no_opts) {
-    defines += [ "SK_BUILD_NO_OPTS" ]
-  }
-
   if (is_component_build) {
     defines += [
       "SKIA_DLL",
@@ -127,7 +114,7 @@
     defines += [ "SKIA_IMPLEMENTATION=1" ]
   }
 
-  if (current_cpu == "arm" && !skia_build_no_opts) {
+  if (current_cpu == "arm") {
     if (arm_use_neon) {
       defines += [ "SK_ARM_HAS_NEON" ]
     } else if (arm_optionally_use_neon) {
@@ -485,114 +472,112 @@
 }
 
 # Separated out so it can be compiled with different flags for SSE.
-if (!skia_build_no_opts) {
-  if (current_cpu == "arm64") {
-    source_set("skia_opts_crc32") {
-      sources = skia_opts.crc32_sources
-      cflags = [ "-march=armv8-a+crc" ]
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
-      ]
-    }
+if (current_cpu == "arm64") {
+  source_set("skia_opts_crc32") {
+    sources = skia_opts.crc32_sources
+    cflags = [ "-march=armv8-a+crc" ]
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
   }
-  if (current_cpu == "x86" || current_cpu == "x64") {
-    source_set("skia_opts_sse3") {
-      sources = skia_opts.ssse3_sources
-      if (!is_win || is_clang) {
-        cflags = [ "-mssse3" ]
-      }
-      if (is_win) {
-        defines = [ "SK_CPU_SSE_LEVEL=31" ]
-      }
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
+}
+if (current_cpu == "x86" || current_cpu == "x64") {
+  source_set("skia_opts_sse3") {
+    sources = skia_opts.ssse3_sources
+    if (!is_win || is_clang) {
+      cflags = [ "-mssse3" ]
+    }
+    if (is_win) {
+      defines = [ "SK_CPU_SSE_LEVEL=31" ]
+    }
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
+  }
+  source_set("skia_opts_sse41") {
+    sources = skia_opts.sse41_sources
+    if (!is_win || is_clang) {
+      cflags = [ "-msse4.1" ]
+    }
+    if (is_win) {
+      defines = [ "SK_CPU_SSE_LEVEL=41" ]
+    }
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
+  }
+  source_set("skia_opts_sse42") {
+    sources = skia_opts.sse42_sources
+    if (!is_win || is_clang) {
+      cflags = [ "-msse4.2" ]
+    }
+    if (is_win) {
+      defines = [ "SK_CPU_SSE_LEVEL=42" ]
+    }
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
+  }
+  source_set("skia_opts_avx") {
+    sources = skia_opts.avx_sources
+    if (!is_win) {
+      cflags = [ "-mavx" ]
+    }
+    if (is_win) {
+      cflags = [ "/arch:AVX" ]
+    }
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
+  }
+  source_set("skia_opts_hsw") {
+    # SyzyAsan doesn't support the AVX2 and F16C instructions.
+    if (!is_syzyasan) {
+      sources = skia_opts.hsw_sources
+    } else {
+      sources = [
+        "ext/SkOpts_hsw_stub.cc",
       ]
     }
-    source_set("skia_opts_sse41") {
-      sources = skia_opts.sse41_sources
-      if (!is_win || is_clang) {
-        cflags = [ "-msse4.1" ]
-      }
-      if (is_win) {
-        defines = [ "SK_CPU_SSE_LEVEL=41" ]
-      }
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
+    if (!is_win) {
+      cflags = [
+        "-mavx2",
+        "-mbmi",
+        "-mbmi2",
+        "-mf16c",
+        "-mfma",
       ]
     }
-    source_set("skia_opts_sse42") {
-      sources = skia_opts.sse42_sources
-      if (!is_win || is_clang) {
-        cflags = [ "-msse4.2" ]
-      }
-      if (is_win) {
-        defines = [ "SK_CPU_SSE_LEVEL=42" ]
-      }
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
-      ]
+    if (is_win && !is_syzyasan) {
+      cflags = [ "/arch:AVX2" ]
     }
-    source_set("skia_opts_avx") {
-      sources = skia_opts.avx_sources
-      if (!is_win) {
-        cflags = [ "-mavx" ]
-      }
-      if (is_win) {
-        cflags = [ "/arch:AVX" ]
-      }
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
-      ]
-    }
-    source_set("skia_opts_hsw") {
-      # SyzyAsan doesn't support the AVX2 and F16C instructions.
-      if (!is_syzyasan) {
-        sources = skia_opts.hsw_sources
-      } else {
-        sources = [
-          "ext/SkOpts_hsw_stub.cc",
-        ]
-      }
-      if (!is_win) {
-        cflags = [
-          "-mavx2",
-          "-mbmi",
-          "-mbmi2",
-          "-mf16c",
-          "-mfma",
-        ]
-      }
-      if (is_win && !is_syzyasan) {
-        cflags = [ "/arch:AVX2" ]
-      }
-      visibility = [ ":skia_opts" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [
-        ":skia_config",
-        ":skia_library_config",
-        "//build/config/compiler:no_chromium_code",
-      ]
-    }
+    visibility = [ ":skia_opts" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [
+      ":skia_config",
+      ":skia_library_config",
+      "//build/config/compiler:no_chromium_code",
+    ]
   }
 }
 
@@ -604,9 +589,7 @@
     "//base",
   ]
 
-  if (skia_build_no_opts) {
-    sources = skia_opts.none_sources
-  } else if (current_cpu == "x86" || current_cpu == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     sources = skia_opts.sse2_sources
     deps += [
       ":skia_opts_avx",
@@ -618,7 +601,9 @@
   } else if (current_cpu == "arm") {
     # The assembly uses the frame pointer register (r7 in Thumb/r11 in
     # ARM), the compiler doesn't like that.
-    cflags += [ "-fomit-frame-pointer" ]
+    if (!is_ios) {
+      cflags += [ "-fomit-frame-pointer" ]
+    }
 
     if (arm_version >= 7) {
       sources = skia_opts.armv7_sources
diff --git a/skia/ext/platform_device.h b/skia/ext/platform_device.h
index a32caab..695c6b7 100644
--- a/skia/ext/platform_device.h
+++ b/skia/ext/platform_device.h
@@ -17,7 +17,6 @@
 #include "third_party/skia/include/core/SkTypes.h"
 
 class SkMatrix;
-class SkPath;
 
 namespace skia {
 
diff --git a/skia/ext/skia_trace_memory_dump_impl.h b/skia/ext/skia_trace_memory_dump_impl.h
index e159a9c..5ab5a2f 100644
--- a/skia/ext/skia_trace_memory_dump_impl.h
+++ b/skia/ext/skia_trace_memory_dump_impl.h
@@ -15,7 +15,6 @@
 
 namespace base {
 namespace trace_event {
-class MemoryAllocatorDump;
 class ProcessMemoryDump;
 }
 }
diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h
index e88c110..8519cba 100644
--- a/skia/ext/skia_utils_mac.h
+++ b/skia/ext/skia_utils_mac.h
@@ -13,7 +13,6 @@
 #include "third_party/skia/include/core/SkPixmap.h"
 
 struct SkIRect;
-struct SkPoint;
 struct SkRect;
 class SkCanvas;
 class SkMatrix;
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index fe982fa0..eec63dfb 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -4642,6 +4642,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-gl"
         ],
         "name": "angle_deqp_gles2_gl_tests",
@@ -4660,6 +4661,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-gl"
         ],
         "name": "angle_deqp_gles31_gl_tests",
@@ -4678,6 +4680,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-gl"
         ],
         "name": "angle_deqp_gles3_gl_tests",
@@ -9544,6 +9547,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-gl"
         ],
         "name": "angle_deqp_gles2_gl_tests",
@@ -9851,6 +9855,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles2_d3d11_tests",
@@ -10016,6 +10021,9 @@
   "Optional Win7 Release (NVIDIA)": {
     "gtest_tests": [
       {
+        "args": [
+          "--test-launcher-batch-limit=400"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -10030,6 +10038,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles2_d3d11_tests",
@@ -10215,6 +10224,466 @@
       }
     ]
   },
+  "Win10 Debug (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "angle_end2end_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "angle_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gl_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=d3d9"
+        ],
+        "name": "gles2_conform_d3d9_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=gl",
+          "--disable-gpu-sandbox"
+        ],
+        "name": "gles2_conform_gl_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "gpu_process_launch_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "gpu_rasterization",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "gpu_rasterization_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "hardware_accelerated_feature_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--os-type",
+          "win",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "maps_pixel_test",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "win",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "screenshot_sync_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "trace_test",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d9_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=gl"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_gl_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=debug",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      }
+    ]
+  },
   "Win10 Debug (New Intel)": {
     "gtest_tests": [
       {
@@ -10675,6 +11144,508 @@
       }
     ]
   },
+  "Win10 Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "angle_end2end_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "angle_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "audio_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gl_unittests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=d3d9"
+        ],
+        "name": "gles2_conform_d3d9_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=gl",
+          "--disable-gpu-sandbox"
+        ],
+        "name": "gles2_conform_gl_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "gles2_conform_test",
+        "use_xvfb": false
+      },
+      {
+        "override_compile_targets": [
+          "tab_capture_end2end_tests_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        },
+        "test": "tab_capture_end2end_tests",
+        "use_xvfb": false
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "context_lost_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "depth_capture_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "gpu_process_launch_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "gpu_rasterization",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "gpu_rasterization_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "hardware_accelerated_feature_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--os-type",
+          "win",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "maps_pixel_test",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--refimg-cloud-storage-bucket",
+          "chromium-gpu-archive/reference-images",
+          "--os-type",
+          "win",
+          "--build-revision",
+          "${got_revision}",
+          "--test-machine-name",
+          "${buildername}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "pixel_test",
+        "non_precommit_args": [
+          "--upload-refimg-to-cloud-storage"
+        ],
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "precommit_args": [
+          "--download-refimg-from-cloud-storage"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "screenshot_sync_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_test",
+        "name": "trace_test",
+        "override_compile_targets": [
+          "telemetry_gpu_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl2_conformance_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ],
+          "shards": 15
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_d3d9_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=gl"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_gl_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "name": "webgl_conformance_tests",
+        "override_compile_targets": [
+          "telemetry_gpu_integration_test_run"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10-10586"
+            }
+          ]
+        }
+      }
+    ]
+  },
   "Win10 Release (New Intel)": {
     "gtest_tests": [
       {
@@ -12988,6 +13959,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles2_d3d11_tests",
@@ -14459,6 +15431,9 @@
   "Win7 Release (NVIDIA)": {
     "gtest_tests": [
       {
+        "args": [
+          "--test-launcher-batch-limit=400"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -14473,6 +15448,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles2_d3d11_tests",
@@ -14491,6 +15467,26 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
+          "--deqp-egl-display-type=angle-d3d11"
+        ],
+        "name": "angle_deqp_gles31_d3d11_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_deqp_gles31_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-gl"
         ],
         "name": "angle_deqp_gles31_gl_tests",
@@ -14509,6 +15505,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles3_d3d11_tests",
@@ -15964,6 +16961,9 @@
   "Win7 x64 Release (NVIDIA)": {
     "gtest_tests": [
       {
+        "args": [
+          "--test-launcher-batch-limit=400"
+        ],
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -15978,6 +16978,7 @@
       },
       {
         "args": [
+          "--test-launcher-batch-limit=400",
           "--deqp-egl-display-type=angle-d3d11"
         ],
         "name": "angle_deqp_gles2_d3d11_tests",
@@ -16506,7 +17507,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16523,7 +17524,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16539,7 +17540,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16555,7 +17556,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16571,7 +17572,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16589,7 +17590,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16608,7 +17609,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16624,7 +17625,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16651,7 +17652,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16674,7 +17675,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16697,7 +17698,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16720,7 +17721,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16743,7 +17744,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16772,7 +17773,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16809,7 +17810,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16832,7 +17833,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16855,7 +17856,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16878,7 +17879,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16901,7 +17902,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16924,7 +17925,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16947,7 +17948,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -16966,7 +17967,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16983,7 +17984,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -16999,7 +18000,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17015,7 +18016,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17031,7 +18032,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17049,7 +18050,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17068,7 +18069,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17084,7 +18085,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17100,7 +18101,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         },
@@ -17127,7 +18128,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17150,7 +18151,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17173,7 +18174,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17196,7 +18197,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17219,7 +18220,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17248,7 +18249,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17285,7 +18286,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17308,7 +18309,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17331,7 +18332,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17356,7 +18357,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ],
           "shards": 15
@@ -17380,7 +18381,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17403,7 +18404,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17426,7 +18427,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
@@ -17449,7 +18450,7 @@
           "dimension_sets": [
             {
               "gpu": "10de:104a",
-              "os": "Windows-2012ServerR2-SP0"
+              "os": "Windows-10-10586"
             }
           ]
         }
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
index 998192dc..9332c7b4 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -1,6 +1,5 @@
 -ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled
 -PDFExtensionTest.PdfAccessibilityInOOPIF
--PlatformAppBrowserTest.ActiveAppsAreRecorded
 -PlatformAppUrlRedirectorBrowserTest.PrerenderedClickInTabIntercepted
 -PredictorBrowserTest.RendererInitiatedNavigationPreconnect
 -PrerenderBrowserTest.PrerenderCrossProcessServerRedirect
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
index 091cd4552..60e438de 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -1,9 +1,6 @@
 -NavigationControllerBrowserTest.EnsureSamePageNavigationUpdatesFrameNavigationEntry
 -RequestDataResourceDispatcherHostBrowserTest.*
 
-# NavigationPreload dosn't work well with PlzNavigate yet. https://crbug.com/649558
--ServiceWorkerBrowserTest/ServiceWorkerNavigationPreloadTest.*
-
 # Browser-initiated fragment navigations are handled improperly. https://crbug.com/663777
 -NavigationControllerBrowserTest.SamePageBrowserInitiated
 
diff --git a/testing/buildbot/filters/site-per-process.browser_tests.filter b/testing/buildbot/filters/site-per-process.browser_tests.filter
index f0b0ba2..06787d05 100644
--- a/testing/buildbot/filters/site-per-process.browser_tests.filter
+++ b/testing/buildbot/filters/site-per-process.browser_tests.filter
@@ -8,9 +8,12 @@
 -ProcessManagementTest.ProcessOverflow
 -RedirectTest.ClientEmptyReferer
 -ReferrerPolicyTest.HttpsRedirect
+
+# crbug.com/671734: WebNavigationApiTest.ServerRedirectSingleProcess from
+# fails with --site-per-process
 -WebNavigationApiTest.ServerRedirectSingleProcess
 
-# crbug.com/448592: Get extension browsertests to pass with --site-per-process
+# crbug.com/671712: All 4 IsolatedAppTest tests time out with --site-per-process
 -IsolatedAppTest.CookieIsolation
 -IsolatedAppTest.CrossProcessClientRedirect
 -IsolatedAppTest.IsolatedAppProcessModel
diff --git a/testing/chromoting/browser_test_commands_linux.txt b/testing/chromoting/browser_test_commands_linux.txt
index 6e7b487..df1a6fd 100644
--- a/testing/chromoting/browser_test_commands_linux.txt
+++ b/testing/chromoting/browser_test_commands_linux.txt
@@ -1,3 +1,3 @@
-/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch:RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail
-/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Remote_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile --remote-host-name=crd-precise.kir.corp.google.com
-/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_CancelShare --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
+/usr/bin/python ../xvfb.py #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch:RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail
+/usr/bin/python ../xvfb.py #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Remote_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile --remote-host-name=crd-precise.kir.corp.google.com
+/usr/bin/python ../xvfb.py #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_CancelShare --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
diff --git a/testing/scripts/gtest_perf_test.py b/testing/scripts/gtest_perf_test.py
index c40d914..7810cbc1 100755
--- a/testing/scripts/gtest_perf_test.py
+++ b/testing/scripts/gtest_perf_test.py
@@ -67,10 +67,7 @@
 
 
 def main_compile_targets(args):
-  if 'android' == args.properties.get('target_platform'):
-    json.dump(['${name}_apk'], args.output)
-  else:
-    json.dump(['$name'], args.output)
+  json.dump(['$name'], args.output)
 
 
 if __name__ == '__main__':
diff --git a/testing/scripts/run_telemetry_as_googletest.py b/testing/scripts/run_telemetry_as_googletest.py
index 77f45e8b..8d3a388 100755
--- a/testing/scripts/run_telemetry_as_googletest.py
+++ b/testing/scripts/run_telemetry_as_googletest.py
@@ -67,7 +67,7 @@
   cmd = [sys.executable] + rest_args + sharding_args + [
       '--write-full-results-to', args.isolated_script_test_output]
   if args.xvfb:
-    return xvfb.run_executable(cmd, '.', env)
+    return xvfb.run_executable(cmd, env)
   else:
     return common.run_command(cmd, env=env)
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index cc4bd23..26048a6 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -218,10 +218,6 @@
                     "name": "EnableViaFlag",
                     "forcing_flag": "enable-browser-task-scheduler",
                     "params": {
-                        "Background": "3;8;0.1;0;30000",
-                        "BackgroundFileIO": "3;8;0.1;0;30000",
-                        "Foreground": "8;32;0.3;0;30000",
-                        "ForegroundFileIO": "8;32;0.3;0;30000",
                         "RedirectHistoryService": "true",
                         "RedirectSequencedWorkerPools": "true"
                     }
@@ -784,6 +780,27 @@
             ]
         }
     ],
+    "Html5ByDefault": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "engagement_threshold_for_flash": "1"
+                    },
+                    "enable_features": [
+                        "PreferHtmlOverPlugins"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSurfaceClearYosemite": [
         {
             "platforms": [
@@ -1485,24 +1502,6 @@
             ]
         }
     ],
-    "PreferHtmlOverPlugins": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PreferHtmlOverPlugins"
-                    ]
-                }
-            ]
-        }
-    ],
     "PrintScaling": [
         {
             "platforms": [
@@ -1904,6 +1903,21 @@
             ]
         }
     ],
+    "SeccompSandboxAndroid": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "SandboxEnabled",
+                    "enable_features": [
+                        "SeccompSandboxAndroid"
+                    ]
+                }
+            ]
+        }
+    ],
     "SecurityChip": [
         {
             "platforms": [
@@ -2307,6 +2321,21 @@
             ]
         }
     ],
+    "VrShell": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "VrShell"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebBluetoothBlocklist": [
         {
             "platforms": [
diff --git a/testing/xvfb.py b/testing/xvfb.py
index 640501d..54d4184 100755
--- a/testing/xvfb.py
+++ b/testing/xvfb.py
@@ -42,7 +42,7 @@
     print >> sys.stderr, 'Xvfb running after SIGTERM and SIGKILL; good luck!'
 
 
-def run_executable(cmd, build_dir, env):
+def run_executable(cmd, env):
   """Runs an executable within Xvfb on Linux or normally on other platforms.
 
   Returns the exit code of the specified commandline, or 1 on failure.
@@ -71,17 +71,17 @@
       env['_CHROMIUM_INSIDE_XVFB'] = '1'
       return subprocess.call(['xvfb-run', '-a', "--server-args=-screen 0 "
                               "1280x800x24 -ac -nolisten tcp -dpi 96",
-                              __file__, build_dir] + cmd, env=env)
+                              __file__] + cmd, env=env)
   else:
     return test_env.run_executable(cmd, env)
 
 
 def main():
-  if len(sys.argv) < 3:
+  if len(sys.argv) < 2:
     print >> sys.stderr, (
-        'Usage: xvfb.py [path to build_dir] [command args...]')
+        'Usage: xvfb.py [command args...]')
     return 2
-  return run_executable(sys.argv[2:], sys.argv[1], os.environ.copy())
+  return run_executable(sys.argv[1:], os.environ.copy())
 
 
 if __name__ == "__main__":
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 4fe49ce6..f93adfb 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -246,6 +246,7 @@
 Bug(none) compositing/layer-creation/fixed-position-out-of-view.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-scroll.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-under-transform.html [ Failure ]
+crbug.com/671838 compositing/layer-creation/iframe-background-attachment-fixed.html [ Failure ]
 Bug(none) compositing/layer-creation/no-compositing-for-fixed-position-under-transform.html [ Failure ]
 Bug(none) compositing/layer-creation/no-compositing-for-preserve-3d.html [ Failure ]
 Bug(none) compositing/layer-creation/overflow-scroll-overlap.html [ Failure ]
@@ -316,6 +317,9 @@
 Bug(none) compositing/overflow/scrollbar-layer-placement.html [ Failure ]
 Bug(none) compositing/overflow/scroller-with-border-radius.html [ Failure ]
 Bug(none) compositing/overflow/scrolling-content-clip-to-viewport.html [ Failure ]
+crbug.com/667946 compositing/overflow/scrolls-with-respect-to-nested.html [ Failure ]
+crbug.com/667946 compositing/overflow/scrolls-with-respect-to-transform.html [ Failure ]
+crbug.com/667946 compositing/overflow/scrolls-with-respect-to.html [ Failure ]
 Bug(none) compositing/overflow/textarea-scroll-touch.html [ Failure ]
 Bug(none) compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure ]
 Bug(none) compositing/overlap-blending/children-opacity-huge.html [ Failure ]
@@ -600,6 +604,7 @@
 Bug(none) fast/encoding/utf-16-big-endian.html [ Failure ]
 Bug(none) fast/encoding/utf-16-little-endian.html [ Failure ]
 Bug(none) fast/events/autoscroll.html [ Failure ]
+crbug.com/667946 fast/events/autoscroll-disabled-in-fix.html [ Skip ]
 Bug(none) fast/events/context-no-deselect.html [ Failure ]
 Bug(none) fast/events/gesture-pinch-zoom-scroll-bubble.html [ Failure ]
 Bug(none) fast/events/keyboard-scroll-by-page.html [ Failure ]
@@ -1225,6 +1230,7 @@
 Bug(none) svg/clip-path/clipper-placement-issue.svg [ Failure ]
 Bug(none) svg/clip-path/deep-nested-clip-in-mask-panning.svg [ Failure ]
 Bug(none) svg/clip-path/multiple-nested-clip-paths-crash.html [ Failure ]
+Bug(none) svg/clip-path/nested-empty-clip.html [ Failure ]
 Bug(none) svg/css/text-shadow-multiple.xhtml [ Failure ]
 Bug(none) svg/custom/absolute-root-position-masking.xhtml [ Failure ]
 Bug(none) svg/custom/alignment-baseline-modes.svg [ Failure ]
@@ -2152,6 +2158,10 @@
 Bug(none) fast/overflow/overflow-float-stacking.html [ Failure ]
 Bug(none) fast/overflow/overflow-stacking.html [ Failure ]
 
+# Paint property under-invalidation for clip changes needs investigation.
+crbug.com/645667 compositing/clip-change.html [ Failure ]
+crbug.com/645667 compositing/layer-creation/iframe-clip-removed.html [ Failure ]
+
 # Notes about rebaselined tests:
 #
 # Rebaselined for small pixel differences.
diff --git a/third_party/WebKit/LayoutTests/MSANExpectations b/third_party/WebKit/LayoutTests/MSANExpectations
index e76b7b3..9caa7f82 100644
--- a/third_party/WebKit/LayoutTests/MSANExpectations
+++ b/third_party/WebKit/LayoutTests/MSANExpectations
@@ -36,10 +36,6 @@
 crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-detached-dom-tree.html [ Timeout ]
 crbug.com/462190 [ Linux ] inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.html [ Timeout ]
 
-crbug.com/671158 [ Linux ] http/tests/streams/writable-streams/write.https.html [ Timeout Pass ]
-crbug.com/671158 [ Linux ] imported/wpt/encoding/textdecoder-labels.html [ Timeout Pass ]
-crbug.com/671158 [ Linux ] virtual/mojo-loading/http/tests/streams/writable-streams/write.https.html [ Timeout Pass ]
-
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag-replace-state.html [ Timeout Pass ]
 crbug.com/671556 [ Linux ] virtual/mojo-loading/http/tests/security/xssAuditor/report-script-tag.html [ Timeout Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/RandomOrderExpectations b/third_party/WebKit/LayoutTests/RandomOrderExpectations
index b1be9acc..1d48c04 100644
--- a/third_party/WebKit/LayoutTests/RandomOrderExpectations
+++ b/third_party/WebKit/LayoutTests/RandomOrderExpectations
@@ -110,7 +110,6 @@
 crbug.com/664841 virtual/mojo-loading/http/tests/workers/worker-performance-timeline.html [ Pass Failure ]
 crbug.com/664843 inspector/elements/styles-4/styles-update-from-js.html [ Pass Failure ]
 crbug.com/664842 inspector-protocol/heap-profiler/heap-snapshot-with-active-dom-object.html [ Pass Failure ]
-crbug.com/664845 media/autoplay-clears-autoplaying-flag.html [ Pass Failure ]
 crbug.com/664844 media/track/track-cue-rendering-tree-is-removed-properly.html [ Pass Failure ]
 crbug.com/664844 media/track/track-default-attribute.html [ Pass Failure ]
 crbug.com/664844 media/track/track-kind-user-preference.html [ Pass Failure ]
@@ -170,18 +169,3 @@
 crbug.com/664859 virtual/threaded/transitions/transition-end-event-unprefixed-02.html [ Pass Failure ]
 crbug.com/664859 virtual/threaded/transitions/transition-end-event-window.html [ Pass Failure ]
 crbug.com/664859 virtual/threaded/transitions/unprefixed-transform.html [ Pass Failure ]
-
-# Adding these temporarily until the Linux builder is upgraded from Precise to Trusty
-crbug.com/670841 [ Linux ] fast/canvas/canvas-composite-video-shadow.html [ Skip ]
-crbug.com/670841 [ Linux ] fast/text/complex-text-opacity.html [ Skip ]
-crbug.com/670841 [ Linux ] fast/text/font-fallback.html [ Skip ]
-crbug.com/670841 [ Linux ] fast/text/international/complex-character-based-fallback.html [ Skip ]
-crbug.com/670841 [ Linux ] fast/text/international/complex-joining-using-gpos.html [ Skip ]
-crbug.com/670841 [ Linux ] inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.html [ Skip ]
-crbug.com/670841 [ Linux ] svg/custom/control-points-for-S-and-T.svg [ Skip ]
-crbug.com/670841 [ Linux ] svg/W3C-SVG-1.1/paths-data-03-f.svg [ Skip ]
-crbug.com/670841 [ Linux ] svg/W3C-SVG-1.1/text-align-08-b.svg [ Skip ]
-crbug.com/670841 [ Linux ] transforms/2d/hindi-rotated.html [ Skip ]
-crbug.com/670841 [ Linux ] virtual/gpu/fast/canvas/canvas-arc-circumference-fill.html [ Skip ]
-crbug.com/670841 [ Linux ] virtual/gpu/fast/canvas/canvas-lost-gpu-context.html [ Skip ]
-crbug.com/670841 [ Linux ] virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 392a0f05..51a57d7 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -387,3 +387,8 @@
 crbug.com/660468 [ Linux ] storage/indexeddb/observer-workers.html [ Slow ]
 
 crbug.com/660492 [ Linux ] storage/indexeddb/structured-clone.html [ Slow ]
+
+# These tests were timing out on the MSAN builder.
+crbug.com/671158 [ Linux ] imported/wpt/encoding/textdecoder-labels.html [ Slow ]
+crbug.com/671158 [ Linux ] http/tests/streams/writable-streams/write.https.html [ Slow ]
+crbug.com/671158 [ Linux ] virtual/mojo-loading/http/tests/streams/writable-streams/write.https.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 6a26b0d..c67f4b9b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -91,6 +91,40 @@
 crbug.com/626748 virtual/spinvalidation/paint/invalidation/table/cached-change-row-border-color.html [ Skip ]
 crbug.com/626748 virtual/spinvalidation/paint/invalidation/table/cached-change-tbody-border-color.html [ Skip ]
 
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-morphology.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-gaussianblur.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-2.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-morphology-yonly.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-gaussianblur-yonly.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-6.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-morphology-xonly.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-displacement.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-lighting.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-offset.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-1.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-4.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-3.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-gaussianblur-xonly.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-composite-5.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/outline-clip-change.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filters/effect-reference-repaint-merge.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filter-repaint-accelerated-on-accelerated-filter.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/position-change-keeping-geometry.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/inner-svg-change-viewBox.svg [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/relative-sized-inner-svg.xhtml [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/marker-viewBox-changes.svg [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/container-repaint.svg [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/text-viewbox-rescale.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/inner-svg-change-viewBox-contract.svg [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/relative-sized-use-on-symbol.xhtml [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/relative-sized-shadow-tree-content-with-symbol.xhtml [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/svg/relative-sized-use-without-attributes-on-symbol.xhtml [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/compositing/should-not-repaint-composited-descendants.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/compositing/resize-repaint.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/compositing/shrink-layer.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants.html [ Skip ]
+crbug.com/645667 virtual/spinvalidation/paint/invalidation/filter-repaint-on-accelerated-layer.html [ Skip ]
+
 # Very slight rendering changes caused by Skia rect clipping change.
 crbug.com/627844 virtual/gpu/fast/canvas/canvas-createImageBitmap-colorClamping.html [ Pass Failure ]
 
@@ -121,11 +155,8 @@
 crbug.com/671097 virtual/spinvalidation/paint/invalidation/filter-on-html-element-with-fixed-position-child.html [ Crash ]
 crbug.com/671097 virtual/spinvalidation/paint/invalidation/reflection-redraw.html [ Crash ]
 crbug.com/671097 virtual/spinvalidation/paint/invalidation/scroll-fixed-reflected-layer.html [ Crash ]
-
-crbug.com/646176 virtual/spinvalidation/paint/invalidation/svg/text-rescale.html [ Failure ]
-
-crbug.com/671605 virtual/spinvalidation/paint/invalidation/compositing/opacity-between-absolute.html [ Skip ]
-crbug.com/671605 virtual/spinvalidation/paint/invalidation/compositing/opacity-between-absolute2.html [ Skip ]
+crbug.com/671097 virtual/spinvalidation/paint/invalidation/compositing/opacity-between-absolute.html [ Pass Crash ]
+crbug.com/671097 virtual/spinvalidation/paint/invalidation/compositing/opacity-between-absolute2.html [ Pass Crash ]
 
 # ====== Paint team owned tests to here ======
 
@@ -133,6 +164,244 @@
 # ====== LayoutNG-only failures from here ======
 # LayoutNG - is a new layout system for Blink.
 
+#### css2.1/20110323
+#### Passed: 175
+#### Skipped: 234
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-height-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-max-height-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-013.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-014.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-016.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-017.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-018.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-019.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-020.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-021.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-022.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-023.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-non-replaced-width-024.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-014.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-016.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-017.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-018.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-019.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-021.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-022.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-023.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-024.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-025.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-026.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-028.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-029.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-030.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-031.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-032.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-033.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-035.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-height-036.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-013.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-020.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-022.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-027.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-029.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-034.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-036.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-041.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-043.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-048.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-050.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-055.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-057.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-062.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-064.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-069.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-071.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/absolute-replaced-width-076.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004a.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004b.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004c.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004d.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004e.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-004f.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-005b.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-005d.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-009a.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-009b.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-009e.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-containing-block-initial-009f.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-non-replaced-width-margin-000.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/abspos-replaced-width-margin-000.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/background-intrinsic-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-013.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-014.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-height-016.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-width-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-width-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-width-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-non-replaced-width-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/block-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/c543-txt-decor-000.html [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/clip-001.html [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-005a.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/dynamic-top-change-005b.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/empty-inline-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/empty-inline-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-non-replaced-width-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-width-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-width-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-width-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/float-replaced-width-011.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floating-replaced-height-008.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-149.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-bfc-001l.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-bfc-001r.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-bfc-002l.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-bfc-002r.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-bfc-003l.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-inline-003l.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/floats-wrap-top-below-inline-003r.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/height-applies-to-010a.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/height-percentage-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/height-width-inline-table-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/height-width-table-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-non-replaced-height-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-non-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-block-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-non-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-non-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-height-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-height-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-height-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-height-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-height-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-replaced-width-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/inline-table-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-003.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-006.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-007.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-009.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-010.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-013.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-014.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-applies-to-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-027.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-012.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-013.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-014.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-015.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-016.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/margin-collapse-clear-017.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/max-height-percentage-003.html [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/outline-color-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/replaced-intrinsic-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/replaced-intrinsic-002.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/replaced-intrinsic-004.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/replaced-intrinsic-005.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/replaced-intrinsic-ratio-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/table-caption-margins-001.htm [ Skip ]
+crbug.com/635619 virtual/layout_ng/css2.1/20110323/width-replaced-element-001.htm [ Skip ]
+
 #### fast/block/basic
 #### Passed: 12
 #### Skipped: 20
@@ -377,12 +646,6 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/trailing-float-with-content.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/vertical-move-relayout.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/width-update-after-clear.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top-2.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top-3.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top-4.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top-5.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top-6.html [ Skip ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/avoid-floats-when-negative-margin-top.html [ Skip ]
 # ====== LayoutNG-only failures until here ======
 
 # Requires ServiceWorkerNavigationPreload feature enabled. Run under
@@ -427,8 +690,6 @@
 
 crbug.com/109362 inspector/sources/debugger-pause/debugger-pause-in-internal.html [ NeedsManualRebaseline ]
 
-crbug.com/522647 http/tests/notifications/close-dispatch-asynchronous.html [ Failure Pass ]
-crbug.com/522647 virtual/mojo-loading/http/tests/notifications/close-dispatch-asynchronous.html [ Failure Pass ]
 crbug.com/522648 fast/events/touch/compositor-touch-hit-rects-iframes.html [ Crash Failure Pass ]
 crbug.com/521858 [ Win7 ] http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
 crbug.com/521858 [ Win7 ] virtual/mojo-loading/http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
@@ -463,7 +724,6 @@
 crbug.com/518883 crbug.com/390452 http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518883 crbug.com/390452 virtual/mojo-loading/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518987 http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
-crbug.com/518987 virtual/mojo-loading/http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
 crbug.com/518988 [ Win7 ] http/tests/xmlhttprequest/xmlhttprequest-50ms-download-dispatch.html [ Failure Pass ]
 crbug.com/518989 [ Mac ] imported/csswg-test/css-writing-modes-3/writing-mode-vertical-rl-002.xht [ Failure Pass Timeout ]
 crbug.com/518998 media/video-poster-after-loadedmetadata.html [ Failure Pass ]
@@ -497,8 +757,6 @@
 crbug.com/248938 virtual/threaded/animations/animation-direction-normal.html [ Pass Failure Timeout ]
 crbug.com/248938 virtual/threaded/animations/animation-iteration-event-destroy-renderer.html [ Pass Timeout ]
 crbug.com/248938 virtual/threaded/animations/transition-and-animation-3.html [ Pass Timeout ]
-crbug.com/446385 [ Linux Mac ] http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Pass Timeout ]
-crbug.com/446385 [ Linux Mac ] virtual/mojo-loading/http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Pass Timeout ]
 crbug.com/446385 [ Win7 Debug ] http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Crash Pass Timeout ]
 crbug.com/446385 [ Win7 Debug ] virtual/mojo-loading/http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Crash Pass Timeout ]
 crbug.com/248938 virtual/threaded/transitions/interrupt-transform-transition.html [ Pass Failure ]
@@ -506,7 +764,6 @@
 crbug.com/248938 virtual/threaded/transitions/change-duration-during-transition.html [ Pass Failure ]
 crbug.com/248938 virtual/threaded/transitions/transition-end-event-nested.html [ Pass Failure ]
 crbug.com/248938 [ Mac ] virtual/threaded/transitions/transition-end-event-unprefixed-01.html [ Pass Failure ]
-crbug.com/513143 virtual/threaded/fast/scroll-behavior/subframe-interrupted-scroll.html [ Failure Pass ]
 crbug.com/248938 virtual/threaded/animations/display-none-terminates-animation.html [ Pass Failure ]
 crbug.com/638693 virtual/threaded/animations/display-inline-style-adjust.html [ Pass Crash Failure ]
 crbug.com/421283 fast/html/marquee-scrollamount.html [ Pass Failure ]
@@ -514,7 +771,6 @@
 crbug.com/248938 virtual/threaded/transitions/transition-end-event-multiple-03.html [ Pass Failure ]
 crbug.com/248938 virtual/threaded/transitions/transition-end-event-rendering.html [ Pass Timeout ]
 crbug.com/248938 [ Linux ] virtual/threaded/transitions/unprefixed-perspective-origin.html [ Pass Timeout ]
-crbug.com/248938 [ Linux ] virtual/threaded/transitions/transition-not-interpolable.html [ Pass Timeout ]
 crbug.com/248938 [ Mac ] virtual/threaded/transitions/interrupted-all-transition.html [ Pass Failure ]
 crbug.com/248938 virtual/threaded/animations/fill-mode-multiple-keyframes.html [ Pass Failure ]
 
@@ -550,6 +806,11 @@
 crbug.com/498539 [ Win7 ] inspector/sources/debugger-pause/debugger-eval-on-call-frame-inside-iframe.html [ Failure Pass ]
 crbug.com/498539 [ Mac10.9 ] inspector/console/console-uncaught-exception.html [ Failure Pass ]
 
+crbug.com/666882 inspector/console/console-log-object-with-getter.html [ NeedsManualRebaseline ]
+crbug.com/645053 inspector/console/console-format-collections.html [ NeedsManualRebaseline ]
+crbug.com/645053 inspector/console/console-format.html [ NeedsManualRebaseline ]
+crbug.com/645053 inspector/console/console-tainted-globals.html [ NeedsManualRebaseline ]
+
 crbug.com/498539 [ Win7 ] inspector/console/console-dir-es6.html [ Failure Pass ]
 crbug.com/498539 [ Win7 ] inspector/elements/styles-4/styles-update-from-js.html [ Crash Pass ]
 
@@ -636,13 +897,21 @@
 crbug.com/659456 virtual/mojo-loading/http/tests/inspector/persistence/automapping-sourcemap.html [ Pass Failure ]
 
 crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphen-min-preferred-width-mock.html [ Skip ]
+crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-align.html [ Skip ]
 crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-auto.html [ Skip ]
 crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-auto-mock.html [ Skip ]
 crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-locale.html [ Skip ]
+crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-orphaned-word.html [ Skip ]
+crbug.com/605840 [ Linux Win ] fast/text/hyphens/midword-break-priority.html [ Skip ]
 crbug.com/605840 [ Android ] fast/text/hyphens/hyphen-min-preferred-width-mock.html [ Pass ]
+crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-align.html [ Pass ]
 crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-auto.html [ Pass ]
 crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-auto-mock.html [ Pass ]
 crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-locale.html [ Pass ]
+crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-orphaned-word.html [ Pass ]
+crbug.com/605840 [ Android ] fast/text/hyphens/midword-break-priority.html [ Pass ]
+
+crbug.com/671341 fast/text/basic/015.html [ NeedsRebaseline ]
 
 crbug.com/655963 inspector/console/console-dir.html [ NeedsManualRebaseline ]
 
@@ -898,7 +1167,6 @@
 crbug.com/613887 http/tests/preload/meta-viewport-link-headers.html [ Failure Pass ]
 crbug.com/613887 virtual/mojo-loading/http/tests/preload/meta-viewport-link-headers.html [ Failure Pass ]
 crbug.com/564403 [ Win Debug ] http/tests/inspector/service-workers/service-worker-manager.html [ Failure Pass Timeout ]
-crbug.com/564403 [ Win Debug ] virtual/mojo-loading/http/tests/inspector/service-workers/service-worker-manager.html [ Failure Pass Timeout ]
 
 crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas.html [ Skip ]
 crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas-2x.html [ Skip ]
@@ -984,9 +1252,6 @@
 crbug.com/480769 http/tests/inspector/service-workers/service-workers-redundant.html [ Pass Failure ]
 crbug.com/480769 virtual/mojo-loading/http/tests/inspector/service-workers/service-workers-redundant.html [ Pass Failure ]
 
-crbug.com/569901 [ Debug ] http/tests/serviceworker/navigation-redirect.html [ Pass Failure ]
-crbug.com/569901 [ Debug ] virtual/mojo-loading/http/tests/serviceworker/navigation-redirect.html [ Pass Failure ]
-
 crbug.com/599670 [ Win Linux ] http/tests/inspector/resource-parameters-ipv6.html [ Failure Pass ]
 crbug.com/599670 [ Win Linux ] virtual/mojo-loading/http/tests/inspector/resource-parameters-ipv6.html [ Failure Pass ]
 
@@ -1164,8 +1429,6 @@
 # We paint in an incorrect order when layers are present
 crbug.com/370604 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml [ Failure ]
 
-crbug.com/658311 virtual/threaded/fast/compositorworker/basic-plumbing-main-to-worker.html [ Failure Pass ]
-
 crbug.com/582836 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/variables/variable-declaration-15.html [ Failure ]
 crbug.com/582836 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/variables/variable-declaration-16.html [ Pass Failure ]
 crbug.com/582836 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/variables/variable-declaration-17.html [ Pass Failure ]
@@ -1243,9 +1506,6 @@
 crbug.com/509025 [ Mac10.10 ] virtual/rootlayerscrolls/scrollbars/rtl/overflow-scroll-rtl.html [ Failure ]
 crbug.com/509025 [ Mac10.10 ] virtual/rootlayerscrolls/scrollbars/short-scrollbar.html [ Failure ]
 
-crbug.com/464736 http/tests/xmlhttprequest/ontimeout-event-override-after-failure.html [ Pass Failure ]
-crbug.com/464736 virtual/mojo-loading/http/tests/xmlhttprequest/ontimeout-event-override-after-failure.html [ Pass Failure ]
-
 crbug.com/621772 fast/forms/color/color-suggestion-picker-with-scrollbar-appearance.html [ Pass Failure ]
 crbug.com/652995 fast/forms/text/input-text-scroll-left-on-blur.html [ Pass Failure ]
 
@@ -1298,8 +1558,6 @@
 
 crbug.com/527270 accessibility/name-calc-img.html [ Failure Pass Timeout ]
 
-crbug.com/532643 [ Mac ] fast/events/hit-test-cache-scrollbar-no-crash.html [ Pass Failure ]
-
 # Win7 does not support the needed DirectWrite APIs for font fallback
 crbug.com/459056 [ Win7 ] fast/text/font-fallback-win.html [ Failure ]
 
@@ -1344,7 +1602,6 @@
 crbug.com/575766 http/tests/inspector/resource-tree/resource-tree-frame-add.html [ Timeout Pass ]
 crbug.com/575766 virtual/mojo-loading/http/tests/inspector/resource-tree/resource-tree-frame-add.html [ Timeout Pass ]
 crbug.com/581468 http/tests/inspector/resource-tree/resource-tree-non-unique-url.html [ Pass Failure ]
-crbug.com/581468 virtual/mojo-loading/http/tests/inspector/resource-tree/resource-tree-non-unique-url.html [ Pass Failure ]
 
 crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ]
 crbug.com/399951 virtual/mojo-loading/http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ]
@@ -1382,7 +1639,6 @@
 crbug.com/600261 imported/wpt/mediacapture-streams/MediaStream-MediaElement-srcObject.https.html [ Failure ]
 
 crbug.com/605525 [ Win ] http/tests/xmlhttprequest/redirect-cross-origin-post.html [ Failure Pass ]
-crbug.com/605525 [ Win ] virtual/mojo-loading/http/tests/xmlhttprequest/redirect-cross-origin-post.html [ Failure Pass ]
 
 crbug.com/600248 imported/wpt/web-animations/interfaces/AnimationEffectTiming/endDelay.html [ Pass Failure ]
 crbug.com/600248 imported/wpt/web-animations/interfaces/Animation/constructor.html [ Failure Timeout ]
@@ -1556,8 +1812,6 @@
 crbug.com/652187 [ Win7 Debug ] http/tests/inspector/network/network-disable-cache-preloads.php [ Failure ]
 crbug.com/652187 [ Win7 Debug ] virtual/mojo-loading/http/tests/inspector/network/network-disable-cache-preloads.php [ Failure ]
 
-crbug.com/658414 [ Win7 Debug ] virtual/gpu/fast/canvas/canvas-lose-restore-googol-size.html [ Failure Pass ]
-
 crbug.com/655458 imported/wpt/workers/constructors/SharedWorker/undefined-arguments.html [ Failure ]
 crbug.com/655458 imported/wpt/workers/baseurl/alpha/worker.html [ Failure ]
 crbug.com/655458 imported/wpt/workers/interfaces/SharedWorkerGlobalScope/onconnect.html [ Failure ]
@@ -1648,8 +1902,6 @@
 
 crbug.com/660295 inspector/elements/elements-panel-restore-selection-when-node-comes-later.html [ Pass Failure ]
 
-crbug.com/660308 [ Mac ] fast/css-grid-layout/grid-auto-repeat-intrinsic.html [ Pass Failure Timeout Crash ]
-
 crbug.com/657968 storage/indexeddb/idbdatabase-createObjectStore-exception-order.html [ Pass Failure ]
 crbug.com/657968 storage/indexeddb/idbdatabase-deleteObjectStore-exception-order.html [ Pass Failure ]
 
@@ -1756,9 +2008,6 @@
 crbug.com/666993 imported/wpt/html/webappapis/idle-callbacks/callback-timeout-with-raf.html [ Timeout ]
 crbug.com/666993 imported/wpt/html/webappapis/idle-callbacks/basic.html [ Timeout ]
 
-# Flaky on Linux dbg
-crbug.com/667953 [ Linux Debug ] virtual/android/media/mediadocument/media-document-with-download-button.html [ Pass Failure ]
-
 # [css-ui] Imported tests from W3C suite.
 crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-014.html [ Failure ]
 crbug.com/669473 imported/csswg-test/css-ui-3/box-sizing-015.html [ Failure ]
@@ -1913,8 +2162,3 @@
 
 # Added 2016-11-30
 crbug.com/669911 [ Win7 Win10 ] virtual/threaded/animations/zoom-responsive-transform-animation.html [ Pass Failure Timeout ]
-
-# Added 2015-12-06
-crbug.com/671618 http/tests/websocket/workers/worker-reload.html [ Pass Timeout ]
-crbug.com/671618 virtual/mojo-loading/http/tests/websocket/workers/worker-reload.html [ Pass Timeout ]
-crbug.com/671618 virtual/mojo-loading/http/tests/websocket/workers/worker-shutdown-race.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 0cf67f8..9e7cc85 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -321,6 +321,11 @@
   },
   {
     "prefix": "layout_ng",
+    "base": "css2.1/20110323",
+    "args": ["--enable-blink-features=LayoutNG"]
+  },
+  {
+    "prefix": "layout_ng",
     "base": "fast/block/float",
     "args": ["--enable-blink-features=LayoutNG"]
   },
diff --git a/third_party/WebKit/LayoutTests/animations/composition/offset-rotate-composition.html b/third_party/WebKit/LayoutTests/animations/composition/offset-rotate-composition.html
new file mode 100644
index 0000000..2f566546
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/composition/offset-rotate-composition.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// Angle rotations compose.
+assertComposition({
+  property: 'offset-rotate',
+  underlying: '20deg',
+  addFrom: '10deg',
+  addTo: '20deg',
+}, [
+  {at: -0.3, is: '27deg'},
+  {at: 0, is: '30deg'},
+  {at: 0.3, is: '33deg'},
+  {at: 0.6, is: '36deg'},
+  {at: 1, is: '40deg'},
+  {at: 1.5, is: '45deg'},
+]);
+
+// Angle rotations don't compose with underlying 'auto'.
+assertComposition({
+  property: 'offset-rotate',
+  underlying: 'auto 20deg',
+  addFrom: '10deg',
+  addTo: '20deg',
+}, [
+  {at: -0.3, is: '7deg'},
+  {at: 0, is: '10deg'},
+  {at: 0.3, is: '13deg'},
+  {at: 0.6, is: '16deg'},
+  {at: 1, is: '20deg'},
+  {at: 1.5, is: '25deg'},
+]);
+
+// Auto rotations compose with underlying 'auto'.
+assertComposition({
+  property: 'offset-rotate',
+  underlying: 'auto 20deg',
+  addFrom: 'reverse 10deg',
+  addTo: 'auto 20deg',
+}, [
+  {at: -0.3, is: 'auto 261deg'},
+  {at: 0, is: 'auto 210deg'},
+  {at: 0.3, is: 'auto 159deg'},
+  {at: 0.6, is: 'auto 108deg'},
+  {at: 1, is: 'auto 40deg'},
+  {at: 1.5, is: 'auto -45deg'},
+]);
+
+// When we can't interpolate, we can't compose.
+assertComposition({
+  property: 'offset-rotate',
+  underlying: '20deg',
+  addFrom: 'reverse 10deg',
+  addTo: '20deg',
+}, [
+  {at: -0.3, is: 'auto 190deg'},
+  {at: 0, is: 'auto 190deg'},
+  {at: 0.3, is: 'auto 190deg'},
+  {at: 0.6, is: '40deg'},
+  {at: 1, is: '40deg'},
+  {at: 1.5, is: '40deg'},
+]);
+
+assertComposition({
+  property: 'offset-rotate',
+  underlying: '20deg',
+  replaceFrom: 'reverse 10deg',
+  addTo: '20deg',
+}, [
+  {at: -0.3, is: 'auto 190deg'},
+  {at: 0, is: 'auto 190deg'},
+  {at: 0.3, is: 'auto 190deg'},
+  {at: 0.6, is: '40deg'},
+  {at: 1, is: '40deg'},
+  {at: 1.5, is: '40deg'},
+]);
+
+// Angle rotations compose with underlying angle.
+assertComposition({
+  property: 'offset-rotate',
+  underlying: '20deg',
+  addFrom: '10deg',
+  replaceTo: '10deg',
+}, [
+  {at: -0.3, is: '36deg'},
+  {at: 0, is: '30deg'},
+  {at: 0.3, is: '24deg'},
+  {at: 0.6, is: '18deg'},
+  {at: 1, is: '10deg'},
+  {at: 1.5, is: '0deg'},
+]);
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/offset-rotate-interpolation.html b/third_party/WebKit/LayoutTests/animations/interpolation/offset-rotate-interpolation.html
new file mode 100644
index 0000000..52d72c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/interpolation/offset-rotate-interpolation.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  offset-rotate: 30deg;
+}
+.target {
+  width: 80px;
+  height: 80px;
+  display: inline-block;
+  background-color: black;
+  margin-right: 5px;
+  offset-rotate: 10deg;
+}
+.expected {
+  background-color: green;
+  margin-right: 15px;
+}
+</style>
+<body>
+<script src="resources/interpolation-test.js"></script>
+<script>
+// Neutral keyframes use the inline style.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: neutralKeyframe,
+  to: '20deg',
+}, [
+  {at: -0.3, is: '7deg'},
+  {at: 0, is: '10deg'},
+  {at: 0.3, is: '13deg'},
+  {at: 0.6, is: '16deg'},
+  {at: 1, is: '20deg'},
+  {at: 1.5, is: '25deg'},
+]);
+
+// No interpolation to an angle from the initial value 'auto'.
+assertNoInterpolation({
+  property: 'offset-rotate',
+  from: 'initial',
+  to: '20deg',
+});
+
+// 'inherit' keyframes use the parent style.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: 'inherit',
+  to: '20deg',
+}, [
+  {at: -0.3, is: '33deg'},
+  {at: 0, is: '30deg'},
+  {at: 0.3, is: '27deg'},
+  {at: 0.6, is: '24deg'},
+  {at: 1, is: '20deg'},
+  {at: 1.5, is: '15deg'},
+]);
+
+// No interpolation to an angle from the initial value 'auto'.
+assertNoInterpolation({
+  property: 'offset-rotate',
+  from: 'unset',
+  to: '20deg',
+});
+
+// Interpolation between angles.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: '10deg',
+  to: '50deg'
+}, [
+  {at: -0.3, is: '-2deg'},
+  {at: 0, is: '10deg'},
+  {at: 0.3, is: '22deg'},
+  {at: 0.6, is: '34deg'},
+  {at: 1, is: '50deg'},
+  {at: 1.5, is: '70deg'},
+]);
+
+// Interpolation between auto angles.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: 'auto 10deg',
+  to: 'auto 50deg'
+}, [
+  {at: -0.3, is: 'auto -2deg'},
+  {at: 0, is: 'auto 10deg'},
+  {at: 0.3, is: 'auto 22deg'},
+  {at: 0.6, is: 'auto 34deg'},
+  {at: 1, is: 'auto 50deg'},
+  {at: 1.5, is: 'auto 70deg'},
+]);
+
+// 'reverse' is like 'auto 180deg'.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: 'reverse -170deg',
+  to: 'reverse -130deg'
+}, [
+  {at: -0.3, is: 'auto -2deg'},
+  {at: 0, is: 'auto 10deg'},
+  {at: 0.3, is: 'auto 22deg'},
+  {at: 0.6, is: 'auto 34deg'},
+  {at: 1, is: 'auto 50deg'},
+  {at: 1.5, is: 'auto 70deg'},
+]);
+
+// Interpolation between 'auto' and 'reverse'.
+assertInterpolation({
+  property: 'offset-rotate',
+  from: 'auto 10deg',
+  to: 'reverse -130deg'
+}, [
+  {at: -0.3, is: 'auto -2deg'},
+  {at: 0, is: 'auto 10deg'},
+  {at: 0.3, is: 'auto 22deg'},
+  {at: 0.6, is: 'auto 34deg'},
+  {at: 1, is: 'auto 50deg'},
+  {at: 1.5, is: 'auto 70deg'},
+]);
+assertInterpolation({
+  property: 'offset-rotate',
+  from: 'reverse -170deg',
+  to: 'auto 50deg'
+}, [
+  {at: -0.3, is: 'auto -2deg'},
+  {at: 0, is: 'auto 10deg'},
+  {at: 0.3, is: 'auto 22deg'},
+  {at: 0.6, is: 'auto 34deg'},
+  {at: 1, is: 'auto 50deg'},
+  {at: 1.5, is: 'auto 70deg'},
+]);
+
+// No interpolation between auto/reverse and angle.
+assertNoInterpolation({
+  property: 'offset-rotate',
+  from: 'auto 200deg',
+  to: '300deg'
+});
+assertNoInterpolation({
+  property: 'offset-rotate',
+  from: '300deg',
+  to: 'reverse 20deg'
+});
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html b/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html
index 175bda21..318436a 100644
--- a/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html
+++ b/third_party/WebKit/LayoutTests/animations/interpolation/transform-interpolation.html
@@ -323,6 +323,18 @@
 ]);
 assertInterpolation({
   property: 'transform',
+  from: 'scaleX(0)',
+  to: 'scaleY(0)',
+}, [
+  {at: -1, is: 'scale(-1, 2)'},
+  {at: 0, is: 'scale(0, 1)'},
+  {at: 0.25, is: 'scale(0.25, 0.75)'},
+  {at: 0.75, is: 'scale(0.75, 0.25)'},
+  {at: 1, is: 'scale(1, 0)'},
+  {at: 2, is: 'scale(2, -1)'},
+]);
+assertInterpolation({
+  property: 'transform',
   from: 'none',
   to: 'scale3d(2, 3, 5)'
 }, [
diff --git a/third_party/WebKit/LayoutTests/animations/responsive/offset-rotate-responsive.html b/third_party/WebKit/LayoutTests/animations/responsive/offset-rotate-responsive.html
new file mode 100644
index 0000000..9ee033c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/responsive/offset-rotate-responsive.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script src="resources/responsive-test.js"></script>
+<script>
+// When the parent's offset-rotate changes during an animation, we respond.
+assertCSSResponsive({
+  property: 'offset-rotate',
+  from: 'inherit',
+  to: 'auto 40deg',
+  configurations: [{
+    state: {inherited: '50deg'},
+    expect: [
+      {at: 0.25, is: '50deg'},
+      {at: 0.75, is: 'auto 40deg'},
+    ],
+  }, {
+    state: {inherited: 'auto 20deg'},
+    expect: [
+      {at: 0.25, is: 'auto 25deg'},
+      {at: 0.75, is: 'auto 35deg'},
+    ],
+  }],
+});
+
+// When the underlying offset-rotate changes during an animation, we respond.
+assertCSSResponsive({
+  property: 'offset-rotate',
+  from: neutralKeyframe,
+  to: '80deg',
+  configurations: [{
+    state: {underlying: 'auto 50deg'},
+    expect: [
+      {at: 0.25, is: 'auto 50deg'},
+      {at: 0.75, is: '80deg'},
+    ],
+  }, {
+    state: {underlying: '40deg'},
+    expect: [
+      {at: 0.25, is: '50deg'},
+      {at: 0.75, is: '70deg'},
+    ],
+  }],
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/transform-post-multiplication.html b/third_party/WebKit/LayoutTests/animations/transform-post-multiplication.html
new file mode 100644
index 0000000..879a56e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/transform-post-multiplication.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<div id="target"></div>
+<script>
+// These tests target post multiplication interpolation behaviour of the transform property.
+// https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms
+
+function assertPostMultiplication(start, end) {
+  test(() => {
+    assert_true(interpolationUsesPostMultiplication(start, end));
+  }, 'Animating transform from ' + start + ' to ' + end + ' should use post multiplication');
+}
+
+function assertNoPostMultiplication(start, end) {
+  test(() => {
+    assert_false(interpolationUsesPostMultiplication(start, end));
+  }, 'Animating transform from ' + start + ' to ' + end + ' should not use post multiplication');
+}
+
+function interpolationUsesPostMultiplication(start, end) {
+    // This value prefix will cause post multiplication to interpolate differently than pairwise interpolation.
+    var testStart = 'rotate(90deg) translateX(10px) ' + start;
+    var testEnd = 'rotate(90deg) translateX(100px) ' + end;
+
+    var animation = target.animate({transform: [toMatrix(testStart), toMatrix(testEnd)]}, 1);
+    animation.currentTime = 0.5;
+    var postMultipliedInterpolation = getComputedStyle(target).transform;
+    animation.cancel();
+
+    animation = target.animate({transform: [testStart, testEnd]}, 1);
+    animation.currentTime = 0.5;
+    var interpolation = getComputedStyle(target).transform;
+    animation.cancel();
+
+    return matricesApproxEqual(interpolation, postMultipliedInterpolation, 0.01);
+}
+
+function toMatrix(transform) {
+  target.style.transform = transform;
+  var matrix = getComputedStyle(target).transform;
+  target.style.transform = '';
+  return matrix;
+}
+
+function matricesApproxEqual(actualMatrix, expectedMatrix, epsilon) {
+  var actualNumbers = actualMatrix.split(/[^\d\.-]/).map(Number);
+  var expectedNumbers = expectedMatrix.split(/[^\d\.-]/).map(Number);
+  if (actualNumbers.length !== expectedNumbers.length) {
+    return false;
+  }
+  for (var i = 0; i < actualNumbers.length; i++) {
+    if (Math.abs(actualNumbers[i] - expectedNumbers[i]) > epsilon) {
+      return false;
+    }
+  }
+  return true;
+}
+
+assertPostMultiplication('translateX(10px)', 'matrix(1, 1, 1, 1, 1, 1)');
+assertPostMultiplication('translateX(10px)', 'rotate(90deg)');
+assertPostMultiplication('skewX(90deg)', 'skewY(90deg)');
+assertPostMultiplication('skew(90deg, 0deg)', 'skewY(90deg)');
+assertPostMultiplication('matrix(1, 0, 0, 1, 0, 0)', 'matrix3d(2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)');
+
+assertNoPostMultiplication('translate(10px, 20px)', 'translateX(100px)');
+assertNoPostMultiplication('translateX(10px)', 'translateY(100px)');
+assertNoPostMultiplication('translateX(10px)', 'translateZ(100px)');
+assertNoPostMultiplication('translateZ(10px)', 'translate3d(100px, 20px, 30px)');
+assertNoPostMultiplication('scaleX(2)', 'scaleY(1)');
+assertNoPostMultiplication('scaleX(2)', 'scaleZ(1)');
+assertNoPostMultiplication('scaleX(2)', 'scale(1, 2)');
+assertNoPostMultiplication('scaleX(2)', 'scale3d(1, 2, 3)');
+assertNoPostMultiplication('skewY(10deg)', 'skewY(90deg)');
+assertNoPostMultiplication('rotate(10deg)', 'rotate(90deg)');
+assertNoPostMultiplication('rotateY(10deg)', 'rotateY(90deg)');
+assertNoPostMultiplication('rotateX(90deg)', 'rotateY(90deg)');
+assertNoPostMultiplication('rotate(10deg)', 'rotate3d(1, 2, 1, 90deg)');
+assertNoPostMultiplication('rotate(10deg)', 'rotateZ(90deg)');
+assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 1, 90deg)');
+assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 2, 90deg)');
+assertNoPostMultiplication('rotateX(10deg)', 'rotate3d(1, 0, 0, 100deg)');
+assertNoPostMultiplication('perspective(10px)', 'perspective(100px)');
+assertNoPostMultiplication('matrix(1, 0, 0, 1, 0, 0)', 'matrix(2, 0, 0, 2, 0, 0)');
+</script>
diff --git a/third_party/WebKit/LayoutTests/css3/motion-path/offset-rotate.html b/third_party/WebKit/LayoutTests/css3/motion-path/offset-rotate.html
new file mode 100644
index 0000000..9253480
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/motion-path/offset-rotate.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="div1"></div>
+<div id="div2" style="offset-rotate: auto"></div>
+<div id="div3" style="offset-rotate: reverse"></div>
+<div id="div4" style="offset-rotate: 180deg"></div>
+<div id="div5" style="offset-rotate: 0rad"></div>
+<div id="div6" style="offset-rotate: -400grad auto"></div>
+<div id="div7" style="offset-rotate: 2turn reverse"></div>
+<div id="div8" style="offset-rotate: reverse 30deg">
+    <div id="div9" style="offset-rotate: inherit"></div>
+    <div id="div10"></div>
+</div>
+<span id="span1" style="offset-rotate: auto -45deg"></span>
+
+<script>
+"use strict";
+
+test(function() {
+    assert_equals(getComputedStyle(div1, null).offsetRotate, 'auto 0deg');
+}, 'offset-rotate default is auto');
+
+test(function() {
+    assert_equals(getComputedStyle(div2, null).offsetRotate, 'auto 0deg');
+}, 'offset-rotate auto expands to auto 0deg');
+
+test(function() {
+    assert_equals(getComputedStyle(div3, null).offsetRotate, 'auto 180deg');
+}, 'offset-rotate reverse expands to auto 180deg');
+
+test(function() {
+    assert_equals(getComputedStyle(div4, null).offsetRotate, '180deg');
+}, 'offset-rotate can be a fixed angle');
+
+test(function() {
+    assert_equals(getComputedStyle(div5, null).offsetRotate, '0deg');
+}, 'offset-rotate angles are converted to degrees');
+
+test(function() {
+    assert_equals(getComputedStyle(div6, null).offsetRotate, 'auto -360deg');
+}, 'offset-rotate can be supplied with angle before auto');
+
+test(function() {
+    assert_equals(getComputedStyle(div7, null).offsetRotate, 'auto 900deg');
+}, 'offset-rotate can be supplied with angle before reverse');
+
+test(function() {
+    assert_equals(getComputedStyle(div8, null).offsetRotate, 'auto 210deg');
+}, 'offset-rotate is unaffected by child elements');
+
+test(function() {
+    assert_equals(getComputedStyle(div9, null).offsetRotate, 'auto 210deg');
+}, 'offset-rotate can be explicitly inherited');
+
+test(function() {
+    assert_equals(getComputedStyle(div10, null).offsetRotate, 'auto 0deg');
+}, 'offset-rotate is not inherited by default');
+
+test(function() {
+    assert_equals(span1.style.offsetRotate, 'auto -45deg');
+}, 'offset-rotate style can be set inline');
+
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/custom-properties/registered-property-initial.html b/third_party/WebKit/LayoutTests/custom-properties/registered-property-initial.html
new file mode 100644
index 0000000..6a6ada6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/custom-properties/registered-property-initial.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+#target {
+  background: var(--inherited-color);
+  color: var(--non-inherited-color);
+}
+</style>
+<div id=target></div>
+<script>
+CSS.registerProperty({name: '--inherited-color', syntax: '<color>', initialValue: 'pink', inherits: true});
+CSS.registerProperty({name: '--non-inherited-color', syntax: '<color>', initialValue: 'purple'});
+
+test(function() {
+    computedStyle = getComputedStyle(target);
+    assert_equals(computedStyle.getPropertyValue('--inherited-color'), 'pink');
+    assert_equals(computedStyle.getPropertyValue('--non-inherited-color'), 'purple');
+
+    assert_equals(computedStyle.backgroundColor, 'rgb(255, 192, 203)');
+    assert_equals(computedStyle.color, 'rgb(128, 0, 128)');
+}, "Initial values of registered properties can be referenced when no custom properties are explicitly set.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent08.js b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent08.js
index 1dda078..f2b16fa 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent08.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent08.js
@@ -73,7 +73,7 @@
 /**
 *
 An EventListener registered on the target node with capture false, should
-recieve any event fired on that node.
+receive any event fired on that node.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent10.js b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent10.js
index ac3f736..9b1fa9a 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent10.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent10.js
@@ -73,7 +73,7 @@
 /**
 *
 The same monitor is registered twice and an event is dispatched.  The monitor should
-recieve only one handleEvent call.
+receive only one handleEvent call.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent11.js b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent11.js
index 95665c6..62a22df5 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent11.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent11.js
@@ -73,7 +73,7 @@
 /**
 *
 The same monitor is registered twice, removed once, and an event is dispatched.
-The monitor should recieve only no handleEvent calls.
+The monitor should receive only no handleEvent calls.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent12.js b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent12.js
index c2ec46a4..5d66696 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent12.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/events/dispatchEvent12.js
@@ -101,7 +101,7 @@
 *
 A monitor is added, multiple calls to removeEventListener
 are mde with similar but not identical arguments, and an event is dispatched.
-The monitor should recieve handleEvent calls.
+The monitor should receive handleEvent calls.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement21.js b/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement21.js
index c8dacc96..ddcfab3 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement21.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement21.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    The noWrap attribute supresses word wrapping.
+    The noWrap attribute suppresses word wrapping.
 
     Retrieve the noWrap attribute of the second TH Element and
     examine its value.
diff --git a/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement22.js b/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement22.js
index 9af9a10..61830f6 100644
--- a/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement22.js
+++ b/third_party/WebKit/LayoutTests/dom/html/level2/html/HTMLTableCellElement22.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    The noWrap attribute supresses word wrapping.
+    The noWrap attribute suppresses word wrapping.
 
     Retrieve the noWrap attribute of the second TD Element and
     examine its value.
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent08.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent08.js
index 7464789..af90353 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent08.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent08.js
@@ -73,7 +73,7 @@
 /**
 *
 An EventListener registered on the target node with capture false, should
-recieve any event fired on that node.
+receive any event fired on that node.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent10.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent10.js
index 81999b2..ac2af6d 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent10.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent10.js
@@ -73,7 +73,7 @@
 /**
 *
 The same monitor is registered twice and an event is dispatched.  The monitor should
-recieve only one handleEvent call.
+receive only one handleEvent call.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent11.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent11.js
index c018407..b7886a0 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent11.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent11.js
@@ -73,7 +73,7 @@
 /**
 *
 The same monitor is registered twice, removed once, and an event is dispatched.
-The monitor should recieve only no handleEvent calls.
+The monitor should receive only no handleEvent calls.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent12.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent12.js
index c8c98089..d6baa0f9 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent12.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/events/dispatchEvent12.js
@@ -101,7 +101,7 @@
 *
 A monitor is added, multiple calls to removeEventListener
 are mde with similar but not identical arguments, and an event is dispatched.
-The monitor should recieve handleEvent calls.
+The monitor should receive handleEvent calls.
 
 * @author Curt Arnold
 * @see http://www.w3.org/TR/DOM-Level-2-Events/events#Events-EventTarget-dispatchEvent
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement21.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement21.js
index c8dacc96..ddcfab3 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement21.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement21.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    The noWrap attribute supresses word wrapping.
+    The noWrap attribute suppresses word wrapping.
 
     Retrieve the noWrap attribute of the second TH Element and
     examine its value.
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement22.js b/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement22.js
index 9af9a10..61830f6 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement22.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level2/html/HTMLTableCellElement22.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    The noWrap attribute supresses word wrapping.
+    The noWrap attribute suppresses word wrapping.
 
     Retrieve the noWrap attribute of the second TD Element and
     examine its value.
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode13.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode13.js
index 956b8de5..d3beed4 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode13.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode13.js
@@ -70,7 +70,7 @@
 /**
 *
     Using the method adoptNode, adopt a newly created DocumentFragment node populated with
-    with the first acronym element of this Document.  Since the decendants of a documentFragment
+    with the first acronym element of this Document.  Since the descendants of a documentFragment
     are recursively adopted, check if the adopted node has children.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode14.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode14.js
index 7fd0144..3902a2f 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode14.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentadoptnode14.js
@@ -70,7 +70,7 @@
 /**
 *
     Using the method adoptNode in a new Document, adopt a newly created DocumentFragment node populated with
-    with the first acronym element of this Document as its newChild.  Since the decendants of a documentFragment
+    with the first acronym element of this Document as its newChild.  Since the descendants of a documentFragment
     are recursively adopted, check if the adopted node has children.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdoctype01.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdoctype01.js
index 5d0bdda..5ea177d 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdoctype01.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdoctype01.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Retreive the doctype node, create a new Doctype node, call replaceChild and try replacing the
+    Retrieve the doctype node, create a new Doctype node, call replaceChild and try replacing the
     docType node with a new docType node.  Check if the docType node was correctly replaced with
     the new one.
 
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi01.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi01.js
index 0384a38e..84fa2ff 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi01.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi01.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Retreive the documentURI of this document, and verify if it is not null.
+    Retrieve the documentURI of this document, and verify if it is not null.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi02.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi02.js
index e07f3c0..e3404c2e 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi02.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetdocumenturi02.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Create a new Document, retreive its documentURI, and verify if it is null.
+    Create a new Document, retrieve its documentURI, and verify if it is null.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone01.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone01.js
index 42b1ff13..b5af93f 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone01.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone01.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Retreive the xmlStandalone attribute of a document for which standalone was not specified, this
+    Retrieve the xmlStandalone attribute of a document for which standalone was not specified, this
     should return false since the default for standalone is no when external markup decls
     are present.
 
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone04.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone04.js
index 1ada4fa..dd82c78 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone04.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentgetxmlstandalone04.js
@@ -70,7 +70,7 @@
 
 /**
 *
-    Retreive the documentURI of a document for which standalone was specified as "yes", this
+    Retrieve the documentURI of a document for which standalone was specified as "yes", this
     should return true.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentnormalizedocument12.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentnormalizedocument12.js
index a16e906..88cc5fe2b 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentnormalizedocument12.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentnormalizedocument12.js
@@ -106,7 +106,7 @@
     and load cycle, putting the document in a "normal" form.
 
     Set the validate feature to true.  Invoke the normalizeDocument method on this
-    document.  Retreive the documentElement node and check the nodeName of this node
+    document.  Retrieve the documentElement node and check the nodeName of this node
     to make sure it has not changed.  Now set validate to false and verify the same.
     Register an error handler on this Document and in each case make sure that it does
     not get called.
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi01.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi01.js
index 64a5e3c..892e29b0 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi01.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi01.js
@@ -71,7 +71,7 @@
 *
     The setDocmentURI method set the location of the document.
 
-    Set the documentURI to a valid string and retreive the documentURI of this
+    Set the documentURI to a valid string and retrieve the documentURI of this
     document and verify if it is was correctly set.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi02.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi02.js
index ee0d0c46..923519a4 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi02.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi02.js
@@ -71,7 +71,7 @@
 *
     The setDocmentURI method set the location of the document.
 
-    Set the documentURI to null and retreive the documentURI of this document and verify
+    Set the documentURI to null and retrieve the documentURI of this document and verify
     if it is was set to null.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi03.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi03.js
index 2fdc678..697c555a 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi03.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/documentsetdocumenturi03.js
@@ -71,7 +71,7 @@
 *
     The setDocmentURI method set the location of the document.
 
-    Create a new document and set its documentURI to a valid string.  Retreive the documentURI
+    Create a new document and set its documentURI to a valid string.  Retrieve the documentURI
     and verify if it is was correctly set.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode08.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode08.js
index abfe6faf..508605e 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode08.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode08.js
@@ -72,7 +72,7 @@
 /**
 *
 
-    Retreive an element node of this Document having nodeName as employeeId and
+    Retrieve an element node of this Document having nodeName as employeeId and
     namespaceURI as http://www.nist.gov.  Create a new Element node having the same attributes
     in this Document and using isEqualNode check if 2 Element nodes are equal.
 
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode10.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode10.js
index 1051503b..93a5f845 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode10.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode10.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Retreive 2 different "em" nodes of this Document   Use isEqualNode
+    Retrieve 2 different "em" nodes of this Document   Use isEqualNode
     check if nodes are not equal.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode11.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode11.js
index f9eef47..6931898 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode11.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode11.js
@@ -75,7 +75,7 @@
 
 /**
 *
-    Retreive the first element node whose localName is "p".  Import it into a new
+    Retrieve the first element node whose localName is "p".  Import it into a new
     Document with deep=false.  Using isEqualNode check if the original and the imported
     Element Node are not equal the child nodes are different.
     Import with deep and the should still be unequal if
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode13.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode13.js
index ac66a2ce..6edbd7f 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode13.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodeisequalnode13.js
@@ -69,7 +69,7 @@
 
 /**
 *
-    Retreive the first element node whose localName is "p".  Import it into a new
+    Retrieve the first element node whose localName is "p".  Import it into a new
     Document with deep=false.  Using isEqualNode check if the original and the imported
     Element Node are not equal.  Now import it once more with deep=true and using isEqual
     verify if they are now equal.
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent03.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent03.js
index 7c21a52b..c30b35c 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent03.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent03.js
@@ -71,7 +71,7 @@
 *
 
     Using setTextContent on this DocumentType node, attempt to set the textContent of this
-    DocumentType node to textContent.  Retreive the textContent and verify if it is null.
+    DocumentType node to textContent.  Retrieve the textContent and verify if it is null.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent05.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent05.js
index 7275e5b..80e2318a 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent05.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent05.js
@@ -70,7 +70,7 @@
 /**
 *
 
-    Using setTextContent on a default Attr node, attempt to set its value to NA.  Retreive
+    Using setTextContent on a default Attr node, attempt to set its value to NA.  Retrieve
     the textContent and verify if it is was set to NA.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent06.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent06.js
index 5132ce12..3824956 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent06.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent06.js
@@ -70,7 +70,7 @@
 /**
 *
 
-    Using setTextContent on a new Attr node with a null value, attempt to set its value to NA.  Retreive
+    Using setTextContent on a new Attr node with a null value, attempt to set its value to NA.  Retrieve
     the textContent and verify if it is was set to NA.
 
 * @author IBM
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent07.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent07.js
index 6f779802..a3583f2b 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent07.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent07.js
@@ -71,7 +71,7 @@
 *
 
     Using setTextContent on an existing Text node, attempt to set its value to Text.
-    Retreive the textContent and verify if it is was set to Text.
+    Retrieve the textContent and verify if it is was set to Text.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent08.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent08.js
index 0d973ea5..32becdb 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent08.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent08.js
@@ -71,7 +71,7 @@
 *
 
     Using setTextContent on a new Processing Instruction node, attempt to set its data to PID.
-    Retreive the textContent and verify if it is was set to PID.
+    Retrieve the textContent and verify if it is was set to PID.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent10.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent10.js
index d930bca..49816bc3 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent10.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent10.js
@@ -74,7 +74,7 @@
     The method setTextContent has no effect when the node is defined to be null.
 
     Using setTextContent on a new Element node, attempt to set its content to ELEMENT.
-    Retreive the textContent and verify if it is was set to ELEMENT.
+    Retrieve the textContent and verify if it is was set to ELEMENT.
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent11.js b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent11.js
index fd57ffc..7e3b5bfa 100644
--- a/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent11.js
+++ b/third_party/WebKit/LayoutTests/dom/xhtml/level3/core/nodesettextcontent11.js
@@ -71,7 +71,7 @@
 *
 
     Using setTextContent on a new DocumentFragment node Element child, attempt to set its content to
-    DOCUMENTFRAGMENT.  Retreive the textContent and verify if it is was set to DOCUMENTFRAGMENT
+    DOCUMENTFRAGMENT.  Retrieve the textContent and verify if it is was set to DOCUMENTFRAGMENT
 
 * @author IBM
 * @author Neil Delima
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
index 66f731b6..f9640ab 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize18-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
index 470de4d..c6eaddd6 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize19-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
index 66f731b6..f9640ab 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize21-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
index 1cdc2b5..63e0eb71 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/size/backgroundSize22-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.png b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.png
deleted file mode 100644
index b3849af..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt
index ffc9c682..f7628e6 100644
--- a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend-expected.txt
@@ -1,82 +1,60 @@
-layer at (0,0) size 800x600
+layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 898
   LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x582
-      LayoutBlockFlow (floating) {DIV} at (0,0) size 144x501.53
-        LayoutFieldset {FIELDSET} at (2,0) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (20,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,45.59) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (20,0) size 112x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,91.19) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (20,0) size 116x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,136.78) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (20,0) size 121x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,182.38) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (8,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,227.97) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (4,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,273.56) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (-1,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,319.16) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (-49,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,364.75) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (-54,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,410.34) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (135,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-        LayoutFieldset {FIELDSET} at (2,455.94) size 140x35.59 [border: (8px double #000000)]
-          LayoutBlockFlow {LEGEND} at (140,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x4
-      LayoutBlockFlow {DIV} at (450,0) size 334x408.78
+layer at (0,0) size 785x898 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow {HTML} at (0,0) size 785x898.31
+    LayoutBlockFlow {BODY} at (8,8) size 769x880.31
+      LayoutFieldset {FIELDSET} at (2,0) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (20,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,45.59) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (20,0) size 112x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,91.19) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (20,0) size 116x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,136.78) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (20,0) size 121x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,182.38) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (8,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,227.97) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (4,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,273.56) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (-1,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,319.16) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (-49,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,364.75) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (-54,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,410.34) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (135,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,455.94) size 140x35.59 [border: (8px double #000000)]
+        LayoutBlockFlow {LEGEND} at (140,0) size 54x12 [bgcolor=#0080004C]
+      LayoutFieldset {FIELDSET} at (2,501.53) size 184x29.59 [border: (2px groove #C0C0C0)]
+        LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
+      LayoutFieldset {FIELDSET} at (2,541.13) size 184x59.59 [border: (2px groove #C0C0C0)]
+        LayoutBlockFlow {LEGEND} at (44,0) size 96x12 [border: (1px solid #0000FF)]
+      LayoutBlockFlow (anonymous) at (0,810.72) size 769x40
         LayoutFieldset {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-        LayoutFieldset {FIELDSET} at (2,39.59) size 184x89.59 [border: (2px groove #C0C0C0)]
-          LayoutBlockFlow {LEGEND} at (44,30) size 96x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 156x70
-        LayoutBlockFlow (anonymous) at (0,339.19) size 334x40
-          LayoutFieldset {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)]
-            LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-            LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-          LayoutText {#text} at (0,0) size 0x0
-        LayoutFieldset {FIELDSET} at (2,379.19) size 128x29.59 [border: (2px groove #C0C0C0)]
-          LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 100x10
-layer at (458,147) size 100x40
-  LayoutBlockFlow (relative positioned) {DIV} at (0,139.19) size 100x40
+        LayoutText {#text} at (0,0) size 0x0
+      LayoutFieldset {FIELDSET} at (2,850.72) size 128x29.59 [border: (2px groove #C0C0C0)]
+        LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
+layer at (8,619) size 100x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow (relative positioned) {DIV} at (0,610.72) size 100x40
     LayoutFieldset {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)]
       LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-      LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-layer at (458,187) size 300x40
-  LayoutBlockFlow (relative positioned) {DIV} at (0,179.19) size 300x40
+layer at (8,659) size 300x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow (relative positioned) {DIV} at (0,650.72) size 300x40
     LayoutFieldset {FIELDSET} at (58,0) size 184x29.59 [border: (2px groove #C0C0C0)]
       LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-      LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-layer at (458,227) size 100x40
-  LayoutBlockFlow (relative positioned) {DIV} at (0,219.19) size 100x40
-layer at (460,227) size 184x30
+layer at (8,699) size 100x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow (relative positioned) {DIV} at (0,690.72) size 100x40
+layer at (10,699) size 184x30 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
   LayoutFieldset (positioned) {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-layer at (458,267) size 334x40
-  LayoutBlockFlow (relative positioned) {DIV} at (0,259.19) size 334x40
-layer at (460,267) size 184x30
+layer at (8,739) size 769x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow (relative positioned) {DIV} at (0,730.72) size 769x40
+layer at (10,739) size 184x30 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
   LayoutFieldset (positioned) {FIELDSET} at (2,0) size 184x29.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
-layer at (458,307) size 300x40
-  LayoutBlockFlow (relative positioned) {DIV} at (0,299.19) size 300x40
-layer at (560,307) size 184x30
+layer at (8,779) size 300x40 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
+  LayoutBlockFlow (relative positioned) {DIV} at (0,770.72) size 300x40
+layer at (110,779) size 184x30 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
   LayoutFieldset (positioned) {FIELDSET} at (102,0) size 184x29.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x10
diff --git a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend.html b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend.html
index a551a46f..db24e16 100644
--- a/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend.html
+++ b/third_party/WebKit/LayoutTests/fast/block/basic/fieldset-stretch-to-legend.html
@@ -7,86 +7,79 @@
         fieldset.render { border: 8px double; }
         fieldset.render legend { height: 12px; width: 50px; background-color: rgba(0, 128, 0, 0.3); border: none; }
         div.rel { position: relative; height: 40px; }
-        #col1 { float: left; }
-        #col2 { margin-left: 450px; }
     </style>
 </head>
 <body>
     <!-- border rendering tests -->
-    <div id="col1">
-        <!-- border rendering tests -->
-        <fieldset class="render" style="width: 100px;">
-            <legend></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="width: 108%;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="width: 112%;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="width: 117%;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: -12px;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: -16px;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: -21px;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: -69px;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: -74px;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: 115%;"></legend>
-        </fieldset>
-        <fieldset class="render" style="width: 100px;">
-            <legend style="margin-left: 120%;"></legend>
-        </fieldset>
-    </div>
+    <fieldset class="render" style="width: 100px;">
+        <legend></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="width: 108%;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="width: 112%;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="width: 117%;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: -12px;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: -16px;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: -21px;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: -69px;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: -74px;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: 115%;"></legend>
+    </fieldset>
+    <fieldset class="render" style="width: 100px;">
+        <legend style="margin-left: 120%;"></legend>
+    </fieldset>
     <!-- layout tests -->
-    <div id="col2">
-        <fieldset style="width: 100px;">
+    <fieldset style="width: 100px;">
+        <legend style="width: 150px;"></legend>
+    </fieldset>
+    <fieldset style="width: 100px;">
+        <legend style="width: 90px; margin: 30px;"></legend>
+    </fieldset>
+    <div class="rel" style="width: 100px;">
+        <fieldset>
             <legend style="width: 150px;"></legend>
         </fieldset>
-        <fieldset style="width: 100px;">
-            <legend style="width: 90px; margin: 30px;"></legend>
-        </fieldset>
-        <div class="rel" style="width: 100px;">
-            <fieldset>
-                <legend style="width: 150px;"></legend>
-            </fieldset>
-        </div>
-        <div class="rel" style="width: 300px;">
-            <fieldset style="width: 100; margin: auto;">
-                <legend style="width: 150px;"></legend>
-            </fieldset>
-        </div>
-        <div class="rel" style="width: 100px;">
-            <fieldset style="position: absolute;">
-                <legend style="width: 150px;"></legend>
-            </fieldset>
-        </div>
-        <div class="rel">
-            <fieldset style="position: absolute; width: 100px;">
-                <legend style="width: 150px;"></legend>
-            </fieldset>
-        </div>
-        <div class="rel" style="width: 300px;">
-            <fieldset style="position: absolute; left: 100px; right: 100px;">
-                <legend style="width: 150px;"></legend>
-            </fieldset>
-        </div>
-        <fieldset style="display: inline; width: 100px;">
-            <legend style="width: 150px;"></legend>
-        </fieldset>
-        <fieldset style="width: 100px;">
-            <legend style="width: 150%;"></legend>
-        </fieldset>
     </div>
+    <div class="rel" style="width: 300px;">
+        <fieldset style="width: 100; margin: auto;">
+            <legend style="width: 150px;"></legend>
+        </fieldset>
+    </div>
+    <div class="rel" style="width: 100px;">
+        <fieldset style="position: absolute;">
+            <legend style="width: 150px;"></legend>
+        </fieldset>
+    </div>
+    <div class="rel">
+        <fieldset style="position: absolute; width: 100px;">
+            <legend style="width: 150px;"></legend>
+        </fieldset>
+    </div>
+    <div class="rel" style="width: 300px;">
+        <fieldset style="position: absolute; left: 100px; right: 100px;">
+            <legend style="width: 150px;"></legend>
+        </fieldset>
+    </div>
+    <fieldset style="display: inline; width: 100px;">
+        <legend style="width: 150px;"></legend>
+    </fieldset>
+    <fieldset style="width: 100px;">
+        <legend style="width: 150%;"></legend>
+    </fieldset>
 </body>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2.html
deleted file mode 100644
index 8042753..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-2.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div class="float"></div>
-  <br clear=all>
-  <div style="width: 20px; height; 1px"></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3.html
deleted file mode 100644
index 0111b37..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-3.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div>
-    <div class="float"></div>
-    <br clear=all>
-  </div>
-  <div style="width: 20px; height; 1px"></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4.html
deleted file mode 100644
index 14f119a..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-4.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div>
-    <div class="float"></div>
-    <br clear=all>
-  </div>
-  <div style="float:left;"></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5.html
deleted file mode 100644
index 9e711bd3..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-5.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div>
-    <div class="float"></div>
-    <br clear=all>
-  </div>
-  <div style="position:absolute;"></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6.html
deleted file mode 100644
index 1c58540..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-6.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div>
-    <div class="float"></div>
-    <br clear=all>
-  </div>
-  <div style="overflow:hidden;"></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-expected.txt b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-expected.txt
deleted file mode 100644
index 9f54bd13..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-crbug.com/666487: There should be a green square below.
-
-
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top.html b/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top.html
deleted file mode 100644
index 2ff4b736..0000000
--- a/third_party/WebKit/LayoutTests/fast/block/float/avoid-floats-when-negative-margin-top.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<style>
-body { margin: 0px; }
-.float { float: left; width: 50px; height: 100px; background: green;}
-.content { overflow: hidden; margin-top: -100px; width: 50px; height: 100px; background: green;}
-.container { width: 100px; background: red;}
-</style>
-<p>crbug.com/666487: There should be a green square below.</p>
-<div class="container">
-  <div class="float"></div>
-  <br clear=all>
-  <div></div>
-  <div class="content" data-offset-x=50></div>
-</div>
-<script src="../../../resources/check-layout.js"></script>
-<script>
-checkLayout('.content');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/block/float/line-break-after-white-space-crash.html b/third_party/WebKit/LayoutTests/fast/block/float/line-break-after-white-space-crash.html
new file mode 100644
index 0000000..5193be03
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/block/float/line-break-after-white-space-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div style="columns:4; column-gap:0; column-fill:auto; width:120px; height:50px; orphans:1; widows:1; font:8px/20px Ahem; -webkit-line-break:after-white-space;">
+    <br>
+    <br>
+    xx
+    <div style="float:left; width:1px; height:20px;"><br></div>
+    <div style="float:left; width:100%; height:10px;"></div>
+</div>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+test(() => {}, "PASS if no assertion failure or heap-use-after-free");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/borders/border-image-rotate-transform-expected.png b/third_party/WebKit/LayoutTests/fast/borders/border-image-rotate-transform-expected.png
index 0fbce82..3f31fe8 100644
--- a/third_party/WebKit/LayoutTests/fast/borders/border-image-rotate-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/borders/border-image-rotate-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png
index 97a18d32..20565063 100644
--- a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt
index 109da2c..c46c845 100644
--- a/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/borders/fieldsetBorderRadius-expected.txt
@@ -6,84 +6,62 @@
       LayoutBlockFlow (floating) {DIV} at (0,0) size 158x459.16
         LayoutFieldset {FIELDSET} at (2,0) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (20,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,61.59) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (20,0) size 112x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,123.19) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (20,0) size 116x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,184.78) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (20,0) size 121x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,246.38) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (8,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,307.97) size 154x65.59 [border: (15px solid #000000)]
           LayoutBlockFlow {LEGEND} at (15,1.50) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (27,20.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,383.56) size 154x65.59 [border: (15px solid #000000)]
           LayoutBlockFlow {LEGEND} at (15,1.50) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (27,20.59) size 100x20
       LayoutBlockFlow (floating) {DIV} at (228,0) size 144x369.56
         LayoutFieldset {FIELDSET} at (2,0) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (4,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,61.59) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (-1,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,123.19) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (-49,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,184.78) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (-54,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,246.38) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (135,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
         LayoutFieldset {FIELDSET} at (2,307.97) size 140x51.59 [border: (8px double #000000)]
           LayoutBlockFlow {LEGEND} at (140,0) size 54x12 [bgcolor=#0080004C]
-          LayoutBlockFlow (anonymous) at (20,13.59) size 100x20
       LayoutBlockFlow {DIV} at (450,0) size 334x388.78
         LayoutFieldset {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
         LayoutFieldset {FIELDSET} at (2,49.59) size 184x39.59 [border: (2px groove #C0C0C0)]
-          LayoutBlockFlow {LEGEND} at (44,30) size 96x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 156x70
+          LayoutBlockFlow {LEGEND} at (44,0) size 96x12 [border: (1px solid #0000FF)]
         LayoutBlockFlow (anonymous) at (0,299.19) size 334x50
           LayoutFieldset {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)]
             LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-            LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
           LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,349.19) size 128x39.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 100x20
 layer at (458,107) size 100x40
   LayoutBlockFlow (relative positioned) {DIV} at (0,99.19) size 100x40
     LayoutFieldset {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)]
       LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-      LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
 layer at (458,147) size 300x40
   LayoutBlockFlow (relative positioned) {DIV} at (0,139.19) size 300x40
     LayoutFieldset {FIELDSET} at (58,0) size 184x39.59 [border: (2px groove #C0C0C0)]
       LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-      LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
 layer at (458,187) size 100x40
   LayoutBlockFlow (relative positioned) {DIV} at (0,179.19) size 100x40
 layer at (460,187) size 184x40
   LayoutFieldset (positioned) {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
 layer at (458,227) size 334x40
   LayoutBlockFlow (relative positioned) {DIV} at (0,219.19) size 334x40
 layer at (460,227) size 184x40
   LayoutFieldset (positioned) {FIELDSET} at (2,0) size 184x39.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
 layer at (458,267) size 300x40
   LayoutBlockFlow (relative positioned) {DIV} at (0,259.19) size 300x40
 layer at (560,267) size 184x40
   LayoutFieldset (positioned) {FIELDSET} at (102,0) size 184x39.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 156x12 [border: (1px solid #0000FF)]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 156x20
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-messagechannel-imagebitmap-transfer.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-messagechannel-imagebitmap-transfer.html
new file mode 100644
index 0000000..d36bcb7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-messagechannel-imagebitmap-transfer.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+  var channel = new MessageChannel;
+  channel.port1.onmessage = function(e) {
+    t.step(function() {
+      assert_true(e.data !== null);
+      assert_equals(e.data.width, 10);
+      assert_equals(e.data.height, 10);
+      var destCtx = document.createElement('canvas').getContext('2d');
+      destCtx.drawImage(e.data, 0, 0);
+      var pixel = destCtx.getImageData(5, 5, 1, 1).data;
+      assert_array_equals(pixel, [0, 255, 0, 255]);
+    });
+    t.done();
+  };
+  channel.port1.start();
+  var sourceCanvas = document.createElement('canvas');
+  sourceCanvas.width = sourceCanvas.height = 10;
+  var sourceCtx = sourceCanvas.getContext('2d');
+  sourceCtx.fillStyle = '#0f0';
+  sourceCtx.fillRect(0, 0, 10, 10);
+  createImageBitmap(sourceCanvas).then( bitmap => {
+    channel.port2.postMessage(bitmap, [bitmap]);
+  });
+}, "Test that an ImageBitmap generated from a canvas survives being transferred via a MessageChannel");
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation-expected.html
new file mode 100644
index 0000000..af7ee10
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation-expected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<p>Expect to see a black square below this line.</p>
+<canvas id = 'c' width='100' height='100'></canvas>
+<script>
+document.getElementById('c').getContext('2d').fillRect(0, 0, 100, 100);
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation.html
new file mode 100644
index 0000000..0427b4c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-no-alpha-invalidation.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<p>Expect to see a black square below this line.</p>
+<canvas id = 'c' width='100' height='100'></canvas>
+<script>
+if (window.testRunner) {
+  testRunner.waitUntilDone();
+  testRunner.layoutAndPaintAsyncThen(function(){
+    // Verify that creating a context with alpha:false trigger a graphics
+    // update even though nothing is drawn to the canvas.
+    document.getElementById('c').getContext('2d', { alpha:false });
+    testRunner.notifyDone();
+  });
+} else {
+  console.log('ERROR: This test requires the testRunner interface.')
+}
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index d631396..3302e00 100644
--- a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -210,6 +210,7 @@
 offset-distance: 0px
 offset-path: none
 offset-position: auto
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index d80b09e0..a952d94f 100644
--- a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -210,6 +210,7 @@
 offset-distance: 0px
 offset-path: none
 offset-position: auto
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
index 48ffb11..79b69c60 100644
--- a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
@@ -15,6 +15,7 @@
     ["pointerEvents", "auto", "all"],
     ["visibility", "visible", "hidden"],
     ["whiteSpace", "normal", "nowrap"],
+    ["borderCollapse", "separate", "collapse"],
 ];
 
 independent_properties.forEach(function(test_data)
diff --git a/third_party/WebKit/LayoutTests/fast/dom/HTMLImageElement/fallback-image-moved-across-documents.html b/third_party/WebKit/LayoutTests/fast/dom/HTMLImageElement/fallback-image-moved-across-documents.html
new file mode 100644
index 0000000..11017f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/dom/HTMLImageElement/fallback-image-moved-across-documents.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<iframe src="about:blank" id="nested"></iframe>
+<script type="text/javascript">
+// Fallback content is implemented by means of shadow DOM, using a dumbed-down
+// HTMLImageElement instance inside to render the broken image icon. This inner
+// image element displays a hard-coded ImageResource, and has no 'src' attribute
+// so normally the `update image data` [1] algorithm is never invoked.
+//
+// However the `update image data` algorithm will be unconditionally invoked in
+// response to an image element (inner or not) being adopted [2], e.g. when a
+// subtree containing an image element (potentially in a shadow tree) is moved
+// across documents.
+//
+// In Blink, when 'src' is empty, as is the case for the inner fallback image,
+// the `update image data` algorithm will synchronously fail and create the
+// shadow tree to render fallback content.
+//
+// Therefore special care must be taken to prevent creating the shadow tree
+// for the inner fallback image itself, otherwise the recursive adoption
+// algorithm would reach those nodes in the newly created shadow tree next, and
+// the same thing would happen again and we end up with infinite nesting.
+//
+// [1]: https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
+// [2]: https://html.spec.whatwg.org/multipage/embedded-content.html#reacting-to-dom-mutations
+
+var nestedDocument = document.getElementById("nested").contentDocument;
+
+promise_test(t => {
+  let i = nestedDocument.createElement("img");
+  i.src = "non-existent/gives-404.png";
+  window.setTimeout(_ => nestedDocument.body.append(i));
+
+  let eventWatcher = new EventWatcher(t, i, [ "load", "error" ]);
+  return eventWatcher.wait_for("error").then(_ => {
+    window.setTimeout(_ => document.body.append(i));
+    return eventWatcher.wait_for("error").then(_ => {
+      assert_equals(i.clientWidth, 20, "Fallback content should be displayed.");
+    });
+  });
+}, "Infinitely nested fallback content shadow trees should not be created, or this test will never terminate.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-invert.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-invert.html
index cb0df9b..e49bd187 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-invert.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-invert.html
@@ -20,7 +20,7 @@
 test(function() {
   var matrix3d = new DOMMatrix([10, 20, 30, 40, 40, 40, 30, 20, 10, 20, 40, 30, 20, 40, 50, 100]);
   matrix3d.invertSelf();
-  assert_3d_matrix_equals(matrix3d, [-1.6, 0.05, 0.6, 0.45, 2.05, -0.025, -0.8, -0.575, -0.4, 0, 0.2, 0.1, -0.3, 0, 0.1, 0.1]);
+  assert_matrix_almost_equals(matrix3d, new DOMMatrix([-1.6, 0.05, 0.6, 0.45, 2.05, -0.025, -0.8, -0.575, -0.4, 0, 0.2, 0.1, -0.3, 0, 0.1, 0.1]));
 }, "DOMMatrix invertSelf() - 3D matrix");
 
 test(function() {
diff --git a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly-inverse.html b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly-inverse.html
index 864ad5b..fa7d912 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly-inverse.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/geometry-interfaces-dom-matrix-readonly-inverse.html
@@ -19,7 +19,7 @@
 test(function() {
   var matrix3d = new DOMMatrixReadOnly([10, 20, 30, 40, 40, 40, 30, 20, 10, 20, 40, 30, 20, 40, 50, 100]);
   var inverse = matrix3d.inverse();
-  assert_3d_matrix_equals(inverse, [-1.6, 0.05, 0.6, 0.45, 2.05, -0.025, -0.8, -0.575, -0.4, 0, 0.2, 0.1, -0.3, 0, 0.1, 0.1]);
+  assert_matrix_almost_equals(inverse, new DOMMatrix([-1.6, 0.05, 0.6, 0.45, 2.05, -0.025, -0.8, -0.575, -0.4, 0, 0.2, 0.1, -0.3, 0, 0.1, 0.1]));
 }, "DOMMatrix inverse() - invertible - 3D matrix");
 
 test(function() {
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex-expected.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex-expected.html
deleted file mode 100644
index 93ee9c6..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex-expected.html
+++ /dev/null
@@ -1,105 +0,0 @@
-<!DOCTYPE html>
-<style>
-.flex-container {
-  background: #333;
-  border: 0px;
-  display: flex;
-  margin: 0px;
-  padding: 0px;
-}
-
-.flex-container.flex-direction-row {
-  flex-direction : row;
-}
-
-.flex-container.flex-direction-row-reverse {
-  flex-direction : row-reverse;
-}
-
-.flex-container.flex-direction-column {
-  flex-direction : column;
-}
-
-.flex-container.flex-direction-column-reverse {
-  flex-direction : column-reverse;
-}
-
-.flex-container.flex-direction-column-reverse {
-  flex-direction : column-reverse;
-}
-
-.flex-container.justify-content-center {
-  justify-content: center;
-}
-
-.flex-container.justify-content-space-around {
-  justify-content: space-around;
-}
-
-.flex-container.justify-content-space-between {
-  justify-content: space-between;
-}
-
-.flex-item {
-  width:50px;
-  height:50px;
-  margin:20px;
-  background: #eee;
-  line-height: 50px;
-  text-align: center;
-}
-</style>
-
-<fieldset>
-  <legend>Fieldset with display: flex</legend>
-  <div>these fieldsshouldn't bestacked vertically</div>
-</fieldset>
-
-<h1>flex-direction: row</h1>
-<div class="flex-container flex-direction-row">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>flex-direction: row-reverse</h1>
-<div class="flex-container flex-direction-row-reverse">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>flex-direction: column</h1>
-<div class="flex-container flex-direction-column">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>flex-direction: column-reverse</h1>
-<div class="flex-container flex-direction-column-reverse">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>justify-content: center</h1>
-<div class="flex-container justify-content-center">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>justify-content: space-around</h1>
-<div class="flex-container justify-content-space-around">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
-
-<h1>justify-content: space-between</h1>
-<div class="flex-container justify-content-space-between">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex.html
deleted file mode 100644
index 280ac837..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-flex.html
+++ /dev/null
@@ -1,107 +0,0 @@
-<!DOCTYPE html>
-<style>
-.flex-container {
-  background: #333;
-  border: 0px;
-  display: flex;
-  margin: 0px;
-  padding: 0px;
-}
-
-.flex-container.flex-direction-row {
-  flex-direction : row;
-}
-
-.flex-container.flex-direction-row-reverse {
-  flex-direction : row-reverse;
-}
-
-.flex-container.flex-direction-column {
-  flex-direction : column;
-}
-
-.flex-container.flex-direction-column-reverse {
-  flex-direction : column-reverse;
-}
-
-.flex-container.flex-direction-column-reverse {
-  flex-direction : column-reverse;
-}
-
-.flex-container.justify-content-center {
-  justify-content: center;
-}
-
-.flex-container.justify-content-space-around {
-  justify-content: space-around;
-}
-
-.flex-container.justify-content-space-between {
-  justify-content: space-between;
-}
-
-.flex-item {
-  width:50px;
-  height:50px;
-  margin:20px;
-  background: #eee;
-  line-height: 50px;
-  text-align: center;
-}
-</style>
-
-<fieldset style="display: flex;">
-  <legend>Fieldset with display: flex</legend>
-  <div>these fields</div>
-  <div>shouldn't be</div>
-  <div>stacked vertically</div>
-</fieldset>
-
-<h1>flex-direction: row</h1>
-<fieldset class="flex-container flex-direction-row">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>flex-direction: row-reverse</h1>
-<fieldset class="flex-container flex-direction-row-reverse">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>flex-direction: column</h1>
-<fieldset class="flex-container flex-direction-column">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>flex-direction: column-reverse</h1>
-<fieldset class="flex-container flex-direction-column-reverse">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>justify-content: center</h1>
-<fieldset class="flex-container justify-content-center">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>justify-content: space-around</h1>
-<fieldset class="flex-container justify-content-space-around">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
-
-<h1>justify-content: space-between</h1>
-<fieldset class="flex-container justify-content-space-between">
-  <div class="flex-item">1</div>
-  <div class="flex-item">2</div>
-  <div class="flex-item">3</div>
-</fieldset>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid-expected.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid-expected.html
deleted file mode 100644
index 6dc5132..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid-expected.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<style>
-.grid > div {
-  border: 1px solid;
-}
-.grid {
-  border: 0px;
-  display: grid;
-  height: 500px;
-  padding: 0px;
-  width: 500px;
-}
-</style>
-
-This should be displayed as a 4x4 grid.
-
-<div class="grid">
-  <div style="grid-column: 1; grid-row: 1;">row/column: 1/1</div>
-  <div style="grid-column: 2; grid-row: 1;">row/column: 1/2</div>
-  <div style="grid-column: 1: grid-row: 2;">row/column: 2/1</div>
-  <div style="grid-column: 2; grid-row: 2;">row/column: 2/2</div>
-</div>
-
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid.html
deleted file mode 100644
index b07a31b8..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-display-grid.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<style>
-.grid > div {
-  border: 1px solid;
-}
-.grid {
-  border: 0px;
-  display: grid;
-  height: 500px;
-  margin: 0px;
-  padding: 0px;
-  width: 500px;
-}
-</style>
-
-This should be displayed as a 4x4 grid.
-
-<fieldset class="grid">
-  <div style="grid-column: 1; grid-row: 1;">row/column: 1/1</div>
-  <div style="grid-column: 2; grid-row: 1;">row/column: 1/2</div>
-  <div style="grid-column: 1: grid-row: 2;">row/column: 2/1</div>
-  <div style="grid-column: 2; grid-row: 2;">row/column: 2/2</div>
-</fieldset>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float-expected.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float-expected.html
deleted file mode 100644
index 0699fe2..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float-expected.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<style>
-  #legend {
-    float: left;
-  }
-</style>
-
-This should display fieldset with the legend that is styled as a left float.
-
-<div id="fieldset">
-  <div id="legend">Left floating legend</legend>
-</div>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float.html
deleted file mode 100644
index 0606f8c..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/fieldset-legend-float.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<style>
-  fieldset {
-    border: 0px;
-    padding: 0px;
-    margin: 0px;
-  }
-
-  legend {
-    float: left;
-    padding: 0px;
-  }
-</style>
-
-This should display fieldset with the legend that is styled as a left float.
-
-<fieldset>
-  <legend>Left floating legend</legend>
-</fieldset>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset-expected.txt
deleted file mode 100644
index f1150ec9..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Legend in fieldset
-PASS
diff --git a/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset.html b/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset.html
deleted file mode 100644
index 0349290..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/fieldset/legend-margin-before-in-fieldset.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<script src="../../../resources/check-layout.js"></script>
-<fieldset id="fieldset">
-    <legend data-offset-y="108" style="margin-top: 100px">Legend in fieldset</legend>
-</fieldset>
-<script>
-    checkLayout("#fieldset");
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/html/imports/import-child-null-document-crash.html b/third_party/WebKit/LayoutTests/fast/html/imports/import-child-null-document-crash.html
new file mode 100644
index 0000000..48242b9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/imports/import-child-null-document-crash.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<link id="importLink" rel="import" href="dummy.html">
+<script>
+    test(() => {
+        document.head.appendChild(importLink.cloneNode());
+    }, "Cloning and appending the loading import should not crash.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor-expected.html b/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor-expected.html
new file mode 100644
index 0000000..a3f2940
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 100px;
+  height: 20px;
+  background-color: green;
+}
+</style>
+<div></div>
diff --git a/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor.html b/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor.html
new file mode 100644
index 0000000..c48c6e4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/marquee-bgcolor.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<marquee width="100px" height="20px" bgcolor="green" scrollamount="0"></marquee>
diff --git a/third_party/WebKit/LayoutTests/fast/html/marquee-shadow-root-no-access.html b/third_party/WebKit/LayoutTests/fast/html/marquee-shadow-root-no-access.html
new file mode 100644
index 0000000..2d934af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/marquee-shadow-root-no-access.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<marquee></marquee>
+<script>
+  var marquee = document.getElementsByTagName('marquee')[0];
+  test(function() {
+    assert_equals(marquee.shadowRoot, null);
+  }, 'Marquee shadow-root should not be accessible');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace-expected.html b/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace-expected.html
new file mode 100644
index 0000000..7bef622
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace-expected.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<style>
+div {
+  margin: 20px 30px;
+}
+</style>
+<div style="display: inline-block;">Marquee</div>
diff --git a/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace.html b/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace.html
new file mode 100644
index 0000000..dbc8bc8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/html/marquee-vspace-hspace.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<marquee behavior="alternate" direction="right" scrollamount="0"
+         hspace="30px" vspace="20px">
+    Marquee
+</marquee>
diff --git a/third_party/WebKit/LayoutTests/fast/inline/inline-marquee-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/inline/inline-marquee-crash-expected.txt
index 6e838a8..7696421 100644
--- a/third_party/WebKit/LayoutTests/fast/inline/inline-marquee-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/inline/inline-marquee-crash-expected.txt
@@ -1,2 +1 @@
-CONSOLE WARNING: line 361: Invalid keyframe value for property transform: translateX(NaNpx)
 No crash means PASS
diff --git a/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop-expected.txt b/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop-expected.txt
new file mode 100644
index 0000000..aa6432a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop-expected.txt
@@ -0,0 +1 @@
+PASS if this does not crash 
diff --git a/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop.html b/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop.html
new file mode 100644
index 0000000..a02f4f18
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/invalid/entity-reference-loop.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script>
+'use strict';
+
+if (window.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+}
+
+function done() {
+  if (window.testRunner) {
+    testRunner.notifyDone();
+  }
+}
+</script>
+PASS if this does not crash
+<iframe src="resources/entity-reference-loop.xml" onload="done();"></iframe>
diff --git a/third_party/WebKit/LayoutTests/fast/invalid/resources/entity-reference-loop.xml b/third_party/WebKit/LayoutTests/fast/invalid/resources/entity-reference-loop.xml
new file mode 100644
index 0000000..f63172b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/invalid/resources/entity-reference-loop.xml
@@ -0,0 +1,134 @@
+<!DOCTYPE doc [
+<!ENTITY a "
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+<a>
+&a;
+">
+]>
+<doc> &a;
diff --git a/third_party/WebKit/LayoutTests/fast/media/mq-shape-expected.html b/third_party/WebKit/LayoutTests/fast/media/mq-shape-expected.html
new file mode 100644
index 0000000..b538565a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/media/mq-shape-expected.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    body {
+        background: gray;
+    }
+
+    div {
+        border: 2px solid black;
+        background: blue;
+        height:300px;
+        width: 300px;
+    }
+</style>
+</head>
+<body>
+<div>
+    This page should have a rectagle box and the background should be blue.
+</div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/media/mq-shape.html b/third_party/WebKit/LayoutTests/fast/media/mq-shape.html
new file mode 100644
index 0000000..5c086a5d2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/media/mq-shape.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    body {
+        background: red;
+    }
+    @media (shape: rect) {
+        body {
+            background: gray;
+        }
+
+        div {
+            border: 2px solid black;
+            background: blue;
+            height:300px;
+            width: 300px;
+        }
+    }
+</style>
+</head>
+<body>
+<div>
+    This page should have a rectagle box and the background should be blue.
+</div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/nested-with-spanner-and-margin-crash.html b/third_party/WebKit/LayoutTests/fast/multicol/nested-with-spanner-and-margin-crash.html
new file mode 100644
index 0000000..7ccd370
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/nested-with-spanner-and-margin-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<div style="columns:2; orphans:1; widows:1;">
+    <div style="columns:2;">
+        <br>
+        <div style="column-span:all;"></div>
+        <div><br></div>
+        <div style="margin-top:1px;"></div>
+    </div>
+</div>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    test(() => {}, "PASS if no crash or assertion failure");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/text/basic/generic-family-changes.html b/third_party/WebKit/LayoutTests/fast/text/basic/generic-family-changes.html
index 93aa6dd11..71d71077 100644
--- a/third_party/WebKit/LayoutTests/fast/text/basic/generic-family-changes.html
+++ b/third_party/WebKit/LayoutTests/fast/text/basic/generic-family-changes.html
@@ -5,7 +5,7 @@
 </style>
 </head>
 <body>
-Tests of WebKit's intepretation of font sizes when no absolute font size is specified.  Percentages and logical keywords scale
+Tests of WebKit's interpretation of font sizes when no absolute font size is specified.  Percentages and logical keywords scale
 to reflect the family type.  Opera 9 matches this behavior as well (except it has a bug with multiple font-family mappings
 as in the first example).
 
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align-expected.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align-expected.html
new file mode 100644
index 0000000..e8d613a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+div {
+  width: 15ch;
+  border: 2px solid blue;
+}
+</style>
+<div style="text-align: justify">
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+</div>
+<div style="text-align: right">
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align.html
new file mode 100644
index 0000000..cb38583
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-align.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script>
+if (window.internals)
+  internals.setMockHyphenation('en-us');
+</script>
+<style>
+div {
+  width: 15ch;
+  border: 2px solid blue;
+}
+.hyphen {
+  hyphens: auto;
+}
+</style>
+<div style="text-align: justify">
+  <div class="hyphen">This is hyphenation enabled paragraph.</div>
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+</div>
+<div style="text-align: right">
+  <div class="hyphen">This is hyphenation enabled paragraph.</div>
+  <div>This is hyphen&shy;ation enabled paragraph.</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock-expected.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock-expected.html
index fb98853..5dbdf0d 100644
--- a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock-expected.html
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock-expected.html
@@ -25,18 +25,6 @@
 </script>
 <div lang="en-us">
   <div>
-    <span style="width: 2ch;">hyphenation</span>
-    <span style="width: 3.1ch;">hyphenation</span>
-    <span style="width: 4ch;">hyphenation</span>
-    <span style="width: 5.1ch;">hyphenation</span>
-    <span style="width: 6ch;">hyphenation</span>
-    <span style="width: 7.1ch;">hyphenation</span>
-    <span style="width: 8ch;">hyphenation</span>
-    <span style="width: 9ch;">hyphenation</span>
-    <span style="width: 10ch;">hyphenation</span>
-    <span style="width: 11ch;">hyphenation</span>
-  </div>
-  <div>
     <span style="width: 2ch;">hyphenation<img></span>
     <span style="width: 3.1ch;">hy-<br>phenation<img></span>
     <span style="width: 4ch;">hy-<br>phenation<img></span>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock.html
index be7e4d7a..01b04542 100644
--- a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock.html
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-auto-mock.html
@@ -27,18 +27,6 @@
 </script>
 <div lang="en-us">
   <div>
-    <span style="width: 2ch;">hyphenation</span>
-    <span style="width: 3.1ch;">hyphenation</span>
-    <span style="width: 4ch;">hyphenation</span>
-    <span style="width: 5.1ch;">hyphenation</span>
-    <span style="width: 6ch;">hyphenation</span>
-    <span style="width: 7.1ch;">hyphenation</span>
-    <span style="width: 8ch;">hyphenation</span>
-    <span style="width: 9ch;">hyphenation</span>
-    <span style="width: 10ch;">hyphenation</span>
-    <span style="width: 11ch;">hyphenation</span>
-  </div>
-  <div>
     <span style="width: 2ch;">hyphenation<img></span>
     <span style="width: 3.1ch;">hyphenation<img></span>
     <span style="width: 4ch;">hyphenation<img></span>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word-expected.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word-expected.html
new file mode 100644
index 0000000..d0205a66
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word-expected.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+div > div {
+  border: thin solid black;
+  font-family: 'Courier New';
+}
+</style>
+<div lang="en-us">
+  <p>Do not hyphenate the last word in a paragraph.
+  <div style="width: 15ch">This is<br>hyphenation</div>
+  <div style="width: 15ch">This is<br>hyphenation</div>
+
+  <p>Do hyphenate single word.
+  <div style="width: 8ch">hyphen-<br>ation</div>
+  <div style="width: 8ch">hyphen-<br>ation</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word.html
new file mode 100644
index 0000000..a14c701
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/hyphens-orphaned-word.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script>
+if (window.internals)
+  internals.setMockHyphenation('en-us');
+</script>
+<style>
+div {
+  border: thin solid black;
+  font-family: 'Courier New';
+  -webkit-hyphens: auto;
+  hyphens: auto;
+}
+</style>
+<body lang="en-us">
+  <p>Do not hyphenate the last word in a paragraph.</p>
+  <div style="width: 15ch">This is hyphenation</div>
+  <div style="width: 15ch">This is hyphenation
+  </div>
+
+  <p>Do hyphenate single word.</p>
+  <div style="width: 8ch">hyphenation</div>
+  <div style="width: 8ch">hyphenation
+  </div>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority-expected.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority-expected.html
new file mode 100644
index 0000000..c762c97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority-expected.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<style>
+dt {
+  float: left;
+  width: 19ch;
+}
+dd {
+  margin-left: 20ch;
+}
+div {
+  border: thick solid blue;
+  width: 8ch;
+  font-family: 'Courier New';
+}
+.ahem {
+  font-family: ahem;
+}
+</style>
+<body lang="en-us">
+<dl>
+  <dt>hyphens
+  <dd><div>hyphen-<br>ation is</div>
+  <dd><div>hyphen-<br>ation</div>
+  <dt>hyphens break-word
+  <dd><div>hyphen-<br>ation is</div>
+  <dd><div>hyphen-<br>ation</div>
+  <dt>hyphens break-all
+  <dd><div>hyphen-<br>ation is</div>
+  <dd><div>hyphen-<br>ation</div>
+  <dt>hyphens break-all break-word
+  <dd><div>hyphen-<br>ation is</div>
+  <dd><div>hyphen-<br>ation</div>
+  <dt>break-word
+  <dd><div class="ahem">XXXXXXXX<br>)</div>
+  <dt>break-all
+  <dd><div class="ahem">XXXXXXX<br>X)</div>
+  <dt>break-all break-word
+  <dd><div class="ahem">XXXXXXX<br>X)</div>
+</dl>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority.html b/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority.html
new file mode 100644
index 0000000..dbcc969
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hyphens/midword-break-priority.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<script>
+if (window.internals)
+  internals.setMockHyphenation('en-us');
+</script>
+<style>
+dt {
+  float: left;
+  width: 19ch;
+}
+dd {
+  margin-left: 20ch;
+}
+div {
+  border: thick solid blue;
+  width: 8ch;
+  font-family: 'Courier New';
+}
+.ahem {
+  font-family: ahem;
+}
+.hyphens {
+  hyphens: auto;
+  -webkit-hyphens: auto;
+  -moz-hyphens: auto;
+  -ms-hyphens: auto;
+}
+.break-word {
+  word-wrap: break-word;
+}
+.break-all {
+  word-break: break-all;
+}
+</style>
+<body lang="en-us">
+<dl>
+  <dt>hyphens
+  <dd><div class="hyphens">hyphenation is</div>
+  <dd><div class="hyphens">hyphenation</div>
+  <dt>hyphens break-word
+  <dd><div class="hyphens break-word">hyphenation is</div>
+  <dd><div class="hyphens break-word">hyphenation</div>
+  <dt>hyphens break-all
+  <dd><div class="hyphens break-all">hyphenation is</div>
+  <dd><div class="hyphens break-all">hyphenation</div>
+  <dt>hyphens break-all break-word
+  <dd><div class="hyphens break-all break-word">hyphenation is</div>
+  <dd><div class="hyphens break-all break-word">hyphenation</div>
+  <dt>break-word
+  <dd><div class="ahem break-word">XXXXXXXX)</div>
+  <dt>break-all
+  <dd><div class="ahem break-all">XXXXXXXX)</div>
+  <dt>break-all break-word
+  <dd><div class="ahem break-all break-word">XXXXXXXX)</div>
+</dl>
+</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled-expected.txt b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled-expected.txt
new file mode 100644
index 0000000..b3040c2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS Fullscreen disabled on URL: resources/feature-policy-fullscreen.html with allowfullscreen = false 
+PASS Fullscreen disabled on URL: http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html with allowfullscreen = false 
+FAIL Fullscreen disabled on URL: resources/feature-policy-fullscreen.html with allowfullscreen = true assert_false: Document.fullscreenEnabled: expected false got true
+FAIL Fullscreen disabled on URL: http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html with allowfullscreen = true assert_false: Document.fullscreenEnabled: expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled.php b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled.php
new file mode 100644
index 0000000..fade4c3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-disabled.php
@@ -0,0 +1,43 @@
+<?php
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test ensures that fullscreen feature when disabled may not be called by
+// any iframe even when allowfullscreen is set.
+
+Header("Feature-Policy: {\"fullscreen\": []}");
+?>
+
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<iframe id="f1"></iframe>
+<iframe id="f2" allowfullscreen></iframe>
+<script>
+var srcs = [
+  "resources/feature-policy-fullscreen.html",
+  "http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html"
+];
+var f1 = document.getElementById('f1');
+var f2 = document.getElementById('f2');
+
+function loadFrames(iframe) {
+  for (var src of srcs) {
+    promise_test(function() {
+      iframe.src = src;
+      return new Promise(function(resolve, reject) {
+        window.addEventListener('message', function(e) {
+          resolve(e.data);
+        }, { once: true });
+      }).then(function(data) {
+        assert_false(data.enabled, 'Document.fullscreenEnabled:');
+        assert_equals(data.type, 'error', 'Document.requestFullscreen():');
+      });
+    }, 'Fullscreen disabled on URL: ' + src + ' with allowfullscreen = ' + iframe.allowFullscreen);
+  }
+}
+
+loadFrames(f1);
+loadFrames(f2);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforall.php b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforall.php
new file mode 100644
index 0000000..18f970a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforall.php
@@ -0,0 +1,49 @@
+<?php
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test ensures that fullscreen feature when enabled for all works across
+// origins when allowfullscreen is set. No iframe may call it when
+// allowfullscreen is not set.
+
+Header("Feature-Policy: {\"fullscreen\": [\"*\"]}");
+?>
+
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<iframe id="f1"></iframe>
+<iframe id="f2" allowfullscreen></iframe>
+<script>
+var srcs = [
+  "resources/feature-policy-fullscreen.html",
+  "http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html"
+];
+var f1 = document.getElementById('f1');
+var f2 = document.getElementById('f2');
+
+function loadFrames(iframe) {
+  for (var src of srcs) {
+    promise_test(function(t) {
+      iframe.src = src;
+      return new Promise(function(resolve, reject) {
+        window.addEventListener('message', function(e) {
+          resolve(e.data);
+        }, { once: true });
+      }).then(function(data) {
+        if (iframe.id === "f2") {
+          assert_true(data.enabled, 'Document.fullscreenEnabled:');
+          assert_equals(data.type, 'change', 'Document.requestFullscreen():');
+        } else {
+          assert_false(data.enabled, 'Document.fullscreenEnabled:');
+          assert_equals(data.type, 'error', 'Document.requestFullscreen():');
+        }
+      });
+    }, 'Fullscreen enabled for all on URL: ' + src + ' with allowfullscreen = ' + iframe.allowFullscreen);
+  }
+}
+
+loadFrames(f1);
+loadFrames(f2);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforself.php b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforself.php
new file mode 100644
index 0000000..42296132
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/fullscreen-enabledforself.php
@@ -0,0 +1,49 @@
+<?php
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test ensures that fullscreen feature when enabled for self only works
+// in the same orgin but not cross origins when allowfullscreen is set. No
+// iframe may call it when allowfullscreen is not set.
+
+Header("Feature-Policy: {\"fullscreen\": [\"self\"]}");
+?>
+
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<iframe id="f1"></iframe>
+<iframe id="f2" allowfullscreen></iframe>
+<script>
+var srcs = [
+  "resources/feature-policy-fullscreen.html",
+  "http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html"
+];
+var f1 = document.getElementById('f1');
+var f2 = document.getElementById('f2');
+
+function loadFrames(iframe) {
+  for (var src of srcs) {
+    promise_test(function(t) {
+      iframe.src = src;
+      return new Promise(function(resolve, reject) {
+        window.addEventListener('message', function(e) {
+          resolve(e.data);
+        }, { once: true });
+      }).then(function(data) {
+        if (src === srcs[0] || iframe.id === "f2") {
+          assert_true(data.enabled, 'Document.fullscreenEnabled:');
+          assert_equals(data.type, 'change', 'Document.requestFullscreen():');
+        } else {
+          assert_false(data.enabled, 'Document.fullscreenEnabled:');
+          assert_equals(data.type, 'error', 'Document.requestFullscreen():');
+        }
+      });
+    }, 'Fullscreen enabled for self on URL: ' + src + ' with allowfullscreen = ' + iframe.allowFullscreen);
+  }
+}
+
+loadFrames(f1);
+loadFrames(f2);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/feature-policy-fullscreen.html b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/feature-policy-fullscreen.html
new file mode 100644
index 0000000..1fe1b74
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/feature-policy/resources/feature-policy-fullscreen.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Feature-Policy Fullscreen</title>
+<div>Fullscreen</div>
+<script>
+document.onwebkitfullscreenerror = function() {
+  parent.postMessage({ type: 'error', enabled: document.webkitFullscreenEnabled }, '*');
+};
+
+document.onwebkitfullscreenchange = function() {
+  document.webkitExitFullscreen();
+  parent.postMessage({ type: 'change', enabled: document.webkitFullscreenEnabled }, '*');
+};
+
+document.addEventListener('keypress', function() {
+  document.querySelector('div').webkitRequestFullscreen();
+});
+
+window.onload = function() {
+  focus();
+  eventSender.keyDown('a', []);
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/fetch.js b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/fetch.js
index 07e5306..9f21bbea 100644
--- a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/fetch.js
+++ b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/fetch.js
@@ -37,6 +37,25 @@
   }, 'fetch data: URL');
 
 promise_test(function(t) {
+    return fetch('data:,Foobar',
+                 {
+                   method: 'POST',
+                   body: 'Test'
+                 })
+      .then(function(response) {
+          assert_equals(response.status, 200);
+          assert_equals(response.statusText, 'OK');
+          assert_equals(response.headers.get('Content-Type'),
+                        'text/plain;charset=US-ASCII');
+          assert_equals(size(response.headers), 1);
+          return response.text();
+        })
+      .then(function(text) {
+          assert_equals(text, 'Foobar');
+        });
+  }, 'fetch data: URL with the POST method');
+
+promise_test(function(t) {
     return fetch('data:text/html;charset=utf-8;base64,5paH5a2X')
       .then(function(response) {
           assert_equals(response.status, 200);
diff --git a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/thorough/scheme-data.js b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/thorough/scheme-data.js
index 1b52059..45d8c85 100644
--- a/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/thorough/scheme-data.js
+++ b/third_party/WebKit/LayoutTests/http/tests/fetch/script-tests/thorough/scheme-data.js
@@ -23,10 +23,14 @@
 // data: requests with non-GET methods.
   [BASE_URL + 'url=' + encodeURIComponent(url) +
    '&mode=same-origin&method=POST',
-   [fetchRejected]],
+   [fetchResolved, noContentLength, hasContentType, noServerHeader, hasBody,
+    typeBasic],
+   [checkJsonpSuccess]],
   [BASE_URL + 'url=' + encodeURIComponent(url) +
    '&mode=same-origin&method=HEAD',
-   [fetchRejected]],
+   [fetchResolved, noContentLength, hasContentType, noServerHeader, hasBody,
+    typeBasic],
+   [checkJsonpSuccess]],
 
 // data: requests with same-origin redirects.
   [REDIRECT_URL + encodeURIComponent(url) + '&mode=same-origin&method=GET',
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/reattach-after-editing-styles-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/reattach-after-editing-styles-expected.txt
index 4418ce14..60a5d1f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/reattach-after-editing-styles-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-enabled/reattach-after-editing-styles-expected.txt
@@ -6,11 +6,11 @@
 element.style { ()
 
 [expanded] 
-#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:1)
+#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:11)
     font-family: arial;
 
 [expanded] 
-#testDiv { (style.css:1 -> style.css:1:1)
+#testDiv { (style.css:1 -> style.css:1:11)
     color: red;
 
 [expanded] 
@@ -22,14 +22,14 @@
 element.style { ()
 
 [expanded] 
-#testDiv, my-custom-tag { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+#testDiv, my-custom-tag { (inspector-stylesheet:1 -> inspector-stylesheet:1:26)
 
 [expanded] 
-#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:1)
+#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:11)
     font-family: Helvetica;
 
 [expanded] 
-#testDiv { (style.css:1 -> style.css:1:1)
+#testDiv { (style.css:1 -> style.css:1:11)
     color: green;
 
 [expanded] 
@@ -42,14 +42,14 @@
 element.style { ()
 
 [expanded] 
-#testDiv, my-custom-tag { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+#testDiv, my-custom-tag { (inspector-stylesheet:1 -> inspector-stylesheet:1:26)
 
 [expanded] 
-#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:1)
+#testDiv { (reattach-after-…-styles.html:7 -> reattach-after-editing-styles.html:7:11)
     font-family: Helvetica;
 
 [expanded] 
-#testDiv { (style.css:1 -> style.css:1:1)
+#testDiv { (style.css:1 -> style.css:1:11)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/edit-css-with-source-url-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/edit-css-with-source-url-expected.txt
index a3768b3e0..633f2bd8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/edit-css-with-source-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/edit-css-with-source-url-expected.txt
@@ -16,7 +16,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (foo.css:1 -> foo.css:1:1)
+#inspected { (foo.css:1 -> foo.css:1:13)
     color: red;
 
 [expanded] 
@@ -30,7 +30,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (foo.css:1 -> foo.css:1:1)
+#inspected { (foo.css:1 -> foo.css:1:13)
     color: green;
     : ;
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-deprecated-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-deprecated-expected.txt
index 849f71c..b6ae0aa8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-deprecated-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-deprecated-expected.txt
@@ -5,11 +5,11 @@
 element.style { ()
 
 [expanded] 
-#container #inspected { (selector-line.scss:4 -> selector-line.scss:4:5)
+#container #inspected { (selector-line.scss:4 -> selector-line.scss:4:16)
     color: green;
 
 [expanded] 
-#inspected { (selector-line-d…recated.html:4 -> selector-line-deprecated.html:4:1)
+#inspected { (selector-line-d…recated.html:5 -> selector-line-deprecated.html:5:2)
 /-- overloaded --/     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-expected.txt
index 0c4e8d6..7f83ae82 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-expected.txt
@@ -5,11 +5,11 @@
 element.style { ()
 
 [expanded] 
-#container #inspected { (selector-line.scss:4 -> selector-line.scss:4:5)
+#container #inspected { (selector-line.scss:4 -> selector-line.scss:4:16)
     color: green;
 
 [expanded] 
-#inspected { (selector-line.html:4 -> selector-line.html:4:1)
+#inspected { (selector-line.html:5 -> selector-line.html:5:2)
 /-- overloaded --/     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-deprecated-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-deprecated-expected.txt
index fe753e9..22df5bc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-deprecated-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-deprecated-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#container #inspected { (selector-line-s…-header.scss:4 -> selector-line-sourcemap-header.scss:4:5)
+#container #inspected { (selector-line-s…-header.scss:4 -> selector-line-sourcemap-header.scss:4:16)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-expected.txt
index fe753e9..22df5bc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/selector-line-sourcemap-header-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#container #inspected { (selector-line-s…-header.scss:4 -> selector-line-sourcemap-header.scss:4:5)
+#container #inspected { (selector-line-s…-header.scss:4 -> selector-line-sourcemap-header.scss:4:16)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted-expected.txt
deleted file mode 100644
index ed3be76..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Tests network columns are sortable.
-
-Sorted by: name
-    abe.png
-    abe.png
-    foo.bar
-    style.css
-
-Sorted by: name
-    style.css
-    foo.bar
-    abe.png
-    abe.png
-
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted.html
deleted file mode 100644
index b1bbcc0..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-columns-sorted.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<html>
-<head>
-<script src="../inspector-test.js"></script>
-<script src="../network-test.js"></script>
-<script>
-function test()
-{
-    InspectorTest.recordNetwork();
-
-    var totalResourceCount = 4;
-    function fetchRequests() {
-        InspectorTest.makeFetch("resources/style.css", {}, ensureAllResources);
-        InspectorTest.makeFetch("resources/abe.png", {}, () => {
-            // Ensures result is cached.
-            InspectorTest.makeFetch("resources/abe.png", {}, ensureAllResources);
-            ensureAllResources();
-        });
-        InspectorTest.makeFetch("missing/foo.bar", {}, ensureAllResources);
-    }
-
-    var resourceCount = 0;
-    function ensureAllResources()
-    {
-        if (++resourceCount >= totalResourceCount) {
-            debugger;
-            UI.panels.network._networkLogView._refresh();
-            sortGrid();
-            InspectorTest.completeTest();
-        }
-    }
-
-    function sortGrid()
-    {
-        var logView = UI.panels.network._networkLogView;
-        var dataGrid = logView._dataGrid;
-        var columnsView = logView._columns;
-        InspectorTest.addSniffer(logView, "dataGridSorted", dataGridSorted.bind(null, logView), true);
-
-        dataGrid.markColumnAsSortedBy('name', UI.DataGrid.Order.Ascending);
-        columnsView.sortByCurrentColumn();
-        dataGrid.markColumnAsSortedBy('name', UI.DataGrid.Order.Descending);
-        columnsView.sortByCurrentColumn();
-    }
-
-    function dataGridSorted(logView)
-    {
-        var nodes = logView._dataGrid.rootNode().flattenChildren();
-        InspectorTest.addResult("Sorted by: " + logView._dataGrid.sortColumnId());
-        for (var node of nodes)
-            InspectorTest.addResult("    " + node.request().name());
-        InspectorTest.addResult("");
-    }
-    fetchRequests();
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests network columns are sortable.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-filters.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-filters.html
index 71805962..2dba135 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-filters.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-filters.html
@@ -90,9 +90,8 @@
      */
     function setNetworkLogFilter(value, isRegex)
     {
-        UI.panels.network._networkLogView._textFilterUI._filterInputElement.value = value;
         UI.panels.network._networkLogView._textFilterUI._regexCheckBox.checked = isRegex;
-        UI.panels.network._networkLogView._textFilterUI._valueChanged();
+        UI.panels.network._networkLogView._textFilterUI.setValue(value);
         UI.panels.network._networkLogView._filterChanged(null); // event not used in this method, so passing null
     }
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-go-to-file-dialog-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-go-to-file-dialog-expected.txt
index 7d06db0..ecf8d8a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-go-to-file-dialog-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-go-to-file-dialog-expected.txt
@@ -15,10 +15,10 @@
 Running: addFileSystemMapping
 
 Running: goToSourceAfterBinding
+file:///var/www/inspector/persistence/resources/foo.js
 http://127.0.0.1:8000/inspector/debugger-test.js
 http://127.0.0.1:8000/inspector/inspector-test.js
 http://127.0.0.1:8000/inspector/isolated-filesystem-test.js
 http://127.0.0.1:8000/inspector/persistence/persistence-go-to-file-dialog.html
 http://127.0.0.1:8000/inspector/persistence/persistence-test.js
-http://127.0.0.1:8000/inspector/persistence/resources/foo.js
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
index ef92edf..68956628 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs-expected.txt
@@ -5,14 +5,14 @@
 
 Running: openNetworkTab
 SourceFrame: http://127.0.0.1:8000/inspector/persistence/resources/foo.js
-    selection: {"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
-    firstVisibleLine: 0
+    selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
+    firstVisibleLine: 2
     isDirty: false
 
 Running: openFileSystemTab
 SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
-    selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
-    firstVisibleLine: 2
+    selection: {"startLine":0,"startColumn":0,"endLine":0,"endColumn":0}
+    firstVisibleLine: 0
     isDirty: true
 Opened tabs: 
     file:///var/www/inspector/persistence/resources/foo.js
@@ -20,8 +20,8 @@
 
 Running: addFileMapping
 Opened tabs: 
-    http://127.0.0.1:8000/inspector/persistence/resources/foo.js
-SourceFrame: http://127.0.0.1:8000/inspector/persistence/resources/foo.js
+    file:///var/www/inspector/persistence/resources/foo.js
+SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
     selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
     firstVisibleLine: 2
     isDirty: true
@@ -29,7 +29,6 @@
 Running: removeFileMapping
 Opened tabs: 
     file:///var/www/inspector/persistence/resources/foo.js
-    http://127.0.0.1:8000/inspector/persistence/resources/foo.js
 SourceFrame: file:///var/www/inspector/persistence/resources/foo.js
     selection: {"startLine":2,"startColumn":0,"endLine":2,"endColumn":5}
     firstVisibleLine: 2
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
index 7435f62d..1386092 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-merge-editor-tabs.html
@@ -28,6 +28,8 @@
             function onNetworkTab(sourceFrame)
             {
                 networkSourceFrame = sourceFrame;
+                networkSourceFrame.setSelection(new Common.TextRange(2, 0, 2, 5));
+                networkSourceFrame.scrollToLine(2);
                 dumpSourceFrame(networkSourceFrame);
                 next();
             }
@@ -48,8 +50,6 @@
             function onFileSystemTab(sourceFrame)
             {
                 fileSystemSourceFrame = sourceFrame;
-                fileSystemSourceFrame.setSelection(new Common.TextRange(2, 0, 2, 5));
-                fileSystemSourceFrame.scrollToLine(2);
                 dumpSourceFrame(fileSystemSourceFrame);
                 dumpEditorTabs();
                 next();
@@ -64,7 +64,7 @@
             function onBindingCreated()
             {
                 dumpEditorTabs();
-                dumpSourceFrame(networkSourceFrame);
+                dumpSourceFrame(fileSystemSourceFrame);
                 next();
             }
         },
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload-expected.txt
index bc0d50ab..5bbab8c5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload-expected.txt
@@ -5,7 +5,7 @@
 
 Running: addNetworkFooJS
 
-Running: setBreakpointInNetworkUISourceCode
+Running: setBreakpointInFileSystemUISourceCode
     http://127.0.0.1:8000/inspector/persistence/resources/foo.js:2
 
 Running: reloadPageAndDumpBreakpoints
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload.html
index 3e63ef2..6be7a48 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-move-breakpoints-on-reload.html
@@ -30,9 +30,9 @@
             InspectorTest.waitForBinding('foo.js').then(next);
         },
 
-        function setBreakpointInNetworkUISourceCode(next)
+        function setBreakpointInFileSystemUISourceCode(next)
         {
-            InspectorTest.waitForUISourceCode('foo.js', Workspace.projectTypes.Network)
+            InspectorTest.waitForUISourceCode('foo.js', Workspace.projectTypes.FileSystem)
                 .then(sourceCode => InspectorTest.showUISourceCodePromise(sourceCode))
                 .then(onSourceFrame);
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-search-across-all-files-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-search-across-all-files-expected.txt
index bcfa7c3b..2a930d0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-search-across-all-files-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-search-across-all-files-expected.txt
@@ -12,6 +12,6 @@
 Running: addFileMapping
 
 Running: dumpSearchResults
-Search result #1: uiSourceCode.url = http://127.0.0.1:8000/inspector/persistence/resources/foo.js
+Search result #1: uiSourceCode.url = file:///var/www/inspector/persistence/resources/foo.js
   search match #1: lineNumber = 2, lineContent = 'window.foo = ()=>'foo';'
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab-expected.txt
new file mode 100644
index 0000000..e305b37ec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab-expected.txt
@@ -0,0 +1,11 @@
+Verify that a network file tab gets substituted with filesystem tab when persistence binding comes.
+
+
+Running: openNetworkTab
+Opened tabs: 
+    http://127.0.0.1:8000/inspector/persistence/resources/foo.js
+
+Running: addMapping
+Opened tabs: 
+    file:///var/www/inspector/persistence/resources/foo.js
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab.html
new file mode 100644
index 0000000..12bc769
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-switch-editor-tab.html
@@ -0,0 +1,57 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../debugger-test.js"></script>
+<script src="../isolated-filesystem-test.js"></script>
+<script src="./persistence-test.js"></script>
+<script src="./resources/foo.js"></script>
+<script>
+
+function test()
+{
+    InspectorTest.runTestSuite([
+        function openNetworkTab(next)
+        {
+            InspectorTest.waitForUISourceCode("foo.js", Workspace.projectTypes.Network)
+                .then(sourceCode => InspectorTest.showUISourceCodePromise(sourceCode))
+                .then(onSourceFrame);
+
+            function onSourceFrame(sourceFrame)
+            {
+                dumpEditorTabs();
+                next();
+            }
+        },
+
+        function addMapping(next)
+        {
+            var fs = new InspectorTest.TestFileSystem("file:///var/www");
+            fs.addFileMapping("http://127.0.0.1:8000", "/");
+            InspectorTest.addFooJSFile(fs);
+            fs.reportCreated(function() { });
+            InspectorTest.waitForBinding("foo.js").then(onBindingCreated);
+
+            function onBindingCreated()
+            {
+                dumpEditorTabs();
+                next();
+            }
+        },
+    ]);
+
+    function dumpEditorTabs()
+    {
+        var editorContainer = UI.panels.sources._sourcesView._editorContainer;
+        var openedUISourceCodes = editorContainer._tabIds.keysArray();
+        openedUISourceCodes.sort((a, b) => a.url().compareTo(b.url()));
+        InspectorTest.addResult("Opened tabs: ");
+        for (code of openedUISourceCodes)
+            InspectorTest.addResult("    " + code.url());
+    }
+};
+</script>
+</head>
+<body onload="runTest()">
+<p>Verify that a network file tab gets substituted with filesystem tab when persistence binding comes.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode-expected.txt
new file mode 100644
index 0000000..05192c33
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode-expected.txt
@@ -0,0 +1,12 @@
+Verify that for a fileSystem UISourceCode with persistence binding TabbedEditorContainer opens filesystem UISourceCode.
+
+Binding created: {
+       network: http://127.0.0.1:8000/inspector/persistence/resources/foo.js
+    fileSystem: file:///var/www/inspector/persistence/resources/foo.js
+    exactMatch: false
+}
+Opened tabs before opening any UISourceCodes:
+request open uiSourceCode: file:///var/www/inspector/persistence/resources/foo.js
+Opened tabs after opening UISourceCode:
+    file:///var/www/inspector/persistence/resources/foo.js
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode.html
new file mode 100644
index 0000000..8af4365a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-filesystem-uisourcecode.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+<script src="../inspector-test.js"></script>
+<script src="../debugger-test.js"></script>
+<script src="../isolated-filesystem-test.js"></script>
+<script src="./persistence-test.js"></script>
+<script src="./resources/foo.js"></script>
+<script>
+
+function test()
+{
+    var fs = new InspectorTest.TestFileSystem("file:///var/www");
+    var fsEntry = InspectorTest.addFooJSFile(fs);
+    fs.addFileMapping("http://127.0.0.1:8000", "/");
+    fs.reportCreated(function() { });
+    InspectorTest.waitForBinding("foo.js").then(onBindingCreated);
+
+    function onBindingCreated(binding)
+    {
+        InspectorTest.addResult("Binding created: " + binding);
+        dumpEditorTabs("Opened tabs before opening any UISourceCodes:");
+        InspectorTest.addResult("request open uiSourceCode: " + binding.fileSystem.url());
+        UI.panels.sources.showUISourceCode(binding.network, 0, 0);
+        dumpEditorTabs("Opened tabs after opening UISourceCode:");
+        InspectorTest.completeTest();
+    }
+
+    function dumpEditorTabs(title)
+    {
+        var editorContainer = UI.panels.sources._sourcesView._editorContainer;
+        var openedUISourceCodes = editorContainer._tabIds.keysArray();
+        openedUISourceCodes.sort((a, b) => a.url().compareTo(b.url()));
+        InspectorTest.addResult(title);
+        for (code of openedUISourceCodes)
+            InspectorTest.addResult("    " + code.url());
+    }
+};
+</script>
+</head>
+<body onload="runTest()">
+<p>Verify that for a fileSystem UISourceCode with persistence binding TabbedEditorContainer opens filesystem UISourceCode.</p>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode-expected.txt
deleted file mode 100644
index be5ee4a..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Verify that for a fileSystem UISourceCode with network binding TabbedEditorContainer opens network UISourceCode.
-
-Binding created: {
-       network: http://127.0.0.1:8000/inspector/persistence/resources/foo.js
-    fileSystem: file:///var/www/inspector/persistence/resources/foo.js
-    exactMatch: false
-}
-Opened tabs before opening any UISourceCodes:
-request open uiSourceCode: file:///var/www/inspector/persistence/resources/foo.js
-Opened tabs after opening UISourceCode:
-    http://127.0.0.1:8000/inspector/persistence/resources/foo.js
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode.html b/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode.html
deleted file mode 100644
index c12eb74..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/persistence/persistence-tabbed-editor-opens-network-uisourcecode.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<html>
-<head>
-<script src="../inspector-test.js"></script>
-<script src="../debugger-test.js"></script>
-<script src="../isolated-filesystem-test.js"></script>
-<script src="./persistence-test.js"></script>
-<script src="./resources/foo.js"></script>
-<script>
-
-function test()
-{
-    var fs = new InspectorTest.TestFileSystem("file:///var/www");
-    var fsEntry = InspectorTest.addFooJSFile(fs);
-    fs.addFileMapping("http://127.0.0.1:8000", "/");
-    fs.reportCreated(function() { });
-    InspectorTest.waitForBinding("foo.js").then(onBindingCreated);
-
-    function onBindingCreated(binding)
-    {
-        InspectorTest.addResult("Binding created: " + binding);
-        dumpEditorTabs("Opened tabs before opening any UISourceCodes:");
-        InspectorTest.addResult("request open uiSourceCode: " + binding.fileSystem.url());
-        UI.panels.sources.showUISourceCode(binding.fileSystem, 0, 0);
-        dumpEditorTabs("Opened tabs after opening UISourceCode:");
-        InspectorTest.completeTest();
-    }
-
-    function dumpEditorTabs(title)
-    {
-        var editorContainer = UI.panels.sources._sourcesView._editorContainer;
-        var openedUISourceCodes = editorContainer._tabIds.keysArray();
-        openedUISourceCodes.sort((a, b) => a.url().compareTo(b.url()));
-        InspectorTest.addResult(title);
-        for (code of openedUISourceCodes)
-            InspectorTest.addResult("    " + code.url());
-    }
-};
-</script>
-</head>
-<body onload="runTest()">
-<p>Verify that for a fileSystem UISourceCode with network binding TabbedEditorContainer opens network UISourceCode.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/embeddedEnforcement/subsumption_algorithm-general.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/embeddedEnforcement/subsumption_algorithm-general.html
index d05e23f..f5bf03f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/embeddedEnforcement/subsumption_algorithm-general.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/embeddedEnforcement/subsumption_algorithm-general.html
@@ -52,6 +52,13 @@
           url = generateUrlWithCSP(CROSS_ORIGIN, returned_csp);
           injectIframeWithCSP(url, EXPECT_LOAD, required_csp, t, "6");
         }, "Iframe with a matching and more restrictive ports should load.");
+
+      async_test(t => {
+          required_csp = "frame-src http://b.com:80";
+          returned_csp = "child-src https://b.com:443";
+          url = generateUrlWithCSP(CROSS_ORIGIN, returned_csp);
+          injectIframeWithCSP(url, EXPECT_LOAD, required_csp, t, "7");
+        }, "Iframe must load even if the ports are different but are default for the protocols.");
     </script>
 </body>
 </html>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html
new file mode 100644
index 0000000..168c1e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-allow-disallow-transitions.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script type="text/javascript">
+// Verify that when the 'src' attribute for an <img> element is changed, the
+// element transitions properly between states where primary content is shown,
+// fallback content is shown, and when the element is collapsed.
+//
+// This test is carried out using a matrix of <img> elements. Initially, all
+// images in the first row are set to show primary content, all images in the
+// second row are set to show fallback content, etc. Once everything is loaded,
+// the 'src' URLs are changed so that all images in the first column should show
+// primary content, in the second column fallback content, etc.
+
+var INITIAL_URLS = {
+  "primary": "resources/alpha.png",
+  "fallback": "resources/non-existent-1.png",
+  "collapsed": "resources/gamma.png"
+};
+
+var FINAL_URLS = {
+  "primary": "resources/beta.png",
+  "fallback": "resources/non-existent-2.png",
+  "collapsed": "resources/delta.png"
+};
+
+var EXPECTED_LOADEND_EVENT = {
+  "primary": "load",
+  "fallback": "error",
+  "collapsed": "error"
+};
+
+var EXPECTED_WIDTH = {
+  "primary": 100,
+  "fallback": 20,
+  "collapsed": 0
+};
+
+var numPendingImages =
+    Object.keys(INITIAL_URLS).length * Object.keys(FINAL_URLS).length;
+
+if (window.testRunner)
+  testRunner.setDisallowedSubresourcePathSuffixes(["gamma.png", "delta.png"]);
+
+let table = document.createElement("table");
+let headerRow = table.insertRow();
+
+headerRow.insertCell().innerHTML = "- - - - \\ Final<br>Initial \\";
+for (let finalState of Object.keys(FINAL_URLS)) {
+  headerRow.insertCell().textContent = finalState;
+}
+
+for (let [initialState, initialUrl] of Object.entries(INITIAL_URLS)) {
+  let row = table.insertRow();
+  row.insertCell().textContent = initialState;
+  for (let [finalState, finalUrl] of Object.entries(FINAL_URLS)) {
+    let cell = row.insertCell();
+    let img = document.createElement("img");
+    img.src = initialUrl;
+    async_test(t => {
+      let eventWatcher = new EventWatcher(t, img, ["load", "error"]);
+      eventWatcher.wait_for(EXPECTED_LOADEND_EVENT[initialState])
+        .then(_ => {
+          window.setTimeout(_ => img.src = finalUrl);
+          return eventWatcher.wait_for(EXPECTED_LOADEND_EVENT[finalState]);
+        })
+        .then(t.step_func_done(_ => {
+          assert_equals(img.clientWidth, EXPECTED_WIDTH[finalState],
+              "Image has incorrect width for the expected final state.");
+        }));
+    }, "State transition " + initialState + " to " + finalState);
+    cell.append(img);
+  }
+}
+document.body.append(table);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html
new file mode 100644
index 0000000..714b255
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed-after-redirect.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script type="text/javascript">
+// Tests that when resource loads initiated by <img> elements are disallowed
+// after a redirect, the corresponding image is collapsed in the layout.
+
+if (window.testRunner) {
+  // Inject a subresource filter to disallow 'beta.png' (but not 'alpha.png').
+  testRunner.setDisallowedSubresourcePathSuffixes(["beta.png"]);
+}
+
+async_test(t => {
+  let i = document.createElement("img");
+  i.onload = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width.");
+    assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height.");
+  });
+  i.onerror = t.unreached_func();
+  i.src = "http://localhost:8000/resources/redirect.php?url=/subresource_filter/resources/alpha.png";
+  document.body.append(i);
+}, "Images for which no URLs in the redirect chain are disallowed should still be displayed as normal.");
+
+async_test(t => {
+  let i = document.createElement("img");
+  i.onload = t.unreached_func();
+  i.onerror = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded.");
+    assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded.");
+  });
+  i.src = "http://localhost:8000/resources/redirect.php?url=/subresource_filter/resources/beta.png";
+  document.body.append(i);
+}, "Images that redirect to disallowed URLs should not be loaded and should be collapsed in the layout.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html
new file mode 100644
index 0000000..34bcfcb9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/image-disallowed.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script type="text/javascript">
+// Tests that when resource loads initiated by <img> elements are disallowed,
+// the corresponding image is collapsed in the layout.
+
+if (window.testRunner) {
+  // Inject a subresource filter to disallow 'beta.png' (but not 'alpha.png').
+  testRunner.setDisallowedSubresourcePathSuffixes(["beta.png"]);
+}
+
+async_test(t => {
+  let i = document.createElement("img");
+  i.onload = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width.");
+    assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height.");
+  });
+  i.onerror = t.unreached_func();
+  i.src = "resources/alpha.png";
+  document.body.append(i);
+}, "Images whose URLs are not disallowed should still be displayed as normal.");
+
+async_test(t => {
+  let i = document.createElement("img");
+  i.onload = t.unreached_func();
+  i.onerror = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded.");
+    assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded.");
+  });
+  i.src = "resources/beta.png";
+  document.body.append(i);
+}, "Images whose initial URL is disallowed should not be loaded and should be collapsed in the layout.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html
new file mode 100644
index 0000000..b7b0346
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/picture-disallowed.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script type="text/javascript">
+// Verify that the resource selection algorithm for <img> elements nested into
+// <picture> elements runs first, and only then is subresource filtering applied
+// to the selected resource.
+
+if (window.testRunner) {
+  // Inject a subresource filter to disallow 'beta.png' (but not 'alpha.png').
+  testRunner.setDisallowedSubresourcePathSuffixes(["beta.png"]);
+}
+
+function createPicture(selectedSourceUrl, notSelectedSourceUrl) {
+  let p = document.createElement("picture");
+  let s1 = document.createElement("source");
+  s1.srcset = notSelectedSourceUrl;
+  s1.type = "not-supported-type/to-disable-this-source";
+  p.append(s1);
+  let s2 = document.createElement("source");
+  s2.srcset = selectedSourceUrl;
+  s2.type = "image/png";
+  p.append(s2);
+  let i = document.createElement("img");
+  i.src = "totally-not-existing-image";
+  p.append(i);
+  return p;
+}
+
+async_test(t => {
+  let p = createPicture("resources/alpha.png", "resources/beta.png");
+  let i = p.lastChild;
+  i.onerror = t.unreached_func();
+  i.onload = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 100, "Images that are not disallowed should be displayed at their natural width.");
+    assert_equals(i.clientHeight, 100, "Images that are not disallowed should be displayed at their natural height.");
+  });
+  document.body.append(p);
+}, "Images whose selected source URL is not disallowed should still be displayed as normal.");
+
+async_test(t => {
+  let p = createPicture("resources/beta.png", "resources/alpha.png");
+  let i = p.lastChild;
+  i.onload = t.unreached_func();
+  i.onerror = t.step_func_done(_ => {
+    assert_equals(i.clientWidth, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.clientHeight, 0, "Images that are disallowed should be collapsed.");
+    assert_equals(i.naturalWidth, 0, "Images that are disallowed should not be loaded.");
+    assert_equals(i.naturalHeight, 0, "Images that are disallowed should not be loaded.");
+  });
+  document.body.append(p);
+}, "Images whose selected source URL is disallowed should not be loaded and should be collapsed in the layout.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/alpha.png b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/alpha.png
new file mode 100644
index 0000000..567babb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/alpha.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/beta.png b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/beta.png
new file mode 100644
index 0000000..567babb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/subresource_filter/resources/beta.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode-expected.txt
index e2a594f7..5ae8477 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode-expected.txt
+++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode-expected.txt
@@ -129,7 +129,7 @@
 PASS createElementNS non-HTML 
 PASS createProcessingInstruction 
 PASS implementation.createDocumentType 
-PASS implementation.createDocument 
+FAIL implementation.createDocument assert_equals: expected "about:blank" but got ""
 PASS implementation.createHTMLDocument 
 PASS node with children 
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode.html b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode.html
index 180dc4e..9f4f8fc 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/dom/nodes/Node-cloneNode.html
@@ -229,7 +229,16 @@
     var doc = document.implementation.createDocument(null, null);
     var copy = doc.cloneNode();
     check_copy(doc, copy, Document);
+    assert_equals(doc.charset, "UTF-8");
+    assert_equals(doc.charset, copy.charset);
+    assert_equals(doc.contentType, "application/xml");
     assert_equals(doc.contentType, copy.contentType);
+    assert_equals(doc.URL, "about:blank")
+    assert_equals(doc.URL, copy.URL);
+    assert_equals(doc.origin, "null")
+    assert_equals(doc.origin, copy.origin);
+    assert_equals(doc.compatMode, "CSS1Compat");
+    assert_equals(doc.compatMode, copy.compatMode);
 }, "implementation.createDocument");
 
 test(function() {
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/event-handler-onauxclick-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/event-handler-onauxclick-expected.txt
deleted file mode 100644
index 9a037eb..0000000
--- a/third_party/WebKit/LayoutTests/imported/wpt/html/webappapis/scripting/events/event-handler-onauxclick-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS onauxclick is on the appropriate locations for GlobalEventHandlers 
-PASS The default value of onauxclick is always null 
-FAIL The onauxclick content attribute must be compiled into the onauxclick property assert_equals: The onauxclick property must be a function expected "function" but got "object"
-FAIL The onauxclick content attribute must execute when an event is dispatched assert_true: Dispatching the event must run the code expected true got false
-PASS dispatching an auxclick event must trigger element.onauxclick 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/shadow-dom/slotchange-event.html b/third_party/WebKit/LayoutTests/imported/wpt/shadow-dom/slotchange-event.html
index ed36bedb..62675de 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/shadow-dom/slotchange-event.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/shadow-dom/slotchange-event.html
@@ -3,6 +3,7 @@
 <head>
 <title>Shadow DOM: slotchange event</title>
 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="author" title="Hayato Ito" href="mailto:hayato@google.com">
 <link rel="help" href="https://dom.spec.whatwg.org/#signaling-slot-change">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -10,8 +11,6 @@
 <body>
 <div id="log"></div>
 <script>
-// FIXME: Fix these test cases once https://github.com/w3c/webcomponents/issues/571 is resolved.
-
 function treeName(mode, connectedToDocument)
 {
     return (mode == 'open' ? 'an ' : 'a ') + mode + ' shadow root '
@@ -490,7 +489,6 @@
         outerShadow.appendChild(document.createElement('span'));
         outerSlot = document.createElement('slot');
         outerSlot.addEventListener('slotchange', function (event) {
-            event.stopPropagation();
             test.step(function () {
                 assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the slot element');
             });
@@ -507,7 +505,7 @@
         innerSlot.addEventListener('slotchange', function (event) {
             event.stopPropagation();
             test.step(function () {
-                assert_equals(event.target, innerSlot, 'slotchange event\'s target must be the slot element');
+                assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the outer slot element');
             });
             innerSlotEventCount++;
         });
@@ -521,10 +519,10 @@
 
     setTimeout(function () {
         test.step(function () {
-            assert_equals(innerSlotEventCount, 1,
-                'slotchange must be fired on a slot element if the assigned nodes changed');
             assert_equals(outerSlotEventCount, 1,
-                'slotchange must be fired on a slot element if the assigned nodes of an inner slot changed');
+                'slotchange must be fired on a slot element if the assigned nodes changed');
+            assert_equals(innerSlotEventCount, 1,
+                'slotchange must be fired on a slot element and must bubble');
         });
         test.done();
     }, 1);
@@ -540,70 +538,51 @@
     var test = async_test('slotchange event must fire at the end of current microtask after mutation observers are invoked inside '
         + treeName(mode, connectedToDocument) + ' when slots\'s contents change');
 
-    var outerHost;
-    var innerHost;
-    var outerSlot;
-    var innerSlot;
-    var slotchangeEvents = [];
+    var host;
+    var slot;
+    var eventCount = 0;
 
     test.step(function () {
-        outerHost = document.createElement('div');
+        host = document.createElement('div');
         if (connectedToDocument)
-            document.body.appendChild(outerHost);
+            document.body.appendChild(host);
 
-        var outerShadow = outerHost.attachShadow({'mode': mode});
-        outerShadow.appendChild(document.createElement('span'));
-        outerSlot = document.createElement('slot');
-        outerSlot.addEventListener('slotchange', function (event) {
-            event.stopPropagation();
+        var shadowRoot = host.attachShadow({'mode': mode});
+        slot = document.createElement('slot');
+
+        slot.addEventListener('slotchange', function (event) {
             test.step(function () {
-                assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the slot element');
+                assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
+                assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
             });
-            slotchangeEvents.push('outer');
+            eventCount++;
         });
 
-        innerHost = document.createElement('div');
-        innerHost.appendChild(outerSlot);
-        outerShadow.appendChild(innerHost);
-
-        var innerShadow = innerHost.attachShadow({'mode': mode});
-        innerShadow.appendChild(document.createElement('span'));
-        innerSlot = document.createElement('slot');
-        innerSlot.addEventListener('slotchange', function (event) {
-            event.stopPropagation();
-            test.step(function () {
-                assert_equals(event.target, innerSlot, 'slotchange event\'s target must be the slot element');
-            });
-            slotchangeEvents.push('inner');
-        });
-        innerShadow.appendChild(innerSlot);
-
-        outerHost.appendChild(document.createElement('span'));
-
-        assert_equals(slotchangeEvents.length, 0, 'slotchange event must not be fired synchronously');
+        shadowRoot.appendChild(slot);
     });
 
     var element = document.createElement('div');
 
     new MutationObserver(function () {
         test.step(function () {
-            assert_equals(slotchangeEvents.length, 0, 'slotchange event must not be fired before mutation records are delivered');
+            assert_equals(eventCount, 0, 'slotchange event must not be fired before mutation records are delivered');
         });
+        host.appendChild(document.createElement('span'));
         element.setAttribute('title', 'bar');
-        innerHost.appendChild(document.createElement('span'));
     }).observe(element, {attributes: true, attributeFilter: ['id']});
 
     new MutationObserver(function () {
         test.step(function () {
-            assert_array_equals(slotchangeEvents, ['outer', 'inner'], 'slotchange event must be fired during a single compound microtask');
+            assert_equals(eventCount, 1, 'slotchange event must be fired during a single compound microtask');
         });
     }).observe(element, {attributes: true, attributeFilter: ['title']});
 
     element.setAttribute('id', 'foo');
+    host.appendChild(document.createElement('div'));
 
     setTimeout(function () {
         test.step(function () {
-            assert_array_equals(slotchangeEvents, ['outer', 'inner', 'inner'],
+            assert_equals(eventCount, 2,
                 'a distinct slotchange event must be enqueued for changes made during a mutation observer delivery');
         });
         test.done();
@@ -614,7 +593,6 @@
 testSlotchangeFiresAtEndOfMicroTask('closed', true);
 testSlotchangeFiresAtEndOfMicroTask('open', false);
 testSlotchangeFiresAtEndOfMicroTask('closed', false);
-
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/webrtc/simplecall.html b/third_party/WebKit/LayoutTests/imported/wpt/webrtc/simplecall.html
index 94d5ea6..23f13d4 100644
--- a/third_party/WebKit/LayoutTests/imported/wpt/webrtc/simplecall.html
+++ b/third_party/WebKit/LayoutTests/imported/wpt/webrtc/simplecall.html
@@ -36,6 +36,14 @@
   var gFirstConnection = null;
   var gSecondConnection = null;
 
+  // if the remote video gets video data that implies the negotiation
+  // as well as the ICE and DTLS connection are up.
+  document.getElementById('remote-view')
+      .addEventListener('loadedmetadata', function() {
+    // Call negotiated: done.
+    test.done();
+  });
+
   function getUserMediaOkCallback(localStream) {
     gFirstConnection = new RTCPeerConnection(null);
     gFirstConnection.onicecandidate = onIceCandidateToFirst;
@@ -78,22 +86,15 @@
     var parsedAnswer = new RTCSessionDescription({ type: 'answer',
                                                    sdp: answerSdp });
     gFirstConnection.setRemoteDescription(parsedAnswer);
-
-    // Call negotiated: done.
-    test.done();
   };
 
   var onIceCandidateToFirst = test.step_func(function(event) {
     // If event.candidate is null = no more candidates.
-    if (event.candidate) {
-      gSecondConnection.addIceCandidate(event.candidate);
-    }
+    gSecondConnection.addIceCandidate(event.candidate);
   });
 
   var onIceCandidateToSecond = test.step_func(function(event) {
-    if (event.candidate) {
-      gFirstConnection.addIceCandidate(event.candidate);
-    }
+    gFirstConnection.addIceCandidate(event.candidate);
   });
 
   var onRemoteStream = test.step_func(function(event) {
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise-expected.txt
index 89e758e..a91057e6 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise-expected.txt
@@ -1,6 +1,3 @@
-CONSOLE ERROR: line 1: Uncaught (in promise) #<Object>
-CONSOLE ERROR: Uncaught (in promise) #<Object>
-CONSOLE ERROR: Uncaught (in promise) 239
 Tests that Runtime.awaitPromise works.
 
 Running test: testResolvedPromise
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise.html b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise.html
index fec2c2c2..7717a11 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-await-promise.html
@@ -30,6 +30,7 @@
         window.gc();
 }
 
+testRunner.setDumpConsoleMessages(false);
 function test()
 {
     InspectorTest.sendCommandPromise("Debugger.enable", {})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async-expected.txt
index 7216acd..755e1dc 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE ERROR: Uncaught (in promise) #<Object>
 Tests that Runtime.callFunctionOn works with awaitPromise flag.
 
 Running test: testArguments
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async.html b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async.html
index 700ab1a..f521765 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-callFunctionOn-async.html
@@ -3,6 +3,7 @@
 <script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script>
 <script>
 
+testRunner.setDumpConsoleMessages(false);
 function test()
 {
     InspectorTest.runTestSuite([
diff --git a/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid-expected.txt b/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid-expected.txt
index 75527c42..7684352 100644
--- a/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid-expected.txt
@@ -5,23 +5,11 @@
 Attached aab to aa
 Attached a to root
 Attached aa to a
-Expanded node a
-Checking flattenChildren():
-  a
-  aa
-
 Attached ab to a
-Checking flattenChildren():
-  a
-  aa
-  ab
-
 Attached b to root
 
 Tree:
  a
-  aa
-  ab
  b
 
 Expanded node a
@@ -106,23 +94,6 @@
  b <- selected
 
 Attached aac to ab
-Checking flattenChildren():
-  a
-  aa
-  aaa
-  aab
-  ab
-  aac
-  b
-
-Removed children of aa
-Checking flattenChildren():
-  a
-  aa
-  ab
-  aac
-  b
-
 
 Tree:
  a
@@ -132,20 +103,7 @@
  b
 
 Attached aac to ab
-Checking flattenChildren():
-  a
-  aa
-  ab
-  aac
-  b
-
 Removing aa from a
-Checking flattenChildren():
-  a
-  ab
-  aac
-  b
-
 
 Tree:
  a
diff --git a/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid.html b/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid.html
index e53225e..d31110b 100644
--- a/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid.html
+++ b/third_party/WebKit/LayoutTests/inspector/components/viewport-datagrid.html
@@ -42,16 +42,6 @@
         InspectorTest.addResult("Collapsed node " + node.data.id);
     }
 
-    function dumpFlattenChildren()
-    {
-        var nodes = dataGrid.rootNode().flattenChildren();
-        InspectorTest.addResult("Checking flattenChildren():");
-        for (var node of nodes) {
-            InspectorTest.addResult("  " + node.data.id);
-        }
-        InspectorTest.addResult("");
-    }
-
     var columns = [{id: "id", title: "ID column", width: "250px"}];
     var dataGrid = new UI.ViewportDataGrid(columns);
     var a = new UI.ViewportDataGridNode({id: "a"});
@@ -84,12 +74,8 @@
 
     // Appending to tree.
     attach(root, a);
-    // a is collapsed.
     attach(a, aa);
-    expand(a);
-    dumpFlattenChildren();
     attach(a, ab);
-    dumpFlattenChildren();
     attach(root, b);
 
     dumpNodes();
@@ -121,16 +107,11 @@
     dumpNodes();
     attach(ab, aac);
     aac.revealAndSelect();
-    dumpFlattenChildren();
-    InspectorTest.addResult("Removed children of aa");
     aa.removeChildren();
-    dumpFlattenChildren();
     dumpNodes();
     attach(ab, aac);
     aac.revealAndSelect();
-    dumpFlattenChildren();
     detach(a, aa);
-    dumpFlattenChildren();
     dumpNodes();
     detach(a, ab);
     dumpNodes();
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
index ad2c609..9bdbd35e 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
@@ -65,4 +65,10 @@
 Checking '// do'
 Not Found: document
 
+Checking '["should'
+Not Found: shouldNotFindThisFunction
+
+Checking 'shou'
+Not Found: should not find this
+
 
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
index b9ceea6..d2a9cec6 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
@@ -11,6 +11,10 @@
     };
 }
 
+function shouldNotFindThisFunction() { }
+
+window["should not find this"] = true;
+
 function test()
 {
     var consoleEditor;
@@ -62,7 +66,9 @@
         () => testCompletions('"string g', ["getComputedStyle"], false),
         () => testCompletions("`template string docu", ["document"], false),
         () => testCompletions("`${do", ["document"], false),
-        () => testCompletions("// do", ["document"], false)
+        () => testCompletions("// do", ["document"], false),
+        () => testCompletions('["should', ["shouldNotFindThisFunction"]),
+        () => testCompletions("shou", ["should not find this"])
     ]).then(InspectorTest.completeTest);
 
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-external-array-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-external-array-expected.txt
index 54a1694f..c02ce3d 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-external-array-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-external-array-expected.txt
@@ -16,14 +16,14 @@
 CONSOLE MESSAGE: line 25: [object Float64Array]
 Tests that console logging detects external arrays as arrays.
 
-console-external-array.html:9 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:10 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:11 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:12 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:13 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:14 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:15 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-console-external-array.html:16 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:9 Int8Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:10 Int16Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:11 Int32Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:12 Uint8Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:13 Uint16Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:14 Uint32Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:15 Float32Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+console-external-array.html:16 Float64Array(10) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 console-external-array.html:18 Int8Array(10)
 console-external-array.html:19 Int16Array(10)
 console-external-array.html:20 Int32Array(10)
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-format-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-format-expected.txt
index a04b23f..7210b77 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-format-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-format-expected.txt
@@ -259,10 +259,10 @@
 console-format.html:8 [String]
 globals[33]
 String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
-console-format.html:7 [1, 2, 3]
+console-format.html:7 Uint16Array(3) [1, 2, 3]
 console-format.html:8 [Uint16Array(3)]
 globals[34]
-[1, 2, 3]
+Uint16Array(3) [1, 2, 3]
 console-format.html:7 #text
 console-format.html:8 [text]
 globals[35]
@@ -271,11 +271,11 @@
 console-format.html:8 [DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of th…]
 globals[36]
 DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
-console-format.html:7 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…]
+console-format.html:7 Uint8Array(400) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…]
 console-format.html:8 [Uint8Array(400)]
 globals[37]
 Uint8Array(400)
-console-format.html:7 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…]
+console-format.html:7 Uint8Array(400000000) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…]
 console-format.html:8 [Uint8Array(400000000)]
 globals[38]
 Uint8Array(400000000)
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter-expected.txt
index 2ee602a..fab585a 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter-expected.txt
@@ -1,5 +1,9 @@
-CONSOLE MESSAGE: line 11: [object Object]
+CONSOLE MESSAGE: line 15: [object Object]
+CONSOLE MESSAGE: line 16: 1,
 Tests that console logging dumps object values defined by getters and allows to expand it.
 
-console-log-object-with-getter.html:11 Objectfoo: Objecta: 1b: 2__proto__: Objectset bar: function (x)get foo: function ()__proto__: Object
+console-log-object-with-getter.html:15 Object {}
+console-log-object-with-getter.html:16 []
+console-log-object-with-getter.html:15 Objectfoo: Objecta: 1b: 2__proto__: Objectset bar: function (x)get foo: function ()__proto__: Object
+console-log-object-with-getter.html:16 Array(2)0: 1length: 2get 0: function ()set 1: function (x)__proto__: Array(0)
 
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter.html b/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter.html
index e3f5922..4bc6d32f 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-log-object-with-getter.html
@@ -6,9 +6,14 @@
 var obj = {}
 Object.defineProperty(obj, 'foo', {enumerable: true, get: function() { return {a:1,b:2}; }});
 Object.defineProperty(obj, 'bar', {enumerable: false, set: function(x) { this.baz = x; }});
+
+var arr = [];
+Object.defineProperty(arr, 0, {enumerable: true, get: function() { return 1; }});
+Object.defineProperty(arr, 1, {enumerable: false, set: function(x) { this.baz = x; }});
 function logObject()
 {
     console.log(obj);
+    console.log(arr);
 }
 
 function test()
@@ -16,14 +21,19 @@
     InspectorTest.evaluateInPage("logObject()", step2);
     function step2()
     {
-        InspectorTest.expandConsoleMessages(step3);
+        InspectorTest.dumpConsoleMessages();
+        step3();
     }
     function step3()
     {
-        InspectorTest.expandGettersInConsoleMessages(step4);
+        InspectorTest.expandConsoleMessages(step4);
     }
     function step4()
     {
+        InspectorTest.expandGettersInConsoleMessages(step5);
+    }
+    function step5()
+    {
         InspectorTest.dumpConsoleMessages();
         InspectorTest.completeTest();
     }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/elements-panel-styles-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/elements-panel-styles-expected.txt
index a80a3e53..ece92d68 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/elements-panel-styles-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/elements-panel-styles-expected.txt
@@ -2,56 +2,56 @@
 
 Foo
 border-bottom-left-radius: 5px;
-    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 border-bottom-right-radius: 5px;
-    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 border-top-left-radius: 5px;
-    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 border-top-right-radius: 5px;
-    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    5px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 color: rgb(0, 0, 255);
-    blue - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:1
-    OVERLOADED black - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
-    OVERLOADED red - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:1
-    OVERLOADED magenta !important - html elements-panel-styles.css:61 -> elements-panel-styles.css:61:1
+    blue - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:21
+    OVERLOADED black - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
+    OVERLOADED red - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:13
+    OVERLOADED magenta !important - html elements-panel-styles.css:61 -> elements-panel-styles.css:61:7
 content: "[before Foo]";
-    "[before Foo]" - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:1
+    "[before Foo]" - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:21
 display: block;
     OVERLOADED none - element.style
-    block !important - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:1
+    block !important - .foo, .foo::before elements-panel-styles.css:34 -> elements-panel-styles.css:34:21
     OVERLOADED block - div user agent stylesheet
 font-family: serif;
-    serif - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:1
+    serif - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:13
 font-size: 14px;
-    14px - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:1
-    OVERLOADED 12px - body elements-panel-styles.css:1 -> elements-panel-styles.css:1:1
+    14px - #container elements-panel-styles.css:10 -> elements-panel-styles.css:10:13
+    OVERLOADED 12px - body elements-panel-styles.css:1 -> elements-panel-styles.css:1:7
 font-style: italic;
-    italic !important - #container .foo elements-panel-styles.css:17 -> elements-panel-styles.css:17:1
-    OVERLOADED normal !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    italic !important - #container .foo elements-panel-styles.css:17 -> elements-panel-styles.css:17:18
+    OVERLOADED normal !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 font-weight: normal;
-    normal !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    normal !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 margin-bottom: 2px;
-    2px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    2px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 margin-left: 0px;
-    1px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    1px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 margin-right: 0px;
-    0px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    0px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 margin-top: 10px;
-    10px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    10px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 padding-bottom: 4px;
-    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 padding-left: 4px;
-    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 padding-right: 1px;
-    1px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    1px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 padding-top: 4px;
-    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
+    4px - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
 text-align: -webkit-left;
     -webkit-left - element.style
 text-indent: 0px;
-    OVERLOADED 10px - body .foo elements-panel-styles.css:6 -> elements-panel-styles.css:6:1
-    0 !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:1
-    OVERLOADED 20px !important - body elements-panel-styles.css:1 -> elements-panel-styles.css:1:1
+    OVERLOADED 10px - body .foo elements-panel-styles.css:6 -> elements-panel-styles.css:6:12
+    0 !important - .foo elements-panel-styles.css:21 -> elements-panel-styles.css:21:7
+    OVERLOADED 20px !important - body elements-panel-styles.css:1 -> elements-panel-styles.css:1:7
 -webkit-font-smoothing: subpixel-antialiased;
     subpixel-antialiased - element.style
 [expanded] 
@@ -60,24 +60,24 @@
     -webkit-font-smoothing: subpixel-antialiased;
 
 [expanded] 
-#container .foo { (elements-panel-styles.css:17 -> elements-panel-styles.css:17:1)
+#container .foo { (elements-panel-styles.css:17 -> elements-panel-styles.css:17:18)
     font-style: italic !important;
 
 [expanded] 
-body .foo { (elements-panel-styles.css:6 -> elements-panel-styles.css:6:1)
+body .foo { (elements-panel-styles.css:6 -> elements-panel-styles.css:6:12)
 /-- overloaded --/     text-indent: 10px;
 
 [expanded] 
-.foo { (elements-panel-styles.css:53 -> elements-panel-styles.css:53:1)
+.foo { (elements-panel-styles.css:53 -> elements-panel-styles.css:53:7)
 
 [expanded] 
-.foo, .foo::before { (elements-panel-styles.css:34 -> elements-panel-styles.css:34:1)
+.foo, .foo::before { (elements-panel-styles.css:34 -> elements-panel-styles.css:34:21)
     content: "[before Foo]";
     color: blue;
     display: block !important;
 
 [expanded] 
-.foo { (elements-panel-styles.css:21 -> elements-panel-styles.css:21:1)
+.foo { (elements-panel-styles.css:21 -> elements-panel-styles.css:21:7)
 /-- overloaded --/     color: black;
     margin-left: 1px;
     margin: 10px 0 2px;
@@ -111,44 +111,44 @@
 
 ======== Inherited from div#container ========
 [expanded] 
-#container { (elements-panel-styles.css:10 -> elements-panel-styles.css:10:1)
+#container { (elements-panel-styles.css:10 -> elements-panel-styles.css:10:13)
     font-family: serif;
     font-size: 14px;
 /-- overloaded --/     color: red;
 
 ======== Inherited from body ========
 [expanded] 
-body { (elements-panel-styles.css:1 -> elements-panel-styles.css:1:1)
+body { (elements-panel-styles.css:1 -> elements-panel-styles.css:1:7)
 /-- overloaded --/     font-size: 12px;
 /-- overloaded --/     text-indent: 20px !important;
 
 ======== Inherited from html ========
 [expanded] 
-html { (elements-panel-styles.css:61 -> elements-panel-styles.css:61:1)
+html { (elements-panel-styles.css:61 -> elements-panel-styles.css:61:7)
 /-- overloaded --/     color: magenta !important;
 
 ======== Pseudo ::before element ========
 [expanded] 
-.foo::before { (elements-panel-styles.css:57 -> elements-panel-styles.css:57:1)
+.foo::before { (elements-panel-styles.css:57 -> elements-panel-styles.css:57:15)
 
 [expanded] 
-.foo::before { (elements-panel-styles.css:40 -> elements-panel-styles.css:40:1)
+.foo::before { (elements-panel-styles.css:40 -> elements-panel-styles.css:40:15)
     color: red;
 
 [expanded] 
-.foo, .foo::before { (elements-panel-styles.css:34 -> elements-panel-styles.css:34:7)
+.foo, .foo::before { (elements-panel-styles.css:34 -> elements-panel-styles.css:34:21)
     content: "[before Foo]";
 /-- overloaded --/     color: blue;
     display: block !important;
 
 ======== Pseudo ::after element ========
 [expanded] 
-.foo::after { (elements-panel-styles.css:48 -> elements-panel-styles.css:48:1)
+.foo::after { (elements-panel-styles.css:48 -> elements-panel-styles.css:48:14)
     font-family: courier;
     content: "[after Foo 2]";
 
 [expanded] 
-.foo::after { (elements-panel-styles.css:44 -> elements-panel-styles.css:44:1)
+.foo::after { (elements-panel-styles.css:44 -> elements-panel-styles.css:44:14)
 /-- overloaded --/     content: "[after Foo]";
     color: green;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/add-new-rule-with-style-after-body-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
index 5fadab0..56e9191 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/add-new-rule-with-style-after-body-expected.txt
@@ -7,7 +7,7 @@
     font-size: 12px;
 
 [expanded] [no-affect] 
-inspected { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+inspected { (inspector-stylesheet:1 -> inspector-stylesheet:1:12)
     color: maroon;
     : ;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/color-aware-property-value-edit-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/color-aware-property-value-edit-expected.txt
index 65d2e14..e46a3d5 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/color-aware-property-value-edit-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/color-aware-property-value-edit-expected.txt
@@ -30,5 +30,5 @@
 rgb(255, 255, 238)
 
 Running: editNewProperty
-hsl(120, 100%, 25%)
+alicebluehsl(120, 100%, 25%)
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-expected.txt
index c3c3b341..55aeab6 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-expected.txt
@@ -7,7 +7,7 @@
     color: red;
 
 [expanded] 
-#inspected { (commit-selector.html:4 -> commit-selector.html:4:1)
+#inspected { (commit-selector.html:4 -> commit-selector.html:4:13)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -20,7 +20,7 @@
     color: red;
 
 [expanded] 
-hr, #inspected { (commit-selector.html:4 -> commit-selector.html:4:5)
+hr, #inspected { (commit-selector.html:4 -> commit-selector.html:4:17)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -33,7 +33,7 @@
     color: red;
 
 [expanded] [no-affect] 
-#inspectedChanged { (commit-selector.html:4 -> commit-selector.html:4:1)
+#inspectedChanged { (commit-selector.html:4 -> commit-selector.html:4:20)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-mark-matching-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-mark-matching-expected.txt
index d9007b5..67d604080 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-mark-matching-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/commit-selector-mark-matching-expected.txt
@@ -8,7 +8,7 @@
 element.style { ()
 
 [expanded] 
-foo, [$#inspected, $].bar, [$#inspected$] { (inspector-stylesheet:1 -> inspector-stylesheet:1:6)
+foo, [$#inspected, $].bar, [$#inspected$] { (inspector-stylesheet:1 -> inspector-stylesheet:1:36)
 
 [expanded] 
 [$div$] { (user agent stylesheet)
@@ -20,7 +20,7 @@
 element.style { ()
 
 [expanded] 
-[$#inspected, $]a, hr { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+[$#inspected, $]a, hr { (inspector-stylesheet:1 -> inspector-stylesheet:1:20)
 
 [expanded] 
 [$div$] { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-live-edit-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-live-edit-expected.txt
index fba422af..a6f73e4b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-live-edit-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-live-edit-expected.txt
@@ -5,7 +5,7 @@
 display: block;
     block - div user agent stylesheet
 font-size: 20px;
-    20px - body css-live-edit.css:1 -> css-live-edit.css:1:1
+    20px - body css-live-edit.css:1 -> css-live-edit.css:1:7
 [expanded] 
 element.style { ()
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/cssom-media-insert-crash-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/cssom-media-insert-crash-expected.txt
index b6db668b..3d9e425 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/cssom-media-insert-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/cssom-media-insert-crash-expected.txt
@@ -9,7 +9,7 @@
     background: red;
 
 [expanded] 
-div { (cssom-media-ins…-crash.html:20 -> cssom-media-insert-crash.html:20:1)
+div { (cssom-media-ins…-crash.html:20 -> cssom-media-insert-crash.html:20:6)
     border: 1px solid black;
 /-- overloaded --/     background-color: white;
     padding: 20px;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/disable-property-workingcopy-update-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/disable-property-workingcopy-update-expected.txt
index 20a3693..085a900b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/disable-property-workingcopy-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/disable-property-workingcopy-update-expected.txt
@@ -24,7 +24,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (disable-propert…y-update.css:1 -> disable-property-workingcopy-update.css:1:1)
+#inspected { (disable-propert…y-update.css:1 -> disable-property-workingcopy-update.css:1:13)
 /-- overloaded --/ /-- disabled --/     /* font-weight: bold; */
 
 [expanded] 
@@ -45,7 +45,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (disable-propert…y-update.css:1 -> disable-property-workingcopy-update.css:1:1)
+#inspected { (disable-propert…y-update.css:1 -> disable-property-workingcopy-update.css:1:13)
     font-weight: bold;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/dynamic-style-tag-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/dynamic-style-tag-expected.txt
index 2222bc0..4aa0b93 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/dynamic-style-tag-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/dynamic-style-tag-expected.txt
@@ -58,7 +58,7 @@
     color: red;
 
 [expanded] 
-.inline-style-created-by-script-with-source-url { (inlineStyleCrea…ByScript.css:1 -> inlineStyleCreatedByScript.css:1:1)
+.inline-style-created-by-script-with-source-url { (inlineStyleCrea…ByScript.css:1 -> inlineStyleCreatedByScript.css:1:50)
 /-- overloaded --/     color: grey;
 
 [expanded] 
@@ -66,7 +66,7 @@
 /-- overloaded --/     color: orange;
 
 [expanded] 
-.inline-style-added-by-document-write-with-source-url { (inlineStyleAdde…entWrite.css:2 -> inlineStyleAddedByDocumentWrite.css:2:1)
+.inline-style-added-by-document-write-with-source-url { (inlineStyleAdde…entWrite.css:2 -> inlineStyleAddedByDocumentWrite.css:2:56)
 /-- overloaded --/     color: yellow;
 
 [expanded] 
@@ -74,11 +74,11 @@
 /-- overloaded --/     color: blue;
 
 [expanded] 
-.inline-style-added-by-parser-with-source-url { (inlineStyleAddedByParser.css:2 -> inlineStyleAddedByParser.css:2:1)
+.inline-style-added-by-parser-with-source-url { (inlineStyleAddedByParser.css:2 -> inlineStyleAddedByParser.css:2:48)
 /-- overloaded --/     color: green;
 
 [expanded] 
-.inline-style-added-by-parser { (dynamic-style-tag.html:6 -> dynamic-style-tag.html:6:14)
+.inline-style-added-by-parser { (dynamic-style-tag.html:6 -> dynamic-style-tag.html:6:45)
 /-- overloaded --/     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-inspector-stylesheet-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-inspector-stylesheet-expected.txt
index 22b5ef1..0579f500 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-inspector-stylesheet-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-inspector-stylesheet-expected.txt
@@ -12,7 +12,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+#inspected { (inspector-stylesheet:1 -> inspector-stylesheet:1:13)
     background-color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-media-text-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-media-text-expected.txt
index 2adf2f2..dfc4c355 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-media-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-media-text-expected.txt
@@ -8,12 +8,12 @@
 
 [expanded] 
 @media screen and (max-device-width: 100000px)
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:5)
+#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
 /-- overloaded --/     color: blue;
 
 [expanded] 
 @media screen and (max-device-width: 100000px)
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:5)
+#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -27,12 +27,12 @@
 
 [expanded] 
 @media screen and (max-device-width: 99999px)
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:5)
+#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
 /-- overloaded --/     color: blue;
 
 [expanded] 
 @media screen and (max-device-width: 99999px)
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:5)
+#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
 /-- overloaded --/     color: green;
 
 [expanded] 
@@ -45,11 +45,11 @@
     color: red;
 
 [expanded] 
-#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:5)
+#inspected { (edit-media-text.html:8 -> edit-media-text.html:8:17)
 /-- overloaded --/     color: blue;
 
 [expanded] 
-#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:5)
+#inspected { (edit-media-text.html:5 -> edit-media-text.html:5:17)
 /-- overloaded --/     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
index f25404e..d10a398 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/edit-resource-referred-by-multiple-styletags-expected.txt
@@ -5,15 +5,15 @@
 element.style { ()
 
 [expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:1)
+#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
     font-size: 2em;
 
 [expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:1)
+#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
 /-- overloaded --/     font-size: 2em;
 
 [expanded] 
-#inspected { (stylesheet.css:2 -> stylesheet.css:2:1)
+#inspected { (stylesheet.css:2 -> stylesheet.css:2:13)
 /-- overloaded --/     font-size: 2em;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/empty-background-url-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/empty-background-url-expected.txt
index e3a5c4c..22812eb 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/empty-background-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/empty-background-url-expected.txt
@@ -4,7 +4,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (empty-background-url.css:1 -> empty-background-url.css:1:1)
+#inspected { (empty-background-url.css:1 -> empty-background-url.css:1:13)
     background-image: url();
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/add-import-rule-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/add-import-rule-expected.txt
index 282273a4..27c79d9 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/add-import-rule-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/add-import-rule-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-span { (add-import-rule.html:4 -> add-import-rule.html:4:8)
+span { (add-import-rule.html:4 -> add-import-rule.html:4:14)
     color: red;
 
 
@@ -15,7 +15,7 @@
 element.style { ()
 
 [expanded] 
-span { (data:text/css,s…color:green}:1 -> data:text/css,span{color:green}:1:1)
+span { (data:text/css,s…color:green}:1 -> data:text/css,span{color:green}:1:6)
     color: green;
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/force-pseudo-state-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/force-pseudo-state-expected.txt
index 0721e85..a956dec 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/force-pseudo-state-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/force-pseudo-state-expected.txt
@@ -7,11 +7,11 @@
 element.style { ()
 
 [expanded] 
-div:active, a:active { (force-pseudo-state.html:12 -> force-pseudo-state.html:12:1)
+div:active, a:active { (force-pseudo-state.html:12 -> force-pseudo-state.html:12:23)
     font-weight: bold;
 
 [expanded] 
-div:hover, a:hover { (force-pseudo-state.html:4 -> force-pseudo-state.html:4:1)
+div:hover, a:hover { (force-pseudo-state.html:4 -> force-pseudo-state.html:4:21)
     color: red;
 
 [expanded] 
@@ -36,11 +36,11 @@
 element.style { ()
 
 [expanded] 
-div:active, a:active { (force-pseudo-state.html:12 -> force-pseudo-state.html:12:1)
+div:active, a:active { (force-pseudo-state.html:12 -> force-pseudo-state.html:12:23)
     font-weight: bold;
 
 [expanded] 
-div:focus, a:focus { (force-pseudo-state.html:8 -> force-pseudo-state.html:8:1)
+div:focus, a:focus { (force-pseudo-state.html:8 -> force-pseudo-state.html:8:21)
     border: 1px solid green;
         border-top-color: green;
         border-top-style: solid;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inactive-properties-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inactive-properties-expected.txt
index 25c336c..7161dcb 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inactive-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inactive-properties-expected.txt
@@ -4,13 +4,13 @@
 display: block;
     block - div user agent stylesheet
 text-align: right;
-    right - #inspected inactive-properties.html:20 -> inactive-properties.html:20:1
+    right - #inspected inactive-properties.html:20 -> inactive-properties.html:20:13
     OVERLOADED -webkit-left - element.style
 [expanded] 
 element.style { ()
 
 [expanded] 
-#inspected { (inactive-properties.html:20 -> inactive-properties.html:20:1)
+#inspected { (inactive-properties.html:20 -> inactive-properties.html:20:13)
 /-- overloaded --/     text-align: left;
 /-- overloaded --/     text-align: bar;
     text-align: right;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inherited-mixed-case-properties-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inherited-mixed-case-properties-expected.txt
index f1c8741..07032bd9 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inherited-mixed-case-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inherited-mixed-case-properties-expected.txt
@@ -17,7 +17,7 @@
     CoLoR: blAck;
 
 [expanded] 
-#container { (inherited-mixed…perties.html:4 -> inherited-mixed-case-properties.html:4:1)
+#container { (inherited-mixed…perties.html:4 -> inherited-mixed-case-properties.html:4:13)
     -webkit-FONT-smoothing: antialiased;
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inject-stylesheet-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inject-stylesheet-expected.txt
index bb3dd06..3522be7 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inject-stylesheet-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/inject-stylesheet-expected.txt
@@ -4,25 +4,25 @@
 
 Main frame style:
 background-attachment: scroll;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-clip: border-box;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-color: rgb(0, 0, 255);
-    blue - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    blue - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-image: none;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-origin: padding-box;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-position-x: 0%;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-position-y: 0%;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-repeat-x: ;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-repeat-y: ;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 background-size: auto;
-    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:3
+    initial - #main inject-stylesheet.html:5 -> inject-stylesheet.html:5:10
 border-bottom-style: solid;
     solid - #main injected stylesheet
 border-left-style: solid;
@@ -41,7 +41,7 @@
 element.style { ()
 
 [expanded] 
-#main { (inject-stylesheet.html:5 -> inject-stylesheet.html:5:3)
+#main { (inject-stylesheet.html:5 -> inject-stylesheet.html:5:10)
     background: blue;
         background-image: initial;
         background-position-x: initial;
@@ -71,34 +71,34 @@
 iframe style:
 background-attachment: scroll;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-clip: border-box;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-color: rgb(255, 0, 0);
     red - #iframeBody injected stylesheet
-    OVERLOADED green - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED green - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-image: none;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-origin: padding-box;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-position-x: 0%;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-position-y: 0%;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-repeat-x: ;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-repeat-y: ;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 background-size: auto;
     initial - #iframeBody injected stylesheet
-    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3
+    OVERLOADED initial - body inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9
 display: block;
     block - body user agent stylesheet
 margin-bottom: 8px;
@@ -127,7 +127,7 @@
         background-color: red;
 
 [expanded] 
-body { (inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:3)
+body { (inject-styleshe…me-data.html:4 -> inject-stylesheet-iframe-data.html:4:9)
 /-- overloaded --/     background: green;
     /-- overloaded --/     background-image: initial;
     /-- overloaded --/     background-position-x: initial;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/keyframes-rules-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/keyframes-rules-expected.txt
index 989f53ef..d6ee1d59 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/keyframes-rules-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/keyframes-rules-expected.txt
@@ -10,7 +10,7 @@
     background-color: white;
 
 [expanded] 
-* { (keyframes-rules.html:9 -> keyframes-rules.html:9:1)
+* { (keyframes-rules.html:9 -> keyframes-rules.html:9:4)
 /-- overloaded --/     background-color: papayawhip;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/lazy-computed-style-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/lazy-computed-style-expected.txt
index 26b032b..a816387 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/lazy-computed-style-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/lazy-computed-style-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (lazy-computed-style.html:15 -> lazy-computed-style.html:15:1)
+#inspected { (lazy-computed-style.html:15 -> lazy-computed-style.html:15:13)
     background: gray;
         background-image: initial;
         background-position-x: initial;
@@ -19,12 +19,12 @@
         background-color: gray;
 
 [expanded] 
-#inspected { (lazy-computed-style.html:10 -> lazy-computed-style.html:10:1)
+#inspected { (lazy-computed-style.html:10 -> lazy-computed-style.html:10:13)
 /-- overloaded --/     background-color: black;
     font-family: Courier;
 
 [expanded] 
-#inspected { (lazy-computed-style.html:5 -> lazy-computed-style.html:5:1)
+#inspected { (lazy-computed-style.html:5 -> lazy-computed-style.html:5:13)
 /-- overloaded --/     background-color: green;
 /-- overloaded --/     font-family: Times;
 
@@ -34,37 +34,37 @@
 
 ==== All styles (computed should be there) ====
 background-attachment: scroll;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-clip: border-box;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-color: rgb(128, 128, 128);
-    gray - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
-    OVERLOADED black - #inspected lazy-computed-style.html:10 -> lazy-computed-style.html:10:1
-    OVERLOADED green - #inspected lazy-computed-style.html:5 -> lazy-computed-style.html:5:1
+    gray - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
+    OVERLOADED black - #inspected lazy-computed-style.html:10 -> lazy-computed-style.html:10:13
+    OVERLOADED green - #inspected lazy-computed-style.html:5 -> lazy-computed-style.html:5:13
 background-image: none;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-origin: padding-box;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-position-x: 0%;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-position-y: 0%;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-repeat-x: ;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-repeat-y: ;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 background-size: auto;
-    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:1
+    initial - #inspected lazy-computed-style.html:15 -> lazy-computed-style.html:15:13
 display: block;
     block - div user agent stylesheet
 font-family: Courier;
-    Courier - #inspected lazy-computed-style.html:10 -> lazy-computed-style.html:10:1
-    OVERLOADED Times - #inspected lazy-computed-style.html:5 -> lazy-computed-style.html:5:1
+    Courier - #inspected lazy-computed-style.html:10 -> lazy-computed-style.html:10:13
+    OVERLOADED Times - #inspected lazy-computed-style.html:5 -> lazy-computed-style.html:5:13
 [expanded] 
 element.style { ()
 
 [expanded] 
-#inspected { (lazy-computed-style.html:15 -> lazy-computed-style.html:15:1)
+#inspected { (lazy-computed-style.html:15 -> lazy-computed-style.html:15:13)
     background: gray;
         background-image: initial;
         background-position-x: initial;
@@ -78,12 +78,12 @@
         background-color: gray;
 
 [expanded] 
-#inspected { (lazy-computed-style.html:10 -> lazy-computed-style.html:10:1)
+#inspected { (lazy-computed-style.html:10 -> lazy-computed-style.html:10:13)
 /-- overloaded --/     background-color: black;
     font-family: Courier;
 
 [expanded] 
-#inspected { (lazy-computed-style.html:5 -> lazy-computed-style.html:5:1)
+#inspected { (lazy-computed-style.html:5 -> lazy-computed-style.html:5:13)
 /-- overloaded --/     background-color: green;
 /-- overloaded --/     font-family: Times;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-emulation-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-emulation-expected.txt
index 3129e9e6..6fbe015 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-emulation-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-emulation-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#main { (media-emulation.html:5 -> media-emulation.html:5:1)
+#main { (media-emulation.html:5 -> media-emulation.html:5:8)
     color: red;
 
 [expanded] 
@@ -18,11 +18,11 @@
 
 [expanded] 
 @media print
-#main { (media-emulation.html:8 -> media-emulation.html:8:1)
+#main { (media-emulation.html:8 -> media-emulation.html:8:8)
     color: black;
 
 [expanded] 
-#main { (media-emulation.html:5 -> media-emulation.html:5:1)
+#main { (media-emulation.html:5 -> media-emulation.html:5:8)
 /-- overloaded --/     color: red;
 
 [expanded] 
@@ -34,11 +34,11 @@
 element.style { ()
 
 [expanded] 
-#main { (media-emulation.html:12 -> media-emulation.html:12:1)
+#main { (media-emulation.html:12 -> media-emulation.html:12:8)
     color: green;
 
 [expanded] 
-#main { (media-emulation.html:5 -> media-emulation.html:5:1)
+#main { (media-emulation.html:5 -> media-emulation.html:5:8)
 /-- overloaded --/     color: red;
 
 [expanded] 
@@ -50,7 +50,7 @@
 element.style { ()
 
 [expanded] 
-#main { (media-emulation.html:5 -> media-emulation.html:5:1)
+#main { (media-emulation.html:5 -> media-emulation.html:5:8)
     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-queries-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-queries-expected.txt
index 64de80ad..7773ad94b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-queries-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-queries-expected.txt
@@ -5,15 +5,15 @@
 element.style { ()
 
 [expanded] 
-#main { (media-queries.css:6 -> media-queries.css:6:7)
+#main { (media-queries.css:6 -> media-queries.css:6:14)
     color: yellow;
 
 [expanded] 
-#main { (media-queries-1.css:6 -> media-queries-1.css:6:5)
+#main { (media-queries-1.css:6 -> media-queries-1.css:6:12)
     border: 1px solid black;
 
 [expanded] 
-#main { (media-queries.html:6 -> media-queries.html:6:5)
+#main { (media-queries.html:6 -> media-queries.html:6:12)
     background: blue;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-using-same-url-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-using-same-url-expected.txt
index 56b5ef22..02ce35b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-using-same-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/media-using-same-url-expected.txt
@@ -5,11 +5,11 @@
 element.style { ()
 
 [expanded] 
-#main { (media-using-same-url.html:14 -> media-using-same-url.html:14:1)
+#main { (media-using-same-url.html:14 -> media-using-same-url.html:14:8)
     color: white;
 
 [expanded] 
-#main { (media-using-same-url.html:6 -> media-using-same-url.html:6:1)
+#main { (media-using-same-url.html:6 -> media-using-same-url.html:6:8)
     background: blue;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-comments-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-comments-expected.txt
index d9c1848..5c062a3 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-comments-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-comments-expected.txt
@@ -5,13 +5,13 @@
 element.style { ()
 
 [expanded] 
-#main { (parse-comments.html:25 -> parse-comments.html:25:1)
+#main { (parse-comments.html:25 -> parse-comments.html:25:24)
 /-- overloaded --/ /-- disabled --/     /* color: red; */
     color/* color: red */:/* color: red */ green/* color: red */;
 /-- overloaded --/ /-- disabled --/     /* color: red; */
 
 [expanded] 
-#main { (parse-comments.html:9 -> parse-comments.html:9:21)
+#main { (parse-comments.html:9 -> parse-comments.html:9:43)
 /-- overloaded --/ /-- disabled --/     /* color: red; */
     background /* color: red */ :/* color: red */blue/* color: red */;
 /-- overloaded --/ /-- disabled --/     /* color: red; */
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-utf8-bom-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-utf8-bom-expected.txt
index 31844c68..93ef35b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-utf8-bom-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/parse-utf8-bom-expected.txt
@@ -6,7 +6,7 @@
 element.style { ()
 
 [expanded] 
-h1 { (parse-utf8-bom-main.css:3 -> parse-utf8-bom-main.css:3:1)
+h1 { (parse-utf8-bom-main.css:3 -> parse-utf8-bom-main.css:3:5)
     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/perform-undo-perform-of-mergable-action-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/perform-undo-perform-of-mergable-action-expected.txt
index 1d3d2ef..f326058 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/perform-undo-perform-of-mergable-action-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/perform-undo-perform-of-mergable-action-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:1)
+.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -17,7 +17,7 @@
 element.style { ()
 
 [expanded] 
-.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:1)
+.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:13)
     font-weight: normal;
 
 [expanded] 
@@ -29,7 +29,7 @@
 element.style { ()
 
 [expanded] 
-.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:1)
+.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -41,7 +41,7 @@
 element.style { ()
 
 [expanded] 
-.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:1)
+.container { (perform-undo-pe…-action.html:7 -> perform-undo-perform-of-mergable-action.html:7:13)
     font-weight: normal;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/pseudo-elements-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/pseudo-elements-expected.txt
index 54ed128..4fd3308 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/pseudo-elements-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/pseudo-elements-expected.txt
@@ -25,24 +25,24 @@
 
 ======== Pseudo ::before element ========
 [expanded] 
-[$#inspected:before, $].some-other-selector { (pseudo-elements.html:4 -> pseudo-elements.html:4:1)
+[$#inspected:before, $].some-other-selector { (pseudo-elements.html:4 -> pseudo-elements.html:4:42)
     content: "BEFORE";
 
 ======== Pseudo ::after element ========
 [expanded] 
-[$#inspected:after$] { (pseudo-elements.html:8 -> pseudo-elements.html:8:1)
+[$#inspected:after$] { (pseudo-elements.html:8 -> pseudo-elements.html:8:19)
     content: "AFTER";
 
 
 Running: dumpBeforeStyles
 [expanded] 
-[$#inspected:before, $].some-other-selector { (pseudo-elements.html:4 -> pseudo-elements.html:4:1)
+[$#inspected:before, $].some-other-selector { (pseudo-elements.html:4 -> pseudo-elements.html:4:42)
     content: "BEFORE";
 
 
 Running: dumpAfterStyles
 [expanded] 
-[$#inspected:after$] { (pseudo-elements.html:8 -> pseudo-elements.html:8:1)
+[$#inspected:after$] { (pseudo-elements.html:8 -> pseudo-elements.html:8:19)
     content: "AFTER";
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/region-style-crash-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/region-style-crash-expected.txt
index 7917e71e..b93d75b 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-2/region-style-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-2/region-style-crash-expected.txt
@@ -3,7 +3,7 @@
 P color styled in region: #008000.
 
 color: rgb(255, 0, 0);
-    #ff0000 - #p1 region-style-crash.html:6 -> region-style-crash.html:6:1
+    #ff0000 - #p1 region-style-crash.html:6 -> region-style-crash.html:6:6
 display: block;
     block - p user agent stylesheet
 -webkit-margin-after: 16px;
@@ -18,7 +18,7 @@
 element.style { ()
 
 [expanded] 
-#p1 { (region-style-crash.html:6 -> region-style-crash.html:6:1)
+#p1 { (region-style-crash.html:6 -> region-style-crash.html:6:6)
     color: #ff0000;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/computed-properties-retain-expanded-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/computed-properties-retain-expanded-expected.txt
index f25c772..40bd6596 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/computed-properties-retain-expanded-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/computed-properties-retain-expanded-expected.txt
@@ -6,14 +6,14 @@
 #inspected computed styles: 
 color: rgb(0, 0, 255);
 display: flex;
-    flex - #inspected computed-proper…xpanded.html:5 -> computed-properties-retain-expanded.html:5:1
-    OVERLOADED block - div computed-proper…panded.html:14 -> computed-properties-retain-expanded.html:14:1
+    flex - #inspected computed-proper…xpanded.html:5 -> computed-properties-retain-expanded.html:5:13
+    OVERLOADED block - div computed-proper…panded.html:14 -> computed-properties-retain-expanded.html:14:6
     OVERLOADED block - div user agent stylesheet
 
 #other computed styles: 
 color: rgb(0, 0, 0);
 display: inline;
-    inline - #other computed-proper…panded.html:10 -> computed-properties-retain-expanded.html:10:1
-    OVERLOADED block - div computed-proper…panded.html:14 -> computed-properties-retain-expanded.html:14:1
+    inline - #other computed-proper…panded.html:10 -> computed-properties-retain-expanded.html:10:9
+    OVERLOADED block - div computed-proper…panded.html:14 -> computed-properties-retain-expanded.html:14:6
     OVERLOADED block - div user agent stylesheet
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/selector-list-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/selector-list-expected.txt
index 5f4aa1a3..148238e 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/selector-list-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/selector-list-expected.txt
@@ -5,17 +5,17 @@
 element.style { ()
 
 [expanded] 
-FOO bAr, #inspected, MOO>BAR, htML div, Foo~Moo, MOO { (selector-list.html:14 -> selector-list.html:14:48)
+FOO bAr, #inspected, MOO>BAR, htML div, Foo~Moo, MOO { (selector-list.html:19 -> selector-list.html:19:2)
     color: green;
 
 [expanded] 
-#inspected { (selector-list.html:10 -> selector-list.html:10:6)
+#inspected { (selector-list.html:10 -> selector-list.html:10:23)
 
 [expanded] 
-#InSpEcTeD { (selector-list.html:7 -> selector-list.html:7:1)
+#InSpEcTeD { (selector-list.html:7 -> selector-list.html:7:13)
 
 [expanded] 
-#inspected { (selector-list.html:4 -> selector-list.html:4:1)
+#inspected { (selector-list.html:4 -> selector-list.html:4:12)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/style-rule-from-imported-stylesheet-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/style-rule-from-imported-stylesheet-expected.txt
index c5236173..63d4f00 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/style-rule-from-imported-stylesheet-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/style-rule-from-imported-stylesheet-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-.square { (imported-stylesheet.css:1 -> imported-stylesheet.css:1:1)
+.square { (imported-stylesheet.css:2 -> imported-stylesheet.css:2:2)
     background-color: red;
     display: inline-block;
 
@@ -18,7 +18,7 @@
 element.style { ()
 
 [expanded] 
-.square { (imported-stylesheet.css:1 -> imported-stylesheet.css:1:1)
+.square { (imported-stylesheet.css:2 -> imported-stylesheet.css:2:2)
 /-- overloaded --/ /-- disabled --/     /* background-color: red; */
     display: inline-block;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-colon-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-colon-expected.txt
index 3b3327f..6efedc8 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-colon-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-colon-expected.txt
@@ -7,7 +7,7 @@
     font-size: 12px;
 
 [expanded] 
-foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:6)
+foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:26)
     color: maroon;
 
 [expanded] 
@@ -20,7 +20,7 @@
     color: red;
 
 [expanded] 
-div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:1)
+div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:12)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-expected.txt
index d74661d..941bb41 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-expected.txt
@@ -7,7 +7,7 @@
     font-size: 12px;
 
 [expanded] 
-foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:6)
+foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:26)
     color: maroon;
 
 [expanded] 
@@ -20,7 +20,7 @@
     color: red;
 
 [expanded] 
-div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:1)
+div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:12)
 
 [expanded] 
 div { (user agent stylesheet)
@@ -30,7 +30,7 @@
 element.style { ()
 
 [expanded] 
-.my-class { (inspector-stylesheet:7 -> inspector-stylesheet:7:1)
+.my-class { (inspector-stylesheet:7 -> inspector-stylesheet:7:12)
 
 [expanded] 
 div { (user agent stylesheet)
@@ -40,7 +40,7 @@
 element.style { ()
 
 [expanded] 
-.class-1.class-2.class-3 { (inspector-stylesheet:9 -> inspector-stylesheet:9:1)
+.class-1.class-2.class-3 { (inspector-stylesheet:9 -> inspector-stylesheet:9:27)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-tab-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-tab-expected.txt
index 3b3327f..6efedc8 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-tab-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-tab-expected.txt
@@ -7,7 +7,7 @@
     font-size: 12px;
 
 [expanded] 
-foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:6)
+foo, [$div#inspected, $]bar { (inspector-stylesheet:1 -> inspector-stylesheet:1:26)
     color: maroon;
 
 [expanded] 
@@ -20,7 +20,7 @@
     color: red;
 
 [expanded] 
-div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:1)
+div#other { (inspector-stylesheet:5 -> inspector-stylesheet:5:12)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-to-stylesheet-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-to-stylesheet-expected.txt
index f2891b1..d8b3fa3 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-to-stylesheet-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-add-new-rule-to-stylesheet-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#other, div { (different-rule-types.css:48 -> different-rule-types.css:48:1)
+#other, div { (different-rule-types.css:48 -> different-rule-types.css:48:14)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-computed-trace-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-computed-trace-expected.txt
index 02ae4786..558dc55 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-computed-trace-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-computed-trace-expected.txt
@@ -2,58 +2,58 @@
 
 ==== Computed style for ID1 ====
 background-attachment: scroll;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-clip: border-box;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-color: rgb(128, 128, 128);
-    gray - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
-    OVERLOADED black - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:1
-    OVERLOADED green - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:1
+    gray - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
+    OVERLOADED black - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:7
+    OVERLOADED green - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:7
 background-image: none;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-origin: padding-box;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-position-x: 0%;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-position-y: 0%;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-repeat-x: ;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-repeat-y: ;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 background-size: auto;
-    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:1
+    initial - #id1 styles-computed-trace.html:23 -> styles-computed-trace.html:23:7
 display: block;
     block - div user agent stylesheet
 font-family: Courier;
-    Courier - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:1
-    OVERLOADED Times - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:1
-    OVERLOADED Arial - body styles-computed-trace.html:4 -> styles-computed-trace.html:4:1
+    Courier - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:7
+    OVERLOADED Times - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:7
+    OVERLOADED Arial - body styles-computed-trace.html:4 -> styles-computed-trace.html:4:7
 text-decoration-color: rgb(0, 0, 0);
-    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 text-decoration-line: underline;
-    underline - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    underline - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 text-decoration-style: solid;
-    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 [expanded] 
 element.style { ()
 
 ==== Computed style for ID2 ====
 background-color: rgb(0, 0, 255);
-    blue - #id2 styles-computed-trace.html:27 -> styles-computed-trace.html:27:1
+    blue - #id2 styles-computed-trace.html:27 -> styles-computed-trace.html:27:7
 display: block;
     block - div user agent stylesheet
 font-family: Courier;
-    Courier - #id2 styles-computed-trace.html:27 -> styles-computed-trace.html:27:1
-    OVERLOADED Courier - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:1
-    OVERLOADED Times - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:1
-    OVERLOADED Arial - body styles-computed-trace.html:4 -> styles-computed-trace.html:4:1
+    Courier - #id2 styles-computed-trace.html:27 -> styles-computed-trace.html:27:7
+    OVERLOADED Courier - #id1 styles-computed-trace.html:18 -> styles-computed-trace.html:18:7
+    OVERLOADED Times - #id1 styles-computed-trace.html:13 -> styles-computed-trace.html:13:7
+    OVERLOADED Arial - body styles-computed-trace.html:4 -> styles-computed-trace.html:4:7
 text-decoration-color: rgb(0, 0, 0);
-    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 text-decoration-line: underline;
-    underline - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    underline - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 text-decoration-style: solid;
-    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:1
+    initial - div styles-computed-trace.html:9 -> styles-computed-trace.html:9:6
 [expanded] 
 element.style { ()
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-disable-property-after-selector-edit-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-disable-property-after-selector-edit-expected.txt
index f03e736..3f6d431 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-disable-property-after-selector-edit-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-disable-property-after-selector-edit-expected.txt
@@ -16,7 +16,7 @@
 element.style { ()
 
 [expanded] 
-#inspected, .INSERTED-OTHER-SELECTOR { (styles-disable-…r-edit.html:41 -> styles-disable-property-after-selector-edit.html:41:1)
+#inspected, .INSERTED-OTHER-SELECTOR { (styles-disable-…r-edit.html:41 -> styles-disable-property-after-selector-edit.html:41:39)
 /-- overloaded --/ /-- disabled --/     /* color: red; */
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-variables-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-variables-expected.txt
index 845933d..4fb6516 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-variables-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-3/styles-variables-expected.txt
@@ -2,14 +2,14 @@
 
 ==== Computed style for ID1 ====
 --b:  44px;
-    44px - #id1 styles-variables.html:8 -> styles-variables.html:8:1
+    44px - #id1 styles-variables.html:8 -> styles-variables.html:8:7
 display: block;
     block - div user agent stylesheet
 [expanded] 
 element.style { ()
 
 [expanded] 
-#id1 { (styles-variables.html:8 -> styles-variables.html:8:1)
+#id1 { (styles-variables.html:8 -> styles-variables.html:8:7)
     --b: 44px;
 
 [expanded] 
@@ -19,14 +19,14 @@
 value of --a:  red
 ==== Computed style for ID2 ====
 --a:  green;
-    green - #id2 styles-variables.html:12 -> styles-variables.html:12:1
+    green - #id2 styles-variables.html:12 -> styles-variables.html:12:7
 display: block;
     block - div user agent stylesheet
 [expanded] 
 element.style { ()
 
 [expanded] 
-#id2 { (styles-variables.html:12 -> styles-variables.html:12:1)
+#id2 { (styles-variables.html:12 -> styles-variables.html:12:7)
     --a: green;
 
 [expanded] 
@@ -36,14 +36,14 @@
 value of --b:  44px
 ==== Computed style for ID3 ====
 --a:  green;
-    inherit - #id3 styles-variables.html:16 -> styles-variables.html:16:1
+    inherit - #id3 styles-variables.html:16 -> styles-variables.html:16:7
 display: block;
     block - div user agent stylesheet
 [expanded] 
 element.style { ()
 
 [expanded] 
-#id3 { (styles-variables.html:16 -> styles-variables.html:16:1)
+#id3 { (styles-variables.html:16 -> styles-variables.html:16:7)
     --a: inherit;
 
 [expanded] 
@@ -58,7 +58,7 @@
 element.style { ()
 
 [expanded] 
-#id4 { (styles-variables.html:20 -> styles-variables.html:20:1)
+#id4 { (styles-variables.html:20 -> styles-variables.html:20:7)
     --a: var(--z);
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/inline-style-sourcemap-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/inline-style-sourcemap-expected.txt
index b92608c..024098c 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/inline-style-sourcemap-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/inline-style-sourcemap-expected.txt
@@ -6,11 +6,11 @@
 element.style { ()
 
 [expanded] 
-body { (test.less:5 -> test.less:5:1)
+body { (test.less:7 -> test.less:7:3)
     background-color: red;
 
 [expanded] 
-.red, body { (test.less:5 -> test.less:5:1)
+.red, body { (mixin.less:2 -> mixin.less:2:3)
     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-do-not-detach-sourcemap-on-edits-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-do-not-detach-sourcemap-on-edits-expected.txt
index c0e5b1e..7fcdb18 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-do-not-detach-sourcemap-on-edits-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-do-not-detach-sourcemap-on-edits-expected.txt
@@ -8,7 +8,7 @@
 
 [expanded] 
 @media (min-width: 6px)
-#container { (styles-do-not-d…n-edits.scss:2 -> styles-do-not-detach-sourcemap-on-edits.scss:2:5)
+#container { (styles-do-not-d…n-edits.scss:3 -> styles-do-not-detach-sourcemap-on-edits.scss:3:9)
     border: 1px solid blue;
     color: blue;
 
@@ -23,7 +23,7 @@
 
 [expanded] 
 @media (min-width: 6px)
-#container { (styles-do-not-d…n-edits.scss:2 -> styles-do-not-detach-sourcemap-on-edits.scss:2:5)
+#container { (styles-do-not-d…n-edits.scss:3 -> styles-do-not-detach-sourcemap-on-edits.scss:3:9)
     border: 1px solid blue;
 /-- overloaded --/     NAME: VALUE;
 
@@ -38,7 +38,7 @@
 
 [expanded] 
 @media (min-width: 6px)
-#container, SELECTOR { (styles-do-not-d…n-edits.scss:2 -> styles-do-not-detach-sourcemap-on-edits.scss:2:5)
+#container, SELECTOR { (styles-do-not-d…n-edits.scss:3 -> styles-do-not-detach-sourcemap-on-edits.scss:3:17)
     border: 1px solid blue;
 /-- overloaded --/     NAME: VALUE;
 
@@ -53,7 +53,7 @@
 
 [expanded] 
 @media (max-width: 9999999px)
-#container, SELECTOR { (styles-do-not-d…n-edits.scss:2 -> styles-do-not-detach-sourcemap-on-edits.scss:2:5)
+#container, SELECTOR { (styles-do-not-d…n-edits.scss:3 -> styles-do-not-detach-sourcemap-on-edits.scss:3:17)
     border: 1px solid blue;
 /-- overloaded --/     NAME: VALUE;
 
@@ -71,7 +71,7 @@
 
 [expanded] 
 @media (max-width: 9999999px)
-#container, SELECTOR { (styles-do-not-d…n-edits.scss:2 -> styles-do-not-detach-sourcemap-on-edits.scss:2:5)
+#container, SELECTOR { (styles-do-not-d…n-edits.scss:3 -> styles-do-not-detach-sourcemap-on-edits.scss:3:17)
     border: 1px solid blue;
 /-- overloaded --/     NAME: VALUE;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-edit-property-after-invalid-rule-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-edit-property-after-invalid-rule-expected.txt
index e3cc99f..47faa4ab 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-edit-property-after-invalid-rule-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-edit-property-after-invalid-rule-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#container { (styles-edit-pro…d-rule.html:10 -> styles-edit-property-after-invalid-rule.html:10:1)
+#container { (styles-edit-pro…d-rule.html:10 -> styles-edit-property-after-invalid-rule.html:10:13)
     padding: 15px;
 
 [expanded] 
@@ -17,7 +17,7 @@
 element.style { ()
 
 [expanded] 
-#container { (styles-edit-pro…d-rule.html:10 -> styles-edit-property-after-invalid-rule.html:10:1)
+#container { (styles-edit-pro…d-rule.html:10 -> styles-edit-property-after-invalid-rule.html:10:13)
     padding: 20px;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-iframe-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-iframe-expected.txt
index 9db05c5..c97575a 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-iframe-expected.txt
@@ -7,7 +7,7 @@
 element.style { ()
 
 [expanded] 
-#main { (styles-iframe.html:2 -> styles-iframe.html:2:3)
+#main { (styles-iframe.html:2 -> styles-iframe.html:2:10)
     background: blue;
 
 [expanded] 
@@ -19,7 +19,7 @@
 element.style { ()
 
 [expanded] 
-body { (styles-iframe-data.html:4 -> styles-iframe-data.html:4:3)
+body { (styles-iframe-data.html:4 -> styles-iframe-data.html:4:9)
     background: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-keyframes-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-keyframes-expected.txt
index b9d47e3..9185941 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-keyframes-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-keyframes-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#element { (keyframes.css:1 -> keyframes.css:1:1)
+#element { (keyframes.css:1 -> keyframes.css:1:11)
     animation: animName 1s 2s, mediaAnim 2s, doesNotExist 3s, styleSheetAnim 0s;
         animation-name: animName, mediaAnim, doesNotExist, styleSheetAnim;
         animation-duration: 1s, 2s, 3s, 0s;
@@ -57,7 +57,7 @@
 element.style { ()
 
 [expanded] 
-#element { (keyframes.css:1 -> keyframes.css:1:1)
+#element { (keyframes.css:1 -> keyframes.css:1:11)
     animation: animName 1s 2s, mediaAnim 2s, doesNotExist 3s, styleSheetAnim 0s;
         animation-name: animName, mediaAnim, doesNotExist, styleSheetAnim;
         animation-duration: 1s, 2s, 3s, 0s;
@@ -109,7 +109,7 @@
 element.style { ()
 
 [expanded] 
-#element { (keyframes.css:1 -> keyframes.css:1:1)
+#element { (keyframes.css:1 -> keyframes.css:1:11)
     animation: animName 1s 2s, mediaAnim 2s, doesNotExist 3s, styleSheetAnim 0s;
         animation-name: animName, mediaAnim, doesNotExist, styleSheetAnim;
         animation-duration: 1s, 2s, 3s, 0s;
@@ -161,7 +161,7 @@
 element.style { ()
 
 [expanded] 
-#element { (keyframes.css:1 -> keyframes.css:1:1)
+#element { (keyframes.css:1 -> keyframes.css:1:11)
     animation: animName 1s 2s, mediaAnim 2s, doesNotExist 3s, styleSheetAnim 0s;
         animation-name: animName, mediaAnim, doesNotExist, styleSheetAnim;
         animation-duration: 1s, 2s, 3s, 0s;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-overriden-properties-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-overriden-properties-expected.txt
index bf7c0d8..004b7357 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-overriden-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-overriden-properties-expected.txt
@@ -4,7 +4,7 @@
 element.style { ()
 
 [expanded] 
-#main { (styles-override…perties.html:4 -> styles-overriden-properties.html:4:1)
+#main { (styles-override…perties.html:4 -> styles-overriden-properties.html:4:8)
 /-- overloaded --/     background: #000;
     background: #bada55;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-properties-overload-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-properties-overload-expected.txt
index a0b30d4..95c2801 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-properties-overload-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-properties-overload-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#inspect { (styles-properti…erload.html:11 -> styles-properties-overload.html:11:1)
+#inspect { (styles-properti…erload.html:11 -> styles-properties-overload.html:11:11)
     margin-top: 1px;
     margin-left: 1px;
     margin-right: 1px;
@@ -22,7 +22,7 @@
         font-family: Arial;
 
 [expanded] 
-div { (styles-properti…verload.html:6 -> styles-properties-overload.html:6:1)
+div { (styles-properti…verload.html:6 -> styles-properties-overload.html:6:6)
 /-- overloaded --/     margin: 1px;
     /-- overloaded --/     margin-top: 1px;
     /-- overloaded --/     margin-right: 1px;
@@ -53,7 +53,7 @@
 
 ======== Inherited from div.container ========
 [expanded] 
-.container { (styles-properti…erload.html:19 -> styles-properties-overload.html:19:1)
+.container { (styles-properti…erload.html:19 -> styles-properties-overload.html:19:13)
 /-- overloaded --/     font-size: 10px;
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-expected.txt
index 49abce2..5ee9b36 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-expected.txt
@@ -4,21 +4,21 @@
 element.style { ()
 
 [expanded] 
-#main, .at_line_49 { (styles-source-lines.html:49 -> styles-source-lines.html:49:1)
+#main, .at_line_49 { (styles-source-lines.html:50 -> styles-source-lines.html:50:2)
     border: 1px solid
     red;
 
 [expanded] 
-#main, .at_line_41 { (styles-source-lines.html:40 -> styles-source-lines.html:40:3)
+#main, .at_line_41 { (styles-source-lines.html:43 -> styles-source-lines.html:43:2)
     font-size: 10px;
 
 [expanded] 
-#main, .at_line_26 { (styles-source-lines.html:26 -> styles-source-lines.html:26:1)
+#main, .at_line_26 { (styles-source-lines.html:29 -> styles-source-lines.html:29:2)
     font-family: /*  Comment in 
                       value  */courier;
 
 [expanded] 
-#main, .at_line_11::before { (styles-source-lines.html:11 -> styles-source-lines.html:11:1)
+#main, .at_line_11::before { (styles-source-lines.html:15 -> styles-source-lines.html:15:2)
     color: red;
     content: "Before";
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-inline-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-inline-expected.txt
index d38b30b..a45e761 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-inline-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-inline-expected.txt
@@ -6,7 +6,7 @@
     display: none;
 
 [expanded] 
-.foo { (styles-source-l…-inline.html:7 -> styles-source-lines-inline.html:7:1)
+.foo { (styles-source-l…-inline.html:7 -> styles-source-lines-inline.html:7:7)
     color: blue;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-recovery-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-recovery-expected.txt
index f12ac1d..74f9617a 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-recovery-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-source-lines-recovery-expected.txt
@@ -4,15 +4,15 @@
 element.style { ()
 
 [expanded] 
-#main { (styles-source-l…covery.html:25 -> styles-source-lines-recovery.html:25:1)
+#main { (styles-source-l…covery.html:25 -> styles-source-lines-recovery.html:25:8)
     color: white;
 
 [expanded] 
-#main { (styles-source-l…covery.html:16 -> styles-source-lines-recovery.html:16:1)
+#main { (styles-source-l…covery.html:16 -> styles-source-lines-recovery.html:16:8)
 /-- overloaded --/     color: blue;
 
 [expanded] 
-#main { (styles-source-l…ecovery.html:7 -> styles-source-lines-recovery.html:7:1)
+#main { (styles-source-l…ecovery.html:7 -> styles-source-lines-recovery.html:7:8)
 /-- overloaded --/     color: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-from-js-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-from-js-expected.txt
index 9d2d5ca..748d2b9 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-from-js-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-from-js-expected.txt
@@ -78,7 +78,7 @@
 element.style { ()
 
 [expanded] 
-.red div:first-child { (styles-update-from-js.html:4 -> styles-update-from-js.html:4:1)
+.red div:first-child { (styles-update-from-js.html:4 -> styles-update-from-js.html:4:23)
     background-color: red;
 
 [expanded] 
@@ -97,7 +97,7 @@
 element.style { ()
 
 [expanded] 
-div[foo="bar"] + div { (styles-update-from-js.html:8 -> styles-update-from-js.html:8:1)
+div[foo="bar"] + div { (styles-update-from-js.html:8 -> styles-update-from-js.html:8:23)
     background-color: blue;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-1-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-1-expected.txt
index 19ae74a..22ac312 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-1-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-1-expected.txt
@@ -10,32 +10,32 @@
 
 [expanded] 
 @media (min-device-width: 1px)
-.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:5)
+.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:21)
     font-family: monospace;
 
 [expanded] 
-.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:63)
+.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:79)
     position: relative;
     margin: 1em;
 
 [expanded] 
-.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:19)
+.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:35)
     border: 1px solid black;
 
 [expanded] 
-.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:3)
+.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:19)
     color: red;
 
 [expanded] 
-.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:1)
+.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:15)
     padding: 1em;
 
 [expanded] 
-.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:1)
+.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:15)
     box-sizing: border-box;
 
 [expanded] 
-.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:1)
+.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:15)
     line-height: 1;
 
 [expanded] 
@@ -52,33 +52,33 @@
 
 [expanded] 
 @media (min-device-width: 1px)
-.should-change { (styles-update-links.css:13 -> styles-update-links.css:13:5)
+.should-change { (styles-update-links.css:13 -> styles-update-links.css:13:21)
     font-family: monospace;
 
 [expanded] 
-.should-change { (styles-update-links.css:9 -> styles-update-links.css:9:51)
+.should-change { (styles-update-links.css:9 -> styles-update-links.css:9:67)
     position: relative;
     margin: 1em;
 
 [expanded] 
-.should-change { (styles-update-links.css:9 -> styles-update-links.css:9:7)
+.should-change { (styles-update-links.css:9 -> styles-update-links.css:9:23)
     border: 1px solid black;
 
 [expanded] 
-.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:3)
+.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:19)
     color: red;
 /-- overloaded --/     PROPERTY: INSERTED;
 
 [expanded] 
-.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:1)
+.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:15)
     padding: 1em;
 
 [expanded] 
-.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:1)
+.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:15)
     box-sizing: border-box;
 
 [expanded] 
-.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:1)
+.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:15)
     line-height: 1;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-2-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-2-expected.txt
index e597de92..d572dc6a 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-2-expected.txt
@@ -15,32 +15,32 @@
 
 [expanded] 
 @media (min-device-width: 1px)
-.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:5)
+.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:21)
     font-family: monospace;
 
 [expanded] 
-.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:89)
+.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:105)
     position: relative;
     margin: 1em;
 
 [expanded] 
-.should-change, .INSERTED-OTHER-SELECTOR { (styles-update-links.css:7 -> styles-update-links.css:7:19)
+.should-change, .INSERTED-OTHER-SELECTOR { (styles-update-links.css:7 -> styles-update-links.css:7:61)
     border: 1px solid black;
 
 [expanded] 
-.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:3)
+.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:19)
     color: red;
 
 [expanded] 
-.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:1)
+.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:15)
     padding: 1em;
 
 [expanded] 
-.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:1)
+.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:15)
     box-sizing: border-box;
 
 [expanded] 
-.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:1)
+.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:15)
     line-height: 1;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-3-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-3-expected.txt
index 2c5c586..b7ad08a 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-3-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-3-expected.txt
@@ -15,32 +15,32 @@
 
 [expanded] 
 @media (min-device-width: 1px)
-.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:5)
+.should-change { (styles-update-links.css:11 -> styles-update-links.css:11:21)
     font-family: monospace;
 
 [expanded] 
-.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:67)
+.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:83)
     position: relative;
     margin: 1em;
 
 [expanded] 
-.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:19)
+.should-change { (styles-update-links.css:7 -> styles-update-links.css:7:35)
 /-- overloaded --/ /-- disabled --/     /* border: 1px solid black; */
 
 [expanded] 
-.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:3)
+.should-change { (styles-update-links.css:6 -> styles-update-links.css:6:19)
     color: red;
 
 [expanded] 
-.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:1)
+.left-intact { (styles-update-links.css:4 -> styles-update-links.css:4:15)
     padding: 1em;
 
 [expanded] 
-.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:1)
+.left-intact { (styles-update-links.css:1 -> styles-update-links.css:1:15)
     box-sizing: border-box;
 
 [expanded] 
-.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:1)
+.left-intact { (styles-update-links-2.css:19 -> styles-update-links-2.css:19:15)
     line-height: 1;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-4-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-4-expected.txt
index 0dcd9e7..eaf111b28 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-4-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-update-links-4-expected.txt
@@ -19,16 +19,16 @@
 
 ======== Pseudo ::before element ========
 [expanded] 
-#pseudo::before { (styles-update-links-4.html:17 -> styles-update-links-4.html:17:1)
+#pseudo::before { (styles-update-links-4.html:17 -> styles-update-links-4.html:17:18)
     color: blue;
 
 ======== Pseudo ::after element ========
 [expanded] 
-#pseudo::after { (styles-update-links-4.html:13 -> styles-update-links-4.html:13:1)
+#pseudo::after { (styles-update-links-4.html:13 -> styles-update-links-4.html:13:17)
     border: 1px solid black;
 
 [expanded] 
-#pseudo::after { (styles-update-links-4.html:7 -> styles-update-links-4.html:7:1)
+#pseudo::after { (styles-update-links-4.html:7 -> styles-update-links-4.html:7:17)
 /-- overloaded --/     pseudo-property: "12";
 /-- overloaded --/     PROPERTY: INSERTED;
     color: red;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-with-spaces-in-sourceURL-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-with-spaces-in-sourceURL-expected.txt
index 9b0535d..f9e11a8 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-with-spaces-in-sourceURL-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-with-spaces-in-sourceURL-expected.txt
@@ -4,7 +4,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (style.css:1 -> style.css:1:1)
+#inspected { (style.css:1 -> style.css:1:13)
     background-color: blue;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/svg-style-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/svg-style-expected.txt
index 39f37888..f8e4ff22 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/svg-style-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/svg-style-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-rect { (svg-style.xhtml:38 -> svg-style.xhtml:38:10)
+rect { (svg-style.xhtml:38 -> svg-style.xhtml:38:16)
     fill: red;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-new-rule-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-new-rule-expected.txt
index 4637466..be0cbdc 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-new-rule-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-new-rule-expected.txt
@@ -7,10 +7,10 @@
     font-size: 12px;
 
 [expanded] 
-div.foo { (inspector-stylesheet:3 -> inspector-stylesheet:3:1)
+div.foo { (inspector-stylesheet:3 -> inspector-stylesheet:3:10)
 
 [expanded] 
-div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:10)
 
 [expanded] 
 div { (user agent stylesheet)
@@ -27,7 +27,7 @@
     color: red;
 
 [expanded] 
-div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:10)
 
 [expanded] 
 div { (user agent stylesheet)
@@ -42,10 +42,10 @@
     font-size: 12px;
 
 [expanded] 
-div.foo { (inspector-stylesheet:3 -> inspector-stylesheet:3:1)
+div.foo { (inspector-stylesheet:3 -> inspector-stylesheet:3:10)
 
 [expanded] 
-div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:1)
+div.foo { (inspector-stylesheet:1 -> inspector-stylesheet:1:10)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-property-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-property-expected.txt
index 5d7958d0..6e9a0d1 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/undo-add-property-expected.txt
@@ -6,7 +6,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -18,7 +18,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
     margin-left: 2px;
 
@@ -31,7 +31,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -43,7 +43,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
     margin-left: 2px;
 
@@ -57,7 +57,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
     margin-left: 2px;
 
@@ -70,7 +70,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     font-weight: bold;
     margin-left: 2px;
@@ -84,7 +84,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     font-weight: bold;
     margin-left: 2px;
 
@@ -97,7 +97,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     font-weight: bold;
     margin-left: 2px;
@@ -112,7 +112,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     font-weight: bold;
     margin-left: 2px;
@@ -126,7 +126,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     margin-right: 1px;
     font-weight: bold;
@@ -141,7 +141,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     font-weight: bold;
     margin-left: 2px;
@@ -155,7 +155,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-add-property.html:7 -> undo-add-property.html:7:1)
+.container { (undo-add-property.html:7 -> undo-add-property.html:7:13)
     margin-top: 0px;
     margin-right: 1px;
     font-weight: bold;
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles/cancel-upon-invalid-property-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles/cancel-upon-invalid-property-expected.txt
index 6ba1027..2be54b0 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles/cancel-upon-invalid-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles/cancel-upon-invalid-property-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (cancel-upon-inv…roperty.html:6 -> cancel-upon-invalid-property.html:6:1)
+#inspected { (cancel-upon-inv…roperty.html:6 -> cancel-upon-invalid-property.html:6:13)
     color: blue;
 
 [expanded] 
@@ -16,7 +16,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (cancel-upon-inv…roperty.html:6 -> cancel-upon-invalid-property.html:6:1)
+#inspected { (cancel-upon-inv…roperty.html:6 -> cancel-upon-invalid-property.html:6:13)
     color: blue;
     : ;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-after-cancelled-editing-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-after-cancelled-editing-expected.txt
index 15a23644..6c2893f 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-after-cancelled-editing-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-after-cancelled-editing-expected.txt
@@ -11,7 +11,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:1)
+#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:13)
     color: blue;
     : ;
 
@@ -27,7 +27,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:1)
+#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:13)
     color: blue;
 
 [expanded] 
@@ -40,7 +40,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:1)
+#inspected { (undo-after-canc…editing.html:6 -> undo-after-cancelled-editing.html:6:13)
 
 [expanded] 
 div { (user agent stylesheet)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-change-property-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-change-property-expected.txt
index e298b17..1adc1a7e 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-change-property-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-change-property-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-change-property.html:7 -> undo-change-property.html:7:1)
+.container { (undo-change-property.html:7 -> undo-change-property.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -17,7 +17,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-change-property.html:7 -> undo-change-property.html:7:1)
+.container { (undo-change-property.html:7 -> undo-change-property.html:7:13)
     font-weight: normal;
 
 [expanded] 
@@ -29,7 +29,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-change-property.html:7 -> undo-change-property.html:7:1)
+.container { (undo-change-property.html:7 -> undo-change-property.html:7:13)
     font-weight: bold;
 
 [expanded] 
@@ -41,7 +41,7 @@
 element.style { ()
 
 [expanded] 
-.container { (undo-change-property.html:7 -> undo-change-property.html:7:1)
+.container { (undo-change-property.html:7 -> undo-change-property.html:7:13)
     font-weight: normal;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-set-selector-text-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-set-selector-text-expected.txt
index 098017d..da476c13 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-set-selector-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles/undo-set-selector-text-expected.txt
@@ -5,7 +5,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:1)
+#inspected { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:13)
     color: green;
 
 [expanded] 
@@ -17,7 +17,7 @@
 element.style { ()
 
 [expanded] 
-#inspected, #other { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:13)
+#inspected, #other { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:21)
     color: green;
 
 [expanded] 
@@ -29,7 +29,7 @@
 element.style { ()
 
 [expanded] 
-#inspected { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:1)
+#inspected { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:13)
     color: green;
 
 [expanded] 
@@ -41,7 +41,7 @@
 element.style { ()
 
 [expanded] 
-#inspected, #other { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:13)
+#inspected, #other { (undo-set-selector-text.html:4 -> undo-set-selector-text.html:4:21)
     color: green;
 
 [expanded] 
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7-expected.txt
index fd216f32..95982df 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7-expected.txt
@@ -79,3 +79,12 @@
 Correct mapping for <a>
 Correct mapping for <b>
 
+Running: ensureExponentialOperator
+====== 8< ------
+2 ** 3
+
+------ >8 ======
+Correct mapping for <2>
+Correct mapping for <**>
+Correct mapping for <3>
+
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7.html b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7.html
index 79064ef7..138907f 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/pretty-print-javascript-7.html
@@ -46,6 +46,12 @@
             var mappingQueries = ["a", "b"];
             testJSFormatter("a();\n\n\n\n\n\n\n\n\nb();", mappingQueries, next);
         },
+
+        function ensureExponentialOperator(next)
+        {
+            var mappingQueries = ["2", "**", "3"];
+            testJSFormatter("2**3", mappingQueries, next);
+        },
     ]);
 }
 
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-fullscreen-button.html b/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-fullscreen-button.html
index db1fd286..30fccc1 100644
--- a/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-fullscreen-button.html
+++ b/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-fullscreen-button.html
@@ -28,9 +28,12 @@
     var coords = elementCoordinates(overflowList.children[OverflowMenuButtons.FULLSCREEN]);
     clickAtCoordinates(coords[0], coords[1]);
 
-    document.onwebkitfullscreenchange = t.step_func_done(() => {
+    document.onwebkitfullscreenchange = t.step_func(() => {
       assert_equals(document.fullscreenElement, video);
-      assert_equals(getComputedStyle(overflowMenu).display, "none");
+      // Hiding the overflow menu is triggered by layout.
+      testRunner.layoutAndPaintAsyncThen(t.step_func_done(() => {
+        assert_equals(getComputedStyle(overflowMenu).display, "none");
+      }));
     });
   }));
 });
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
index b55dc80..aa5b644 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/resize-scrollable-iframe-expected.txt
@@ -18,11 +18,21 @@
         },
         {
           "object": "LayoutView #document",
+          "rect": [8, 193, 285, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 393, 285, 15],
           "reason": "scroll"
         },
         {
           "object": "LayoutView #document",
+          "rect": [93, 108, 200, 285],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 193, 85, 15],
           "reason": "scroll"
         },
@@ -77,6 +87,10 @@
     {
       "object": "VerticalScrollbar",
       "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-gradient-expected.png b/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-gradient-expected.png
index f4391cc..de942810 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/masks/fieldset-mask-expected.txt b/third_party/WebKit/LayoutTests/paint/masks/fieldset-mask-expected.txt
index cc07791..1ca4d51 100644
--- a/third_party/WebKit/LayoutTests/paint/masks/fieldset-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/masks/fieldset-mask-expected.txt
@@ -6,4 +6,3 @@
 layer at (10,8) size 780x30 transparent
   LayoutFieldset {FIELDSET} at (2,0) size 780x29.59 [border: (2px groove #C0C0C0)]
     LayoutBlockFlow {LEGEND} at (14,0) size 50x12 [bgcolor=#0000FF]
-    LayoutBlockFlow (anonymous) at (14,7.59) size 752x10
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 4216adb..2f72a738 100644
--- a/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -206,6 +206,7 @@
 object-position: 50% 50%
 offset-distance: 0px
 offset-path: none
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 9decd43..cf3a76e 100644
--- a/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -206,6 +206,7 @@
 object-position: 50% 50%
 offset-distance: 0px
 offset-path: none
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/css/getComputedStyle-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/css/getComputedStyle-listing-expected.txt
index 50fafa01..4e4e333 100644
--- a/third_party/WebKit/LayoutTests/platform/android/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/svg/css/getComputedStyle-listing-expected.txt
@@ -206,6 +206,7 @@
 object-position: 50% 50%
 offset-distance: 0px
 offset-path: none
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/platform/android/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/platform/android/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 5d81ccd..a5d8849 100644
--- a/third_party/WebKit/LayoutTests/platform/android/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -165,6 +165,7 @@
 offset
 offsetDistance
 offsetPath
+offsetRotate
 offsetRotation
 opacity
 order
diff --git a/third_party/WebKit/LayoutTests/platform/android/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/platform/android/webexposed/css-properties-as-js-properties-expected.txt
index 1935ada..1139fb4 100644
--- a/third_party/WebKit/LayoutTests/platform/android/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/webexposed/css-properties-as-js-properties-expected.txt
@@ -194,6 +194,7 @@
 offsetDistance
 offsetPath
 offsetPosition
+offsetRotate
 offsetRotation
 opacity
 order
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/backgroundSize16-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/backgroundSize16-expected.png
index cf547e2..a16356e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/backgroundSize16-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/size/backgroundSize16-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-rotate-transform-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-rotate-transform-expected.png
index 331e518..bd65662 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-rotate-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-rotate-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/css/fieldset-display-row-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/css/fieldset-display-row-expected.txt
index 77c7787..f3ef739 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/css/fieldset-display-row-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/css/fieldset-display-row-expected.txt
@@ -4,6 +4,5 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x39.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x20
-          LayoutText {#text} at (0,0) size 350x19
-            text run at (0,0) width 350: "If you can see this fieldset without crashing, then all is well."
+        LayoutText {#text} at (14,8) size 350x19
+          text run at (14,8) width 350: "If you can see this fieldset without crashing, then all is well."
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/006-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/006-expected.txt
index 4331a102..f220000 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/006-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/006-expected.txt
@@ -8,7 +8,6 @@
           LayoutBlockFlow {LEGEND} at (42,0) size 114.89x20
             LayoutText {#text} at (2,0) size 111x19
               text run at (2,0) width 111: "Test without forms"
-          LayoutBlockFlow (anonymous) at (22,15.59) size 726x30
-            LayoutBlockFlow {DIV} at (0,10) size 726x20
-              LayoutText {#text} at (0,0) size 263x19
-                text run at (0,0) width 263: "A DIV inside a fieldset, not related to forms"
+          LayoutBlockFlow {DIV} at (22,25.59) size 726x20
+            LayoutText {#text} at (0,0) size 263x19
+              text run at (0,0) width 263: "A DIV inside a fieldset, not related to forms"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/007-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/007-expected.txt
index 8d6d89fa..c1e5f66 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/007-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/007-expected.txt
@@ -7,19 +7,19 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 84x20
           LayoutText {#text} at (2,0) size 80x19
             text run at (2,0) width 80: "Number One"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 84x58
-          LayoutText {#text} at (0,18) size 75x19
-            text run at (0,18) width 75: "Hello world."
-          LayoutBR {BR} at (75,33) size 0x0
-          LayoutText {#text} at (0,38) size 75x19
-            text run at (0,38) width 75: "Hello world."
-      LayoutText {#text} at (116,63) size 4x19
-        text run at (116,63) width 4: " "
+        LayoutBlockFlow (anonymous) at (14,25.59) size 84x40
+          LayoutText {#text} at (0,0) size 75x19
+            text run at (0,0) width 75: "Hello world."
+          LayoutBR {BR} at (75,15) size 0x0
+          LayoutText {#text} at (0,20) size 75x19
+            text run at (0,20) width 75: "Hello world."
+      LayoutText {#text} at (116,45) size 4x19
+        text run at (116,45) width 4: " "
       LayoutFieldset {FIELDSET} at (122,20) size 112.89x57.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 84.89x20
           LayoutText {#text} at (2,0) size 81x19
             text run at (2,0) width 81: "Number Two"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 84.89x38
-          LayoutText {#text} at (0,18) size 75x19
-            text run at (0,18) width 75: "Hello world."
+        LayoutBlockFlow (anonymous) at (14,25.59) size 84.89x20
+          LayoutText {#text} at (0,0) size 75x19
+            text run at (0,0) width 75: "Hello world."
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-align-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-align-expected.txt
index cd64da0..07f313797 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-align-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-align-expected.txt
@@ -10,29 +10,29 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 96x20
           LayoutText {#text} at (2,0) size 92x19
             text run at (2,0) width 92: "My Legend left"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,95.59) size 765x59.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (325,0) size 115x20
           LayoutText {#text} at (2,0) size 111x19
             text run at (2,0) width 111: "My Legend center"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,155.19) size 765x59.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (647,0) size 104x20
           LayoutText {#text} at (2,0) size 100x19
             text run at (2,0) width 100: "My Legend right"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,214.78) size 765x59.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 118x20
           LayoutText {#text} at (2,0) size 114x19
             text run at (2,0) width 114: "My Legend default"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,282.38) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,300.38) size 769x20
@@ -46,29 +46,29 @@
           LayoutBlockFlow {LEGEND} at (14,0) size 96x20
             LayoutText {#text} at (2,0) size 92x19
               text run at (2,0) width 92: "My Legend left"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-            LayoutTextControl {INPUT} at (583,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+            LayoutTextControl {INPUT} at (583,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,59.59) size 765x59.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (325,0) size 115x20
             LayoutText {#text} at (2,0) size 111x19
               text run at (2,0) width 111: "My Legend center"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-            LayoutTextControl {INPUT} at (583,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+            LayoutTextControl {INPUT} at (583,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,119.19) size 765x59.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (647,0) size 104x20
             LayoutText {#text} at (2,0) size 100x19
               text run at (2,0) width 100: "My Legend right"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-            LayoutTextControl {INPUT} at (583,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+            LayoutTextControl {INPUT} at (583,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,178.78) size 765x59.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (633,0) size 118x20
             LayoutText {#text} at (2,0) size 114x19
               text run at (2,0) width 114: "My Legend default"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x40
-            LayoutTextControl {INPUT} at (583,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,25.59) size 737x22
+            LayoutTextControl {INPUT} at (583,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,574.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
@@ -81,29 +81,29 @@
         LayoutBlockFlow {LEGEND} at (17,0) size 96x20
           LayoutText {#text} at (2,0) size 92x19
             text run at (2,0) width 92: "My Legend left"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,35) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,725.75) size 739x74 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (312,0) size 115x20
           LayoutText {#text} at (2,0) size 111x19
             text run at (2,0) width 111: "My Legend center"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,35) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,814.75) size 739x74 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (618,0) size 104x20
           LayoutText {#text} at (2,0) size 100x19
             text run at (2,0) width 100: "My Legend right"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,35) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,903.75) size 739x74 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (17,0) size 118x20
           LayoutText {#text} at (2,0) size 114x19
             text run at (2,0) width 114: "My Legend default"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x40
-          LayoutTextControl {INPUT} at (0,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,35) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,992.75) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,1010.75) size 769x20
@@ -117,29 +117,29 @@
           LayoutBlockFlow {LEGEND} at (17,0) size 96x20
             LayoutText {#text} at (2,0) size 92x19
               text run at (2,0) width 92: "My Legend left"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x40
-            LayoutTextControl {INPUT} at (551,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,35) size 705x22
+            LayoutTextControl {INPUT} at (551,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,89) size 739x74 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (312,0) size 115x20
             LayoutText {#text} at (2,0) size 111x19
               text run at (2,0) width 111: "My Legend center"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x40
-            LayoutTextControl {INPUT} at (551,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,35) size 705x22
+            LayoutTextControl {INPUT} at (551,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,178) size 739x74 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (618,0) size 104x20
             LayoutText {#text} at (2,0) size 100x19
               text run at (2,0) width 100: "My Legend right"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x40
-            LayoutTextControl {INPUT} at (551,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,35) size 705x22
+            LayoutTextControl {INPUT} at (551,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,267) size 739x74 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (604,0) size 118x20
             LayoutText {#text} at (2,0) size 114x19
               text run at (2,0) width 114: "My Legend default"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x40
-            LayoutTextControl {INPUT} at (551,18) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,35) size 705x22
+            LayoutTextControl {INPUT} at (551,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,1402.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
index ad4402d..a75bf7b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
@@ -17,4 +17,3 @@
       LayoutBlockFlow {FORM} at (0,72) size 784x53.59
         LayoutFieldset {FIELDSET} at (2,0) size 780x53.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 36x36 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 752x34
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-with-float-expected.txt
index f3127e5..6f68e89 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-with-float-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/fieldset-with-float-expected.txt
@@ -4,8 +4,7 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x335.59
     LayoutBlockFlow {BODY} at (8,8) size 784x319.59
       LayoutFieldset {FIELDSET} at (2,0) size 780x319.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x300
-          LayoutBlockFlow (floating) {DIV} at (0,0) size 300x300 [bgcolor=#008000]
-          LayoutBlockFlow {DIV} at (0,0) size 752x50
-            LayoutText {#text} at (300,0) size 83x19
-              text run at (300,0) width 83: "Other content"
+        LayoutBlockFlow (floating) {DIV} at (14,7.59) size 300x300 [bgcolor=#008000]
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x50
+          LayoutText {#text} at (300,0) size 83x19
+            text run at (300,0) width 83: "Other content"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/float-before-fieldset-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/float-before-fieldset-expected.txt
index 6f3cd0c..9652aac 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/float-before-fieldset-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/fieldset/float-before-fieldset-expected.txt
@@ -8,6 +8,6 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 75x20
           LayoutText {#text} at (2,0) size 71x19
             text run at (2,0) width 71: "Hello world"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 704x38
-          LayoutText {#text} at (0,18) size 133x19
-            text run at (0,18) width 133: "Some fieldset content."
+        LayoutBlockFlow (anonymous) at (14,25.59) size 704x20
+          LayoutText {#text} at (0,0) size 133x19
+            text run at (0,0) width 133: "Some fieldset content."
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/ruby/rubyDOM-remove-text2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/ruby/rubyDOM-remove-text2-expected.txt
index 56c82c9..b9e53808 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/ruby/rubyDOM-remove-text2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/ruby/rubyDOM-remove-text2-expected.txt
@@ -20,7 +20,6 @@
             LayoutRubyText {RT} at (0,-12) size 112.83x12
               LayoutText {#text} at (0,0) size 113x12
                 text run at (0,0) width 113: "Hyper-text Markup Language"
-            LayoutRubyBase (anonymous) at (0,0) size 112.83x0
           LayoutRubyRun (anonymous) at (258.83,0) size 8x20
             LayoutRubyBase (anonymous) at (0,0) size 8x20
               LayoutText {#text} at (0,0) size 8x19
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.png
index 5a0fc735..71ca0658 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.txt
index f3615077..d91e257 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/basic/generic-family-changes-expected.txt
@@ -5,8 +5,8 @@
     LayoutBlockFlow {BODY} at (8,8) size 784x293
       LayoutBlockFlow (anonymous) at (0,0) size 784x60
         LayoutText {#text} at (0,0) size 773x59
-          text run at (0,0) width 501: "Tests of WebKit's intepretation of font sizes when no absolute font size is specified. "
-          text run at (500,0) width 256: "Percentages and logical keywords scale to"
+          text run at (0,0) width 506: "Tests of WebKit's interpretation of font sizes when no absolute font size is specified. "
+          text run at (505,0) width 256: "Percentages and logical keywords scale to"
           text run at (0,20) width 136: "reflect the family type. "
           text run at (136,20) width 637: "Opera 9 matches this behavior as well (except it has a bug with multiple font-family mappings as in the first"
           text run at (0,40) width 59: "example)."
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
index 9471b70e..feea0754 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
@@ -4,503 +4,502 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x370.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x351
-          LayoutBlockFlow {DIV} at (0,0) size 752x351
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x351
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,0) size 50x34
-                LayoutText {#text} at (50,19) size 4x19
-                  text run at (50,19) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (74,19) size 43x19
-                    text run at (74,19) width 43: "Classic"
-                LayoutText {#text} at (117,19) size 4x19
-                  text run at (117,19) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,0) size 50x34
+              LayoutText {#text} at (50,19) size 4x19
+                text run at (50,19) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (74,19) size 43x19
+                  text run at (74,19) width 43: "Classic"
+              LayoutText {#text} at (117,19) size 4x19
+                text run at (117,19) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 115x19
             LayoutInline {DIV} at (0,0) size 115x19
-              LayoutInline {DIV} at (0,0) size 115x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (121,0) size 50x34
-                LayoutText {#text} at (171,19) size 4x19
-                  text run at (171,19) width 4: " "
-                LayoutBlockFlow {INPUT} at (179,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (195,19) size 37x19
-                    text run at (195,19) width 37: "Africa"
-                LayoutText {#text} at (232,19) size 4x19
-                  text run at (232,19) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (121,0) size 50x34
+              LayoutText {#text} at (171,19) size 4x19
+                text run at (171,19) width 4: " "
+              LayoutBlockFlow {INPUT} at (179,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (195,19) size 37x19
+                  text run at (195,19) width 37: "Africa"
+              LayoutText {#text} at (232,19) size 4x19
+                text run at (232,19) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (236,0) size 50x34
-                LayoutText {#text} at (286,19) size 4x19
-                  text run at (286,19) width 4: " "
-                LayoutBlockFlow {INPUT} at (294,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (310,19) size 118x19
-                    text run at (310,19) width 118: "Alexander's Empire"
-                LayoutText {#text} at (428,19) size 4x19
-                  text run at (428,19) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (236,0) size 50x34
+              LayoutText {#text} at (286,19) size 4x19
+                text run at (286,19) width 4: " "
+              LayoutBlockFlow {INPUT} at (294,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (310,19) size 118x19
+                  text run at (310,19) width 118: "Alexander's Empire"
+              LayoutText {#text} at (428,19) size 4x19
+                text run at (428,19) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (432,0) size 50x34
-                LayoutText {#text} at (482,19) size 4x19
-                  text run at (482,19) width 4: " "
-                LayoutBlockFlow {INPUT} at (490,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (506,19) size 94x19
-                    text run at (506,19) width 94: "Ancient Greece"
-                LayoutText {#text} at (600,19) size 4x19
-                  text run at (600,19) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (432,0) size 50x34
+              LayoutText {#text} at (482,19) size 4x19
+                text run at (482,19) width 4: " "
+              LayoutBlockFlow {INPUT} at (490,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (506,19) size 94x19
+                  text run at (506,19) width 94: "Ancient Greece"
+              LayoutText {#text} at (600,19) size 4x19
+                text run at (600,19) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x19
             LayoutInline {DIV} at (0,0) size 117x19
-              LayoutInline {DIV} at (0,0) size 117x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (604,0) size 50x34
-                LayoutText {#text} at (654,19) size 4x19
-                  text run at (654,19) width 4: " "
-                LayoutBlockFlow {INPUT} at (662,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (678,19) size 43x19
-                    text run at (678,19) width 43: "Classic"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (604,0) size 50x34
+              LayoutText {#text} at (654,19) size 4x19
+                text run at (654,19) width 4: " "
+              LayoutBlockFlow {INPUT} at (662,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (678,19) size 43x19
+                  text run at (678,19) width 43: "Classic"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 115x19
             LayoutInline {DIV} at (0,0) size 115x19
-              LayoutInline {DIV} at (0,0) size 115x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,39) size 50x34
-                LayoutText {#text} at (50,58) size 4x19
-                  text run at (50,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,60) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (74,58) size 37x19
-                    text run at (74,58) width 37: "Africa"
-                LayoutText {#text} at (111,58) size 4x19
-                  text run at (111,58) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,39) size 50x34
+              LayoutText {#text} at (50,58) size 4x19
+                text run at (50,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,60) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (74,58) size 37x19
+                  text run at (74,58) width 37: "Africa"
+              LayoutText {#text} at (111,58) size 4x19
+                text run at (111,58) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (115,39) size 50x34
-                LayoutText {#text} at (165,58) size 4x19
-                  text run at (165,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (173,60) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (189,58) size 118x19
-                    text run at (189,58) width 118: "Alexander's Empire"
-                LayoutText {#text} at (307,58) size 4x19
-                  text run at (307,58) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (115,39) size 50x34
+              LayoutText {#text} at (165,58) size 4x19
+                text run at (165,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (173,60) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (189,58) size 118x19
+                  text run at (189,58) width 118: "Alexander's Empire"
+              LayoutText {#text} at (307,58) size 4x19
+                text run at (307,58) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (311,39) size 50x34
-                LayoutText {#text} at (361,58) size 4x19
-                  text run at (361,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (369,60) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (385,58) size 94x19
-                    text run at (385,58) width 94: "Ancient Greece"
-                LayoutText {#text} at (479,58) size 4x19
-                  text run at (479,58) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (311,39) size 50x34
+              LayoutText {#text} at (361,58) size 4x19
+                text run at (361,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (369,60) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (385,58) size 94x19
+                  text run at (385,58) width 94: "Ancient Greece"
+              LayoutText {#text} at (479,58) size 4x19
+                text run at (479,58) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (483,39) size 50x34
-                LayoutText {#text} at (533,58) size 4x19
-                  text run at (533,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (541,60) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (557,58) size 43x19
-                    text run at (557,58) width 43: "Classic"
-                LayoutText {#text} at (600,58) size 4x19
-                  text run at (600,58) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (483,39) size 50x34
+              LayoutText {#text} at (533,58) size 4x19
+                text run at (533,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (541,60) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (557,58) size 43x19
+                  text run at (557,58) width 43: "Classic"
+              LayoutText {#text} at (600,58) size 4x19
+                text run at (600,58) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (604,39) size 50x34
-                LayoutText {#text} at (654,58) size 4x19
-                  text run at (654,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (662,60) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (678,58) size 37x19
-                    text run at (678,58) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (604,39) size 50x34
+              LayoutText {#text} at (654,58) size 4x19
+                text run at (654,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (662,60) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (678,58) size 37x19
+                  text run at (678,58) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,78) size 50x34
-                LayoutText {#text} at (50,97) size 4x19
-                  text run at (50,97) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,99) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,97) size 118x19
-                    text run at (74,97) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,97) size 4x19
-                  text run at (192,97) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,78) size 50x34
+              LayoutText {#text} at (50,97) size 4x19
+                text run at (50,97) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,99) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,97) size 118x19
+                  text run at (74,97) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,97) size 4x19
+                text run at (192,97) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,78) size 50x34
-                LayoutText {#text} at (246,97) size 4x19
-                  text run at (246,97) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,99) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,97) size 94x19
-                    text run at (270,97) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,97) size 4x19
-                  text run at (364,97) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,78) size 50x34
+              LayoutText {#text} at (246,97) size 4x19
+                text run at (246,97) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,99) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,97) size 94x19
+                  text run at (270,97) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,97) size 4x19
+                text run at (364,97) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,78) size 50x34
-                LayoutText {#text} at (418,97) size 4x19
-                  text run at (418,97) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,99) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,97) size 43x19
-                    text run at (442,97) width 43: "Classic"
-                LayoutText {#text} at (485,97) size 4x19
-                  text run at (485,97) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,78) size 50x34
+              LayoutText {#text} at (418,97) size 4x19
+                text run at (418,97) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,99) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,97) size 43x19
+                  text run at (442,97) width 43: "Classic"
+              LayoutText {#text} at (485,97) size 4x19
+                text run at (485,97) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,78) size 50x34
-                LayoutText {#text} at (539,97) size 4x19
-                  text run at (539,97) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,99) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,97) size 37x19
-                    text run at (563,97) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,78) size 50x34
+              LayoutText {#text} at (539,97) size 4x19
+                text run at (539,97) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,99) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,97) size 37x19
+                  text run at (563,97) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,117) size 50x34
-                LayoutText {#text} at (50,136) size 4x19
-                  text run at (50,136) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,138) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,136) size 118x19
-                    text run at (74,136) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,136) size 4x19
-                  text run at (192,136) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,117) size 50x34
+              LayoutText {#text} at (50,136) size 4x19
+                text run at (50,136) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,138) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,136) size 118x19
+                  text run at (74,136) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,136) size 4x19
+                text run at (192,136) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,117) size 50x34
-                LayoutText {#text} at (246,136) size 4x19
-                  text run at (246,136) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,138) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,136) size 94x19
-                    text run at (270,136) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,136) size 4x19
-                  text run at (364,136) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,117) size 50x34
+              LayoutText {#text} at (246,136) size 4x19
+                text run at (246,136) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,138) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,136) size 94x19
+                  text run at (270,136) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,136) size 4x19
+                text run at (364,136) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,117) size 50x34
-                LayoutText {#text} at (418,136) size 4x19
-                  text run at (418,136) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,138) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,136) size 43x19
-                    text run at (442,136) width 43: "Classic"
-                LayoutText {#text} at (485,136) size 4x19
-                  text run at (485,136) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,117) size 50x34
+              LayoutText {#text} at (418,136) size 4x19
+                text run at (418,136) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,138) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,136) size 43x19
+                  text run at (442,136) width 43: "Classic"
+              LayoutText {#text} at (485,136) size 4x19
+                text run at (485,136) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,117) size 50x34
-                LayoutText {#text} at (539,136) size 4x19
-                  text run at (539,136) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,138) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,136) size 37x19
-                    text run at (563,136) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,117) size 50x34
+              LayoutText {#text} at (539,136) size 4x19
+                text run at (539,136) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,138) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,136) size 37x19
+                  text run at (563,136) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,156) size 50x34
-                LayoutText {#text} at (50,175) size 4x19
-                  text run at (50,175) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,177) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,175) size 118x19
-                    text run at (74,175) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,175) size 4x19
-                  text run at (192,175) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,156) size 50x34
+              LayoutText {#text} at (50,175) size 4x19
+                text run at (50,175) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,177) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,175) size 118x19
+                  text run at (74,175) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,175) size 4x19
+                text run at (192,175) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,156) size 50x34
-                LayoutText {#text} at (246,175) size 4x19
-                  text run at (246,175) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,177) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,175) size 94x19
-                    text run at (270,175) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,175) size 4x19
-                  text run at (364,175) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,156) size 50x34
+              LayoutText {#text} at (246,175) size 4x19
+                text run at (246,175) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,177) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,175) size 94x19
+                  text run at (270,175) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,175) size 4x19
+                text run at (364,175) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,156) size 50x34
-                LayoutText {#text} at (418,175) size 4x19
-                  text run at (418,175) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,177) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,175) size 43x19
-                    text run at (442,175) width 43: "Classic"
-                LayoutText {#text} at (485,175) size 4x19
-                  text run at (485,175) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,156) size 50x34
+              LayoutText {#text} at (418,175) size 4x19
+                text run at (418,175) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,177) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,175) size 43x19
+                  text run at (442,175) width 43: "Classic"
+              LayoutText {#text} at (485,175) size 4x19
+                text run at (485,175) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,156) size 50x34
-                LayoutText {#text} at (539,175) size 4x19
-                  text run at (539,175) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,177) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,175) size 37x19
-                    text run at (563,175) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,156) size 50x34
+              LayoutText {#text} at (539,175) size 4x19
+                text run at (539,175) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,177) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,175) size 37x19
+                  text run at (563,175) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,195) size 50x34
-                LayoutText {#text} at (50,214) size 4x19
-                  text run at (50,214) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,216) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,214) size 118x19
-                    text run at (74,214) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,214) size 4x19
-                  text run at (192,214) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,195) size 50x34
+              LayoutText {#text} at (50,214) size 4x19
+                text run at (50,214) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,216) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,214) size 118x19
+                  text run at (74,214) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,214) size 4x19
+                text run at (192,214) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,195) size 50x34
-                LayoutText {#text} at (246,214) size 4x19
-                  text run at (246,214) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,216) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,214) size 94x19
-                    text run at (270,214) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,214) size 4x19
-                  text run at (364,214) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,195) size 50x34
+              LayoutText {#text} at (246,214) size 4x19
+                text run at (246,214) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,216) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,214) size 94x19
+                  text run at (270,214) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,214) size 4x19
+                text run at (364,214) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,195) size 50x34
-                LayoutText {#text} at (418,214) size 4x19
-                  text run at (418,214) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,216) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,214) size 43x19
-                    text run at (442,214) width 43: "Classic"
-                LayoutText {#text} at (485,214) size 4x19
-                  text run at (485,214) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,195) size 50x34
+              LayoutText {#text} at (418,214) size 4x19
+                text run at (418,214) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,216) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,214) size 43x19
+                  text run at (442,214) width 43: "Classic"
+              LayoutText {#text} at (485,214) size 4x19
+                text run at (485,214) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,195) size 50x34
-                LayoutText {#text} at (539,214) size 4x19
-                  text run at (539,214) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,216) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,214) size 37x19
-                    text run at (563,214) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,195) size 50x34
+              LayoutText {#text} at (539,214) size 4x19
+                text run at (539,214) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,216) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,214) size 37x19
+                  text run at (563,214) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,234) size 50x34
-                LayoutText {#text} at (50,253) size 4x19
-                  text run at (50,253) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,255) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,253) size 118x19
-                    text run at (74,253) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,253) size 4x19
-                  text run at (192,253) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,234) size 50x34
+              LayoutText {#text} at (50,253) size 4x19
+                text run at (50,253) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,255) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,253) size 118x19
+                  text run at (74,253) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,253) size 4x19
+                text run at (192,253) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,234) size 50x34
-                LayoutText {#text} at (246,253) size 4x19
-                  text run at (246,253) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,255) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,253) size 94x19
-                    text run at (270,253) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,253) size 4x19
-                  text run at (364,253) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,234) size 50x34
+              LayoutText {#text} at (246,253) size 4x19
+                text run at (246,253) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,255) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,253) size 94x19
+                  text run at (270,253) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,253) size 4x19
+                text run at (364,253) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,234) size 50x34
-                LayoutText {#text} at (418,253) size 4x19
-                  text run at (418,253) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,255) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,253) size 43x19
-                    text run at (442,253) width 43: "Classic"
-                LayoutText {#text} at (485,253) size 4x19
-                  text run at (485,253) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,234) size 50x34
+              LayoutText {#text} at (418,253) size 4x19
+                text run at (418,253) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,255) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,253) size 43x19
+                  text run at (442,253) width 43: "Classic"
+              LayoutText {#text} at (485,253) size 4x19
+                text run at (485,253) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,234) size 50x34
-                LayoutText {#text} at (539,253) size 4x19
-                  text run at (539,253) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,255) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,253) size 37x19
-                    text run at (563,253) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,234) size 50x34
+              LayoutText {#text} at (539,253) size 4x19
+                text run at (539,253) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,255) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,253) size 37x19
+                  text run at (563,253) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,273) size 50x34
-                LayoutText {#text} at (50,292) size 4x19
-                  text run at (50,292) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,294) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,292) size 118x19
-                    text run at (74,292) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,292) size 4x19
-                  text run at (192,292) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,273) size 50x34
+              LayoutText {#text} at (50,292) size 4x19
+                text run at (50,292) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,294) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,292) size 118x19
+                  text run at (74,292) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,292) size 4x19
+                text run at (192,292) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 172x19
             LayoutInline {DIV} at (0,0) size 172x19
-              LayoutInline {DIV} at (0,0) size 172x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,273) size 50x34
-                LayoutText {#text} at (246,292) size 4x19
-                  text run at (246,292) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,294) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,292) size 94x19
-                    text run at (270,292) width 94: "Ancient Greece"
-                LayoutText {#text} at (364,292) size 4x19
-                  text run at (364,292) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,273) size 50x34
+              LayoutText {#text} at (246,292) size 4x19
+                text run at (246,292) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,294) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,292) size 94x19
+                  text run at (270,292) width 94: "Ancient Greece"
+              LayoutText {#text} at (364,292) size 4x19
+                text run at (364,292) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 121x19
             LayoutInline {DIV} at (0,0) size 121x19
-              LayoutInline {DIV} at (0,0) size 121x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (368,273) size 50x34
-                LayoutText {#text} at (418,292) size 4x19
-                  text run at (418,292) width 4: " "
-                LayoutBlockFlow {INPUT} at (426,294) size 13x13
-                LayoutInline {LABEL} at (0,0) size 43x19
-                  LayoutText {#text} at (442,292) size 43x19
-                    text run at (442,292) width 43: "Classic"
-                LayoutText {#text} at (485,292) size 4x19
-                  text run at (485,292) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (368,273) size 50x34
+              LayoutText {#text} at (418,292) size 4x19
+                text run at (418,292) width 4: " "
+              LayoutBlockFlow {INPUT} at (426,294) size 13x13
+              LayoutInline {LABEL} at (0,0) size 43x19
+                LayoutText {#text} at (442,292) size 43x19
+                  text run at (442,292) width 43: "Classic"
+              LayoutText {#text} at (485,292) size 4x19
+                text run at (485,292) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 111x19
             LayoutInline {DIV} at (0,0) size 111x19
-              LayoutInline {DIV} at (0,0) size 111x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (489,273) size 50x34
-                LayoutText {#text} at (539,292) size 4x19
-                  text run at (539,292) width 4: " "
-                LayoutBlockFlow {INPUT} at (547,294) size 13x13
-                LayoutInline {LABEL} at (0,0) size 37x19
-                  LayoutText {#text} at (563,292) size 37x19
-                    text run at (563,292) width 37: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (489,273) size 50x34
+              LayoutText {#text} at (539,292) size 4x19
+                text run at (539,292) width 4: " "
+              LayoutBlockFlow {INPUT} at (547,294) size 13x13
+              LayoutInline {LABEL} at (0,0) size 37x19
+                LayoutText {#text} at (563,292) size 37x19
+                  text run at (563,292) width 37: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 196x19
             LayoutInline {DIV} at (0,0) size 196x19
-              LayoutInline {DIV} at (0,0) size 196x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,312) size 50x34
-                LayoutText {#text} at (50,331) size 4x19
-                  text run at (50,331) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,333) size 13x13
-                LayoutInline {LABEL} at (0,0) size 118x19
-                  LayoutText {#text} at (74,331) size 118x19
-                    text run at (74,331) width 118: "Alexander's Empire"
-                LayoutText {#text} at (192,331) size 4x19
-                  text run at (192,331) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,312) size 50x34
+              LayoutText {#text} at (50,331) size 4x19
+                text run at (50,331) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,333) size 13x13
+              LayoutInline {LABEL} at (0,0) size 118x19
+                LayoutText {#text} at (74,331) size 118x19
+                  text run at (74,331) width 118: "Alexander's Empire"
+              LayoutText {#text} at (192,331) size 4x19
+                text run at (192,331) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 168x19
             LayoutInline {DIV} at (0,0) size 168x19
-              LayoutInline {DIV} at (0,0) size 168x19
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (196,312) size 50x34
-                LayoutText {#text} at (246,331) size 4x19
-                  text run at (246,331) width 4: " "
-                LayoutBlockFlow {INPUT} at (254,333) size 13x13
-                LayoutInline {LABEL} at (0,0) size 94x19
-                  LayoutText {#text} at (270,331) size 94x19
-                    text run at (270,331) width 94: "Ancient Greece"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x19 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (196,312) size 50x34
+              LayoutText {#text} at (246,331) size 4x19
+                text run at (246,331) width 4: " "
+              LayoutBlockFlow {INPUT} at (254,333) size 13x13
+              LayoutInline {LABEL} at (0,0) size 94x19
+                LayoutText {#text} at (270,331) size 94x19
+                  text run at (270,331) width 94: "Ancient Greece"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
 layer at (24,16) size 50x34 clip at (25,17) size 48x32
   LayoutBlockFlow {DIV} at (0,0) size 50x34 [border: (1px solid #C0C0C0)]
     LayoutImage (floating) {IMG} at (2,2) size 16x16
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt
index 61fe929..da7921d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/fieldsets-expected.txt
@@ -9,18 +9,15 @@
       LayoutFieldset {FIELDSET} at (16,36) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (34,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,157) size 4x19
         text run at (260,157) width 4: " "
       LayoutFieldset {FIELDSET} at (280,36) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (110,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,193) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,314) size 4x19
         text run at (260,314) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -30,18 +27,15 @@
       LayoutFieldset {FIELDSET} at (16,370) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,491) size 4x19
         text run at (260,491) width 4: " "
       LayoutFieldset {FIELDSET} at (280,370) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (90,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,527) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,648) size 4x19
         text run at (260,648) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -51,19 +45,16 @@
       LayoutFieldset {FIELDSET} at (16,704) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,34) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,933) size 5x19
         text run at (151,933) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,704) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,110) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,933) size 5x19
         text run at (307,933) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,704) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (462,933) size 5x19
         text run at (462,933) width 5: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -73,17 +64,14 @@
       LayoutFieldset {FIELDSET} at (16,989) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,14) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,1218) size 5x19
         text run at (151,1218) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,989) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,90) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,1218) size 5x19
         text run at (307,1218) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,989) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
index 61b869b..bab242a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-scrollable-iframe-expected.txt
@@ -18,11 +18,21 @@
         },
         {
           "object": "LayoutView #document",
+          "rect": [8, 193, 285, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 393, 285, 15],
           "reason": "scroll"
         },
         {
           "object": "LayoutView #document",
+          "rect": [93, 108, 200, 285],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
           "rect": [8, 193, 85, 15],
           "reason": "scroll"
         },
@@ -77,6 +87,10 @@
     {
       "object": "VerticalScrollbar",
       "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
index 517185f9..e422693 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/colourpicker-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/colourpicker-expected.png
index 252f577..7900367 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/colourpicker-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/carto.net/colourpicker-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/dominant-baseline-hanging-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/dominant-baseline-hanging-expected.png
index 5ecf6abb..75b4bef 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/dominant-baseline-hanging-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/inline-svg-in-xhtml-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/inline-svg-in-xhtml-expected.txt
index 4cf8ddb..67495c0b2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/inline-svg-in-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/inline-svg-in-xhtml-expected.txt
@@ -17,19 +17,18 @@
         LayoutBlockFlow {legend} at (14,0) size 84x20
           LayoutText {#text} at (2,0) size 80x19
             text run at (2,0) width 80: "HTML Form"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 688x110
-          LayoutBlockFlow {p} at (0,34) size 688x22
-            LayoutInline {label} at (0,0) size 99x19
-              LayoutText {#text} at (0,1) size 99x19
-                text run at (0,1) width 99: "Enter something:"
-            LayoutText {#text} at (99,1) size 4x19
-              text run at (99,1) width 4: " "
-            LayoutTextControl {input} at (103,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-            LayoutText {#text} at (0,0) size 0x0
-          LayoutBlockFlow {p} at (0,72) size 688x22
-            LayoutButton {button} at (0,0) size 65x22 [bgcolor=#C0C0C0] [border: (2px outset #C0C0C0)]
-              LayoutBlockFlow (anonymous) at (8,3) size 49x16
-                LayoutText {#text} at (0,0) size 49x16
-                  text run at (0,0) width 49: "Activate!"
+        LayoutBlockFlow {p} at (14,41.59) size 688x22
+          LayoutInline {label} at (0,0) size 99x19
+            LayoutText {#text} at (0,1) size 99x19
+              text run at (0,1) width 99: "Enter something:"
+          LayoutText {#text} at (99,1) size 4x19
+            text run at (99,1) width 4: " "
+          LayoutTextControl {input} at (103,0) size 154x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutText {#text} at (0,0) size 0x0
+        LayoutBlockFlow {p} at (14,79.59) size 688x22
+          LayoutButton {button} at (0,0) size 65x22 [bgcolor=#C0C0C0] [border: (2px outset #C0C0C0)]
+            LayoutBlockFlow (anonymous) at (8,3) size 49x16
+              LayoutText {#text} at (0,0) size 49x16
+                text run at (0,0) width 49: "Activate!"
 layer at (170,84) size 150x16
   LayoutBlockFlow {div} at (2,3) size 150x16
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-rotated-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-rotated-gradient-expected.png
index 0ffeade..a2801e7 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-rotated-gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-rotated-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-scalable-background-image2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-scalable-background-image2-expected.png
index c2133fc..b253373 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-scalable-background-image2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/wicd/test-scalable-background-image2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
index b652fd1..d2ad9830 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
index 26cfd49..9df0fc9 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-group-expected.png
index c7a894f..ac7f11c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-group-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-group-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index b0ee65b..48bd86f 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
new file mode 100644
index 0000000..384fa84d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
@@ -0,0 +1,93 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow P",
+          "rect": [8, 136, 784, 68],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 184, 32, 19],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 156, 32, 19],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutInline SPAN id='d'",
+          "rect": [8, 150, 4, 19],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 150, 4, 19],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 136, 4, 19],
+          "reason": "style change"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow P",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutInline SPAN id='d'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Hello'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/basic/generic-family-changes-expected.png
index 71ef9de..9ed4c6a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
index da70baa..a67a483 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
@@ -4,503 +4,502 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x370.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x351
-          LayoutBlockFlow {DIV} at (0,0) size 752x351
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x351
+          LayoutInline {DIV} at (0,0) size 122x18
             LayoutInline {DIV} at (0,0) size 122x18
-              LayoutInline {DIV} at (0,0) size 122x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,0) size 50x34
-                LayoutText {#text} at (50,20) size 4x18
-                  text run at (50,20) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (71,20) size 47x18
-                    text run at (71,20) width 47: "Classic"
-                LayoutText {#text} at (117,20) size 5x18
-                  text run at (117,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,0) size 50x34
+              LayoutText {#text} at (50,20) size 4x18
+                text run at (50,20) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (71,20) size 47x18
+                  text run at (71,20) width 47: "Classic"
+              LayoutText {#text} at (117,20) size 5x18
+                text run at (117,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 118x18
             LayoutInline {DIV} at (0,0) size 118x18
-              LayoutInline {DIV} at (0,0) size 118x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (121.97,0) size 50x34
-                LayoutText {#text} at (171,20) size 5x18
-                  text run at (171,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (178.84,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (193,20) size 42x18
-                    text run at (193,20) width 42: "Africa"
-                LayoutText {#text} at (234,20) size 5x18
-                  text run at (234,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (121.97,0) size 50x34
+              LayoutText {#text} at (171,20) size 5x18
+                text run at (171,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (178.84,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (193,20) size 42x18
+                  text run at (193,20) width 42: "Africa"
+              LayoutText {#text} at (234,20) size 5x18
+                text run at (234,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (238.58,0) size 50x34
-                LayoutText {#text} at (288,20) size 5x18
-                  text run at (288,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (295.45,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (310,20) size 128x18
-                    text run at (310,20) width 128: "Alexander's Empire"
-                LayoutText {#text} at (437,20) size 5x18
-                  text run at (437,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (238.58,0) size 50x34
+              LayoutText {#text} at (288,20) size 5x18
+                text run at (288,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (295.45,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (310,20) size 128x18
+                  text run at (310,20) width 128: "Alexander's Empire"
+              LayoutText {#text} at (437,20) size 5x18
+                text run at (437,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 176x18
             LayoutInline {DIV} at (0,0) size 176x18
-              LayoutInline {DIV} at (0,0) size 176x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (441.16,0) size 50x34
-                LayoutText {#text} at (491,20) size 5x18
-                  text run at (491,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (498.03,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (512,20) size 101x18
-                    text run at (512,20) width 101: "Ancient Greece"
-                LayoutText {#text} at (612,20) size 5x18
-                  text run at (612,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (441.16,0) size 50x34
+              LayoutText {#text} at (491,20) size 5x18
+                text run at (491,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (498.03,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (512,20) size 101x18
+                  text run at (512,20) width 101: "Ancient Greece"
+              LayoutText {#text} at (612,20) size 5x18
+                text run at (612,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 119x18
             LayoutInline {DIV} at (0,0) size 119x18
-              LayoutInline {DIV} at (0,0) size 119x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (616.84,0) size 50x34
-                LayoutText {#text} at (666,20) size 5x18
-                  text run at (666,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (673.72,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (688,20) size 47x18
-                    text run at (688,20) width 47: "Classic"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (616.84,0) size 50x34
+              LayoutText {#text} at (666,20) size 5x18
+                text run at (666,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (673.72,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (688,20) size 47x18
+                  text run at (688,20) width 47: "Classic"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x18
             LayoutInline {DIV} at (0,0) size 117x18
-              LayoutInline {DIV} at (0,0) size 117x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,39) size 50x34
-                LayoutText {#text} at (50,59) size 4x18
-                  text run at (50,59) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (71,59) size 42x18
-                    text run at (71,59) width 42: "Africa"
-                LayoutText {#text} at (112,59) size 5x18
-                  text run at (112,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,39) size 50x34
+              LayoutText {#text} at (50,59) size 4x18
+                text run at (50,59) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (71,59) size 42x18
+                  text run at (71,59) width 42: "Africa"
+              LayoutText {#text} at (112,59) size 5x18
+                text run at (112,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (116.61,39) size 50x34
-                LayoutText {#text} at (166,59) size 5x18
-                  text run at (166,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (173.48,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (188,59) size 128x18
-                    text run at (188,59) width 128: "Alexander's Empire"
-                LayoutText {#text} at (315,59) size 5x18
-                  text run at (315,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (116.61,39) size 50x34
+              LayoutText {#text} at (166,59) size 5x18
+                text run at (166,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (173.48,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (188,59) size 128x18
+                  text run at (188,59) width 128: "Alexander's Empire"
+              LayoutText {#text} at (315,59) size 5x18
+                text run at (315,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 176x18
             LayoutInline {DIV} at (0,0) size 176x18
-              LayoutInline {DIV} at (0,0) size 176x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (319.19,39) size 50x34
-                LayoutText {#text} at (369,59) size 5x18
-                  text run at (369,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (376.06,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (390,59) size 101x18
-                    text run at (390,59) width 101: "Ancient Greece"
-                LayoutText {#text} at (490,59) size 5x18
-                  text run at (490,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (319.19,39) size 50x34
+              LayoutText {#text} at (369,59) size 5x18
+                text run at (369,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (376.06,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (390,59) size 101x18
+                  text run at (390,59) width 101: "Ancient Greece"
+              LayoutText {#text} at (490,59) size 5x18
+                text run at (490,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (494.88,39) size 50x34
-                LayoutText {#text} at (544,59) size 5x18
-                  text run at (544,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (551.75,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (566,59) size 47x18
-                    text run at (566,59) width 47: "Classic"
-                LayoutText {#text} at (612,59) size 5x18
-                  text run at (612,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (494.88,39) size 50x34
+              LayoutText {#text} at (544,59) size 5x18
+                text run at (544,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (551.75,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (566,59) size 47x18
+                  text run at (566,59) width 47: "Classic"
+              LayoutText {#text} at (612,59) size 5x18
+                text run at (612,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (616.84,39) size 50x34
-                LayoutText {#text} at (666,59) size 5x18
-                  text run at (666,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (673.72,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (688,59) size 42x18
-                    text run at (688,59) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (616.84,39) size 50x34
+              LayoutText {#text} at (666,59) size 5x18
+                text run at (666,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (673.72,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (688,59) size 42x18
+                  text run at (688,59) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,78) size 50x34
-                LayoutText {#text} at (50,98) size 4x18
-                  text run at (50,98) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,98) size 128x18
-                    text run at (71,98) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,98) size 5x18
-                  text run at (198,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,78) size 50x34
+              LayoutText {#text} at (50,98) size 4x18
+                text run at (50,98) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,98) size 128x18
+                  text run at (71,98) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,98) size 5x18
+                text run at (198,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,78) size 50x34
-                LayoutText {#text} at (252,98) size 5x18
-                  text run at (252,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,98) size 101x18
-                    text run at (274,98) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,98) size 5x18
-                  text run at (374,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,78) size 50x34
+              LayoutText {#text} at (252,98) size 5x18
+                text run at (252,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,98) size 101x18
+                  text run at (274,98) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,98) size 5x18
+                text run at (374,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,78) size 50x34
-                LayoutText {#text} at (428,98) size 5x18
-                  text run at (428,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,98) size 47x18
-                    text run at (450,98) width 47: "Classic"
-                LayoutText {#text} at (496,98) size 5x18
-                  text run at (496,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,78) size 50x34
+              LayoutText {#text} at (428,98) size 5x18
+                text run at (428,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,98) size 47x18
+                  text run at (450,98) width 47: "Classic"
+              LayoutText {#text} at (496,98) size 5x18
+                text run at (496,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,78) size 50x34
-                LayoutText {#text} at (550,98) size 5x18
-                  text run at (550,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,98) size 42x18
-                    text run at (571,98) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,78) size 50x34
+              LayoutText {#text} at (550,98) size 5x18
+                text run at (550,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,98) size 42x18
+                  text run at (571,98) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,117) size 50x34
-                LayoutText {#text} at (50,137) size 4x18
-                  text run at (50,137) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,137) size 128x18
-                    text run at (71,137) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,137) size 5x18
-                  text run at (198,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,117) size 50x34
+              LayoutText {#text} at (50,137) size 4x18
+                text run at (50,137) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,137) size 128x18
+                  text run at (71,137) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,137) size 5x18
+                text run at (198,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,117) size 50x34
-                LayoutText {#text} at (252,137) size 5x18
-                  text run at (252,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,137) size 101x18
-                    text run at (274,137) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,137) size 5x18
-                  text run at (374,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,117) size 50x34
+              LayoutText {#text} at (252,137) size 5x18
+                text run at (252,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,137) size 101x18
+                  text run at (274,137) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,137) size 5x18
+                text run at (374,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,117) size 50x34
-                LayoutText {#text} at (428,137) size 5x18
-                  text run at (428,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,137) size 47x18
-                    text run at (450,137) width 47: "Classic"
-                LayoutText {#text} at (496,137) size 5x18
-                  text run at (496,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,117) size 50x34
+              LayoutText {#text} at (428,137) size 5x18
+                text run at (428,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,137) size 47x18
+                  text run at (450,137) width 47: "Classic"
+              LayoutText {#text} at (496,137) size 5x18
+                text run at (496,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,117) size 50x34
-                LayoutText {#text} at (550,137) size 5x18
-                  text run at (550,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,137) size 42x18
-                    text run at (571,137) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,117) size 50x34
+              LayoutText {#text} at (550,137) size 5x18
+                text run at (550,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,137) size 42x18
+                  text run at (571,137) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,156) size 50x34
-                LayoutText {#text} at (50,176) size 4x18
-                  text run at (50,176) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,176) size 128x18
-                    text run at (71,176) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,176) size 5x18
-                  text run at (198,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,156) size 50x34
+              LayoutText {#text} at (50,176) size 4x18
+                text run at (50,176) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,176) size 128x18
+                  text run at (71,176) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,176) size 5x18
+                text run at (198,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,156) size 50x34
-                LayoutText {#text} at (252,176) size 5x18
-                  text run at (252,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,176) size 101x18
-                    text run at (274,176) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,176) size 5x18
-                  text run at (374,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,156) size 50x34
+              LayoutText {#text} at (252,176) size 5x18
+                text run at (252,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,176) size 101x18
+                  text run at (274,176) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,176) size 5x18
+                text run at (374,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,156) size 50x34
-                LayoutText {#text} at (428,176) size 5x18
-                  text run at (428,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,176) size 47x18
-                    text run at (450,176) width 47: "Classic"
-                LayoutText {#text} at (496,176) size 5x18
-                  text run at (496,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,156) size 50x34
+              LayoutText {#text} at (428,176) size 5x18
+                text run at (428,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,176) size 47x18
+                  text run at (450,176) width 47: "Classic"
+              LayoutText {#text} at (496,176) size 5x18
+                text run at (496,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,156) size 50x34
-                LayoutText {#text} at (550,176) size 5x18
-                  text run at (550,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,176) size 42x18
-                    text run at (571,176) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,156) size 50x34
+              LayoutText {#text} at (550,176) size 5x18
+                text run at (550,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,176) size 42x18
+                  text run at (571,176) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,195) size 50x34
-                LayoutText {#text} at (50,215) size 4x18
-                  text run at (50,215) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,215) size 128x18
-                    text run at (71,215) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,215) size 5x18
-                  text run at (198,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,195) size 50x34
+              LayoutText {#text} at (50,215) size 4x18
+                text run at (50,215) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,215) size 128x18
+                  text run at (71,215) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,215) size 5x18
+                text run at (198,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,195) size 50x34
-                LayoutText {#text} at (252,215) size 5x18
-                  text run at (252,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,215) size 101x18
-                    text run at (274,215) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,215) size 5x18
-                  text run at (374,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,195) size 50x34
+              LayoutText {#text} at (252,215) size 5x18
+                text run at (252,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,215) size 101x18
+                  text run at (274,215) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,215) size 5x18
+                text run at (374,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,195) size 50x34
-                LayoutText {#text} at (428,215) size 5x18
-                  text run at (428,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,215) size 47x18
-                    text run at (450,215) width 47: "Classic"
-                LayoutText {#text} at (496,215) size 5x18
-                  text run at (496,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,195) size 50x34
+              LayoutText {#text} at (428,215) size 5x18
+                text run at (428,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,215) size 47x18
+                  text run at (450,215) width 47: "Classic"
+              LayoutText {#text} at (496,215) size 5x18
+                text run at (496,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,195) size 50x34
-                LayoutText {#text} at (550,215) size 5x18
-                  text run at (550,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,215) size 42x18
-                    text run at (571,215) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,195) size 50x34
+              LayoutText {#text} at (550,215) size 5x18
+                text run at (550,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,215) size 42x18
+                  text run at (571,215) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,234) size 50x34
-                LayoutText {#text} at (50,254) size 4x18
-                  text run at (50,254) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,254) size 128x18
-                    text run at (71,254) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,254) size 5x18
-                  text run at (198,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,234) size 50x34
+              LayoutText {#text} at (50,254) size 4x18
+                text run at (50,254) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,254) size 128x18
+                  text run at (71,254) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,254) size 5x18
+                text run at (198,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,234) size 50x34
-                LayoutText {#text} at (252,254) size 5x18
-                  text run at (252,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,254) size 101x18
-                    text run at (274,254) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,254) size 5x18
-                  text run at (374,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,234) size 50x34
+              LayoutText {#text} at (252,254) size 5x18
+                text run at (252,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,254) size 101x18
+                  text run at (274,254) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,254) size 5x18
+                text run at (374,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,234) size 50x34
-                LayoutText {#text} at (428,254) size 5x18
-                  text run at (428,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,254) size 47x18
-                    text run at (450,254) width 47: "Classic"
-                LayoutText {#text} at (496,254) size 5x18
-                  text run at (496,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,234) size 50x34
+              LayoutText {#text} at (428,254) size 5x18
+                text run at (428,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,254) size 47x18
+                  text run at (450,254) width 47: "Classic"
+              LayoutText {#text} at (496,254) size 5x18
+                text run at (496,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,234) size 50x34
-                LayoutText {#text} at (550,254) size 5x18
-                  text run at (550,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,254) size 42x18
-                    text run at (571,254) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,234) size 50x34
+              LayoutText {#text} at (550,254) size 5x18
+                text run at (550,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,254) size 42x18
+                  text run at (571,254) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,273) size 50x34
-                LayoutText {#text} at (50,293) size 4x18
-                  text run at (50,293) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,293) size 128x18
-                    text run at (71,293) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,293) size 5x18
-                  text run at (198,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,273) size 50x34
+              LayoutText {#text} at (50,293) size 4x18
+                text run at (50,293) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,293) size 128x18
+                  text run at (71,293) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,293) size 5x18
+                text run at (198,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,273) size 50x34
-                LayoutText {#text} at (252,293) size 5x18
-                  text run at (252,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,293) size 101x18
-                    text run at (274,293) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,293) size 5x18
-                  text run at (374,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,273) size 50x34
+              LayoutText {#text} at (252,293) size 5x18
+                text run at (252,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,293) size 101x18
+                  text run at (274,293) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,293) size 5x18
+                text run at (374,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.27,273) size 50x34
-                LayoutText {#text} at (428,293) size 5x18
-                  text run at (428,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.14,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,293) size 47x18
-                    text run at (450,293) width 47: "Classic"
-                LayoutText {#text} at (496,293) size 5x18
-                  text run at (496,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.27,273) size 50x34
+              LayoutText {#text} at (428,293) size 5x18
+                text run at (428,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.14,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,293) size 47x18
+                  text run at (450,293) width 47: "Classic"
+              LayoutText {#text} at (496,293) size 5x18
+                text run at (496,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.23,273) size 50x34
-                LayoutText {#text} at (550,293) size 5x18
-                  text run at (550,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.11,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (571,293) size 42x18
-                    text run at (571,293) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.23,273) size 50x34
+              LayoutText {#text} at (550,293) size 5x18
+                text run at (550,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.11,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (571,293) size 42x18
+                  text run at (571,293) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,312) size 50x34
-                LayoutText {#text} at (50,332) size 4x18
-                  text run at (50,332) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.88,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,332) size 128x18
-                    text run at (71,332) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,332) size 5x18
-                  text run at (198,332) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,312) size 50x34
+              LayoutText {#text} at (50,332) size 4x18
+                text run at (50,332) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.88,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,332) size 128x18
+                  text run at (71,332) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,332) size 5x18
+                text run at (198,332) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 173x18
             LayoutInline {DIV} at (0,0) size 173x18
-              LayoutInline {DIV} at (0,0) size 173x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.58,312) size 50x34
-                LayoutText {#text} at (252,332) size 5x18
-                  text run at (252,332) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.45,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,332) size 101x18
-                    text run at (274,332) width 101: "Ancient Greece"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.58,312) size 50x34
+              LayoutText {#text} at (252,332) size 5x18
+                text run at (252,332) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.45,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,332) size 101x18
+                  text run at (274,332) width 101: "Ancient Greece"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
 layer at (24,16) size 50x34 clip at (25,17) size 48x32
   LayoutBlockFlow {DIV} at (0,0) size 50x34 [border: (1px solid #C0C0C0)]
     LayoutImage (floating) {IMG} at (2,2) size 16x16
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/dominant-baseline-hanging-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/dominant-baseline-hanging-expected.png
index c5466451..a795b6d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/dominant-baseline-hanging-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/inline-svg-in-xhtml-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/inline-svg-in-xhtml-expected.txt
index 3f1d626..db683e2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/inline-svg-in-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/svg/custom/inline-svg-in-xhtml-expected.txt
@@ -17,19 +17,18 @@
         LayoutBlockFlow {legend} at (14,0) size 88x18
           LayoutText {#text} at (2,0) size 84x18
             text run at (2,0) width 84: "HTML Form"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 688x102
-          LayoutBlockFlow {p} at (0,32) size 688x19
-            LayoutInline {label} at (0,0) size 110x18
-              LayoutText {#text} at (0,0) size 110x18
-                text run at (0,0) width 110: "Enter something:"
-            LayoutText {#text} at (109,0) size 5x18
-              text run at (109,0) width 5: " "
-            LayoutTextControl {input} at (113.75,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-            LayoutText {#text} at (0,0) size 0x0
-          LayoutBlockFlow {p} at (0,67) size 688x19
-            LayoutButton {button} at (0,1) size 59.27x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
-              LayoutBlockFlow (anonymous) at (8,2) size 43.27x13
-                LayoutText {#text} at (0,0) size 44x13
-                  text run at (0,0) width 44: "Activate!"
+        LayoutBlockFlow {p} at (14,39.59) size 688x19
+          LayoutInline {label} at (0,0) size 110x18
+            LayoutText {#text} at (0,0) size 110x18
+              text run at (0,0) width 110: "Enter something:"
+          LayoutText {#text} at (109,0) size 5x18
+            text run at (109,0) width 5: " "
+          LayoutTextControl {input} at (113.75,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutText {#text} at (0,0) size 0x0
+        LayoutBlockFlow {p} at (14,74.59) size 688x19
+          LayoutButton {button} at (0,1) size 59.27x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
+            LayoutBlockFlow (anonymous) at (8,2) size 43.27x13
+              LayoutText {#text} at (0,0) size 44x13
+                text run at (0,0) width 44: "Activate!"
 layer at (182,82) size 125x13
   LayoutBlockFlow {div} at (3,3) size 125x13
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/block/basic/fieldset-stretch-to-legend-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/block/basic/fieldset-stretch-to-legend-expected.png
new file mode 100644
index 0000000..68f57a65
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/block/basic/fieldset-stretch-to-legend-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/fieldset/fieldset-align-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/fieldset/fieldset-align-expected.txt
index 9bff39f9..a64b251 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/fieldset/fieldset-align-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/fieldset/fieldset-align-expected.txt
@@ -10,29 +10,29 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x18
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,88.59) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x18
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,143.19) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x18
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,197.78) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x18
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,260.38) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,278.38) size 769x18
@@ -46,29 +46,29 @@
           LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x18
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (614,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (614,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,54.59) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x18
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (614,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (614,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,109.19) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x18
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (614,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (614,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,163.78) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (624.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x18
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (614,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (614,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,530.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
@@ -81,29 +81,29 @@
         LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x18
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,674.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x18
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,758.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x18
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,842.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (17,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x18
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,926.75) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,944.75) size 769x18
@@ -117,29 +117,29 @@
           LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x18
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (582,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (582,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,84) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x18
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (582,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (582,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,168) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x18
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (582,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (582,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,252) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (595.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x18
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (582,16) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (582,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,1314.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/basic/generic-family-changes-expected.png
index cbbc762..4f44b53 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
index f7dcae9..5f11d5a7 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
@@ -4,503 +4,502 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x370.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x351
-          LayoutBlockFlow {DIV} at (0,0) size 752x351
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x351
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,0) size 50x34
-                LayoutText {#text} at (50,20) size 4x18
-                  text run at (50,20) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 48x18
-                  LayoutText {#text} at (71,20) size 48x18
-                    text run at (71,20) width 48: "Classic"
-                LayoutText {#text} at (118,20) size 5x18
-                  text run at (118,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,0) size 50x34
+              LayoutText {#text} at (50,20) size 4x18
+                text run at (50,20) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 48x18
+                LayoutText {#text} at (71,20) size 48x18
+                  text run at (71,20) width 48: "Classic"
+              LayoutText {#text} at (118,20) size 5x18
+                text run at (118,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x18
             LayoutInline {DIV} at (0,0) size 117x18
-              LayoutInline {DIV} at (0,0) size 117x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (122.03,0) size 50x34
-                LayoutText {#text} at (172,20) size 5x18
-                  text run at (172,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (178.94,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (193,20) size 42x18
-                    text run at (193,20) width 42: "Africa"
-                LayoutText {#text} at (234,20) size 5x18
-                  text run at (234,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (122.03,0) size 50x34
+              LayoutText {#text} at (172,20) size 5x18
+                text run at (172,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (178.94,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (193,20) size 42x18
+                  text run at (193,20) width 42: "Africa"
+              LayoutText {#text} at (234,20) size 5x18
+                text run at (234,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (238.70,0) size 50x34
-                LayoutText {#text} at (288,20) size 5x18
-                  text run at (288,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (295.61,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (310,20) size 128x18
-                    text run at (310,20) width 128: "Alexander's Empire"
-                LayoutText {#text} at (437,20) size 5x18
-                  text run at (437,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (238.70,0) size 50x34
+              LayoutText {#text} at (288,20) size 5x18
+                text run at (288,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (295.61,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (310,20) size 128x18
+                  text run at (310,20) width 128: "Alexander's Empire"
+              LayoutText {#text} at (437,20) size 5x18
+                text run at (437,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (441.34,0) size 50x34
-                LayoutText {#text} at (491,20) size 5x18
-                  text run at (491,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (498.25,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (513,20) size 101x18
-                    text run at (513,20) width 101: "Ancient Greece"
-                LayoutText {#text} at (613,20) size 5x18
-                  text run at (613,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (441.34,0) size 50x34
+              LayoutText {#text} at (491,20) size 5x18
+                text run at (491,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (498.25,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (513,20) size 101x18
+                  text run at (513,20) width 101: "Ancient Greece"
+              LayoutText {#text} at (613,20) size 5x18
+                text run at (613,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 119x18
             LayoutInline {DIV} at (0,0) size 119x18
-              LayoutInline {DIV} at (0,0) size 119x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (617.09,0) size 50x34
-                LayoutText {#text} at (667,20) size 5x18
-                  text run at (667,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (674,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 48x18
-                  LayoutText {#text} at (688,20) size 48x18
-                    text run at (688,20) width 48: "Classic"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (617.09,0) size 50x34
+              LayoutText {#text} at (667,20) size 5x18
+                text run at (667,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (674,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 48x18
+                LayoutText {#text} at (688,20) size 48x18
+                  text run at (688,20) width 48: "Classic"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x18
             LayoutInline {DIV} at (0,0) size 117x18
-              LayoutInline {DIV} at (0,0) size 117x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,39) size 50x34
-                LayoutText {#text} at (50,59) size 4x18
-                  text run at (50,59) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (71,59) size 42x18
-                    text run at (71,59) width 42: "Africa"
-                LayoutText {#text} at (112,59) size 5x18
-                  text run at (112,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,39) size 50x34
+              LayoutText {#text} at (50,59) size 4x18
+                text run at (50,59) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (71,59) size 42x18
+                  text run at (71,59) width 42: "Africa"
+              LayoutText {#text} at (112,59) size 5x18
+                text run at (112,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (116.67,39) size 50x34
-                LayoutText {#text} at (166,59) size 5x18
-                  text run at (166,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (173.58,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (188,59) size 128x18
-                    text run at (188,59) width 128: "Alexander's Empire"
-                LayoutText {#text} at (315,59) size 5x18
-                  text run at (315,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (116.67,39) size 50x34
+              LayoutText {#text} at (166,59) size 5x18
+                text run at (166,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (173.58,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (188,59) size 128x18
+                  text run at (188,59) width 128: "Alexander's Empire"
+              LayoutText {#text} at (315,59) size 5x18
+                text run at (315,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (319.31,39) size 50x34
-                LayoutText {#text} at (369,59) size 5x18
-                  text run at (369,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (376.22,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (391,59) size 101x18
-                    text run at (391,59) width 101: "Ancient Greece"
-                LayoutText {#text} at (491,59) size 5x18
-                  text run at (491,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (319.31,39) size 50x34
+              LayoutText {#text} at (369,59) size 5x18
+                text run at (369,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (376.22,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (391,59) size 101x18
+                  text run at (391,59) width 101: "Ancient Greece"
+              LayoutText {#text} at (491,59) size 5x18
+                text run at (491,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (495.06,39) size 50x34
-                LayoutText {#text} at (545,59) size 5x18
-                  text run at (545,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (551.97,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 48x18
-                  LayoutText {#text} at (566,59) size 48x18
-                    text run at (566,59) width 48: "Classic"
-                LayoutText {#text} at (613,59) size 5x18
-                  text run at (613,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (495.06,39) size 50x34
+              LayoutText {#text} at (545,59) size 5x18
+                text run at (545,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (551.97,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 48x18
+                LayoutText {#text} at (566,59) size 48x18
+                  text run at (566,59) width 48: "Classic"
+              LayoutText {#text} at (613,59) size 5x18
+                text run at (613,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (617.09,39) size 50x34
-                LayoutText {#text} at (667,59) size 5x18
-                  text run at (667,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (674,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (688,59) size 42x18
-                    text run at (688,59) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (617.09,39) size 50x34
+              LayoutText {#text} at (667,59) size 5x18
+                text run at (667,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (674,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (688,59) size 42x18
+                  text run at (688,59) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,78) size 50x34
-                LayoutText {#text} at (50,98) size 4x18
-                  text run at (50,98) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,98) size 128x18
-                    text run at (71,98) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,98) size 5x18
-                  text run at (198,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,78) size 50x34
+              LayoutText {#text} at (50,98) size 4x18
+                text run at (50,98) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,98) size 128x18
+                  text run at (71,98) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,98) size 5x18
+                text run at (198,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,78) size 50x34
-                LayoutText {#text} at (252,98) size 5x18
-                  text run at (252,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,98) size 101x18
-                    text run at (274,98) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,98) size 5x18
-                  text run at (374,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,78) size 50x34
+              LayoutText {#text} at (252,98) size 5x18
+                text run at (252,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,98) size 101x18
+                  text run at (274,98) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,98) size 5x18
+                text run at (374,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,78) size 50x34
-                LayoutText {#text} at (428,98) size 5x18
-                  text run at (428,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,98) size 47x18
-                    text run at (450,98) width 47: "Classic"
-                LayoutText {#text} at (496,98) size 5x18
-                  text run at (496,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,78) size 50x34
+              LayoutText {#text} at (428,98) size 5x18
+                text run at (428,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,98) size 47x18
+                  text run at (450,98) width 47: "Classic"
+              LayoutText {#text} at (496,98) size 5x18
+                text run at (496,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,78) size 50x34
-                LayoutText {#text} at (550,98) size 5x18
-                  text run at (550,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,98) size 42x18
-                    text run at (572,98) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,78) size 50x34
+              LayoutText {#text} at (550,98) size 5x18
+                text run at (550,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,98) size 42x18
+                  text run at (572,98) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,117) size 50x34
-                LayoutText {#text} at (50,137) size 4x18
-                  text run at (50,137) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,137) size 128x18
-                    text run at (71,137) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,137) size 5x18
-                  text run at (198,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,117) size 50x34
+              LayoutText {#text} at (50,137) size 4x18
+                text run at (50,137) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,137) size 128x18
+                  text run at (71,137) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,137) size 5x18
+                text run at (198,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,117) size 50x34
-                LayoutText {#text} at (252,137) size 5x18
-                  text run at (252,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,137) size 101x18
-                    text run at (274,137) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,137) size 5x18
-                  text run at (374,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,117) size 50x34
+              LayoutText {#text} at (252,137) size 5x18
+                text run at (252,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,137) size 101x18
+                  text run at (274,137) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,137) size 5x18
+                text run at (374,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,117) size 50x34
-                LayoutText {#text} at (428,137) size 5x18
-                  text run at (428,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,137) size 47x18
-                    text run at (450,137) width 47: "Classic"
-                LayoutText {#text} at (496,137) size 5x18
-                  text run at (496,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,117) size 50x34
+              LayoutText {#text} at (428,137) size 5x18
+                text run at (428,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,137) size 47x18
+                  text run at (450,137) width 47: "Classic"
+              LayoutText {#text} at (496,137) size 5x18
+                text run at (496,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,117) size 50x34
-                LayoutText {#text} at (550,137) size 5x18
-                  text run at (550,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,137) size 42x18
-                    text run at (572,137) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,117) size 50x34
+              LayoutText {#text} at (550,137) size 5x18
+                text run at (550,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,137) size 42x18
+                  text run at (572,137) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,156) size 50x34
-                LayoutText {#text} at (50,176) size 4x18
-                  text run at (50,176) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,176) size 128x18
-                    text run at (71,176) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,176) size 5x18
-                  text run at (198,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,156) size 50x34
+              LayoutText {#text} at (50,176) size 4x18
+                text run at (50,176) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,176) size 128x18
+                  text run at (71,176) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,176) size 5x18
+                text run at (198,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,156) size 50x34
-                LayoutText {#text} at (252,176) size 5x18
-                  text run at (252,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,176) size 101x18
-                    text run at (274,176) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,176) size 5x18
-                  text run at (374,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,156) size 50x34
+              LayoutText {#text} at (252,176) size 5x18
+                text run at (252,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,176) size 101x18
+                  text run at (274,176) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,176) size 5x18
+                text run at (374,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,156) size 50x34
-                LayoutText {#text} at (428,176) size 5x18
-                  text run at (428,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,176) size 47x18
-                    text run at (450,176) width 47: "Classic"
-                LayoutText {#text} at (496,176) size 5x18
-                  text run at (496,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,156) size 50x34
+              LayoutText {#text} at (428,176) size 5x18
+                text run at (428,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,176) size 47x18
+                  text run at (450,176) width 47: "Classic"
+              LayoutText {#text} at (496,176) size 5x18
+                text run at (496,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,156) size 50x34
-                LayoutText {#text} at (550,176) size 5x18
-                  text run at (550,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,176) size 42x18
-                    text run at (572,176) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,156) size 50x34
+              LayoutText {#text} at (550,176) size 5x18
+                text run at (550,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,176) size 42x18
+                  text run at (572,176) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,195) size 50x34
-                LayoutText {#text} at (50,215) size 4x18
-                  text run at (50,215) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,215) size 128x18
-                    text run at (71,215) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,215) size 5x18
-                  text run at (198,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,195) size 50x34
+              LayoutText {#text} at (50,215) size 4x18
+                text run at (50,215) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,215) size 128x18
+                  text run at (71,215) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,215) size 5x18
+                text run at (198,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,195) size 50x34
-                LayoutText {#text} at (252,215) size 5x18
-                  text run at (252,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,215) size 101x18
-                    text run at (274,215) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,215) size 5x18
-                  text run at (374,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,195) size 50x34
+              LayoutText {#text} at (252,215) size 5x18
+                text run at (252,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,215) size 101x18
+                  text run at (274,215) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,215) size 5x18
+                text run at (374,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,195) size 50x34
-                LayoutText {#text} at (428,215) size 5x18
-                  text run at (428,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,215) size 47x18
-                    text run at (450,215) width 47: "Classic"
-                LayoutText {#text} at (496,215) size 5x18
-                  text run at (496,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,195) size 50x34
+              LayoutText {#text} at (428,215) size 5x18
+                text run at (428,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,215) size 47x18
+                  text run at (450,215) width 47: "Classic"
+              LayoutText {#text} at (496,215) size 5x18
+                text run at (496,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,195) size 50x34
-                LayoutText {#text} at (550,215) size 5x18
-                  text run at (550,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,215) size 42x18
-                    text run at (572,215) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,195) size 50x34
+              LayoutText {#text} at (550,215) size 5x18
+                text run at (550,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,215) size 42x18
+                  text run at (572,215) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,234) size 50x34
-                LayoutText {#text} at (50,254) size 4x18
-                  text run at (50,254) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,254) size 128x18
-                    text run at (71,254) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,254) size 5x18
-                  text run at (198,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,234) size 50x34
+              LayoutText {#text} at (50,254) size 4x18
+                text run at (50,254) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,254) size 128x18
+                  text run at (71,254) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,254) size 5x18
+                text run at (198,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,234) size 50x34
-                LayoutText {#text} at (252,254) size 5x18
-                  text run at (252,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,254) size 101x18
-                    text run at (274,254) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,254) size 5x18
-                  text run at (374,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,234) size 50x34
+              LayoutText {#text} at (252,254) size 5x18
+                text run at (252,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,254) size 101x18
+                  text run at (274,254) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,254) size 5x18
+                text run at (374,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,234) size 50x34
-                LayoutText {#text} at (428,254) size 5x18
-                  text run at (428,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,254) size 47x18
-                    text run at (450,254) width 47: "Classic"
-                LayoutText {#text} at (496,254) size 5x18
-                  text run at (496,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,234) size 50x34
+              LayoutText {#text} at (428,254) size 5x18
+                text run at (428,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,254) size 47x18
+                  text run at (450,254) width 47: "Classic"
+              LayoutText {#text} at (496,254) size 5x18
+                text run at (496,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,234) size 50x34
-                LayoutText {#text} at (550,254) size 5x18
-                  text run at (550,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,254) size 42x18
-                    text run at (572,254) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,234) size 50x34
+              LayoutText {#text} at (550,254) size 5x18
+                text run at (550,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,254) size 42x18
+                  text run at (572,254) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,273) size 50x34
-                LayoutText {#text} at (50,293) size 4x18
-                  text run at (50,293) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,293) size 128x18
-                    text run at (71,293) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,293) size 5x18
-                  text run at (198,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,273) size 50x34
+              LayoutText {#text} at (50,293) size 4x18
+                text run at (50,293) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,293) size 128x18
+                  text run at (71,293) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,293) size 5x18
+                text run at (198,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,273) size 50x34
-                LayoutText {#text} at (252,293) size 5x18
-                  text run at (252,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,293) size 101x18
-                    text run at (274,293) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,293) size 5x18
-                  text run at (374,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,273) size 50x34
+              LayoutText {#text} at (252,293) size 5x18
+                text run at (252,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,293) size 101x18
+                  text run at (274,293) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,293) size 5x18
+                text run at (374,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.39,273) size 50x34
-                LayoutText {#text} at (428,293) size 5x18
-                  text run at (428,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.30,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,293) size 47x18
-                    text run at (450,293) width 47: "Classic"
-                LayoutText {#text} at (496,293) size 5x18
-                  text run at (496,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.39,273) size 50x34
+              LayoutText {#text} at (428,293) size 5x18
+                text run at (428,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.30,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,293) size 47x18
+                  text run at (450,293) width 47: "Classic"
+              LayoutText {#text} at (496,293) size 5x18
+                text run at (496,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.42,273) size 50x34
-                LayoutText {#text} at (550,293) size 5x18
-                  text run at (550,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.33,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (572,293) size 42x18
-                    text run at (572,293) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.42,273) size 50x34
+              LayoutText {#text} at (550,293) size 5x18
+                text run at (550,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.33,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (572,293) size 42x18
+                  text run at (572,293) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,312) size 50x34
-                LayoutText {#text} at (50,332) size 4x18
-                  text run at (50,332) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.91,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,332) size 128x18
-                    text run at (71,332) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,332) size 5x18
-                  text run at (198,332) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,312) size 50x34
+              LayoutText {#text} at (50,332) size 4x18
+                text run at (50,332) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.91,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,332) size 128x18
+                  text run at (71,332) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,332) size 5x18
+                text run at (198,332) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 173x18
             LayoutInline {DIV} at (0,0) size 173x18
-              LayoutInline {DIV} at (0,0) size 173x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.64,312) size 50x34
-                LayoutText {#text} at (252,332) size 5x18
-                  text run at (252,332) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.55,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,332) size 101x18
-                    text run at (274,332) width 101: "Ancient Greece"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.64,312) size 50x34
+              LayoutText {#text} at (252,332) size 5x18
+                text run at (252,332) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.55,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,332) size 101x18
+                  text run at (274,332) width 101: "Ancient Greece"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
 layer at (24,16) size 50x34 clip at (25,17) size 48x32
   LayoutBlockFlow {DIV} at (0,0) size 50x34 [border: (1px solid #C0C0C0)]
     LayoutImage (floating) {IMG} at (2,2) size 16x16
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/inline-svg-in-xhtml-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/inline-svg-in-xhtml-expected.txt
index fb6b8a91..1ffb273 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/inline-svg-in-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/inline-svg-in-xhtml-expected.txt
@@ -17,19 +17,18 @@
         LayoutBlockFlow {legend} at (14,0) size 88x18
           LayoutText {#text} at (2,0) size 84x18
             text run at (2,0) width 84: "HTML Form"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 688x102
-          LayoutBlockFlow {p} at (0,32) size 688x19
-            LayoutInline {label} at (0,0) size 110x18
-              LayoutText {#text} at (0,0) size 110x18
-                text run at (0,0) width 110: "Enter something:"
-            LayoutText {#text} at (109,0) size 5x18
-              text run at (109,0) width 5: " "
-            LayoutTextControl {input} at (113.75,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-            LayoutText {#text} at (0,0) size 0x0
-          LayoutBlockFlow {p} at (0,67) size 688x19
-            LayoutButton {button} at (0,1) size 62.02x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
-              LayoutBlockFlow (anonymous) at (8,2) size 46.02x13
-                LayoutText {#text} at (0,0) size 46x13
-                  text run at (0,0) width 46: "Activate!"
+        LayoutBlockFlow {p} at (14,39.59) size 688x19
+          LayoutInline {label} at (0,0) size 110x18
+            LayoutText {#text} at (0,0) size 110x18
+              text run at (0,0) width 110: "Enter something:"
+          LayoutText {#text} at (109,0) size 5x18
+            text run at (109,0) width 5: " "
+          LayoutTextControl {input} at (113.75,0) size 123x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutText {#text} at (0,0) size 0x0
+        LayoutBlockFlow {p} at (14,74.59) size 688x19
+          LayoutButton {button} at (0,1) size 62.02x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
+            LayoutBlockFlow (anonymous) at (8,2) size 46.02x13
+              LayoutText {#text} at (0,0) size 46x13
+                text run at (0,0) width 46: "Activate!"
 layer at (182,82) size 117x13
   LayoutBlockFlow {div} at (3,3) size 117x13
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png
index c659b982..53561fe 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/size/backgroundSize16-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/size/backgroundSize16-expected.png
index 3068980c..ba0c3f86 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/size/backgroundSize16-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/size/backgroundSize16-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/block/basic/fieldset-stretch-to-legend-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/block/basic/fieldset-stretch-to-legend-expected.png
new file mode 100644
index 0000000..5b11206
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/block/basic/fieldset-stretch-to-legend-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/css/fieldset-display-row-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/css/fieldset-display-row-expected.txt
index 140adf166..a896daf 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/css/fieldset-display-row-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/css/fieldset-display-row-expected.txt
@@ -4,6 +4,5 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x37.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x18
-          LayoutText {#text} at (0,0) size 384x18
-            text run at (0,0) width 384: "If you can see this fieldset without crashing, then all is well."
+        LayoutText {#text} at (14,8) size 384x18
+          text run at (14,8) width 384: "If you can see this fieldset without crashing, then all is well."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/006-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/006-expected.txt
index dc020d8..3831666 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/006-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/006-expected.txt
@@ -8,7 +8,6 @@
           LayoutBlockFlow {LEGEND} at (42,0) size 124.66x18
             LayoutText {#text} at (2,0) size 121x18
               text run at (2,0) width 121: "Test without forms"
-          LayoutBlockFlow (anonymous) at (22,15.59) size 726x26
-            LayoutBlockFlow {DIV} at (0,8) size 726x18
-              LayoutText {#text} at (0,0) size 282x18
-                text run at (0,0) width 282: "A DIV inside a fieldset, not related to forms"
+          LayoutBlockFlow {DIV} at (22,23.59) size 726x18
+            LayoutText {#text} at (0,0) size 282x18
+              text run at (0,0) width 282: "A DIV inside a fieldset, not related to forms"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/007-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/007-expected.txt
index fa3c167..f4b2c90 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/007-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/007-expected.txt
@@ -7,19 +7,19 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 87.09x18
           LayoutText {#text} at (2,0) size 84x18
             text run at (2,0) width 84: "Number One"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 87.09x52
-          LayoutText {#text} at (0,16) size 81x18
-            text run at (0,16) width 81: "Hello world."
-          LayoutBR {BR} at (80,30) size 1x0
-          LayoutText {#text} at (0,34) size 81x18
-            text run at (0,34) width 81: "Hello world."
-      LayoutText {#text} at (119,58) size 5x18
-        text run at (119,58) width 5: " "
+        LayoutBlockFlow (anonymous) at (14,23.59) size 87.09x36
+          LayoutText {#text} at (0,0) size 81x18
+            text run at (0,0) width 81: "Hello world."
+          LayoutBR {BR} at (80,14) size 1x0
+          LayoutText {#text} at (0,18) size 81x18
+            text run at (0,18) width 81: "Hello world."
+      LayoutText {#text} at (119,41) size 5x18
+        text run at (119,41) width 5: " "
       LayoutFieldset {FIELDSET} at (125.09,18) size 116.64x53.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 88.64x18
           LayoutText {#text} at (2,0) size 85x18
             text run at (2,0) width 85: "Number Two"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 88.64x34
-          LayoutText {#text} at (0,16) size 81x18
-            text run at (0,16) width 81: "Hello world."
+        LayoutBlockFlow (anonymous) at (14,23.59) size 88.64x18
+          LayoutText {#text} at (0,0) size 81x18
+            text run at (0,0) width 81: "Hello world."
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-align-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-align-expected.txt
index d4dd21f..7c225fe 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-align-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-align-expected.txt
@@ -10,29 +10,29 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x18
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,88.59) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x18
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,143.19) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x18
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,197.78) size 765x54.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x18
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,260.38) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,278.38) size 769x18
@@ -46,29 +46,29 @@
           LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x18
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (606,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (606,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,54.59) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x18
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (606,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (606,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,109.19) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x18
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (606,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (606,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,163.78) size 765x54.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (624.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x18
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x35
-            LayoutTextControl {INPUT} at (606,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x19
+            LayoutTextControl {INPUT} at (606,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,530.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
@@ -81,29 +81,29 @@
         LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x18
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,674.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x18
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,758.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x18
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,842.75) size 739x69 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (17,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x18
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x35
-          LayoutTextControl {INPUT} at (0,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x19
+          LayoutTextControl {INPUT} at (0,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,926.75) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,944.75) size 769x18
@@ -117,29 +117,29 @@
           LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x18
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (574,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (574,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,84) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x18
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (574,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (574,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,168) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x18
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (574,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (574,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,252) size 739x69 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (595.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x18
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x35
-            LayoutTextControl {INPUT} at (574,16) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x19
+            LayoutTextControl {INPUT} at (574,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,1314.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
index 55e1e05..97b3f6cd 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
@@ -17,4 +17,3 @@
       LayoutBlockFlow {FORM} at (0,68) size 784x53.59
         LayoutFieldset {FIELDSET} at (2,0) size 780x53.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 36x36 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 752x34
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-with-float-expected.txt
index 5d3544e..f430b90 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-with-float-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/fieldset-with-float-expected.txt
@@ -4,8 +4,7 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x335.59
     LayoutBlockFlow {BODY} at (8,8) size 784x319.59
       LayoutFieldset {FIELDSET} at (2,0) size 780x319.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x300
-          LayoutBlockFlow (floating) {DIV} at (0,0) size 300x300 [bgcolor=#008000]
-          LayoutBlockFlow {DIV} at (0,0) size 752x50
-            LayoutText {#text} at (300,0) size 88x18
-              text run at (300,0) width 88: "Other content"
+        LayoutBlockFlow (floating) {DIV} at (14,7.59) size 300x300 [bgcolor=#008000]
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x50
+          LayoutText {#text} at (300,0) size 88x18
+            text run at (300,0) width 88: "Other content"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/float-before-fieldset-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/float-before-fieldset-expected.txt
index 1f0c8a6..7c6e0c1f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/float-before-fieldset-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/fieldset/float-before-fieldset-expected.txt
@@ -8,6 +8,6 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 80.88x18
           LayoutText {#text} at (2,0) size 77x18
             text run at (2,0) width 77: "Hello world"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 704x34
-          LayoutText {#text} at (0,16) size 142x18
-            text run at (0,16) width 142: "Some fieldset content."
+        LayoutBlockFlow (anonymous) at (14,23.59) size 704x18
+          LayoutText {#text} at (0,0) size 142x18
+            text run at (0,0) width 142: "Some fieldset content."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/ruby/rubyDOM-remove-text2-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/ruby/rubyDOM-remove-text2-expected.txt
index d65e9b5..f3f9672 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/ruby/rubyDOM-remove-text2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/ruby/rubyDOM-remove-text2-expected.txt
@@ -20,7 +20,6 @@
             LayoutRubyText {RT} at (0,-10) size 107.30x10
               LayoutText {#text} at (0,0) size 108x10
                 text run at (0,0) width 108: "Hyper-text Markup Language"
-            LayoutRubyBase (anonymous) at (0,0) size 107.30x0
           LayoutRubyRun (anonymous) at (264.44,0) size 8x18
             LayoutRubyBase (anonymous) at (0,0) size 8x18
               LayoutText {#text} at (0,0) size 8x18
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.png
index fd9b689..15d0081f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.txt
index 6658e2cf..fb991598 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/basic/generic-family-changes-expected.txt
@@ -4,9 +4,9 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x302
     LayoutBlockFlow {BODY} at (8,8) size 784x278
       LayoutBlockFlow (anonymous) at (0,0) size 784x54
-        LayoutText {#text} at (0,0) size 755x54
-          text run at (0,0) width 538: "Tests of WebKit's intepretation of font sizes when no absolute font size is specified. "
-          text run at (537,0) width 218: "Percentages and logical keywords"
+        LayoutText {#text} at (0,0) size 761x54
+          text run at (0,0) width 544: "Tests of WebKit's interpretation of font sizes when no absolute font size is specified. "
+          text run at (543,0) width 218: "Percentages and logical keywords"
           text run at (0,18) width 202: "scale to reflect the family type. "
           text run at (201,18) width 539: "Opera 9 matches this behavior as well (except it has a bug with multiple font-family"
           text run at (0,36) width 216: "mappings as in the first example)."
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
index af15cc6..54e36c3 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
@@ -4,503 +4,502 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x370.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x351
-          LayoutBlockFlow {DIV} at (0,0) size 752x351
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x351
+          LayoutInline {DIV} at (0,0) size 122x18
             LayoutInline {DIV} at (0,0) size 122x18
-              LayoutInline {DIV} at (0,0) size 122x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,0) size 50x34
-                LayoutText {#text} at (50,20) size 4x18
-                  text run at (50,20) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (71,20) size 47x18
-                    text run at (71,20) width 47: "Classic"
-                LayoutText {#text} at (118,20) size 4x18
-                  text run at (118,20) width 4: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,0) size 50x34
+              LayoutText {#text} at (50,20) size 4x18
+                text run at (50,20) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (71,20) size 47x18
+                  text run at (71,20) width 47: "Classic"
+              LayoutText {#text} at (118,20) size 4x18
+                text run at (118,20) width 4: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x18
             LayoutInline {DIV} at (0,0) size 117x18
-              LayoutInline {DIV} at (0,0) size 117x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (122,0) size 50x34
-                LayoutText {#text} at (172,20) size 4x18
-                  text run at (172,20) width 4: " "
-                LayoutBlockFlow {INPUT} at (178.89,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (193,20) size 42x18
-                    text run at (193,20) width 42: "Africa"
-                LayoutText {#text} at (234,20) size 5x18
-                  text run at (234,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (122,0) size 50x34
+              LayoutText {#text} at (172,20) size 4x18
+                text run at (172,20) width 4: " "
+              LayoutBlockFlow {INPUT} at (178.89,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (193,20) size 42x18
+                  text run at (193,20) width 42: "Africa"
+              LayoutText {#text} at (234,20) size 5x18
+                text run at (234,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (238.64,0) size 50x34
-                LayoutText {#text} at (288,20) size 5x18
-                  text run at (288,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (295.53,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (310,20) size 128x18
-                    text run at (310,20) width 128: "Alexander's Empire"
-                LayoutText {#text} at (437,20) size 5x18
-                  text run at (437,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (238.64,0) size 50x34
+              LayoutText {#text} at (288,20) size 5x18
+                text run at (288,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (295.53,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (310,20) size 128x18
+                  text run at (310,20) width 128: "Alexander's Empire"
+              LayoutText {#text} at (437,20) size 5x18
+                text run at (437,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 176x18
             LayoutInline {DIV} at (0,0) size 176x18
-              LayoutInline {DIV} at (0,0) size 176x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (441.25,0) size 50x34
-                LayoutText {#text} at (491,20) size 5x18
-                  text run at (491,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (498.14,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 100x18
-                  LayoutText {#text} at (513,20) size 100x18
-                    text run at (513,20) width 100: "Ancient Greece"
-                LayoutText {#text} at (612,20) size 5x18
-                  text run at (612,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (441.25,0) size 50x34
+              LayoutText {#text} at (491,20) size 5x18
+                text run at (491,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (498.14,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 100x18
+                LayoutText {#text} at (513,20) size 100x18
+                  text run at (513,20) width 100: "Ancient Greece"
+              LayoutText {#text} at (612,20) size 5x18
+                text run at (612,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 119x18
             LayoutInline {DIV} at (0,0) size 119x18
-              LayoutInline {DIV} at (0,0) size 119x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (616.97,0) size 50x34
-                LayoutText {#text} at (666,20) size 5x18
-                  text run at (666,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (673.86,24) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (688,20) size 47x18
-                    text run at (688,20) width 47: "Classic"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (616.97,0) size 50x34
+              LayoutText {#text} at (666,20) size 5x18
+                text run at (666,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (673.86,24) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (688,20) size 47x18
+                  text run at (688,20) width 47: "Classic"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 117x18
             LayoutInline {DIV} at (0,0) size 117x18
-              LayoutInline {DIV} at (0,0) size 117x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,39) size 50x34
-                LayoutText {#text} at (50,59) size 4x18
-                  text run at (50,59) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (71,59) size 42x18
-                    text run at (71,59) width 42: "Africa"
-                LayoutText {#text} at (112,59) size 5x18
-                  text run at (112,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,39) size 50x34
+              LayoutText {#text} at (50,59) size 4x18
+                text run at (50,59) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (71,59) size 42x18
+                  text run at (71,59) width 42: "Africa"
+              LayoutText {#text} at (112,59) size 5x18
+                text run at (112,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 204x18
             LayoutInline {DIV} at (0,0) size 204x18
-              LayoutInline {DIV} at (0,0) size 204x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (116.64,39) size 50x34
-                LayoutText {#text} at (166,59) size 5x18
-                  text run at (166,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (173.53,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (188,59) size 128x18
-                    text run at (188,59) width 128: "Alexander's Empire"
-                LayoutText {#text} at (315,59) size 5x18
-                  text run at (315,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (116.64,39) size 50x34
+              LayoutText {#text} at (166,59) size 5x18
+                text run at (166,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (173.53,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (188,59) size 128x18
+                  text run at (188,59) width 128: "Alexander's Empire"
+              LayoutText {#text} at (315,59) size 5x18
+                text run at (315,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 176x18
             LayoutInline {DIV} at (0,0) size 176x18
-              LayoutInline {DIV} at (0,0) size 176x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (319.25,39) size 50x34
-                LayoutText {#text} at (369,59) size 5x18
-                  text run at (369,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (376.14,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 100x18
-                  LayoutText {#text} at (391,59) size 100x18
-                    text run at (391,59) width 100: "Ancient Greece"
-                LayoutText {#text} at (490,59) size 5x18
-                  text run at (490,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (319.25,39) size 50x34
+              LayoutText {#text} at (369,59) size 5x18
+                text run at (369,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (376.14,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 100x18
+                LayoutText {#text} at (391,59) size 100x18
+                  text run at (391,59) width 100: "Ancient Greece"
+              LayoutText {#text} at (490,59) size 5x18
+                text run at (490,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (494.97,39) size 50x34
-                LayoutText {#text} at (544,59) size 5x18
-                  text run at (544,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (551.86,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (566,59) size 47x18
-                    text run at (566,59) width 47: "Classic"
-                LayoutText {#text} at (612,59) size 5x18
-                  text run at (612,59) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (494.97,39) size 50x34
+              LayoutText {#text} at (544,59) size 5x18
+                text run at (544,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (551.86,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (566,59) size 47x18
+                  text run at (566,59) width 47: "Classic"
+              LayoutText {#text} at (612,59) size 5x18
+                text run at (612,59) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 114x18
             LayoutInline {DIV} at (0,0) size 114x18
-              LayoutInline {DIV} at (0,0) size 114x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (616.97,39) size 50x34
-                LayoutText {#text} at (666,59) size 5x18
-                  text run at (666,59) width 5: " "
-                LayoutBlockFlow {INPUT} at (673.86,63) size 12x12
-                LayoutInline {LABEL} at (0,0) size 42x18
-                  LayoutText {#text} at (688,59) size 42x18
-                    text run at (688,59) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (616.97,39) size 50x34
+              LayoutText {#text} at (666,59) size 5x18
+                text run at (666,59) width 5: " "
+              LayoutBlockFlow {INPUT} at (673.86,63) size 12x12
+              LayoutInline {LABEL} at (0,0) size 42x18
+                LayoutText {#text} at (688,59) size 42x18
+                  text run at (688,59) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,78) size 50x34
-                LayoutText {#text} at (50,98) size 4x18
-                  text run at (50,98) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,98) size 128x18
-                    text run at (71,98) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,98) size 5x18
-                  text run at (198,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,78) size 50x34
+              LayoutText {#text} at (50,98) size 4x18
+                text run at (50,98) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,98) size 128x18
+                  text run at (71,98) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,98) size 5x18
+                text run at (198,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,78) size 50x34
-                LayoutText {#text} at (252,98) size 5x18
-                  text run at (252,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,98) size 101x18
-                    text run at (274,98) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,98) size 5x18
-                  text run at (374,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,78) size 50x34
+              LayoutText {#text} at (252,98) size 5x18
+                text run at (252,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,98) size 101x18
+                  text run at (274,98) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,98) size 5x18
+                text run at (374,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,78) size 50x34
-                LayoutText {#text} at (428,98) size 5x18
-                  text run at (428,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,98) size 47x18
-                    text run at (450,98) width 47: "Classic"
-                LayoutText {#text} at (496,98) size 5x18
-                  text run at (496,98) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,78) size 50x34
+              LayoutText {#text} at (428,98) size 5x18
+                text run at (428,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,98) size 47x18
+                  text run at (450,98) width 47: "Classic"
+              LayoutText {#text} at (496,98) size 5x18
+                text run at (496,98) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,78) size 50x34
-                LayoutText {#text} at (550,98) size 5x18
-                  text run at (550,98) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,102) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,98) size 41x18
-                    text run at (572,98) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,78) size 50x34
+              LayoutText {#text} at (550,98) size 5x18
+                text run at (550,98) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,102) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,98) size 41x18
+                  text run at (572,98) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,117) size 50x34
-                LayoutText {#text} at (50,137) size 4x18
-                  text run at (50,137) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,137) size 128x18
-                    text run at (71,137) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,137) size 5x18
-                  text run at (198,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,117) size 50x34
+              LayoutText {#text} at (50,137) size 4x18
+                text run at (50,137) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,137) size 128x18
+                  text run at (71,137) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,137) size 5x18
+                text run at (198,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,117) size 50x34
-                LayoutText {#text} at (252,137) size 5x18
-                  text run at (252,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,137) size 101x18
-                    text run at (274,137) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,137) size 5x18
-                  text run at (374,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,117) size 50x34
+              LayoutText {#text} at (252,137) size 5x18
+                text run at (252,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,137) size 101x18
+                  text run at (274,137) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,137) size 5x18
+                text run at (374,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,117) size 50x34
-                LayoutText {#text} at (428,137) size 5x18
-                  text run at (428,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,137) size 47x18
-                    text run at (450,137) width 47: "Classic"
-                LayoutText {#text} at (496,137) size 5x18
-                  text run at (496,137) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,117) size 50x34
+              LayoutText {#text} at (428,137) size 5x18
+                text run at (428,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,137) size 47x18
+                  text run at (450,137) width 47: "Classic"
+              LayoutText {#text} at (496,137) size 5x18
+                text run at (496,137) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,117) size 50x34
-                LayoutText {#text} at (550,137) size 5x18
-                  text run at (550,137) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,141) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,137) size 41x18
-                    text run at (572,137) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,117) size 50x34
+              LayoutText {#text} at (550,137) size 5x18
+                text run at (550,137) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,141) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,137) size 41x18
+                  text run at (572,137) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,156) size 50x34
-                LayoutText {#text} at (50,176) size 4x18
-                  text run at (50,176) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,176) size 128x18
-                    text run at (71,176) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,176) size 5x18
-                  text run at (198,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,156) size 50x34
+              LayoutText {#text} at (50,176) size 4x18
+                text run at (50,176) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,176) size 128x18
+                  text run at (71,176) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,176) size 5x18
+                text run at (198,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,156) size 50x34
-                LayoutText {#text} at (252,176) size 5x18
-                  text run at (252,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,176) size 101x18
-                    text run at (274,176) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,176) size 5x18
-                  text run at (374,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,156) size 50x34
+              LayoutText {#text} at (252,176) size 5x18
+                text run at (252,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,176) size 101x18
+                  text run at (274,176) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,176) size 5x18
+                text run at (374,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,156) size 50x34
-                LayoutText {#text} at (428,176) size 5x18
-                  text run at (428,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,176) size 47x18
-                    text run at (450,176) width 47: "Classic"
-                LayoutText {#text} at (496,176) size 5x18
-                  text run at (496,176) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,156) size 50x34
+              LayoutText {#text} at (428,176) size 5x18
+                text run at (428,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,176) size 47x18
+                  text run at (450,176) width 47: "Classic"
+              LayoutText {#text} at (496,176) size 5x18
+                text run at (496,176) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,156) size 50x34
-                LayoutText {#text} at (550,176) size 5x18
-                  text run at (550,176) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,180) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,176) size 41x18
-                    text run at (572,176) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,156) size 50x34
+              LayoutText {#text} at (550,176) size 5x18
+                text run at (550,176) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,180) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,176) size 41x18
+                  text run at (572,176) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,195) size 50x34
-                LayoutText {#text} at (50,215) size 4x18
-                  text run at (50,215) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,215) size 128x18
-                    text run at (71,215) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,215) size 5x18
-                  text run at (198,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,195) size 50x34
+              LayoutText {#text} at (50,215) size 4x18
+                text run at (50,215) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,215) size 128x18
+                  text run at (71,215) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,215) size 5x18
+                text run at (198,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,195) size 50x34
-                LayoutText {#text} at (252,215) size 5x18
-                  text run at (252,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,215) size 101x18
-                    text run at (274,215) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,215) size 5x18
-                  text run at (374,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,195) size 50x34
+              LayoutText {#text} at (252,215) size 5x18
+                text run at (252,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,215) size 101x18
+                  text run at (274,215) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,215) size 5x18
+                text run at (374,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,195) size 50x34
-                LayoutText {#text} at (428,215) size 5x18
-                  text run at (428,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,215) size 47x18
-                    text run at (450,215) width 47: "Classic"
-                LayoutText {#text} at (496,215) size 5x18
-                  text run at (496,215) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,195) size 50x34
+              LayoutText {#text} at (428,215) size 5x18
+                text run at (428,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,215) size 47x18
+                  text run at (450,215) width 47: "Classic"
+              LayoutText {#text} at (496,215) size 5x18
+                text run at (496,215) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,195) size 50x34
-                LayoutText {#text} at (550,215) size 5x18
-                  text run at (550,215) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,219) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,215) size 41x18
-                    text run at (572,215) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,195) size 50x34
+              LayoutText {#text} at (550,215) size 5x18
+                text run at (550,215) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,219) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,215) size 41x18
+                  text run at (572,215) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,234) size 50x34
-                LayoutText {#text} at (50,254) size 4x18
-                  text run at (50,254) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,254) size 128x18
-                    text run at (71,254) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,254) size 5x18
-                  text run at (198,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,234) size 50x34
+              LayoutText {#text} at (50,254) size 4x18
+                text run at (50,254) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,254) size 128x18
+                  text run at (71,254) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,254) size 5x18
+                text run at (198,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,234) size 50x34
-                LayoutText {#text} at (252,254) size 5x18
-                  text run at (252,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,254) size 101x18
-                    text run at (274,254) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,254) size 5x18
-                  text run at (374,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,234) size 50x34
+              LayoutText {#text} at (252,254) size 5x18
+                text run at (252,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,254) size 101x18
+                  text run at (274,254) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,254) size 5x18
+                text run at (374,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,234) size 50x34
-                LayoutText {#text} at (428,254) size 5x18
-                  text run at (428,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,254) size 47x18
-                    text run at (450,254) width 47: "Classic"
-                LayoutText {#text} at (496,254) size 5x18
-                  text run at (496,254) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,234) size 50x34
+              LayoutText {#text} at (428,254) size 5x18
+                text run at (428,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,254) size 47x18
+                  text run at (450,254) width 47: "Classic"
+              LayoutText {#text} at (496,254) size 5x18
+                text run at (496,254) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,234) size 50x34
-                LayoutText {#text} at (550,254) size 5x18
-                  text run at (550,254) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,258) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,254) size 41x18
-                    text run at (572,254) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,234) size 50x34
+              LayoutText {#text} at (550,254) size 5x18
+                text run at (550,254) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,258) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,254) size 41x18
+                  text run at (572,254) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,273) size 50x34
-                LayoutText {#text} at (50,293) size 4x18
-                  text run at (50,293) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,293) size 128x18
-                    text run at (71,293) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,293) size 5x18
-                  text run at (198,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,273) size 50x34
+              LayoutText {#text} at (50,293) size 4x18
+                text run at (50,293) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,293) size 128x18
+                  text run at (71,293) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,293) size 5x18
+                text run at (198,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 177x18
             LayoutInline {DIV} at (0,0) size 177x18
-              LayoutInline {DIV} at (0,0) size 177x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,273) size 50x34
-                LayoutText {#text} at (252,293) size 5x18
-                  text run at (252,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,293) size 101x18
-                    text run at (274,293) width 101: "Ancient Greece"
-                LayoutText {#text} at (374,293) size 5x18
-                  text run at (374,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,273) size 50x34
+              LayoutText {#text} at (252,293) size 5x18
+                text run at (252,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,293) size 101x18
+                  text run at (274,293) width 101: "Ancient Greece"
+              LayoutText {#text} at (374,293) size 5x18
+                text run at (374,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 123x18
             LayoutInline {DIV} at (0,0) size 123x18
-              LayoutInline {DIV} at (0,0) size 123x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (378.33,273) size 50x34
-                LayoutText {#text} at (428,293) size 5x18
-                  text run at (428,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (435.22,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 47x18
-                  LayoutText {#text} at (450,293) size 47x18
-                    text run at (450,293) width 47: "Classic"
-                LayoutText {#text} at (496,293) size 5x18
-                  text run at (496,293) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (378.33,273) size 50x34
+              LayoutText {#text} at (428,293) size 5x18
+                text run at (428,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (435.22,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 47x18
+                LayoutText {#text} at (450,293) size 47x18
+                  text run at (450,293) width 47: "Classic"
+              LayoutText {#text} at (496,293) size 5x18
+                text run at (496,293) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 113x18
             LayoutInline {DIV} at (0,0) size 113x18
-              LayoutInline {DIV} at (0,0) size 113x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (500.33,273) size 50x34
-                LayoutText {#text} at (550,293) size 5x18
-                  text run at (550,293) width 5: " "
-                LayoutBlockFlow {INPUT} at (557.22,297) size 12x12
-                LayoutInline {LABEL} at (0,0) size 41x18
-                  LayoutText {#text} at (572,293) size 41x18
-                    text run at (572,293) width 41: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (500.33,273) size 50x34
+              LayoutText {#text} at (550,293) size 5x18
+                text run at (550,293) width 5: " "
+              LayoutBlockFlow {INPUT} at (557.22,297) size 12x12
+              LayoutInline {LABEL} at (0,0) size 41x18
+                LayoutText {#text} at (572,293) size 41x18
+                  text run at (572,293) width 41: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 203x18
             LayoutInline {DIV} at (0,0) size 203x18
-              LayoutInline {DIV} at (0,0) size 203x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,312) size 50x34
-                LayoutText {#text} at (50,332) size 4x18
-                  text run at (50,332) width 4: " "
-                LayoutBlockFlow {INPUT} at (56.89,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 128x18
-                  LayoutText {#text} at (71,332) size 128x18
-                    text run at (71,332) width 128: "Alexander's Empire"
-                LayoutText {#text} at (198,332) size 5x18
-                  text run at (198,332) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,312) size 50x34
+              LayoutText {#text} at (50,332) size 4x18
+                text run at (50,332) width 4: " "
+              LayoutBlockFlow {INPUT} at (56.89,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 128x18
+                LayoutText {#text} at (71,332) size 128x18
+                  text run at (71,332) width 128: "Alexander's Empire"
+              LayoutText {#text} at (198,332) size 5x18
+                text run at (198,332) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 173x18
             LayoutInline {DIV} at (0,0) size 173x18
-              LayoutInline {DIV} at (0,0) size 173x18
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (202.61,312) size 50x34
-                LayoutText {#text} at (252,332) size 5x18
-                  text run at (252,332) width 5: " "
-                LayoutBlockFlow {INPUT} at (259.50,336) size 12x12
-                LayoutInline {LABEL} at (0,0) size 101x18
-                  LayoutText {#text} at (274,332) size 101x18
-                    text run at (274,332) width 101: "Ancient Greece"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x18 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (202.61,312) size 50x34
+              LayoutText {#text} at (252,332) size 5x18
+                text run at (252,332) width 5: " "
+              LayoutBlockFlow {INPUT} at (259.50,336) size 12x12
+              LayoutInline {LABEL} at (0,0) size 101x18
+                LayoutText {#text} at (274,332) size 101x18
+                  text run at (274,332) width 101: "Ancient Greece"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
 layer at (24,16) size 50x34 clip at (25,17) size 48x32
   LayoutBlockFlow {DIV} at (0,0) size 50x34 [border: (1px solid #C0C0C0)]
     LayoutImage (floating) {IMG} at (2,2) size 16x16
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt
index 5d37ea2..ebf48827 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/writing-mode/fieldsets-expected.txt
@@ -9,18 +9,15 @@
       LayoutFieldset {FIELDSET} at (16,34) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (34,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,156) size 4x18
         text run at (260,156) width 4: " "
       LayoutFieldset {FIELDSET} at (280,34) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (110,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,190) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,312) size 4x18
         text run at (260,312) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -30,18 +27,15 @@
       LayoutFieldset {FIELDSET} at (16,364) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,486) size 4x18
         text run at (260,486) width 4: " "
       LayoutFieldset {FIELDSET} at (280,364) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (90,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,520) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,642) size 4x18
         text run at (260,642) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -51,19 +45,16 @@
       LayoutFieldset {FIELDSET} at (16,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,34) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,924) size 5x18
         text run at (151,924) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,110) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,924) size 5x18
         text run at (307,924) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (462,924) size 5x18
         text run at (462,924) width 5: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -73,17 +64,14 @@
       LayoutFieldset {FIELDSET} at (16,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,14) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,1206) size 5x18
         text run at (151,1206) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,90) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,1206) size 5x18
         text run at (307,1206) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
index e4a9ce9..f5e66e64 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -94,6 +94,11 @@
         {
           "object": "LayoutView #document",
           "rect": [485, 0, 15, 600],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [485, 0, 15, 600],
           "reason": "scroll"
         }
       ]
@@ -109,6 +114,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
index 66b125b..fe24915e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/colourpicker-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/colourpicker-expected.png
index d5137ae..93b2ca0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/colourpicker-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/carto.net/colourpicker-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
index 9f4545e..d719824 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/inline-svg-in-xhtml-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/inline-svg-in-xhtml-expected.txt
index b92e274..7f391cf1 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/inline-svg-in-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/inline-svg-in-xhtml-expected.txt
@@ -17,19 +17,18 @@
         LayoutBlockFlow {legend} at (14,0) size 88x18
           LayoutText {#text} at (2,0) size 84x18
             text run at (2,0) width 84: "HTML Form"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 688x102
-          LayoutBlockFlow {p} at (0,32) size 688x19
-            LayoutInline {label} at (0,0) size 110x18
-              LayoutText {#text} at (0,0) size 110x18
-                text run at (0,0) width 110: "Enter something:"
-            LayoutText {#text} at (109,0) size 5x18
-              text run at (109,0) width 5: " "
-            LayoutTextControl {input} at (113.75,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-            LayoutText {#text} at (0,0) size 0x0
-          LayoutBlockFlow {p} at (0,67) size 688x19
-            LayoutButton {button} at (0,1) size 61.50x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
-              LayoutBlockFlow (anonymous) at (8,2) size 45.50x13
-                LayoutText {#text} at (0,0) size 46x13
-                  text run at (0,0) width 46: "Activate!"
+        LayoutBlockFlow {p} at (14,39.59) size 688x19
+          LayoutInline {label} at (0,0) size 110x18
+            LayoutText {#text} at (0,0) size 110x18
+              text run at (0,0) width 110: "Enter something:"
+          LayoutText {#text} at (109,0) size 5x18
+            text run at (109,0) width 5: " "
+          LayoutTextControl {input} at (113.75,0) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutText {#text} at (0,0) size 0x0
+        LayoutBlockFlow {p} at (14,74.59) size 688x19
+          LayoutButton {button} at (0,1) size 61.50x18 [bgcolor=#C0C0C0] [border: none (2px outset #C0C0C0) none (2px outset #C0C0C0)]
+            LayoutBlockFlow (anonymous) at (8,2) size 45.50x13
+              LayoutText {#text} at (0,0) size 46x13
+                text run at (0,0) width 46: "Activate!"
 layer at (182,82) size 125x13
   LayoutBlockFlow {div} at (3,3) size 125x13
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-rotated-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-rotated-gradient-expected.png
index fdb665d3..b68ca610 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-rotated-gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-rotated-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-scalable-background-image2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-scalable-background-image2-expected.png
index 5b5a9ae..5ca9d45 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-scalable-background-image2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/wicd/test-scalable-background-image2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
index 01951c1..8e8ec58 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
index 382a6e7..8e6b71f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-group-expected.png
index 29c1f9d..65dbd65 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-group-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-group-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 4d8fb783..a29eb95 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
new file mode 100644
index 0000000..1d6541a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
@@ -0,0 +1,103 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow P",
+          "rect": [8, 130, 784, 66],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 178, 36, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 148, 36, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutInline SPAN id='d'",
+          "rect": [8, 145, 4, 18],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 145, 4, 18],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 130, 4, 18],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBR BR",
+          "rect": [43, 178, 1, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBR BR",
+          "rect": [43, 148, 1, 18],
+          "reason": "forced by layout"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow P",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutInline SPAN id='d'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Hello'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png
index 6e5bbbc..8514de3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/size/backgroundSize16-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/size/backgroundSize16-expected.png
index 91a87a6..1431343 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/size/backgroundSize16-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/size/backgroundSize16-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png
new file mode 100644
index 0000000..312d40b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/block/basic/fieldset-stretch-to-legend-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/css/fieldset-display-row-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/css/fieldset-display-row-expected.txt
index 9727a61..3b7fc845 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/css/fieldset-display-row-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/css/fieldset-display-row-expected.txt
@@ -4,6 +4,5 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x37.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x18
-          LayoutText {#text} at (0,0) size 385x17
-            text run at (0,0) width 385: "If you can see this fieldset without crashing, then all is well."
+        LayoutText {#text} at (14,8) size 385x17
+          text run at (14,8) width 385: "If you can see this fieldset without crashing, then all is well."
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/006-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/006-expected.txt
index 5951bb5..e8e1cd9 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/006-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/006-expected.txt
@@ -8,7 +8,6 @@
           LayoutBlockFlow {LEGEND} at (42,0) size 124.66x18
             LayoutText {#text} at (2,0) size 121x17
               text run at (2,0) width 121: "Test without forms"
-          LayoutBlockFlow (anonymous) at (22,15.59) size 726x26
-            LayoutBlockFlow {DIV} at (0,8) size 726x18
-              LayoutText {#text} at (0,0) size 283x17
-                text run at (0,0) width 283: "A DIV inside a fieldset, not related to forms"
+          LayoutBlockFlow {DIV} at (22,23.59) size 726x18
+            LayoutText {#text} at (0,0) size 283x17
+              text run at (0,0) width 283: "A DIV inside a fieldset, not related to forms"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/007-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/007-expected.txt
index eb373bcc..1f7c8441 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/007-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/007-expected.txt
@@ -7,19 +7,19 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 87.09x18
           LayoutText {#text} at (2,0) size 84x17
             text run at (2,0) width 84: "Number One"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 87.09x52
-          LayoutText {#text} at (0,16) size 81x17
-            text run at (0,16) width 81: "Hello world."
-          LayoutBR {BR} at (80,30) size 1x0
-          LayoutText {#text} at (0,34) size 81x17
-            text run at (0,34) width 81: "Hello world."
-      LayoutText {#text} at (119,58) size 5x17
-        text run at (119,58) width 5: " "
+        LayoutBlockFlow (anonymous) at (14,23.59) size 87.09x36
+          LayoutText {#text} at (0,0) size 81x17
+            text run at (0,0) width 81: "Hello world."
+          LayoutBR {BR} at (80,14) size 1x0
+          LayoutText {#text} at (0,18) size 81x17
+            text run at (0,18) width 81: "Hello world."
+      LayoutText {#text} at (119,41) size 5x17
+        text run at (119,41) width 5: " "
       LayoutFieldset {FIELDSET} at (125.09,18) size 116.64x53.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 88.64x18
           LayoutText {#text} at (2,0) size 85x17
             text run at (2,0) width 85: "Number Two"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 88.64x34
-          LayoutText {#text} at (0,16) size 81x17
-            text run at (0,16) width 81: "Hello world."
+        LayoutBlockFlow (anonymous) at (14,23.59) size 88.64x18
+          LayoutText {#text} at (0,0) size 81x17
+            text run at (0,0) width 81: "Hello world."
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-align-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-align-expected.txt
index 068f47ea..ff8e4064 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-align-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-align-expected.txt
@@ -10,29 +10,29 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x17
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,91.59) size 765x57.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x17
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,149.19) size 765x57.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x17
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (2,206.78) size 765x57.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x17
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,272.38) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,290.38) size 769x18
@@ -46,29 +46,29 @@
           LayoutBlockFlow {LEGEND} at (14,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x17
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-            LayoutTextControl {INPUT} at (564,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+            LayoutTextControl {INPUT} at (564,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,57.59) size 765x57.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (321.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x17
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-            LayoutTextControl {INPUT} at (564,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+            LayoutTextControl {INPUT} at (564,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,115.19) size 765x57.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (638.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x17
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-            LayoutTextControl {INPUT} at (564,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+            LayoutTextControl {INPUT} at (564,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (2,172.78) size 765x57.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (624.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x17
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (14,7.59) size 737x38
-            LayoutTextControl {INPUT} at (564,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (14,23.59) size 737x22
+            LayoutTextControl {INPUT} at (564,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,554.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
@@ -81,29 +81,29 @@
         LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
           LayoutText {#text} at (2,0) size 100x17
             text run at (2,0) width 100: "My Legend left"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,701.75) size 739x72 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
           LayoutText {#text} at (2,0) size 118x17
             text run at (2,0) width 118: "My Legend center"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,788.75) size 739x72 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
           LayoutText {#text} at (2,0) size 109x17
             text run at (2,0) width 109: "My Legend right"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (15,875.75) size 739x72 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (17,0) size 126.63x18
           LayoutText {#text} at (2,0) size 123x17
             text run at (2,0) width 123: "My Legend default"
-        LayoutBlockFlow (anonymous) at (17,17) size 705x38
-          LayoutTextControl {INPUT} at (0,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+        LayoutBlockFlow (anonymous) at (17,33) size 705x22
+          LayoutTextControl {INPUT} at (0,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
           LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow {HR} at (0,962.75) size 769x2 [border: (1px inset #EEEEEE)]
       LayoutBlockFlow {P} at (0,980.75) size 769x18
@@ -117,29 +117,29 @@
           LayoutBlockFlow {LEGEND} at (17,0) size 103.53x18
             LayoutText {#text} at (2,0) size 100x17
               text run at (2,0) width 100: "My Legend left"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x38
-            LayoutTextControl {INPUT} at (532,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x22
+            LayoutTextControl {INPUT} at (532,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,87) size 739x72 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (308.86,0) size 121.28x18
             LayoutText {#text} at (2,0) size 118x17
               text run at (2,0) width 118: "My Legend center"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x38
-            LayoutTextControl {INPUT} at (532,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x22
+            LayoutTextControl {INPUT} at (532,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,174) size 739x72 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (609.58,0) size 112.42x18
             LayoutText {#text} at (2,0) size 109x17
               text run at (2,0) width 109: "My Legend right"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x38
-            LayoutTextControl {INPUT} at (532,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x22
+            LayoutTextControl {INPUT} at (532,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
         LayoutFieldset {FIELDSET} at (15,261) size 739x72 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (595.38,0) size 126.63x18
             LayoutText {#text} at (2,0) size 123x17
               text run at (2,0) width 123: "My Legend default"
-          LayoutBlockFlow (anonymous) at (17,17) size 705x38
-            LayoutTextControl {INPUT} at (532,16) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutBlockFlow (anonymous) at (17,33) size 705x22
+            LayoutTextControl {INPUT} at (532,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
             LayoutText {#text} at (0,0) size 0x0
       LayoutBlockFlow (anonymous) at (0,1362.75) size 769x0
         LayoutInline {SPAN} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
index 9f0d70e..255f18e4 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-legend-padding-unclipped-fieldset-border-expected.txt
@@ -17,4 +17,3 @@
       LayoutBlockFlow {FORM} at (0,68) size 784x53.59
         LayoutFieldset {FIELDSET} at (2,0) size 780x53.59 [border: (2px groove #C0C0C0)]
           LayoutBlockFlow {LEGEND} at (14,0) size 36x36 [border: (1px solid #0000FF)]
-          LayoutBlockFlow (anonymous) at (14,7.59) size 752x34
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-with-float-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-with-float-expected.txt
index 7fe5e66..1e769d3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-with-float-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/fieldset-with-float-expected.txt
@@ -4,8 +4,7 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x335.59
     LayoutBlockFlow {BODY} at (8,8) size 784x319.59
       LayoutFieldset {FIELDSET} at (2,0) size 780x319.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x300
-          LayoutBlockFlow (floating) {DIV} at (0,0) size 300x300 [bgcolor=#008000]
-          LayoutBlockFlow {DIV} at (0,0) size 752x50
-            LayoutText {#text} at (300,0) size 88x17
-              text run at (300,0) width 88: "Other content"
+        LayoutBlockFlow (floating) {DIV} at (14,7.59) size 300x300 [bgcolor=#008000]
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x50
+          LayoutText {#text} at (300,0) size 88x17
+            text run at (300,0) width 88: "Other content"
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/float-before-fieldset-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/float-before-fieldset-expected.txt
index b7a45923..7e13bb3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/float-before-fieldset-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/fieldset/float-before-fieldset-expected.txt
@@ -8,6 +8,6 @@
         LayoutBlockFlow {LEGEND} at (14,0) size 80.88x18
           LayoutText {#text} at (2,0) size 77x17
             text run at (2,0) width 77: "Hello world"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 704x34
-          LayoutText {#text} at (0,16) size 143x17
-            text run at (0,16) width 143: "Some fieldset content."
+        LayoutBlockFlow (anonymous) at (14,23.59) size 704x18
+          LayoutText {#text} at (0,0) size 143x17
+            text run at (0,0) width 143: "Some fieldset content."
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/ruby/rubyDOM-remove-text2-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/ruby/rubyDOM-remove-text2-expected.txt
index 4a23fa6..8b7ba3e6d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/ruby/rubyDOM-remove-text2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/ruby/rubyDOM-remove-text2-expected.txt
@@ -20,7 +20,6 @@
             LayoutRubyText {RT} at (0,-12) size 112.83x12
               LayoutText {#text} at (0,0) size 113x12
                 text run at (0,0) width 113: "Hyper-text Markup Language"
-            LayoutRubyBase (anonymous) at (0,0) size 112.83x0
           LayoutRubyRun (anonymous) at (270.84,0) size 8x18
             LayoutRubyBase (anonymous) at (0,0) size 8x18
               LayoutText {#text} at (0,0) size 8x17
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.png
index e0405e5..b8c7d6e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.txt
index 2c80771..f594d485 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/basic/generic-family-changes-expected.txt
@@ -4,9 +4,9 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x306
     LayoutBlockFlow {BODY} at (8,8) size 784x282
       LayoutBlockFlow (anonymous) at (0,0) size 784x54
-        LayoutText {#text} at (0,0) size 756x53
-          text run at (0,0) width 539: "Tests of WebKit's intepretation of font sizes when no absolute font size is specified. "
-          text run at (538,0) width 218: "Percentages and logical keywords"
+        LayoutText {#text} at (0,0) size 762x53
+          text run at (0,0) width 544: "Tests of WebKit's interpretation of font sizes when no absolute font size is specified. "
+          text run at (543,0) width 219: "Percentages and logical keywords"
           text run at (0,18) width 203: "scale to reflect the family type. "
           text run at (202,18) width 539: "Opera 9 matches this behavior as well (except it has a bug with multiple font-family"
           text run at (0,36) width 217: "mappings as in the first example)."
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
index 2654b311..9a71543 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/whitespace/normal-after-nowrap-breaking-expected.txt
@@ -4,503 +4,502 @@
   LayoutBlockFlow {HTML} at (0,0) size 800x600
     LayoutBlockFlow {BODY} at (8,8) size 784x584
       LayoutFieldset {FIELDSET} at (2,0) size 780x361.59 [border: (2px groove #C0C0C0)]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 752x342
-          LayoutBlockFlow {DIV} at (0,0) size 752x342
+        LayoutBlockFlow {DIV} at (14,7.59) size 752x342
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,0) size 50x34
-                LayoutText {#text} at (50,20) size 4x17
-                  text run at (50,20) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (74,20) size 47x17
-                    text run at (74,20) width 47: "Classic"
-                LayoutText {#text} at (120,20) size 5x17
-                  text run at (120,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,0) size 50x34
+              LayoutText {#text} at (50,20) size 4x17
+                text run at (50,20) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (74,20) size 47x17
+                  text run at (74,20) width 47: "Classic"
+              LayoutText {#text} at (120,20) size 5x17
+                text run at (120,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 120x17
             LayoutInline {DIV} at (0,0) size 120x17
-              LayoutInline {DIV} at (0,0) size 120x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (124.22,0) size 50x34
-                LayoutText {#text} at (174,20) size 5x17
-                  text run at (174,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (182.22,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (198,20) size 42x17
-                    text run at (198,20) width 42: "Africa"
-                LayoutText {#text} at (239,20) size 5x17
-                  text run at (239,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (124.22,0) size 50x34
+              LayoutText {#text} at (174,20) size 5x17
+                text run at (174,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (182.22,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (198,20) size 42x17
+                  text run at (198,20) width 42: "Africa"
+              LayoutText {#text} at (239,20) size 5x17
+                text run at (239,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (243.08,0) size 50x34
-                LayoutText {#text} at (293,20) size 5x17
-                  text run at (293,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (301.08,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (317,20) size 127x17
-                    text run at (317,20) width 127: "Alexander's Empire"
-                LayoutText {#text} at (443,20) size 5x17
-                  text run at (443,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (243.08,0) size 50x34
+              LayoutText {#text} at (293,20) size 5x17
+                text run at (293,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (301.08,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (317,20) size 127x17
+                  text run at (317,20) width 127: "Alexander's Empire"
+              LayoutText {#text} at (443,20) size 5x17
+                text run at (443,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (447.91,0) size 50x34
-                LayoutText {#text} at (497,20) size 5x17
-                  text run at (497,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (505.91,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (521,20) size 101x17
-                    text run at (521,20) width 101: "Ancient Greece"
-                LayoutText {#text} at (621,20) size 5x17
-                  text run at (621,20) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (447.91,0) size 50x34
+              LayoutText {#text} at (497,20) size 5x17
+                text run at (497,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (505.91,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (521,20) size 101x17
+                  text run at (521,20) width 101: "Ancient Greece"
+              LayoutText {#text} at (621,20) size 5x17
+                text run at (621,20) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 122x17
             LayoutInline {DIV} at (0,0) size 122x17
-              LayoutInline {DIV} at (0,0) size 122x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (625.84,0) size 50x34
-                LayoutText {#text} at (675,20) size 5x17
-                  text run at (675,20) width 5: " "
-                LayoutBlockFlow {INPUT} at (683.84,21) size 13x13
-                LayoutInline {LABEL} at (0,0) size 48x17
-                  LayoutText {#text} at (699,20) size 48x17
-                    text run at (699,20) width 48: "Classic"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (625.84,0) size 50x34
+              LayoutText {#text} at (675,20) size 5x17
+                text run at (675,20) width 5: " "
+              LayoutBlockFlow {INPUT} at (683.84,21) size 13x13
+              LayoutInline {LABEL} at (0,0) size 48x17
+                LayoutText {#text} at (699,20) size 48x17
+                  text run at (699,20) width 48: "Classic"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 119x17
             LayoutInline {DIV} at (0,0) size 119x17
-              LayoutInline {DIV} at (0,0) size 119x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,38) size 50x34
-                LayoutText {#text} at (50,58) size 4x17
-                  text run at (50,58) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,59) size 13x13
-                LayoutInline {LABEL} at (0,0) size 41x17
-                  LayoutText {#text} at (74,58) size 41x17
-                    text run at (74,58) width 41: "Africa"
-                LayoutText {#text} at (114,58) size 5x17
-                  text run at (114,58) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,38) size 50x34
+              LayoutText {#text} at (50,58) size 4x17
+                text run at (50,58) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,59) size 13x13
+              LayoutInline {LABEL} at (0,0) size 41x17
+                LayoutText {#text} at (74,58) size 41x17
+                  text run at (74,58) width 41: "Africa"
+              LayoutText {#text} at (114,58) size 5x17
+                text run at (114,58) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 206x17
             LayoutInline {DIV} at (0,0) size 206x17
-              LayoutInline {DIV} at (0,0) size 206x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (118.86,38) size 50x34
-                LayoutText {#text} at (168,58) size 5x17
-                  text run at (168,58) width 5: " "
-                LayoutBlockFlow {INPUT} at (176.86,59) size 13x13
-                LayoutInline {LABEL} at (0,0) size 128x17
-                  LayoutText {#text} at (192,58) size 128x17
-                    text run at (192,58) width 128: "Alexander's Empire"
-                LayoutText {#text} at (319,58) size 5x17
-                  text run at (319,58) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (118.86,38) size 50x34
+              LayoutText {#text} at (168,58) size 5x17
+                text run at (168,58) width 5: " "
+              LayoutBlockFlow {INPUT} at (176.86,59) size 13x13
+              LayoutInline {LABEL} at (0,0) size 128x17
+                LayoutText {#text} at (192,58) size 128x17
+                  text run at (192,58) width 128: "Alexander's Empire"
+              LayoutText {#text} at (319,58) size 5x17
+                text run at (319,58) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (323.69,38) size 50x34
-                LayoutText {#text} at (373,58) size 5x17
-                  text run at (373,58) width 5: " "
-                LayoutBlockFlow {INPUT} at (381.69,59) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (397,58) size 101x17
-                    text run at (397,58) width 101: "Ancient Greece"
-                LayoutText {#text} at (497,58) size 5x17
-                  text run at (497,58) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (323.69,38) size 50x34
+              LayoutText {#text} at (373,58) size 5x17
+                text run at (373,58) width 5: " "
+              LayoutBlockFlow {INPUT} at (381.69,59) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (397,58) size 101x17
+                  text run at (397,58) width 101: "Ancient Greece"
+              LayoutText {#text} at (497,58) size 5x17
+                text run at (497,58) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (501.63,38) size 50x34
-                LayoutText {#text} at (551,58) size 5x17
-                  text run at (551,58) width 5: " "
-                LayoutBlockFlow {INPUT} at (559.63,59) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (575,58) size 47x17
-                    text run at (575,58) width 47: "Classic"
-                LayoutText {#text} at (621,58) size 5x17
-                  text run at (621,58) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (501.63,38) size 50x34
+              LayoutText {#text} at (551,58) size 5x17
+                text run at (551,58) width 5: " "
+              LayoutBlockFlow {INPUT} at (559.63,59) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (575,58) size 47x17
+                  text run at (575,58) width 47: "Classic"
+              LayoutText {#text} at (621,58) size 5x17
+                text run at (621,58) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (625.84,38) size 50x34
-                LayoutText {#text} at (675,58) size 5x17
-                  text run at (675,58) width 5: " "
-                LayoutBlockFlow {INPUT} at (683.84,59) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (699,58) size 42x17
-                    text run at (699,58) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (625.84,38) size 50x34
+              LayoutText {#text} at (675,58) size 5x17
+                text run at (675,58) width 5: " "
+              LayoutBlockFlow {INPUT} at (683.84,59) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (699,58) size 42x17
+                  text run at (699,58) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,76) size 50x34
-                LayoutText {#text} at (50,96) size 4x17
-                  text run at (50,96) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,97) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,96) size 127x17
-                    text run at (74,96) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,96) size 5x17
-                  text run at (200,96) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,76) size 50x34
+              LayoutText {#text} at (50,96) size 4x17
+                text run at (50,96) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,97) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,96) size 127x17
+                  text run at (74,96) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,96) size 5x17
+                text run at (200,96) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,76) size 50x34
-                LayoutText {#text} at (254,96) size 5x17
-                  text run at (254,96) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,97) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,96) size 101x17
-                    text run at (278,96) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,96) size 5x17
-                  text run at (378,96) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,76) size 50x34
+              LayoutText {#text} at (254,96) size 5x17
+                text run at (254,96) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,97) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,96) size 101x17
+                  text run at (278,96) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,96) size 5x17
+                text run at (378,96) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,76) size 50x34
-                LayoutText {#text} at (432,96) size 5x17
-                  text run at (432,96) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,97) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,96) size 47x17
-                    text run at (456,96) width 47: "Classic"
-                LayoutText {#text} at (502,96) size 5x17
-                  text run at (502,96) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,76) size 50x34
+              LayoutText {#text} at (432,96) size 5x17
+                text run at (432,96) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,97) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,96) size 47x17
+                  text run at (456,96) width 47: "Classic"
+              LayoutText {#text} at (502,96) size 5x17
+                text run at (502,96) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,76) size 50x34
-                LayoutText {#text} at (556,96) size 5x17
-                  text run at (556,96) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,97) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,96) size 42x17
-                    text run at (580,96) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,76) size 50x34
+              LayoutText {#text} at (556,96) size 5x17
+                text run at (556,96) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,97) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,96) size 42x17
+                  text run at (580,96) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,114) size 50x34
-                LayoutText {#text} at (50,134) size 4x17
-                  text run at (50,134) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,135) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,134) size 127x17
-                    text run at (74,134) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,134) size 5x17
-                  text run at (200,134) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,114) size 50x34
+              LayoutText {#text} at (50,134) size 4x17
+                text run at (50,134) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,135) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,134) size 127x17
+                  text run at (74,134) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,134) size 5x17
+                text run at (200,134) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,114) size 50x34
-                LayoutText {#text} at (254,134) size 5x17
-                  text run at (254,134) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,135) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,134) size 101x17
-                    text run at (278,134) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,134) size 5x17
-                  text run at (378,134) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,114) size 50x34
+              LayoutText {#text} at (254,134) size 5x17
+                text run at (254,134) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,135) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,134) size 101x17
+                  text run at (278,134) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,134) size 5x17
+                text run at (378,134) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,114) size 50x34
-                LayoutText {#text} at (432,134) size 5x17
-                  text run at (432,134) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,135) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,134) size 47x17
-                    text run at (456,134) width 47: "Classic"
-                LayoutText {#text} at (502,134) size 5x17
-                  text run at (502,134) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,114) size 50x34
+              LayoutText {#text} at (432,134) size 5x17
+                text run at (432,134) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,135) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,134) size 47x17
+                  text run at (456,134) width 47: "Classic"
+              LayoutText {#text} at (502,134) size 5x17
+                text run at (502,134) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,114) size 50x34
-                LayoutText {#text} at (556,134) size 5x17
-                  text run at (556,134) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,135) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,134) size 42x17
-                    text run at (580,134) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,114) size 50x34
+              LayoutText {#text} at (556,134) size 5x17
+                text run at (556,134) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,135) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,134) size 42x17
+                  text run at (580,134) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,152) size 50x34
-                LayoutText {#text} at (50,172) size 4x17
-                  text run at (50,172) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,173) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,172) size 127x17
-                    text run at (74,172) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,172) size 5x17
-                  text run at (200,172) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,152) size 50x34
+              LayoutText {#text} at (50,172) size 4x17
+                text run at (50,172) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,173) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,172) size 127x17
+                  text run at (74,172) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,172) size 5x17
+                text run at (200,172) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,152) size 50x34
-                LayoutText {#text} at (254,172) size 5x17
-                  text run at (254,172) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,173) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,172) size 101x17
-                    text run at (278,172) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,172) size 5x17
-                  text run at (378,172) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,152) size 50x34
+              LayoutText {#text} at (254,172) size 5x17
+                text run at (254,172) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,173) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,172) size 101x17
+                  text run at (278,172) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,172) size 5x17
+                text run at (378,172) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,152) size 50x34
-                LayoutText {#text} at (432,172) size 5x17
-                  text run at (432,172) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,173) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,172) size 47x17
-                    text run at (456,172) width 47: "Classic"
-                LayoutText {#text} at (502,172) size 5x17
-                  text run at (502,172) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,152) size 50x34
+              LayoutText {#text} at (432,172) size 5x17
+                text run at (432,172) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,173) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,172) size 47x17
+                  text run at (456,172) width 47: "Classic"
+              LayoutText {#text} at (502,172) size 5x17
+                text run at (502,172) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,152) size 50x34
-                LayoutText {#text} at (556,172) size 5x17
-                  text run at (556,172) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,173) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,172) size 42x17
-                    text run at (580,172) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,152) size 50x34
+              LayoutText {#text} at (556,172) size 5x17
+                text run at (556,172) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,173) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,172) size 42x17
+                  text run at (580,172) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,190) size 50x34
-                LayoutText {#text} at (50,210) size 4x17
-                  text run at (50,210) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,211) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,210) size 127x17
-                    text run at (74,210) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,210) size 5x17
-                  text run at (200,210) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,190) size 50x34
+              LayoutText {#text} at (50,210) size 4x17
+                text run at (50,210) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,211) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,210) size 127x17
+                  text run at (74,210) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,210) size 5x17
+                text run at (200,210) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,190) size 50x34
-                LayoutText {#text} at (254,210) size 5x17
-                  text run at (254,210) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,211) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,210) size 101x17
-                    text run at (278,210) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,210) size 5x17
-                  text run at (378,210) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,190) size 50x34
+              LayoutText {#text} at (254,210) size 5x17
+                text run at (254,210) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,211) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,210) size 101x17
+                  text run at (278,210) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,210) size 5x17
+                text run at (378,210) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,190) size 50x34
-                LayoutText {#text} at (432,210) size 5x17
-                  text run at (432,210) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,211) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,210) size 47x17
-                    text run at (456,210) width 47: "Classic"
-                LayoutText {#text} at (502,210) size 5x17
-                  text run at (502,210) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,190) size 50x34
+              LayoutText {#text} at (432,210) size 5x17
+                text run at (432,210) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,211) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,210) size 47x17
+                  text run at (456,210) width 47: "Classic"
+              LayoutText {#text} at (502,210) size 5x17
+                text run at (502,210) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,190) size 50x34
-                LayoutText {#text} at (556,210) size 5x17
-                  text run at (556,210) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,211) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,210) size 42x17
-                    text run at (580,210) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,190) size 50x34
+              LayoutText {#text} at (556,210) size 5x17
+                text run at (556,210) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,211) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,210) size 42x17
+                  text run at (580,210) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,228) size 50x34
-                LayoutText {#text} at (50,248) size 4x17
-                  text run at (50,248) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,249) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,248) size 127x17
-                    text run at (74,248) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,248) size 5x17
-                  text run at (200,248) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,228) size 50x34
+              LayoutText {#text} at (50,248) size 4x17
+                text run at (50,248) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,249) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,248) size 127x17
+                  text run at (74,248) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,248) size 5x17
+                text run at (200,248) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,228) size 50x34
-                LayoutText {#text} at (254,248) size 5x17
-                  text run at (254,248) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,249) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,248) size 101x17
-                    text run at (278,248) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,248) size 5x17
-                  text run at (378,248) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,228) size 50x34
+              LayoutText {#text} at (254,248) size 5x17
+                text run at (254,248) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,249) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,248) size 101x17
+                  text run at (278,248) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,248) size 5x17
+                text run at (378,248) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,228) size 50x34
-                LayoutText {#text} at (432,248) size 5x17
-                  text run at (432,248) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,249) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,248) size 47x17
-                    text run at (456,248) width 47: "Classic"
-                LayoutText {#text} at (502,248) size 5x17
-                  text run at (502,248) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,228) size 50x34
+              LayoutText {#text} at (432,248) size 5x17
+                text run at (432,248) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,249) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,248) size 47x17
+                  text run at (456,248) width 47: "Classic"
+              LayoutText {#text} at (502,248) size 5x17
+                text run at (502,248) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,228) size 50x34
-                LayoutText {#text} at (556,248) size 5x17
-                  text run at (556,248) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,249) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,248) size 42x17
-                    text run at (580,248) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,228) size 50x34
+              LayoutText {#text} at (556,248) size 5x17
+                text run at (556,248) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,249) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,248) size 42x17
+                  text run at (580,248) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,266) size 50x34
-                LayoutText {#text} at (50,286) size 4x17
-                  text run at (50,286) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,287) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,286) size 127x17
-                    text run at (74,286) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,286) size 5x17
-                  text run at (200,286) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,266) size 50x34
+              LayoutText {#text} at (50,286) size 4x17
+                text run at (50,286) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,287) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,286) size 127x17
+                  text run at (74,286) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,286) size 5x17
+                text run at (200,286) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 179x17
             LayoutInline {DIV} at (0,0) size 179x17
-              LayoutInline {DIV} at (0,0) size 179x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,266) size 50x34
-                LayoutText {#text} at (254,286) size 5x17
-                  text run at (254,286) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,287) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,286) size 101x17
-                    text run at (278,286) width 101: "Ancient Greece"
-                LayoutText {#text} at (378,286) size 5x17
-                  text run at (378,286) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,266) size 50x34
+              LayoutText {#text} at (254,286) size 5x17
+                text run at (254,286) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,287) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,286) size 101x17
+                  text run at (278,286) width 101: "Ancient Greece"
+              LayoutText {#text} at (378,286) size 5x17
+                text run at (378,286) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 125x17
             LayoutInline {DIV} at (0,0) size 125x17
-              LayoutInline {DIV} at (0,0) size 125x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (382.77,266) size 50x34
-                LayoutText {#text} at (432,286) size 5x17
-                  text run at (432,286) width 5: " "
-                LayoutBlockFlow {INPUT} at (440.77,287) size 13x13
-                LayoutInline {LABEL} at (0,0) size 47x17
-                  LayoutText {#text} at (456,286) size 47x17
-                    text run at (456,286) width 47: "Classic"
-                LayoutText {#text} at (502,286) size 5x17
-                  text run at (502,286) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (382.77,266) size 50x34
+              LayoutText {#text} at (432,286) size 5x17
+                text run at (432,286) width 5: " "
+              LayoutBlockFlow {INPUT} at (440.77,287) size 13x13
+              LayoutInline {LABEL} at (0,0) size 47x17
+                LayoutText {#text} at (456,286) size 47x17
+                  text run at (456,286) width 47: "Classic"
+              LayoutText {#text} at (502,286) size 5x17
+                text run at (502,286) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 116x17
             LayoutInline {DIV} at (0,0) size 116x17
-              LayoutInline {DIV} at (0,0) size 116x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (506.98,266) size 50x34
-                LayoutText {#text} at (556,286) size 5x17
-                  text run at (556,286) width 5: " "
-                LayoutBlockFlow {INPUT} at (564.98,287) size 13x13
-                LayoutInline {LABEL} at (0,0) size 42x17
-                  LayoutText {#text} at (580,286) size 42x17
-                    text run at (580,286) width 42: "Africa"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (506.98,266) size 50x34
+              LayoutText {#text} at (556,286) size 5x17
+                text run at (556,286) width 5: " "
+              LayoutBlockFlow {INPUT} at (564.98,287) size 13x13
+              LayoutInline {LABEL} at (0,0) size 42x17
+                LayoutText {#text} at (580,286) size 42x17
+                  text run at (580,286) width 42: "Africa"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 205x17
             LayoutInline {DIV} at (0,0) size 205x17
-              LayoutInline {DIV} at (0,0) size 205x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (0,304) size 50x34
-                LayoutText {#text} at (50,324) size 4x17
-                  text run at (50,324) width 4: " "
-                LayoutBlockFlow {INPUT} at (58,325) size 13x13
-                LayoutInline {LABEL} at (0,0) size 127x17
-                  LayoutText {#text} at (74,324) size 127x17
-                    text run at (74,324) width 127: "Alexander's Empire"
-                LayoutText {#text} at (200,324) size 5x17
-                  text run at (200,324) width 5: " "
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 50x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (0,304) size 50x34
+              LayoutText {#text} at (50,324) size 4x17
+                text run at (50,324) width 4: " "
+              LayoutBlockFlow {INPUT} at (58,325) size 13x13
+              LayoutInline {LABEL} at (0,0) size 127x17
+                LayoutText {#text} at (74,324) size 127x17
+                  text run at (74,324) width 127: "Alexander's Empire"
+              LayoutText {#text} at (200,324) size 5x17
+                text run at (200,324) width 5: " "
+          LayoutText {#text} at (0,0) size 0x0
+          LayoutInline {DIV} at (0,0) size 175x17
             LayoutInline {DIV} at (0,0) size 175x17
-              LayoutInline {DIV} at (0,0) size 175x17
-                LayoutText {#text} at (0,0) size 0x0
-                LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
-                  LayoutBlockFlow {IMG} at (204.83,304) size 50x34
-                LayoutText {#text} at (254,324) size 5x17
-                  text run at (254,324) width 5: " "
-                LayoutBlockFlow {INPUT} at (262.83,325) size 13x13
-                LayoutInline {LABEL} at (0,0) size 101x17
-                  LayoutText {#text} at (278,324) size 101x17
-                    text run at (278,324) width 101: "Ancient Greece"
-                LayoutText {#text} at (0,0) size 0x0
-            LayoutText {#text} at (0,0) size 0x0
+              LayoutText {#text} at (0,0) size 0x0
+              LayoutInline {A} at (0,0) size 51x17 [color=#0000EE]
+                LayoutBlockFlow {IMG} at (204.83,304) size 50x34
+              LayoutText {#text} at (254,324) size 5x17
+                text run at (254,324) width 5: " "
+              LayoutBlockFlow {INPUT} at (262.83,325) size 13x13
+              LayoutInline {LABEL} at (0,0) size 101x17
+                LayoutText {#text} at (278,324) size 101x17
+                  text run at (278,324) width 101: "Ancient Greece"
+              LayoutText {#text} at (0,0) size 0x0
+          LayoutText {#text} at (0,0) size 0x0
 layer at (24,16) size 50x34 clip at (25,17) size 48x32
   LayoutBlockFlow {DIV} at (0,0) size 50x34 [border: (1px solid #C0C0C0)]
     LayoutImage (floating) {IMG} at (2,2) size 16x16
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt
index fce90af4..bd360bc 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/writing-mode/fieldsets-expected.txt
@@ -9,18 +9,15 @@
       LayoutFieldset {FIELDSET} at (16,34) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (34,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,156) size 4x17
         text run at (260,156) width 4: " "
       LayoutFieldset {FIELDSET} at (280,34) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (110,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,190) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,312) size 4x17
         text run at (260,312) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -30,18 +27,15 @@
       LayoutFieldset {FIELDSET} at (16,364) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (14,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,486) size 4x17
         text run at (260,486) width 4: " "
       LayoutFieldset {FIELDSET} at (280,364) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (90,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (0,0) size 0x0
       LayoutFieldset {FIELDSET} at (16,520) size 228x119.59 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (62,0) size 104x25
           LayoutBlockFlow {DIV} at (2,0) size 100x25 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (14,7.59) size 200x100
       LayoutText {#text} at (260,642) size 4x17
         text run at (260,642) width 4: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -51,19 +45,16 @@
       LayoutFieldset {FIELDSET} at (16,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,34) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,924) size 5x17
         text run at (151,924) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,110) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,924) size 5x17
         text run at (307,924) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,694) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (462,924) size 5x17
         text run at (462,924) width 5: " "
       LayoutBR {BR} at (0,0) size 0x0
@@ -73,17 +64,14 @@
       LayoutFieldset {FIELDSET} at (16,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,14) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (151,1206) size 5x17
         text run at (151,1206) width 5: " "
       LayoutFieldset {FIELDSET} at (171.59,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,90) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (307,1206) size 5x17
         text run at (307,1206) width 5: " "
       LayoutFieldset {FIELDSET} at (327.19,976) size 119.59x228 [border: (2px groove #C0C0C0)]
         LayoutBlockFlow {LEGEND} at (0,62) size 25x104
           LayoutBlockFlow {DIV} at (0,2) size 25x100 [bgcolor=#FFA500]
-        LayoutBlockFlow (anonymous) at (7.59,14) size 100x200
       LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
index bc46f17..8a565500 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -94,6 +94,11 @@
         {
           "object": "LayoutView #document",
           "rect": [485, 0, 15, 600],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [485, 0, 15, 600],
           "reason": "scroll"
         }
       ]
@@ -109,6 +114,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
index 2deca4d5..f122bb90 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/pservers-grad-05-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/colourpicker-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/colourpicker-expected.png
index c9669ba7..0c2bd2db4 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/colourpicker-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/carto.net/colourpicker-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/dominant-baseline-hanging-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/dominant-baseline-hanging-expected.png
index b0e46b83..1a6a182 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/dominant-baseline-hanging-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/inline-svg-in-xhtml-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/inline-svg-in-xhtml-expected.txt
index 29c5a5e..fc88438 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/inline-svg-in-xhtml-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/inline-svg-in-xhtml-expected.txt
@@ -17,19 +17,18 @@
         LayoutBlockFlow {legend} at (14,0) size 88x18
           LayoutText {#text} at (2,0) size 84x17
             text run at (2,0) width 84: "HTML Form"
-        LayoutBlockFlow (anonymous) at (14,7.59) size 688x108
-          LayoutBlockFlow {p} at (0,32) size 688x22
-            LayoutInline {label} at (0,0) size 110x17
-              LayoutText {#text} at (0,2) size 110x17
-                text run at (0,2) width 110: "Enter something:"
-            LayoutText {#text} at (109,2) size 5x17
-              text run at (109,2) width 5: " "
-            LayoutTextControl {input} at (113.75,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
-            LayoutText {#text} at (0,0) size 0x0
-          LayoutBlockFlow {p} at (0,70) size 688x22
-            LayoutButton {button} at (0,0) size 65x22 [bgcolor=#C0C0C0] [border: (2px outset #C0C0C0)]
-              LayoutBlockFlow (anonymous) at (8,3) size 49x16
-                LayoutText {#text} at (0,0) size 49x16
-                  text run at (0,0) width 49: "Activate!"
+        LayoutBlockFlow {p} at (14,39.59) size 688x22
+          LayoutInline {label} at (0,0) size 110x17
+            LayoutText {#text} at (0,2) size 110x17
+              text run at (0,2) width 110: "Enter something:"
+          LayoutText {#text} at (109,2) size 5x17
+            text run at (109,2) width 5: " "
+          LayoutTextControl {input} at (113.75,0) size 173x22 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
+          LayoutText {#text} at (0,0) size 0x0
+        LayoutBlockFlow {p} at (14,77.59) size 688x22
+          LayoutButton {button} at (0,0) size 65x22 [bgcolor=#C0C0C0] [border: (2px outset #C0C0C0)]
+            LayoutBlockFlow (anonymous) at (8,3) size 49x16
+              LayoutText {#text} at (0,0) size 49x16
+                text run at (0,0) width 49: "Activate!"
 layer at (181,82) size 169x16
   LayoutBlockFlow {div} at (2,3) size 169x16
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-rotated-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-rotated-gradient-expected.png
index 61deec6c..935863a 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-rotated-gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-rotated-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-scalable-background-image2-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-scalable-background-image2-expected.png
index c8a589d..51dd7e7d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-scalable-background-image2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/wicd/test-scalable-background-image2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
index 302d2b4..12c55af 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
index c232af9..b4c92bdf 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-group-expected.png
index 64e2142..a5b6506 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-group-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-group-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 1374a2e..427c9c8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
new file mode 100644
index 0000000..578361f3a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/spinvalidation/paint/invalidation/bugzilla-5699-expected.txt
@@ -0,0 +1,103 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow P",
+          "rect": [8, 130, 784, 66],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 178, 36, 17],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 148, 36, 17],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutInline SPAN id='d'",
+          "rect": [8, 145, 4, 17],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 145, 4, 17],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 130, 4, 17],
+          "reason": "style change"
+        },
+        {
+          "object": "LayoutBR BR",
+          "rect": [43, 178, 1, 17],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBR BR",
+          "rect": [43, 148, 1, 17],
+          "reason": "forced by layout"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow P",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutInline SPAN id='d'",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineFlowBox",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "style change"
+    },
+    {
+      "object": "InlineTextBox '\u00A0'",
+      "reason": "style change"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Hello'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBR BR",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox '\n'",
+      "reason": "forced by layout"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/basic/generic-family-changes-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/text/basic/generic-family-changes-expected.png
index 4105e4bb..be3dd77c 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/fast/text/basic/generic-family-changes-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/basic/generic-family-changes-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html
new file mode 100644
index 0000000..816b9de1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLCanvasElement.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../resources/mojo-helpers.js"></script>
+<script src="resources/mock-barcodedetection.js"></script>
+<script src="resources/mock-facedetection.js"></script>
+<script>
+
+var createTestForCanvasElement = function(createDetector,
+                                          createCanvas,
+                                          mockReady,
+                                          detectionResultTest) {
+  async_test(function(t) {
+
+    var img = new Image();
+    img.onload = function() {
+
+      var canvas = createCanvas();
+      canvas.getContext("2d").drawImage(img, 0, 0);
+
+      var theMock = null;
+      mockReady()
+        .then(mock => {
+          theMock = mock;
+          var detector = createDetector();
+          return detector;
+        })
+        .catch(error => {
+          assert_unreached("Error creating Mock Detector: " + error);
+        })
+        .then(detector => {
+          return detector.detect(canvas);
+        })
+        .then(detectionResult => {
+          detectionResultTest(detectionResult, theMock);
+          t.done();
+        })
+        .catch(error => {
+          assert_unreached("Error during detect(canvas): " + error);
+        });
+    }
+
+    img.src = "../media/content/greenbox.png";
+  }, "Detector detect(HTMLCanvasElement)");
+};
+
+function FaceDetectorDetectionResultTest(detectionResult, mock) {
+  const imageReceivedByMock = mock.getFrameData();
+  assert_equals(imageReceivedByMock.byteLength, 180000,"Image length");
+  const GREEN_PIXEL = 0xFF00FF00;
+  assert_equals(imageReceivedByMock[0], GREEN_PIXEL, "Pixel color");
+  assert_equals(detectionResult.length, 3, "Number of faces");
+}
+
+function BarcodeDetectorDetectionResultTest(detectionResult, mock) {
+  assert_equals(detectionResult.length, 2, "Number of barcodes");
+  assert_equals(detectionResult[0].rawValue, "cats", "barcode 1");
+  assert_equals(detectionResult[1].rawValue, "dogs", "barcode 2");
+}
+
+// These tests verify that a Detector's detect() works on an HTMLCanvasElement
+// and on an OffscreenCanvas. Use the mock mojo server implemented in
+// mock-{barcode,face}detection.js.
+generate_tests(createTestForCanvasElement, [
+  [
+    "Face - HTMLCanvasElement",
+    () => { return new FaceDetector(); },
+    () => { return document.createElement("canvas"); },
+    () => { return mockFaceDetectionReady; },
+    FaceDetectorDetectionResultTest
+  ],
+  [
+    "Face - OffscreenCanvas",
+    () => { return new FaceDetector(); },
+    () => { return new OffscreenCanvas(300, 150); },
+    () => { return mockFaceDetectionReady; },
+    FaceDetectorDetectionResultTest
+  ],
+  [
+    "Barcode - HTMLCanvasElement",
+    () => { return new BarcodeDetector(); },
+    () => { return document.createElement("canvas"); },
+    () => { return mockBarcodeDetectionReady; },
+    BarcodeDetectorDetectionResultTest
+  ],
+  [
+    "Barcode - OffscreenCanvas",
+    () => { return new BarcodeDetector(); },
+    () => { return new OffscreenCanvas(300, 150); },
+    () => { return mockBarcodeDetectionReady; },
+    BarcodeDetectorDetectionResultTest
+  ]
+]);
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
index b9b3b8a..3eef1a2 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLImageElement.html
@@ -5,11 +5,11 @@
 <script src="resources/mock-barcodedetection.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <body>
-<img id='img' src='../media/content/greenbox.png'/>
+<img id="img" src="../media/content/greenbox.png"/>
 </body>
 <script>
 
-var createTestForImageElement = function(detectorName, mockReady,
+var createTestForImageElement = function(createDetector, mockReady,
                                          detectionResultTest) {
   async_test(function(t) {
     var img = document.getElementById("img");
@@ -18,11 +18,11 @@
     mockReady()
       .then(mock => {
         theMock = mock;
-        var detector = eval("new " + detectorName + "();");
+        var detector = createDetector();
         return detector;
       })
       .catch(error => {
-        assert_unreached("Error creating MockFaceDetection: " + error);
+        assert_unreached("Error creating Mock Detector: " + error);
       })
       .then(detector => {
         return detector.detect(img);
@@ -57,13 +57,13 @@
 generate_tests(createTestForImageElement, [
   [
     "Face",
-    "FaceDetector",
+    () => { return new FaceDetector(); },
     () => { return mockFaceDetectionReady; },
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode",
-    "BarcodeDetector",
+    () => { return new BarcodeDetector(); },
     () => { return mockBarcodeDetectionReady; },
     BarcodeDetectorDetectionResultTest
   ]
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
index 879251e2..65ef40e 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-HTMLVideoElement.html
@@ -6,10 +6,10 @@
 <script src="resources/mock-facedetection.js"></script>
 <script>
 
-var createTestForVideoElement = function(detectorName, mockReady,
+var createTestForVideoElement = function(createDetector, mockReady,
                                          detectionResultTest) {
   async_test(function(t) {
-    var video = document.createElement('video');
+    var video = document.createElement("video");
     video.src = "../imported/wpt/media/white.webm";
     video.loop = true;
     video.autoplay = true;
@@ -19,11 +19,11 @@
       mockReady()
         .then(mock => {
           theMock = mock;
-          var detector = eval("new " + detectorName + "();");
+          var detector = createDetector();
           return detector;
         })
         .catch(error => {
-          assert_unreached("Error creating MockFaceDetection: " + error);
+          assert_unreached("Error creating Mock Detector: " + error);
         })
         .then(detector => {
           return detector.detect(video);
@@ -61,13 +61,13 @@
 generate_tests(createTestForVideoElement, [
   [
     "Face",
-    "FaceDetector",
+    () => { return new FaceDetector(); },
     () => { return mockFaceDetectionReady; },
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode",
-    "BarcodeDetector",
+    () => { return new BarcodeDetector(); },
     () => { return mockBarcodeDetectionReady; },
     BarcodeDetectorDetectionResultTest
   ]
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
index ce09411b..f89df492 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-ImageBitmap.html
@@ -6,7 +6,7 @@
 <script src="resources/mock-facedetection.js"></script>
 <script>
 
-var createTestForImageBitmap = function(detectorName, mockReady,
+var createTestForImageBitmap = function(createDetector, mockReady,
                                         detectionResultTest) {
   async_test(function(t) {
     var img = new Image();
@@ -21,15 +21,15 @@
             return mockReady();
           })
           .catch(error => {
-            assert_unreached('createImageBitmap() error: ' + error);
+            assert_unreached("createImageBitmap() error: " + error);
           })
           .then(mock => {
             theMock = mock;
-            var detector = eval("new " + detectorName + "();");
+            var detector = createDetector();
             return detector;
           })
           .catch(error => {
-            assert_unreached("Error creating MockFaceDetection: " + error);
+            assert_unreached("Error creating Mock Detector: " + error);
           })
           .then(detector => {
             return detector.detect(theImageBitmap);
@@ -42,7 +42,7 @@
             assert_unreached("Error during detect(img): " + error);
           });
     }
-    img.src = '../media/content/greenbox.png';
+    img.src = "../media/content/greenbox.png";
   }, "Detector detect(ImageBitmap)");
 };
 
@@ -65,13 +65,13 @@
 generate_tests(createTestForImageBitmap, [
   [
     "Face",
-    "FaceDetector",
+    () => { return new FaceDetector(); },
     () => { return mockFaceDetectionReady; },
     FaceDetectorDetectionResultTest
   ],
   [
     "Barcode",
-    "BarcodeDetector",
+    () => { return new BarcodeDetector(); },
     () => { return mockBarcodeDetectionReady; },
     BarcodeDetectorDetectionResultTest
   ]
diff --git a/third_party/WebKit/LayoutTests/shapedetection/detection-options.html b/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
index fc8dc3e4..4954886 100644
--- a/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
+++ b/third_party/WebKit/LayoutTests/shapedetection/detection-options.html
@@ -4,7 +4,7 @@
 <script src="../resources/mojo-helpers.js"></script>
 <script src="resources/mock-facedetection.js"></script>
 <body>
-<img id='img' src='../media/content/greenbox.png'/>
+<img id="img" src="../media/content/greenbox.png"/>
 </body>
 <script>
 async_test(function(t) {
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text-expected.html b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text.html b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text.html
new file mode 100644
index 0000000..2c39a4b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-clipped-text.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<svg>
+  <defs>
+    <clipPath id="textclip">
+      <rect width="100" height="100"/>
+    </clipPath>
+    <text id="text" y="80" font-size="100" font-family="Ahem" clip-path="url(#textclip)">PASS</text>
+    <clipPath id="clip">
+      <use xlink:href="#text"/>
+    </clipPath>
+  </defs>
+  <rect width="400" height="200" fill="green" clip-path="url(#clip)"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text-expected.html b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text.html b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text.html
new file mode 100644
index 0000000..0c6e405
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/clip-path-use-referencing-text.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<svg>
+  <defs>
+    <text id="text" y="80" font-size="100" font-family="Ahem">X</text>
+    <clipPath id="clip">
+      <use xlink:href="#text"/>
+    </clipPath>
+  </defs>
+  <rect width="200" height="100" fill="green" clip-path="url(#clip)"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip-expected.html b/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip.html b/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip.html
new file mode 100644
index 0000000..c173714
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/clip-path/nested-empty-clip.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<svg>
+  <clipPath id="nested">
+    <path d="M0,0h100z"/>
+  </clipPath>
+  <clipPath id="clip" clip-path="url(#nested)">
+    <rect width="100" height="100"/>
+  </clipPath>
+  <rect width="100" height="100" fill="green"/>
+  <rect width="200" height="100" fill="red" clip-path="url(#clip)"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt b/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt
index 8199c61..fbc161f 100644
--- a/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt
@@ -210,6 +210,7 @@
 offset-distance: 0px
 offset-path: none
 offset-position: auto
+offset-rotate: auto 0deg
 offset-rotation: auto 0deg
 opacity: 1
 order: 0
diff --git a/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group-expected.html b/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group.html b/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group.html
new file mode 100644
index 0000000..9d24abc79
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/masking/mask-with-visibility-hidden-group.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<svg>
+  <mask id="m">
+    <g visibility="hidden">
+      <rect width="100" height="100" fill="white" visibility="visible"/>
+      <rect width="200" height="100" fill="white"/>
+    </g>
+  </mask>
+  <rect width="200" height="100" fill="green" mask="url(#m)"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/virtual/feature-policy/http/tests/feature-policy/fullscreen-disabled-expected.txt b/third_party/WebKit/LayoutTests/virtual/feature-policy/http/tests/feature-policy/fullscreen-disabled-expected.txt
new file mode 100644
index 0000000..ca7eb48
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/feature-policy/http/tests/feature-policy/fullscreen-disabled-expected.txt
@@ -0,0 +1,11 @@
+CONSOLE WARNING: line 15: Fullscreen API is disabled by feature policy for this frame
+CONSOLE WARNING: line 6: Fullscreen API is disabled by feature policy for this frame
+CONSOLE WARNING: line 15: Fullscreen API is disabled by feature policy for this frame
+CONSOLE WARNING: line 6: Fullscreen API is disabled by feature policy for this frame
+This is a testharness.js-based test.
+PASS Fullscreen disabled on URL: resources/feature-policy-fullscreen.html with allowfullscreen = false 
+PASS Fullscreen disabled on URL: http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html with allowfullscreen = false 
+PASS Fullscreen disabled on URL: resources/feature-policy-fullscreen.html with allowfullscreen = true 
+PASS Fullscreen disabled on URL: http://localhost:8000/feature-policy/resources/feature-policy-fullscreen.html with allowfullscreen = true 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/layout_ng/css2.1/20110323/README.txt b/third_party/WebKit/LayoutTests/virtual/layout_ng/css2.1/20110323/README.txt
new file mode 100644
index 0000000..5c91a6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/layout_ng/css2.1/20110323/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in css2.1/20110323 with
+# --enable-blink-features=LayoutNG.
+# The LayoutNG project is described here: http://goo.gl/1hwhfX
diff --git a/third_party/WebKit/LayoutTests/virtual/spinvalidation/paint/invalidation/svg/js-update-gradient-expected.png b/third_party/WebKit/LayoutTests/virtual/spinvalidation/paint/invalidation/svg/js-update-gradient-expected.png
new file mode 100644
index 0000000..de942810
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/spinvalidation/paint/invalidation/svg/js-update-gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
index 02c8750b..1131a5b 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -191,6 +191,7 @@
 offset
 offsetDistance
 offsetPath
+offsetRotate
 offsetRotation
 opacity
 order
diff --git a/third_party/WebKit/LayoutTests/webaudio/event-constructor.html b/third_party/WebKit/LayoutTests/webaudio/event-constructor.html
new file mode 100644
index 0000000..190f8ba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/event-constructor.html
@@ -0,0 +1,180 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test Event Constructors</title>
+    <script src="../resources/testharness.js"></script>
+    <script src="../resources/testharnessreport.js"></script>
+    <script src="resources/audio-testing.js"></script>
+  </head>
+
+  <body>
+    <script>
+      var audit = Audit.createTaskRunner();
+
+      audit.defineTask("offline-constructor", function (taskDone) {
+        var success = true;
+
+        success = Should("new OfflineAudioCompletionEvent()", function () {
+          new OfflineAudioCompletionEvent();
+        }).throw("TypeError") && success;
+
+        success = Should('new OfflineAudioCompletionEvent("complete")',
+          function () {
+            new OfflineAudioCompletionEvent("complete");
+          }).throw("TypeError") && success;
+
+        success = Should('new OfflineAudioCompletionEvent("complete", {})',
+          function () {
+            new OfflineAudioCompletionEvent("complete", {});
+          }).throw("TypeError") && success;
+
+        success = Should(
+          'new OfflineAudioCompletionEvent("complete", {foo: 42})',
+          function () {
+            new OfflineAudioCompletionEvent("complete", {
+              foo: 42
+            });
+          }).throw("TypeError") && success;
+
+        var context = new OfflineAudioContext(1, 100, 48000);
+        var buffer = new AudioBuffer(context, {
+          length: 42
+        });
+
+        success = Should(
+          'new OfflineAudioCompletionEvent("complete", {renderedBuffer: buffer})',
+          function () {
+            new OfflineAudioCompletionEvent("complete", {
+              renderedBuffer: buffer
+            });
+          }).notThrow() && success;
+
+        Should("OfflineAudioCompletionEvent construction handled", success)
+          .summarize("correctly", "incorrectly");
+
+        taskDone();
+      });
+
+      audit.defineTask("offline-event", function (taskDone) {
+        // Only need the context for constructing the AudioBuffers for the
+        // tests.
+        var context = new OfflineAudioContext(1, 100, 48000);
+
+        var success = true;
+
+        // Just an arbitrary AudioBuffer.
+        var buffer = new AudioBuffer(context, {
+          length: 10
+        });
+
+        var event;
+        success = Should("new OfflineAudioCompletionEvent()", function () {
+          event = new OfflineAudioCompletionEvent("foo", {
+            renderedBuffer: buffer
+          });
+        }).notThrow() && success;
+
+        success = Should("event.renderedBuffer == buffer", event.renderedBuffer ==
+            buffer)
+          .beEqualTo(true) && success;
+
+        Should("OfflineAudioCompletionEvent constructed", success)
+          .summarize("correctly",
+            "incorrectly");
+
+        taskDone();
+      });
+
+      audit.defineTask("audio-processing", function (taskDone) {
+        // Only need the context for constructing the AudioBuffers for the
+        // tests.
+        var context = new OfflineAudioContext(1, 100, 48000);
+
+        // Fairly arbitrary buffers and time
+        var input = new AudioBuffer(context, {
+          length: 10
+        });
+        var output = new AudioBuffer(context, {
+          length: 20
+        });
+        var time = Math.PI;
+
+        var success = true;
+
+        // Verify required arguments.
+        success = Should("new AudioProcessingEvent()", function () {
+          new AudioProcessingEvent();
+        }).throw("TypeError") && success;
+
+        success = Should('new AudioProcessingEvent("proc")', function () {
+          new AudioProcessingEvent("proc");
+        }).throw("TypeError") && success;
+
+        success = Should('new AudioProcessingEvent("proc", {foo: 99})',
+          function () {
+            new AudioProcessingEvent("proc", {
+              foo: 99
+            });
+          }).throw("TypeError") && success;
+
+        success = Should(
+          'new AudioProcessingEvent("proc", {inputBuffer: input, outputBuffer: output})',
+          function () {
+            new AudioProcessingEvent("proc", {
+              inputBuffer: input,
+              outputBuffer: output
+            });
+          }).throw("TypeError") && success;
+
+        success = Should(
+          'new AudioProcessingEvent("proc", {inputBuffer: input, playbackTime: time})',
+          function () {
+            new AudioProcessingEvent("proc", {
+              inputBuffer: input,
+              playbackTime: time
+            });
+          }).throw("TypeError") && success;
+
+        success = Should(
+          'new AudioProcessingEvent("proc", {outputBuffer: output, playbackTime: time})',
+          function () {
+            new AudioProcessingEvent("proc", {
+              outputBuffer: output,
+              playbackTime: time
+            });
+          }).throw("TypeError") && success;
+
+
+        // Finally test valid call
+        var event;
+        success = Should(
+          'new AudioProcessingEvent("proc", {inputBuffer: input, outputBuffer: output, playbackTime: time})',
+          function () {
+            event = new AudioProcessingEvent("proc", {
+              inputBuffer: input,
+              outputBuffer: output,
+              playbackTime: time
+            });
+          }).notThrow() && success;
+
+        success = Should("event.playbackTime", event.playbackTime)
+          .beEqualTo(time) && success;
+
+        success = Should("event.inputBuffer == input", event.inputBuffer ==
+            input)
+          .beEqualTo(true) && success;
+
+        success = Should("event.outputBuffer == output", event.outputBuffer ==
+            output)
+          .beEqualTo(true) && success;
+
+        Should("AudioProcessingEvent construction handled", success)
+          .summarize("correctly", "incorrectly");
+
+        taskDone();
+      });
+
+      audit.runTasks();
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/hrtf-database.html b/third_party/WebKit/LayoutTests/webaudio/hrtf-database.html
new file mode 100644
index 0000000..fc2dab3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/hrtf-database.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test FLAC-encoded HRTF databse</title>
+    <script src="../resources/testharness.js"></script>
+    <script src="../resources/testharnessreport.js"></script>
+    <script src="resources/audit.js"></script>
+    <script src="resources/buffer-loader.js"></script>
+  </head>
+
+  <body>
+    <script>
+      // This MUST be the sample rate used by the HTRF database!
+      var sampleRate = 44100;
+
+      var context;
+      var wavBuffer;
+      var flacBuffer;
+
+      var audit = Audit.createTaskRunner();
+
+      audit.define("loadfiles", function (task, should) {
+        task.describe("Load HRTF database files");
+
+        // Any valid context with the right sample rate will do.
+        context = new OfflineAudioContext(1, 1, sampleRate);
+
+        var bufferLoader = new BufferLoader(
+          context, [
+            "../../Source/platform/audio/resources/Composite.wav",
+            "../../Source/platform/audio/resources/Composite.flac",
+          ],
+          function (bufferList) {
+            should(bufferList.length, "Number of buffers loaded")
+              .beEqualTo(2);
+            wavBuffer = bufferList[0];
+            flacBuffer = bufferList[1];
+            task.done();
+          });
+
+        bufferLoader.load();
+      });
+
+      audit.define("verify-flac", function (task, should) {
+        task.describe("Verify FLAC-encoded HRTF database matches original");
+
+        should(flacBuffer.numberOfChannels, "Number of FLAC channels")
+          .beEqualTo(wavBuffer.numberOfChannels);
+
+        for (var k = 0; k < wavBuffer.numberOfChannels; ++k) {
+          should(flacBuffer.getChannelData(k),
+              "FLAC-encoded HRTF database channel " + k
+            )
+            .beEqualToArray(wavBuffer.getChannelData(k));
+        }
+
+        task.done();
+      });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
index 177484a..28d291b 100644
--- a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
@@ -851,7 +851,7 @@
       let message = '< [' + this._label + '] ';
 
       if (this._result) {
-        message += 'All assertion passed. (total ' + this._totalAssertions
+        message += 'All assertions passed. (total ' + this._totalAssertions
           + ' assertions)';
         _logPassed(message);
       } else {
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/buffer-loader.js b/third_party/WebKit/LayoutTests/webaudio/resources/buffer-loader.js
index 8aa98b4..f089290 100644
--- a/third_party/WebKit/LayoutTests/webaudio/resources/buffer-loader.js
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/buffer-loader.js
@@ -23,7 +23,8 @@
                     if (++loader.loadCount == loader.urlList.length)
                         loader.onload(loader.bufferList);
                 } catch(e) {
-                    alert('BufferLoader: unable to load buffer' + index);
+                  console.log(e);
+                  alert('BufferLoader: unable to load buffer ' + index + ", url: " + loader.urlList[index]);
                 }
             },
             function () {
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
index d92a4a4..c6218ff7 100644
--- a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
@@ -198,6 +198,7 @@
 offsetDistance
 offsetPath
 offsetPosition
+offsetRotate
 offsetRotation
 opacity
 order
diff --git a/third_party/WebKit/PerformanceTests/resources/runner.js b/third_party/WebKit/PerformanceTests/resources/runner.js
index 632287a..10b23b5 100644
--- a/third_party/WebKit/PerformanceTests/resources/runner.js
+++ b/third_party/WebKit/PerformanceTests/resources/runner.js
@@ -164,7 +164,6 @@
     }
 
     function scheduleNextRun(scheduler, runner) {
-        PerfTestRunner.gc();
         scheduler(function () {
             try {
                 if (currentTest.setup)
@@ -248,6 +247,9 @@
     PerfTestRunner.measureFrameTime = function (test) {
         PerfTestRunner.unit = "ms";
         PerfTestRunner.bufferedLog = true;
+        // Force gc before starting the test to avoid the measured time from
+        // being affected by gc performance. See crbug.com/667811#c16.
+        PerfTestRunner.gc();
         start(test, requestAnimationFrame, measureFrameTimeOnce);
     }
 
@@ -278,6 +280,9 @@
     }
 
     function measureTimeOnce() {
+        // Force gc before measuring time to avoid interference between tests.
+        PerfTestRunner.gc();
+
         var start = PerfTestRunner.now();
         var returnValue = currentTest.run();
         var end = PerfTestRunner.now();
@@ -312,6 +317,9 @@
     }
 
     function callRunAndMeasureTime(callsPerIteration) {
+        // Force gc before measuring time to avoid interference between tests.
+        PerfTestRunner.gc();
+
         var startTime = PerfTestRunner.now();
         for (var i = 0; i < callsPerIteration; i++)
             currentTest.run();
diff --git a/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.cpp b/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.cpp
index 6b80705b..a9458246 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.cpp
@@ -37,14 +37,17 @@
 namespace blink {
 
 ActiveDOMCallback::ActiveDOMCallback(ExecutionContext* context)
-    : ContextLifecycleObserver(context) {}
+    : m_context(context) {}
 
 ActiveDOMCallback::~ActiveDOMCallback() {}
 
 bool ActiveDOMCallback::canInvokeCallback() const {
-  ExecutionContext* context = getExecutionContext();
-  return context && !context->activeDOMObjectsAreSuspended() &&
-         !context->isContextDestroyed();
+  return !m_context->activeDOMObjectsAreSuspended() &&
+         !m_context->isContextDestroyed();
+}
+
+DEFINE_TRACE(ActiveDOMCallback) {
+  visitor->trace(m_context);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.h b/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.h
index 63ed27f9..b274290 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ActiveDOMCallback.h
@@ -32,7 +32,7 @@
 #define ActiveDOMCallback_h
 
 #include "core/CoreExport.h"
-#include "core/dom/ContextLifecycleObserver.h"
+#include "platform/heap/Handle.h"
 
 namespace blink {
 
@@ -45,12 +45,17 @@
 //
 // Should only be created, used, and destroyed on the script execution
 // context thread.
-class CORE_EXPORT ActiveDOMCallback : public ContextLifecycleObserver {
+class CORE_EXPORT ActiveDOMCallback : public GarbageCollectedMixin {
  public:
   explicit ActiveDOMCallback(ExecutionContext*);
   virtual ~ActiveDOMCallback();
 
   bool canInvokeCallback() const;
+
+  DECLARE_TRACE();
+
+ private:
+  Member<ExecutionContext> m_context;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
index 7515ae8..b8ebbbf 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/DOMWrapperWorld.cpp
@@ -140,11 +140,11 @@
 void DOMWrapperWorld::allWorldsInMainThread(
     Vector<RefPtr<DOMWrapperWorld>>& worlds) {
   ASSERT(isMainThread());
-  worlds.append(&mainWorld());
+  worlds.push_back(&mainWorld());
   WorldMap& isolatedWorlds = isolatedWorldMap();
   for (WorldMap::iterator it = isolatedWorlds.begin();
        it != isolatedWorlds.end(); ++it)
-    worlds.append(it->value);
+    worlds.push_back(it->value);
 }
 
 void DOMWrapperWorld::markWrappersInAllWorlds(
diff --git a/third_party/WebKit/Source/bindings/core/v8/Dictionary.cpp b/third_party/WebKit/Source/bindings/core/v8/Dictionary.cpp
index 1bedf591..9af7fc8 100644
--- a/third_party/WebKit/Source/bindings/core/v8/Dictionary.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/Dictionary.cpp
@@ -196,7 +196,7 @@
     if (!stringKey.prepare(isolate(), exceptionState))
       return Vector<String>();
 
-    names.append(stringKey);
+    names.push_back(stringKey);
   }
 
   return names;
diff --git a/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp b/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp
index 8ef90f6..0a60f97 100644
--- a/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp
@@ -276,7 +276,7 @@
              .ToLocal(&indexedValue))
       return false;
     TOSTRING_DEFAULT(V8StringResource<>, stringValue, indexedValue, false);
-    value.append(stringValue);
+    value.push_back(stringValue);
   }
 
   return true;
@@ -306,7 +306,7 @@
         v8IndexedValue, i, dictionary.isolate(), exceptionState);
     if (exceptionState.hadException())
       return false;
-    value.append(indexedValue);
+    value.push_back(indexedValue);
   }
 
   return true;
diff --git a/third_party/WebKit/Source/bindings/core/v8/Iterable.h b/third_party/WebKit/Source/bindings/core/v8/Iterable.h
index bf126f6..8b005d55 100644
--- a/third_party/WebKit/Source/bindings/core/v8/Iterable.h
+++ b/third_party/WebKit/Source/bindings/core/v8/Iterable.h
@@ -123,9 +123,9 @@
       v8::Isolate* isolate = scriptState->isolate();
 
       Vector<ScriptValue, 2> entry;
-      entry.append(
+      entry.push_back(
           ScriptValue(scriptState, toV8(key, creationContext, isolate)));
-      entry.append(
+      entry.push_back(
           ScriptValue(scriptState, toV8(value, creationContext, isolate)));
       return entry;
     }
diff --git a/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp b/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
index df79a05..9b1c254 100644
--- a/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
@@ -269,7 +269,7 @@
     if (!message->hasHandler()) {
       message->report();
       message->makePromiseWeak();
-      m_reportedAsErrors.append(std::move(message));
+      m_reportedAsErrors.push_back(std::move(message));
       if (m_reportedAsErrors.size() > maxReportedHandlersPendingResolution)
         m_reportedAsErrors.remove(0, maxReportedHandlersPendingResolution / 10);
     }
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
index f0c1db8d..aace7e2 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScheduledAction.cpp
@@ -179,7 +179,7 @@
     Vector<v8::Local<v8::Value>>* handles) {
   handles->reserveCapacity(m_info.Size());
   for (size_t i = 0; i < m_info.Size(); ++i)
-    handles->append(m_info.Get(i));
+    handles->push_back(m_info.Get(i));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
index 949601e..d6d58c05 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp
@@ -89,13 +89,8 @@
   MainThreadDebugger::instance()->didClearContextsForFrame(frame());
 }
 
-void ScriptController::updateSecurityOrigin(SecurityOrigin* origin) {
-  m_windowProxyManager->mainWorldProxy()->updateSecurityOrigin(origin);
-  Vector<std::pair<ScriptState*, SecurityOrigin*>> isolatedContexts;
-  m_windowProxyManager->collectIsolatedContexts(isolatedContexts);
-  for (auto isolatedContext : isolatedContexts)
-    m_windowProxyManager->windowProxy(isolatedContext.first->world())
-        ->updateSecurityOrigin(isolatedContext.second);
+void ScriptController::updateSecurityOrigin(SecurityOrigin* securityOrigin) {
+  m_windowProxyManager->updateSecurityOrigin(securityOrigin);
 }
 
 v8::Local<v8::Value> ScriptController::executeScriptAndReturnValue(
@@ -242,7 +237,7 @@
       return;
   }
   v8::RegisterExtension(extension);
-  registeredExtensions().append(extension);
+  registeredExtensions().push_back(extension);
 }
 
 void ScriptController::clearWindowProxy() {
@@ -252,11 +247,6 @@
   MainThreadDebugger::instance()->didClearContextsForFrame(frame());
 }
 
-void ScriptController::collectIsolatedContexts(
-    Vector<std::pair<ScriptState*, SecurityOrigin*>>& result) {
-  m_windowProxyManager->collectIsolatedContexts(result);
-}
-
 void ScriptController::updateDocument() {
   // For an uninitialized main window windowProxy, do not incur the cost of
   // context initialization.
@@ -457,7 +447,7 @@
       v8::Local<v8::Value> value;
       if (!resultArray->Get(scriptState->context(), i).ToLocal(&value))
         return;
-      results->append(value);
+      results->push_back(value);
     }
   }
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
index eb78263f..425a2083 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptController.h
@@ -50,7 +50,6 @@
 class Element;
 class HTMLDocument;
 class KURL;
-class ScriptState;
 class ScriptSourceCode;
 class SecurityOrigin;
 class WindowProxy;
@@ -124,9 +123,6 @@
   void enableEval();
   void disableEval(const String& errorMessage);
 
-  void collectIsolatedContexts(
-      Vector<std::pair<ScriptState*, SecurityOrigin*>>&);
-
   bool canExecuteScripts(ReasonForCallingCanExecuteScripts);
 
   TextPosition eventHandlerPosition() const;
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyBase.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyBase.cpp
index e1c0ab83..9c7795b1 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyBase.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptPromisePropertyBase.cpp
@@ -147,7 +147,7 @@
       wrapUnique(new ScopedPersistent<v8::Object>);
   weakPersistent->set(m_isolate, wrapper);
   weakPersistent->setPhantom();
-  m_wrappers.append(std::move(weakPersistent));
+  m_wrappers.push_back(std::move(weakPersistent));
   return wrapper;
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseTest.cpp
index 7813369..e794739 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptPromiseTest.cpp
@@ -312,10 +312,10 @@
   ScriptValue onFulfilled, onRejected;
 
   Vector<ScriptPromise> promises;
-  promises.append(ScriptPromise::cast(scope.getScriptState(),
-                                      v8String(scope.isolate(), "hello")));
-  promises.append(ScriptPromise::cast(scope.getScriptState(),
-                                      v8String(scope.isolate(), "world")));
+  promises.push_back(ScriptPromise::cast(scope.getScriptState(),
+                                         v8String(scope.isolate(), "hello")));
+  promises.push_back(ScriptPromise::cast(scope.getScriptState(),
+                                         v8String(scope.isolate(), "world")));
 
   ScriptPromise promise = ScriptPromise::all(scope.getScriptState(), promises);
   ASSERT_FALSE(promise.isEmpty());
@@ -341,10 +341,10 @@
   ScriptValue onFulfilled, onRejected;
 
   Vector<ScriptPromise> promises;
-  promises.append(ScriptPromise::cast(scope.getScriptState(),
-                                      v8String(scope.isolate(), "hello")));
-  promises.append(ScriptPromise::reject(scope.getScriptState(),
-                                        v8String(scope.isolate(), "world")));
+  promises.push_back(ScriptPromise::cast(scope.getScriptState(),
+                                         v8String(scope.isolate(), "hello")));
+  promises.push_back(ScriptPromise::reject(scope.getScriptState(),
+                                           v8String(scope.isolate(), "world")));
 
   ScriptPromise promise = ScriptPromise::all(scope.getScriptState(), promises);
   ASSERT_FALSE(promise.isEmpty());
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.cpp
index e45fd531..0452b15 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.cpp
@@ -1149,7 +1149,7 @@
     if (appendFileInfo(file, &blobIndex)) {
       ASSERT(!i || blobIndex > 0);
       ASSERT(blobIndex >= 0);
-      blobIndices.append(blobIndex);
+      blobIndices.push_back(blobIndex);
     }
   }
   if (!blobIndices.isEmpty())
@@ -1397,7 +1397,7 @@
   if (!m_blobInfo)
     return false;
   *index = m_blobInfo->size();
-  m_blobInfo->append(WebBlobInfo(uuid, type, size));
+  m_blobInfo->push_back(WebBlobInfo(uuid, type, size));
   return true;
 }
 
@@ -1411,8 +1411,8 @@
   *index = m_blobInfo->size();
   // FIXME: transition WebBlobInfo.lastModified to be milliseconds-based also.
   double lastModified = lastModifiedMS / msPerSecond;
-  m_blobInfo->append(WebBlobInfo(file->uuid(), file->path(), file->name(),
-                                 file->type(), lastModified, size));
+  m_blobInfo->push_back(WebBlobInfo(file->uuid(), file->path(), file->name(),
+                                    file->type(), lastModified, size));
   return true;
 }
 
@@ -2475,7 +2475,7 @@
 
 void ScriptValueDeserializer::pushObjectReference(
     const v8::Local<v8::Value>& object) {
-  m_objectPool.append(object);
+  m_objectPool.push_back(object);
 }
 
 bool ScriptValueDeserializer::tryGetTransferredMessagePort(
@@ -2641,8 +2641,8 @@
 void ScriptValueDeserializer::openComposite(
     const v8::Local<v8::Value>& object) {
   uint32_t newObjectReference = m_objectPool.size();
-  m_openCompositeReferenceStack.append(newObjectReference);
-  m_objectPool.append(object);
+  m_openCompositeReferenceStack.push_back(newObjectReference);
+  m_objectPool.push_back(object);
 }
 
 bool ScriptValueDeserializer::closeComposite(v8::Local<v8::Value>* object) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
index c29ad4c6..adcc8ac 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptValueSerializer.h
@@ -656,7 +656,7 @@
                         uint32_t numProperties,
                         v8::Local<v8::Value>*);
   bool doDeserialize();
-  void push(v8::Local<v8::Value> value) { m_stack.append(value); };
+  void push(v8::Local<v8::Value> value) { m_stack.push_back(value); };
   void pop(unsigned length) {
     ASSERT(length <= m_stack.size());
     m_stack.shrink(m_stack.size() - length);
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp
index 7227fa53..1c12873 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.cpp
@@ -204,7 +204,7 @@
     return false;
 
   header->markWrapperHeader();
-  m_headersToUnmark.append(header);
+  m_headersToUnmark.push_back(header);
   return true;
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.h b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.h
index 745ff4af..4387453 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitor.h
@@ -44,18 +44,14 @@
     }
   }
 
-  /**
-     * Returns true when object was marked. Ignores (returns true) invalidated
-     * objects.
-     */
+  // Returns true when object was marked. Ignores (returns true) invalidated
+  // objects.
   inline bool isWrapperHeaderMarked() {
     return !m_rawObjectPointer || heapObjectHeader()->isWrapperHeaderMarked();
   }
 
-  /**
-     * Returns raw object pointer. Beware it doesn't necessarily point to the
-     * beginning of the object.
-     */
+  // Returns raw object pointer. Beware it doesn't necessarily point to the
+  // beginning of the object.
   const void* rawObjectPointer() { return m_rawObjectPointer; }
 
  private:
@@ -75,15 +71,13 @@
   const void* m_rawObjectPointer;
 };
 
-/**
- * ScriptWrappableVisitor is able to trace through the objects to get all
- * wrappers. It is used during V8 garbage collection.  When this visitor is
- * set to the v8::Isolate as its embedder heap tracer, V8 will call it during
- * its garbage collection. At the beginning, it will call TracePrologue, then
- * repeatedly it will call AdvanceTracing, and at the end it will call
- * TraceEpilogue. Everytime V8 finds new wrappers, it will let the tracer know
- * using RegisterV8References.
- */
+// ScriptWrappableVisitor is able to trace through the objects to get all
+// wrappers. It is used during V8 garbage collection.  When this visitor is
+// set to the v8::Isolate as its embedder heap tracer, V8 will call it during
+// its garbage collection. At the beginning, it will call TracePrologue, then
+// repeatedly it will call AdvanceTracing, and at the end it will call
+// TraceEpilogue. Everytime V8 finds new wrappers, it will let the tracer
+// know using RegisterV8References.
 class CORE_EXPORT ScriptWrappableVisitor : public v8::EmbedderHeapTracer,
                                            public WrapperVisitor {
   DISALLOW_IMPLICIT_CONSTRUCTORS(ScriptWrappableVisitor);
@@ -91,15 +85,12 @@
  public:
   ScriptWrappableVisitor(v8::Isolate* isolate) : m_isolate(isolate){};
   ~ScriptWrappableVisitor() override;
-  /**
-     * Replace all dead objects in the marking deque with nullptr after oilpan
-     * gc.
-     */
+
+  // Replace all dead objects in the marking deque with nullptr after oilpan
+  // gc.
   static void invalidateDeadObjectsInMarkingDeque(v8::Isolate*);
 
-  /**
-     * Immediately clean up all wrappers.
-     */
+  // Immediately clean up all wrappers.
   static void performCleanup(v8::Isolate*);
 
   void TracePrologue() override;
@@ -182,12 +173,10 @@
   }
 
   bool markWrapperHeader(HeapObjectHeader*) const;
-  /**
-     * Mark wrappers in all worlds for the given script wrappable as alive in
-     * V8.
-     */
+
+  // Mark wrappers in all worlds for the given script wrappable as alive in
+  // V8.
   void markWrappersInAllWorlds(const ScriptWrappable*) const override;
-  void markWrappersInAllWorlds(const void*) const override {}
 
   WTF::Deque<WrapperMarkingData>* getMarkingDeque() { return &m_markingDeque; }
   WTF::Deque<WrapperMarkingData>* getVerifierDeque() {
@@ -198,70 +187,55 @@
   }
 
  private:
-  /**
-     * Is wrapper tracing currently in progress? True if TracePrologue has been
-     * called, and TraceEpilogue has not yet been called.
-     */
+  // Returns true if wrapper tracing is currently in progress, i.e.,
+  // TracePrologue has been called, and TraceEpilogue has not yet been called.
   bool m_tracingInProgress = false;
-  /**
-     * Is AdvanceTracing currently running? If not, we know that all calls of
-     * pushToMarkingDeque are from V8 or new wrapper associations. And this
-     * information is used by the verifier feature.
-     */
+
+  // Is AdvanceTracing currently running? If not, we know that all calls of
+  // pushToMarkingDeque are from V8 or new wrapper associations. And this
+  // information is used by the verifier feature.
   bool m_advancingTracing = false;
 
-  /**
-     * Indicates whether an idle task for a lazy cleanup has already been
-     * scheduled.  The flag is used to avoid scheduling multiple idle tasks for
-     * cleaning up.
-     */
+  // Indicates whether an idle task for a lazy cleanup has already been
+  // scheduled. The flag is used to avoid scheduling multiple idle tasks for
+  // cleaning up.
   bool m_idleCleanupTaskScheduled = false;
 
-  /**
-     * Indicates whether cleanup should currently happen.
-     * The flag is used to avoid cleaning up in the next GC cycle.
-     */
+  // Indicates whether cleanup should currently happen. The flag is used to
+  // avoid cleaning up in the next GC cycle.
   bool m_shouldCleanup = false;
 
-  /**
-     * Immediately cleans up all wrappers.
-     */
+  // Immediately cleans up all wrappers.
   void performCleanup();
 
-  /**
-     * Schedule an idle task to perform a lazy (incremental) clean up of
-     * wrappers.
-     */
+  // Schedule an idle task to perform a lazy (incremental) clean up of
+  // wrappers.
   void scheduleIdleLazyCleanup();
   void performLazyCleanup(double deadlineSeconds);
 
-  /**
-     * Collection of objects we need to trace from. We assume it is safe to hold
-     * on to the raw pointers because:
-     *     * oilpan object cannot move
-     *     * oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete
-     *       all obsolete objects
-     */
+  // Collection of objects we need to trace from. We assume it is safe to hold
+  // on to the raw pointers because:
+  // - oilpan object cannot move
+  // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete all
+  //   obsolete objects
   mutable WTF::Deque<WrapperMarkingData> m_markingDeque;
-  /**
-     * Collection of objects we started tracing from. We assume it is safe to
-     * hold on to the raw pointers because:
-     *     * oilpan object cannot move
-     *     * oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete
-     *       all obsolete objects
-     *
-     * These objects are used when TraceWrappablesVerifier feature is enabled to
-     * verify that all objects reachable in the atomic pause were marked
-     * incrementally. If not, there is one or multiple write barriers missing.
-     */
+
+  // Collection of objects we started tracing from. We assume it is safe to
+  // hold on to the raw pointers because:
+  // - oilpan object cannot move
+  // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete
+  //   all obsolete objects
+  //
+  // These objects are used when TraceWrappablesVerifier feature is enabled to
+  // verify that all objects reachable in the atomic pause were marked
+  // incrementally. If not, there is one or multiple write barriers missing.
   mutable WTF::Deque<WrapperMarkingData> m_verifierDeque;
-  /**
-     * Collection of headers we need to unmark after the tracing finished. We
-     * assume it is safe to hold on to the headers because:
-     *     * oilpan objects cannot move
-     *     * objects this headers belong to are invalidated by the oilpan
-     *       gc in invalidateDeadObjectsInMarkingDeque.
-     */
+
+  // Collection of headers we need to unmark after the tracing finished. We
+  // assume it is safe to hold on to the headers because:
+  // - oilpan objects cannot move
+  // - objects this headers belong to are invalidated by the oilpan GC in
+  //   invalidateDeadObjectsInMarkingDeque.
   mutable WTF::Vector<HeapObjectHeader*> m_headersToUnmark;
   v8::Isolate* m_isolate;
 };
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorTest.cpp
index c28a5b0..0442037 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorTest.cpp
@@ -170,7 +170,7 @@
   ScriptWrappableVisitor* visitor =
       V8PerIsolateData::from(scope.isolate())->scriptWrappableVisitor();
   auto header = HeapObjectHeader::fromPayload(object);
-  visitor->getHeadersToUnmark()->append(header);
+  visitor->getHeadersToUnmark()->push_back(header);
 
   preciselyCollectGarbage();
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorVerifier.h b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorVerifier.h
index 27c8dc12..c840a7b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorVerifier.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptWrappableVisitorVerifier.h
@@ -64,7 +64,6 @@
     return false;
   }
   void markWrappersInAllWorlds(const ScriptWrappable*) const override {}
-  void markWrappersInAllWorlds(const void*) const override {}
 
  private:
   mutable WTF::HashSet<HeapObjectHeader*> m_visitedHeaders;
diff --git a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
index 7c0ef07..934396330 100644
--- a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
@@ -198,29 +198,30 @@
       v8::Local<v8::Object> wrapper =
           worlds[i]->domDataStore().get(object, isolate);
       if (!wrapper.IsEmpty())
-        buffers.append(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
+        buffers.push_back(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
     }
   } else {
     v8::Local<v8::Object> wrapper =
         DOMWrapperWorld::current(isolate).domDataStore().get(object, isolate);
     if (!wrapper.IsEmpty())
-      buffers.append(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
+      buffers.push_back(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
   }
 }
 
-void SerializedScriptValue::transferImageBitmaps(
+std::unique_ptr<ImageBitmapContentsArray>
+SerializedScriptValue::transferImageBitmapContents(
     v8::Isolate* isolate,
     const ImageBitmapArray& imageBitmaps,
     ExceptionState& exceptionState) {
   if (!imageBitmaps.size())
-    return;
+    return nullptr;
 
   for (size_t i = 0; i < imageBitmaps.size(); ++i) {
     if (imageBitmaps[i]->isNeutered()) {
       exceptionState.throwDOMException(
           DataCloneError, "ImageBitmap at index " + String::number(i) +
                               " is already detached.");
-      return;
+      return nullptr;
     }
   }
 
@@ -231,8 +232,17 @@
     if (visited.contains(imageBitmaps[i]))
       continue;
     visited.add(imageBitmaps[i]);
-    contents->append(imageBitmaps[i]->transfer());
+    contents->push_back(imageBitmaps[i]->transfer());
   }
+  return contents;
+}
+
+void SerializedScriptValue::transferImageBitmaps(
+    v8::Isolate* isolate,
+    const ImageBitmapArray& imageBitmaps,
+    ExceptionState& exceptionState) {
+  std::unique_ptr<ImageBitmapContentsArray> contents =
+      transferImageBitmapContents(isolate, imageBitmaps, exceptionState);
   m_imageBitmapContentsArray = std::move(contents);
 }
 
@@ -332,7 +342,7 @@
                                 " is a duplicate of an earlier port.");
         return false;
       }
-      transferables.messagePorts.append(port);
+      transferables.messagePorts.push_back(port);
     } else if (transferableObject->IsArrayBuffer()) {
       DOMArrayBuffer* arrayBuffer = V8ArrayBuffer::toImpl(
           v8::Local<v8::Object>::Cast(transferableObject));
@@ -342,7 +352,7 @@
                                 " is a duplicate of an earlier ArrayBuffer.");
         return false;
       }
-      transferables.arrayBuffers.append(arrayBuffer);
+      transferables.arrayBuffers.push_back(arrayBuffer);
     } else if (transferableObject->IsSharedArrayBuffer()) {
       DOMSharedArrayBuffer* sharedArrayBuffer = V8SharedArrayBuffer::toImpl(
           v8::Local<v8::Object>::Cast(transferableObject));
@@ -353,7 +363,7 @@
                 " is a duplicate of an earlier SharedArrayBuffer.");
         return false;
       }
-      transferables.arrayBuffers.append(sharedArrayBuffer);
+      transferables.arrayBuffers.push_back(sharedArrayBuffer);
     } else if (V8ImageBitmap::hasInstance(transferableObject, isolate)) {
       ImageBitmap* imageBitmap = V8ImageBitmap::toImpl(
           v8::Local<v8::Object>::Cast(transferableObject));
@@ -363,7 +373,7 @@
                                 " is a duplicate of an earlier ImageBitmap.");
         return false;
       }
-      transferables.imageBitmaps.append(imageBitmap);
+      transferables.imageBitmaps.push_back(imageBitmap);
     } else if (V8OffscreenCanvas::hasInstance(transferableObject, isolate)) {
       OffscreenCanvas* offscreenCanvas = V8OffscreenCanvas::toImpl(
           v8::Local<v8::Object>::Cast(transferableObject));
@@ -374,7 +384,7 @@
                 " is a duplicate of an earlier OffscreenCanvas.");
         return false;
       }
-      transferables.offscreenCanvases.append(offscreenCanvas);
+      transferables.offscreenCanvases.push_back(offscreenCanvas);
     } else {
       exceptionState.throwTypeError("Value at index " + String::number(i) +
                                     " does not have a transferable type.");
diff --git a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.h b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.h
index 711c01f..85f0f0dd 100644
--- a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.h
+++ b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.h
@@ -123,6 +123,11 @@
       const ArrayBufferArray&,
       ExceptionState&);
 
+  static std::unique_ptr<ImageBitmapContentsArray> transferImageBitmapContents(
+      v8::Isolate*,
+      const ImageBitmapArray&,
+      ExceptionState&);
+
   // Informs the V8 about external memory allocated and owned by this object.
   // Large values should contribute to GC counters to eventually trigger a GC,
   // otherwise flood of postMessage() can cause OOM.
diff --git a/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp b/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
index 4d9c863..ec48f15 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ToV8Test.cpp
@@ -155,60 +155,60 @@
 TEST(ToV8Test, stringVectors) {
   V8TestingScope scope;
   Vector<String> stringVector;
-  stringVector.append("foo");
-  stringVector.append("bar");
+  stringVector.push_back("foo");
+  stringVector.push_back("bar");
   TEST_TOV8("foo,bar", stringVector);
 
   Vector<AtomicString> atomicStringVector;
-  atomicStringVector.append("quux");
-  atomicStringVector.append("bar");
+  atomicStringVector.push_back("quux");
+  atomicStringVector.push_back("bar");
   TEST_TOV8("quux,bar", atomicStringVector);
 }
 
 TEST(ToV8Test, basicTypeVectors) {
   V8TestingScope scope;
   Vector<int> intVector;
-  intVector.append(42);
-  intVector.append(23);
+  intVector.push_back(42);
+  intVector.push_back(23);
   TEST_TOV8("42,23", intVector);
 
   Vector<long> longVector;
-  longVector.append(31773);
-  longVector.append(404);
+  longVector.push_back(31773);
+  longVector.push_back(404);
   TEST_TOV8("31773,404", longVector);
 
   Vector<unsigned> unsignedVector;
-  unsignedVector.append(1);
-  unsignedVector.append(2);
+  unsignedVector.push_back(1);
+  unsignedVector.push_back(2);
   TEST_TOV8("1,2", unsignedVector);
 
   Vector<unsigned long> unsignedLongVector;
-  unsignedLongVector.append(1001);
-  unsignedLongVector.append(2002);
+  unsignedLongVector.push_back(1001);
+  unsignedLongVector.push_back(2002);
   TEST_TOV8("1001,2002", unsignedLongVector);
 
   Vector<float> floatVector;
-  floatVector.append(0.125);
-  floatVector.append(1.);
+  floatVector.push_back(0.125);
+  floatVector.push_back(1.);
   TEST_TOV8("0.125,1", floatVector);
 
   Vector<double> doubleVector;
-  doubleVector.append(2.3);
-  doubleVector.append(4.2);
+  doubleVector.push_back(2.3);
+  doubleVector.push_back(4.2);
   TEST_TOV8("2.3,4.2", doubleVector);
 
   Vector<bool> boolVector;
-  boolVector.append(true);
-  boolVector.append(true);
-  boolVector.append(false);
+  boolVector.push_back(true);
+  boolVector.push_back(true);
+  boolVector.push_back(false);
   TEST_TOV8("true,true,false", boolVector);
 }
 
 TEST(ToV8Test, dictionaryVector) {
   V8TestingScope scope;
   Vector<std::pair<String, int>> dictionary;
-  dictionary.append(std::make_pair("one", 1));
-  dictionary.append(std::make_pair("two", 2));
+  dictionary.push_back(std::make_pair("one", 1));
+  dictionary.push_back(std::make_pair("two", 2));
   TEST_TOV8("[object Object]", dictionary);
   v8::Local<v8::Context> context = scope.getScriptState()->context();
   v8::Local<v8::Object> result =
@@ -226,9 +226,9 @@
 TEST(ToV8Test, heapVector) {
   V8TestingScope scope;
   HeapVector<Member<GarbageCollectedScriptWrappable>> v;
-  v.append(new GarbageCollectedScriptWrappable("hoge"));
-  v.append(new GarbageCollectedScriptWrappable("fuga"));
-  v.append(nullptr);
+  v.push_back(new GarbageCollectedScriptWrappable("hoge"));
+  v.push_back(new GarbageCollectedScriptWrappable("fuga"));
+  v.push_back(nullptr);
 
   TEST_TOV8("hoge,fuga,", v);
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMember.h b/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMember.h
index 611f028..39b2560 100644
--- a/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMember.h
+++ b/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMember.h
@@ -94,17 +94,17 @@
   HeapVector<TraceWrapperMember<T>> temp;
   temp.reserveCapacity(a.size());
   for (auto item : a) {
-    temp.append(TraceWrapperMember<T>(parentForB, item.get()));
+    temp.push_back(TraceWrapperMember<T>(parentForB, item.get()));
   }
   a.clear();
   a.reserveCapacity(b.size());
   for (auto item : b) {
-    a.append(TraceWrapperMember<T>(parentForA, item.get()));
+    a.push_back(TraceWrapperMember<T>(parentForA, item.get()));
   }
   b.clear();
   b.reserveCapacity(temp.size());
   for (auto item : temp) {
-    b.append(TraceWrapperMember<T>(parentForB, item.get()));
+    b.push_back(TraceWrapperMember<T>(parentForB, item.get()));
   }
 }
 
@@ -121,17 +121,17 @@
   HeapVector<TraceWrapperMember<T>> temp;
   temp.reserveCapacity(a.size());
   for (auto item : a) {
-    temp.append(TraceWrapperMember<T>(nullptr, item.get()));
+    temp.push_back(TraceWrapperMember<T>(nullptr, item.get()));
   }
   a.clear();
   a.reserveCapacity(b.size());
   for (auto item : b) {
-    a.append(TraceWrapperMember<T>(parentForA, item.get()));
+    a.push_back(TraceWrapperMember<T>(parentForA, item.get()));
   }
   b.clear();
   b.reserveCapacity(temp.size());
   for (auto item : temp) {
-    b.append(item.get());
+    b.push_back(item.get());
   }
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMemberTest.cpp b/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMemberTest.cpp
index 19cd82e..3a3b637ac 100644
--- a/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMemberTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/TraceWrapperMemberTest.cpp
@@ -15,12 +15,12 @@
   HeapVector<Wrapper> vector1;
   DeathAwareScriptWrappable* parent1 = DeathAwareScriptWrappable::create();
   DeathAwareScriptWrappable* child1 = DeathAwareScriptWrappable::create();
-  vector1.append(Wrapper(parent1, child1));
+  vector1.push_back(Wrapper(parent1, child1));
 
   HeapVector<Wrapper> vector2;
   DeathAwareScriptWrappable* parent2 = DeathAwareScriptWrappable::create();
   DeathAwareScriptWrappable* child2 = DeathAwareScriptWrappable::create();
-  vector2.append(Wrapper(parent2, child2));
+  vector2.push_back(Wrapper(parent2, child2));
 
   swap(vector1, vector2, parent1, parent2);
   EXPECT_EQ(parent1, vector1.front().parent());
@@ -33,11 +33,11 @@
   HeapVector<Wrapper> vector1;
   DeathAwareScriptWrappable* parent1 = DeathAwareScriptWrappable::create();
   DeathAwareScriptWrappable* child1 = DeathAwareScriptWrappable::create();
-  vector1.append(Wrapper(parent1, child1));
+  vector1.push_back(Wrapper(parent1, child1));
 
   HeapVector<Member<DeathAwareScriptWrappable>> vector2;
   DeathAwareScriptWrappable* child2 = DeathAwareScriptWrappable::create();
-  vector2.append(child2);
+  vector2.push_back(child2);
 
   swap(vector1, vector2, parent1);
   EXPECT_EQ(1u, vector1.size());
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
index 860cfd51..29a5a46 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -1075,8 +1075,8 @@
     }
     if (doneBoolean->Value())
       break;
-    result.append(NativeValueTraits<ValueType>::nativeValue(isolate, element,
-                                                            exceptionState));
+    result.push_back(NativeValueTraits<ValueType>::nativeValue(isolate, element,
+                                                               exceptionState));
   }
   return result;
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8GCController.cpp b/third_party/WebKit/Source/bindings/core/v8/V8GCController.cpp
index 43a67b6a..f12153a5 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8GCController.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8GCController.cpp
@@ -200,7 +200,7 @@
       m_isolate->SetObjectGroupId(
           *value, v8::UniqueId(reinterpret_cast<intptr_t>(root)));
       if (m_constructRetainedObjectInfos)
-        m_groupsWhichNeedRetainerInfo.append(root);
+        m_groupsWhichNeedRetainerInfo.push_back(root);
     } else if (classId == WrapperTypeInfo::ObjectClassId) {
       if (!RuntimeEnabledFeatures::traceWrappablesEnabled()) {
         type->visitDOMWrapper(m_isolate, toScriptWrappable(wrapper),
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h b/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h
index cad05b2..4641bb36 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverCallback.h
@@ -28,8 +28,9 @@
 
   void handleEvent(const HeapVector<Member<IntersectionObserverEntry>>&,
                    IntersectionObserver&) override;
+
   ExecutionContext* getExecutionContext() const override {
-    return ContextLifecycleObserver::getExecutionContext();
+    return m_scriptState->getExecutionContext();
   }
 
  private:
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8MutationCallback.h b/third_party/WebKit/Source/bindings/core/v8/V8MutationCallback.h
index 69bd90a..5dc0f7a 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8MutationCallback.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8MutationCallback.h
@@ -28,6 +28,7 @@
 
 #include "bindings/core/v8/ActiveDOMCallback.h"
 #include "bindings/core/v8/ScopedPersistent.h"
+#include "bindings/core/v8/ScriptState.h"
 #include "core/dom/MutationCallback.h"
 #include "wtf/RefPtr.h"
 #include <v8.h>
@@ -35,7 +36,6 @@
 namespace blink {
 
 class ExecutionContext;
-class ScriptState;
 
 class V8MutationCallback final : public MutationCallback,
                                  public ActiveDOMCallback {
@@ -51,8 +51,9 @@
 
   void call(const HeapVector<Member<MutationRecord>>&,
             MutationObserver*) override;
+
   ExecutionContext* getExecutionContext() const override {
-    return ContextLifecycleObserver::getExecutionContext();
+    return m_scriptState->getExecutionContext();
   }
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8PerContextData.cpp b/third_party/WebKit/Source/bindings/core/v8/V8PerContextData.cpp
index 5fcbae5..56a922d9 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8PerContextData.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8PerContextData.cpp
@@ -172,7 +172,7 @@
 
 void V8PerContextData::addCustomElementBinding(
     std::unique_ptr<V0CustomElementBinding> binding) {
-  m_customElementBindings.append(std::move(binding));
+  m_customElementBindings.push_back(std::move(binding));
 }
 
 v8::Local<v8::Value> V8PerContextData::compiledPrivateScript(String className) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp b/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
index dd91e90..6cb772b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8PerIsolateData.cpp
@@ -365,7 +365,7 @@
 }
 
 void V8PerIsolateData::addEndOfScopeTask(std::unique_ptr<EndOfScopeTask> task) {
-  m_endOfScopeTasks.append(std::move(task));
+  m_endOfScopeTasks.push_back(std::move(task));
 }
 
 void V8PerIsolateData::runEndOfScopeTasks() {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8PersistentValueVector.h b/third_party/WebKit/Source/bindings/core/v8/V8PersistentValueVector.h
index 45058f5..d5f37c8 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8PersistentValueVector.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8PersistentValueVector.h
@@ -44,7 +44,7 @@
  public:
   typedef Vector<v8::PersistentContainerValue> Impl;
   static void Append(Impl* impl, v8::PersistentContainerValue value) {
-    impl->append(value);
+    impl->push_back(value);
   }
   static bool IsEmpty(const Impl* impl) { return impl->isEmpty(); }
   static size_t Size(const Impl* impl) { return impl->size(); }
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8ResizeObserverCallbackCustom.cpp b/third_party/WebKit/Source/bindings/core/v8/V8ResizeObserverCallbackCustom.cpp
index b23f4238..88d364ba 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8ResizeObserverCallbackCustom.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8ResizeObserverCallbackCustom.cpp
@@ -43,7 +43,7 @@
   v8::TryCatch exceptionCatcher(m_scriptState->isolate());
   exceptionCatcher.SetVerbose(true);
   V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
-                               getExecutionContext(), thisObject,
+                               m_scriptState->getExecutionContext(), thisObject,
                                WTF_ARRAY_LENGTH(argv), argv, isolate);
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.cpp b/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.cpp
index 9d053dd..9aae8791 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.cpp
@@ -104,7 +104,6 @@
     v8::MaybeLocal<v8::Function> attributeChanged)
     : V0CustomElementLifecycleCallbacks(
           flagSet(attached, detached, attributeChanged)),
-      ContextLifecycleObserver(scriptState->getExecutionContext()),
       m_scriptState(scriptState),
       m_prototype(scriptState->isolate(), prototype),
       m_created(scriptState->isolate(), created),
@@ -122,7 +121,7 @@
 }
 
 V8PerContextData* V8V0CustomElementLifecycleCallbacks::creationContextData() {
-  if (!getExecutionContext())
+  if (!m_scriptState->contextIsValid())
     return 0;
 
   v8::Local<v8::Context> context = m_scriptState->context();
@@ -151,9 +150,6 @@
   // FIXME: callbacks while paused should be queued up for execution to
   // continue then be delivered in order rather than delivered immediately.
   // Bug 329665 tracks similar behavior for other synchronous events.
-  if (!getExecutionContext() || getExecutionContext()->isContextDestroyed())
-    return;
-
   if (!m_scriptState->contextIsValid())
     return;
 
@@ -181,8 +177,8 @@
 
   v8::TryCatch exceptionCatcher(isolate);
   exceptionCatcher.SetVerbose(true);
-  V8ScriptRunner::callFunction(callback, getExecutionContext(), receiver, 0, 0,
-                               isolate);
+  V8ScriptRunner::callFunction(callback, m_scriptState->getExecutionContext(),
+                               receiver, 0, 0, isolate);
 }
 
 void V8V0CustomElementLifecycleCallbacks::attached(Element* element) {
@@ -201,9 +197,6 @@
   // FIXME: callbacks while paused should be queued up for execution to
   // continue then be delivered in order rather than delivered immediately.
   // Bug 329665 tracks similar behavior for other synchronous events.
-  if (!getExecutionContext() || getExecutionContext()->isContextDestroyed())
-    return;
-
   if (!m_scriptState->contextIsValid())
     return;
   ScriptState::Scope scope(m_scriptState.get());
@@ -226,8 +219,8 @@
 
   v8::TryCatch exceptionCatcher(isolate);
   exceptionCatcher.SetVerbose(true);
-  V8ScriptRunner::callFunction(callback, getExecutionContext(), receiver,
-                               WTF_ARRAY_LENGTH(argv), argv, isolate);
+  V8ScriptRunner::callFunction(callback, m_scriptState->getExecutionContext(),
+                               receiver, WTF_ARRAY_LENGTH(argv), argv, isolate);
 }
 
 void V8V0CustomElementLifecycleCallbacks::call(
@@ -236,9 +229,6 @@
   // FIXME: callbacks while paused should be queued up for execution to
   // continue then be delivered in order rather than delivered immediately.
   // Bug 329665 tracks similar behavior for other synchronous events.
-  if (!getExecutionContext() || getExecutionContext()->isContextDestroyed())
-    return;
-
   if (!m_scriptState->contextIsValid())
     return;
   ScriptState::Scope scope(m_scriptState.get());
@@ -254,13 +244,12 @@
 
   v8::TryCatch exceptionCatcher(isolate);
   exceptionCatcher.SetVerbose(true);
-  V8ScriptRunner::callFunction(callback, getExecutionContext(), receiver, 0, 0,
-                               isolate);
+  V8ScriptRunner::callFunction(callback, m_scriptState->getExecutionContext(),
+                               receiver, 0, 0, isolate);
 }
 
 DEFINE_TRACE(V8V0CustomElementLifecycleCallbacks) {
   V0CustomElementLifecycleCallbacks::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.h b/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.h
index 86a7625..7a36751 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8V0CustomElementLifecycleCallbacks.h
@@ -33,7 +33,6 @@
 
 #include "bindings/core/v8/ScopedPersistent.h"
 #include "bindings/core/v8/ScriptState.h"
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/dom/custom/V0CustomElementLifecycleCallbacks.h"
 #include "wtf/PassRefPtr.h"
 #include <memory>
@@ -46,10 +45,7 @@
 class V8PerContextData;
 
 class V8V0CustomElementLifecycleCallbacks final
-    : public V0CustomElementLifecycleCallbacks,
-      public ContextLifecycleObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(V8V0CustomElementLifecycleCallbacks);
-
+    : public V0CustomElementLifecycleCallbacks {
  public:
   static V8V0CustomElementLifecycleCallbacks* create(
       ScriptState*,
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
index 598b9c5..2f0c099 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
@@ -331,7 +331,7 @@
               extension->name(), extensionGroup, worldId))
         continue;
 
-      extensionNames.append(extension->name());
+      extensionNames.push_back(extension->name());
     }
   }
   v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(),
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
index 28a13dde..77bec2c 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.cpp
@@ -48,6 +48,19 @@
     entry.value->clearForNavigation();
 }
 
+void WindowProxyManager::updateSecurityOrigin(SecurityOrigin* securityOrigin) {
+  m_windowProxy->updateSecurityOrigin(securityOrigin);
+
+  for (auto& entry : m_isolatedWorlds) {
+    WindowProxy* isolatedWindowProxy = entry.value.get();
+    if (!isolatedWindowProxy->isContextInitialized())
+      continue;
+    SecurityOrigin* isolatedSecurityOrigin =
+        isolatedWindowProxy->world().isolatedWorldSecurityOrigin();
+    isolatedWindowProxy->updateSecurityOrigin(isolatedSecurityOrigin);
+  }
+}
+
 WindowProxy* WindowProxyManager::existingWindowProxy(DOMWrapperWorld& world) {
   if (world.isMainWorld())
     return m_windowProxy->isContextInitialized() ? m_windowProxy.get()
@@ -59,19 +72,6 @@
   return iter->value->isContextInitialized() ? iter->value.get() : nullptr;
 }
 
-void WindowProxyManager::collectIsolatedContexts(
-    Vector<std::pair<ScriptState*, SecurityOrigin*>>& result) {
-  for (auto& entry : m_isolatedWorlds) {
-    WindowProxy* isolatedWorldWindowProxy = entry.value.get();
-    SecurityOrigin* origin =
-        isolatedWorldWindowProxy->world().isolatedWorldSecurityOrigin();
-    if (!isolatedWorldWindowProxy->isContextInitialized())
-      continue;
-    result.append(
-        std::make_pair(isolatedWorldWindowProxy->getScriptState(), origin));
-  }
-}
-
 void WindowProxyManager::releaseGlobals(
     HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>& map) {
   map.add(&m_windowProxy->world(), m_windowProxy->releaseGlobal());
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
index 47f134d..10610fe6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxyManager.h
@@ -15,7 +15,6 @@
 
 class DOMWrapperWorld;
 class Frame;
-class ScriptState;
 class SecurityOrigin;
 class WindowProxy;
 
@@ -35,10 +34,12 @@
   void clearForClose();
   void clearForNavigation();
 
+  // Sets the given security origin to the main world's context.  Also updates
+  // the security origin of the context for each isolated world.
+  void updateSecurityOrigin(SecurityOrigin*);
+
   // For devtools:
   WindowProxy* existingWindowProxy(DOMWrapperWorld&);
-  void collectIsolatedContexts(
-      Vector<std::pair<ScriptState*, SecurityOrigin*>>&);
 
   void releaseGlobals(HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>&);
   void setGlobals(const HashMap<DOMWrapperWorld*, v8::Local<v8::Object>>&);
diff --git a/third_party/WebKit/Source/bindings/core/v8/WorkerOrWorkletScriptController.cpp b/third_party/WebKit/Source/bindings/core/v8/WorkerOrWorkletScriptController.cpp
index af15442..588ab45 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WorkerOrWorkletScriptController.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WorkerOrWorkletScriptController.cpp
@@ -163,7 +163,7 @@
       const V8Extensions& extensions = ScriptController::registeredExtensions();
       extensionNames.reserveInitialCapacity(extensions.size());
       for (const auto* extension : extensions)
-        extensionNames.append(extension->name());
+        extensionNames.push_back(extension->name());
     }
     v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(),
                                                       extensionNames.data());
diff --git a/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp b/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
index 529e7c0..1fd917a 100644
--- a/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/custom/V8CSSStyleDeclarationCustom.cpp
@@ -158,7 +158,7 @@
     for (int id = firstCSSProperty; id <= lastCSSProperty; ++id) {
       CSSPropertyID propertyId = static_cast<CSSPropertyID>(id);
       if (CSSPropertyMetadata::isEnabledProperty(propertyId))
-        propertyNames.append(getJSPropertyName(propertyId));
+        propertyNames.push_back(getJSPropertyName(propertyId));
     }
     std::sort(propertyNames.begin(), propertyNames.end(),
               codePointCompareLessThan);
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
index 98b5457..0d807a2 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueDeserializer.cpp
@@ -97,7 +97,7 @@
     m_transferredImageBitmaps.reserveInitialCapacity(
         imageBitmapContents->size());
     for (const auto& image : *imageBitmapContents)
-      m_transferredImageBitmaps.append(ImageBitmap::create(image));
+      m_transferredImageBitmaps.push_back(ImageBitmap::create(image));
   }
 }
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
index 1568332..26b1d6f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
@@ -211,7 +211,7 @@
   ASSERT_FALSE(arrayBuffer->isNeutered());
   v8::Local<v8::Value> object = eval("({ get a() { throw 'party'; }})", scope);
   Transferables transferables;
-  transferables.arrayBuffers.append(arrayBuffer);
+  transferables.arrayBuffers.push_back(arrayBuffer);
 
   roundTrip(object, scope, &exceptionState, &transferables);
   ASSERT_TRUE(exceptionState.hadException());
@@ -289,7 +289,7 @@
       makeMessagePort(scope.getExecutionContext(), &unownedChannel);
   v8::Local<v8::Value> wrapper = toV8(port, scope.getScriptState());
   Transferables transferables;
-  transferables.messagePorts.append(port);
+  transferables.messagePorts.push_back(port);
 
   v8::Local<v8::Value> result =
       roundTrip(wrapper, scope, nullptr, &transferables);
@@ -311,7 +311,7 @@
   EXPECT_TRUE(port->isNeutered());
   v8::Local<v8::Value> wrapper = toV8(port, scope.getScriptState());
   Transferables transferables;
-  transferables.messagePorts.append(port);
+  transferables.messagePorts.push_back(port);
 
   roundTrip(wrapper, scope, &exceptionState, &transferables);
   ASSERT_TRUE(hadDOMException("DataCloneError", scope.getScriptState(),
@@ -356,15 +356,15 @@
   }
   {
     MessagePortArray* ports = new MessagePortArray;
-    ports->append(port1);
+    ports->push_back(port1);
     V8ScriptValueDeserializer deserializer(scriptState, input);
     deserializer.setTransferredMessagePorts(ports);
     ASSERT_TRUE(deserializer.deserialize()->IsNull());
   }
   {
     MessagePortArray* ports = new MessagePortArray;
-    ports->append(port1);
-    ports->append(port2);
+    ports->push_back(port1);
+    ports->push_back(port2);
     V8ScriptValueDeserializer deserializer(scriptState, input);
     deserializer.setTransferredMessagePorts(ports);
     v8::Local<v8::Value> result = deserializer.deserialize();
@@ -489,7 +489,7 @@
 
   v8::Local<v8::Value> wrapper = toV8(imageBitmap, scope.getScriptState());
   Transferables transferables;
-  transferables.imageBitmaps.append(imageBitmap);
+  transferables.imageBitmaps.push_back(imageBitmap);
   v8::Local<v8::Value> result =
       roundTrip(wrapper, scope, nullptr, &transferables);
   ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.isolate()));
@@ -518,7 +518,7 @@
   canvas->setPlaceholderCanvasId(519);
   v8::Local<v8::Value> wrapper = toV8(canvas, scope.getScriptState());
   Transferables transferables;
-  transferables.offscreenCanvases.append(canvas);
+  transferables.offscreenCanvases.push_back(canvas);
   v8::Local<v8::Value> result =
       roundTrip(wrapper, scope, nullptr, &transferables);
   ASSERT_TRUE(V8OffscreenCanvas::hasInstance(result, scope.isolate()));
diff --git a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModules.cpp b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModules.cpp
index 8bc97f7..6fcdb35 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModules.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModules.cpp
@@ -218,7 +218,7 @@
       return nullptr;
     if (stack.size() >= maximumDepth)
       return nullptr;
-    stack.append(array);
+    stack.push_back(array);
 
     IDBKey::KeyArray subkeys;
     uint32_t length = array->Length();
@@ -235,9 +235,9 @@
       IDBKey* subkey = createIDBKeyFromValue(
           isolate, item, stack, exceptionState, allowExperimentalTypes);
       if (!subkey)
-        subkeys.append(IDBKey::createInvalid());
+        subkeys.push_back(IDBKey::createInvalid());
       else
-        subkeys.append(subkey);
+        subkeys.push_back(subkey);
     }
 
     stack.pop_back();
@@ -376,7 +376,7 @@
           isolate, value, array[i], exceptionState, allowExperimentalTypes);
       if (!key)
         return nullptr;
-      result.append(key);
+      result.push_back(key);
     }
     return IDBKey::createArray(result);
   }
diff --git a/third_party/WebKit/Source/bindings/modules/v8/custom/V8CustomSQLStatementErrorCallback.cpp b/third_party/WebKit/Source/bindings/modules/v8/custom/V8CustomSQLStatementErrorCallback.cpp
index 2166e85..4164179 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/custom/V8CustomSQLStatementErrorCallback.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/custom/V8CustomSQLStatementErrorCallback.cpp
@@ -69,7 +69,7 @@
   // the error callback did not return false, or there was no error callback.
   // Jump to the last step in the overall steps.
   if (!V8ScriptRunner::callFunction(m_callback.newLocal(isolate),
-                                    getExecutionContext(),
+                                    m_scriptState->getExecutionContext(),
                                     m_scriptState->context()->Global(),
                                     WTF_ARRAY_LENGTH(argv), argv, isolate)
            .ToLocal(&result))
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_methods.py b/third_party/WebKit/Source/bindings/scripts/v8_methods.py
index 92e9646..ff55df8 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_methods.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_methods.py
@@ -173,6 +173,7 @@
         includes.add('bindings/core/v8/SerializedScriptValueFactory.h')
         includes.add('bindings/core/v8/Transferables.h')
         includes.add('core/dom/DOMArrayBufferBase.h')
+        includes.add('core/frame/ImageBitmap.h')
 
     if 'LenientThis' in extended_attributes:
         raise Exception('[LenientThis] is not supported for operations.')
diff --git a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
index 573953a..c0457dc 100644
--- a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
@@ -447,22 +447,24 @@
   }
 
   RefPtr<SerializedScriptValue> message;
-  if (instance->canTransferArrayBuffer()) {
+  if (instance->canTransferArrayBuffersAndImageBitmaps()) {
     // This instance supports sending array buffers by move semantics.
     message = SerializedScriptValue::serialize(info.GetIsolate(), info[0], &transferables, nullptr, exceptionState);
     if (exceptionState.hadException())
       return;
   } else {
-    // This instance doesn't support sending array buffers by move
-    // semantics. Emulate it by copy-and-neuter semantics that sends array
-    // buffers by copy semantics and then neuters the original array
-    // buffers.
+    // This instance doesn't support sending array buffers and image bitmaps
+    // by move semantics. Emulate it by copy-and-neuter semantics that sends
+    // array buffers and image bitmaps via structured clone and then neuters
+    // the original objects
 
-    // Clear references to array buffers from transferables so that the
-    // serializer can consider the array buffers as non-transferable and
-    // copy them into the message.
+    // Clear references to array buffers and image bitmaps from transferables
+    // so that the serializer can consider the array buffers as
+    // non-transferable and serialize them into the message.
     ArrayBufferArray transferableArrayBuffers = transferables.arrayBuffers;
     transferables.arrayBuffers.clear();
+    ImageBitmapArray transferableImageBitmaps = transferables.imageBitmaps;
+    transferables.imageBitmaps.clear();
     message = SerializedScriptValue::serialize(info.GetIsolate(), info[0], &transferables, nullptr, exceptionState);
     if (exceptionState.hadException())
       return;
@@ -471,6 +473,10 @@
     SerializedScriptValue::transferArrayBufferContents(info.GetIsolate(), transferableArrayBuffers, exceptionState);
     if (exceptionState.hadException())
       return;
+    // Neuter the original image bitmaps on the sender context.
+    SerializedScriptValue::transferImageBitmapContents(info.GetIsolate(), transferableImageBitmaps, exceptionState);
+    if (exceptionState.hadException())
+      return;
   }
 
   // FIXME: Only pass context/exceptionState if instance really requires it.
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index d475d69f..2d78946 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -71,6 +71,7 @@
 #include "core/dom/TagCollection.h"
 #include "core/dom/custom/V0CustomElementProcessingStack.h"
 #include "core/frame/Deprecation.h"
+#include "core/frame/ImageBitmap.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/UseCounter.h"
 #include "core/html/HTMLCollection.h"
@@ -10245,22 +10246,24 @@
   }
 
   RefPtr<SerializedScriptValue> message;
-  if (instance->canTransferArrayBuffer()) {
+  if (instance->canTransferArrayBuffersAndImageBitmaps()) {
     // This instance supports sending array buffers by move semantics.
     message = SerializedScriptValue::serialize(info.GetIsolate(), info[0], &transferables, nullptr, exceptionState);
     if (exceptionState.hadException())
       return;
   } else {
-    // This instance doesn't support sending array buffers by move
-    // semantics. Emulate it by copy-and-neuter semantics that sends array
-    // buffers by copy semantics and then neuters the original array
-    // buffers.
+    // This instance doesn't support sending array buffers and image bitmaps
+    // by move semantics. Emulate it by copy-and-neuter semantics that sends
+    // array buffers and image bitmaps via structured clone and then neuters
+    // the original objects
 
-    // Clear references to array buffers from transferables so that the
-    // serializer can consider the array buffers as non-transferable and
-    // copy them into the message.
+    // Clear references to array buffers and image bitmaps from transferables
+    // so that the serializer can consider the array buffers as
+    // non-transferable and serialize them into the message.
     ArrayBufferArray transferableArrayBuffers = transferables.arrayBuffers;
     transferables.arrayBuffers.clear();
+    ImageBitmapArray transferableImageBitmaps = transferables.imageBitmaps;
+    transferables.imageBitmaps.clear();
     message = SerializedScriptValue::serialize(info.GetIsolate(), info[0], &transferables, nullptr, exceptionState);
     if (exceptionState.hadException())
       return;
@@ -10269,6 +10272,10 @@
     SerializedScriptValue::transferArrayBufferContents(info.GetIsolate(), transferableArrayBuffers, exceptionState);
     if (exceptionState.hadException())
       return;
+    // Neuter the original image bitmaps on the sender context.
+    SerializedScriptValue::transferImageBitmapContents(info.GetIsolate(), transferableImageBitmaps, exceptionState);
+    if (exceptionState.hadException())
+      return;
   }
 
   // FIXME: Only pass context/exceptionState if instance really requires it.
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index 1503f42..6bf236ea 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -3,7 +3,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-from collections import namedtuple
 import math
 import sys
 
@@ -13,6 +12,56 @@
 
 from name_utilities import camel_case
 
+
+class Field(object):
+    """
+    The generated ComputedStyle object is made up of a series of Fields.
+    Each Field has a name, size, type, etc, and a bunch of attributes to
+    determine which methods it will be used in.
+
+    A Field also has enough information to use any storage type in C++, such as
+    regular member variables, or more complex storage like vectors or hashmaps.
+    Almost all properties will have at least one Field, often more than one.
+
+    Fields also fall into various families, which determine the logic that is
+    used to generate them. The available field families are:
+      - 'enum', for fields that store the values of an enum
+      - 'inherited_flag', for single-bit flags that store whether a property is
+                          inherited by this style or set explicitly
+    """
+    def __init__(self, field_family, **kwargs):
+        # Values common to all fields
+        # Name of field
+        self.name = kwargs.pop('name')
+        # Property field is for
+        self.property = kwargs.pop('property')
+        # Field storage type
+        self.type = kwargs.pop('type')
+        # Bits needed for storage
+        self.size = kwargs.pop('size')
+        # Default value for field
+        self.default_value = kwargs.pop('default_value')
+        # Method names
+        self.getter_method_name = kwargs.pop('getter_method_name')
+        self.setter_method_name = kwargs.pop('setter_method_name')
+        self.initial_method_name = kwargs.pop('initial_method_name')
+        self.resetter_method_name = kwargs.pop('resetter_method_name')
+
+        # Field family: one of these must be true
+        self.is_enum = field_family == 'enum'
+        self.is_inherited_flag = field_family == 'inherited_flag'
+        assert not (self.is_enum and self.is_inherited_flag), 'Only one field family can be specified at a time'
+
+        if self.is_enum:
+            # Enum-only fields
+            self.is_inherited_method_name = kwargs.pop('is_inherited_method_name')
+        elif self.is_inherited_flag:
+            # Inherited flag-only fields
+            pass
+
+        assert len(kwargs) == 0, 'Unexpected arguments provided to Field: ' + str(kwargs)
+
+
 class ComputedStyleBaseWriter(make_style_builder.StyleBuilderWriter):
     def __init__(self, in_file_path):
         super(ComputedStyleBaseWriter, self).__init__(in_file_path)
@@ -31,32 +80,7 @@
                 enum_values = [camel_case(k) for k in property['keywords']]
                 self._computed_enums[enum_name] = enum_values
 
-        # A list of fields for the generated class.
-        # TODO(sashab): Rename this to Member, and add better documentation for what this list is for,
-        # including asserts to show inter-field dependencies.
-        Field = namedtuple('Field', [
-            # Name of field member variable
-            'name',
-            # Property field is for
-            'property',
-            # Field family: one of these must be true
-            'is_enum',
-            'is_inherited_flag',
-            # Field storage type
-            'type',
-            # Bits needed for storage
-            'size',
-            # Default value for field
-            'default_value',
-            # Method names
-            'getter_method_name',
-            'setter_method_name',
-            'initial_method_name',
-            'resetter_method_name',
-            'is_inherited_method_name',
-        ])
-
-        # A list of all the fields to be generated. Add new fields here.
+        # A list of all the fields to be generated.
         self._fields = []
         for property in self._properties.values():
             if property['keyword_only']:
@@ -76,15 +100,14 @@
                 default_value = type_name + '::' + camel_case(property['initial_keyword'])
 
                 # If the property is independent, add the single-bit sized isInherited flag
-                # to the list of member variable as well.
+                # to the list of Fields as well.
                 if property['independent']:
                     field_name_suffix_upper = property['upper_camel_name'] + 'IsInherited'
                     field_name_suffix_lower = property_name_lower + 'IsInherited'
                     self._fields.append(Field(
+                        'inherited_flag',
                         name='m_' + field_name_suffix_lower,
                         property=property,
-                        is_enum=False,
-                        is_inherited_flag=True,
                         type='bool',
                         size=1,
                         default_value='true',
@@ -92,15 +115,13 @@
                         setter_method_name='set' + field_name_suffix_upper,
                         initial_method_name='initial' + field_name_suffix_upper,
                         resetter_method_name='reset' + field_name_suffix_upper,
-                        is_inherited_method_name='',
                     ))
 
                 # Add the property itself as a member variable.
                 self._fields.append(Field(
+                    'enum',
                     name=field_name,
                     property=property,
-                    is_enum=True,
-                    is_inherited_flag=False,
                     type=type_name,
                     size=int(math.ceil(bits_needed)),
                     default_value=default_value,
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index 7c52e20c..34a22e9e 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -700,7 +700,6 @@
 
   inputs = [
     "../bindings/core/v8/PrivateScriptRunner.js",
-    "html/HTMLMarqueeElement.js",
     "xml/DocumentXMLTreeViewer.js",
   ]
   outputs = [
@@ -1116,6 +1115,7 @@
     "dom/NthIndexCacheTest.cpp",
     "dom/RangeTest.cpp",
     "dom/ScriptRunnerTest.cpp",
+    "dom/ScriptedAnimationControllerTest.cpp",
     "dom/SelectorQueryTest.cpp",
     "dom/StaticRangeTest.cpp",
     "dom/StyleElementTest.cpp",
@@ -1169,6 +1169,7 @@
     "frame/csp/CSPDirectiveListTest.cpp",
     "frame/csp/CSPSourceTest.cpp",
     "frame/csp/ContentSecurityPolicyTest.cpp",
+    "frame/csp/MediaListDirectiveTest.cpp",
     "frame/csp/SourceListDirectiveTest.cpp",
     "html/AutoplayUmaHelperTest.cpp",
     "html/FormDataTest.cpp",
diff --git a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h b/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h
index fd0f0dd..d83662c1 100644
--- a/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h
+++ b/third_party/WebKit/Source/core/animation/CSSOffsetRotationInterpolationType.h
@@ -13,7 +13,8 @@
  public:
   CSSOffsetRotationInterpolationType(PropertyHandle property)
       : CSSInterpolationType(property) {
-    DCHECK_EQ(cssProperty(), CSSPropertyOffsetRotation);
+    DCHECK(cssProperty() == CSSPropertyOffsetRotate ||
+           cssProperty() == CSSPropertyOffsetRotation);
   }
 
   InterpolationValue maybeConvertUnderlyingValue(
diff --git a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.cpp b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.cpp
index 87b6fbc5..e480166 100644
--- a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.cpp
+++ b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.cpp
@@ -16,13 +16,14 @@
     return;
 
   if (m_currentFraction == 0 || m_currentFraction == 1 || fraction == 0 ||
-      fraction == 1)
-    clearCache();
+      fraction == 1) {
+    clearConversionCache();
+  }
 
   m_currentFraction = fraction;
-  if (m_isCached && m_cachedPairConversion)
+  if (m_isConversionCached && m_cachedPairConversion)
     m_cachedPairConversion->interpolateValue(fraction, m_cachedValue);
-  // We defer the interpolation to ensureValidInterpolation() if
+  // We defer the interpolation to ensureValidConversion() if
   // m_cachedPairConversion is null.
 }
 
@@ -110,17 +111,17 @@
          (m_endKeyframe->isNeutral() && m_currentFraction != 0);
 }
 
-void InvalidatableInterpolation::clearCache() const {
-  m_isCached = false;
+void InvalidatableInterpolation::clearConversionCache() const {
+  m_isConversionCached = false;
   m_cachedPairConversion.reset();
   m_conversionCheckers.clear();
   m_cachedValue.reset();
 }
 
-bool InvalidatableInterpolation::isCacheValid(
+bool InvalidatableInterpolation::isConversionCacheValid(
     const InterpolationEnvironment& environment,
     const UnderlyingValueOwner& underlyingValueOwner) const {
-  if (!m_isCached)
+  if (!m_isConversionCached)
     return false;
   if (isNeutralKeyframeActive()) {
     if (m_cachedPairConversion && m_cachedPairConversion->isFlip())
@@ -139,13 +140,13 @@
 }
 
 const TypedInterpolationValue*
-InvalidatableInterpolation::ensureValidInterpolation(
+InvalidatableInterpolation::ensureValidConversion(
     const InterpolationEnvironment& environment,
     const UnderlyingValueOwner& underlyingValueOwner) const {
   DCHECK(!std::isnan(m_currentFraction));
-  if (isCacheValid(environment, underlyingValueOwner))
+  if (isConversionCacheValid(environment, underlyingValueOwner))
     return m_cachedValue.get();
-  clearCache();
+  clearConversionCache();
   if (m_currentFraction == 0) {
     m_cachedValue = convertSingleKeyframe(*m_startKeyframe, environment,
                                           underlyingValueOwner);
@@ -167,7 +168,7 @@
     }
     m_cachedPairConversion->interpolateValue(m_currentFraction, m_cachedValue);
   }
-  m_isCached = true;
+  m_isConversionCached = true;
   return m_cachedValue.get();
 }
 
@@ -212,8 +213,8 @@
         firstInterpolation.maybeConvertUnderlyingValue(environment));
   } else {
     const TypedInterpolationValue* firstValue =
-        firstInterpolation.ensureValidInterpolation(environment,
-                                                    underlyingValueOwner);
+        firstInterpolation.ensureValidConversion(environment,
+                                                 underlyingValueOwner);
     // Fast path for replace interpolations that are the only one to apply.
     if (interpolations.size() == 1) {
       if (firstValue) {
@@ -235,8 +236,8 @@
         toInvalidatableInterpolation(*interpolations.at(i));
     DCHECK(currentInterpolation.dependsOnUnderlyingValue());
     const TypedInterpolationValue* currentValue =
-        currentInterpolation.ensureValidInterpolation(environment,
-                                                      underlyingValueOwner);
+        currentInterpolation.ensureValidConversion(environment,
+                                                   underlyingValueOwner);
     if (!currentValue)
       continue;
     shouldApply = true;
diff --git a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
index 6307cce0..1763d46 100644
--- a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
+++ b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
@@ -49,18 +49,18 @@
         m_startKeyframe(startKeyframe),
         m_endKeyframe(endKeyframe),
         m_currentFraction(std::numeric_limits<double>::quiet_NaN()),
-        m_isCached(false) {}
+        m_isConversionCached(false) {}
 
   using ConversionCheckers = InterpolationType::ConversionCheckers;
 
   std::unique_ptr<TypedInterpolationValue> maybeConvertUnderlyingValue(
       const InterpolationEnvironment&) const;
-  const TypedInterpolationValue* ensureValidInterpolation(
+  const TypedInterpolationValue* ensureValidConversion(
       const InterpolationEnvironment&,
       const UnderlyingValueOwner&) const;
-  void clearCache() const;
-  bool isCacheValid(const InterpolationEnvironment&,
-                    const UnderlyingValueOwner&) const;
+  void clearConversionCache() const;
+  bool isConversionCacheValid(const InterpolationEnvironment&,
+                              const UnderlyingValueOwner&) const;
   bool isNeutralKeyframeActive() const;
   std::unique_ptr<PairwisePrimitiveInterpolation> maybeConvertPairwise(
       const InterpolationEnvironment&,
@@ -79,7 +79,7 @@
   RefPtr<PropertySpecificKeyframe> m_startKeyframe;
   RefPtr<PropertySpecificKeyframe> m_endKeyframe;
   double m_currentFraction;
-  mutable bool m_isCached;
+  mutable bool m_isConversionCached;
   mutable std::unique_ptr<PrimitiveInterpolation> m_cachedPairConversion;
   mutable ConversionCheckers m_conversionCheckers;
   mutable std::unique_ptr<TypedInterpolationValue> m_cachedValue;
diff --git a/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp b/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp
index b860c78..19bc5fb 100644
--- a/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp
+++ b/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp
@@ -209,6 +209,7 @@
         applicableTypes->append(
             makeUnique<CSSClipInterpolationType>(usedProperty));
         break;
+      case CSSPropertyOffsetRotate:
       case CSSPropertyOffsetRotation:
         applicableTypes->append(
             makeUnique<CSSOffsetRotationInterpolationType>(usedProperty));
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
index 6641d8a..cc0489f 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
@@ -620,6 +620,7 @@
       return createFromLength(style.offsetDistance(), style);
     case CSSPropertyOffsetPosition:
       return createFromLengthPoint(style.offsetPosition(), style);
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       return createFromDoubleAndBool(
           style.offsetRotation().angle,
diff --git a/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp b/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp
index dcb895f..efaf6f2 100644
--- a/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp
+++ b/third_party/WebKit/Source/core/clipboard/DataTransfer.cpp
@@ -40,9 +40,9 @@
 #include "core/layout/LayoutImage.h"
 #include "core/layout/LayoutObject.h"
 #include "platform/DragImage.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/clipboard/ClipboardMimeTypes.h"
 #include "platform/clipboard/ClipboardUtilities.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include <memory>
 
 namespace blink {
diff --git a/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp b/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp
index 58dfc435..84dc880 100644
--- a/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp
+++ b/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp
@@ -88,28 +88,28 @@
     CSSPropertyMaxWidth, CSSPropertyMinHeight, CSSPropertyMinWidth,
     CSSPropertyMixBlendMode, CSSPropertyObjectFit, CSSPropertyObjectPosition,
     CSSPropertyOffsetAnchor, CSSPropertyOffsetDistance, CSSPropertyOffsetPath,
-    CSSPropertyOffsetPosition, CSSPropertyOffsetRotation, CSSPropertyOpacity,
-    CSSPropertyOrphans, CSSPropertyOutlineColor, CSSPropertyOutlineOffset,
-    CSSPropertyOutlineStyle, CSSPropertyOutlineWidth, CSSPropertyOverflowAnchor,
-    CSSPropertyOverflowWrap, CSSPropertyOverflowX, CSSPropertyOverflowY,
-    CSSPropertyPaddingBottom, CSSPropertyPaddingLeft, CSSPropertyPaddingRight,
-    CSSPropertyPaddingTop, CSSPropertyPointerEvents, CSSPropertyPosition,
-    CSSPropertyResize, CSSPropertyRight, CSSPropertyScrollBehavior,
-    CSSPropertySnapHeight, CSSPropertySpeak, CSSPropertyTableLayout,
-    CSSPropertyTabSize, CSSPropertyTextAlign, CSSPropertyTextAlignLast,
-    CSSPropertyTextDecoration, CSSPropertyTextDecorationLine,
-    CSSPropertyTextDecorationStyle, CSSPropertyTextDecorationColor,
-    CSSPropertyTextDecorationSkip, CSSPropertyTextJustify,
-    CSSPropertyTextUnderlinePosition, CSSPropertyTextIndent,
-    CSSPropertyTextRendering, CSSPropertyTextShadow, CSSPropertyTextSizeAdjust,
-    CSSPropertyTextOverflow, CSSPropertyTextTransform, CSSPropertyTop,
-    CSSPropertyTouchAction, CSSPropertyTransitionDelay,
-    CSSPropertyTransitionDuration, CSSPropertyTransitionProperty,
-    CSSPropertyTransitionTimingFunction, CSSPropertyUnicodeBidi,
-    CSSPropertyVerticalAlign, CSSPropertyVisibility, CSSPropertyWhiteSpace,
-    CSSPropertyWidows, CSSPropertyWidth, CSSPropertyWillChange,
-    CSSPropertyWordBreak, CSSPropertyWordSpacing, CSSPropertyWordWrap,
-    CSSPropertyZIndex, CSSPropertyZoom,
+    CSSPropertyOffsetPosition, CSSPropertyOffsetRotate,
+    CSSPropertyOffsetRotation, CSSPropertyOpacity, CSSPropertyOrphans,
+    CSSPropertyOutlineColor, CSSPropertyOutlineOffset, CSSPropertyOutlineStyle,
+    CSSPropertyOutlineWidth, CSSPropertyOverflowAnchor, CSSPropertyOverflowWrap,
+    CSSPropertyOverflowX, CSSPropertyOverflowY, CSSPropertyPaddingBottom,
+    CSSPropertyPaddingLeft, CSSPropertyPaddingRight, CSSPropertyPaddingTop,
+    CSSPropertyPointerEvents, CSSPropertyPosition, CSSPropertyResize,
+    CSSPropertyRight, CSSPropertyScrollBehavior, CSSPropertySnapHeight,
+    CSSPropertySpeak, CSSPropertyTableLayout, CSSPropertyTabSize,
+    CSSPropertyTextAlign, CSSPropertyTextAlignLast, CSSPropertyTextDecoration,
+    CSSPropertyTextDecorationLine, CSSPropertyTextDecorationStyle,
+    CSSPropertyTextDecorationColor, CSSPropertyTextDecorationSkip,
+    CSSPropertyTextJustify, CSSPropertyTextUnderlinePosition,
+    CSSPropertyTextIndent, CSSPropertyTextRendering, CSSPropertyTextShadow,
+    CSSPropertyTextSizeAdjust, CSSPropertyTextOverflow,
+    CSSPropertyTextTransform, CSSPropertyTop, CSSPropertyTouchAction,
+    CSSPropertyTransitionDelay, CSSPropertyTransitionDuration,
+    CSSPropertyTransitionProperty, CSSPropertyTransitionTimingFunction,
+    CSSPropertyUnicodeBidi, CSSPropertyVerticalAlign, CSSPropertyVisibility,
+    CSSPropertyWhiteSpace, CSSPropertyWidows, CSSPropertyWidth,
+    CSSPropertyWillChange, CSSPropertyWordBreak, CSSPropertyWordSpacing,
+    CSSPropertyWordWrap, CSSPropertyZIndex, CSSPropertyZoom,
 
     CSSPropertyWebkitAppearance, CSSPropertyBackfaceVisibility,
     CSSPropertyWebkitBackgroundClip, CSSPropertyWebkitBackgroundOrigin,
diff --git a/third_party/WebKit/Source/core/css/CSSFontFaceSrcValue.cpp b/third_party/WebKit/Source/core/css/CSSFontFaceSrcValue.cpp
index e5ec2f6..d098c35 100644
--- a/third_party/WebKit/Source/core/css/CSSFontFaceSrcValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontFaceSrcValue.cpp
@@ -32,7 +32,6 @@
 #include "core/fetch/FetchInitiatorTypeNames.h"
 #include "core/fetch/FetchRequest.h"
 #include "core/fetch/ResourceFetcher.h"
-#include "core/loader/MixedContentChecker.h"
 #include "core/loader/resource/FontResource.h"
 #include "platform/CrossOriginAttributeValue.h"
 #include "platform/fonts/FontCache.h"
@@ -115,21 +114,11 @@
   ASSERT(document && document->fetcher());
 
   const String resourceURL = document->completeURL(m_absoluteResource);
-  if (document->fetcher()->cachedResource(KURL(ParsedURLString, resourceURL)))
-    return;
-
-  FetchRequest request(ResourceRequest(resourceURL),
-                       FetchInitiatorTypeNames::css);
-  request.setContentSecurityCheck(m_shouldCheckContentSecurityPolicy);
-  request.mutableResourceRequest().setRequestContext(
-      WebURLRequest::RequestContextFont);
-  MixedContentChecker::shouldBlockFetch(
-      document->frame(), m_fetched->resource()->lastResourceRequest(),
-      m_fetched->resource()->lastResourceRequest().url(),
-      MixedContentChecker::SendReport);
-  document->fetcher()->requestLoadStarted(
-      m_fetched->resource()->identifier(), m_fetched->resource(), request,
-      ResourceFetcher::ResourceLoadingFromCache);
+  DCHECK_EQ(m_shouldCheckContentSecurityPolicy,
+            m_fetched->resource()->options().contentSecurityPolicyOption);
+  document->fetcher()->emulateLoadStartedForInspector(
+      m_fetched->resource(), KURL(ParsedURLString, resourceURL),
+      WebURLRequest::RequestContextFont, FetchInitiatorTypeNames::css);
 }
 
 bool CSSFontFaceSrcValue::equals(const CSSFontFaceSrcValue& other) const {
diff --git a/third_party/WebKit/Source/core/css/CSSImageValue.cpp b/third_party/WebKit/Source/core/css/CSSImageValue.cpp
index 931bf35..a0dd40c 100644
--- a/third_party/WebKit/Source/core/css/CSSImageValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSImageValue.cpp
@@ -27,7 +27,6 @@
 #include "core/fetch/ImageResource.h"
 #include "core/fetch/ResourceFetcher.h"
 #include "core/frame/Settings.h"
-#include "core/loader/MixedContentChecker.h"
 #include "core/style/StyleFetchedImage.h"
 #include "core/style/StyleInvalidImage.h"
 #include "platform/CrossOriginAttributeValue.h"
@@ -83,25 +82,16 @@
     const Document& document) const {
   if (!m_cachedImage || !document.fetcher() || m_absoluteURL.isNull())
     return;
-  if (document.fetcher()->cachedResource(KURL(ParsedURLString, m_absoluteURL)))
-    return;
 
   ImageResource* resource = m_cachedImage->cachedImage();
   if (!resource)
     return;
 
-  FetchRequest request(ResourceRequest(m_absoluteURL),
-                       m_initiatorName.isEmpty() ? FetchInitiatorTypeNames::css
-                                                 : m_initiatorName,
-                       resource->options());
-  request.mutableResourceRequest().setRequestContext(
-      WebURLRequest::RequestContextImage);
-  MixedContentChecker::shouldBlockFetch(
-      document.frame(), resource->lastResourceRequest(),
-      resource->lastResourceRequest().url(), MixedContentChecker::SendReport);
-  document.fetcher()->requestLoadStarted(
-      resource->identifier(), resource, request,
-      ResourceFetcher::ResourceLoadingFromCache);
+  document.fetcher()->emulateLoadStartedForInspector(
+      resource, KURL(ParsedURLString, m_absoluteURL),
+      WebURLRequest::RequestContextImage,
+      m_initiatorName.isEmpty() ? FetchInitiatorTypeNames::css
+                                : m_initiatorName);
 }
 
 bool CSSImageValue::hasFailedOrCanceledSubresources() const {
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index 80c40e5..a270ca7d 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -1271,17 +1271,58 @@
 
 template <>
 inline EDisplay CSSIdentifierValue::convertTo() const {
-  if (m_valueID == CSSValueNone)
-    return EDisplay::None;
-
-  if (m_valueID == CSSValueWebkitFlex)
-    return EDisplay::Flex;
-  if (m_valueID == CSSValueWebkitInlineFlex)
-    return EDisplay::InlineFlex;
-
-  EDisplay display = static_cast<EDisplay>(m_valueID - CSSValueInline);
-  // TODO(sashab): Check display is a valid EDisplay here.
-  return display;
+  switch (m_valueID) {
+    case CSSValueInline:
+      return EDisplay::Inline;
+    case CSSValueBlock:
+      return EDisplay::Block;
+    case CSSValueListItem:
+      return EDisplay::ListItem;
+    case CSSValueInlineBlock:
+      return EDisplay::InlineBlock;
+    case CSSValueTable:
+      return EDisplay::Table;
+    case CSSValueInlineTable:
+      return EDisplay::InlineTable;
+    case CSSValueTableRowGroup:
+      return EDisplay::TableRowGroup;
+    case CSSValueTableHeaderGroup:
+      return EDisplay::TableHeaderGroup;
+    case CSSValueTableFooterGroup:
+      return EDisplay::TableFooterGroup;
+    case CSSValueTableRow:
+      return EDisplay::TableRow;
+    case CSSValueTableColumnGroup:
+      return EDisplay::TableColumnGroup;
+    case CSSValueTableColumn:
+      return EDisplay::TableColumn;
+    case CSSValueTableCell:
+      return EDisplay::TableCell;
+    case CSSValueTableCaption:
+      return EDisplay::TableCaption;
+    case CSSValueWebkitBox:
+      return EDisplay::WebkitBox;
+    case CSSValueWebkitInlineBox:
+      return EDisplay::WebkitInlineBox;
+    case CSSValueFlex:
+    case CSSValueWebkitFlex:
+      return EDisplay::Flex;
+    case CSSValueInlineFlex:
+    case CSSValueWebkitInlineFlex:
+      return EDisplay::InlineFlex;
+    case CSSValueGrid:
+      return EDisplay::Grid;
+    case CSSValueInlineGrid:
+      return EDisplay::InlineGrid;
+    case CSSValueContents:
+      return EDisplay::Contents;
+    case CSSValueNone:
+      return EDisplay::None;
+      break;
+    default:
+      NOTREACHED();
+      return EDisplay::None;
+  }
 }
 
 template <>
@@ -2679,7 +2720,7 @@
     case EWhiteSpace::Nowrap:
       m_valueID = CSSValueNowrap;
       break;
-    case EWhiteSpace::KhtmlNowrap:
+    case EWhiteSpace::WebkitNowrap:
       m_valueID = CSSValueWebkitNowrap;
       break;
   }
@@ -2689,7 +2730,7 @@
 inline EWhiteSpace CSSIdentifierValue::convertTo() const {
   switch (m_valueID) {
     case CSSValueWebkitNowrap:
-      return EWhiteSpace::KhtmlNowrap;
+      return EWhiteSpace::WebkitNowrap;
     case CSSValueNowrap:
       return EWhiteSpace::Nowrap;
     case CSSValuePre:
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in
index eed5ea0..442903a 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.in
+++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -183,7 +183,7 @@
 border-bottom-right-radius interpolable, initial=initialBorderRadius, converter=convertRadius
 border-bottom-style type_name=EBorderStyle, initial=initialBorderStyle
 border-bottom-width interpolable, initial=initialBorderWidth, converter=convertLineWidth<unsigned>
-border-collapse inherited, keyword_only, keywords=[separate|collapse], initial_keyword=separate
+border-collapse inherited, independent, keyword_only, keywords=[separate|collapse], initial_keyword=separate
 border-image-outset interpolable, custom_all
 border-image-repeat custom_all
 border-image-slice interpolable, custom_all
@@ -291,7 +291,8 @@
 offset-distance interpolable, converter=convertLength
 offset-path converter=convertPathOrNone
 offset-position runtime_flag=CSSOffsetPositionAnchor, interpolable, converter=convertPositionOrAuto
-offset-rotation interpolable, converter=convertOffsetRotation
+offset-rotate runtime_flag=CSSOffsetRotate, interpolable, converter=convertOffsetRotation
+offset-rotation runtime_flag=CSSOffsetRotation, interpolable, converter=convertOffsetRotation
 opacity interpolable, type_name=float
 order type_name=int
 orphans interpolable, inherited, type_name=short
diff --git a/third_party/WebKit/Source/core/css/CSSPropertyEquality.cpp b/third_party/WebKit/Source/core/css/CSSPropertyEquality.cpp
index be8e6f28..63d0f7e 100644
--- a/third_party/WebKit/Source/core/css/CSSPropertyEquality.cpp
+++ b/third_party/WebKit/Source/core/css/CSSPropertyEquality.cpp
@@ -198,6 +198,7 @@
       return a.offsetDistance() == b.offsetDistance();
     case CSSPropertyOffsetPosition:
       return a.offsetPosition() == b.offsetPosition();
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       return a.offsetRotation() == b.offsetRotation();
     case CSSPropertyOpacity:
diff --git a/third_party/WebKit/Source/core/css/CSSTiming.cpp b/third_party/WebKit/Source/core/css/CSSTiming.cpp
index abb0805..e38a4422 100644
--- a/third_party/WebKit/Source/core/css/CSSTiming.cpp
+++ b/third_party/WebKit/Source/core/css/CSSTiming.cpp
@@ -25,6 +25,11 @@
     m_parseTimeBeforeFCP += seconds;
 }
 
+void CSSTiming::recordUpdateDuration(double seconds) {
+  if (!m_paintTiming->firstContentfulPaint())
+    m_updateTimeBeforeFCP += seconds;
+}
+
 DEFINE_TRACE(CSSTiming) {
   visitor->trace(m_document);
   visitor->trace(m_paintTiming);
diff --git a/third_party/WebKit/Source/core/css/CSSTiming.h b/third_party/WebKit/Source/core/css/CSSTiming.h
index 3e9873e..871ef620 100644
--- a/third_party/WebKit/Source/core/css/CSSTiming.h
+++ b/third_party/WebKit/Source/core/css/CSSTiming.h
@@ -22,13 +22,15 @@
  public:
   virtual ~CSSTiming() {}
 
-  // TODO(csharrison): Also record update style time before first paint.
   void recordAuthorStyleSheetParseTime(double seconds);
+  void recordUpdateDuration(double seconds);
 
   double authorStyleSheetParseDurationBeforeFCP() const {
     return m_parseTimeBeforeFCP;
   }
 
+  double updateDurationBeforeFCP() const { return m_updateTimeBeforeFCP; }
+
   static CSSTiming& from(Document&);
   DECLARE_VIRTUAL_TRACE();
 
@@ -36,6 +38,7 @@
   explicit CSSTiming(Document&);
 
   double m_parseTimeBeforeFCP = 0;
+  double m_updateTimeBeforeFCP = 0;
 
   Member<Document> m_document;
   Member<PaintTiming> m_paintTiming;
diff --git a/third_party/WebKit/Source/core/css/CSSValueKeywords.in b/third_party/WebKit/Source/core/css/CSSValueKeywords.in
index 1ad63ec2..1cebf6a 100644
--- a/third_party/WebKit/Source/core/css/CSSValueKeywords.in
+++ b/third_party/WebKit/Source/core/css/CSSValueKeywords.in
@@ -332,7 +332,7 @@
 //none
 //
 // display
-// The order here must match the order of the EDisplay enum in ComputedStyleConstants.h.
+// The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
 //
 inline
 block
@@ -1106,3 +1106,8 @@
 recto
 verso
 avoid-column
+
+// shape
+// rect
+// round
+
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index d001c42..3ce7aed 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -3310,6 +3310,7 @@
     case CSSPropertyOffsetDistance:
       return zoomAdjustedPixelValueForLength(style.offsetDistance(), style);
 
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation: {
       CSSValueList* list = CSSValueList::createSpaceSeparated();
       if (style.offsetRotation().type == OffsetRotationAuto)
diff --git a/third_party/WebKit/Source/core/css/MediaFeatureNames.in b/third_party/WebKit/Source/core/css/MediaFeatureNames.in
index 61fce47d..c2902727 100644
--- a/third_party/WebKit/Source/core/css/MediaFeatureNames.in
+++ b/third_party/WebKit/Source/core/css/MediaFeatureNames.in
@@ -43,3 +43,4 @@
 resolution
 -webkit-transform-3d
 scan
+shape
diff --git a/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp b/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
index 1242991b..80da8eb 100644
--- a/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
+++ b/third_party/WebKit/Source/core/css/MediaQueryEvaluator.cpp
@@ -52,6 +52,7 @@
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/geometry/FloatRect.h"
 #include "public/platform/PointerProperties.h"
+#include "public/platform/ShapeProperties.h"
 #include "public/platform/WebDisplayMode.h"
 #include "wtf/HashMap.h"
 
@@ -687,6 +688,28 @@
          (pointer == PointerTypeFine && value.id == CSSValueFine);
 }
 
+static bool shapeMediaFeatureEval(const MediaQueryExpValue& value,
+                                  MediaFeaturePrefix,
+                                  const MediaValues& mediaValues) {
+  if (!value.isValid())
+    return true;
+
+  if (!value.isID)
+    return false;
+
+  DisplayShape shape = mediaValues.displayShape();
+
+  switch (value.id) {
+    case CSSValueRect:
+      return shape == DisplayShapeRect;
+    case CSSValueRound:
+      return shape == DisplayShapeRound;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
 static bool anyPointerMediaFeatureEval(const MediaQueryExpValue& value,
                                        MediaFeaturePrefix,
                                        const MediaValues& mediaValues) {
diff --git a/third_party/WebKit/Source/core/css/MediaQueryEvaluatorTest.cpp b/third_party/WebKit/Source/core/css/MediaQueryEvaluatorTest.cpp
index b264896..7b227378 100644
--- a/third_party/WebKit/Source/core/css/MediaQueryEvaluatorTest.cpp
+++ b/third_party/WebKit/Source/core/css/MediaQueryEvaluatorTest.cpp
@@ -64,6 +64,8 @@
     {"(display-mode: @browser)", 0},
     {"(display-mode: 'browser')", 0},
     {"(display-mode: @junk browser)", 0},
+    {"(shape: rect)", 1},
+    {"(shape: round)", 0},
     {0, 0}  // Do not remove the terminator line.
 };
 
@@ -160,6 +162,7 @@
   data.mediaType = MediaTypeNames::screen;
   data.strictMode = true;
   data.displayMode = WebDisplayModeBrowser;
+  data.displayShape = DisplayShapeRect;
   MediaValues* mediaValues = MediaValuesCached::create(data);
 
   MediaQueryEvaluator mediaQueryEvaluator(*mediaValues);
diff --git a/third_party/WebKit/Source/core/css/MediaQueryExp.cpp b/third_party/WebKit/Source/core/css/MediaQueryExp.cpp
index e6f9e4de..d41baa58 100644
--- a/third_party/WebKit/Source/core/css/MediaQueryExp.cpp
+++ b/third_party/WebKit/Source/core/css/MediaQueryExp.cpp
@@ -61,6 +61,10 @@
   if (mediaFeature == scanMediaFeature)
     return ident == CSSValueInterlace || ident == CSSValueProgressive;
 
+  if (RuntimeEnabledFeatures::mediaQueryShapeEnabled()) {
+    if (mediaFeature == shapeMediaFeature)
+      return ident == CSSValueRect || ident == CSSValueRound;
+  }
   return false;
 }
 
@@ -164,7 +168,7 @@
          mediaFeature == devicePixelRatioMediaFeature ||
          mediaFeature == resolutionMediaFeature ||
          mediaFeature == displayModeMediaFeature ||
-         mediaFeature == scanMediaFeature;
+         mediaFeature == scanMediaFeature || mediaFeature == shapeMediaFeature;
 }
 
 bool MediaQueryExp::isViewportDependent() const {
@@ -193,7 +197,8 @@
          m_mediaFeature == minDeviceHeightMediaFeature ||
          m_mediaFeature == maxDeviceAspectRatioMediaFeature ||
          m_mediaFeature == maxDeviceWidthMediaFeature ||
-         m_mediaFeature == maxDeviceHeightMediaFeature;
+         m_mediaFeature == maxDeviceHeightMediaFeature ||
+         m_mediaFeature == shapeMediaFeature;
 }
 
 MediaQueryExp::MediaQueryExp(const MediaQueryExp& other)
diff --git a/third_party/WebKit/Source/core/css/MediaValues.cpp b/third_party/WebKit/Source/core/css/MediaValues.cpp
index 298802c..4f5866a 100644
--- a/third_party/WebKit/Source/core/css/MediaValues.cpp
+++ b/third_party/WebKit/Source/core/css/MediaValues.cpp
@@ -140,6 +140,11 @@
   return frame->settings()->availableHoverTypes();
 }
 
+DisplayShape MediaValues::calculateDisplayShape(LocalFrame* frame) {
+  DCHECK(frame && frame->host());
+  return frame->host()->chromeClient().screenInfo().displayShape;
+}
+
 bool MediaValues::computeLengthImpl(double value,
                                     CSSPrimitiveValue::UnitType type,
                                     unsigned defaultFontSize,
diff --git a/third_party/WebKit/Source/core/css/MediaValues.h b/third_party/WebKit/Source/core/css/MediaValues.h
index 706e172c..6195ace 100644
--- a/third_party/WebKit/Source/core/css/MediaValues.h
+++ b/third_party/WebKit/Source/core/css/MediaValues.h
@@ -9,6 +9,7 @@
 #include "core/css/CSSPrimitiveValue.h"
 #include "platform/heap/Handle.h"
 #include "public/platform/PointerProperties.h"
+#include "public/platform/ShapeProperties.h"
 #include "public/platform/WebDisplayMode.h"
 
 namespace blink {
@@ -71,6 +72,7 @@
   virtual bool hasValues() const = 0;
 
   virtual void overrideViewportDimensions(double width, double height) = 0;
+  virtual DisplayShape displayShape() const = 0;
 
  protected:
   static double calculateViewportWidth(LocalFrame*);
@@ -89,6 +91,7 @@
   static int calculateAvailablePointerTypes(LocalFrame*);
   static HoverType calculatePrimaryHoverType(LocalFrame*);
   static int calculateAvailableHoverTypes(LocalFrame*);
+  static DisplayShape calculateDisplayShape(LocalFrame*);
   static LocalFrame* frameFrom(Document&);
 };
 
diff --git a/third_party/WebKit/Source/core/css/MediaValuesCached.cpp b/third_party/WebKit/Source/core/css/MediaValuesCached.cpp
index 3e7a806..c7cc9e07 100644
--- a/third_party/WebKit/Source/core/css/MediaValuesCached.cpp
+++ b/third_party/WebKit/Source/core/css/MediaValuesCached.cpp
@@ -43,6 +43,7 @@
     strictMode = MediaValues::calculateStrictMode(frame);
     displayMode = MediaValues::calculateDisplayMode(frame);
     mediaType = MediaValues::calculateMediaType(frame);
+    displayShape = MediaValues::calculateDisplayShape(frame);
   }
 }
 
@@ -154,4 +155,8 @@
   m_data.viewportHeight = height;
 }
 
+DisplayShape MediaValuesCached::displayShape() const {
+  return m_data.displayShape;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/MediaValuesCached.h b/third_party/WebKit/Source/core/css/MediaValuesCached.h
index 2e2bbc8..e99edff 100644
--- a/third_party/WebKit/Source/core/css/MediaValuesCached.h
+++ b/third_party/WebKit/Source/core/css/MediaValuesCached.h
@@ -32,6 +32,7 @@
     bool strictMode;
     String mediaType;
     WebDisplayMode displayMode;
+    DisplayShape displayShape;
 
     MediaValuesCachedData()
         : viewportWidth(0),
@@ -48,7 +49,8 @@
           defaultFontSize(16),
           threeDEnabled(false),
           strictMode(true),
-          displayMode(WebDisplayModeBrowser) {}
+          displayMode(WebDisplayModeBrowser),
+          displayShape(DisplayShapeRect) {}
 
     explicit MediaValuesCachedData(Document&);
 
@@ -70,6 +72,7 @@
       data.strictMode = strictMode;
       data.mediaType = mediaType.isolatedCopy();
       data.displayMode = displayMode;
+      data.displayShape = displayShape;
       return data;
     }
   };
@@ -101,6 +104,7 @@
   bool hasValues() const override;
   const String mediaType() const override;
   WebDisplayMode displayMode() const override;
+  DisplayShape displayShape() const override;
 
   void overrideViewportDimensions(double width, double height) override;
 
diff --git a/third_party/WebKit/Source/core/css/MediaValuesDynamic.cpp b/third_party/WebKit/Source/core/css/MediaValuesDynamic.cpp
index acc4f25..99a9345c 100644
--- a/third_party/WebKit/Source/core/css/MediaValuesDynamic.cpp
+++ b/third_party/WebKit/Source/core/css/MediaValuesDynamic.cpp
@@ -132,6 +132,10 @@
   return calculateStrictMode(m_frame);
 }
 
+DisplayShape MediaValuesDynamic::displayShape() const {
+  return calculateDisplayShape(m_frame);
+}
+
 Document* MediaValuesDynamic::document() const {
   return m_frame->document();
 }
diff --git a/third_party/WebKit/Source/core/css/MediaValuesDynamic.h b/third_party/WebKit/Source/core/css/MediaValuesDynamic.h
index 5a01280..d5686a2 100644
--- a/third_party/WebKit/Source/core/css/MediaValuesDynamic.h
+++ b/third_party/WebKit/Source/core/css/MediaValuesDynamic.h
@@ -38,6 +38,7 @@
   bool strictMode() const override;
   const String mediaType() const override;
   WebDisplayMode displayMode() const override;
+  DisplayShape displayShape() const override;
   Document* document() const override;
   bool hasValues() const override;
   void overrideViewportDimensions(double width, double height) override;
diff --git a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
index 1403ca2..b24baaa 100644
--- a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
+++ b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
@@ -328,7 +328,7 @@
     const SecurityOrigin* securityOrigin) {
   TRACE_EVENT1("blink,devtools.timeline", "ParseAuthorStyleSheet", "data",
                InspectorParseAuthorStyleSheetEvent::data(cachedStyleSheet));
-  double startTimeMS = monotonicallyIncreasingTimeMS();
+  double startTime = monotonicallyIncreasingTime();
 
   bool isSameOriginRequest =
       securityOrigin && securityOrigin->canRequest(baseURL());
@@ -364,12 +364,11 @@
 
   DEFINE_STATIC_LOCAL(CustomCountHistogram, parseHistogram,
                       ("Style.AuthorStyleSheet.ParseTime", 0, 10000000, 50));
-  double parseDurationMS = (monotonicallyIncreasingTimeMS() - startTimeMS);
-  parseHistogram.count(parseDurationMS * 1000);
+  double parseDurationSeconds = (monotonicallyIncreasingTime() - startTime);
+  parseHistogram.count(parseDurationSeconds * 1000 * 1000);
   if (Document* document = singleOwnerDocument()) {
-    // CSSTiming expects time in seconds.
-    CSSTiming::from(*document).recordAuthorStyleSheetParseTime(parseDurationMS /
-                                                               1000);
+    CSSTiming::from(*document).recordAuthorStyleSheetParseTime(
+        parseDurationSeconds);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/css/html.css b/third_party/WebKit/Source/core/css/html.css
index 586eef8..be7cef3 100644
--- a/third_party/WebKit/Source/core/css/html.css
+++ b/third_party/WebKit/Source/core/css/html.css
@@ -85,6 +85,7 @@
 
 marquee {
     display: inline-block;
+    width: -webkit-fill-available;
 }
 
 address {
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index 39ac942..cd0ae887 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -3659,6 +3659,7 @@
               unresolvedProperty == CSSPropertyAliasMotionPath);
     case CSSPropertyOffsetDistance:
       return consumeLengthOrPercent(m_range, m_context.mode(), ValueRangeAll);
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       return consumeOffsetRotation(m_range);
     case CSSPropertyWebkitTextEmphasisStyle:
diff --git a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
index 8849d8fe..c2b859e 100644
--- a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
@@ -762,6 +762,7 @@
     case CSSPropertyOffsetPosition:
       style->setOffsetPosition(animatableValueToLengthPoint(value, state));
       return;
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       style->setOffsetRotation(StyleOffsetRotation(
           toAnimatableDoubleAndBool(value)->toDouble(),
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp b/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp
index 219ea54e..15835b9 100644
--- a/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp
@@ -53,7 +53,8 @@
     if (m_inheritedVariables)
       variableData = m_inheritedVariables->getVariable(name);
   } else {
-    variableData = m_nonInheritedVariables->getVariable(name);
+    if (m_nonInheritedVariables)
+      variableData = m_nonInheritedVariables->getVariable(name);
   }
   if (!variableData)
     return registration ? registration->initialVariableData() : nullptr;
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
index 615341c..5ba97158 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
@@ -196,7 +196,7 @@
     return;
 
   if (isHTMLTableCellElement(element)) {
-    if (style.whiteSpace() == EWhiteSpace::KhtmlNowrap) {
+    if (style.whiteSpace() == EWhiteSpace::WebkitNowrap) {
       // Figure out if we are really nowrapping or if we should just
       // use normal instead. If the width of the cell is fixed, then
       // we don't actually use NOWRAP.
diff --git a/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp b/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
index f01b29cc..a45caa6a 100644
--- a/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
+++ b/third_party/WebKit/Source/core/dom/CSSSelectorWatch.cpp
@@ -92,8 +92,7 @@
     const Vector<String>& addedSelectors) {
   bool shouldUpdateTimer = false;
 
-  for (unsigned i = 0; i < removedSelectors.size(); ++i) {
-    const String& selector = removedSelectors[i];
+  for (const auto& selector : removedSelectors) {
     if (!m_matchingCallbackSelectors.remove(selector))
       continue;
 
@@ -106,8 +105,7 @@
       m_removedSelectors.add(selector);
   }
 
-  for (unsigned i = 0; i < addedSelectors.size(); ++i) {
-    const String& selector = addedSelectors[i];
+  for (const auto& selector : addedSelectors) {
     HashCountedSet<String>::AddResult result =
         m_matchingCallbackSelectors.add(selector);
     if (!result.isNewEntry)
@@ -151,9 +149,9 @@
   StylePropertySet* callbackPropertySet =
       ImmutableStylePropertySet::create(nullptr, 0, UASheetMode);
 
-  for (unsigned i = 0; i < selectors.size(); ++i) {
+  for (const auto& selector : selectors) {
     CSSSelectorList selectorList = CSSParser::parseSelector(
-        CSSParserContext(UASheetMode, nullptr), nullptr, selectors[i]);
+        CSSParserContext(UASheetMode, nullptr), nullptr, selector);
     if (!selectorList.isValid())
       continue;
 
diff --git a/third_party/WebKit/Source/core/dom/ClientRectList.cpp b/third_party/WebKit/Source/core/dom/ClientRectList.cpp
index be5ff50c..02c2dbf 100644
--- a/third_party/WebKit/Source/core/dom/ClientRectList.cpp
+++ b/third_party/WebKit/Source/core/dom/ClientRectList.cpp
@@ -32,8 +32,8 @@
 
 ClientRectList::ClientRectList(const Vector<FloatQuad>& quads) {
   m_list.reserveInitialCapacity(quads.size());
-  for (size_t i = 0; i < quads.size(); ++i)
-    m_list.append(ClientRect::create(quads[i].boundingBox()));
+  for (const auto& quad : quads)
+    m_list.append(ClientRect::create(quad.boundingBox()));
 }
 
 unsigned ClientRectList::length() const {
diff --git a/third_party/WebKit/Source/core/dom/CompositorProxy.cpp b/third_party/WebKit/Source/core/dom/CompositorProxy.cpp
index d419c25..a10bd04 100644
--- a/third_party/WebKit/Source/core/dom/CompositorProxy.cpp
+++ b/third_party/WebKit/Source/core/dom/CompositorProxy.cpp
@@ -98,10 +98,8 @@
 static bool sanityCheckMutableProperties(uint32_t properties) {
   // Ensures that we only have bits set for valid mutable properties.
   uint32_t sanityCheckProperties = properties;
-  for (unsigned i = 0; i < WTF_ARRAY_LENGTH(allowedProperties); ++i) {
-    sanityCheckProperties &=
-        ~static_cast<uint32_t>(allowedProperties[i].property);
-  }
+  for (const auto& property : allowedProperties)
+    sanityCheckProperties &= ~static_cast<uint32_t>(property.property);
   return !sanityCheckProperties;
 }
 #endif
diff --git a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
index 93c95f80..2b04a24 100644
--- a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
@@ -51,9 +51,9 @@
 #include "core/html/TextDocument.h"
 #include "core/loader/FrameLoader.h"
 #include "core/page/Page.h"
-#include "platform/ContentType.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/graphics/Image.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/plugins/PluginData.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "wtf/StdLibExtras.h"
diff --git a/third_party/WebKit/Source/core/dom/DOMStringList.cpp b/third_party/WebKit/Source/core/dom/DOMStringList.cpp
index 03a312b..4350e60b 100644
--- a/third_party/WebKit/Source/core/dom/DOMStringList.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMStringList.cpp
@@ -72,9 +72,8 @@
   // FIXME: Currently, all consumers of DOMStringList store fairly small lists
   // and thus an O(n) algorithm is OK.  But this may need to be optimized if
   // larger amounts of data are stored in m_strings.
-  size_t count = m_strings.size();
-  for (size_t i = 0; i < count; ++i) {
-    if (m_strings[i] == string)
+  for (const auto& item : m_strings) {
+    if (item == string)
       return true;
   }
   return false;
diff --git a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
index f78cc63..7a939fa 100644
--- a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
@@ -80,8 +80,8 @@
 
 bool DOMTokenList::validateTokens(const Vector<String>& tokens,
                                   ExceptionState& exceptionState) const {
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    if (!validateToken(tokens[i], exceptionState))
+  for (const auto& token : tokens) {
+    if (!validateToken(token, exceptionState))
       return false;
   }
 
@@ -115,14 +115,14 @@
                        ExceptionState& exceptionState) {
   Vector<String> filteredTokens;
   filteredTokens.reserveCapacity(tokens.size());
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    if (!validateToken(tokens[i], exceptionState))
+  for (const auto& token : tokens) {
+    if (!validateToken(token, exceptionState))
       return;
-    if (containsInternal(AtomicString(tokens[i])))
+    if (containsInternal(AtomicString(token)))
       continue;
-    if (filteredTokens.contains(tokens[i]))
+    if (filteredTokens.contains(token))
       continue;
-    filteredTokens.append(tokens[i]);
+    filteredTokens.append(token);
   }
 
   if (!filteredTokens.isEmpty())
@@ -146,8 +146,8 @@
   // Check using containsInternal first since it is a lot faster than going
   // through the string character by character.
   bool found = false;
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    if (containsInternal(AtomicString(tokens[i]))) {
+  for (const auto& token : tokens) {
+    if (containsInternal(AtomicString(token))) {
       found = true;
       break;
     }
@@ -221,10 +221,10 @@
     needsSpace = !isHTMLSpace<UChar>(input[input.length() - 1]);
   }
 
-  for (size_t i = 0; i < tokens.size(); ++i) {
+  for (const auto& token : tokens) {
     if (needsSpace)
       builder.append(' ');
-    builder.append(tokens[i]);
+    builder.append(token);
     needsSpace = true;
   }
 
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index e1c4a76..ef948f51 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -56,6 +56,7 @@
 #include "core/css/CSSFontSelector.h"
 #include "core/css/CSSStyleDeclaration.h"
 #include "core/css/CSSStyleSheet.h"
+#include "core/css/CSSTiming.h"
 #include "core/css/FontFaceSet.h"
 #include "core/css/MediaQueryMatcher.h"
 #include "core/css/PropertyRegistry.h"
@@ -232,6 +233,7 @@
 #include "platform/PluginScriptForbiddenScope.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/ScriptForbiddenScope.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/network/ContentSecurityPolicyParsers.h"
 #include "platform/network/HTTPParsers.h"
 #include "platform/scroll/Scrollbar.h"
@@ -245,7 +247,6 @@
 #include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebAddressSpace.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebPrerenderingSupport.h"
 #include "public/platform/WebScheduler.h"
 #include "public/platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom-blink.h"
@@ -1359,15 +1360,16 @@
 }
 
 // We use HashMap::set over HashMap::add here as we want to
-// replace the ComputedStyle but not the Element if the Element is
+// replace the ComputedStyle but not the Node if the Node is
 // already present.
-void Document::addStyleReattachData(Element& element,
+void Document::addStyleReattachData(Node& node,
                                     StyleReattachData& styleReattachData) {
-  m_styleReattachDataMap.set(&element, styleReattachData);
+  DCHECK(node.isElementNode() || node.isTextNode());
+  m_styleReattachDataMap.set(&node, styleReattachData);
 }
 
-StyleReattachData Document::getStyleReattachData(Element& element) {
-  return m_styleReattachDataMap.get(&element);
+StyleReattachData Document::getStyleReattachData(Node& node) {
+  return m_styleReattachDataMap.get(&node);
 }
 
 /*
@@ -2002,7 +2004,7 @@
 void Document::updateStyle() {
   DCHECK(!view()->shouldThrottleRendering());
   TRACE_EVENT_BEGIN0("blink,blink_style", "Document::updateStyle");
-  SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Style.UpdateTime");
+  double startTime = monotonicallyIncreasingTime();
 
   unsigned initialElementCount = styleEngine().styleForElementCount();
 
@@ -2074,6 +2076,12 @@
         "blink,blink_style", "Document::updateStyle", "resolverAccessCount",
         styleEngine().styleForElementCount() - initialElementCount);
   }
+
+  double updateDurationSeconds = monotonicallyIncreasingTime() - startTime;
+  DEFINE_STATIC_LOCAL(CustomCountHistogram, updateHistogram,
+                      ("Style.UpdateTime", 0, 10000000, 50));
+  updateHistogram.count(updateDurationSeconds * 1000 * 1000);
+  CSSTiming::from(*this).recordUpdateDuration(updateDurationSeconds);
 }
 
 void Document::notifyLayoutTreeOfSubtreeChanges() {
@@ -4274,6 +4282,10 @@
   return m_domWindow->getEventQueue();
 }
 
+void Document::enqueueAnimationFrameTask(std::unique_ptr<WTF::Closure> task) {
+  ensureScriptedAnimationController().enqueueTask(std::move(task));
+}
+
 void Document::enqueueAnimationFrameEvent(Event* event) {
   ensureScriptedAnimationController().enqueueEvent(event);
 }
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 648751e..67ac81e 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -353,8 +353,8 @@
   Range* caretRangeFromPoint(int x, int y);
   Element* scrollingElement();
 
-  void addStyleReattachData(Element&, StyleReattachData&);
-  StyleReattachData getStyleReattachData(Element&);
+  void addStyleReattachData(Node&, StyleReattachData&);
+  StyleReattachData getStyleReattachData(Node&);
 
   String readyState() const;
 
@@ -1079,6 +1079,7 @@
 
   void enqueueResizeEvent();
   void enqueueScrollEventForNode(Node*);
+  void enqueueAnimationFrameTask(std::unique_ptr<WTF::Closure>);
   void enqueueAnimationFrameEvent(Event*);
   // Only one event for a target/event type combination will be dispatched per
   // frame.
@@ -1285,8 +1286,9 @@
 
   Element* rootScroller() const;
   void setRootScroller(Element*, ExceptionState&);
-  RootScrollerController* rootScrollerController() const {
-    return m_rootScrollerController.get();
+  RootScrollerController& rootScrollerController() const {
+    DCHECK(m_rootScrollerController);
+    return *m_rootScrollerController;
   }
 
   bool isInMainFrame() const;
@@ -1442,7 +1444,10 @@
   Member<DocumentParser> m_parser;
   Member<ContextFeatures> m_contextFeatures;
 
-  HeapHashMap<Member<Element>, StyleReattachData> m_styleReattachDataMap;
+  // This HashMap is used to stash information (ComputedStyle, nextTextSibling)
+  // generated in the Style Resolution phase that is required in the
+  // Layout Tree construction phase.
+  HeapHashMap<Member<Node>, StyleReattachData> m_styleReattachDataMap;
 
   bool m_wellFormed;
 
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 8cf3b0f..7c28693 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -568,7 +568,7 @@
 
   LayoutBox* boxToScroll = nullptr;
 
-  if (document().rootScrollerController()->scrollsViewport(*this))
+  if (document().rootScrollerController().scrollsViewport(*this))
     boxToScroll = document().layoutView();
   else if (layoutObject())
     boxToScroll = toLayoutBox(layoutObject());
@@ -1664,7 +1664,7 @@
 
   // We've already been through detach when doing an attach, but we might
   // need to clear any state that's been added since then.
-  if (hasRareData() && getStyleChangeType() == NeedsReattachStyleChange) {
+  if (hasRareData() && needsAttach()) {
     ElementRareData* data = elementRareData();
     data->clearComputedStyle();
   }
diff --git a/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp b/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
index 8ecc1e3..5002f3d4 100644
--- a/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
+++ b/third_party/WebKit/Source/core/dom/FrameRequestCallbackCollection.cpp
@@ -46,16 +46,15 @@
       return;
     }
   }
-  for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
-    if (m_callbacksToInvoke[i]->m_id == id) {
-      InspectorInstrumentation::asyncTaskCanceled(m_context,
-                                                  m_callbacksToInvoke[i]);
+  for (const auto& callback : m_callbacksToInvoke) {
+    if (callback->m_id == id) {
+      InspectorInstrumentation::asyncTaskCanceled(m_context, callback);
       InspectorInstrumentation::NativeBreakpoint nativeBreakpoint(
           m_context, "cancelAnimationFrame", true);
       TRACE_EVENT_INSTANT1("devtools.timeline", "CancelAnimationFrame",
                            TRACE_EVENT_SCOPE_THREAD, "data",
                            InspectorAnimationFrameEvent::data(m_context, id));
-      m_callbacksToInvoke[i]->m_cancelled = true;
+      callback->m_cancelled = true;
       // will be removed at the end of executeCallbacks()
       return;
     }
@@ -70,8 +69,7 @@
   DCHECK(m_callbacksToInvoke.isEmpty());
   m_callbacksToInvoke.swap(m_callbacks);
 
-  for (size_t i = 0; i < m_callbacksToInvoke.size(); ++i) {
-    FrameRequestCallback* callback = m_callbacksToInvoke[i].get();
+  for (const auto& callback : m_callbacksToInvoke) {
     if (!callback->m_cancelled) {
       TRACE_EVENT1(
           "devtools.timeline", "FireAnimationFrame", "data",
diff --git a/third_party/WebKit/Source/core/dom/Fullscreen.cpp b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
index 5ff6e9a..ce9f4d9 100644
--- a/third_party/WebKit/Source/core/dom/Fullscreen.cpp
+++ b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
@@ -29,6 +29,7 @@
 
 #include "core/dom/Fullscreen.h"
 
+#include "bindings/core/v8/ConditionalFeatures.h"
 #include "core/dom/Document.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/dom/StyleEngine.h"
@@ -86,8 +87,11 @@
     return true;
 
   //  The algorithm is triggered by a user generated orientation change.
-  if (ScopedOrientationChangeIndicator::processingOrientationChange())
+  if (ScopedOrientationChangeIndicator::processingOrientationChange()) {
+    UseCounter::count(document,
+                      UseCounter::FullscreenAllowedByOrientationChange);
     return true;
+  }
 
   String message = ExceptionMessages::failedToExecute(
       "requestFullscreen", "Element",
@@ -99,10 +103,43 @@
 }
 
 // https://fullscreen.spec.whatwg.org/#fullscreen-is-supported
-bool fullscreenIsSupported(const Document& document) {
+// TODO(lunalu): update the placement of the feature policy code once it is in
+// https://fullscreen.spec.whatwg.org/.
+bool fullscreenIsSupported(Document& document) {
+  LocalFrame* frame = document.frame();
+  if (!frame)
+    return false;
+
   // Fullscreen is supported if there is no previously-established user
   // preference, security risk, or platform limitation.
-  return !document.settings() || document.settings()->fullscreenSupported();
+  bool fullscreenSupported =
+      !document.settings() || document.settings()->fullscreenSupported();
+
+  if (!RuntimeEnabledFeatures::featurePolicyEnabled()) {
+    return fullscreenSupported;
+  }
+
+  // TODO(lunalu): clean all of this up once iframe attributes are supported
+  // for feature policy.
+  if (Frame* parent = frame->tree().parent()) {
+    // If FeaturePolicy is enabled, check the fullscreen is not disabled by
+    // policy in the parent frame.
+    if (fullscreenSupported &&
+        parent->securityContext()->getFeaturePolicy()->isFeatureEnabled(
+            kFullscreenFeature)) {
+      return true;
+    }
+  }
+  // Even if the iframe allowfullscreen attribute is not present, allow
+  // fullscreen to be enabled by feature policy.
+  else if (isFeatureEnabledInFrame(kFullscreenFeature, frame)) {
+    return true;
+  }
+
+  document.addConsoleMessage(ConsoleMessage::create(
+      JSMessageSource, WarningMessageLevel,
+      "Fullscreen API is disabled by feature policy for this frame"));
+  return false;
 }
 
 // https://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check
diff --git a/third_party/WebKit/Source/core/dom/MessagePort.h b/third_party/WebKit/Source/core/dom/MessagePort.h
index 9a3e38a..9944dba 100644
--- a/third_party/WebKit/Source/core/dom/MessagePort.h
+++ b/third_party/WebKit/Source/core/dom/MessagePort.h
@@ -66,7 +66,7 @@
                    PassRefPtr<SerializedScriptValue> message,
                    const MessagePortArray&,
                    ExceptionState&);
-  static bool canTransferArrayBuffer() { return false; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return false; }
 
   void start();
   void close();
diff --git a/third_party/WebKit/Source/core/dom/MutationObserver.cpp b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
index 42a63b93..cce4f48 100644
--- a/third_party/WebKit/Source/core/dom/MutationObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
@@ -75,9 +75,8 @@
 
   HashSet<AtomicString> attributeFilter;
   if (observerInit.hasAttributeFilter()) {
-    const Vector<String>& sequence = observerInit.attributeFilter();
-    for (unsigned i = 0; i < sequence.size(); ++i)
-      attributeFilter.add(AtomicString(sequence[i]));
+    for (const auto& name : observerInit.attributeFilter())
+      attributeFilter.add(AtomicString(name));
     options |= AttributeFilter;
   }
 
@@ -227,8 +226,8 @@
     if (registration->hasTransientRegistrations())
       transientRegistrations.append(registration);
   }
-  for (size_t i = 0; i < transientRegistrations.size(); ++i)
-    transientRegistrations[i]->clearTransientRegistrations();
+  for (const auto& registration : transientRegistrations)
+    registration->clearTransientRegistrations();
 
   if (m_records.isEmpty())
     return;
@@ -249,10 +248,10 @@
 
   MutationObserverVector suspended;
   copyToVector(suspendedMutationObservers(), suspended);
-  for (size_t i = 0; i < suspended.size(); ++i) {
-    if (!suspended[i]->shouldBeSuspended()) {
-      suspendedMutationObservers().remove(suspended[i]);
-      activateObserver(suspended[i]);
+  for (const auto& observer : suspended) {
+    if (!observer->shouldBeSuspended()) {
+      suspendedMutationObservers().remove(observer);
+      activateObserver(observer);
     }
   }
 }
@@ -263,11 +262,11 @@
   copyToVector(activeMutationObservers(), observers);
   activeMutationObservers().clear();
   std::sort(observers.begin(), observers.end(), ObserverLessThan());
-  for (size_t i = 0; i < observers.size(); ++i) {
-    if (observers[i]->shouldBeSuspended())
-      suspendedMutationObservers().add(observers[i]);
+  for (const auto& observer : observers) {
+    if (observer->shouldBeSuspended())
+      suspendedMutationObservers().add(observer);
     else
-      observers[i]->deliver();
+      observer->deliver();
   }
 }
 
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp
index aeaa766..d5feaa1 100644
--- a/third_party/WebKit/Source/core/dom/Node.cpp
+++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -1814,9 +1814,8 @@
   if (const EventTargetData* eventTargetData = this->eventTargetData()) {
     const EventListenerMap& listenerMap = eventTargetData->eventListenerMap;
     if (!listenerMap.isEmpty()) {
-      Vector<AtomicString> types = listenerMap.eventTypes();
-      for (unsigned i = 0; i < types.size(); ++i)
-        document().addListenerTypeIfNeeded(types[i]);
+      for (const auto& type : listenerMap.eventTypes())
+        document().addListenerTypeIfNeeded(type);
     }
   }
 
@@ -1832,8 +1831,8 @@
 
   if (const HeapVector<TraceWrapperMember<MutationObserverRegistration>>*
           registry = mutationObserverRegistry()) {
-    for (size_t i = 0; i < registry->size(); ++i) {
-      document().addMutationObserverTypes(registry->at(i)->mutationTypes());
+    for (const auto& registration : *registry) {
+      document().addMutationObserverTypes(registration->mutationTypes());
     }
   }
 
@@ -1975,11 +1974,10 @@
     MutationObserverOptions options,
     const HashSet<AtomicString>& attributeFilter) {
   MutationObserverRegistration* registration = nullptr;
-  const HeapVector<TraceWrapperMember<MutationObserverRegistration>>& registry =
-      ensureRareData().ensureMutationObserverData().registry();
-  for (size_t i = 0; i < registry.size(); ++i) {
-    if (&registry[i]->observer() == &observer) {
-      registration = registry[i].get();
+  for (const auto& item :
+       ensureRareData().ensureMutationObserverData().registry()) {
+    if (&item->observer() == &observer) {
+      registration = item.get();
       registration->resetObservation(options, attributeFilter);
     }
   }
@@ -2035,9 +2033,8 @@
   for (Node* node = parentNode(); node; node = node->parentNode()) {
     if (const HeapVector<TraceWrapperMember<MutationObserverRegistration>>*
             registry = node->mutationObserverRegistry()) {
-      const size_t size = registry->size();
-      for (size_t i = 0; i < size; ++i)
-        registry->at(i)->observedSubtreeNodeWillDetach(*this);
+      for (const auto& registration : *registry)
+        registration->observedSubtreeNodeWillDetach(*this);
     }
 
     if (const HeapHashSet<TraceWrapperMember<MutationObserverRegistration>>*
@@ -2293,8 +2290,7 @@
   HeapVector<Member<InsertionPoint>, 8> insertionPoints;
   collectDestinationInsertionPoints(*this, insertionPoints);
   HeapVector<Member<Node>> filteredInsertionPoints;
-  for (size_t i = 0; i < insertionPoints.size(); ++i) {
-    InsertionPoint* insertionPoint = insertionPoints[i];
+  for (const auto& insertionPoint : insertionPoints) {
     DCHECK(insertionPoint->containingShadowRoot());
     if (!insertionPoint->containingShadowRoot()->isOpenOrV0())
       break;
diff --git a/third_party/WebKit/Source/core/dom/Node.h b/third_party/WebKit/Source/core/dom/Node.h
index 00beb275..8725bf2 100644
--- a/third_party/WebKit/Source/core/dom/Node.h
+++ b/third_party/WebKit/Source/core/dom/Node.h
@@ -965,7 +965,7 @@
 }
 
 inline void Node::lazyReattachIfAttached() {
-  if (getStyleChangeType() == NeedsReattachStyleChange)
+  if (needsAttach())
     return;
   if (!inActiveDocument())
     return;
diff --git a/third_party/WebKit/Source/core/dom/ScopedWindowFocusAllowedIndicator.h b/third_party/WebKit/Source/core/dom/ScopedWindowFocusAllowedIndicator.h
index 7f69237..b52a331 100644
--- a/third_party/WebKit/Source/core/dom/ScopedWindowFocusAllowedIndicator.h
+++ b/third_party/WebKit/Source/core/dom/ScopedWindowFocusAllowedIndicator.h
@@ -5,7 +5,6 @@
 #ifndef ScopedWindowFocusAllowedIndicator_h
 #define ScopedWindowFocusAllowedIndicator_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/dom/ExecutionContext.h"
 #include "wtf/Noncopyable.h"
 
@@ -17,32 +16,17 @@
 
  public:
   explicit ScopedWindowFocusAllowedIndicator(ExecutionContext* executionContext)
-      : m_observer(new Observer(executionContext)) {}
-  ~ScopedWindowFocusAllowedIndicator() { m_observer->dispose(); }
+      : m_executionContext(executionContext) {
+    executionContext->allowWindowInteraction();
+  }
+  ~ScopedWindowFocusAllowedIndicator() {
+    m_executionContext->consumeWindowInteraction();
+  }
 
  private:
-  class Observer final : public GarbageCollected<Observer>,
-                         public ContextLifecycleObserver {
-    USING_GARBAGE_COLLECTED_MIXIN(Observer);
-
-   public:
-    explicit Observer(ExecutionContext* executionContext)
-        : ContextLifecycleObserver(executionContext) {
-      if (executionContext)
-        executionContext->allowWindowInteraction();
-    }
-
-    void dispose() {
-      if (getExecutionContext())
-        getExecutionContext()->consumeWindowInteraction();
-    }
-  };
-
-  // In Oilpan, destructors are not allowed to touch other on-heap objects.
-  // The Observer indirection is needed to keep
-  // ScopedWindowFocusAllowedIndicator off-heap and thus allows its destructor
-  // to call getExecutionContext()->consumeWindowInteraction().
-  Persistent<Observer> m_observer;
+  // This doesn't create a cycle because ScopedWindowFocusAllowedIndicator
+  // is used only on a machine stack.
+  Persistent<ExecutionContext> m_executionContext;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
index ff12ae9..a02e2ca20 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
@@ -50,10 +50,10 @@
 #include "core/html/parser/HTMLParserIdioms.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/svg/SVGScriptElement.h"
-#include "platform/MIMETypeRegistry.h"
+#include "platform/WebFrameScheduler.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "public/platform/WebCachePolicy.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "wtf/StdLibExtras.h"
 #include "wtf/text/StringBuilder.h"
 #include "wtf/text/StringHash.h"
diff --git a/third_party/WebKit/Source/core/dom/ScriptRunner.cpp b/third_party/WebKit/Source/core/dom/ScriptRunner.cpp
index f6de77a..e13964f2 100644
--- a/third_party/WebKit/Source/core/dom/ScriptRunner.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptRunner.cpp
@@ -28,6 +28,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/Element.h"
 #include "core/dom/ScriptLoader.h"
+#include "core/dom/TaskRunnerHelper.h"
 #include "platform/heap/Handle.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebScheduler.h"
@@ -37,10 +38,8 @@
 
 ScriptRunner::ScriptRunner(Document* document)
     : m_document(document),
-      m_taskRunner(Platform::current()
-                       ->currentThread()
-                       ->scheduler()
-                       ->loadingTaskRunner()),
+      m_taskRunner(
+          TaskRunnerHelper::get(TaskType::Networking, document)->clone()),
       m_numberOfInOrderScriptsWithPendingNotification(0),
       m_isSuspended(false) {
   DCHECK(document);
diff --git a/third_party/WebKit/Source/core/dom/ScriptRunner.h b/third_party/WebKit/Source/core/dom/ScriptRunner.h
index 9f6b004f0..1758210 100644
--- a/third_party/WebKit/Source/core/dom/ScriptRunner.h
+++ b/third_party/WebKit/Source/core/dom/ScriptRunner.h
@@ -39,7 +39,8 @@
 class ScriptLoader;
 class WebTaskRunner;
 
-class CORE_EXPORT ScriptRunner final : public GarbageCollected<ScriptRunner> {
+class CORE_EXPORT ScriptRunner final
+    : public GarbageCollectedFinalized<ScriptRunner> {
   WTF_MAKE_NONCOPYABLE(ScriptRunner);
 
  public:
@@ -90,7 +91,7 @@
   HeapDeque<Member<ScriptLoader>> m_asyncScriptsToExecuteSoon;
   HeapDeque<Member<ScriptLoader>> m_inOrderScriptsToExecuteSoon;
 
-  WebTaskRunner* m_taskRunner;
+  std::unique_ptr<WebTaskRunner> m_taskRunner;
 
   int m_numberOfInOrderScriptsWithPendingNotification;
 
diff --git a/third_party/WebKit/Source/core/dom/ScriptedAnimationController.cpp b/third_party/WebKit/Source/core/dom/ScriptedAnimationController.cpp
index 05e09a4..74a2496 100644
--- a/third_party/WebKit/Source/core/dom/ScriptedAnimationController.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptedAnimationController.cpp
@@ -81,6 +81,13 @@
   m_callbackCollection.cancelCallback(id);
 }
 
+void ScriptedAnimationController::runTasks() {
+  Vector<std::unique_ptr<WTF::Closure>> tasks;
+  tasks.swap(m_taskQueue);
+  for (auto& task : tasks)
+    (*task)();
+}
+
 void ScriptedAnimationController::dispatchEvents(
     const AtomicString& eventInterfaceFilter) {
   HeapVector<Member<Event>> events;
@@ -100,18 +107,18 @@
     remaining.swap(m_eventQueue);
   }
 
-  for (size_t i = 0; i < events.size(); ++i) {
-    EventTarget* eventTarget = events[i]->target();
+  for (const auto& event : events) {
+    EventTarget* eventTarget = event->target();
     // FIXME: we should figure out how to make dispatchEvent properly virtual to
     // avoid special casting window.
     // FIXME: We should not fire events for nodes that are no longer in the
     // tree.
     InspectorInstrumentation::AsyncTask asyncTask(
-        eventTarget->getExecutionContext(), events[i]);
+        eventTarget->getExecutionContext(), event);
     if (LocalDOMWindow* window = eventTarget->toLocalDOMWindow())
-      window->dispatchEvent(events[i], nullptr);
+      window->dispatchEvent(event, nullptr);
     else
-      eventTarget->dispatchEvent(events[i]);
+      eventTarget->dispatchEvent(event);
   }
 }
 
@@ -144,8 +151,8 @@
   if (m_suspendCount)
     return false;
 
-  return !m_callbackCollection.isEmpty() || !m_eventQueue.isEmpty() ||
-         !m_mediaQueryListListeners.isEmpty();
+  return !m_callbackCollection.isEmpty() || !m_taskQueue.isEmpty() ||
+         !m_eventQueue.isEmpty() || !m_mediaQueryListListeners.isEmpty();
 }
 
 void ScriptedAnimationController::serviceScriptedAnimations(
@@ -155,11 +162,18 @@
 
   callMediaQueryListListeners();
   dispatchEvents();
+  runTasks();
   executeCallbacks(monotonicTimeNow);
 
   scheduleAnimationIfNeeded();
 }
 
+void ScriptedAnimationController::enqueueTask(
+    std::unique_ptr<WTF::Closure> task) {
+  m_taskQueue.append(std::move(task));
+  scheduleAnimationIfNeeded();
+}
+
 void ScriptedAnimationController::enqueueEvent(Event* event) {
   InspectorInstrumentation::asyncTaskScheduled(
       event->target()->getExecutionContext(), event->type(), event);
@@ -175,8 +189,8 @@
 
 void ScriptedAnimationController::enqueueMediaQueryChangeListeners(
     HeapVector<Member<MediaQueryListListener>>& listeners) {
-  for (size_t i = 0; i < listeners.size(); ++i) {
-    m_mediaQueryListListeners.add(listeners[i]);
+  for (const auto& listener : listeners) {
+    m_mediaQueryListListeners.add(listener);
   }
   scheduleAnimationIfNeeded();
 }
diff --git a/third_party/WebKit/Source/core/dom/ScriptedAnimationController.h b/third_party/WebKit/Source/core/dom/ScriptedAnimationController.h
index 1962092..3fa371f0 100644
--- a/third_party/WebKit/Source/core/dom/ScriptedAnimationController.h
+++ b/third_party/WebKit/Source/core/dom/ScriptedAnimationController.h
@@ -43,7 +43,7 @@
 class MediaQueryListListener;
 
 class CORE_EXPORT ScriptedAnimationController
-    : public GarbageCollected<ScriptedAnimationController> {
+    : public GarbageCollectedFinalized<ScriptedAnimationController> {
  public:
   static ScriptedAnimationController* create(Document* document) {
     return new ScriptedAnimationController(document);
@@ -52,17 +52,26 @@
   DECLARE_TRACE();
   void clearDocumentPointer() { m_document = nullptr; }
 
+  // Animation frame callbacks are used for requestAnimationFrame().
   typedef int CallbackId;
-
-  int registerCallback(FrameRequestCallback*);
+  CallbackId registerCallback(FrameRequestCallback*);
   void cancelCallback(CallbackId);
-  void serviceScriptedAnimations(double monotonicTimeNow);
 
+  // Animation frame events are used for resize events, scroll events, etc.
   void enqueueEvent(Event*);
   void enqueuePerFrameEvent(Event*);
+
+  // Animation frame tasks are used for Fullscreen.
+  void enqueueTask(std::unique_ptr<WTF::Closure>);
+
+  // Used for the MediaQueryList change event.
   void enqueueMediaQueryChangeListeners(
       HeapVector<Member<MediaQueryListListener>>&);
 
+  // Invokes callbacks, dispatches events, etc. The order is defined by HTML:
+  // https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
+  void serviceScriptedAnimations(double monotonicTimeNow);
+
   void suspend();
   void resume();
 
@@ -73,6 +82,7 @@
 
   void scheduleAnimationIfNeeded();
 
+  void runTasks();
   void dispatchEvents(
       const AtomicString& eventInterfaceFilter = AtomicString());
   void executeCallbacks(double monotonicTimeNow);
@@ -83,6 +93,7 @@
   Member<Document> m_document;
   FrameRequestCallbackCollection m_callbackCollection;
   int m_suspendCount;
+  Vector<std::unique_ptr<WTF::Closure>> m_taskQueue;
   HeapVector<Member<Event>> m_eventQueue;
   HeapListHashSet<std::pair<Member<const EventTarget>, const StringImpl*>>
       m_perFrameEvents;
diff --git a/third_party/WebKit/Source/core/dom/ScriptedAnimationControllerTest.cpp b/third_party/WebKit/Source/core/dom/ScriptedAnimationControllerTest.cpp
new file mode 100644
index 0000000..1830b53
--- /dev/null
+++ b/third_party/WebKit/Source/core/dom/ScriptedAnimationControllerTest.cpp
@@ -0,0 +1,184 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/dom/ScriptedAnimationController.h"
+
+#include "core/dom/Document.h"
+#include "core/dom/FrameRequestCallback.h"
+#include "core/events/Event.h"
+#include "core/events/EventListener.h"
+#include "core/events/EventTarget.h"
+#include "core/testing/DummyPageHolder.h"
+#include "platform/heap/Handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/Functional.h"
+#include <memory>
+
+namespace blink {
+
+class ScriptedAnimationControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override;
+
+  Document& document() const { return m_dummyPageHolder->document(); }
+  ScriptedAnimationController& controller() { return *m_controller; }
+
+ private:
+  std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
+  Persistent<ScriptedAnimationController> m_controller;
+};
+
+void ScriptedAnimationControllerTest::SetUp() {
+  m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
+
+  // Note: The document doesn't know about this ScriptedAnimationController
+  // instance, and will create another if
+  // Document::ensureScriptedAnimationController is called.
+  m_controller =
+      wrapPersistent(ScriptedAnimationController::create(&document()));
+}
+
+namespace {
+
+class TaskOrderObserver {
+ public:
+  std::unique_ptr<WTF::Closure> createTask(int id) {
+    return WTF::bind(&TaskOrderObserver::runTask, unretained(this), id);
+  }
+  const Vector<int>& order() const { return m_order; }
+
+ private:
+  void runTask(int id) { m_order.append(id); }
+  Vector<int> m_order;
+};
+
+}  // anonymous namespace
+
+TEST_F(ScriptedAnimationControllerTest, EnqueueOneTask) {
+  TaskOrderObserver observer;
+
+  controller().enqueueTask(observer.createTask(1));
+  EXPECT_EQ(0u, observer.order().size());
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(1u, observer.order().size());
+  EXPECT_EQ(1, observer.order()[0]);
+}
+
+TEST_F(ScriptedAnimationControllerTest, EnqueueTwoTasks) {
+  TaskOrderObserver observer;
+
+  controller().enqueueTask(observer.createTask(1));
+  controller().enqueueTask(observer.createTask(2));
+  EXPECT_EQ(0u, observer.order().size());
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(2u, observer.order().size());
+  EXPECT_EQ(1, observer.order()[0]);
+  EXPECT_EQ(2, observer.order()[1]);
+}
+
+namespace {
+
+void enqueueTask(ScriptedAnimationController* controller,
+                 TaskOrderObserver* observer,
+                 int id) {
+  controller->enqueueTask(observer->createTask(id));
+}
+
+}  // anonymous namespace
+
+// A task enqueued while running tasks should not be run immediately after, but
+// the next time tasks are run.
+TEST_F(ScriptedAnimationControllerTest, EnqueueWithinTask) {
+  TaskOrderObserver observer;
+
+  controller().enqueueTask(observer.createTask(1));
+  controller().enqueueTask(WTF::bind(
+      &enqueueTask, wrapPersistent(&controller()), unretained(&observer), 2));
+  controller().enqueueTask(observer.createTask(3));
+  EXPECT_EQ(0u, observer.order().size());
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(2u, observer.order().size());
+  EXPECT_EQ(1, observer.order()[0]);
+  EXPECT_EQ(3, observer.order()[1]);
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(3u, observer.order().size());
+  EXPECT_EQ(1, observer.order()[0]);
+  EXPECT_EQ(3, observer.order()[1]);
+  EXPECT_EQ(2, observer.order()[2]);
+}
+
+namespace {
+
+class RunTaskEventListener final : public EventListener {
+ public:
+  RunTaskEventListener(std::unique_ptr<WTF::Closure> task)
+      : EventListener(CPPEventListenerType), m_task(std::move(task)) {}
+  void handleEvent(ExecutionContext*, Event*) override { (*m_task)(); }
+  bool operator==(const EventListener& other) const override {
+    return this == &other;
+  }
+
+ private:
+  std::unique_ptr<WTF::Closure> m_task;
+};
+
+}  // anonymous namespace
+
+// Tasks should be run after events are dispatched, even if they were enqueued
+// first.
+TEST_F(ScriptedAnimationControllerTest, EnqueueTaskAndEvent) {
+  TaskOrderObserver observer;
+
+  controller().enqueueTask(observer.createTask(1));
+  document().addEventListener("test",
+                              new RunTaskEventListener(observer.createTask(2)));
+  Event* event = Event::create("test");
+  event->setTarget(&document());
+  controller().enqueueEvent(event);
+  EXPECT_EQ(0u, observer.order().size());
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(2u, observer.order().size());
+  EXPECT_EQ(2, observer.order()[0]);
+  EXPECT_EQ(1, observer.order()[1]);
+}
+
+namespace {
+
+class RunTaskCallback final : public FrameRequestCallback {
+ public:
+  RunTaskCallback(std::unique_ptr<WTF::Closure> task)
+      : m_task(std::move(task)) {}
+  void handleEvent(double) override { (*m_task)(); }
+
+ private:
+  std::unique_ptr<WTF::Closure> m_task;
+};
+
+}  // anonymous namespace
+
+// Animation frame callbacks should be run after tasks, even if they were
+// enqueued first.
+TEST_F(ScriptedAnimationControllerTest, RegisterCallbackAndEnqueueTask) {
+  TaskOrderObserver observer;
+
+  Event* event = Event::create("test");
+  event->setTarget(&document());
+
+  controller().registerCallback(new RunTaskCallback(observer.createTask(1)));
+  controller().enqueueTask(observer.createTask(2));
+  EXPECT_EQ(0u, observer.order().size());
+
+  controller().serviceScriptedAnimations(0);
+  EXPECT_EQ(2u, observer.order().size());
+  EXPECT_EQ(2, observer.order()[0]);
+  EXPECT_EQ(1, observer.order()[1]);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/SelectorQuery.cpp b/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
index bb2599bfc..b07c6362 100644
--- a/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
+++ b/third_party/WebKit/Source/core/dom/SelectorQuery.cpp
@@ -145,9 +145,8 @@
   if (m_needsUpdatedDistribution)
     targetElement.updateDistribution();
 
-  unsigned selectorCount = m_selectors.size();
-  for (unsigned i = 0; i < selectorCount; ++i) {
-    if (selectorMatches(*m_selectors[i], targetElement, targetElement))
+  for (const auto& selector : m_selectors) {
+    if (selectorMatches(*selector, targetElement, targetElement))
       return true;
   }
 
@@ -155,16 +154,15 @@
 }
 
 Element* SelectorDataList::closest(Element& targetElement) const {
-  unsigned selectorCount = m_selectors.size();
-  if (!selectorCount)
+  if (m_selectors.size() == 0)
     return nullptr;
   if (m_needsUpdatedDistribution)
     targetElement.updateDistribution();
 
   for (Element* currentElement = &targetElement; currentElement;
        currentElement = currentElement->parentElement()) {
-    for (unsigned i = 0; i < selectorCount; ++i) {
-      if (selectorMatches(*m_selectors[i], *currentElement, targetElement))
+    for (const auto& selector : m_selectors) {
+      if (selectorMatches(*selector, *currentElement, targetElement))
         return currentElement;
     }
   }
@@ -409,8 +407,8 @@
     ContainerNode& rootNode,
     Element& element,
     typename SelectorQueryTrait::OutputType& output) const {
-  for (unsigned i = 0; i < m_selectors.size(); ++i) {
-    if (selectorMatches(*m_selectors[i], element, rootNode)) {
+  for (const auto& selector : m_selectors) {
+    if (selectorMatches(*selector, element, rootNode)) {
       SelectorQueryTrait::appendElement(output, element);
       return true;
     }
@@ -546,13 +544,11 @@
     if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) {
       const HeapVector<Member<Element>>& elements =
           rootNode.treeScope().getAllElementsById(idToMatch);
-      size_t count = elements.size();
-      for (size_t i = 0; i < count; ++i) {
-        Element& element = *elements[i];
-        if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode)))
+      for (const auto& element : elements) {
+        if (!(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
           continue;
-        if (selectorMatches(selector, element, rootNode)) {
-          SelectorQueryTrait::appendElement(output, element);
+        if (selectorMatches(selector, *element, rootNode)) {
+          SelectorQueryTrait::appendElement(output, *element);
           if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
             return;
         }
diff --git a/third_party/WebKit/Source/core/dom/SpaceSplitString.h b/third_party/WebKit/Source/core/dom/SpaceSplitString.h
index e9a0a97..cb52132 100644
--- a/third_party/WebKit/Source/core/dom/SpaceSplitString.h
+++ b/third_party/WebKit/Source/core/dom/SpaceSplitString.h
@@ -67,9 +67,8 @@
     ~Data();
 
     bool contains(const AtomicString& string) {
-      size_t size = m_vector.size();
-      for (size_t i = 0; i < size; ++i) {
-        if (m_vector[i] == string)
+      for (const auto& item : m_vector) {
+        if (item == string)
           return true;
       }
       return false;
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index d707d0d..c3443800 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -559,8 +559,8 @@
     return;
 
   FontFaceCache* cache = m_fontSelector->fontFaceCache();
-  for (unsigned i = 0; i < fontFaceRules.size(); ++i)
-    cache->remove(fontFaceRules[i]);
+  for (const auto& rule : fontFaceRules)
+    cache->remove(rule);
   if (m_resolver)
     m_resolver->invalidateMatchedPropertiesCache();
 }
diff --git a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
index 13ddafc..f733694 100644
--- a/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
+++ b/third_party/WebKit/Source/core/dom/TaskRunnerHelper.cpp
@@ -6,9 +6,9 @@
 
 #include "core/dom/Document.h"
 #include "core/frame/LocalFrame.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/WebTaskRunner.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebThread.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/core/dom/TreeScopeStyleSheetCollection.cpp b/third_party/WebKit/Source/core/dom/TreeScopeStyleSheetCollection.cpp
index 21363da..a643306 100644
--- a/third_party/WebKit/Source/core/dom/TreeScopeStyleSheetCollection.cpp
+++ b/third_party/WebKit/Source/core/dom/TreeScopeStyleSheetCollection.cpp
@@ -89,9 +89,8 @@
   // StyleSheets of <style> elements that @import stylesheets are active but
   // loading. We need to trigger a full recalc when such loads are done.
   bool hasActiveLoadingStylesheet = false;
-  unsigned newStylesheetCount = newStyleSheets.size();
-  for (unsigned i = 0; i < newStylesheetCount; ++i) {
-    if (newStyleSheets[i]->isLoading())
+  for (const auto& sheet : newStyleSheets) {
+    if (sheet->isLoading())
       hasActiveLoadingStylesheet = true;
   }
   if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
@@ -107,11 +106,11 @@
     HeapVector<Member<const StyleRuleFontFace>>& fontFaceRules) {
   bool hasFontFaceRule = false;
 
-  for (unsigned i = 0; i < sheets.size(); ++i) {
-    DCHECK(sheets[i]);
-    if (sheets[i]->hasFontFaceRule()) {
+  for (const auto& sheet : sheets) {
+    DCHECK(sheet);
+    if (sheet->hasFontFaceRule()) {
       // FIXME: We don't need this for styles in shadow tree.
-      sheets[i]->findFontFaceRules(fontFaceRules);
+      sheet->findFontFaceRules(fontFaceRules);
       hasFontFaceRule = true;
     }
   }
@@ -171,8 +170,8 @@
 }
 
 void TreeScopeStyleSheetCollection::clearMediaQueryRuleSetStyleSheets() {
-  for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) {
-    StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents();
+  for (const auto& sheet : m_activeAuthorStyleSheets) {
+    StyleSheetContents* contents = sheet->contents();
     if (contents->hasMediaQueries())
       contents->clearRuleSet();
   }
diff --git a/third_party/WebKit/Source/core/dom/custom/V0CustomElementAsyncImportMicrotaskQueue.cpp b/third_party/WebKit/Source/core/dom/custom/V0CustomElementAsyncImportMicrotaskQueue.cpp
index 67b29af..a1061215 100644
--- a/third_party/WebKit/Source/core/dom/custom/V0CustomElementAsyncImportMicrotaskQueue.cpp
+++ b/third_party/WebKit/Source/core/dom/custom/V0CustomElementAsyncImportMicrotaskQueue.cpp
@@ -42,9 +42,9 @@
 void V0CustomElementAsyncImportMicrotaskQueue::doDispatch() {
   HeapVector<Member<V0CustomElementMicrotaskStep>> remaining;
 
-  for (unsigned i = 0; i < m_queue.size(); ++i) {
-    if (V0CustomElementMicrotaskStep::Processing == m_queue[i]->process())
-      remaining.append(m_queue[i].release());
+  for (auto& step : m_queue) {
+    if (V0CustomElementMicrotaskStep::Processing == step->process())
+      remaining.append(step.release());
   }
 
   m_queue.swap(remaining);
diff --git a/third_party/WebKit/Source/core/dom/custom/V0CustomElementMicrotaskQueueBase.cpp b/third_party/WebKit/Source/core/dom/custom/V0CustomElementMicrotaskQueueBase.cpp
index 27f17ce3..e6f7d0d 100644
--- a/third_party/WebKit/Source/core/dom/custom/V0CustomElementMicrotaskQueueBase.cpp
+++ b/third_party/WebKit/Source/core/dom/custom/V0CustomElementMicrotaskQueueBase.cpp
@@ -21,9 +21,9 @@
 
 #if !defined(NDEBUG)
 void V0CustomElementMicrotaskQueueBase::show(unsigned indent) {
-  for (unsigned q = 0; q < m_queue.size(); ++q) {
-    if (m_queue[q])
-      m_queue[q]->show(indent);
+  for (const auto& step : m_queue) {
+    if (step)
+      step->show(indent);
     else
       fprintf(stderr, "%*snull\n", indent, "");
   }
diff --git a/third_party/WebKit/Source/core/dom/shadow/ElementShadowV0.cpp b/third_party/WebKit/Source/core/dom/shadow/ElementShadowV0.cpp
index 5436c159..28d5fda 100644
--- a/third_party/WebKit/Source/core/dom/shadow/ElementShadowV0.cpp
+++ b/third_party/WebKit/Source/core/dom/shadow/ElementShadowV0.cpp
@@ -164,10 +164,7 @@
   for (ShadowRoot* root = &youngestShadowRoot(); root;
        root = root->olderShadowRoot()) {
     HTMLShadowElement* shadowInsertionPoint = 0;
-    const HeapVector<Member<InsertionPoint>>& insertionPoints =
-        root->descendantInsertionPoints();
-    for (size_t i = 0; i < insertionPoints.size(); ++i) {
-      InsertionPoint* point = insertionPoints[i];
+    for (const auto& point : root->descendantInsertionPoints()) {
       if (!point->isActive())
         continue;
       if (isHTMLShadowElement(*point)) {
diff --git a/third_party/WebKit/Source/core/dom/shadow/InsertionPoint.cpp b/third_party/WebKit/Source/core/dom/shadow/InsertionPoint.cpp
index da2cae9..43ebb05 100644
--- a/third_party/WebKit/Source/core/dom/shadow/InsertionPoint.cpp
+++ b/third_party/WebKit/Source/core/dom/shadow/InsertionPoint.cpp
@@ -157,10 +157,7 @@
 
   // Slow path only when there are more than one shadow elements in a shadow
   // tree. That should be a rare case.
-  const HeapVector<Member<InsertionPoint>>& insertionPoints =
-      shadowRoot->descendantInsertionPoints();
-  for (size_t i = 0; i < insertionPoints.size(); ++i) {
-    InsertionPoint* point = insertionPoints[i].get();
+  for (const auto& point : shadowRoot->descendantInsertionPoints()) {
     if (isHTMLShadowElement(*point))
       return point == this;
   }
diff --git a/third_party/WebKit/Source/core/events/EventPath.cpp b/third_party/WebKit/Source/core/events/EventPath.cpp
index 1c7c689..9b3ebc30 100644
--- a/third_party/WebKit/Source/core/events/EventPath.cpp
+++ b/third_party/WebKit/Source/core/events/EventPath.cpp
@@ -238,15 +238,15 @@
   TreeScopeEventContextMap treeScopeEventContextMap;
   TreeScopeEventContext* lastTreeScopeEventContext = nullptr;
 
-  for (size_t i = 0; i < size(); ++i) {
-    Node* currentNode = at(i).node();
+  for (auto& context : m_nodeEventContexts) {
+    Node* currentNode = context.node();
     TreeScope& currentTreeScope = currentNode->treeScope();
     if (lastTreeScope != &currentTreeScope) {
       lastTreeScopeEventContext = ensureTreeScopeEventContext(
           currentNode, &currentTreeScope, treeScopeEventContextMap);
     }
     DCHECK(lastTreeScopeEventContext);
-    at(i).setTreeScopeEventContext(lastTreeScopeEventContext);
+    context.setTreeScopeEventContext(lastTreeScopeEventContext);
     lastTreeScope = &currentTreeScope;
   }
   m_treeScopeEventContexts.appendRange(
@@ -258,10 +258,8 @@
                                     RelatedTargetMap& relatedTargetMap) {
   EventPath* relatedTargetEventPath =
       new EventPath(const_cast<Node&>(relatedNode));
-  for (size_t i = 0;
-       i < relatedTargetEventPath->m_treeScopeEventContexts.size(); ++i) {
-    TreeScopeEventContext* treeScopeEventContext =
-        relatedTargetEventPath->m_treeScopeEventContexts[i].get();
+  for (const auto& treeScopeEventContext :
+       relatedTargetEventPath->m_treeScopeEventContexts) {
     relatedTargetMap.add(&treeScopeEventContext->treeScope(),
                          treeScopeEventContext->target());
   }
diff --git a/third_party/WebKit/Source/core/events/EventSender.h b/third_party/WebKit/Source/core/events/EventSender.h
index 2da45cb..b6a2319d 100644
--- a/third_party/WebKit/Source/core/events/EventSender.h
+++ b/third_party/WebKit/Source/core/events/EventSender.h
@@ -85,15 +85,13 @@
 void EventSender<T>::cancelEvent(T* sender) {
   // Remove instances of this sender from both lists.
   // Use loops because we allow multiple instances to get into the lists.
-  size_t size = m_dispatchSoonList.size();
-  for (size_t i = 0; i < size; ++i) {
-    if (m_dispatchSoonList[i] == sender)
-      m_dispatchSoonList[i] = nullptr;
+  for (auto& senderInList : m_dispatchSoonList) {
+    if (senderInList == sender)
+      senderInList = nullptr;
   }
-  size = m_dispatchingList.size();
-  for (size_t i = 0; i < size; ++i) {
-    if (m_dispatchingList[i] == sender)
-      m_dispatchingList[i] = nullptr;
+  for (auto& senderInList : m_dispatchingList) {
+    if (senderInList == sender)
+      senderInList = nullptr;
   }
 }
 
@@ -108,10 +106,9 @@
   m_timer.stop();
 
   m_dispatchingList.swap(m_dispatchSoonList);
-  size_t size = m_dispatchingList.size();
-  for (size_t i = 0; i < size; ++i) {
-    if (T* sender = m_dispatchingList[i]) {
-      m_dispatchingList[i] = nullptr;
+  for (auto& senderInList : m_dispatchingList) {
+    if (T* sender = senderInList) {
+      senderInList = nullptr;
       sender->dispatchPendingEvent(this);
     }
   }
diff --git a/third_party/WebKit/Source/core/events/EventTarget.cpp b/third_party/WebKit/Source/core/events/EventTarget.cpp
index 4b272b8..a125d77 100644
--- a/third_party/WebKit/Source/core/events/EventTarget.cpp
+++ b/third_party/WebKit/Source/core/events/EventTarget.cpp
@@ -367,8 +367,7 @@
   // Notify firing events planning to invoke the listener at 'index' that
   // they have one less listener to invoke.
   if (d->firingEventIterators) {
-    for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
-      FiringEventIterator& firingIterator = d->firingEventIterators->at(i);
+    for (const auto& firingIterator : *d->firingEventIterators) {
       if (eventType != firingIterator.eventType)
         continue;
 
@@ -750,9 +749,9 @@
   // Notify firing events planning to invoke the listener at 'index' that
   // they have one less listener to invoke.
   if (d->firingEventIterators) {
-    for (size_t i = 0; i < d->firingEventIterators->size(); ++i) {
-      d->firingEventIterators->at(i).iterator = 0;
-      d->firingEventIterators->at(i).end = 0;
+    for (const auto& iterator : *d->firingEventIterators) {
+      iterator.iterator = 0;
+      iterator.end = 0;
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/events/ScopedEventQueue.cpp b/third_party/WebKit/Source/core/events/ScopedEventQueue.cpp
index 4d54982c..3fdd596 100644
--- a/third_party/WebKit/Source/core/events/ScopedEventQueue.cpp
+++ b/third_party/WebKit/Source/core/events/ScopedEventQueue.cpp
@@ -66,8 +66,8 @@
   HeapVector<Member<EventDispatchMediator>> queuedEventDispatchMediators;
   queuedEventDispatchMediators.swap(m_queuedEventDispatchMediators);
 
-  for (size_t i = 0; i < queuedEventDispatchMediators.size(); i++)
-    dispatchEvent(queuedEventDispatchMediators[i].release());
+  for (auto& mediator : queuedEventDispatchMediators)
+    dispatchEvent(mediator.release());
 }
 
 void ScopedEventQueue::dispatchEvent(EventDispatchMediator* mediator) const {
diff --git a/third_party/WebKit/Source/core/events/TreeScopeEventContext.cpp b/third_party/WebKit/Source/core/events/TreeScopeEventContext.cpp
index 93bfbea6..b7ab3e2 100644
--- a/third_party/WebKit/Source/core/events/TreeScopeEventContext.cpp
+++ b/third_party/WebKit/Source/core/events/TreeScopeEventContext.cpp
@@ -112,10 +112,10 @@
       (rootNode().isShadowRoot() && !toShadowRoot(rootNode()).isOpenOrV0())
           ? this
           : nearestAncestorClosedTreeScopeEventContext;
-  for (size_t i = 0; i < m_children.size(); ++i)
-    orderNumber =
-        m_children[i]->calculateTreeOrderAndSetNearestAncestorClosedTree(
-            orderNumber + 1, containingClosedShadowTree());
+  for (const auto& context : m_children) {
+    orderNumber = context->calculateTreeOrderAndSetNearestAncestorClosedTree(
+        orderNumber + 1, containingClosedShadowTree());
+  }
   m_postOrder = orderNumber + 1;
 
   return orderNumber + 1;
diff --git a/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp b/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
index 9ed546e0..7140b8e 100644
--- a/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
+++ b/third_party/WebKit/Source/core/events/UIEventWithKeyState.cpp
@@ -129,9 +129,9 @@
       {"NumLock", PlatformEvent::NumLockOn},
       {"Symbol", PlatformEvent::SymbolKey},
   };
-  for (size_t i = 0; i < WTF_ARRAY_LENGTH(kIdentifiers); ++i) {
-    if (keyIdentifier == kIdentifiers[i].identifier)
-      return m_modifiers & kIdentifiers[i].mask;
+  for (const auto& identifier : kIdentifiers) {
+    if (keyIdentifier == identifier.identifier)
+      return m_modifiers & identifier.mask;
   }
   return false;
 }
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.cpp b/third_party/WebKit/Source/core/fetch/FetchContext.cpp
index de0b4cf9..3278e745 100644
--- a/third_party/WebKit/Source/core/fetch/FetchContext.cpp
+++ b/third_party/WebKit/Source/core/fetch/FetchContext.cpp
@@ -76,14 +76,18 @@
 
 void FetchContext::dispatchDidReceiveData(unsigned long,
                                           const char*,
-                                          int,
                                           int) {}
 
+void FetchContext::dispatchDidReceiveEncodedData(unsigned long, int) {}
+
 void FetchContext::dispatchDidDownloadData(unsigned long, int, int) {}
 
 void FetchContext::dispatchDidFinishLoading(unsigned long, double, int64_t) {}
 
-void FetchContext::dispatchDidFail(unsigned long, const ResourceError&, bool) {}
+void FetchContext::dispatchDidFail(unsigned long,
+                                   const ResourceError&,
+                                   int64_t,
+                                   bool) {}
 
 void FetchContext::willStartLoadingResource(unsigned long,
                                             ResourceRequest&,
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.h b/third_party/WebKit/Source/core/fetch/FetchContext.h
index ebd4f5a6b..5c31515 100644
--- a/third_party/WebKit/Source/core/fetch/FetchContext.h
+++ b/third_party/WebKit/Source/core/fetch/FetchContext.h
@@ -38,6 +38,7 @@
 #include "core/fetch/Resource.h"
 #include "platform/heap/Handle.h"
 #include "platform/network/ResourceLoadPriority.h"
+#include "platform/network/ResourceRequest.h"
 #include "wtf/Noncopyable.h"
 
 namespace blink {
@@ -46,7 +47,6 @@
 class MHTMLArchive;
 class ResourceError;
 class ResourceResponse;
-class ResourceRequest;
 class ResourceTimingInfo;
 class WebTaskRunner;
 enum class WebCachePolicy;
@@ -101,8 +101,9 @@
                                           Resource*);
   virtual void dispatchDidReceiveData(unsigned long identifier,
                                       const char* data,
-                                      int dataLength,
-                                      int encodedDataLength);
+                                      int dataLength);
+  virtual void dispatchDidReceiveEncodedData(unsigned long identifier,
+                                             int encodedDataLength);
   virtual void dispatchDidDownloadData(unsigned long identifier,
                                        int dataLength,
                                        int encodedDataLength);
@@ -111,6 +112,7 @@
                                         int64_t encodedDataLength);
   virtual void dispatchDidFail(unsigned long identifier,
                                const ResourceError&,
+                               int64_t encodedDataLength,
                                bool isInternalRequest);
 
   virtual bool shouldLoadNewResource(Resource::Type) const { return false; }
@@ -123,19 +125,21 @@
 
   virtual void addResourceTiming(const ResourceTimingInfo&);
   virtual bool allowImage(bool, const KURL&) const { return false; }
-  virtual bool canRequest(Resource::Type,
-                          const ResourceRequest&,
-                          const KURL&,
-                          const ResourceLoaderOptions&,
-                          bool forPreload,
-                          FetchRequest::OriginRestriction) const {
-    return false;
+  virtual ResourceRequestBlockedReason canRequest(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&,
+      bool forPreload,
+      FetchRequest::OriginRestriction) const {
+    return ResourceRequestBlockedReason::Other;
   }
-  virtual bool allowResponse(Resource::Type,
-                             const ResourceRequest&,
-                             const KURL&,
-                             const ResourceLoaderOptions&) const {
-    return false;
+  virtual ResourceRequestBlockedReason allowResponse(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&) const {
+    return ResourceRequestBlockedReason::Other;
   }
 
   virtual bool isControlledByServiceWorker() const { return false; }
diff --git a/third_party/WebKit/Source/core/fetch/ImageResource.cpp b/third_party/WebKit/Source/core/fetch/ImageResource.cpp
index 0a45439..6fe000c8 100644
--- a/third_party/WebKit/Source/core/fetch/ImageResource.cpp
+++ b/third_party/WebKit/Source/core/fetch/ImageResource.cpp
@@ -83,12 +83,14 @@
   }
   if (fetcher->context().pageDismissalEventBeingDispatched()) {
     KURL requestURL = request.resourceRequest().url();
-    if (requestURL.isValid() &&
-        fetcher->context().canRequest(Resource::Image,
-                                      request.resourceRequest(), requestURL,
-                                      request.options(), request.forPreload(),
-                                      request.getOriginRestriction()))
-      fetcher->context().sendImagePing(requestURL);
+    if (requestURL.isValid()) {
+      ResourceRequestBlockedReason blockReason = fetcher->context().canRequest(
+          Resource::Image, request.resourceRequest(), requestURL,
+          request.options(), request.forPreload(),
+          request.getOriginRestriction());
+      if (blockReason == ResourceRequestBlockedReason::None)
+        fetcher->context().sendImagePing(requestURL);
+    }
     return nullptr;
   }
 
diff --git a/third_party/WebKit/Source/core/fetch/ImageResourceTest.cpp b/third_party/WebKit/Source/core/fetch/ImageResourceTest.cpp
index 82f6294..69ef203 100644
--- a/third_party/WebKit/Source/core/fetch/ImageResourceTest.cpp
+++ b/third_party/WebKit/Source/core/fetch/ImageResourceTest.cpp
@@ -171,13 +171,14 @@
   bool allowImage(bool imagesEnabled, const KURL&) const override {
     return true;
   }
-  bool canRequest(Resource::Type,
-                  const ResourceRequest&,
-                  const KURL&,
-                  const ResourceLoaderOptions&,
-                  bool forPreload,
-                  FetchRequest::OriginRestriction) const override {
-    return true;
+  ResourceRequestBlockedReason canRequest(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&,
+      bool forPreload,
+      FetchRequest::OriginRestriction) const override {
+    return ResourceRequestBlockedReason::None;
   }
   bool shouldLoadNewResource(Resource::Type) const override { return true; }
   WebTaskRunner* loadingTaskRunner() const override { return m_runner.get(); }
diff --git a/third_party/WebKit/Source/core/fetch/MockFetchContext.h b/third_party/WebKit/Source/core/fetch/MockFetchContext.h
index 3073307..076d7dd 100644
--- a/third_party/WebKit/Source/core/fetch/MockFetchContext.h
+++ b/third_party/WebKit/Source/core/fetch/MockFetchContext.h
@@ -38,13 +38,14 @@
   bool allowImage(bool imagesEnabled, const KURL&) const override {
     return true;
   }
-  bool canRequest(Resource::Type,
-                  const ResourceRequest&,
-                  const KURL&,
-                  const ResourceLoaderOptions&,
-                  bool forPreload,
-                  FetchRequest::OriginRestriction) const override {
-    return true;
+  ResourceRequestBlockedReason canRequest(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&,
+      bool forPreload,
+      FetchRequest::OriginRestriction) const override {
+    return ResourceRequestBlockedReason::None;
   }
   bool shouldLoadNewResource(Resource::Type) const override {
     return m_loadPolicy == kShouldLoadNewResource;
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
index cb7b2ccc..de6d97d 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
@@ -258,22 +258,24 @@
   return resource.get();
 }
 
-bool ResourceFetcher::canAccessResponse(
+ResourceRequestBlockedReason ResourceFetcher::canAccessResponse(
     Resource* resource,
     const ResourceResponse& response) const {
   // Redirects can change the response URL different from one of request.
   bool forPreload = resource->isUnusedPreload();
-  if (!context().canRequest(resource->getType(), resource->resourceRequest(),
-                            response.url(), resource->options(), forPreload,
-                            FetchRequest::UseDefaultOriginRestrictionForType))
-    return false;
+  ResourceRequestBlockedReason blockedReason =
+      context().canRequest(resource->getType(), resource->resourceRequest(),
+                           response.url(), resource->options(), forPreload,
+                           FetchRequest::UseDefaultOriginRestrictionForType);
+  if (blockedReason != ResourceRequestBlockedReason::None)
+    return blockedReason;
 
   SecurityOrigin* sourceOrigin = resource->options().securityOrigin.get();
   if (!sourceOrigin)
     sourceOrigin = context().getSecurityOrigin();
 
   if (sourceOrigin->canRequestNoSuborigin(response.url()))
-    return true;
+    return ResourceRequestBlockedReason::None;
 
   // Use the original response instead of the 304 response for a successful
   // revaldiation.
@@ -295,9 +297,9 @@
           "' from origin '" + sourceOrigin->toString() +
           "' has been blocked by CORS policy: " + errorDescription);
     }
-    return false;
+    return ResourceRequestBlockedReason::Other;
   }
-  return true;
+  return ResourceRequestBlockedReason::None;
 }
 
 bool ResourceFetcher::isControlledByServiceWorker() const {
@@ -437,10 +439,12 @@
 
 Resource* ResourceFetcher::resourceForBlockedRequest(
     const FetchRequest& request,
-    const ResourceFactory& factory) {
+    const ResourceFactory& factory,
+    ResourceRequestBlockedReason blockedReason) {
   Resource* resource = factory.create(request.resourceRequest(),
                                       request.options(), request.charset());
-  resource->error(ResourceError::cancelledDueToAccessCheckError(request.url()));
+  resource->error(ResourceError::cancelledDueToAccessCheckError(request.url(),
+                                                                blockedReason));
   return resource;
 }
 
@@ -513,13 +517,13 @@
   network_instrumentation::resourcePrioritySet(
       identifier, request.resourceRequest().priority());
 
-  if (!context().canRequest(
-          factory.type(), request.resourceRequest(),
-          MemoryCache::removeFragmentIdentifierIfNeeded(request.url()),
-          request.options(), request.forPreload(),
-          request.getOriginRestriction())) {
+  ResourceRequestBlockedReason blockedReason = context().canRequest(
+      factory.type(), request.resourceRequest(),
+      MemoryCache::removeFragmentIdentifierIfNeeded(request.url()),
+      request.options(), request.forPreload(), request.getOriginRestriction());
+  if (blockedReason != ResourceRequestBlockedReason::None) {
     DCHECK(!substituteData.forceSynchronousLoad());
-    return resourceForBlockedRequest(request, factory);
+    return resourceForBlockedRequest(request, factory, blockedReason);
   }
 
   context().willStartLoadingResource(
@@ -1187,7 +1191,9 @@
   m_resourceTimingInfoMap.take(const_cast<Resource*>(resource));
   bool isInternalRequest = resource->options().initiatorInfo.name ==
                            FetchInitiatorTypeNames::internal;
-  context().dispatchDidFail(resource->identifier(), error, isInternalRequest);
+  context().dispatchDidFail(resource->identifier(), error,
+                            resource->response().encodedDataLength(),
+                            isInternalRequest);
   resource->error(error);
   context().didLoadResource(resource);
 
@@ -1232,19 +1238,25 @@
     // not to load the resources which are forbidden by the page CSP.
     // https://w3c.github.io/webappsec-csp/#should-block-response
     const KURL& originalURL = response.originalURLViaServiceWorker();
-    if (!originalURL.isEmpty() &&
-        !context().allowResponse(resource->getType(),
-                                 resource->resourceRequest(), originalURL,
-                                 resource->options())) {
-      resource->loader()->didFail(
-          ResourceError::cancelledDueToAccessCheckError(originalURL));
+    if (!originalURL.isEmpty()) {
+      ResourceRequestBlockedReason blockedReason = context().allowResponse(
+          resource->getType(), resource->resourceRequest(), originalURL,
+          resource->options());
+      if (blockedReason != ResourceRequestBlockedReason::None) {
+        resource->loader()->didFail(
+            ResourceError::cancelledDueToAccessCheckError(originalURL,
+                                                          blockedReason));
+        return;
+      }
+    }
+  } else if (resource->options().corsEnabled == IsCORSEnabled) {
+    ResourceRequestBlockedReason blockedReason =
+        canAccessResponse(resource, response);
+    if (blockedReason != ResourceRequestBlockedReason::None) {
+      resource->loader()->didFail(ResourceError::cancelledDueToAccessCheckError(
+          response.url(), blockedReason));
       return;
     }
-  } else if (resource->options().corsEnabled == IsCORSEnabled &&
-             !canAccessResponse(resource, response)) {
-    resource->loader()->didFail(
-        ResourceError::cancelledDueToAccessCheckError(response.url()));
-    return;
   }
 
   context().dispatchDidReceiveResponse(
@@ -1261,8 +1273,9 @@
                                      const char* data,
                                      int dataLength,
                                      int encodedDataLength) {
-  context().dispatchDidReceiveData(resource->identifier(), data, dataLength,
-                                   encodedDataLength);
+  context().dispatchDidReceiveData(resource->identifier(), data, dataLength);
+  context().dispatchDidReceiveEncodedData(resource->identifier(),
+                                          encodedDataLength);
 }
 
 void ResourceFetcher::didDownloadData(const Resource* resource,
@@ -1363,15 +1376,17 @@
          request.requestContext() == WebURLRequest::RequestContextFetch;
 }
 
-bool ResourceFetcher::willFollowRedirect(
+ResourceRequestBlockedReason ResourceFetcher::willFollowRedirect(
     Resource* resource,
     ResourceRequest& newRequest,
     const ResourceResponse& redirectResponse) {
   if (!isManualRedirectFetchRequest(resource->resourceRequest())) {
-    if (!context().canRequest(resource->getType(), newRequest, newRequest.url(),
-                              resource->options(), resource->isUnusedPreload(),
-                              FetchRequest::UseDefaultOriginRestrictionForType))
-      return false;
+    ResourceRequestBlockedReason blockedReason =
+        context().canRequest(resource->getType(), newRequest, newRequest.url(),
+                             resource->options(), resource->isUnusedPreload(),
+                             FetchRequest::UseDefaultOriginRestrictionForType);
+    if (blockedReason != ResourceRequestBlockedReason::None)
+      return blockedReason;
     if (resource->options().corsEnabled == IsCORSEnabled) {
       RefPtr<SecurityOrigin> sourceOrigin = resource->options().securityOrigin;
       if (!sourceOrigin.get())
@@ -1387,12 +1402,13 @@
               resource->mutableOptions(), errorMessage)) {
         resource->setCORSFailed();
         context().addConsoleMessage(errorMessage);
-        return false;
+        return ResourceRequestBlockedReason::Other;
       }
     }
     if (resource->getType() == Resource::Image &&
-        shouldDeferImageLoad(newRequest.url()))
-      return false;
+        shouldDeferImageLoad(newRequest.url())) {
+      return ResourceRequestBlockedReason::Other;
+    }
   }
 
   ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
@@ -1404,7 +1420,7 @@
                                        AllowStoredCredentials);
   willSendRequest(resource->identifier(), newRequest, redirectResponse,
                   resource->options());
-  return true;
+  return ResourceRequestBlockedReason::None;
 }
 
 void ResourceFetcher::willSendRequest(unsigned long identifier,
@@ -1597,6 +1613,23 @@
   return MemoryCache::defaultCacheIdentifier();
 }
 
+void ResourceFetcher::emulateLoadStartedForInspector(
+    Resource* resource,
+    const KURL& url,
+    WebURLRequest::RequestContext requestContext,
+    const AtomicString& initiatorName) {
+  if (cachedResource(url))
+    return;
+  ResourceRequest resourceRequest(url);
+  resourceRequest.setRequestContext(requestContext);
+  FetchRequest request(resourceRequest, initiatorName, resource->options());
+  context().canRequest(resource->getType(), resource->lastResourceRequest(),
+                       resource->lastResourceRequest().url(), request.options(),
+                       false, request.getOriginRestriction());
+  requestLoadStarted(resource->identifier(), resource, request,
+                     ResourceLoadingFromCache);
+}
+
 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder()
     : m_useCount(0), m_revalidateCount(0), m_loadCount(0) {}
 
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
index 3b73a98d..7fe48c2b 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.h
@@ -115,7 +115,9 @@
   void stopFetching();
   bool isFetching() const;
 
-  bool willFollowRedirect(Resource*, ResourceRequest&, const ResourceResponse&);
+  ResourceRequestBlockedReason willFollowRedirect(Resource*,
+                                                  ResourceRequest&,
+                                                  const ResourceResponse&);
   enum DidFinishLoadingReason {
     DidFinishLoading,
     DidFinishFirstPartInMultipart
@@ -139,11 +141,6 @@
     ResourceLoadingFromNetwork,
     ResourceLoadingFromCache
   };
-  void requestLoadStarted(unsigned long identifier,
-                          Resource*,
-                          const FetchRequest&,
-                          ResourceLoadStartType,
-                          bool isStaticData = false);
   static const ResourceLoaderOptions& defaultResourceOptions();
 
   String getCacheIdentifier() const;
@@ -160,6 +157,13 @@
   // This is only exposed for testing purposes.
   HeapListHashSet<Member<Resource>>* preloads() { return m_preloads.get(); }
 
+  // Workaround for https://crbug.com/666214.
+  // TODO(hiroshige): Remove this hack.
+  void emulateLoadStartedForInspector(Resource*,
+                                      const KURL&,
+                                      WebURLRequest::RequestContext,
+                                      const AtomicString& initiatorName);
+
  private:
   friend class ResourceCacheValidationSuppressor;
 
@@ -178,7 +182,8 @@
                                   const ResourceFactory&,
                                   const SubstituteData&);
   Resource* resourceForBlockedRequest(const FetchRequest&,
-                                      const ResourceFactory&);
+                                      const ResourceFactory&,
+                                      ResourceRequestBlockedReason);
 
   // RevalidationPolicy enum values are used in UMAs https://crbug.com/579496.
   enum RevalidationPolicy { Use, Revalidate, Reload, Load };
@@ -198,7 +203,14 @@
                        ResourceRequest&,
                        const ResourceResponse&,
                        const ResourceLoaderOptions&);
-  bool canAccessResponse(Resource*, const ResourceResponse&) const;
+  ResourceRequestBlockedReason canAccessResponse(Resource*,
+                                                 const ResourceResponse&) const;
+
+  void requestLoadStarted(unsigned long identifier,
+                          Resource*,
+                          const FetchRequest&,
+                          ResourceLoadStartType,
+                          bool isStaticData = false);
 
   bool resourceNeedsLoad(Resource*, const FetchRequest&, RevalidationPolicy);
   bool shouldDeferImageLoad(const KURL&) const;
diff --git a/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp b/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp
index 1dc79fd..e655e4aa 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp
+++ b/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp
@@ -133,11 +133,15 @@
       ResourceError::cancelledError(m_resource->lastResourceRequest().url()));
 }
 
-void ResourceLoader::cancelForRedirectAccessCheckError(const KURL& newURL) {
+void ResourceLoader::cancelForRedirectAccessCheckError(
+    const KURL& newURL,
+    ResourceRequestBlockedReason blockedReason) {
   m_resource->willNotFollowRedirect();
 
-  if (m_loader)
-    didFail(ResourceError::cancelledDueToAccessCheckError(newURL));
+  if (m_loader) {
+    didFail(
+        ResourceError::cancelledDueToAccessCheckError(newURL, blockedReason));
+  }
 }
 
 bool ResourceLoader::willFollowRedirect(
@@ -161,9 +165,10 @@
 
   const KURL originalURL = newRequest.url();
 
-  if (!m_fetcher->willFollowRedirect(m_resource.get(), newRequest,
-                                     redirectResponse)) {
-    cancelForRedirectAccessCheckError(newRequest.url());
+  ResourceRequestBlockedReason blockedReason = m_fetcher->willFollowRedirect(
+      m_resource.get(), newRequest, redirectResponse);
+  if (blockedReason != ResourceRequestBlockedReason::None) {
+    cancelForRedirectAccessCheckError(newRequest.url(), blockedReason);
     return false;
   }
 
@@ -174,12 +179,14 @@
   // rewriting but currently we cannot. So, return false to make the
   // redirect fail.
   if (newRequest.url() != originalURL) {
-    cancelForRedirectAccessCheckError(newRequest.url());
+    cancelForRedirectAccessCheckError(newRequest.url(),
+                                      ResourceRequestBlockedReason::Other);
     return false;
   }
 
   if (!m_resource->willFollowRedirect(newRequest, redirectResponse)) {
-    cancelForRedirectAccessCheckError(newRequest.url());
+    cancelForRedirectAccessCheckError(newRequest.url(),
+                                      ResourceRequestBlockedReason::Other);
     return false;
   }
 
diff --git a/third_party/WebKit/Source/core/fetch/ResourceLoader.h b/third_party/WebKit/Source/core/fetch/ResourceLoader.h
index 5caf8327..19da551 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceLoader.h
+++ b/third_party/WebKit/Source/core/fetch/ResourceLoader.h
@@ -112,7 +112,8 @@
   // Assumes ResourceFetcher and Resource are non-null.
   ResourceLoader(ResourceFetcher*, Resource*);
 
-  void cancelForRedirectAccessCheckError(const KURL&);
+  void cancelForRedirectAccessCheckError(const KURL&,
+                                         ResourceRequestBlockedReason);
   void requestSynchronously(const ResourceRequest&);
 
   std::unique_ptr<WebURLLoader> m_loader;
diff --git a/third_party/WebKit/Source/core/fileapi/File.cpp b/third_party/WebKit/Source/core/fileapi/File.cpp
index 936b82d5..5ca1b943 100644
--- a/third_party/WebKit/Source/core/fileapi/File.cpp
+++ b/third_party/WebKit/Source/core/fileapi/File.cpp
@@ -30,8 +30,8 @@
 #include "core/fileapi/FilePropertyBag.h"
 #include "core/frame/UseCounter.h"
 #include "platform/FileMetadata.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/blob/BlobData.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebFileUtilities.h"
 #include "wtf/CurrentTime.h"
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 4419357..449368e 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -103,6 +103,7 @@
 #include "platform/HostWindow.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/ScriptForbiddenScope.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/fonts/FontCache.h"
 #include "platform/geometry/DoubleRect.h"
 #include "platform/geometry/FloatRect.h"
@@ -121,7 +122,6 @@
 #include "platform/tracing/TraceEvent.h"
 #include "platform/tracing/TracedValue.h"
 #include "public/platform/WebDisplayItemList.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "wtf/CurrentTime.h"
 #include "wtf/PtrUtil.h"
 #include "wtf/StdLibExtras.h"
@@ -329,6 +329,10 @@
   if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
     scrollingCoordinator->willDestroyScrollableArea(this);
 
+  FrameHost* frameHost = m_frame->host();
+  DCHECK(frameHost);
+  frameHost->globalRootScrollerController().didDisposeScrollableArea(*this);
+
   // We need to clear the RootFrameViewport's animator since it gets called
   // from non-GC'd objects and RootFrameViewport will still have a pointer to
   // this class.
@@ -647,6 +651,10 @@
   updateParentScrollableAreaSet();
 
   page->chromeClient().contentsSizeChanged(m_frame.get(), size);
+
+  // Ensure the scrollToFragmentAnchor is called before
+  // restoreScrollPositionAndViewState when reload
+  scrollToFragmentAnchor();
   frame().loader().restoreScrollPositionAndViewState();
 }
 
@@ -1252,7 +1260,7 @@
   if (m_nestedLayoutCount)
     return;
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   // Post-layout assert that nobody was re-marked as needing layout during
   // layout.
   layoutView()->assertSubtreeIsLaidOut();
@@ -1281,7 +1289,7 @@
   invalidatePaintIfNeeded(paintInvalidationState);
   rootForPaintInvalidation.invalidateTreeIfNeeded(paintInvalidationState);
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   layoutView()->assertSubtreeClearedPaintInvalidationFlags();
 #endif
 
@@ -1431,6 +1439,16 @@
     m_frame->document()->mediaQueryAffectingValueChanged();
 }
 
+void FrameView::setDisplayShape(DisplayShape displayShape) {
+  if (displayShape == m_displayShape)
+    return;
+
+  m_displayShape = displayShape;
+
+  if (m_frame->document())
+    m_frame->document()->mediaQueryAffectingValueChanged();
+}
+
 void FrameView::setMediaType(const AtomicString& mediaType) {
   DCHECK(m_frame->document());
   m_mediaType = mediaType;
@@ -2870,8 +2888,11 @@
       if (targetState >= DocumentLifecycle::PrePaintClean) {
         if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
           invalidateTreeIfNeededRecursive();
-        if (view.compositor()->inCompositingMode())
-          scrollingCoordinator()->updateAfterCompositingChangeIfNeeded();
+
+        if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
+          if (view.compositor()->inCompositingMode())
+            scrollingCoordinator()->updateAfterCompositingChangeIfNeeded();
+        }
 
         updateCompositedSelectionIfNeeded();
       }
@@ -3106,7 +3127,7 @@
   // These asserts ensure that parent frames are clean, when child frames
   // finished updating layout and style.
   checkDoesNotNeedLayout();
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   m_frame->document()->layoutView()->assertLaidOut();
 #endif
 
@@ -3790,11 +3811,6 @@
   frame().loader().client()->didChangeScrollOffset();
   if (frame().isMainFrame())
     frame().host()->chromeClient().mainFrameScrollOffsetChanged();
-
-  if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
-    // The scroll translation paint property depends on scroll offset.
-    setNeedsPaintPropertyUpdate();
-  }
 }
 
 void FrameView::clearScrollAnchor() {
diff --git a/third_party/WebKit/Source/core/frame/FrameView.h b/third_party/WebKit/Source/core/frame/FrameView.h
index 31a2d1ec..f349659 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.h
+++ b/third_party/WebKit/Source/core/frame/FrameView.h
@@ -45,6 +45,7 @@
 #include "platform/graphics/GraphicsLayerClient.h"
 #include "platform/scroll/ScrollTypes.h"
 #include "platform/scroll/Scrollbar.h"
+#include "public/platform/ShapeProperties.h"
 #include "public/platform/WebDisplayMode.h"
 #include "public/platform/WebRect.h"
 #include "wtf/Allocator.h"
@@ -216,6 +217,9 @@
   WebDisplayMode displayMode() { return m_displayMode; }
   void setDisplayMode(WebDisplayMode);
 
+  DisplayShape displayShape() { return m_displayShape; }
+  void setDisplayShape(DisplayShape);
+
   // Fixed-position objects.
   typedef HashSet<LayoutObject*> ViewportConstrainedObjectSet;
   void addViewportConstrainedObject(LayoutObject*);
@@ -978,6 +982,8 @@
 
   WebDisplayMode m_displayMode;
 
+  DisplayShape m_displayShape;
+
   bool m_canHaveScrollbars;
 
   bool m_hasPendingLayout;
diff --git a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
index 3bebb361..776800c 100644
--- a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
+++ b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
@@ -275,9 +275,8 @@
 static PassRefPtr<StaticBitmapImage> cropImage(
     Image* image,
     const ParsedOptions& parsedOptions,
-    AlphaDisposition imageFormat = PremultiplyAlpha,
-    ImageDecoder::ColorSpaceOption colorSpaceOp =
-        ImageDecoder::ColorSpaceTransformed) {
+    AlphaDisposition imageFormat,
+    ColorBehavior colorBehavior) {
   ASSERT(image);
   IntRect imgRect(IntPoint(), IntSize(image->width(), image->height()));
   const IntRect srcRect = intersection(imgRect, parsedOptions.cropRect);
@@ -307,14 +306,12 @@
   if ((((!parsedOptions.premultiplyAlpha && !skiaImage->isOpaque()) ||
         !skiaImage) &&
        image->data() && imageFormat == PremultiplyAlpha) ||
-      colorSpaceOp == ImageDecoder::ColorSpaceIgnored) {
+      colorBehavior.isIgnore()) {
     std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
         image->data(), true,
         parsedOptions.premultiplyAlpha ? ImageDecoder::AlphaPremultiplied
                                        : ImageDecoder::AlphaNotPremultiplied,
-        colorSpaceOp, colorSpaceOp == ImageDecoder::ColorSpaceTransformed
-                          ? ImageDecoder::globalTargetColorSpace()
-                          : nullptr));
+        colorBehavior));
     if (!decoder)
       return nullptr;
     skiaImage = ImageBitmap::getSkImageFromDecoder(std::move(decoder));
@@ -392,10 +389,10 @@
 
   if (options.colorSpaceConversion() == "none") {
     m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha,
-                        ImageDecoder::ColorSpaceIgnored);
+                        ColorBehavior::ignore());
   } else {
     m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha,
-                        ImageDecoder::ColorSpaceTransformed);
+                        ColorBehavior::transformToGlobalTarget());
   }
   if (!m_image)
     return;
@@ -486,7 +483,8 @@
     parsedOptions.premultiplyAlpha = true;
     isPremultiplyAlphaReverted = true;
   }
-  m_image = cropImage(input.get(), parsedOptions);
+  m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha,
+                      ColorBehavior::transformToGlobalTarget());
   if (!m_image)
     return;
   if (isPremultiplyAlphaReverted) {
@@ -697,9 +695,10 @@
   if (dstBufferSizeHasOverflow(parsedOptions))
     return;
 
-  m_image = cropImage(input.get(), parsedOptions, bitmap->isPremultiplied()
-                                                      ? PremultiplyAlpha
-                                                      : DontPremultiplyAlpha);
+  m_image = cropImage(
+      input.get(), parsedOptions,
+      bitmap->isPremultiplied() ? PremultiplyAlpha : DontPremultiplyAlpha,
+      ColorBehavior::transformToGlobalTarget());
   if (!m_image)
     return;
   m_image->setOriginClean(bitmap->originClean());
@@ -715,7 +714,8 @@
   if (dstBufferSizeHasOverflow(parsedOptions))
     return;
 
-  m_image = cropImage(input.get(), parsedOptions);
+  m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha,
+                      ColorBehavior::transformToGlobalTarget());
   if (!m_image)
     return;
 
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index ad7dc4f..c30f6aa 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -79,10 +79,10 @@
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
 #include "platform/EventDispatchForbiddenScope.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "platform/weborigin/Suborigin.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebScreenInfo.h"
 #include <memory>
 
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index fce5576..10194ae 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -78,6 +78,7 @@
 #include "platform/PluginScriptForbiddenScope.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/ScriptForbiddenScope.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/graphics/GraphicsContext.h"
 #include "platform/graphics/StaticBitmapImage.h"
 #include "platform/graphics/paint/ClipRecorder.h"
@@ -89,7 +90,6 @@
 #include "platform/text/TextStream.h"
 #include "public/platform/InterfaceProvider.h"
 #include "public/platform/InterfaceRegistry.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebScreenInfo.h"
 #include "public/platform/WebViewScheduler.h"
 #include "third_party/skia/include/core/SkImage.h"
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.cpp b/third_party/WebKit/Source/core/frame/UseCounter.cpp
index e8ea148..e07d0d6 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.cpp
+++ b/third_party/WebKit/Source/core/frame/UseCounter.cpp
@@ -1050,7 +1050,9 @@
       return 540;
     case CSSPropertyOffsetPath:
       return 541;
+    case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
+      // TODO(ericwilligers): Distinct use counter for CSSPropertyOffsetRotate.
       return 542;
     case CSSPropertyOffset:
       return 543;
@@ -1226,9 +1228,10 @@
   if (!m_CSSRecorded.quickGet(property)) {
     // Note that HTTPArchive tooling looks specifically for this event - see
     // https://github.com/HTTPArchive/httparchive/issues/59
+    int sampleId = mapCSSPropertyIdToCSSSampleIdForHistogram(property);
     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("blink.feature_usage"),
-                 "CSSFeatureFirstUsed", "feature", property);
-    cssHistogram().count(mapCSSPropertyIdToCSSSampleIdForHistogram(property));
+                 "CSSFirstUsed", "feature", sampleId);
+    cssHistogram().count(sampleId);
     m_CSSRecorded.quickSet(property);
   }
   m_legacyCounter.countCSS(property);
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index bb6e1f5..4a3af7d 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1386,6 +1386,7 @@
     CSSGridLayout = 1693,
     V8BarcodeDetector_Detect_Method = 1694,
     V8FaceDetector_Detect_Method = 1695,
+    FullscreenAllowedByOrientationChange = 1696,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
index 4d57b23e..f202d21 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
@@ -1200,9 +1200,9 @@
       return operativeDirective(m_styleSrc.get());
     // Directives that default to child-src, which defaults to default-src.
     case ContentSecurityPolicy::DirectiveType::FrameSrc:
-      return operativeDirective(m_frameSrc,
+      return operativeDirective(m_frameSrc.get(),
                                 operativeDirective(m_childSrc.get()));
-    // TODO(mkwst): Reevaluate this
+    // TODO(mkwst): Reevaluate this.
     case ContentSecurityPolicy::DirectiveType::WorkerSrc:
       return operativeDirective(m_workerSrc.get(),
                                 operativeDirective(m_childSrc.get()));
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp
index bbde1965..81f6696 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPSource.cpp
@@ -132,7 +132,8 @@
   bool hostsMatch = (m_host == other->m_host) || hostMatches(other->m_host) ||
                     other->hostMatches(m_host);
   bool portsMatch = (other->m_portWildcard == HasWildcard) ||
-                    portMatches(other->m_port, other->m_scheme);
+                    portMatches(other->m_port, other->m_scheme) ||
+                    other->portMatches(m_port, m_scheme);
   bool pathsMatch = pathMatches(other->m_path) || other->pathMatches(m_path);
   if (hostsMatch && portsMatch && pathsMatch)
     return true;
@@ -154,7 +155,11 @@
 
   String host = m_hostWildcard == NoWildcard ? m_host : other->m_host;
   String path = other->pathMatches(m_path) ? m_path : other->m_path;
-  int port = (other->m_portWildcard == HasWildcard || !other->m_port)
+  // Choose this port if the other port is empty, has wildcard or is a port for
+  // a less secure scheme such as "http" whereas scheme of this is "https", in
+  // which case the lengths would differ.
+  int port = (other->m_portWildcard == HasWildcard || !other->m_port ||
+              m_scheme.length() > other->m_scheme.length())
                  ? m_port
                  : other->m_port;
   WildcardDisposition hostWildcard =
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp
index e595028..39fb4f6 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPSourceTest.cpp
@@ -388,6 +388,12 @@
        {"wss", "example.com", "/", 0},
        true},  // use default port
       {{"http", "example.com", "/", 80}, {"http", "example.com", "/", 0}, true},
+      {{"http", "example.com", "/", 80},
+       {"https", "example.com", "/", 443},
+       true},
+      {{"http", "example.com", "/", 80},
+       {"https", "example.com", "/", 444},
+       false},
       // Paths
       {{"http", "example.com", "/", 0},
        {"http", "example.com", "/1.html", 0},
@@ -600,6 +606,18 @@
         CSPSource::NoWildcard},
        {"http", "example.com", "/", 80, CSPSource::NoWildcard,
         CSPSource::NoWildcard}},
+      {{"http", "example.com", "/", 80, CSPSource::NoWildcard,
+        CSPSource::NoWildcard},
+       {"https", "example.com", "/", 443, CSPSource::NoWildcard,
+        CSPSource::NoWildcard},
+       {"https", "example.com", "/", 443, CSPSource::NoWildcard,
+        CSPSource::NoWildcard}},
+      {{"https", "example.com", "/", 443, CSPSource::NoWildcard,
+        CSPSource::NoWildcard},
+       {"http", "example.com", "/", 80, CSPSource::NoWildcard,
+        CSPSource::NoWildcard},
+       {"https", "example.com", "/", 443, CSPSource::NoWildcard,
+        CSPSource::NoWildcard}},
       // Paths
       {{"http", "example.com", "/", 0, CSPSource::NoWildcard,
         CSPSource::NoWildcard},
diff --git a/third_party/WebKit/Source/core/frame/csp/MediaListDirective.cpp b/third_party/WebKit/Source/core/frame/csp/MediaListDirective.cpp
index c067b3c..f0c215dc 100644
--- a/third_party/WebKit/Source/core/frame/csp/MediaListDirective.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/MediaListDirective.cpp
@@ -26,6 +26,8 @@
 }
 
 void MediaListDirective::parse(const UChar* begin, const UChar* end) {
+  // TODO(amalika): Revisit parsing algorithm. Right now plugin types are not
+  // validated when they are added to m_pluginTypes.
   const UChar* position = begin;
 
   // 'plugin-types ____;' OR 'plugin-types;'
@@ -81,4 +83,37 @@
   }
 }
 
+bool MediaListDirective::subsumes(
+    const std::vector<MediaListDirective*>& other) {
+  if (!other.size())
+    return false;
+
+  // Find the effective set of plugins allowed by `other`.
+  HashSet<String> normalizedB = other[0]->m_pluginTypes;
+  for (size_t i = 1; i < other.size(); i++)
+    normalizedB = other[i]->getIntersect(normalizedB);
+
+  // Empty list of plugins is equivalent to no plugins being allowed.
+  if (!m_pluginTypes.size())
+    return !normalizedB.size();
+
+  // Check that each element of `normalizedB` is allowed by `m_pluginTypes`.
+  for (auto it = normalizedB.begin(); it != normalizedB.end(); ++it) {
+    if (!allows(*it))
+      return false;
+  }
+
+  return true;
+}
+
+HashSet<String> MediaListDirective::getIntersect(const HashSet<String>& other) {
+  HashSet<String> normalized;
+  for (const auto& type : m_pluginTypes) {
+    if (other.contains(type))
+      normalized.add(type);
+  }
+
+  return normalized;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/csp/MediaListDirective.h b/third_party/WebKit/Source/core/frame/csp/MediaListDirective.h
index d6c67217..c9e5cbf 100644
--- a/third_party/WebKit/Source/core/frame/csp/MediaListDirective.h
+++ b/third_party/WebKit/Source/core/frame/csp/MediaListDirective.h
@@ -14,7 +14,7 @@
 
 class ContentSecurityPolicy;
 
-class MediaListDirective final : public CSPDirective {
+class CORE_EXPORT MediaListDirective final : public CSPDirective {
   WTF_MAKE_NONCOPYABLE(MediaListDirective);
 
  public:
@@ -23,9 +23,20 @@
                      ContentSecurityPolicy*);
   bool allows(const String& type);
 
+  // The algorothm is described more extensively here:
+  // https://w3c.github.io/webappsec-csp/embedded/#subsume-policy.
+  bool subsumes(const std::vector<MediaListDirective*>& other);
+
  private:
+  FRIEND_TEST_ALL_PREFIXES(MediaListDirectiveTest, GetIntersect);
+  FRIEND_TEST_ALL_PREFIXES(MediaListDirectiveTest, Subsumes);
+
   void parse(const UChar* begin, const UChar* end);
 
+  // The algorothm is described more extensively here:
+  // https://w3c.github.io/webappsec-csp/embedded/#subsume-policy.
+  HashSet<String> getIntersect(const HashSet<String>& other);
+
   HashSet<String> m_pluginTypes;
 };
 
diff --git a/third_party/WebKit/Source/core/frame/csp/MediaListDirectiveTest.cpp b/third_party/WebKit/Source/core/frame/csp/MediaListDirectiveTest.cpp
new file mode 100644
index 0000000..c5939fb
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/csp/MediaListDirectiveTest.cpp
@@ -0,0 +1,152 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/frame/csp/MediaListDirective.h"
+
+#include "core/frame/csp/ContentSecurityPolicy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class MediaListDirectiveTest : public ::testing::Test {
+ public:
+  MediaListDirectiveTest() : csp(ContentSecurityPolicy::create()) {}
+
+ protected:
+  Persistent<ContentSecurityPolicy> csp;
+};
+
+TEST_F(MediaListDirectiveTest, GetIntersect) {
+  MediaListDirective A(
+      "plugin-types",
+      "application/x-shockwave-flash application/pdf text/plain", csp.get());
+  MediaListDirective emptyA("plugin-types", "", csp.get());
+
+  struct TestCase {
+    const char* policyB;
+    const std::vector<const char*> expected;
+  } cases[] = {
+      {"", std::vector<const char*>()},
+      {"text/", std::vector<const char*>()},
+      {"text/*", std::vector<const char*>()},
+      {"*/plain", std::vector<const char*>()},
+      {"text/plain */plain", {"text/plain"}},
+      {"text/plain application/*", {"text/plain"}},
+      {"text/plain", {"text/plain"}},
+      {"application/pdf", {"application/pdf"}},
+      {"application/x-shockwave-flash", {"application/x-shockwave-flash"}},
+      {"application/x-shockwave-flash text/plain",
+       {"application/x-shockwave-flash", "text/plain"}},
+      {"application/pdf text/plain", {"text/plain", "application/pdf"}},
+      {"application/x-shockwave-flash application/pdf text/plain",
+       {"application/x-shockwave-flash", "application/pdf", "text/plain"}},
+  };
+
+  for (const auto& test : cases) {
+    MediaListDirective B("plugin-types", test.policyB, csp.get());
+
+    HashSet<String> result = A.getIntersect(B.m_pluginTypes);
+    EXPECT_EQ(result.size(), test.expected.size());
+
+    for (const auto& type : test.expected)
+      EXPECT_TRUE(result.contains(type));
+
+    // If we change the order of `A` and `B`, intersection should not change.
+    result = B.getIntersect(A.m_pluginTypes);
+    EXPECT_EQ(result.size(), test.expected.size());
+
+    for (const auto& type : test.expected)
+      EXPECT_TRUE(result.contains(type));
+
+    // When `A` is empty, there should not be any intersection.
+    result = emptyA.getIntersect(B.m_pluginTypes);
+    EXPECT_FALSE(result.size());
+  }
+}
+
+TEST_F(MediaListDirectiveTest, Subsumes) {
+  MediaListDirective A(
+      "plugin-types",
+      "application/x-shockwave-flash application/pdf text/plain text/*",
+      csp.get());
+
+  struct TestCase {
+    const std::vector<const char*> policiesB;
+    bool subsumed;
+    bool subsumedByEmptyA;
+  } cases[] = {
+      // `A` subsumes `policiesB`.
+      {{""}, true, true},
+      {{"text/"}, true, true},
+      {{"text/*"}, true, false},
+      {{"application/*"}, false, false},
+      {{"application/"}, true, true},
+      {{"*/plain"}, false, false},
+      {{"application"}, true, true},
+      {{"text/plain"}, true, false},
+      {{"application/pdf"}, true, false},
+      {{"application/x-shockwave-flash"}, true, false},
+      {{"application/x-shockwave-flash text/plain"}, true, false},
+      {{"application/pdf text/plain"}, true, false},
+      {{"application/x-shockwave-flash text/plain application/pdf"},
+       true,
+       false},
+      {{"application/x-shockwave-flash text "}, true, false},
+      {{"text/* application/x-shockwave-flash"}, true, false},
+      {{"application/ application/x-shockwave-flash"}, true, false},
+      {{"*/plain application/x-shockwave-flash"}, false, false},
+      {{"text/ application/x-shockwave-flash"}, true, false},
+      {{"application application/x-shockwave-flash"}, true, false},
+      {{"application/x-shockwave-flash text/plain "
+        "application/x-blink-test-plugin",
+        "application/x-shockwave-flash text/plain"},
+       true,
+       false},
+      {{"application/x-shockwave-flash text/plain "
+        "application/x-blink-test-plugin",
+        "text/plain"},
+       true,
+       false},
+      {{"application/x-blink-test-plugin", "text/plain"}, true, true},
+      {{"application/x-shockwave-flash",
+        "text/plain application/x-shockwave-flash"},
+       true,
+       false},
+      {{"application/x-shockwave-flash text/plain",
+        "application/x-blink-test-plugin", "text/plain"},
+       true,
+       true},
+      // `A` does not subsumes `policiesB`.
+      {std::vector<const char*>(), false, false},
+      {{"application/x-blink-test-plugin"}, false, false},
+      {{"application/x-shockwave-flash text/plain "
+        "application/x-blink-test-plugin"},
+       false,
+       false},
+      {{"application/x-shockwave-flash text application/x-blink-test-plugin"},
+       false,
+       false},
+      {{"application/x-invalid-type text application/"}, false, false},
+      {{"application/x-blink-test-plugin text application/",
+        "application/x-blink-test-plugin"},
+       false,
+       false},
+  };
+
+  MediaListDirective emptyA("plugin-types", "", csp.get());
+  EXPECT_TRUE(emptyA.subsumes({&emptyA}));
+
+  for (const auto& test : cases) {
+    std::vector<MediaListDirective*> policiesB;
+    for (const auto& policy : test.policiesB) {
+      policiesB.push_back(
+          new MediaListDirective("plugin-types", policy, csp.get()));
+    }
+
+    EXPECT_EQ(A.subsumes(policiesB), test.subsumed);
+    EXPECT_EQ(emptyA.subsumes(policiesB), test.subsumedByEmptyA);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLAttributeNames.in b/third_party/WebKit/Source/core/html/HTMLAttributeNames.in
index 6c546dd..50d07db 100644
--- a/third_party/WebKit/Source/core/html/HTMLAttributeNames.in
+++ b/third_party/WebKit/Source/core/html/HTMLAttributeNames.in
@@ -170,6 +170,7 @@
 onanimationstart
 onanimationiteration
 onanimationend
+onauxclick
 onbeforecopy
 onbeforecut
 onbeforepaste
@@ -231,6 +232,14 @@
 onpause
 onplay
 onplaying
+onpointercancel
+onpointerdown
+onpointerenter
+onpointerleave
+onpointermove
+onpointerout
+onpointerover
+onpointerup
 onpopstate
 onprogress
 onratechange
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 142aeed..a3d9d23 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -152,7 +152,11 @@
     m_context->detachCanvas();
     m_context = nullptr;
   }
-  m_imageBuffer = nullptr;
+
+  if (m_imageBuffer) {
+    m_imageBuffer->setClient(nullptr);
+    m_imageBuffer = nullptr;
+  }
 }
 
 void HTMLCanvasElement::parseAttribute(const QualifiedName& name,
@@ -262,6 +266,15 @@
   if (m_context->is3d()) {
     updateExternallyAllocatedMemory();
   }
+
+  LayoutObject* layoutObject = this->layoutObject();
+  if (layoutObject && m_context->is2d() &&
+      !m_context->creationAttributes().alpha()) {
+    // In the alpha false case, canvas is initially opaque even though there is
+    // no ImageBuffer, so we need to trigger an invalidation.
+    didDraw(FloatRect(0, 0, size().width(), size().height()));
+  }
+
   setNeedsCompositingUpdate();
 
   return m_context.get();
@@ -338,7 +351,7 @@
   if (RuntimeEnabledFeatures::
           enableCanvas2dDynamicRenderingModeSwitchingEnabled() &&
       !RuntimeEnabledFeatures::canvas2dFixedRenderingModeEnabled()) {
-    if (m_context->is2d() && buffer() && buffer()->isAccelerated() &&
+    if (m_context->is2d() && hasImageBuffer() && buffer()->isAccelerated() &&
         m_numFramesSinceLastRenderingModeSwitch >=
             ExpensiveCanvasHeuristicParameters::MinFramesBeforeSwitch &&
         !m_pendingRenderingModeSwitch) {
@@ -383,10 +396,10 @@
   if (!m_context->is2d()) {
     didFinalizeFrame();
   } else {
-    DCHECK(hasImageBuffer());
     FloatRect srcRect(0, 0, size().width(), size().height());
     m_dirtyRect.intersect(srcRect);
     LayoutBox* lb = layoutBox();
+    FloatRect invalidationRect;
     if (lb) {
       FloatRect mappedDirtyRect =
           mapRect(m_dirtyRect, srcRect, FloatRect(lb->contentBoxRect()));
@@ -395,9 +408,14 @@
         // to the content box, as opposed to the layout box.
         mappedDirtyRect.move(-lb->contentBoxOffset());
       }
-      m_imageBuffer->finalizeFrame(mappedDirtyRect);
+      invalidationRect = mappedDirtyRect;
     } else {
-      m_imageBuffer->finalizeFrame(m_dirtyRect);
+      invalidationRect = m_dirtyRect;
+    }
+    if (hasImageBuffer()) {
+      m_imageBuffer->finalizeFrame(invalidationRect);
+    } else {
+      didFinalizeFrame();
     }
   }
   DCHECK(m_dirtyRect.isEmpty());
diff --git a/third_party/WebKit/Source/core/html/HTMLCollection.cpp b/third_party/WebKit/Source/core/html/HTMLCollection.cpp
index 1606c27..74f3b7f 100644
--- a/third_party/WebKit/Source/core/html/HTMLCollection.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCollection.cpp
@@ -512,13 +512,13 @@
 
   const NamedItemCache& cache = namedItemCache();
   if (HeapVector<Member<Element>>* idResults = cache.getElementsById(name)) {
-    for (unsigned i = 0; i < idResults->size(); ++i)
-      result.append(idResults->at(i));
+    for (const auto& element : *idResults)
+      result.append(element);
   }
   if (HeapVector<Member<Element>>* nameResults =
           cache.getElementsByName(name)) {
-    for (unsigned i = 0; i < nameResults->size(); ++i)
-      result.append(nameResults->at(i));
+    for (const auto& element : *nameResults)
+      result.append(element);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLCollection.h b/third_party/WebKit/Source/core/html/HTMLCollection.h
index 6c0f0c5..94a81ef3a 100644
--- a/third_party/WebKit/Source/core/html/HTMLCollection.h
+++ b/third_party/WebKit/Source/core/html/HTMLCollection.h
@@ -33,6 +33,38 @@
 
 namespace blink {
 
+// A simple iterator based on an index number in an HTMLCollection.
+// This doesn't work if the HTMLCollection is updated during iteration.
+template <class CollectionType, class NodeType>
+class HTMLCollectionIterator {
+  STACK_ALLOCATED();
+
+ public:
+  explicit HTMLCollectionIterator(const CollectionType* collection)
+      : m_collection(collection) {}
+  NodeType* operator*() { return m_collection->item(m_index); }
+
+  void operator++() {
+    if (m_index < m_collection->length())
+      ++m_index;
+  }
+
+  bool operator!=(const HTMLCollectionIterator& other) const {
+    return m_collection != other.m_collection || m_index != other.m_index;
+  }
+
+  static HTMLCollectionIterator createEnd(const CollectionType* collection) {
+    HTMLCollectionIterator iterator(collection);
+    iterator.m_index = collection->length();
+    return iterator;
+  }
+
+ private:
+  Member<const CollectionType> m_collection;
+  unsigned m_index = 0;
+};
+
+// blink::HTMLCollection implements HTMLCollection IDL interface.
 class CORE_EXPORT HTMLCollection
     : public GarbageCollectedFinalized<HTMLCollection>,
       public ScriptWrappable,
@@ -77,6 +109,10 @@
                                     Element& currentElement,
                                     unsigned& currentOffset) const;
 
+  using Iterator = HTMLCollectionIterator<HTMLCollection, Element>;
+  Iterator begin() const { return Iterator(this); }
+  Iterator end() const { return Iterator::createEnd(this); }
+
   DECLARE_VIRTUAL_TRACE();
 
  protected:
diff --git a/third_party/WebKit/Source/core/html/HTMLElement.cpp b/third_party/WebKit/Source/core/html/HTMLElement.cpp
index dc111ef..f0992219 100644
--- a/third_party/WebKit/Source/core/html/HTMLElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLElement.cpp
@@ -326,6 +326,7 @@
         {onanimationendAttr, EventTypeNames::animationend},
         {onanimationiterationAttr, EventTypeNames::animationiteration},
         {onanimationstartAttr, EventTypeNames::animationstart},
+        {onauxclickAttr, EventTypeNames::auxclick},
         {onbeforecopyAttr, EventTypeNames::beforecopy},
         {onbeforecutAttr, EventTypeNames::beforecut},
         {onbeforepasteAttr, EventTypeNames::beforepaste},
@@ -378,6 +379,14 @@
         {onpauseAttr, EventTypeNames::pause},
         {onplayAttr, EventTypeNames::play},
         {onplayingAttr, EventTypeNames::playing},
+        {onpointercancelAttr, EventTypeNames::pointercancel},
+        {onpointerdownAttr, EventTypeNames::pointerdown},
+        {onpointerenterAttr, EventTypeNames::pointerenter},
+        {onpointerleaveAttr, EventTypeNames::pointerleave},
+        {onpointermoveAttr, EventTypeNames::pointermove},
+        {onpointeroutAttr, EventTypeNames::pointerout},
+        {onpointeroverAttr, EventTypeNames::pointerover},
+        {onpointerupAttr, EventTypeNames::pointerup},
         {onprogressAttr, EventTypeNames::progress},
         {onratechangeAttr, EventTypeNames::ratechange},
         {onresetAttr, EventTypeNames::reset},
@@ -410,9 +419,8 @@
         {onwheelAttr, EventTypeNames::wheel},
     };
 
-    for (size_t i = 0; i < WTF_ARRAY_LENGTH(attrToEventNames); i++)
-      attributeNameToEventNameMap.set(attrToEventNames[i].attr.localName(),
-                                      attrToEventNames[i].event);
+    for (const auto& name : attrToEventNames)
+      attributeNameToEventNameMap.set(name.attr.localName(), name.event);
   }
 
   return attributeNameToEventNameMap.get(attrName.localName());
diff --git a/third_party/WebKit/Source/core/html/HTMLFieldSetElement.cpp b/third_party/WebKit/Source/core/html/HTMLFieldSetElement.cpp
index 6cfd1bb..00cf5d37 100644
--- a/third_party/WebKit/Source/core/html/HTMLFieldSetElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFieldSetElement.cpp
@@ -50,11 +50,10 @@
 }
 
 bool HTMLFieldSetElement::isValidElement() {
-  HTMLCollection* elements = this->elements();
-  for (unsigned i = 0; i < elements->length(); ++i) {
-    if (elements->item(i)->isFormControlElement()) {
-      if (!toHTMLFormControlElement(elements->item(i))
-               ->checkValidity(nullptr, CheckValidityDispatchNoEvent))
+  for (Element* element : *elements()) {
+    if (element->isFormControlElement()) {
+      if (!toHTMLFormControlElement(element)->checkValidity(
+              nullptr, CheckValidityDispatchNoEvent))
         return false;
     }
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
index 5f6d4e1..a80049c 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormControlElement.cpp
@@ -60,7 +60,6 @@
       m_validityIsDirty(false),
       m_wasFocusedByMouse(false) {
   setHasCustomStyleCallbacks();
-  associateByParser(form);
 }
 
 HTMLFormControlElement::~HTMLFormControlElement() {}
@@ -627,6 +626,8 @@
   setNeedsValidityCheck();
 }
 
-void HTMLFormControlElement::associateWith(HTMLFormElement*){};
+void HTMLFormControlElement::associateWith(HTMLFormElement* form) {
+  associateByParser(form);
+};
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLFormControlsCollection.cpp b/third_party/WebKit/Source/core/html/HTMLFormControlsCollection.cpp
index d9185f5..6801399 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormControlsCollection.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormControlsCollection.cpp
@@ -108,9 +108,9 @@
                                    const String& name) {
   DCHECK(attrName == idAttr || attrName == nameAttr);
 
-  for (unsigned i = 0; i < elementsArray.size(); ++i) {
-    HTMLElement* element = toHTMLElement(elementsArray[i]);
-    if (elementsArray[i]->isEnumeratable() &&
+  for (const auto& listedElement : elementsArray) {
+    HTMLElement* element = toHTMLElement(listedElement);
+    if (listedElement->isEnumeratable() &&
         element->fastGetAttribute(attrName) == name)
       return element;
   }
@@ -136,10 +136,7 @@
   NamedItemCache* cache = NamedItemCache::create();
   HashSet<StringImpl*> foundInputElements;
 
-  const ListedElement::List& elementsArray = listedElements();
-
-  for (unsigned i = 0; i < elementsArray.size(); ++i) {
-    ListedElement* listedElement = elementsArray[i];
+  for (const auto& listedElement : listedElements()) {
     if (listedElement->isEnumeratable()) {
       HTMLElement* element = toHTMLElement(listedElement);
       const AtomicString& idAttrVal = element->getIdAttribute();
@@ -158,10 +155,7 @@
   // HTMLFormControlsCollection doesn't support named getter for IMG
   // elements. However we still need to handle IMG elements here because
   // HTMLFormElement named getter relies on this.
-  const HeapVector<Member<HTMLImageElement>>& imageElementsArray =
-      formImageElements();
-  for (unsigned i = 0; i < imageElementsArray.size(); ++i) {
-    HTMLImageElement* element = imageElementsArray[i];
+  for (const auto& element : formImageElements()) {
     const AtomicString& idAttrVal = element->getIdAttribute();
     const AtomicString& nameAttrVal = element->getNameAttribute();
     if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl()))
diff --git a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
index ac4fd44..fcfa50f 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormElement.cpp
@@ -194,10 +194,9 @@
 }
 
 unsigned HTMLFormElement::length() const {
-  const ListedElement::List& elements = listedElements();
   unsigned len = 0;
-  for (unsigned i = 0; i < elements.size(); ++i) {
-    if (elements[i]->isEnumeratable())
+  for (const auto& element : listedElements()) {
+    if (element->isEnumeratable())
       ++len;
   }
   return len;
@@ -211,12 +210,10 @@
                                        bool fromImplicitSubmissionTrigger) {
   int submissionTriggerCount = 0;
   bool seenDefaultButton = false;
-  const ListedElement::List& elements = listedElements();
-  for (unsigned i = 0; i < elements.size(); ++i) {
-    ListedElement* ListedElement = elements[i];
-    if (!ListedElement->isFormControlElement())
+  for (const auto& element : listedElements()) {
+    if (!element->isFormControlElement())
       continue;
-    HTMLFormControlElement* control = toHTMLFormControlElement(ListedElement);
+    HTMLFormControlElement* control = toHTMLFormControlElement(element);
     if (!seenDefaultButton && control->canBeSuccessfulSubmitButton()) {
       if (fromImplicitSubmissionTrigger)
         seenDefaultButton = true;
@@ -238,10 +235,9 @@
 
 bool HTMLFormElement::validateInteractively() {
   UseCounter::count(document(), UseCounter::FormValidationStarted);
-  const ListedElement::List& elements = listedElements();
-  for (unsigned i = 0; i < elements.size(); ++i) {
-    if (elements[i]->isFormControlElement())
-      toHTMLFormControlElement(elements[i])->hideVisibleValidationMessage();
+  for (const auto& element : listedElements()) {
+    if (element->isFormControlElement())
+      toHTMLFormControlElement(element)->hideVisibleValidationMessage();
   }
 
   HeapVector<Member<HTMLFormControlElement>> unhandledInvalidControls;
@@ -257,8 +253,7 @@
   document().updateStyleAndLayoutIgnorePendingStylesheets();
 
   // Focus on the first focusable control and show a validation message.
-  for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
-    HTMLFormControlElement* unhandled = unhandledInvalidControls[i].get();
+  for (const auto& unhandled : unhandledInvalidControls) {
     if (unhandled->isFocusable()) {
       unhandled->showValidationMessage();
       UseCounter::count(document(), UseCounter::FormValidationShowedMessage);
@@ -267,8 +262,7 @@
   }
   // Warn about all of unfocusable controls.
   if (document().frame()) {
-    for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
-      HTMLFormControlElement* unhandled = unhandledInvalidControls[i].get();
+    for (const auto& unhandled : unhandledInvalidControls) {
       if (unhandled->isFocusable())
         continue;
       String message(
@@ -471,10 +465,9 @@
     return;
   }
 
-  const ListedElement::List& elements = listedElements();
-  for (unsigned i = 0; i < elements.size(); ++i) {
-    if (elements[i]->isFormControlElement())
-      toHTMLFormControlElement(elements[i])->reset();
+  for (const auto& element : listedElements()) {
+    if (element->isFormControlElement())
+      toHTMLFormControlElement(element)->reset();
   }
 
   m_isInResetFunction = false;
@@ -664,13 +657,12 @@
   const ListedElement::List& listedElements = this->listedElements();
   HeapVector<Member<ListedElement>> elements;
   elements.reserveCapacity(listedElements.size());
-  for (unsigned i = 0; i < listedElements.size(); ++i)
-    elements.append(listedElements[i]);
+  for (const auto& element : listedElements)
+    elements.append(element);
   int invalidControlsCount = 0;
-  for (unsigned i = 0; i < elements.size(); ++i) {
-    if (elements[i]->form() == this && elements[i]->isFormControlElement()) {
-      HTMLFormControlElement* control =
-          toHTMLFormControlElement(elements[i].get());
+  for (const auto& element : elements) {
+    if (element->form() == this && element->isFormControlElement()) {
+      HTMLFormControlElement* control = toHTMLFormControlElement(element);
       if (control->isSubmittableElement() &&
           !control->checkValidity(unhandledInvalidControls, eventBehavior) &&
           control->formOwner() == this) {
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
index e5c5aae..5a2fff14 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
@@ -54,9 +54,9 @@
 #include "core/page/Page.h"
 #include "core/style/ContentData.h"
 #include "core/svg/graphics/SVGImageForContainer.h"
-#include "platform/ContentType.h"
 #include "platform/EventDispatchForbiddenScope.h"
-#include "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/weborigin/SecurityPolicy.h"
 
 namespace blink {
@@ -94,18 +94,12 @@
       m_imageLoader(HTMLImageLoader::create(this)),
       m_imageDevicePixelRatio(1.0f),
       m_source(nullptr),
+      m_layoutDisposition(LayoutDisposition::PrimaryContent),
       m_formWasSetByParser(false),
       m_elementCreatedByParser(createdByParser),
-      m_useFallbackContent(false),
       m_isFallbackImage(false),
       m_referrerPolicy(ReferrerPolicyDefault) {
   setHasCustomStyleCallbacks();
-  if (form && form->isConnected()) {
-    m_form = form;
-    m_formWasSetByParser = true;
-    m_form->associate(*this);
-    m_form->didAssociateByParser();
-  }
 }
 
 HTMLImageElement* HTMLImageElement::create(Document& document) {
@@ -340,6 +334,11 @@
   return ImageCandidate();
 }
 
+bool HTMLImageElement::layoutObjectIsNeeded(const ComputedStyle& style) {
+  return m_layoutDisposition != LayoutDisposition::Collapsed &&
+         HTMLElement::layoutObjectIsNeeded(style);
+}
+
 LayoutObject* HTMLImageElement::createLayoutObject(const ComputedStyle& style) {
   const ContentData* contentData = style.contentData();
   if (contentData && contentData->isImage()) {
@@ -350,13 +349,20 @@
       return LayoutObject::createObject(this, style);
   }
 
-  if (m_useFallbackContent)
-    return new LayoutBlockFlow(this);
-
-  LayoutImage* image = new LayoutImage(this);
-  image->setImageResource(LayoutImageResource::create());
-  image->setImageDevicePixelRatio(m_imageDevicePixelRatio);
-  return image;
+  switch (m_layoutDisposition) {
+    case LayoutDisposition::FallbackContent:
+      return new LayoutBlockFlow(this);
+    case LayoutDisposition::PrimaryContent: {
+      LayoutImage* image = new LayoutImage(this);
+      image->setImageResource(LayoutImageResource::create());
+      image->setImageDevicePixelRatio(m_imageDevicePixelRatio);
+      return image;
+    }
+    case LayoutDisposition::Collapsed:  // Falls through.
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
 }
 
 void HTMLImageElement::attachLayoutTree(const AttachContext& context) {
@@ -771,6 +777,7 @@
         fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr), &document());
     setBestFitURLAndDPRFromImageCandidate(candidate);
   }
+
   imageLoader().updateFromElement(behavior, m_referrerPolicy);
 
   // Images such as data: uri's can return immediately and may already have
@@ -802,7 +809,7 @@
   if ((imageHasLoaded && imageHasImage) || imageStillLoading || imageIsDocument)
     ensurePrimaryContent();
   else
-    ensureFallbackContent();
+    ensureCollapsedOrFallbackContent();
 }
 
 const KURL& HTMLImageElement::sourceURL() const {
@@ -814,49 +821,62 @@
 }
 
 void HTMLImageElement::ensureFallbackForGeneratedContent() {
-  setUseFallbackContent();
-  reattachFallbackContent();
+  // The special casing for generated content in createLayoutObject breaks the
+  // invariant that the layout object attached to this element will always be
+  // appropriate for |m_layoutDisposition|. Force recreate it.
+  // TODO(engedy): Remove this hack. See: https://crbug.com/671953.
+  setLayoutDisposition(LayoutDisposition::FallbackContent,
+                       true /* forceReattach */);
 }
 
-void HTMLImageElement::ensureFallbackContent() {
-  if (m_useFallbackContent || m_isFallbackImage)
+void HTMLImageElement::ensureCollapsedOrFallbackContent() {
+  if (m_isFallbackImage)
     return;
-  setUseFallbackContent();
-  reattachFallbackContent();
+
+  bool resourceErrorIndicatesElementShouldBeCollapsed =
+      imageLoader().image() &&
+      imageLoader().image()->resourceError().shouldCollapseInitiator();
+  setLayoutDisposition(resourceErrorIndicatesElementShouldBeCollapsed
+                           ? LayoutDisposition::Collapsed
+                           : LayoutDisposition::FallbackContent);
 }
 
 void HTMLImageElement::ensurePrimaryContent() {
-  if (!m_useFallbackContent)
-    return;
-  m_useFallbackContent = false;
-  reattachFallbackContent();
+  setLayoutDisposition(LayoutDisposition::PrimaryContent);
 }
 
-void HTMLImageElement::reattachFallbackContent() {
+void HTMLImageElement::setLayoutDisposition(LayoutDisposition layoutDisposition,
+                                            bool forceReattach) {
+  if (m_layoutDisposition == layoutDisposition && !forceReattach)
+    return;
+
+  m_layoutDisposition = layoutDisposition;
+
   // This can happen inside of attachLayoutTree() in the middle of a recalcStyle
   // so we need to reattach synchronously here.
-  if (document().inStyleRecalc())
+  if (document().inStyleRecalc()) {
     reattachLayoutTree();
-  else
+  } else {
+    if (m_layoutDisposition == LayoutDisposition::FallbackContent) {
+      EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents;
+      ensureUserAgentShadowRoot();
+    }
     lazyReattachIfAttached();
+  }
 }
 
 PassRefPtr<ComputedStyle> HTMLImageElement::customStyleForLayoutObject() {
-  RefPtr<ComputedStyle> newStyle = originalStyleForLayoutObject();
-
-  if (!m_useFallbackContent)
-    return newStyle;
-
-  RefPtr<ComputedStyle> style = ComputedStyle::clone(*newStyle);
-  return HTMLImageFallbackHelper::customStyleForAltText(*this, style);
-}
-
-void HTMLImageElement::setUseFallbackContent() {
-  m_useFallbackContent = true;
-  if (document().inStyleRecalc())
-    return;
-  EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents;
-  ensureUserAgentShadowRoot();
+  switch (m_layoutDisposition) {
+    case LayoutDisposition::PrimaryContent:  // Fall through.
+    case LayoutDisposition::Collapsed:
+      return originalStyleForLayoutObject();
+    case LayoutDisposition::FallbackContent:
+      return HTMLImageFallbackHelper::customStyleForAltText(
+          *this, ComputedStyle::clone(*originalStyleForLayoutObject()));
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
 }
 
 bool HTMLImageElement::isOpaque() const {
@@ -892,6 +912,13 @@
   return IntSize(lSize.width().toInt(), lSize.height().toInt());
 }
 
-void HTMLImageElement::associateWith(HTMLFormElement*){};
+void HTMLImageElement::associateWith(HTMLFormElement* form) {
+  if (form && form->isConnected()) {
+    m_form = form;
+    m_formWasSetByParser = true;
+    m_form->associate(*this);
+    m_form->didAssociateByParser();
+  }
+};
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.h b/third_party/WebKit/Source/core/html/HTMLImageElement.h
index 884a8b3e..9ad59ca 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLImageElement.h
@@ -104,7 +104,7 @@
 
   HTMLFormElement* formOwner() const override;
   void formRemovedFromTree(const Node& formRoot);
-  virtual void ensureFallbackContent();
+  virtual void ensureCollapsedOrFallbackContent();
   virtual void ensureFallbackForGeneratedContent();
   virtual void ensurePrimaryContent();
 
@@ -125,8 +125,7 @@
 
   // public so that HTMLPictureElement can call this as well.
   void selectSourceURL(ImageLoader::UpdateFromElementBehavior);
-  void reattachFallbackContent();
-  void setUseFallbackContent();
+
   void setIsFallbackImage() { m_isFallbackImage = true; }
 
   FetchRequest::ResourceWidth getResourceWidth();
@@ -146,12 +145,27 @@
   void associateWith(HTMLFormElement*) override;
 
  protected:
+  // Controls how an image element appears in the layout. See:
+  // https://html.spec.whatwg.org/multipage/embedded-content.html#image-request
+  enum class LayoutDisposition : uint8_t {
+    // Displayed as a partially or completely loaded image. Corresponds to the
+    // `current request` state being: `unavailable`, `partially available`, or
+    // `completely available`.
+    PrimaryContent,
+    // Showing a broken image icon and 'alt' text, if any. Corresponds to the
+    // `current request` being in the `broken` state.
+    FallbackContent,
+    // No layout object. Corresponds to the `current request` being in the
+    // `broken` state when the resource load failed with an error that has the
+    // |shouldCollapseInitiator| flag set.
+    Collapsed
+  };
+
   explicit HTMLImageElement(Document&,
                             HTMLFormElement* = 0,
                             bool createdByParser = false);
 
   void didMoveToNewDocument(Document& oldDocument) override;
-  virtual bool useFallbackContent() const { return m_useFallbackContent; }
 
   void didAddUserAgentShadowRoot(ShadowRoot&) override;
   PassRefPtr<ComputedStyle> customStyleForLayoutObject() override;
@@ -166,8 +180,10 @@
   void collectStyleForPresentationAttribute(const QualifiedName&,
                                             const AtomicString&,
                                             MutableStylePropertySet*) override;
+  void setLayoutDisposition(LayoutDisposition, bool forceReattach = false);
 
   void attachLayoutTree(const AttachContext& = AttachContext()) override;
+  bool layoutObjectIsNeeded(const ComputedStyle&) override;
   LayoutObject* createLayoutObject(const ComputedStyle&) override;
 
   bool canStartSelection() const override { return false; }
@@ -198,9 +214,9 @@
   AtomicString m_bestFitImageURL;
   float m_imageDevicePixelRatio;
   Member<HTMLSourceElement> m_source;
+  LayoutDisposition m_layoutDisposition;
   unsigned m_formWasSetByParser : 1;
   unsigned m_elementCreatedByParser : 1;
-  unsigned m_useFallbackContent : 1;
   unsigned m_isFallbackImage : 1;
 
   ReferrerPolicy m_referrerPolicy;
diff --git a/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp b/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
index 1c33485e..ec64186 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
@@ -56,18 +56,16 @@
                                                        : EventTypeNames::load));
 }
 
-static void loadFallbackContentForElement(Element* element) {
-  if (isHTMLImageElement(element))
-    toHTMLImageElement(element)->ensureFallbackContent();
-  else if (isHTMLInputElement(element))
-    toHTMLInputElement(element)->ensureFallbackContent();
-}
-
 void HTMLImageLoader::noImageResourceToLoad() {
   // FIXME: Use fallback content even when there is no alt-text. The only
   // blocker is the large amount of rebaselining it requires.
-  if (!toHTMLElement(element())->altText().isEmpty())
-    loadFallbackContentForElement(element());
+  if (toHTMLElement(element())->altText().isEmpty())
+    return;
+
+  if (isHTMLImageElement(element()))
+    toHTMLImageElement(element())->ensureCollapsedOrFallbackContent();
+  else if (isHTMLInputElement(element()))
+    toHTMLInputElement(element())->ensureFallbackContent();
 }
 
 void HTMLImageLoader::imageNotifyFinished(ImageResource*) {
@@ -78,14 +76,14 @@
   bool loadError = cachedImage->errorOccurred();
   if (isHTMLImageElement(*element)) {
     if (loadError)
-      ensureFallbackContent();
+      toHTMLImageElement(element)->ensureCollapsedOrFallbackContent();
     else
       toHTMLImageElement(element)->ensurePrimaryContent();
   }
 
   if (isHTMLInputElement(*element)) {
     if (loadError)
-      ensureFallbackContent();
+      toHTMLInputElement(element)->ensureFallbackContent();
     else
       toHTMLInputElement(element)->ensurePrimaryContent();
   }
@@ -95,8 +93,4 @@
     toHTMLObjectElement(element)->renderFallbackContent();
 }
 
-void HTMLImageLoader::ensureFallbackContent() {
-  loadFallbackContentForElement(element());
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLImageLoader.h b/third_party/WebKit/Source/core/html/HTMLImageLoader.h
index 7f886c84..31ca3f3 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageLoader.h
+++ b/third_party/WebKit/Source/core/html/HTMLImageLoader.h
@@ -43,7 +43,6 @@
  private:
   explicit HTMLImageLoader(Element*);
   void noImageResourceToLoad() override;
-  void ensureFallbackContent();
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
index dba5ef4..1d1fc3b 100644
--- a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
@@ -1476,13 +1476,15 @@
 }
 
 void HTMLInputElement::willChangeForm() {
-  removeFromRadioButtonGroup();
+  if (m_inputType)
+    removeFromRadioButtonGroup();
   TextControlElement::willChangeForm();
 }
 
 void HTMLInputElement::didChangeForm() {
   TextControlElement::didChangeForm();
-  addToRadioButtonGroup();
+  if (m_inputType)
+    addToRadioButtonGroup();
 }
 
 Node::InsertionNotificationRequest HTMLInputElement::insertedInto(
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
index 1da81e76..394dc765 100644
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
@@ -22,64 +22,444 @@
 
 #include "core/html/HTMLMarqueeElement.h"
 
-#include "bindings/core/v8/PrivateScriptRunner.h"
+#include "bindings/core/v8/ExceptionStatePlaceholder.h"
 #include "bindings/core/v8/V8HTMLMarqueeElement.h"
+#include "core/CSSPropertyNames.h"
 #include "core/HTMLNames.h"
+#include "core/animation/DocumentTimeline.h"
+#include "core/animation/KeyframeEffect.h"
+#include "core/animation/KeyframeEffectModel.h"
+#include "core/animation/KeyframeEffectOptions.h"
+#include "core/animation/StringKeyframe.h"
+#include "core/animation/TimingInput.h"
+#include "core/css/CSSStyleDeclaration.h"
+#include "core/css/StylePropertySet.h"
 #include "core/dom/Document.h"
+#include "core/dom/shadow/ShadowRoot.h"
+#include "core/frame/LocalDOMWindow.h"
 #include "core/frame/UseCounter.h"
-#include "platform/ScriptForbiddenScope.h"
+#include "core/html/HTMLContentElement.h"
+#include "core/html/HTMLDivElement.h"
+#include "core/html/HTMLStyleElement.h"
+#include <cstdlib>
 
 namespace blink {
 
 inline HTMLMarqueeElement::HTMLMarqueeElement(Document& document)
     : HTMLElement(HTMLNames::marqueeTag, document) {
-  if (document.contextDocument()) {
-    ScriptForbiddenScope::AllowUserAgentScript script;
-    v8::Local<v8::Value> classObject =
-        PrivateScriptRunner::installClassIfNeeded(&document,
-                                                  "HTMLMarqueeElement");
-    RELEASE_ASSERT(!classObject.IsEmpty());
-  }
   UseCounter::count(document, UseCounter::HTMLMarqueeElement);
 }
 
 HTMLMarqueeElement* HTMLMarqueeElement::create(Document& document) {
   HTMLMarqueeElement* marqueeElement = new HTMLMarqueeElement(document);
-  V8HTMLMarqueeElement::PrivateScript::createdCallbackMethod(document.frame(),
-                                                             marqueeElement);
+  marqueeElement->ensureUserAgentShadowRoot();
   return marqueeElement;
 }
 
-void HTMLMarqueeElement::attributeChanged(const QualifiedName& name,
-                                          const AtomicString& oldValue,
-                                          const AtomicString& newValue,
-                                          AttributeModificationReason reason) {
-  HTMLElement::attributeChanged(name, oldValue, newValue, reason);
-  V8HTMLMarqueeElement::PrivateScript::attributeChangedCallbackMethod(
-      document().frame(), this, name.toString(), oldValue, newValue);
+void HTMLMarqueeElement::didAddUserAgentShadowRoot(ShadowRoot& shadowRoot) {
+  Element* style = HTMLStyleElement::create(document(), false);
+  style->setTextContent(
+      ":host { display: inline-block; overflow: hidden;"
+      "text-align: initial; white-space: nowrap; }"
+      ":host([direction=\"up\"]), :host([direction=\"down\"]) { overflow: "
+      "initial; overflow-y: hidden; white-space: initial; }"
+      ":host > div { will-change: transform; }");
+  shadowRoot.appendChild(style);
+
+  Element* mover = HTMLDivElement::create(document());
+  shadowRoot.appendChild(mover);
+
+  mover->appendChild(HTMLContentElement::create(document()));
+  m_mover = mover;
 }
 
 Node::InsertionNotificationRequest HTMLMarqueeElement::insertedInto(
     ContainerNode* insertionPoint) {
   HTMLElement::insertedInto(insertionPoint);
-  if (isConnected()) {
-    V8HTMLMarqueeElement::PrivateScript::attachedCallbackMethod(
-        document().frame(), this);
-  }
+
+  if (isConnected())
+    start();
+
   return InsertionDone;
 }
 
 void HTMLMarqueeElement::removedFrom(ContainerNode* insertionPoint) {
   HTMLElement::removedFrom(insertionPoint);
   if (insertionPoint->isConnected()) {
-    V8HTMLMarqueeElement::PrivateScript::detachedCallbackMethod(
-        insertionPoint->document().frame(), this);
+    stop();
   }
 }
 
 bool HTMLMarqueeElement::isHorizontal() const {
-  AtomicString direction = getAttribute(HTMLNames::directionAttr);
-  return direction != "down" && direction != "up";
+  Direction direction = this->direction();
+  return direction != Up && direction != Down;
+}
+
+int HTMLMarqueeElement::scrollAmount() const {
+  bool ok;
+  int scrollAmount = getAttribute(HTMLNames::scrollamountAttr).toInt(&ok);
+  if (!ok || scrollAmount < 0)
+    return kDefaultScrollAmount;
+  return scrollAmount;
+}
+
+void HTMLMarqueeElement::setScrollAmount(int value,
+                                         ExceptionState& exceptionState) {
+  if (value < 0) {
+    exceptionState.throwDOMException(
+        IndexSizeError,
+        "The provided value (" + String::number(value) + ") is negative.");
+    return;
+  }
+  setIntegralAttribute(HTMLNames::scrollamountAttr, value);
+}
+
+int HTMLMarqueeElement::scrollDelay() const {
+  bool ok;
+  int scrollDelay = getAttribute(HTMLNames::scrolldelayAttr).toInt(&ok);
+  if (!ok || scrollDelay < 0)
+    return kDefaultScrollDelayMS;
+  return scrollDelay;
+}
+
+void HTMLMarqueeElement::setScrollDelay(int value,
+                                        ExceptionState& exceptionState) {
+  if (value < 0) {
+    exceptionState.throwDOMException(
+        IndexSizeError,
+        "The provided value (" + String::number(value) + ") is negative.");
+    return;
+  }
+  setIntegralAttribute(HTMLNames::scrolldelayAttr, value);
+}
+
+int HTMLMarqueeElement::loop() const {
+  bool ok;
+  int loop = getAttribute(HTMLNames::loopAttr).toInt(&ok);
+  if (!ok || loop <= 0)
+    return kDefaultLoopLimit;
+  return loop;
+}
+
+void HTMLMarqueeElement::setLoop(int value, ExceptionState& exceptionState) {
+  if (value <= 0 && value != -1) {
+    exceptionState.throwDOMException(
+        IndexSizeError, "The provided value (" + String::number(value) +
+                            ") is neither positive nor -1.");
+    return;
+  }
+  setIntegralAttribute(HTMLNames::loopAttr, value);
+}
+
+void HTMLMarqueeElement::start() {
+  if (m_continueCallbackRequestId)
+    return;
+
+  RequestAnimationFrameCallback* callback =
+      new RequestAnimationFrameCallback(this);
+  m_continueCallbackRequestId = document().requestAnimationFrame(callback);
+}
+
+void HTMLMarqueeElement::stop() {
+  if (m_continueCallbackRequestId) {
+    document().cancelAnimationFrame(m_continueCallbackRequestId);
+    m_continueCallbackRequestId = 0;
+    return;
+  }
+
+  if (m_player)
+    m_player->pause();
+}
+
+bool HTMLMarqueeElement::isPresentationAttribute(
+    const QualifiedName& attr) const {
+  if (attr == HTMLNames::bgcolorAttr || attr == HTMLNames::heightAttr ||
+      attr == HTMLNames::hspaceAttr || attr == HTMLNames::vspaceAttr ||
+      attr == HTMLNames::widthAttr) {
+    return true;
+  }
+  return HTMLElement::isPresentationAttribute(attr);
+}
+
+void HTMLMarqueeElement::collectStyleForPresentationAttribute(
+    const QualifiedName& attr,
+    const AtomicString& value,
+    MutableStylePropertySet* style) {
+  if (attr == HTMLNames::bgcolorAttr) {
+    addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value);
+  } else if (attr == HTMLNames::heightAttr) {
+    addHTMLLengthToStyle(style, CSSPropertyHeight, value);
+  } else if (attr == HTMLNames::hspaceAttr) {
+    addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
+    addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
+  } else if (attr == HTMLNames::vspaceAttr) {
+    addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
+    addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
+  } else if (attr == HTMLNames::widthAttr) {
+    addHTMLLengthToStyle(style, CSSPropertyWidth, value);
+  } else {
+    HTMLElement::collectStyleForPresentationAttribute(attr, value, style);
+  }
+}
+
+void HTMLMarqueeElement::RequestAnimationFrameCallback::handleEvent(double) {
+  m_marquee->m_continueCallbackRequestId = 0;
+  m_marquee->continueAnimation();
+}
+
+void HTMLMarqueeElement::AnimationFinished::handleEvent(
+    ExecutionContext* context,
+    Event* event) {
+  ++m_marquee->m_loopCount;
+  m_marquee->start();
+}
+
+StringKeyframeEffectModel* HTMLMarqueeElement::createEffectModel(
+    AnimationParameters& parameters) {
+  StyleSheetContents* styleSheetContents =
+      m_mover->document().elementSheet().contents();
+  MutableStylePropertySet::SetResult setResult;
+
+  RefPtr<StringKeyframe> keyframe1 = StringKeyframe::create();
+  setResult = keyframe1->setCSSPropertyValue(
+      CSSPropertyTransform, parameters.transformBegin, styleSheetContents);
+  DCHECK(setResult.didParse);
+
+  RefPtr<StringKeyframe> keyframe2 = StringKeyframe::create();
+  setResult = keyframe2->setCSSPropertyValue(
+      CSSPropertyTransform, parameters.transformEnd, styleSheetContents);
+  DCHECK(setResult.didParse);
+
+  return StringKeyframeEffectModel::create(
+      {std::move(keyframe1), std::move(keyframe2)},
+      LinearTimingFunction::shared());
+}
+
+void HTMLMarqueeElement::continueAnimation() {
+  if (!shouldContinue())
+    return;
+
+  if (m_player && m_player->playState() == "paused") {
+    m_player->play();
+    return;
+  }
+
+  AnimationParameters parameters = getAnimationParameters();
+  int scrollDelay = this->scrollDelay();
+  int scrollAmount = this->scrollAmount();
+
+  if (scrollDelay < kMinimumScrollDelayMS && !trueSpeed())
+    scrollDelay = kDefaultScrollDelayMS;
+  double duration = 0;
+  if (scrollAmount)
+    duration = parameters.distance * scrollDelay / scrollAmount;
+  if (!duration)
+    return;
+
+  StringKeyframeEffectModel* effectModel = createEffectModel(parameters);
+  Timing timing;
+  timing.fillMode = Timing::FillMode::FORWARDS;
+  TimingInput::setIterationDuration(
+      timing, UnrestrictedDoubleOrString::fromUnrestrictedDouble(duration),
+      ASSERT_NO_EXCEPTION);
+
+  KeyframeEffect* keyframeEffect =
+      KeyframeEffect::create(m_mover, effectModel, timing);
+  Animation* player = m_mover->document().timeline().play(keyframeEffect);
+  player->setId(emptyString());
+  player->setOnfinish(new AnimationFinished(this));
+
+  m_player = player;
+}
+
+bool HTMLMarqueeElement::shouldContinue() {
+  int loopCount = loop();
+
+  // By default, slide loops only once.
+  if (loopCount <= 0 && behavior() == Slide)
+    loopCount = 1;
+
+  if (loopCount <= 0)
+    return true;
+  return m_loopCount < loopCount;
+}
+
+HTMLMarqueeElement::Behavior HTMLMarqueeElement::behavior() const {
+  const AtomicString& behavior = getAttribute(HTMLNames::behaviorAttr);
+  if (behavior == "alternate")
+    return Alternate;
+  if (behavior == "slide")
+    return Slide;
+  return Scroll;
+}
+
+HTMLMarqueeElement::Direction HTMLMarqueeElement::direction() const {
+  const AtomicString& direction = getAttribute(HTMLNames::directionAttr);
+  if (direction == "down")
+    return Down;
+  if (direction == "up")
+    return Up;
+  if (direction == "right")
+    return Right;
+  return Left;
+}
+
+bool HTMLMarqueeElement::trueSpeed() const {
+  return hasAttribute(HTMLNames::truespeedAttr);
+}
+
+HTMLMarqueeElement::Metrics HTMLMarqueeElement::getMetrics() {
+  Metrics metrics;
+  CSSStyleDeclaration* marqueeStyle =
+      document().domWindow()->getComputedStyle(this, String());
+  // For marquees that are declared inline, getComputedStyle returns "auto" for
+  // width and height. Setting all the metrics to zero disables animation for
+  // inline marquees.
+  if (marqueeStyle->getPropertyValue("width") == "auto" &&
+      marqueeStyle->getPropertyValue("height") == "auto") {
+    metrics.contentHeight = 0;
+    metrics.contentWidth = 0;
+    metrics.marqueeWidth = 0;
+    metrics.marqueeHeight = 0;
+    return metrics;
+  }
+
+  if (isHorizontal()) {
+    m_mover->style()->setProperty("width", "-webkit-max-content", "important",
+                                  ASSERT_NO_EXCEPTION);
+  } else {
+    m_mover->style()->setProperty("height", "-webkit-max-content", "important",
+                                  ASSERT_NO_EXCEPTION);
+  }
+  CSSStyleDeclaration* moverStyle =
+      document().domWindow()->getComputedStyle(m_mover, String());
+
+  metrics.contentWidth = moverStyle->getPropertyValue("width").toDouble();
+  metrics.contentHeight = moverStyle->getPropertyValue("height").toDouble();
+  metrics.marqueeWidth = marqueeStyle->getPropertyValue("width").toDouble();
+  metrics.marqueeHeight = marqueeStyle->getPropertyValue("height").toDouble();
+
+  if (isHorizontal()) {
+    m_mover->style()->setProperty("width", "", "important",
+                                  ASSERT_NO_EXCEPTION);
+  } else {
+    m_mover->style()->setProperty("height", "", "important",
+                                  ASSERT_NO_EXCEPTION);
+  }
+
+  return metrics;
+}
+
+HTMLMarqueeElement::AnimationParameters
+HTMLMarqueeElement::getAnimationParameters() {
+  AnimationParameters parameters;
+  Metrics metrics = getMetrics();
+
+  double totalWidth = metrics.marqueeWidth + metrics.contentWidth;
+  double totalHeight = metrics.marqueeHeight + metrics.contentHeight;
+
+  double innerWidth = metrics.marqueeWidth - metrics.contentWidth;
+  double innerHeight = metrics.marqueeHeight - metrics.contentHeight;
+
+  switch (behavior()) {
+    case Alternate:
+      switch (direction()) {
+        case Right:
+          parameters.transformBegin =
+              createTransform(innerWidth >= 0 ? 0 : innerWidth);
+          parameters.transformEnd =
+              createTransform(innerWidth >= 0 ? innerWidth : 0);
+          parameters.distance = std::abs(innerWidth);
+          break;
+        case Up:
+          parameters.transformBegin =
+              createTransform(innerHeight >= 0 ? innerHeight : 0);
+          parameters.transformEnd =
+              createTransform(innerHeight >= 0 ? 0 : innerHeight);
+          parameters.distance = std::abs(innerHeight);
+          break;
+        case Down:
+          parameters.transformBegin =
+              createTransform(innerHeight >= 0 ? 0 : innerHeight);
+          parameters.transformEnd =
+              createTransform(innerHeight >= 0 ? innerHeight : 0);
+          parameters.distance = std::abs(innerHeight);
+          break;
+        case Left:
+        default:
+          parameters.transformBegin =
+              createTransform(innerWidth >= 0 ? innerWidth : 0);
+          parameters.transformEnd =
+              createTransform(innerWidth >= 0 ? 0 : innerWidth);
+          parameters.distance = std::abs(innerWidth);
+      }
+
+      if (m_loopCount % 2)
+        std::swap(parameters.transformBegin, parameters.transformEnd);
+      break;
+    case Slide:
+      switch (direction()) {
+        case Right:
+          parameters.transformBegin = createTransform(-metrics.contentWidth);
+          parameters.transformEnd = createTransform(innerWidth);
+          parameters.distance = metrics.marqueeWidth;
+          break;
+        case Up:
+          parameters.transformBegin = createTransform(metrics.marqueeHeight);
+          parameters.transformEnd = "translateY(0)";
+          parameters.distance = metrics.marqueeHeight;
+          break;
+        case Down:
+          parameters.transformBegin = createTransform(-metrics.contentHeight);
+          parameters.transformEnd = createTransform(innerHeight);
+          parameters.distance = metrics.marqueeHeight;
+          break;
+        case Left:
+        default:
+          parameters.transformBegin = createTransform(metrics.marqueeWidth);
+          parameters.transformEnd = "translateX(0)";
+          parameters.distance = metrics.marqueeWidth;
+      }
+      break;
+    case Scroll:
+    default:
+      switch (direction()) {
+        case Right:
+          parameters.transformBegin = createTransform(-metrics.contentWidth);
+          parameters.transformEnd = createTransform(metrics.marqueeWidth);
+          parameters.distance = totalWidth;
+          break;
+        case Up:
+          parameters.transformBegin = createTransform(metrics.marqueeHeight);
+          parameters.transformEnd = createTransform(-metrics.contentHeight);
+          parameters.distance = totalHeight;
+          break;
+        case Down:
+          parameters.transformBegin = createTransform(-metrics.contentHeight);
+          parameters.transformEnd = createTransform(metrics.marqueeHeight);
+          parameters.distance = totalHeight;
+          break;
+        case Left:
+        default:
+          parameters.transformBegin = createTransform(metrics.marqueeWidth);
+          parameters.transformEnd = createTransform(-metrics.contentWidth);
+          parameters.distance = totalWidth;
+      }
+      break;
+  }
+
+  return parameters;
+}
+
+AtomicString HTMLMarqueeElement::createTransform(double value) const {
+  char axis = isHorizontal() ? 'X' : 'Y';
+  return AtomicString(String::format("translate%c(%fpx)", axis, value));
+}
+
+DEFINE_TRACE(HTMLMarqueeElement) {
+  visitor->trace(m_mover);
+  visitor->trace(m_player);
+  HTMLElement::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.h b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.h
index 45a4af5..a423e73 100644
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.h
@@ -23,7 +23,11 @@
 #ifndef HTMLMarqueeElement_h
 #define HTMLMarqueeElement_h
 
+#include "core/animation/Animation.h"
+#include "core/animation/KeyframeEffectModel.h"
+#include "core/dom/FrameRequestCallback.h"
 #include "core/html/HTMLElement.h"
+#include "wtf/Noncopyable.h"
 
 namespace blink {
 
@@ -31,19 +35,114 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  DECLARE_VIRTUAL_TRACE();
+
   static HTMLMarqueeElement* create(Document&);
 
-  void attributeChanged(const QualifiedName&,
-                        const AtomicString& oldValue,
-                        const AtomicString& newValue,
-                        AttributeModificationReason) final;
   InsertionNotificationRequest insertedInto(ContainerNode*) final;
   void removedFrom(ContainerNode*) final;
 
   bool isHorizontal() const;
 
+  int scrollAmount() const;
+  void setScrollAmount(int, ExceptionState&);
+
+  int scrollDelay() const;
+  void setScrollDelay(int, ExceptionState&);
+
+  int loop() const;
+  void setLoop(int, ExceptionState&);
+
+  void start();
+  void stop();
+
  private:
   explicit HTMLMarqueeElement(Document&);
+  void didAddUserAgentShadowRoot(ShadowRoot&) override;
+
+  bool isPresentationAttribute(const QualifiedName&) const override;
+  void collectStyleForPresentationAttribute(const QualifiedName&,
+                                            const AtomicString&,
+                                            MutableStylePropertySet*) override;
+
+  class RequestAnimationFrameCallback final : public FrameRequestCallback {
+    WTF_MAKE_NONCOPYABLE(RequestAnimationFrameCallback);
+
+   public:
+    explicit RequestAnimationFrameCallback(HTMLMarqueeElement* marquee)
+        : m_marquee(marquee) {}
+    void handleEvent(double) override;
+
+    DEFINE_INLINE_VIRTUAL_TRACE() {
+      visitor->trace(m_marquee);
+      FrameRequestCallback::trace(visitor);
+    }
+
+   private:
+    Member<HTMLMarqueeElement> m_marquee;
+  };
+
+  class AnimationFinished final : public EventListener {
+    WTF_MAKE_NONCOPYABLE(AnimationFinished);
+
+   public:
+    explicit AnimationFinished(HTMLMarqueeElement* marquee)
+        : EventListener(CPPEventListenerType), m_marquee(marquee) {}
+
+    bool operator==(const EventListener& that) const override {
+      return this == &that;
+    }
+
+    void handleEvent(ExecutionContext*, Event*) override;
+
+    DEFINE_INLINE_VIRTUAL_TRACE() {
+      visitor->trace(m_marquee);
+      EventListener::trace(visitor);
+    }
+
+   private:
+    Member<HTMLMarqueeElement> m_marquee;
+  };
+
+  struct AnimationParameters {
+    String transformBegin;
+    String transformEnd;
+    double distance;
+  };
+
+  struct Metrics {
+    double contentWidth;
+    double contentHeight;
+    double marqueeWidth;
+    double marqueeHeight;
+  };
+
+  StringKeyframeEffectModel* createEffectModel(AnimationParameters&);
+
+  void continueAnimation();
+  bool shouldContinue();
+
+  enum Behavior { Scroll, Slide, Alternate };
+  Behavior behavior() const;
+
+  enum Direction { Left, Right, Up, Down };
+  Direction direction() const;
+
+  bool trueSpeed() const;
+
+  Metrics getMetrics();
+  AnimationParameters getAnimationParameters();
+  AtomicString createTransform(double value) const;
+
+  static const int kDefaultScrollAmount = 6;
+  static const int kDefaultScrollDelayMS = 85;
+  static const int kMinimumScrollDelayMS = 60;
+  static const int kDefaultLoopLimit = -1;
+
+  int m_continueCallbackRequestId = 0;
+  int m_loopCount = 0;
+  Member<Element> m_mover;
+  Member<Animation> m_player;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.idl b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.idl
index 4103315..6a95e65 100644
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.idl
+++ b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.idl
@@ -20,18 +20,18 @@
 // https://html.spec.whatwg.org/#htmlmarqueeelement
 
 interface HTMLMarqueeElement : HTMLElement {
-    [ImplementedInPrivateScript, Reflect] attribute DOMString behavior;
-    [ImplementedInPrivateScript, Reflect] attribute DOMString bgColor;
-    [ImplementedInPrivateScript, Reflect] attribute DOMString direction;
-    [ImplementedInPrivateScript, Reflect] attribute DOMString height;
-    [ImplementedInPrivateScript, Reflect] attribute unsigned long hspace;
-    [ImplementedInPrivateScript] attribute long loop;
+    [Reflect] attribute DOMString behavior;
+    [Reflect] attribute DOMString bgColor;
+    [Reflect] attribute DOMString direction;
+    [Reflect] attribute DOMString height;
+    [Reflect] attribute unsigned long hspace;
+    [RaisesException=Setter] attribute long loop;
     // FIXME: scrollAmount and scrollDelay should be unsigned long.
-    [ImplementedInPrivateScript] attribute long scrollAmount;
-    [ImplementedInPrivateScript] attribute long scrollDelay;
-    [ImplementedInPrivateScript, Reflect] attribute boolean trueSpeed;
-    [ImplementedInPrivateScript, Reflect] attribute unsigned long vspace;
-    [ImplementedInPrivateScript, Reflect] attribute DOMString width;
+    [RaisesException=Setter] attribute long scrollAmount;
+    [RaisesException=Setter] attribute long scrollDelay;
+    [Reflect] attribute boolean trueSpeed;
+    [Reflect] attribute unsigned long vspace;
+    [Reflect] attribute DOMString width;
 
     // FIXME: Implement the following event handler attributes
     // https://bugs.webkit.org/show_bug.cgi?id=49788
@@ -39,12 +39,6 @@
     // attribute EventHandler onfinish;
     // attribute EventHandler onstart;
 
-    [ImplementedInPrivateScript] void start();
-    [ImplementedInPrivateScript] void stop();
-
-    // Private script APIs
-    [ImplementedInPrivateScript, OnlyExposedToPrivateScript] void createdCallback();
-    [ImplementedInPrivateScript, OnlyExposedToPrivateScript] void attachedCallback();
-    [ImplementedInPrivateScript, OnlyExposedToPrivateScript] void detachedCallback();
-    [ImplementedInPrivateScript, OnlyExposedToPrivateScript] void attributeChangedCallback(DOMString name, DOMString oldValue, DOMString newValue);
+    void start();
+    void stop();
 };
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.js b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.js
deleted file mode 100644
index fbaff76..0000000
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.js
+++ /dev/null
@@ -1,402 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-privateScriptController.installClass('HTMLMarqueeElement', function(HTMLMarqueeElementPrototype) {
-
-    var kDefaultScrollAmount = 6;
-    var kDefaultScrollDelayMS = 85;
-    var kMinimumScrollDelayMS = 60;
-
-    var kDefaultLoopLimit = -1;
-
-    var kBehaviorScroll = 'scroll';
-    var kBehaviorSlide = 'slide';
-    var kBehaviorAlternate = 'alternate';
-
-    var kDirectionLeft = 'left';
-    var kDirectionRight = 'right';
-    var kDirectionUp = 'up';
-    var kDirectionDown = 'down';
-
-    var kPresentationalAttributes = [
-        'bgcolor',
-        'height',
-        'hspace',
-        'vspace',
-        'width',
-    ];
-
-    function convertHTMLLengthToCSSLength(value) {
-        var match = value.match(/^\s*([\d.]+)(%)?\s*/);
-        if (match)
-            return match[2] === '%' ? match[1] + '%' : match[1] + 'px';
-        return null;
-    }
-
-    // FIXME: Consider moving these utility functions to PrivateScriptRunner.js.
-    var kInt32Max = Math.pow(2, 31);
-
-    function convertToLong(n) {
-        // Using parseInt() is wrong but this aligns with the existing behavior of StringImpl::toInt().
-        // FIXME: Implement a correct algorithm of the Web IDL value conversion.
-        var value = parseInt(n);
-        if (!isNaN(value) && -kInt32Max <= value && value < kInt32Max)
-            return value;
-        return NaN;
-    }
-
-    function reflectAttribute(prototype, attributeName, propertyName) {
-        Object.defineProperty(prototype, propertyName, {
-            get: function() {
-                return this.getAttribute(attributeName) || '';
-            },
-            set: function(value) {
-                this.setAttribute(attributeName, value);
-            },
-            configurable: true,
-            enumerable: true,
-        });
-    }
-
-    function reflectBooleanAttribute(prototype, attributeName, propertyName) {
-        Object.defineProperty(prototype, propertyName, {
-            get: function() {
-                return this.getAttribute(attributeName);
-            },
-            set: function(value) {
-                if (value)
-                    this.setAttribute(attributeName, '');
-                else
-                    this.removeAttribute(attributeName);
-            },
-        });
-    }
-
-    reflectAttribute(HTMLMarqueeElementPrototype, 'behavior', 'behavior');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'bgcolor', 'bgColor');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'direction', 'direction');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'height', 'height');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'hspace', 'hspace');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'vspace', 'vspace');
-    reflectAttribute(HTMLMarqueeElementPrototype, 'width', 'width');
-    reflectBooleanAttribute(HTMLMarqueeElementPrototype, 'truespeed', 'trueSpeed');
-
-    HTMLMarqueeElementPrototype.createdCallback = function() {
-        var shadow = this.createShadowRoot();
-        var style = document.createElement('style');
-        style.textContent = ':host { display: inline-block; width: -webkit-fill-available; overflow: hidden; text-align: initial; white-space: nowrap; }'
-            + ':host([direction="up"]), :host([direction="down"]) { overflow: initial; overflow-y: hidden; white-space: initial; }';
-        shadow.appendChild(style);
-
-        var mover = document.createElement('div');
-        shadow.appendChild(mover);
-
-        mover.appendChild(document.createElement('content'));
-
-        this.loopCount_ = 0;
-        this.mover_ = mover;
-        this.player_ = null;
-        this.continueCallback_ = null;
-    };
-
-    HTMLMarqueeElementPrototype.attachedCallback = function() {
-        for (var i = 0; i < kPresentationalAttributes.length; ++i) {
-            this.initializeAttribute_(kPresentationalAttributes[i]);
-        }
-
-        this.start();
-    };
-
-    HTMLMarqueeElementPrototype.detachedCallback = function() {
-        this.stop();
-    };
-
-    HTMLMarqueeElementPrototype.attributeChangedCallback = function(name, oldValue, newValue) {
-        switch (name) {
-        case 'bgcolor':
-            this.style.backgroundColor = newValue;
-            break;
-        case 'height':
-            this.style.height = convertHTMLLengthToCSSLength(newValue);
-            break;
-        case 'hspace':
-            var margin = convertHTMLLengthToCSSLength(newValue);
-            this.style.marginLeft = margin;
-            this.style.marginRight = margin;
-            break;
-        case 'vspace':
-            var margin = convertHTMLLengthToCSSLength(newValue);
-            this.style.marginTop = margin;
-            this.style.marginBottom = margin;
-            break;
-        case 'width':
-            this.style.width = convertHTMLLengthToCSSLength(newValue);
-            break;
-        case 'behavior':
-        case 'direction':
-        case 'loop':
-        case 'scrollAmount':
-        case 'scrollDelay':
-        case 'trueSpeed':
-            // FIXME: Not implemented.
-            break;
-        }
-    };
-
-    HTMLMarqueeElementPrototype.initializeAttribute_ = function(name) {
-        var value = this.getAttribute(name);
-        if (value === null)
-            return;
-        this.attributeChangedCallback(name, null, value);
-    };
-
-    Object.defineProperty(HTMLMarqueeElementPrototype, 'scrollAmount', {
-        get: function() {
-            var value = this.getAttribute('scrollamount');
-            var scrollAmount = convertToLong(value);
-            if (isNaN(scrollAmount) || scrollAmount < 0)
-                return kDefaultScrollAmount;
-            return scrollAmount;
-        },
-        set: function(value) {
-            if (value < 0)
-                privateScriptController.throwException(privateScriptController.DOMException.IndexSizeError, "The provided value (" + value + ") is negative.");
-            this.setAttribute('scrollamount', value);
-        },
-    });
-
-    Object.defineProperty(HTMLMarqueeElementPrototype, 'scrollDelay', {
-        get: function() {
-            var value = this.getAttribute('scrolldelay');
-            var scrollDelay = convertToLong(value);
-            if (isNaN(scrollDelay) || scrollDelay < 0)
-                return kDefaultScrollDelayMS;
-            return scrollDelay;
-        },
-        set: function(value) {
-            if (value < 0)
-                privateScriptController.throwException(privateScriptController.DOMException.IndexSizeError, "The provided value (" + value + ") is negative.");
-            this.setAttribute('scrolldelay', value);
-        },
-    });
-
-    Object.defineProperty(HTMLMarqueeElementPrototype, 'loop', {
-        get: function() {
-            var value = this.getAttribute('loop');
-            var loop = convertToLong(value);
-            if (isNaN(loop) || loop <= 0)
-                return kDefaultLoopLimit;
-            return loop;
-        },
-        set: function(value) {
-            if (value <= 0 && value != -1)
-                privateScriptController.throwException(privateScriptController.DOMException.IndexSizeError, "The provided value (" + value + ") is neither positive nor -1.");
-            this.setAttribute('loop', value);
-        },
-    });
-
-    HTMLMarqueeElementPrototype.getGetMetrics_ = function() {
-        var direction = this.direction.toLowerCase();
-        if (direction === 'up' || direction === 'down')
-            this.mover_.style.height = '-webkit-max-content';
-        else
-            this.mover_.style.width = '-webkit-max-content';
-
-        var moverStyle = getComputedStyle(this.mover_);
-        var marqueeStyle = getComputedStyle(this);
-
-        var metrics = {};
-        metrics.contentWidth = parseInt(moverStyle.width);
-        metrics.contentHeight = parseInt(moverStyle.height);
-        metrics.marqueeWidth = parseInt(marqueeStyle.width);
-        metrics.marqueeHeight = parseInt(marqueeStyle.height);
-
-        if (direction === 'up' || direction === 'down')
-            this.mover_.style.height = '';
-        else
-            this.mover_.style.width = '';
-        return metrics;
-    };
-
-    HTMLMarqueeElementPrototype.getAnimationParameters_ = function() {
-        var metrics = this.getGetMetrics_();
-
-        var totalWidth = metrics.marqueeWidth + metrics.contentWidth;
-        var totalHeight = metrics.marqueeHeight + metrics.contentHeight;
-
-        var innerWidth = metrics.marqueeWidth - metrics.contentWidth;
-        var innerHeight = metrics.marqueeHeight - metrics.contentHeight;
-
-        var parameters = {};
-        var direction = this.direction.toLowerCase();
-
-        switch (this.behavior) {
-        case kBehaviorScroll:
-        default:
-            switch (direction) {
-            case kDirectionLeft:
-            default:
-                parameters.transformBegin = 'translateX(' + metrics.marqueeWidth + 'px)';
-                parameters.transformEnd = 'translateX(-' + metrics.contentWidth + 'px)';
-                parameters.distance = totalWidth;
-                break;
-            case kDirectionRight:
-                parameters.transformBegin = 'translateX(-' + metrics.contentWidth + 'px)';
-                parameters.transformEnd = 'translateX(' + metrics.marqueeWidth + 'px)';
-                parameters.distance = totalWidth;
-                break;
-            case kDirectionUp:
-                parameters.transformBegin = 'translateY(' + metrics.marqueeHeight + 'px)';
-                parameters.transformEnd = 'translateY(-' + metrics.contentHeight + 'px)';
-                parameters.distance = totalHeight;
-                break;
-            case kDirectionDown:
-                parameters.transformBegin = 'translateY(-' + metrics.contentHeight + 'px)';
-                parameters.transformEnd = 'translateY(' + metrics.marqueeHeight + 'px)';
-                parameters.distance = totalHeight;
-                break;
-            }
-            break;
-        case kBehaviorAlternate:
-            switch (direction) {
-            case kDirectionLeft:
-            default:
-                parameters.transformBegin = 'translateX(' + (innerWidth >= 0 ? innerWidth : 0) + 'px)';
-                parameters.transformEnd = 'translateX(' + (innerWidth >= 0 ? 0 : innerWidth) + 'px)';
-                parameters.distance = Math.abs(innerWidth);
-                break;
-            case kDirectionRight:
-                parameters.transformBegin = 'translateX(' + (innerWidth >= 0 ? 0 : innerWidth) + 'px)';
-                parameters.transformEnd = 'translateX(' + (innerWidth >= 0 ? innerWidth : 0) + 'px)';
-                parameters.distance = Math.abs(innerWidth);
-                break;
-            case kDirectionUp:
-                parameters.transformBegin = 'translateY(' + (innerHeight >= 0 ? innerHeight : 0) + 'px)';
-                parameters.transformEnd = 'translateY(' + (innerHeight >= 0 ? 0 : innerHeight) + 'px)';
-                parameters.distance = Math.abs(innerHeight);
-                break;
-            case kDirectionDown:
-                parameters.transformBegin = 'translateY(' + (innerHeight >= 0 ? 0 : innerHeight) + 'px)';
-                parameters.transformEnd = 'translateY(' + (innerHeight >= 0 ? innerHeight : 0) + 'px)';
-                parameters.distance = Math.abs(innerHeight);
-                break;
-            }
-
-            if (this.loopCount_ % 2) {
-                var transform = parameters.transformBegin;
-                parameters.transformBegin = parameters.transformEnd;
-                parameters.transformEnd = transform;
-            }
-
-            break;
-        case kBehaviorSlide:
-            switch (direction) {
-            case kDirectionLeft:
-            default:
-                parameters.transformBegin = 'translateX(' + metrics.marqueeWidth + 'px)';
-                parameters.transformEnd = 'translateX(0)';
-                parameters.distance = metrics.marqueeWidth;
-                break;
-            case kDirectionRight:
-                parameters.transformBegin = 'translateX(-' + metrics.contentWidth + 'px)';
-                parameters.transformEnd = 'translateX(' + innerWidth + 'px)';
-                parameters.distance = metrics.marqueeWidth;
-                break;
-            case kDirectionUp:
-                parameters.transformBegin = 'translateY(' + metrics.marqueeHeight + 'px)';
-                parameters.transformEnd = 'translateY(0)';
-                parameters.distance = metrics.marqueeHeight;
-                break;
-            case kDirectionDown:
-                parameters.transformBegin = 'translateY(-' + metrics.contentHeight + 'px)';
-                parameters.transformEnd = 'translateY(' + innerHeight + 'px)';
-                parameters.distance = metrics.marqueeHeight;
-                break;
-            }
-            break;
-        }
-
-        return parameters
-    };
-
-    function animationFinished_(event) {
-        var player = event.target;
-        var marquee = player.marquee_;
-        marquee.loopCount_++;
-        marquee.start();
-    };
-
-    HTMLMarqueeElementPrototype.shouldContinue_ = function() {
-        var loop = this.loop;
-
-        // By default, slide loops only once.
-        if (loop <= 0 && this.behavior === kBehaviorSlide)
-            loop = 1;
-
-        if (loop <= 0)
-            return true;
-        return this.loopCount_ < loop;
-    };
-
-    HTMLMarqueeElementPrototype.continue_ = function() {
-        if (!this.shouldContinue_()) {
-            return;
-        }
-
-        if (this.player_ && this.player_.playState === 'paused') {
-            this.player_.play();
-            return;
-        }
-
-        var parameters = this.getAnimationParameters_();
-        var scrollDelay = this.scrollDelay;
-        if (scrollDelay < kMinimumScrollDelayMS && !this.trueSpeed)
-            scrollDelay = kDefaultScrollDelayMS;
-        var duration = 0;
-        if (this.scrollAmount && !isNaN(parameters.distance))
-            duration = parameters.distance * scrollDelay / this.scrollAmount;
-        var player = this.mover_.animate([
-            { transform: parameters.transformBegin },
-            { transform: parameters.transformEnd },
-        ], {
-            duration,
-            fill: 'forwards',
-        });
-        player.marquee_ = this;
-        player.onfinish = animationFinished_;
-
-        this.player_ = player;
-    };
-
-    HTMLMarqueeElementPrototype.start = function() {
-        // User script must not run in a SVGImage, but it's okay to run user
-        // agent's script such as <marquee>.  However, a function scheduled with
-        // requestAnimationFrame is indistinguishable if it's scheduled by user
-        // script or user agent's script.  Thus we disallow scheduling a task
-        // in svg (not limited to SVGImage) entirely.
-        if (document.rootElement instanceof SVGElement)
-            return;
-
-        if (this.continueCallback_)
-            return;
-        this.continueCallback_ = requestAnimationFrame(function() {
-            this.continueCallback_ = null;
-            this.continue_();
-        }.bind(this));
-    };
-
-    HTMLMarqueeElementPrototype.stop = function() {
-        if (this.continueCallback_) {
-            cancelAnimationFrame(this.continueCallback_);
-            this.continueCallback_ = null;
-            return;
-        }
-
-        if (this.player_) {
-            this.player_.pause();
-        }
-    };
-});
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 3e66e71..1556c0b1 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -73,16 +73,16 @@
 #include "core/loader/FrameLoaderClient.h"
 #include "core/page/ChromeClient.h"
 #include "core/page/NetworkStateNotifier.h"
-#include "platform/ContentType.h"
 #include "platform/Histogram.h"
 #include "platform/LayoutTestSupport.h"
-#include "platform/MIMETypeFromURL.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/UserGestureIndicator.h"
 #include "platform/audio/AudioBus.h"
 #include "platform/audio/AudioSourceProviderClient.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/mediastream/MediaStreamDescriptor.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeFromURL.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebAudioSourceProvider.h"
@@ -1230,11 +1230,9 @@
   // mode was not in the disabled state when the element's resource selection
   // algorithm last started now have a text track readiness state of loaded or
   // failed to load.
-  for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
-    if (m_textTracksWhenResourceSelectionBegan[i]->getReadinessState() ==
-            TextTrack::Loading ||
-        m_textTracksWhenResourceSelectionBegan[i]->getReadinessState() ==
-            TextTrack::NotLoaded)
+  for (const auto& textTrack : m_textTracksWhenResourceSelectionBegan) {
+    if (textTrack->getReadinessState() == TextTrack::Loading ||
+        textTrack->getReadinessState() == TextTrack::NotLoaded)
       return false;
   }
 
@@ -3389,6 +3387,7 @@
 }
 
 void HTMLMediaElement::didEnterFullscreen() {
+  configureMediaControls();
   if (mediaControls())
     mediaControls()->enteredFullscreen();
   // FIXME: There is no embedder-side handling in layout test mode.
@@ -3402,6 +3401,7 @@
 }
 
 void HTMLMediaElement::didExitFullscreen() {
+  configureMediaControls();
   if (mediaControls())
     mediaControls()->exitedFullscreen();
   if (webMediaPlayer())
@@ -3642,14 +3642,6 @@
   updateTextTrackDisplay();
 }
 
-EventDispatchHandlingState* HTMLMediaElement::preDispatchEventHandler(
-    Event* event) {
-  if (event && event->type() == EventTypeNames::webkitfullscreenchange)
-    configureMediaControls();
-
-  return nullptr;
-}
-
 // TODO(srirama.m): Merge it to resetMediaElement if possible and remove it.
 void HTMLMediaElement::resetMediaPlayerAndMediaSource() {
   closeMediaSource();
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index 36a4468..540c9271 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -36,10 +36,10 @@
 #include "core/events/GenericEventQueue.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/track/TextTrack.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/Supplementable.h"
 #include "platform/WebTaskRunner.h"
 #include "platform/audio/AudioSourceProvider.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "public/platform/WebAudioSourceProviderClient.h"
 #include "public/platform/WebMediaPlayerClient.h"
 #include <memory>
@@ -483,8 +483,6 @@
 
   TextTrackContainer& ensureTextTrackContainer();
 
-  EventDispatchHandlingState* preDispatchEventHandler(Event*) final;
-
   void changeNetworkStateFromLoadingToIdle();
 
   bool isAutoplaying() const { return m_autoplaying; }
diff --git a/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp b/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
index abd237c..c0ce0a42 100644
--- a/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
@@ -42,8 +42,8 @@
 #include "core/layout/api/LayoutEmbeddedItem.h"
 #include "core/loader/FrameLoaderClient.h"
 #include "core/plugins/PluginView.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/Widget.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 
 namespace blink {
 
@@ -57,7 +57,6 @@
                         createdByParser,
                         ShouldNotPreferPlugInsForImages),
       m_useFallbackContent(false) {
-  associateByParser(form);
 }
 
 inline HTMLObjectElement::~HTMLObjectElement() {}
@@ -465,6 +464,8 @@
   return !hasValidClassId() && hasFallbackContent();
 }
 
-void HTMLObjectElement::associateWith(HTMLFormElement*){};
+void HTMLObjectElement::associateWith(HTMLFormElement* form) {
+  associateByParser(form);
+};
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
index 19a31667..bb47c4e 100644
--- a/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLPlugInElement.cpp
@@ -47,10 +47,10 @@
 #include "core/page/scrolling/ScrollingCoordinator.h"
 #include "core/plugins/PluginView.h"
 #include "platform/Histogram.h"
-#include "platform/MIMETypeFromURL.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/Widget.h"
 #include "platform/network/ResourceRequest.h"
+#include "platform/network/mime/MIMETypeFromURL.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/plugins/PluginData.h"
 #include "public/platform/WebURLRequest.h"
 
diff --git a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
index 1d9e854..aebef28 100644
--- a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
@@ -480,9 +480,8 @@
     if (n->isTextNode())
       textNodes.append(n);
   }
-  size_t size = textNodes.size();
-  for (size_t i = 0; i < size; ++i)
-    removeChild(textNodes[i].get(), IGNORE_EXCEPTION);
+  for (const auto& text : textNodes)
+    removeChild(text.get(), IGNORE_EXCEPTION);
 
   // Normalize line endings.
   String value = defaultValue;
diff --git a/third_party/WebKit/Source/core/html/LinkStyle.cpp b/third_party/WebKit/Source/core/html/LinkStyle.cpp
index 4a3297d..412fe942 100644
--- a/third_party/WebKit/Source/core/html/LinkStyle.cpp
+++ b/third_party/WebKit/Source/core/html/LinkStyle.cpp
@@ -12,9 +12,9 @@
 #include "core/html/HTMLLinkElement.h"
 #include "core/loader/FrameLoaderClient.h"
 #include "core/loader/resource/CSSStyleSheetResource.h"
-#include "platform/ContentType.h"
 #include "platform/Histogram.h"
-#include "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/html/MediaFragmentURIParser.cpp b/third_party/WebKit/Source/core/html/MediaFragmentURIParser.cpp
index 3630fc50..3db9fae 100644
--- a/third_party/WebKit/Source/core/html/MediaFragmentURIParser.cpp
+++ b/third_party/WebKit/Source/core/html/MediaFragmentURIParser.cpp
@@ -150,9 +150,7 @@
 
   m_timeFormat = Invalid;
 
-  for (unsigned i = 0; i < m_fragments.size(); ++i) {
-    std::pair<String, String>& fragment = m_fragments[i];
-
+  for (const auto& fragment : m_fragments) {
     DCHECK(fragment.first.is8Bit());
     DCHECK(fragment.second.is8Bit());
 
diff --git a/third_party/WebKit/Source/core/html/PublicURLManager.cpp b/third_party/WebKit/Source/core/html/PublicURLManager.cpp
index e50b4af..57655b2 100644
--- a/third_party/WebKit/Source/core/html/PublicURLManager.cpp
+++ b/third_party/WebKit/Source/core/html/PublicURLManager.cpp
@@ -87,8 +87,8 @@
         urlsToRemove.append(registeredUrl.key);
       }
     }
-    for (unsigned j = 0; j < urlsToRemove.size(); j++)
-      registeredURLs.remove(urlsToRemove[j]);
+    for (const auto& url : urlsToRemove)
+      registeredURLs.remove(url);
     urlsToRemove.clear();
   }
 }
diff --git a/third_party/WebKit/Source/core/html/forms/EmailInputType.cpp b/third_party/WebKit/Source/core/html/forms/EmailInputType.cpp
index 8891afb6..755ae39 100644
--- a/third_party/WebKit/Source/core/html/forms/EmailInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/EmailInputType.cpp
@@ -186,8 +186,8 @@
     return isValidEmailAddress(ensureEmailRegexp(), value) ? String() : value;
   Vector<String> addresses;
   value.split(',', true, addresses);
-  for (unsigned i = 0; i < addresses.size(); ++i) {
-    String stripped = stripLeadingAndTrailingHTMLSpaces(addresses[i]);
+  for (const auto& address : addresses) {
+    String stripped = stripLeadingAndTrailingHTMLSpaces(address);
     if (!isValidEmailAddress(ensureEmailRegexp(), stripped))
       return stripped;
   }
diff --git a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
index 70eb512..f278b868 100644
--- a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
@@ -225,24 +225,22 @@
     int rootLength = rootPath.length();
     if (rootPath[rootLength - 1] != '\\' && rootPath[rootLength - 1] != '/')
       rootLength += 1;
-    for (size_t i = 0; i < size; ++i) {
+    for (const auto& file : files) {
       // Normalize backslashes to slashes before exposing the relative path to
       // script.
-      String relativePath =
-          files[i].path.substring(rootLength).replace('\\', '/');
-      fileList->append(
-          File::createWithRelativePath(files[i].path, relativePath));
+      String relativePath = file.path.substring(rootLength).replace('\\', '/');
+      fileList->append(File::createWithRelativePath(file.path, relativePath));
     }
     return fileList;
   }
 
-  for (size_t i = 0; i < size; ++i) {
-    if (files[i].fileSystemURL.isEmpty()) {
+  for (const auto& file : files) {
+    if (file.fileSystemURL.isEmpty()) {
       fileList->append(
-          File::createForUserProvidedFile(files[i].path, files[i].displayName));
+          File::createForUserProvidedFile(file.path, file.displayName));
     } else {
       fileList->append(File::createForFileSystemFile(
-          files[i].fileSystemURL, files[i].metadata, File::IsUserVisible));
+          file.fileSystemURL, file.metadata, File::IsUserVisible));
     }
   }
   return fileList;
@@ -352,8 +350,8 @@
   }
 
   Vector<FileChooserFileInfo> files;
-  for (unsigned i = 0; i < paths.size(); ++i)
-    files.append(FileChooserFileInfo(paths[i]));
+  for (const auto& path : paths)
+    files.append(FileChooserFileInfo(path));
 
   if (input.fastHasAttribute(multipleAttr)) {
     filesChosen(files);
diff --git a/third_party/WebKit/Source/core/html/forms/FormController.cpp b/third_party/WebKit/Source/core/html/forms/FormController.cpp
index 3849e8fa..f03e8544 100644
--- a/third_party/WebKit/Source/core/html/forms/FormController.cpp
+++ b/third_party/WebKit/Source/core/html/forms/FormController.cpp
@@ -60,8 +60,8 @@
 void FormControlState::serializeTo(Vector<String>& stateVector) const {
   DCHECK(!isFailure());
   stateVector.append(String::number(m_values.size()));
-  for (size_t i = 0; i < m_values.size(); ++i)
-    stateVector.append(m_values[i].isNull() ? emptyString() : m_values[i]);
+  for (const auto& value : m_values)
+    stateVector.append(value.isNull() ? emptyString() : value);
 }
 
 FormControlState FormControlState::deserialize(
@@ -295,8 +295,8 @@
       const Vector<FileChooserFileInfo>& selectedFiles =
           HTMLInputElement::filesFromFileInputFormControlState(
               formControlState);
-      for (size_t i = 0; i < selectedFiles.size(); ++i)
-        toReturn.append(selectedFiles[i].path);
+      for (const auto& file : selectedFiles)
+        toReturn.append(file.path);
     }
   }
   return toReturn;
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
index 3d92b117a..213dcea 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
@@ -106,8 +106,8 @@
       updated.append(i);
   }
 
-  for (size_t i = 0; i < updated.size(); ++i)
-    updated[i]->stateDidChange();
+  for (const auto& import : updated)
+    import->stateDidChange();
 }
 
 #if !defined(NDEBUG)
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
index 8718ce5..33f3951 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
@@ -47,14 +47,19 @@
 HTMLImportChild::HTMLImportChild(const KURL& url,
                                  HTMLImportLoader* loader,
                                  SyncMode sync)
-    : HTMLImport(sync), m_url(url), m_loader(loader), m_client(nullptr) {}
+    : HTMLImport(sync), m_url(url), m_loader(loader), m_client(nullptr) {
+  DCHECK(loader);
+}
 
 HTMLImportChild::~HTMLImportChild() {}
 
 void HTMLImportChild::ownerInserted() {
   if (!m_loader->isDone())
     return;
-  document()->styleEngine().htmlImportAddedOrRemoved();
+
+  DCHECK(root());
+  DCHECK(root()->document());
+  root()->document()->styleEngine().htmlImportAddedOrRemoved();
   // TODO(rune@opera.com): resolverChanged() can be removed once stylesheet
   // updates are async. https://crbug.com/567021
   root()->document()->styleEngine().resolverChanged(AnalyzedStyleUpdate);
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportLoader.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportLoader.cpp
index 779287e..1b85900 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportLoader.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportLoader.cpp
@@ -171,8 +171,8 @@
 }
 
 void HTMLImportLoader::didFinishLoading() {
-  for (size_t i = 0; i < m_imports.size(); ++i)
-    m_imports[i]->didFinishLoading();
+  for (const auto& importChild : m_imports)
+    importChild->didFinishLoading();
 
   clearResource();
 
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
index d23db10b..47e1329 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
@@ -25,8 +25,8 @@
 HTMLImportTreeRoot::~HTMLImportTreeRoot() {}
 
 void HTMLImportTreeRoot::dispose() {
-  for (size_t i = 0; i < m_imports.size(); ++i)
-    m_imports[i]->dispose();
+  for (const auto& importChild : m_imports)
+    importChild->dispose();
   m_imports.clear();
   m_document = nullptr;
   m_recalcTimer.stop();
@@ -67,8 +67,7 @@
 }
 
 HTMLImportChild* HTMLImportTreeRoot::find(const KURL& url) const {
-  for (size_t i = 0; i < m_imports.size(); ++i) {
-    HTMLImportChild* candidate = m_imports[i].get();
+  for (const auto& candidate : m_imports) {
     if (equalIgnoringFragmentIdentifier(candidate->url(), url))
       return candidate;
   }
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
index 76192bc..b5f43eb 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
@@ -52,8 +52,8 @@
     m_root.clear();
   }
 
-  for (size_t i = 0; i < m_loaders.size(); ++i)
-    m_loaders[i]->dispose();
+  for (const auto& loader : m_loaders)
+    loader->dispose();
   m_loaders.clear();
 }
 
@@ -139,9 +139,9 @@
 
 HTMLImportLoader* HTMLImportsController::loaderFor(
     const Document& document) const {
-  for (size_t i = 0; i < m_loaders.size(); ++i) {
-    if (m_loaders[i]->document() == &document)
-      return m_loaders[i].get();
+  for (const auto& loader : m_loaders) {
+    if (loader->document() == &document)
+      return loader.get();
   }
 
   return nullptr;
diff --git a/third_party/WebKit/Source/core/html/parser/AtomicHTMLToken.h b/third_party/WebKit/Source/core/html/parser/AtomicHTMLToken.h
index 514c955..fb6d1a0 100644
--- a/third_party/WebKit/Source/core/html/parser/AtomicHTMLToken.h
+++ b/third_party/WebKit/Source/core/html/parser/AtomicHTMLToken.h
@@ -222,8 +222,7 @@
 
   m_attributes.clear();
   m_attributes.reserveInitialCapacity(size);
-  for (size_t i = 0; i < size; ++i) {
-    const HTMLToken::Attribute& attribute = attributes[i];
+  for (const auto& attribute : attributes) {
     if (attribute.nameAsVector().isEmpty())
       continue;
 
diff --git a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLInputStream.cpp b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLInputStream.cpp
index 906541c2..dfcc058 100644
--- a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLInputStream.cpp
+++ b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLInputStream.cpp
@@ -115,10 +115,10 @@
 
 void BackgroundHTMLInputStream::updateTotalCheckpointTokenCount() {
   m_totalCheckpointTokenCount = 0;
-  size_t lastCheckpointIndex = m_checkpoints.size();
-  for (size_t i = 0; i < lastCheckpointIndex; ++i)
+  for (const auto& checkpoint : m_checkpoints) {
     m_totalCheckpointTokenCount +=
-        m_checkpoints[i].tokensExtractedSincePreviousCheckpoint;
+        checkpoint.tokensExtractedSincePreviousCheckpoint;
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
index 217d3898..3a5f00a1 100644
--- a/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/CSSPreloadScanner.cpp
@@ -282,6 +282,12 @@
 void CSSPreloaderResourceClient::scanCSS(
     const CSSStyleSheetResource* resource) {
   DCHECK(m_preloader);
+
+  // Early abort if there is no document loader. Do this early to ensure that
+  // scan histograms and preload histograms do not count different quantities.
+  if (!m_preloader->document()->loader())
+    return;
+
   // Passing an empty SegmentedString here results in PreloadRequest with no
   // file/line information.
   // TODO(csharrison): If this becomes an issue the CSSPreloadScanner may be
diff --git a/third_party/WebKit/Source/core/html/parser/CSSPreloadScannerTest.cpp b/third_party/WebKit/Source/core/html/parser/CSSPreloadScannerTest.cpp
index 428b8935..17a87f9 100644
--- a/third_party/WebKit/Source/core/html/parser/CSSPreloadScannerTest.cpp
+++ b/third_party/WebKit/Source/core/html/parser/CSSPreloadScannerTest.cpp
@@ -19,49 +19,62 @@
 
 namespace blink {
 
-class PreloadSuppressingCSSPreloaderResourceClient final
+namespace {
+
+class MockHTMLResourcePreloader : public HTMLResourcePreloader {
+  WTF_MAKE_NONCOPYABLE(MockHTMLResourcePreloader);
+
+ public:
+  MockHTMLResourcePreloader(Document& document)
+      : HTMLResourcePreloader(document) {}
+
+  void preload(std::unique_ptr<PreloadRequest> preloadRequest,
+               const NetworkHintsInterface&) override {}
+};
+
+class PreloadRecordingCSSPreloaderResourceClient final
     : public CSSPreloaderResourceClient {
  public:
-  PreloadSuppressingCSSPreloaderResourceClient(Resource* resource,
-                                               HTMLResourcePreloader* preloader)
+  PreloadRecordingCSSPreloaderResourceClient(Resource* resource,
+                                             HTMLResourcePreloader* preloader)
       : CSSPreloaderResourceClient(resource, preloader) {}
+
   void fetchPreloads(PreloadRequestStream& preloads) override {
-    PreloadRequestStream movedPreloads;
-    movedPreloads.swap(preloads);
-    for (PreloadRequestStream::iterator it = movedPreloads.begin();
-         it != movedPreloads.end(); ++it) {
-      m_preloads.append(std::move(*it));
-    }
+    for (const auto& it : preloads)
+      m_preloadUrls.append(it->resourceURL());
+    CSSPreloaderResourceClient::fetchPreloads(preloads);
   }
 
-  PreloadRequestStream m_preloads;
+  Vector<String> m_preloadUrls;
 };
 
 class CSSPreloadScannerTest : public ::testing::Test {};
 
+}  // namespace
+
 TEST_F(CSSPreloadScannerTest, ScanFromResourceClient) {
   std::unique_ptr<DummyPageHolder> dummyPageHolder =
       DummyPageHolder::create(IntSize(500, 500));
   dummyPageHolder->document().settings()->setCSSExternalScannerNoPreload(true);
 
-  HTMLResourcePreloader* preloader =
-      HTMLResourcePreloader::create(dummyPageHolder->document());
+  MockHTMLResourcePreloader* preloader =
+      new MockHTMLResourcePreloader(dummyPageHolder->document());
 
   KURL url(ParsedURLString, "http://127.0.0.1/foo.css");
   CSSStyleSheetResource* resource =
       CSSStyleSheetResource::createForTest(ResourceRequest(url), "utf-8");
   resource->setStatus(Resource::Pending);
 
-  PreloadSuppressingCSSPreloaderResourceClient* resourceClient =
-      new PreloadSuppressingCSSPreloaderResourceClient(resource, preloader);
+  PreloadRecordingCSSPreloaderResourceClient* resourceClient =
+      new PreloadRecordingCSSPreloaderResourceClient(resource, preloader);
 
   const char* data = "@import url('http://127.0.0.1/preload.css');";
   resource->appendData(data, strlen(data));
 
   EXPECT_EQ(Resource::PreloadNotReferenced, resource->getPreloadResult());
-  EXPECT_EQ(1u, resourceClient->m_preloads.size());
+  EXPECT_EQ(1u, resourceClient->m_preloadUrls.size());
   EXPECT_EQ("http://127.0.0.1/preload.css",
-            resourceClient->m_preloads.front()->resourceURL());
+            resourceClient->m_preloadUrls.front());
 }
 
 // Regression test for crbug.com/608310 where the client is destroyed but was
@@ -71,15 +84,15 @@
       DummyPageHolder::create(IntSize(500, 500));
   dummyPageHolder->document().settings()->setCSSExternalScannerNoPreload(true);
 
-  Persistent<HTMLResourcePreloader> preloader =
-      HTMLResourcePreloader::create(dummyPageHolder->document());
+  Persistent<MockHTMLResourcePreloader> preloader =
+      new MockHTMLResourcePreloader(dummyPageHolder->document());
 
   KURL url(ParsedURLString, "http://127.0.0.1/foo.css");
   Persistent<CSSStyleSheetResource> resource =
       CSSStyleSheetResource::createForTest(ResourceRequest(url), "utf-8");
   resource->setStatus(Resource::Pending);
 
-  new PreloadSuppressingCSSPreloaderResourceClient(resource, preloader);
+  new PreloadRecordingCSSPreloaderResourceClient(resource, preloader);
 
   // Destroys the resourceClient.
   ThreadState::current()->collectAllGarbage();
@@ -96,8 +109,8 @@
       DummyPageHolder::create(IntSize(500, 500));
   dummyPageHolder->document().settings()->setCSSExternalScannerNoPreload(true);
 
-  HTMLResourcePreloader* preloader =
-      HTMLResourcePreloader::create(dummyPageHolder->document());
+  MockHTMLResourcePreloader* preloader =
+      new MockHTMLResourcePreloader(dummyPageHolder->document());
 
   KURL url(ParsedURLString, "http://127.0.0.1/foo.css");
   CSSStyleSheetResource* resource =
@@ -109,10 +122,10 @@
   resource->error(error);
 
   // Should not crash.
-  PreloadSuppressingCSSPreloaderResourceClient* resourceClient =
-      new PreloadSuppressingCSSPreloaderResourceClient(resource, preloader);
+  PreloadRecordingCSSPreloaderResourceClient* resourceClient =
+      new PreloadRecordingCSSPreloaderResourceClient(resource, preloader);
 
-  EXPECT_EQ(0u, resourceClient->m_preloads.size());
+  EXPECT_EQ(0u, resourceClient->m_preloadUrls.size());
 }
 
 // Regression test for crbug.com/645331, where a resource client gets callbacks
@@ -122,25 +135,25 @@
       DummyPageHolder::create(IntSize(500, 500));
   dummyPageHolder->document().settings()->setCSSExternalScannerNoPreload(true);
 
-  HTMLResourcePreloader* preloader =
-      HTMLResourcePreloader::create(dummyPageHolder->document());
+  MockHTMLResourcePreloader* preloader =
+      new MockHTMLResourcePreloader(dummyPageHolder->document());
 
   KURL url(ParsedURLString, "http://127.0.0.1/foo.css");
   CSSStyleSheetResource* resource =
       CSSStyleSheetResource::createForTest(ResourceRequest(url), "utf-8");
   resource->setStatus(Resource::Pending);
 
-  PreloadSuppressingCSSPreloaderResourceClient* resourceClient =
-      new PreloadSuppressingCSSPreloaderResourceClient(resource, preloader);
+  PreloadRecordingCSSPreloaderResourceClient* resourceClient =
+      new PreloadRecordingCSSPreloaderResourceClient(resource, preloader);
 
   dummyPageHolder->document().shutdown();
 
   const char* data = "@import url('http://127.0.0.1/preload.css');";
   resource->appendData(data, strlen(data));
 
-  EXPECT_EQ(1u, resourceClient->m_preloads.size());
-  EXPECT_EQ("http://127.0.0.1/preload.css",
-            resourceClient->m_preloads.front()->resourceURL());
+  // Do not expect to gather any preloads, as the document loader is invalid,
+  // which means we can't notify WebLoadingBehaviorData of the preloads.
+  EXPECT_EQ(0u, resourceClient->m_preloadUrls.size());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
index c14205ed..f91c2ce2 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLConstructionSite.cpp
@@ -321,8 +321,8 @@
   TaskQueue queue;
   queue.swap(m_taskQueue);
 
-  for (size_t i = 0; i < size; ++i)
-    executeTask(queue[i]);
+  for (auto& task : queue)
+    executeTask(task);
 
   // We might be detached now.
 }
@@ -407,8 +407,7 @@
   if (token->attributes().isEmpty())
     return;
 
-  for (unsigned i = 0; i < token->attributes().size(); ++i) {
-    const Attribute& tokenAttribute = token->attributes().at(i);
+  for (const auto& tokenAttribute : token->attributes()) {
     if (element->attributesWithoutUpdate().findIndex(tokenAttribute.name()) ==
         kNotFound)
       element->setAttribute(tokenAttribute.name(), tokenAttribute.value());
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
index f5cad31..6d7a665 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
@@ -51,10 +51,10 @@
 #include "platform/CrossThreadFunctional.h"
 #include "platform/Histogram.h"
 #include "platform/SharedBuffer.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/heap/Handle.h"
 #include "platform/tracing/TraceEvent.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebLoadingBehaviorFlag.h"
 #include "public/platform/WebScheduler.h"
 #include "public/platform/WebThread.h"
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLFormattingElementList.cpp b/third_party/WebKit/Source/core/html/parser/HTMLFormattingElementList.cpp
index 392fcd8..2fcba8b1 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLFormattingElementList.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLFormattingElementList.cpp
@@ -166,13 +166,8 @@
   HeapVector<Member<HTMLStackItem>> remainingCandidates;
   remainingCandidates.reserveInitialCapacity(candidates.size());
 
-  const Vector<Attribute>& attributes = newItem->attributes();
-  for (size_t i = 0; i < attributes.size(); ++i) {
-    const Attribute& attribute = attributes[i];
-
-    for (size_t j = 0; j < candidates.size(); ++j) {
-      HTMLStackItem* candidate = candidates[j];
-
+  for (const auto& attribute : newItem->attributes()) {
+    for (const auto& candidate : candidates) {
       // These properties should already have been checked by
       // tryToEnsureNoahsArkConditionQuickly.
       ASSERT(newItem->attributes().size() == candidate->attributes().size());
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
index 49e2abc7..01f22d7d 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
@@ -47,9 +47,9 @@
 #include "core/html/parser/HTMLSrcsetParser.h"
 #include "core/html/parser/HTMLTokenizer.h"
 #include "core/loader/LinkLoader.h"
-#include "platform/ContentType.h"
 #include "platform/Histogram.h"
-#include "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/tracing/TraceEvent.h"
 #include <memory>
 
@@ -669,15 +669,6 @@
   if (!m_documentParameters->doDocumentWritePreloadScanning)
     return false;
 
-  // Log inline script length counts, which will help tune
-  // kMaxLengthForEvaluating. The 50,000 limit was found experimentally.
-  DEFINE_STATIC_LOCAL(
-      CustomCountHistogram, scriptLengthHistogram,
-      ("PreloadScanner.DocumentWrite.ScriptLength", 0, 50000, 50));
-  scriptLengthHistogram.count(source.length());
-
-  // Script length is already logged, but include a count for script length
-  // for easy comparison with the rest of the reasons.
   if (source.length() > kMaxLengthForEvaluating) {
     LogGatedEvaluation(GatedEvaluationScriptTooLong);
     return false;
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.cpp b/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.cpp
index 554b47e..3e11e745 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.cpp
@@ -38,7 +38,7 @@
 
 namespace blink {
 
-inline HTMLResourcePreloader::HTMLResourcePreloader(Document& document)
+HTMLResourcePreloader::HTMLResourcePreloader(Document& document)
     : m_document(document) {}
 
 HTMLResourcePreloader* HTMLResourcePreloader::create(Document& document) {
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.h b/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.h
index cc6065a..d90b21b 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLResourcePreloader.h
@@ -39,7 +39,7 @@
 
 class Document;
 
-class CORE_EXPORT HTMLResourcePreloader final
+class CORE_EXPORT HTMLResourcePreloader
     : public GarbageCollected<HTMLResourcePreloader>,
       public ResourcePreloader {
   WTF_MAKE_NONCOPYABLE(HTMLResourcePreloader);
@@ -54,10 +54,9 @@
  protected:
   void preload(std::unique_ptr<PreloadRequest>,
                const NetworkHintsInterface&) override;
-
- private:
   explicit HTMLResourcePreloader(Document&);
 
+ private:
   Member<Document> m_document;
   HeapHashSet<Member<CSSPreloaderResourceClient>> m_cssPreloaders;
 };
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLScriptRunner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLScriptRunner.cpp
index 899dd1d..3617bf3 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLScriptRunner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLScriptRunner.cpp
@@ -41,10 +41,10 @@
 #include "core/html/parser/NestingLevelIncrementer.h"
 #include "core/loader/resource/ScriptResource.h"
 #include "platform/Histogram.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/tracing/TraceEvent.h"
 #include "platform/tracing/TracedValue.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 #include <inttypes.h>
 #include <memory>
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
index 7a48396..9fff25be 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
@@ -480,8 +480,7 @@
     mapLoweredLocalNameToName(caseMap, attrs.get(), length);
   }
 
-  for (unsigned i = 0; i < token->attributes().size(); ++i) {
-    Attribute& tokenAttribute = token->attributes().at(i);
+  for (auto& tokenAttribute : token->attributes()) {
     const QualifiedName& casedName = caseMap->get(tokenAttribute.localName());
     if (!casedName.localName().isNull())
       tokenAttribute.parserSetName(casedName);
diff --git a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
index ba949984..df0366d 100644
--- a/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/DateTimeEditElement.cpp
@@ -535,8 +535,8 @@
 }
 
 bool DateTimeEditElement::anyEditableFieldsHaveValues() const {
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
-    if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
+  for (const auto& field : m_fields) {
+    if (!field->isDisabled() && field->hasValue())
       return true;
   }
   return false;
@@ -767,8 +767,8 @@
 }
 
 void DateTimeEditElement::resetFields() {
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
-    m_fields[fieldIndex]->removeEventHandler();
+  for (const auto& field : m_fields)
+    field->removeEventHandler();
   m_fields.shrink(0);
 }
 
@@ -788,22 +788,22 @@
     const LayoutParameters& layoutParameters,
     const DateComponents& date) {
   layout(layoutParameters, date);
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
-    m_fields[fieldIndex]->setValueAsDate(date);
+  for (const auto& field : m_fields)
+    field->setValueAsDate(date);
 }
 
 void DateTimeEditElement::setValueAsDateTimeFieldsState(
     const DateTimeFieldsState& dateTimeFieldsState) {
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
-    m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
+  for (const auto& field : m_fields)
+    field->setValueAsDateTimeFieldsState(dateTimeFieldsState);
 }
 
 void DateTimeEditElement::setEmptyValue(
     const LayoutParameters& layoutParameters,
     const DateComponents& dateForReadOnlyField) {
   layout(layoutParameters, dateForReadOnlyField);
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
-    m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
+  for (const auto& field : m_fields)
+    field->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
 }
 
 bool DateTimeEditElement::hasFocusedField() {
@@ -850,8 +850,8 @@
 
 DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const {
   DateTimeFieldsState dateTimeFieldsState;
-  for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
-    m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
+  for (const auto& field : m_fields)
+    field->populateDateTimeFieldsState(dateTimeFieldsState);
   return dateTimeFieldsState;
 }
 
diff --git a/third_party/WebKit/Source/core/html/track/AutomaticTrackSelection.cpp b/third_party/WebKit/Source/core/html/track/AutomaticTrackSelection.cpp
index ef7cc89..651d790 100644
--- a/third_party/WebKit/Source/core/html/track/AutomaticTrackSelection.cpp
+++ b/third_party/WebKit/Source/core/html/track/AutomaticTrackSelection.cpp
@@ -75,9 +75,7 @@
   TextTrack* fallbackTrack = nullptr;
   int highestTrackScore = 0;
 
-  for (size_t i = 0; i < group.tracks.size(); ++i) {
-    TextTrack* textTrack = group.tracks[i];
-
+  for (const auto& textTrack : group.tracks) {
     if (m_configuration.disableCurrentlyEnabledTracks &&
         textTrack->mode() == TextTrack::showingKeyword())
       currentlyEnabledTracks.append(textTrack);
@@ -127,8 +125,7 @@
   }
 
   if (currentlyEnabledTracks.size()) {
-    for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
-      TextTrack* textTrack = currentlyEnabledTracks[i];
+    for (const auto& textTrack : currentlyEnabledTracks) {
       if (textTrack != trackToEnable)
         textTrack->setMode(TextTrack::disabledKeyword());
     }
diff --git a/third_party/WebKit/Source/core/html/track/CueTimeline.cpp b/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
index 7b0ee446..718106b 100644
--- a/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
+++ b/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
@@ -189,7 +189,6 @@
     mediaElement.scheduleTimeupdateEvent(true);
 
   // Explicitly cache vector sizes, as their content is constant from here.
-  size_t currentCuesSize = currentCues.size();
   size_t missedCuesSize = missedCues.size();
   size_t previousCuesSize = previousCues.size();
 
@@ -243,11 +242,11 @@
   // 8 - Let affected tracks be a list of text tracks, initially empty.
   HeapVector<Member<TextTrack>> affectedTracks;
 
-  for (size_t i = 0; i < missedCuesSize; ++i) {
+  for (const auto& missedCue : missedCues) {
     // 9 - For each text track cue in missed cues, prepare an event named enter
     // for the TextTrackCue object with the text track cue start time.
-    eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
-                                     missedCues[i].data()));
+    eventTasks.append(
+        std::make_pair(missedCue.data()->startTime(), missedCue.data()));
 
     // 10 - For each text track [...] in missed cues, prepare an event
     // named exit for the TextTrackCue object with the  with the later of
@@ -258,36 +257,39 @@
     // checked when these tasks are actually queued below. This doesn't
     // affect sorting events before dispatch either, because the exit
     // event has the same time as the enter event.
-    if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
-      eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
-                                       missedCues[i].data()));
+    if (missedCue.data()->startTime() < missedCue.data()->endTime()) {
+      eventTasks.append(
+          std::make_pair(missedCue.data()->endTime(), missedCue.data()));
+    }
   }
 
-  for (size_t i = 0; i < previousCuesSize; ++i) {
+  for (const auto& previousCue : previousCues) {
     // 10 - For each text track cue in other cues that has its text
     // track cue active flag set prepare an event named exit for the
     // TextTrackCue object with the text track cue end time.
-    if (!currentCues.contains(previousCues[i]))
-      eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
-                                       previousCues[i].data()));
+    if (!currentCues.contains(previousCue)) {
+      eventTasks.append(
+          std::make_pair(previousCue.data()->endTime(), previousCue.data()));
+    }
   }
 
-  for (size_t i = 0; i < currentCuesSize; ++i) {
+  for (const auto& currentCue : currentCues) {
     // 11 - For each text track cue in current cues that does not have its
     // text track cue active flag set, prepare an event named enter for the
     // TextTrackCue object with the text track cue start time.
-    if (!previousCues.contains(currentCues[i]))
-      eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
-                                       currentCues[i].data()));
+    if (!previousCues.contains(currentCue)) {
+      eventTasks.append(
+          std::make_pair(currentCue.data()->startTime(), currentCue.data()));
+    }
   }
 
   // 12 - Sort the tasks in events in ascending time order (tasks with earlier
   // times first).
   nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
 
-  for (size_t i = 0; i < eventTasks.size(); ++i) {
-    if (!affectedTracks.contains(eventTasks[i].second->track()))
-      affectedTracks.append(eventTasks[i].second->track());
+  for (const auto& task : eventTasks) {
+    if (!affectedTracks.contains(task.second->track()))
+      affectedTracks.append(task.second->track());
 
     // 13 - Queue each task in events, in list order.
 
@@ -295,18 +297,17 @@
     // depending on the time that is associated with the event. This
     // correctly identifies the type of the event, if the startTime is
     // less than the endTime in the cue.
-    if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
-      mediaElement.scheduleEvent(createEventWithTarget(
-          EventTypeNames::enter, eventTasks[i].second.get()));
-      mediaElement.scheduleEvent(createEventWithTarget(
-          EventTypeNames::exit, eventTasks[i].second.get()));
+    if (task.second->startTime() >= task.second->endTime()) {
+      mediaElement.scheduleEvent(
+          createEventWithTarget(EventTypeNames::enter, task.second.get()));
+      mediaElement.scheduleEvent(
+          createEventWithTarget(EventTypeNames::exit, task.second.get()));
     } else {
-      bool isEnterEvent =
-          eventTasks[i].first == eventTasks[i].second->startTime();
+      bool isEnterEvent = task.first == task.second->startTime();
       AtomicString eventName =
           isEnterEvent ? EventTypeNames::enter : EventTypeNames::exit;
       mediaElement.scheduleEvent(
-          createEventWithTarget(eventName, eventTasks[i].second.get()));
+          createEventWithTarget(eventName, task.second.get()));
     }
   }
 
@@ -318,16 +319,15 @@
   // 15 - For each text track in affected tracks, in the list order, queue a
   // task to fire a simple event named cuechange at the TextTrack object, and,
   // ...
-  for (size_t i = 0; i < affectedTracks.size(); ++i) {
-    mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::cuechange,
-                                                     affectedTracks[i].get()));
+  for (const auto& track : affectedTracks) {
+    mediaElement.scheduleEvent(
+        createEventWithTarget(EventTypeNames::cuechange, track.get()));
 
     // ... if the text track has a corresponding track element, to then fire a
     // simple event named cuechange at the track element as well.
-    if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
+    if (track->trackType() == TextTrack::TrackElement) {
       HTMLTrackElement* trackElement =
-          static_cast<LoadableTextTrack*>(affectedTracks[i].get())
-              ->trackElement();
+          static_cast<LoadableTextTrack*>(track.get())->trackElement();
       DCHECK(trackElement);
       mediaElement.scheduleEvent(
           createEventWithTarget(EventTypeNames::cuechange, trackElement));
@@ -337,12 +337,12 @@
   // 16 - Set the text track cue active flag of all the cues in the current
   // cues, and unset the text track cue active flag of all the cues in the
   // other cues.
-  for (size_t i = 0; i < currentCuesSize; ++i)
-    currentCues[i].data()->setIsActive(true);
+  for (const auto& cue : currentCues)
+    cue.data()->setIsActive(true);
 
-  for (size_t i = 0; i < previousCuesSize; ++i) {
-    if (!currentCues.contains(previousCues[i])) {
-      TextTrackCue* cue = previousCues[i].data();
+  for (const auto& previousCue : previousCues) {
+    if (!currentCues.contains(previousCue)) {
+      TextTrackCue* cue = previousCue.data();
       cue->setIsActive(false);
       cue->removeDisplayTree();
     }
diff --git a/third_party/WebKit/Source/core/html/track/LoadableTextTrack.cpp b/third_party/WebKit/Source/core/html/track/LoadableTextTrack.cpp
index a9357ed8..6f7872b 100644
--- a/third_party/WebKit/Source/core/html/track/LoadableTextTrack.cpp
+++ b/third_party/WebKit/Source/core/html/track/LoadableTextTrack.cpp
@@ -55,9 +55,9 @@
 
 void LoadableTextTrack::addRegions(
     const HeapVector<Member<VTTRegion>>& newRegions) {
-  for (size_t i = 0; i < newRegions.size(); ++i) {
-    newRegions[i]->setTrack(this);
-    regions()->add(newRegions[i]);
+  for (const auto& region : newRegions) {
+    region->setTrack(this);
+    regions()->add(region);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackContainer.cpp b/third_party/WebKit/Source/core/html/track/TextTrackContainer.cpp
index 93053b6..f5bf1fc 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrackContainer.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrackContainer.cpp
@@ -109,8 +109,8 @@
   // corresponding CSS boxes added to output, in text track cue order, run the
   // following substeps:
   double movieTime = video.currentTime();
-  for (size_t i = 0; i < activeCues.size(); ++i) {
-    TextTrackCue* cue = activeCues[i].data();
+  for (const auto& activeCue : activeCues) {
+    TextTrackCue* cue = activeCue.data();
 
     DCHECK(cue->isActive());
     if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp b/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
index 873258cd..97c8269a 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
@@ -43,9 +43,9 @@
 }
 
 TextTrackCue* TextTrackCueList::getCueById(const AtomicString& id) const {
-  for (size_t i = 0; i < m_list.size(); ++i) {
-    if (m_list[i]->id() == id)
-      return m_list[i].get();
+  for (const auto& cue : m_list) {
+    if (cue->id() == id)
+      return cue.get();
   }
   return nullptr;
 }
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
index 4b2b023..05f5292 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
@@ -67,29 +67,29 @@
   // track."
   int trackIndex = 0;
 
-  for (size_t i = 0; i < m_elementTracks.size(); ++i) {
-    if (!m_elementTracks[i]->isRendered())
+  for (const auto& track : m_elementTracks) {
+    if (!track->isRendered())
       continue;
 
-    if (m_elementTracks[i] == textTrack)
+    if (track == textTrack)
       return trackIndex;
     ++trackIndex;
   }
 
-  for (size_t i = 0; i < m_addTrackTracks.size(); ++i) {
-    if (!m_addTrackTracks[i]->isRendered())
+  for (const auto& track : m_addTrackTracks) {
+    if (!track->isRendered())
       continue;
 
-    if (m_addTrackTracks[i] == textTrack)
+    if (track == textTrack)
       return trackIndex;
     ++trackIndex;
   }
 
-  for (size_t i = 0; i < m_inbandTracks.size(); ++i) {
-    if (!m_inbandTracks[i]->isRendered())
+  for (const auto& track : m_inbandTracks) {
+    if (!track->isRendered())
       continue;
 
-    if (m_inbandTracks[i] == textTrack)
+    if (track == textTrack)
       return trackIndex;
     ++trackIndex;
   }
@@ -144,14 +144,14 @@
 
   if (track->trackType() == TextTrack::TrackElement) {
     tracks = &m_elementTracks;
-    for (size_t i = 0; i < m_addTrackTracks.size(); ++i)
-      m_addTrackTracks[i]->invalidateTrackIndex();
-    for (size_t i = 0; i < m_inbandTracks.size(); ++i)
-      m_inbandTracks[i]->invalidateTrackIndex();
+    for (const auto& addTrack : m_addTrackTracks)
+      addTrack->invalidateTrackIndex();
+    for (const auto& inbandTrack : m_inbandTracks)
+      inbandTrack->invalidateTrackIndex();
   } else if (track->trackType() == TextTrack::AddTrack) {
     tracks = &m_addTrackTracks;
-    for (size_t i = 0; i < m_inbandTracks.size(); ++i)
-      m_inbandTracks[i]->invalidateTrackIndex();
+    for (const auto& inbandTrack : m_inbandTracks)
+      inbandTrack->invalidateTrackIndex();
   } else if (track->trackType() == TextTrack::InBand) {
     tracks = &m_inbandTracks;
   } else {
@@ -215,8 +215,8 @@
 }
 
 void TextTrackList::removeAllInbandTracks() {
-  for (unsigned i = 0; i < m_inbandTracks.size(); ++i) {
-    m_inbandTracks[i]->setTrackList(0);
+  for (const auto& track : m_inbandTracks) {
+    track->setTrackList(0);
   }
   m_inbandTracks.clear();
 }
diff --git a/third_party/WebKit/Source/core/html/track/TrackListBase.h b/third_party/WebKit/Source/core/html/track/TrackListBase.h
index 57e6e401..05bb6cf 100644
--- a/third_party/WebKit/Source/core/html/track/TrackListBase.h
+++ b/third_party/WebKit/Source/core/html/track/TrackListBase.h
@@ -29,9 +29,9 @@
   }
 
   T* getTrackById(const String& id) const {
-    for (unsigned i = 0; i < m_tracks.size(); ++i) {
-      if (String(m_tracks[i]->id()) == id)
-        return m_tracks[i].get();
+    for (const auto& track : m_tracks) {
+      if (String(track->id()) == id)
+        return track.get();
     }
 
     return nullptr;
@@ -69,8 +69,8 @@
   }
 
   void removeAll() {
-    for (unsigned i = 0; i < m_tracks.size(); ++i)
-      m_tracks[i]->setMediaElement(0);
+    for (const auto& track : m_tracks)
+      track->setMediaElement(0);
 
     m_tracks.clear();
   }
diff --git a/third_party/WebKit/Source/core/html/track/vtt/VTTRegionList.cpp b/third_party/WebKit/Source/core/html/track/vtt/VTTRegionList.cpp
index e6f9904..d0671f0 100644
--- a/third_party/WebKit/Source/core/html/track/vtt/VTTRegionList.cpp
+++ b/third_party/WebKit/Source/core/html/track/vtt/VTTRegionList.cpp
@@ -44,9 +44,9 @@
   if (id.isEmpty())
     return nullptr;
 
-  for (size_t i = 0; i < m_list.size(); ++i) {
-    if (m_list[i]->id() == id)
-      return m_list[i].get();
+  for (const auto& region : m_list) {
+    if (region->id() == id)
+      return region.get();
   }
 
   return nullptr;
diff --git a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
index f053a33e..0e3afe2 100644
--- a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
+++ b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
@@ -279,17 +279,15 @@
   ImageDecoder::AlphaOption alphaOp = ImageDecoder::AlphaPremultiplied;
   if (premultiplyAlphaOption == "none")
     alphaOp = ImageDecoder::AlphaNotPremultiplied;
-  ImageDecoder::ColorSpaceOption colorSpaceOp =
-      ImageDecoder::ColorSpaceTransformed;
+  bool ignoreColorSpace = false;
   if (colorSpaceConversionOption == "none")
-    colorSpaceOp = ImageDecoder::ColorSpaceIgnored;
+    ignoreColorSpace = true;
   std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
       SegmentReader::createFromSkData(SkData::MakeWithoutCopy(
           arrayBuffer->data(), arrayBuffer->byteLength())),
-      true, alphaOp, colorSpaceOp,
-      colorSpaceOp == ImageDecoder::ColorSpaceTransformed
-          ? ImageDecoder::globalTargetColorSpace()
-          : nullptr));
+      true, alphaOp,
+      ignoreColorSpace ? ColorBehavior::ignore()
+                       : ColorBehavior::transformToGlobalTarget()));
   sk_sp<SkImage> frame;
   if (decoder) {
     frame = ImageBitmap::getSkImageFromDecoder(std::move(decoder));
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.cpp b/third_party/WebKit/Source/core/input/ScrollManager.cpp
index 857bfe9a..925cd2a9 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.cpp
+++ b/third_party/WebKit/Source/core/input/ScrollManager.cpp
@@ -374,8 +374,7 @@
   if (!m_frame->document())
     return false;
 
-  return m_frame->document()->rootScrollerController()->scrollsViewport(
-      element);
+  return m_frame->document()->rootScrollerController().scrollsViewport(element);
 }
 
 WebInputEventResult ScrollManager::handleGestureScrollEvent(
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl
index dd80a97..768c6b0 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl
@@ -165,7 +165,10 @@
     void didReceiveResourceResponse([Keep] LocalFrame*, unsigned long identifier, DocumentLoader*, const ResourceResponse&, Resource*);
 
     [Network]
-    void didReceiveData([Keep] LocalFrame*, unsigned long identifier, const char* data, int dataLength, int encodedDataLength);
+    void didReceiveData([Keep] LocalFrame*, unsigned long identifier, const char* data, int dataLength);
+
+    [Network]
+    void didReceiveEncodedDataLength([Keep] LocalFrame*, unsigned long identifier, int encodedDataLength);
 
     [Network]
     void didFinishLoading(LocalFrame* frame, unsigned long identifier, double finishTime, int64_t encodedDataLength);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
index 89b7515..66d67d1e 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.cpp
@@ -246,19 +246,19 @@
 
 String buildBlockedReason(ResourceRequestBlockedReason reason) {
   switch (reason) {
-    case ResourceRequestBlockedReasonCSP:
+    case ResourceRequestBlockedReason::CSP:
       return protocol::Network::BlockedReasonEnum::Csp;
-    case ResourceRequestBlockedReasonMixedContent:
+    case ResourceRequestBlockedReason::MixedContent:
       return protocol::Network::BlockedReasonEnum::MixedContent;
-    case ResourceRequestBlockedReasonOrigin:
+    case ResourceRequestBlockedReason::Origin:
       return protocol::Network::BlockedReasonEnum::Origin;
-    case ResourceRequestBlockedReasonInspector:
+    case ResourceRequestBlockedReason::Inspector:
       return protocol::Network::BlockedReasonEnum::Inspector;
-    case ResourceRequestBlockedReasonSubresourceFilter:
+    case ResourceRequestBlockedReason::SubresourceFilter:
       return protocol::Network::BlockedReasonEnum::SubresourceFilter;
-    case ResourceRequestBlockedReasonOther:
+    case ResourceRequestBlockedReason::Other:
       return protocol::Network::BlockedReasonEnum::Other;
-    case ResourceRequestBlockedReasonNone:
+    case ResourceRequestBlockedReason::None:
     default:
       NOTREACHED();
       return protocol::Network::BlockedReasonEnum::Other;
@@ -725,7 +725,7 @@
   // following didReceiveResponse as there will be no calls to didReceiveData
   // from the network stack.
   if (isNotModified && cachedResource && cachedResource->encodedSize())
-    didReceiveData(frame, identifier, 0, cachedResource->encodedSize(), 0);
+    didReceiveData(frame, identifier, 0, cachedResource->encodedSize());
 }
 
 static bool isErrorStatusCode(int statusCode) {
@@ -735,8 +735,7 @@
 void InspectorNetworkAgent::didReceiveData(LocalFrame*,
                                            unsigned long identifier,
                                            const char* data,
-                                           int dataLength,
-                                           int encodedDataLength) {
+                                           int dataLength) {
   String requestId = IdentifiersFactory::requestId(identifier);
 
   if (data) {
@@ -750,8 +749,17 @@
       m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
   }
 
-  frontend()->dataReceived(requestId, monotonicallyIncreasingTime(), dataLength,
-                           encodedDataLength);
+  frontend()->dataReceived(
+      requestId, monotonicallyIncreasingTime(), dataLength,
+      m_resourcesData->getAndClearPendingEncodedDataLength(requestId));
+}
+
+void InspectorNetworkAgent::didReceiveEncodedDataLength(
+    LocalFrame*,
+    unsigned long identifier,
+    int encodedDataLength) {
+  String requestId = IdentifiersFactory::requestId(identifier);
+  m_resourcesData->addPendingEncodedDataLength(requestId, encodedDataLength);
 }
 
 void InspectorNetworkAgent::didFinishLoading(unsigned long identifier,
@@ -760,6 +768,14 @@
   String requestId = IdentifiersFactory::requestId(identifier);
   NetworkResourcesData::ResourceData const* resourceData =
       m_resourcesData->data(requestId);
+
+  int pendingEncodedDataLength =
+      m_resourcesData->getAndClearPendingEncodedDataLength(requestId);
+  if (pendingEncodedDataLength > 0) {
+    frontend()->dataReceived(requestId, monotonicallyIncreasingTime(), 0,
+                             pendingEncodedDataLength);
+  }
+
   if (resourceData &&
       (!resourceData->cachedResource() ||
        resourceData->cachedResource()->getDataBufferingPolicy() ==
@@ -1311,7 +1327,7 @@
     return Response::Error("Given id does not correspond to XHR");
 
   ExecutionContext* executionContext = xhrReplayData->getExecutionContext();
-  if (!executionContext) {
+  if (executionContext->isContextDestroyed()) {
     m_resourcesData->setXHRReplayData(requestId, 0);
     return Response::Error("Document is already detached");
   }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
index 59ce9b5..6e443845 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorNetworkAgent.h
@@ -97,8 +97,10 @@
   void didReceiveData(LocalFrame*,
                       unsigned long identifier,
                       const char* data,
-                      int dataLength,
-                      int encodedDataLength);
+                      int dataLength);
+  void didReceiveEncodedDataLength(LocalFrame*,
+                                   unsigned long identifier,
+                                   int encodedDataLength);
   void didFinishLoading(unsigned long identifier,
                         double monotonicFinishTime,
                         int64_t encodedDataLength);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
index 24f73f5..0bc5741 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorPageAgent.cpp
@@ -60,11 +60,10 @@
 #include "core/loader/DocumentLoader.h"
 #include "core/loader/FrameLoader.h"
 #include "core/loader/resource/CSSStyleSheetResource.h"
-#include "core/loader/resource/FontResource.h"
 #include "core/loader/resource/ScriptResource.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/PlatformResourceLoader.h"
 #include "platform/UserGestureIndicator.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "wtf/CurrentTime.h"
 #include "wtf/ListHashSet.h"
diff --git a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
index 4288635a..e1fa0e5 100644
--- a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
+++ b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.cpp
@@ -61,7 +61,7 @@
                              bool async,
                              PassRefPtr<EncodedFormData> formData,
                              bool includeCredentials)
-    : ContextLifecycleObserver(executionContext),
+    : m_executionContext(executionContext),
       m_method(method),
       m_url(url),
       m_async(async),
@@ -69,7 +69,7 @@
       m_includeCredentials(includeCredentials) {}
 
 DEFINE_TRACE(XHRReplayData) {
-  ContextLifecycleObserver::trace(visitor);
+  visitor->trace(m_executionContext);
 }
 
 // ResourceData
@@ -87,6 +87,7 @@
       m_type(InspectorPageAgent::OtherResource),
       m_httpStatusCode(0),
       m_rawHeaderSize(0),
+      m_pendingEncodedDataLength(0),
       m_cachedResource(nullptr) {}
 
 DEFINE_TRACE(NetworkResourcesData::ResourceData) {
@@ -354,6 +355,26 @@
   return result;
 }
 
+int NetworkResourcesData::getAndClearPendingEncodedDataLength(
+    const String& requestId) {
+  ResourceData* resourceData = resourceDataForRequestId(requestId);
+  if (!resourceData)
+    return 0;
+
+  int pendingEncodedDataLength = resourceData->pendingEncodedDataLength();
+  resourceData->clearPendingEncodedDataLength();
+  return pendingEncodedDataLength;
+}
+
+void NetworkResourcesData::addPendingEncodedDataLength(const String& requestId,
+                                                       int encodedDataLength) {
+  ResourceData* resourceData = resourceDataForRequestId(requestId);
+  if (!resourceData)
+    return;
+
+  resourceData->addPendingEncodedDataLength(encodedDataLength);
+}
+
 void NetworkResourcesData::clear(const String& preservedLoaderId) {
   if (!m_requestIdToResourceDataMap.size())
     return;
diff --git a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.h b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.h
index d516266..fd52e0a 100644
--- a/third_party/WebKit/Source/core/inspector/NetworkResourcesData.h
+++ b/third_party/WebKit/Source/core/inspector/NetworkResourcesData.h
@@ -29,7 +29,6 @@
 #ifndef NetworkResourcesData_h
 #define NetworkResourcesData_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/html/parser/TextResourceDecoder.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "platform/blob/BlobData.h"
@@ -50,10 +49,7 @@
 class SharedBuffer;
 class TextResourceDecoder;
 
-class XHRReplayData final : public GarbageCollectedFinalized<XHRReplayData>,
-                            public ContextLifecycleObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(XHRReplayData);
-
+class XHRReplayData final : public GarbageCollectedFinalized<XHRReplayData> {
  public:
   static XHRReplayData* create(ExecutionContext*,
                                const AtomicString& method,
@@ -69,6 +65,7 @@
   PassRefPtr<EncodedFormData> formData() const { return m_formData; }
   const HTTPHeaderMap& headers() const { return m_headers; }
   bool includeCredentials() const { return m_includeCredentials; }
+  ExecutionContext* getExecutionContext() const { return m_executionContext; }
 
   DECLARE_VIRTUAL_TRACE();
 
@@ -80,6 +77,7 @@
                 PassRefPtr<EncodedFormData>,
                 bool includeCredentials);
 
+  Member<ExecutionContext> m_executionContext;
   AtomicString m_method;
   KURL m_url;
   bool m_async;
@@ -159,6 +157,11 @@
     void setCertificate(const Vector<AtomicString>& certificate) {
       m_certificate = certificate;
     }
+    int pendingEncodedDataLength() const { return m_pendingEncodedDataLength; }
+    void clearPendingEncodedDataLength() { m_pendingEncodedDataLength = 0; }
+    void addPendingEncodedDataLength(int encodedDataLength) {
+      m_pendingEncodedDataLength += encodedDataLength;
+    }
 
     DECLARE_TRACE();
 
@@ -185,6 +188,7 @@
     String m_mimeType;
     String m_textEncodingName;
     int m_rawHeaderSize;
+    int m_pendingEncodedDataLength;
 
     RefPtr<SharedBuffer> m_buffer;
     WeakMember<Resource> m_cachedResource;
@@ -226,6 +230,10 @@
                       const Vector<AtomicString>& certificate);
   HeapVector<Member<ResourceData>> resources();
 
+  int getAndClearPendingEncodedDataLength(const String& requestId);
+  void addPendingEncodedDataLength(const String& requestId,
+                                   int encodedDataLength);
+
   DECLARE_TRACE();
 
  private:
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
index f114f6e..ece99a8 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
@@ -256,6 +256,11 @@
     if (!shouldClipOverflow)
       getScrollableArea()->invalidateAllStickyConstraints();
     setMayNeedPaintInvalidationSubtree();
+    if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+      // The overflow clip paint property depends on whether overflow clip is
+      // present so we need to update paint properties if this changes.
+      setNeedsPaintPropertyUpdate();
+    }
   }
   setHasOverflowClip(shouldClipOverflow);
 }
@@ -357,9 +362,6 @@
   if (child->continuation())
     return;
 
-  if (isFieldset())
-    return;
-
   // Promote all the leftover anonymous block's children (to become children of
   // this block instead). We still want to keep the leftover block in the tree
   // for a moment, for notification purposes done further below (flow threads
@@ -1985,9 +1987,6 @@
   if (display == EDisplay::Flex || display == EDisplay::InlineFlex) {
     newBox = LayoutFlexibleBox::createAnonymous(&parent->document());
     newDisplay = EDisplay::Flex;
-  } else if (display == EDisplay::Grid || display == EDisplay::InlineGrid) {
-    newBox = LayoutGrid::createAnonymous(&parent->document());
-    newDisplay = EDisplay::Grid;
   } else {
     newBox = LayoutBlockFlow::createAnonymous(&parent->document());
     newDisplay = EDisplay::Block;
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.h b/third_party/WebKit/Source/core/layout/LayoutBlock.h
index 47922010..82a3d60 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlock.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBlock.h
@@ -507,10 +507,6 @@
   bool widthAvailableToChildrenHasChanged();
 
  protected:
-  bool isPageLogicalHeightKnown() const {
-    return pageLogicalHeightForOffset(LayoutUnit());
-  }
-
   // Paginated content inside this block was laid out.
   // |logicalBottomOffsetAfterPagination| is the logical bottom offset of the
   // child content after applying any forced or unforced breaks as needed.
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
index 8d1bdb0..79350deb 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
@@ -1421,6 +1421,11 @@
   BlockChildrenLayoutInfo layoutInfo(this, beforeEdge, afterEdge);
   MarginInfo& marginInfo = layoutInfo.marginInfo();
 
+  // Fieldsets need to find their legend and position it inside the border of
+  // the object.
+  // The legend then gets skipped during normal layout. The same is true for
+  // ruby text.
+  // It doesn't get included in the normal layout process but is instead skipped
   LayoutObject* childToExclude =
       layoutSpecialExcludedChild(relayoutChildren, layoutScope);
 
@@ -1442,7 +1447,7 @@
 
     if (childToExclude == child)
       continue;  // Skip this child, since it will be positioned by the
-                 // specialized subclass (ruby runs).
+                 // specialized subclass (fieldsets and ruby runs).
 
     updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, *child);
 
@@ -1704,10 +1709,10 @@
 
   LayoutObject* prev = child.previousSibling();
   LayoutBlockFlow* previousBlockFlow =
-      prev && prev->isLayoutBlockFlow() ? toLayoutBlockFlow(prev) : nullptr;
-  bool previousBlockFlowCanSelfCollapse =
-      previousBlockFlow &&
-      !previousBlockFlow->isFloatingOrOutOfFlowPositioned();
+      prev && prev->isLayoutBlockFlow() &&
+              !prev->isFloatingOrOutOfFlowPositioned()
+          ? toLayoutBlockFlow(prev)
+          : 0;
   // If the child's previous sibling is a self-collapsing block that cleared a
   // float then its top border edge has been set at the bottom border edge of
   // the float. Since we want to collapse the child's top margin with the self-
@@ -1715,8 +1720,7 @@
   // height to match the margin top of the self-collapsing block. If the
   // resulting collapsed margin leaves the child still intruding into the float
   // then we will want to clear it.
-  if (!marginInfo.canCollapseWithMarginBefore() &&
-      previousBlockFlowCanSelfCollapse &&
+  if (!marginInfo.canCollapseWithMarginBefore() && previousBlockFlow &&
       marginInfo.lastChildIsSelfCollapsingBlockWithClearance())
     setLogicalHeight(
         logicalHeight() -
@@ -1797,26 +1801,18 @@
     setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop));
   }
 
-  // If |child| has moved up into previous siblings it needs to avoid or clear
-  // any floats they contain.
-  LayoutUnit oldLogicalHeight = logicalHeight();
-  setLogicalHeight(logicalTop);
-  while (previousBlockFlow) {
-    auto lowestFloat = previousBlockFlow->logicalTop() +
-                       previousBlockFlow->lowestFloatLogicalBottom();
-    if (lowestFloat > logicalTop)
+  if (previousBlockFlow) {
+    // If |child| is a self-collapsing block it may have collapsed into a
+    // previous sibling and although it hasn't reduced the height of the parent
+    // yet any floats from the parent will now overhang.
+    LayoutUnit oldLogicalHeight = logicalHeight();
+    setLogicalHeight(logicalTop);
+    if (!previousBlockFlow->avoidsFloats() &&
+        (previousBlockFlow->logicalTop() +
+         previousBlockFlow->lowestFloatLogicalBottom()) > logicalTop)
       addOverhangingFloats(previousBlockFlow, false);
-    else
-      break;
-    LayoutObject* prev = previousBlockFlow->previousSibling();
-    if (prev && prev->isLayoutBlockFlow())
-      previousBlockFlow = toLayoutBlockFlow(prev);
-    else
-      previousBlockFlow = nullptr;
-  }
-  setLogicalHeight(oldLogicalHeight);
+    setLogicalHeight(oldLogicalHeight);
 
-  if (previousBlockFlowCanSelfCollapse) {
     // If |child|'s previous sibling is or contains a self-collapsing block that
     // cleared a float and margin collapsing resulted in |child| moving up
     // into the margin area of the self-collapsing block then the float it
@@ -2964,7 +2960,7 @@
   LayoutBox::addChild(newChild, beforeChild);
 
   if (madeBoxesNonInline && parent() && isAnonymousBlock() &&
-      parent()->isLayoutBlock() && !parent()->createsAnonymousWrapper()) {
+      parent()->isLayoutBlock()) {
     toLayoutBlock(parent())->removeLeftoverAnonymousBlock(this);
     // |this| may be dead now.
   }
@@ -3636,6 +3632,11 @@
 LayoutUnit LayoutBlockFlow::positionAndLayoutFloat(
     FloatingObject& floatingObject,
     LayoutUnit logicalTopMarginEdge) {
+  // Once a float has been placed, we cannot update its position, or the float
+  // interval tree will be out of sync with reality. This may in turn lead to
+  // objects being used after they have been deleted.
+  CHECK(!floatingObject.isPlaced());
+
   LayoutBox& child = *floatingObject.layoutObject();
 
   // FIXME Investigate if this can be removed. crbug.com/370006
@@ -4243,6 +4244,12 @@
   if (isRuby())
     return;
 
+  // Fieldsets look for a legend special child (layoutSpecialExcludedChild()).
+  // We currently only support one special child per layout object, and the
+  // flow thread would make for a second one.
+  if (isFieldset())
+    return;
+
   // Form controls are replaced content, and are therefore not supposed to
   // support multicol.
   if (isFileUploadControl() || isTextControl() || isListBox())
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index fc48cab..5435f01 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -325,6 +325,15 @@
     updateScrollSnapMappingAfterStyleChange(&newStyle, oldStyle);
   }
 
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    if (hasOverflowClip() || styleRef().containsPaint() || hasControlClip()) {
+      // The overflow clip paint property depends on border sizes through
+      // overflowClipRect() so we update properties on border size changes.
+      if (oldStyle && !oldStyle->border().sizeEquals(newStyle.border()))
+        setNeedsPaintPropertyUpdate();
+    }
+  }
+
   // Non-atomic inlines should be LayoutInline or LayoutText, not LayoutBox.
   DCHECK(!isInline() || isAtomicInlineLevel());
 }
@@ -673,10 +682,10 @@
     }
   }
 
-  // If we are fixed-position and scroll with the viewport, it is useless to
+  // If we are fixed-position and stick to the viewport, it is useless to
   // scroll the parent.
-  if (style()->position() == FixedPosition && hasLayer() &&
-      layer()->scrollsWithViewport()) {
+  if (style()->position() == FixedPosition &&
+      containerForFixedPosition() == view()) {
     return;
   }
 
@@ -1027,7 +1036,7 @@
       !(layoutObject->isBox() && toLayoutBox(layoutObject)->canAutoscroll())) {
     // Do not start autoscroll when the node is inside a fixed-position element.
     if (layoutObject->isBox() && toLayoutBox(layoutObject)->hasLayer() &&
-        toLayoutBox(layoutObject)->layer()->scrollsWithViewport()) {
+        toLayoutBox(layoutObject)->layer()->sticksToViewport()) {
       return nullptr;
     }
 
@@ -1159,7 +1168,7 @@
 
 LayoutUnit LayoutBox::minPreferredLogicalWidth() const {
   if (preferredLogicalWidthsDirty()) {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
     SetLayoutNeededForbiddenScope layoutForbiddenScope(
         const_cast<LayoutBox&>(*this));
 #endif
@@ -1173,7 +1182,7 @@
 DISABLE_CFI_PERF
 LayoutUnit LayoutBox::maxPreferredLogicalWidth() const {
   if (preferredLogicalWidthsDirty()) {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
     SetLayoutNeededForbiddenScope layoutForbiddenScope(
         const_cast<LayoutBox&>(*this));
 #endif
@@ -1692,6 +1701,14 @@
   // Should check this object for paint invalidation.
   if (!needsLayout())
     setMayNeedPaintInvalidation();
+
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    // The overflow clip paint property depends on the border box rect through
+    // overflowClipRect(). The border box rect's size equals the frame rect's
+    // size, so we trigger a paint property update when the framerect changes.
+    if (hasOverflowClip() || styleRef().containsPaint() || hasControlClip())
+      setNeedsPaintPropertyUpdate();
+  }
 }
 
 bool LayoutBox::intersectsVisibleViewport() const {
@@ -5516,6 +5533,12 @@
       offset + offsetFromLogicalTopOfFirstPage());
 }
 
+bool LayoutBox::isPageLogicalHeightKnown() const {
+  if (const LayoutFlowThread* flowThread = flowThreadContainingBlock())
+    return flowThread->isPageLogicalHeightKnown();
+  return view()->pageLogicalHeight();
+}
+
 LayoutUnit LayoutBox::pageRemainingLogicalHeightForOffset(
     LayoutUnit offset,
     PageBoundaryRule pageBoundaryRule) const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index 2be975e..76046f4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -779,6 +779,7 @@
   // exactly at a page or column boundary.
   enum PageBoundaryRule { AssociateWithFormerPage, AssociateWithLatterPage };
   LayoutUnit pageLogicalHeightForOffset(LayoutUnit) const;
+  bool isPageLogicalHeightKnown() const;
   LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit,
                                                  PageBoundaryRule) const;
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h b/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
index ad9e858..a118552 100644
--- a/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
+++ b/third_party/WebKit/Source/core/layout/LayoutDetailsMarker.h
@@ -40,6 +40,9 @@
     return type == LayoutObjectDetailsMarker || LayoutBlockFlow::isOfType(type);
   }
   void paint(const PaintInfo&, const LayoutPoint&) const override;
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override {
+    return false;
+  }
 
   bool isOpen() const;
 };
diff --git a/third_party/WebKit/Source/core/layout/LayoutFieldset.cpp b/third_party/WebKit/Source/core/layout/LayoutFieldset.cpp
index d8d6192..c1a37a6d 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFieldset.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutFieldset.cpp
@@ -25,7 +25,6 @@
 
 #include "core/CSSPropertyNames.h"
 #include "core/HTMLNames.h"
-#include "core/dom/AXObjectCache.h"
 #include "core/html/HTMLLegendElement.h"
 #include "core/paint/FieldsetPainter.h"
 
@@ -35,87 +34,24 @@
 
 using namespace HTMLNames;
 
-namespace {
+LayoutFieldset::LayoutFieldset(Element* element) : LayoutBlockFlow(element) {}
 
-void setInnerBlockPadding(bool isHorizontalWritingMode,
-                          const LayoutObject* innerBlock,
-                          const LayoutUnit& padding) {
-  if (isHorizontalWritingMode)
-    innerBlock->mutableStyleRef().setPaddingTop(Length(padding, Fixed));
-  else
-    innerBlock->mutableStyleRef().setPaddingLeft(Length(padding, Fixed));
-}
+void LayoutFieldset::computePreferredLogicalWidths() {
+  LayoutBlockFlow::computePreferredLogicalWidths();
+  if (LayoutBox* legend = findInFlowLegend()) {
+    int legendMinWidth = legend->minPreferredLogicalWidth().toInt();
 
-void resetInnerBlockPadding(bool isHorizontalWritingMode,
-                            const LayoutObject* innerBlock) {
-  if (isHorizontalWritingMode)
-    innerBlock->mutableStyleRef().setPaddingTop(Length(0, Fixed));
-  else
-    innerBlock->mutableStyleRef().setPaddingLeft(Length(0, Fixed));
-}
+    Length legendMarginLeft = legend->style()->marginLeft();
+    Length legendMarginRight = legend->style()->marginRight();
 
-}  // namespace
+    if (legendMarginLeft.isFixed())
+      legendMinWidth += legendMarginLeft.value();
 
-LayoutFieldset::LayoutFieldset(Element* element)
-    : LayoutFlexibleBox(element), m_innerBlock(nullptr) {}
+    if (legendMarginRight.isFixed())
+      legendMinWidth += legendMarginRight.value();
 
-int LayoutFieldset::baselinePosition(FontBaseline baseline,
-                                     bool firstLine,
-                                     LineDirectionMode direction,
-                                     LinePositionMode position) const {
-  return LayoutBlock::baselinePosition(baseline, firstLine, direction,
-                                       position);
-}
-
-void LayoutFieldset::computeIntrinsicLogicalWidths(
-    LayoutUnit& minLogicalWidth,
-    LayoutUnit& maxLogicalWidth) const {
-  for (LayoutBox* child = firstChildBox(); child;
-       child = child->nextSiblingBox()) {
-    if (child->isOutOfFlowPositioned())
-      continue;
-
-    LayoutUnit margin = marginIntrinsicLogicalWidthForChild(*child);
-
-    LayoutUnit minPreferredLogicalWidth;
-    LayoutUnit maxPreferredLogicalWidth;
-    computeChildPreferredLogicalWidths(*child, minPreferredLogicalWidth,
-                                       maxPreferredLogicalWidth);
-    DCHECK_GE(minPreferredLogicalWidth, LayoutUnit());
-    DCHECK_GE(maxPreferredLogicalWidth, LayoutUnit());
-    minPreferredLogicalWidth += margin;
-    maxPreferredLogicalWidth += margin;
-    minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth);
-    maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth);
-  }
-
-  maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
-
-  // Due to negative margins, it is possible that we calculated a negative
-  // intrinsic width. Make sure that we never return a negative width.
-  minLogicalWidth = std::max(LayoutUnit(), minLogicalWidth);
-  maxLogicalWidth = std::max(LayoutUnit(), maxLogicalWidth);
-
-  LayoutUnit scrollbarWidth(scrollbarLogicalWidth());
-  maxLogicalWidth += scrollbarWidth;
-  minLogicalWidth += scrollbarWidth;
-}
-
-void LayoutFieldset::setLogicalLeftForChild(LayoutBox& child,
-                                            LayoutUnit logicalLeft) {
-  if (isHorizontalWritingMode()) {
-    child.setX(logicalLeft);
-  } else {
-    child.setY(logicalLeft);
-  }
-}
-
-void LayoutFieldset::setLogicalTopForChild(LayoutBox& child,
-                                           LayoutUnit logicalTop) {
-  if (isHorizontalWritingMode()) {
-    child.setY(logicalTop);
-  } else {
-    child.setX(logicalTop);
+    m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth,
+                                     legendMinWidth + borderAndPaddingWidth());
   }
 }
 
@@ -173,33 +109,22 @@
 
     LayoutUnit legendLogicalTop;
     LayoutUnit collapsedLegendExtent;
-    LayoutUnit innerBlockPadding;
-
-    if (legendLogicalHeight < fieldsetBorderBefore) {
-      // Center legend in fieldset border
+    // FIXME: We need to account for the legend's margin before too.
+    if (fieldsetBorderBefore > legendLogicalHeight) {
+      // The <legend> is smaller than the associated fieldset before border
+      // so the latter determines positioning of the <legend>. The sizing
+      // depends
+      // on the legend's margins as we want to still follow the author's cues.
+      // Firefox completely ignores the margins in this case which seems wrong.
       legendLogicalTop = (fieldsetBorderBefore - legendLogicalHeight) / 2;
+      collapsedLegendExtent = max<LayoutUnit>(
+          fieldsetBorderBefore, legendLogicalTop + legendLogicalHeight +
+                                    marginAfterForChild(*legend));
+    } else {
+      collapsedLegendExtent =
+          legendLogicalHeight + marginAfterForChild(*legend);
     }
 
-    // Calculate how much legend + bottom margin sticks below the fieldset
-    // border
-    innerBlockPadding = (legendLogicalTop + legendLogicalHeight +
-                         marginAfterForChild(*legend) - fieldsetBorderBefore)
-                            .clampNegativeToZero();
-
-    if (legendLogicalTop < marginBeforeForChild(*legend)) {
-      // legend margin pushes everything down
-      innerBlockPadding += marginBeforeForChild(*legend) - legendLogicalTop;
-      legendLogicalTop = marginBeforeForChild(*legend);
-    }
-
-    collapsedLegendExtent =
-        std::max(fieldsetBorderBefore, marginBeforeForChild(*legend) +
-                                           legendLogicalHeight +
-                                           marginAfterForChild(*legend));
-
-    if (m_innerBlock)
-      setInnerBlockPadding(isHorizontalWritingMode(), m_innerBlock,
-                           innerBlockPadding);
     setLogicalTopForChild(*legend, legendLogicalTop);
     setLogicalHeight(paddingBefore() + collapsedLegendExtent);
 
@@ -207,9 +132,6 @@
       // We need to invalidate the fieldset border if the legend's frame
       // changed.
       setShouldDoFullPaintInvalidation();
-      if (m_innerBlock)
-        m_innerBlock->setNeedsLayout(LayoutInvalidationReason::FieldsetChanged,
-                                     MarkOnlyThis);
     }
   }
   return legend;
@@ -238,75 +160,4 @@
   FieldsetPainter(*this).paintMask(paintInfo, paintOffset);
 }
 
-void LayoutFieldset::updateAnonymousChildStyle(
-    const LayoutObject& child,
-    ComputedStyle& childStyle) const {
-  childStyle.setFlexShrink(1.0f);
-  childStyle.setFlexGrow(1.0f);
-  // min-width: 0; is needed for correct shrinking.
-  childStyle.setMinWidth(Length(0, Fixed));
-  childStyle.setFlexDirection(style()->flexDirection());
-  childStyle.setJustifyContent(style()->justifyContent());
-  childStyle.setFlexWrap(style()->flexWrap());
-  childStyle.setAlignItems(style()->alignItems());
-  childStyle.setAlignContent(style()->alignContent());
-  // Let anonymous block to be the 1st for correct layout positioning.
-  childStyle.setOrder(1);
-}
-
-void LayoutFieldset::addChild(LayoutObject* newChild,
-                              LayoutObject* beforeChild) {
-  if (!m_innerBlock)
-    createInnerBlock();
-
-  if (isHTMLLegendElement(newChild->node())) {
-    // Flexbox forces the floating legend to be non-floating => add the floating
-    // legend to the inner block.
-    if (newChild->isFloatingOrOutOfFlowPositioned()) {
-      // Always add the legend first, i.e. before m_innerBlock->firstChild()
-      m_innerBlock->addChild(newChild, m_innerBlock->firstChild());
-    } else {
-      // Let legend block to be the 2nd for correct layout positioning.
-      newChild->mutableStyle()->setOrder(2);
-      LayoutFlexibleBox::addChild(newChild, m_innerBlock);
-    }
-  } else {
-    if (beforeChild && isHTMLLegendElement(beforeChild->node())) {
-      m_innerBlock->addChild(newChild);
-    } else {
-      m_innerBlock->addChild(newChild, beforeChild);
-    }
-    if (AXObjectCache* cache = document().existingAXObjectCache())
-      cache->childrenChanged(this);
-  }
-}
-
-void LayoutFieldset::createInnerBlock() {
-  if (m_innerBlock) {
-    DCHECK(firstChild() == m_innerBlock);
-    return;
-  }
-  m_innerBlock = createAnonymousBlock(style()->display());
-  LayoutFlexibleBox::addChild(m_innerBlock);
-}
-
-void LayoutFieldset::removeChild(LayoutObject* oldChild) {
-  if (isHTMLLegendElement(oldChild->node())) {
-    LayoutFlexibleBox::removeChild(oldChild);
-    if (m_innerBlock) {
-      resetInnerBlockPadding(isHorizontalWritingMode(), m_innerBlock);
-      m_innerBlock->setNeedsLayout(LayoutInvalidationReason::FieldsetChanged,
-                                   MarkOnlyThis);
-    }
-    setShouldDoFullPaintInvalidation();
-  } else if (oldChild == m_innerBlock) {
-    LayoutFlexibleBox::removeChild(oldChild);
-    m_innerBlock = nullptr;
-  } else if (oldChild->parent() == this) {
-    LayoutFlexibleBox::removeChild(oldChild);
-  } else if (m_innerBlock) {
-    m_innerBlock->removeChild(oldChild);
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutFieldset.h b/third_party/WebKit/Source/core/layout/LayoutFieldset.h
index ceff1d4b..2e3f35a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFieldset.h
+++ b/third_party/WebKit/Source/core/layout/LayoutFieldset.h
@@ -24,11 +24,11 @@
 #ifndef LayoutFieldset_h
 #define LayoutFieldset_h
 
-#include "core/layout/LayoutFlexibleBox.h"
+#include "core/layout/LayoutBlockFlow.h"
 
 namespace blink {
 
-class LayoutFieldset final : public LayoutFlexibleBox {
+class LayoutFieldset final : public LayoutBlockFlow {
  public:
   explicit LayoutFieldset(Element*);
 
@@ -37,39 +37,19 @@
   const char* name() const override { return "LayoutFieldset"; }
 
  private:
-  void addChild(LayoutObject* newChild,
-                LayoutObject* beforeChild = nullptr) override;
-  bool avoidsFloats() const override { return true; }
-
-  // We override the two baseline functions because we want our baseline to be
-  // the bottom of our margin box.
-  int baselinePosition(FontBaseline,
-                       bool firstLine,
-                       LineDirectionMode,
-                       LinePositionMode) const override;
-  int inlineBlockBaseline(LineDirectionMode) const override { return -1; }
-
-  void computeIntrinsicLogicalWidths(
-      LayoutUnit& minLogicalWidth,
-      LayoutUnit& maxLogicalWidth) const override;
-  bool createsAnonymousWrapper() const override { return true; }
   bool isOfType(LayoutObjectType type) const override {
-    return type == LayoutObjectFieldset || LayoutFlexibleBox::isOfType(type);
+    return type == LayoutObjectFieldset || LayoutBlockFlow::isOfType(type);
   }
+
   LayoutObject* layoutSpecialExcludedChild(bool relayoutChildren,
                                            SubtreeLayoutScope&) override;
+
+  void computePreferredLogicalWidths() override;
+  bool avoidsFloats() const override { return true; }
+
   void paintBoxDecorationBackground(const PaintInfo&,
                                     const LayoutPoint&) const override;
   void paintMask(const PaintInfo&, const LayoutPoint&) const override;
-  void updateAnonymousChildStyle(const LayoutObject& child,
-                                 ComputedStyle& childStyle) const override;
-
-  void createInnerBlock();
-  void setLogicalLeftForChild(LayoutBox& child, LayoutUnit logicalLeft);
-  void setLogicalTopForChild(LayoutBox& child, LayoutUnit logicalTop);
-  void removeChild(LayoutObject*) override;
-
-  LayoutBlock* m_innerBlock;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutFieldset, isFieldset());
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
index 10507de2..fc80cc4 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.cpp
@@ -937,11 +937,6 @@
   PaintLayerScrollableArea::PreventRelayoutScope preventRelayoutScope(
       layoutScope);
 
-  // Fieldsets need to find their legend and position it inside the border of
-  // the object. The legend then gets skipped during normal layout. It
-  // doesn't get included in the normal layout process but is instead skipped.
-  LayoutObject* childToExclude =
-      layoutSpecialExcludedChild(relayoutChildren, layoutScope);
 
   // Set up our master list of flex items. All of the rest of the algorithm
   // should work off this list of a subset.
@@ -951,9 +946,6 @@
   m_orderIterator.first();
   for (LayoutBox* child = m_orderIterator.currentChild(); child;
        child = m_orderIterator.next()) {
-    if (childToExclude == child)
-      continue;  // Skip this child, since it will be positioned by the
-                 // specialized subclass (fieldsets runs).
 
     if (child->isOutOfFlowPositioned()) {
       // Out-of-flow children are not flex items, so we skip them here.
diff --git a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h
index 4140b4b..396a526 100644
--- a/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutFlexibleBox.h
@@ -65,10 +65,6 @@
   void paintChildren(const PaintInfo&, const LayoutPoint&) const final;
 
   bool isHorizontalFlow() const;
-  virtual LayoutObject* layoutSpecialExcludedChild(bool relayoutChildren,
-                                                   SubtreeLayoutScope&) {
-    return nullptr;
-  }
 
   const OrderIterator& orderIterator() const { return m_orderIterator; }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
index 39220d7..c110f78 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
@@ -164,7 +164,14 @@
   m_hasAnyOrthogonalGridItem = hasAnyOrthogonalGridItem;
 }
 
-void LayoutGrid::Grid::clear() {
+void LayoutGrid::Grid::setNeedsItemsPlacement(bool needsItemsPlacement) {
+  m_needsItemsPlacement = needsItemsPlacement;
+
+  if (!needsItemsPlacement) {
+    m_grid.shrinkToFit();
+    return;
+  }
+
   m_grid.resize(0);
   m_gridItemArea.clear();
   m_gridItemsIndexesMap.clear();
@@ -462,8 +469,7 @@
   Vector<GridItemWithSpan>::iterator rangeEnd;
 };
 
-LayoutGrid::LayoutGrid(Element* element)
-    : LayoutBlock(element), m_grid(this), m_gridIsDirty(true) {
+LayoutGrid::LayoutGrid(Element* element) : LayoutBlock(element), m_grid(this) {
   ASSERT(!childrenInline());
   if (!isAnonymous())
     UseCounter::count(document(), UseCounter::CSSGridLayout);
@@ -594,7 +600,8 @@
 
   // We cannot perform a simplifiedLayout() on a dirty grid that
   // has positioned items to be laid out.
-  if (!relayoutChildren && (!m_gridIsDirty || !posChildNeedsLayout()) &&
+  if (!relayoutChildren &&
+      (!m_grid.needsItemsPlacement() || !posChildNeedsLayout()) &&
       simplifiedLayout())
     return;
 
@@ -637,7 +644,8 @@
       dirtyGrid();
     placeItemsOnGrid(m_grid, TrackSizing);
 
-    GridSizingData sizingData(numTracks(ForColumns), numTracks(ForRows));
+    GridSizingData sizingData(numTracks(ForColumns, m_grid),
+                              numTracks(ForRows, m_grid));
 
     // 1- First, the track sizing algorithm is used to resolve the sizes of the
     // grid columns.
@@ -796,7 +804,8 @@
   const_cast<LayoutGrid*>(this)->placeItemsOnGrid(const_cast<Grid&>(m_grid),
                                                   IntrinsicSizeComputation);
 
-  GridSizingData sizingData(numTracks(ForColumns), numTracks(ForRows));
+  GridSizingData sizingData(numTracks(ForColumns, m_grid),
+                            numTracks(ForRows, m_grid));
   computeTrackSizesForIndefiniteSize(ForColumns, sizingData, minLogicalWidth,
                                      maxLogicalWidth);
 
@@ -2019,6 +2028,7 @@
 
 std::unique_ptr<LayoutGrid::OrderedTrackIndexSet>
 LayoutGrid::computeEmptyTracksForAutoRepeat(
+    Grid& grid,
     GridTrackSizingDirection direction) const {
   bool isRowAxis = direction == ForColumns;
   if ((isRowAxis && styleRef().gridAutoRepeatColumnsType() != AutoFit) ||
@@ -2030,9 +2040,9 @@
                               ? styleRef().gridAutoRepeatColumnsInsertionPoint()
                               : styleRef().gridAutoRepeatRowsInsertionPoint();
   size_t firstAutoRepeatTrack =
-      insertionPoint + std::abs(m_grid.smallestTrackStart(direction));
+      insertionPoint + std::abs(grid.smallestTrackStart(direction));
   size_t lastAutoRepeatTrack =
-      firstAutoRepeatTrack + autoRepeatCountForDirection(direction);
+      firstAutoRepeatTrack + grid.autoRepeatTracks(direction);
 
   if (!m_grid.hasGridItems()) {
     emptyTrackIndexes = wrapUnique(new OrderedTrackIndexSet);
@@ -2042,7 +2052,7 @@
   } else {
     for (size_t trackIndex = firstAutoRepeatTrack;
          trackIndex < lastAutoRepeatTrack; ++trackIndex) {
-      GridIterator iterator(m_grid, direction, trackIndex);
+      GridIterator iterator(grid, direction, trackIndex);
       if (!iterator.nextGridItem()) {
         if (!emptyTrackIndexes)
           emptyTrackIndexes = wrapUnique(new OrderedTrackIndexSet);
@@ -2055,7 +2065,7 @@
 
 void LayoutGrid::placeItemsOnGrid(LayoutGrid::Grid& grid,
                                   SizingOperation sizingOperation) {
-  if (!m_gridIsDirty)
+  if (!grid.needsItemsPlacement())
     return;
 
   DCHECK(!grid.hasGridItems());
@@ -2073,16 +2083,13 @@
 
   populateExplicitGridAndOrderIterator(grid);
 
-  // We clear the dirty bit here as the grid sizes have been updated.
-  m_gridIsDirty = false;
-  bool hasAnyOrthogonalGridItem = false;
-
   Vector<LayoutBox*> autoMajorAxisAutoGridItems;
   Vector<LayoutBox*> specifiedMajorAxisAutoGridItems;
 #if ENABLE(ASSERT)
   DCHECK(!grid.hasAnyGridItemPaintOrder());
 #endif
   DCHECK(!grid.hasAnyOrthogonalGridItem());
+  bool hasAnyOrthogonalGridItem = false;
   size_t childIndex = 0;
   for (LayoutBox* child = grid.orderIterator().first(); child;
        child = grid.orderIterator().next()) {
@@ -2112,7 +2119,7 @@
     }
     grid.insert(*child, area);
   }
-  m_grid.setHasAnyOrthogonalGridItem(hasAnyOrthogonalGridItem);
+  grid.setHasAnyOrthogonalGridItem(hasAnyOrthogonalGridItem);
 
 #if ENABLE(ASSERT)
   if (grid.hasGridItems()) {
@@ -2128,11 +2135,12 @@
   placeSpecifiedMajorAxisItemsOnGrid(grid, specifiedMajorAxisAutoGridItems);
   placeAutoMajorAxisItemsOnGrid(grid, autoMajorAxisAutoGridItems);
 
-  grid.shrinkToFit();
-
   // Compute collapsable tracks for auto-fit.
-  grid.setAutoRepeatEmptyColumns(computeEmptyTracksForAutoRepeat(ForColumns));
-  grid.setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(ForRows));
+  grid.setAutoRepeatEmptyColumns(
+      computeEmptyTracksForAutoRepeat(grid, ForColumns));
+  grid.setAutoRepeatEmptyRows(computeEmptyTracksForAutoRepeat(grid, ForRows));
+
+  grid.setNeedsItemsPlacement(false);
 
 #if ENABLE(ASSERT)
   for (LayoutBox* child = grid.orderIterator().first(); child;
@@ -2208,12 +2216,13 @@
 
 std::unique_ptr<GridArea>
 LayoutGrid::createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
+    const Grid& grid,
     const LayoutBox& gridItem,
     GridTrackSizingDirection specifiedDirection,
     const GridSpan& specifiedPositions) const {
   GridTrackSizingDirection crossDirection =
       specifiedDirection == ForColumns ? ForRows : ForColumns;
-  const size_t endOfCrossDirection = m_grid.numTracks(crossDirection);
+  const size_t endOfCrossDirection = grid.numTracks(crossDirection);
   size_t crossDirectionSpanSize =
       GridPositionsResolver::spanSizeForAutoPlacedItem(*style(), gridItem,
                                                        crossDirection);
@@ -2256,9 +2265,11 @@
                             : minorAxisCursors.get(majorAxisInitialPosition));
     std::unique_ptr<GridArea> emptyGridArea = iterator.nextEmptyGridArea(
         majorAxisPositions.integerSpan(), minorAxisSpanSize);
-    if (!emptyGridArea)
+    if (!emptyGridArea) {
       emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
-          *autoGridItem, autoPlacementMajorAxisDirection(), majorAxisPositions);
+          grid, *autoGridItem, autoPlacementMajorAxisDirection(),
+          majorAxisPositions);
+    }
 
     grid.insert(*autoGridItem, *emptyGridArea);
 
@@ -2323,9 +2334,11 @@
           minorAxisPositions.integerSpan(), majorAxisSpanSize);
     }
 
-    if (!emptyGridArea)
+    if (!emptyGridArea) {
       emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
-          gridItem, autoPlacementMinorAxisDirection(), minorAxisPositions);
+          grid, gridItem, autoPlacementMinorAxisDirection(),
+          minorAxisPositions);
+    }
   } else {
     size_t minorAxisSpanSize = GridPositionsResolver::spanSizeForAutoPlacedItem(
         *style(), gridItem, autoPlacementMinorAxisDirection());
@@ -2363,7 +2376,7 @@
 
     if (!emptyGridArea)
       emptyGridArea = createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
-          gridItem, autoPlacementMinorAxisDirection(),
+          grid, gridItem, autoPlacementMinorAxisDirection(),
           GridSpan::translatedDefiniteGridSpan(0, minorAxisSpanSize));
   }
 
@@ -2382,12 +2395,11 @@
 }
 
 void LayoutGrid::dirtyGrid() {
-  if (m_gridIsDirty)
+  if (m_grid.needsItemsPlacement())
     return;
 
-  m_grid.clear();
+  m_grid.setNeedsItemsPlacement(true);
   m_gridItemsOverflowingGridArea.resize(0);
-  m_gridIsDirty = true;
 }
 
 Vector<LayoutUnit> LayoutGrid::trackSizesForComputedStyle(
@@ -2632,7 +2644,7 @@
                                             : child.style()->gridRowStart();
   GridPosition endPosition = isForColumns ? child.style()->gridColumnEnd()
                                           : child.style()->gridRowEnd();
-  int lastLine = numTracks(direction);
+  int lastLine = numTracks(direction, m_grid);
 
   bool startIsAuto =
       startPosition.isAuto() ||
@@ -3575,7 +3587,8 @@
   return m_hasDefiniteLogicalHeight.value();
 }
 
-size_t LayoutGrid::numTracks(GridTrackSizingDirection direction) const {
+size_t LayoutGrid::numTracks(GridTrackSizingDirection direction,
+                             const Grid& grid) const {
   // Due to limitations in our internal representation, we cannot know the
   // number of columns from m_grid *if* there is no row (because m_grid would be
   // empty). That's why in that case we need to get it from the style. Note that
@@ -3583,12 +3596,12 @@
   // rows implies that there are no "normal" children (out-of-flow children are
   // not stored in m_grid).
   if (direction == ForRows)
-    return m_grid.numTracks(ForRows);
+    return grid.numTracks(ForRows);
 
-  return m_grid.numTracks(ForRows)
-             ? m_grid.numTracks(ForColumns)
+  return grid.numTracks(ForRows)
+             ? grid.numTracks(ForColumns)
              : GridPositionsResolver::explicitGridColumnCount(
-                   styleRef(), m_grid.autoRepeatTracks(ForColumns));
+                   styleRef(), grid.autoRepeatTracks(ForColumns));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.h b/third_party/WebKit/Source/core/layout/LayoutGrid.h
index a89e613..35add01 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGrid.h
+++ b/third_party/WebKit/Source/core/layout/LayoutGrid.h
@@ -63,28 +63,28 @@
   Vector<LayoutUnit> trackSizesForComputedStyle(GridTrackSizingDirection) const;
 
   const Vector<LayoutUnit>& columnPositions() const {
-    ASSERT(!m_gridIsDirty);
+    DCHECK(!m_grid.needsItemsPlacement());
     return m_columnPositions;
   }
 
   const Vector<LayoutUnit>& rowPositions() const {
-    ASSERT(!m_gridIsDirty);
+    DCHECK(!m_grid.needsItemsPlacement());
     return m_rowPositions;
   }
 
   typedef Vector<LayoutBox*, 1> GridCell;
   const GridCell& gridCell(int row, int column) const {
-    SECURITY_DCHECK(!m_gridIsDirty);
+    SECURITY_DCHECK(!m_grid.needsItemsPlacement());
     return m_grid.cell(row, column);
   }
 
   const Vector<LayoutBox*>& itemsOverflowingGridArea() const {
-    SECURITY_DCHECK(!m_gridIsDirty);
+    SECURITY_DCHECK(!m_grid.needsItemsPlacement());
     return m_gridItemsOverflowingGridArea;
   }
 
   int paintIndexForGridItem(const LayoutBox* layoutBox) const {
-    SECURITY_DCHECK(!m_gridIsDirty);
+    SECURITY_DCHECK(!m_grid.needsItemsPlacement());
     return m_grid.gridItemPaintOrder(*layoutBox);
   }
 
@@ -116,6 +116,7 @@
   bool explicitGridDidResize(const ComputedStyle&) const;
   bool namedGridLinesDefinitionDidChange(const ComputedStyle&) const;
 
+  class Grid;
   class GridIterator;
   struct GridSizingData;
   enum SizingOperation { TrackSizing, IntrinsicSizeComputation };
@@ -144,12 +145,13 @@
 
   typedef ListHashSet<size_t> OrderedTrackIndexSet;
   std::unique_ptr<OrderedTrackIndexSet> computeEmptyTracksForAutoRepeat(
+      Grid&,
       GridTrackSizingDirection) const;
 
-  class Grid;
   void placeItemsOnGrid(Grid&, SizingOperation);
   void populateExplicitGridAndOrderIterator(Grid&) const;
   std::unique_ptr<GridArea> createEmptyGridAreaAtSpecifiedPositionsOutsideGrid(
+      const Grid&,
       const LayoutBox&,
       GridTrackSizingDirection,
       const GridSpan& specifiedPositions) const;
@@ -337,7 +339,7 @@
 
   bool cachedHasDefiniteLogicalHeight() const;
 
-  size_t numTracks(GridTrackSizingDirection) const;
+  size_t numTracks(GridTrackSizingDirection, const Grid&) const;
 
   // TODO(svillar): move into this class once GridIterator is added.
   typedef Vector<Vector<GridCell>> GridAsMatrix;
@@ -387,8 +389,8 @@
 
     OrderIterator& orderIterator() { return m_orderIterator; }
 
-    void shrinkToFit() { m_grid.shrinkToFit(); }
-    void clear();
+    void setNeedsItemsPlacement(bool);
+    bool needsItemsPlacement() const { return m_needsItemsPlacement; };
 
 #if ENABLE(ASSERT)
     bool hasAnyGridItemPaintOrder() const;
@@ -406,6 +408,8 @@
     size_t m_autoRepeatRows{0};
 
     bool m_hasAnyOrthogonalGridItem{false};
+    bool m_needsItemsPlacement{true};
+
     GridAsMatrix m_grid;
 
     HashMap<const LayoutBox*, GridArea> m_gridItemArea;
@@ -416,7 +420,6 @@
   };
   Grid m_grid;
 
-  bool m_gridIsDirty;
   Vector<LayoutUnit> m_rowPositions;
   Vector<LayoutUnit> m_columnPositions;
   LayoutUnit m_offsetBetweenColumns;
diff --git a/third_party/WebKit/Source/core/layout/LayoutMedia.cpp b/third_party/WebKit/Source/core/layout/LayoutMedia.cpp
index 7cba355..c9744c5a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMedia.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMedia.cpp
@@ -136,6 +136,12 @@
   if (document().page()->mainFrame()->isRemoteFrame())
     return mediaRect.width();
 
+  // TODO(foolip): when going fullscreen, the animation sometimes does not clear
+  // up properly and the last `absoluteXOffset` received is incorrect. This is
+  // a shortcut that we could ideally avoid. See https://crbug.com/663680
+  if (mediaElement() && mediaElement()->isFullscreen())
+    return mediaRect.width();
+
   FrameHost* frameHost = document().frameHost();
   LocalFrame* mainFrame = document().page()->deprecatedLocalMainFrame();
   FrameView* pageView = mainFrame ? mainFrame->view() : nullptr;
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 6f2be64..49d84c95 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -236,7 +236,9 @@
 }
 
 LayoutObject::~LayoutObject() {
-  ASSERT(!m_hasAXObject);
+#if DCHECK_IS_ON()
+  DCHECK(!m_hasAXObject);
+#endif
   InstanceCounters::decrementCounter(InstanceCounters::LayoutObjectCounter);
 }
 
@@ -737,7 +739,9 @@
 
 void LayoutObject::markContainerChainForLayout(bool scheduleRelayout,
                                                SubtreeLayoutScope* layouter) {
-  ASSERT(!isSetNeedsLayoutForbidden());
+#if DCHECK_IS_ON()
+  DCHECK(!isSetNeedsLayoutForbidden());
+#endif
   ASSERT(!layouter || this != layouter->root());
   // When we're in layout, we're marking a descendant as needing layout with
   // the intention of visiting it during this layout. We shouldn't be
@@ -768,18 +772,18 @@
       container = object->container();
       object->setPosChildNeedsLayout(true);
       simplifiedNormalFlowLayout = true;
-      ASSERT(!object->isSetNeedsLayoutForbidden());
     } else if (simplifiedNormalFlowLayout) {
       if (object->needsSimplifiedNormalFlowLayout())
         return;
       object->setNeedsSimplifiedNormalFlowLayout(true);
-      ASSERT(!object->isSetNeedsLayoutForbidden());
     } else {
       if (object->normalChildNeedsLayout())
         return;
       object->setNormalChildNeedsLayout(true);
-      ASSERT(!object->isSetNeedsLayoutForbidden());
     }
+#if DCHECK_IS_ON()
+    DCHECK(!object->isSetNeedsLayoutForbidden());
+#endif
 
     if (layouter) {
       layouter->recordObjectMarkedForLayout(object);
@@ -1463,7 +1467,7 @@
       diff.setNeedsFullLayout();
   }
 
-  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
     // Text nodes share style with their parents but the checked styles don't
     // apply to them, hence the !isText() check.
     if (!isText() && (diff.transformChanged() || diff.opacityChanged() ||
@@ -1473,13 +1477,16 @@
       // property or paint order change. Mark the painting layer needing repaint
       // for changed paint property or paint order. Raster invalidation will be
       // issued if needed during paint.
-      ObjectPaintInvalidator(*this).slowSetPaintingLayerNeedsRepaint();
+      if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+        ObjectPaintInvalidator(*this).slowSetPaintingLayerNeedsRepaint();
 
       // When transform, opacity, etc. change, paint properties will also change
       // so we need to mark this object as needing an update.
       getMutableForPainting().setNeedsPaintPropertyUpdate();
     }
-  } else {
+  }
+
+  if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     // If transform changed, and the layer does not paint into its own separate
     // backing, then we need to invalidate paints.
     if (diff.transformChanged()) {
@@ -2807,8 +2814,7 @@
 
   LayoutObject* destroyRoot = this;
   for (LayoutObject *destroyRootParent = destroyRoot->parent();
-       destroyRootParent && destroyRootParent->isAnonymous() &&
-       !destroyRootParent->parent()->createsAnonymousWrapper();
+       destroyRootParent && destroyRootParent->isAnonymous();
        destroyRoot = destroyRootParent,
                     destroyRootParent = destroyRootParent->parent()) {
     // Anonymous block continuations are tracked and destroyed elsewhere (see
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index f4c880c..7df6925 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1309,18 +1309,6 @@
 
   void setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
 
-  // Returns true if the object itself will not generate any effective painted
-  // output no matter what size the object is. For example, this function can
-  // return false for an object whose size is currently 0x0 but would have
-  // effective painted output if it was set a non-empty size.
-  // It's used to skip unforced paint invalidation (which is when
-  // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or
-  // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint
-  // invalidations of empty areas covered by such objects.
-  virtual bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
-    return false;
-  }
-
   // Returns the rect that should have paint invalidated whenever this object
   // changes. The rect is in the view's coordinate space. This method deals with
   // outlines and overflow.
@@ -2502,7 +2490,9 @@
     LayoutInvalidationReasonForTracing reason,
     MarkingBehavior markParents,
     SubtreeLayoutScope* layouter) {
-  ASSERT(!isSetNeedsLayoutForbidden());
+#if DCHECK_IS_ON()
+  DCHECK(!isSetNeedsLayoutForbidden());
+#endif
   bool alreadyNeededLayout = m_bitfields.selfNeedsLayout();
   setSelfNeedsLayout(true);
   if (!alreadyNeededLayout) {
@@ -2546,7 +2536,9 @@
 
 inline void LayoutObject::setChildNeedsLayout(MarkingBehavior markParents,
                                               SubtreeLayoutScope* layouter) {
-  ASSERT(!isSetNeedsLayoutForbidden());
+#if DCHECK_IS_ON()
+  DCHECK(!isSetNeedsLayoutForbidden());
+#endif
   bool alreadyNeededLayout = normalChildNeedsLayout();
   setNormalChildNeedsLayout(true);
   // FIXME: Replace MarkOnlyThis with the SubtreeLayoutScope code path and
@@ -2559,7 +2551,9 @@
 inline void LayoutObject::setNeedsPositionedMovementLayout() {
   bool alreadyNeededLayout = needsPositionedMovementLayout();
   setNeedsPositionedMovementLayout(true);
-  ASSERT(!isSetNeedsLayoutForbidden());
+#if DCHECK_IS_ON()
+  DCHECK(!isSetNeedsLayoutForbidden());
+#endif
   if (!alreadyNeededLayout)
     markContainerChainForLayout();
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
index 572ded7..caf148b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTableSection.cpp
@@ -829,7 +829,7 @@
 }
 
 int LayoutTableSection::calcRowLogicalHeight() {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
 #endif
 
@@ -1120,7 +1120,7 @@
 }
 
 void LayoutTableSection::layoutRows() {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
 #endif
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.h b/third_party/WebKit/Source/core/layout/LayoutText.h
index 9dff725..9829726b 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.h
+++ b/third_party/WebKit/Source/core/layout/LayoutText.h
@@ -128,6 +128,7 @@
   unsigned textLength() const {
     return m_text.length();
   }  // non virtual implementation of length()
+  bool containsOnlyWhitespace(unsigned from, unsigned len) const;
   void positionLineBox(InlineBox*);
 
   virtual float width(unsigned from,
@@ -258,7 +259,6 @@
   }
 
   void deleteTextBoxes();
-  bool containsOnlyWhitespace(unsigned from, unsigned len) const;
   float widthFromFont(const Font&,
                       int start,
                       int len,
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index ef2a285..00192c6 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -1026,4 +1026,13 @@
   return rect;
 }
 
+bool LayoutView::paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
+  // Frame scroll corner is painted using LayoutView as the display item client.
+  if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
+      (frameView()->horizontalScrollbar() || frameView()->verticalScrollbar()))
+    return false;
+
+  return LayoutBlockFlow::paintedOutputOfObjectHasNoEffectRegardlessOfSize();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.h b/third_party/WebKit/Source/core/layout/LayoutView.h
index c2adf61..d2036212 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.h
+++ b/third_party/WebKit/Source/core/layout/LayoutView.h
@@ -265,6 +265,8 @@
   int viewLogicalWidthForBoxSizing() const;
   int viewLogicalHeightForBoxSizing() const;
 
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
+
   UntracedMember<FrameView> m_frameView;
 
   // The current selection represented as 2 boundaries.
diff --git a/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.cpp b/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.cpp
index 1d6bd57..212ac74e 100644
--- a/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.cpp
+++ b/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.cpp
@@ -42,7 +42,7 @@
 SubtreeLayoutScope::~SubtreeLayoutScope() {
   RELEASE_ASSERT(!m_root.needsLayout());
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   for (auto* layoutObject : m_layoutObjectsToLayout)
     layoutObject->assertLaidOut();
 #endif
@@ -62,7 +62,7 @@
 
 void SubtreeLayoutScope::recordObjectMarkedForLayout(
     LayoutObject* layoutObject) {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   m_layoutObjectsToLayout.add(layoutObject);
 #endif
 }
diff --git a/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.h b/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.h
index cf4c649..8b5e3f7 100644
--- a/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.h
+++ b/third_party/WebKit/Source/core/layout/SubtreeLayoutScope.h
@@ -65,7 +65,7 @@
  private:
   LayoutObject& m_root;
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   HashSet<LayoutObject*> m_layoutObjectsToLayout;
 #endif
 };
diff --git a/third_party/WebKit/Source/core/layout/api/LineLayoutText.h b/third_party/WebKit/Source/core/layout/api/LineLayoutText.h
index 4e664b2..8722e36 100644
--- a/third_party/WebKit/Source/core/layout/api/LineLayoutText.h
+++ b/third_party/WebKit/Source/core/layout/api/LineLayoutText.h
@@ -79,6 +79,10 @@
 
   const String& text() const { return toText()->text(); }
 
+  bool containsOnlyWhitespace(unsigned from, unsigned len) const {
+    return toText()->containsOnlyWhitespace(from, len);
+  }
+
   float width(unsigned from,
               unsigned len,
               const Font& font,
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
index 91d0532..7f3fb6d 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -2796,7 +2796,7 @@
         roundedIntSize(paintInfo.paintLayer->subpixelAccumulation()));
   }
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   if (!layoutObject()->view()->frame() ||
       !layoutObject()->view()->frame()->shouldThrottleRendering())
     paintInfo.paintLayer->layoutObject()->assertSubtreeIsLaidOut();
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
index 79777cffa..1ee5b2d 100644
--- a/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
+++ b/third_party/WebKit/Source/core/layout/compositing/CompositingReasonFinder.cpp
@@ -194,7 +194,7 @@
   // Don't promote fixed position elements that are descendants of a non-view
   // container, e.g. transformed elements.  They will stay fixed wrt the
   // container rather than the enclosing frame.
-  if (layer->scrollsWithViewport())
+  if (layer->sticksToViewport())
     return m_layoutView.frameView()->isScrollable();
   return layer->layoutObject()->style()->position() == StickyPosition &&
          layer->ancestorOverflowLayer()->scrollsOverflow();
diff --git a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
index 75cb21f4..d2db37f 100644
--- a/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
+++ b/third_party/WebKit/Source/core/layout/line/BreakingContextInlineHeaders.h
@@ -492,8 +492,14 @@
 
   if (m_floatsFitOnLine) {
     // We need to calculate the logical width of the float before we can tell
-    // whether it's going to fit on the line.
-    m_block.positionAndLayoutFloat(*floatingObject, m_block.logicalHeight());
+    // whether it's going to fit on the line. That means that we need to
+    // position and lay it out. Note that we have to avoid positioning floats
+    // that have been placed prematurely: Sometimes, floats are inserted too
+    // early by skipTrailingWhitespace(), and later on they all get placed by
+    // the first float here in handleFloat(). Their position may then be wrong,
+    // but it's too late to do anything about that now. See crbug.com/671577
+    if (!floatingObject->isPlaced())
+      m_block.positionAndLayoutFloat(*floatingObject, m_block.logicalHeight());
 
     // Check if it fits in the current line; if it does, place it now,
     // otherwise, place it after moving to next line (in newLine() func).
@@ -892,7 +898,7 @@
   // ignores the kerning between the last letter and the hyphen.
   return rewindToMidWordBreak(
       wordMeasurement, start + prefixLength,
-      font.getCharacterRange(run, 0, prefixLength).width() + hyphenWidth);
+      font.getCharacterRange(run, 0, prefixLength).width());
 }
 
 ALWAYS_INLINE bool BreakingContext::isBreakAtSoftHyphen() const {
@@ -901,6 +907,12 @@
              : m_current.previousInSameNode() == softHyphenCharacter;
 }
 
+static ALWAYS_INLINE bool hasVisibleText(LineLayoutText layoutText,
+                                         unsigned offset) {
+  return !layoutText.containsOnlyWhitespace(offset,
+                                            layoutText.textLength() - offset);
+}
+
 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements,
                                         bool& hyphenated) {
   if (!m_current.offset())
@@ -949,6 +961,7 @@
   Hyphenation* hyphenation = style.getHyphenation();
   bool disableSoftHyphen = style.getHyphens() == HyphensNone;
   float hyphenWidth = 0;
+  bool isLineEmpty = m_lineInfo.isEmpty();
 
   if (layoutText.isSVGInlineText()) {
     breakWords = false;
@@ -1100,15 +1113,8 @@
 
     midWordBreak = false;
     if (!m_width.fitsOnLine()) {
-      if (canBreakMidWord) {
-        m_width.addUncommittedWidth(-wordMeasurement.width);
-        if (rewindToMidWordBreak(layoutText, style, font, breakAll,
-                                 wordMeasurement)) {
-          lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing;
-          midWordBreak = true;
-        }
-        m_width.addUncommittedWidth(wordMeasurement.width);
-      } else if (hyphenation) {
+      if (hyphenation && (m_nextObject || isLineEmpty ||
+                          hasVisibleText(layoutText, m_current.offset()))) {
         m_width.addUncommittedWidth(-wordMeasurement.width);
         DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset));
         DCHECK(m_current.offset() ==
@@ -1122,6 +1128,15 @@
         }
         m_width.addUncommittedWidth(wordMeasurement.width);
       }
+      if (canBreakMidWord) {
+        m_width.addUncommittedWidth(-wordMeasurement.width);
+        if (rewindToMidWordBreak(layoutText, style, font, breakAll,
+                                 wordMeasurement)) {
+          lastWidthMeasurement = wordMeasurement.width + lastSpaceWordSpacing;
+          midWordBreak = true;
+        }
+        m_width.addUncommittedWidth(wordMeasurement.width);
+      }
     }
 
     // If we haven't hit a breakable position yet and already don't fit on the
@@ -1197,6 +1212,7 @@
     prepareForNextCharacter(layoutText, prohibitBreakInside,
                             previousCharacterIsSpace);
     m_atStart = false;
+    isLineEmpty = m_lineInfo.isEmpty();
     nextCharacter(c, lastCharacter, secondToLastCharacter);
   }
 
@@ -1211,7 +1227,6 @@
   float lastWidthMeasurement = 0;
   wordMeasurement.startOffset = lastSpace;
   wordMeasurement.endOffset = m_current.offset();
-  midWordBreak = false;
   if (!m_ignoringSpaces) {
     lastWidthMeasurement =
         textWidth(layoutText, lastSpace, m_current.offset() - lastSpace, font,
@@ -1220,13 +1235,6 @@
     wordMeasurement.width =
         lastWidthMeasurement + wordSpacingForWordMeasurement;
     wordMeasurement.glyphBounds.move(wordSpacingForWordMeasurement, 0);
-
-    if (canBreakMidWord && !m_width.fitsOnLine(lastWidthMeasurement) &&
-        rewindToMidWordBreak(layoutText, style, font, breakAll,
-                             wordMeasurement)) {
-      lastWidthMeasurement = wordMeasurement.width;
-      midWordBreak = true;
-    }
   }
   lastWidthMeasurement += lastSpaceWordSpacing;
 
@@ -1243,15 +1251,12 @@
 
   m_includeEndWidth = false;
 
-  if (midWordBreak) {
-    m_width.commit();
-    m_atEnd = true;
-  } else if (!m_width.fitsOnLine()) {
-    if (hyphenation && (m_nextObject || m_lineInfo.isEmpty())) {
+  if (!m_ignoringSpaces && !m_width.fitsOnLine()) {
+    if (hyphenation && (m_nextObject || isLineEmpty)) {
       m_width.addUncommittedWidth(-wordMeasurement.width);
-      DCHECK(lastSpace == static_cast<unsigned>(wordMeasurement.startOffset));
-      DCHECK(m_current.offset() ==
-             static_cast<unsigned>(wordMeasurement.endOffset));
+      DCHECK_EQ(lastSpace, static_cast<unsigned>(wordMeasurement.startOffset));
+      DCHECK_EQ(m_current.offset(),
+                static_cast<unsigned>(wordMeasurement.endOffset));
       if (hyphenate(layoutText, style, font, *hyphenation, lastSpaceWordSpacing,
                     wordMeasurement)) {
         hyphenated = true;
@@ -1262,9 +1267,19 @@
     if (!hyphenated && isBreakAtSoftHyphen() && !disableSoftHyphen) {
       hyphenated = true;
       m_atEnd = true;
-    } else if (!m_ignoringSpaces && canBreakMidWord &&
-               m_width.committedWidth()) {
-      m_atEnd = true;
+    }
+    if (!hyphenated && canBreakMidWord) {
+      m_width.addUncommittedWidth(-wordMeasurement.width);
+      if (rewindToMidWordBreak(layoutText, style, font, breakAll,
+                               wordMeasurement)) {
+        m_width.addUncommittedWidth(wordMeasurement.width);
+        m_width.commit();
+        m_atEnd = true;
+      } else {
+        m_width.addUncommittedWidth(wordMeasurement.width);
+        if (m_width.committedWidth())
+          m_atEnd = true;
+      }
     }
   }
   return false;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
index 59f54fc..d2dae30f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
@@ -59,7 +59,7 @@
   String collapsed("text text text text");
   TestWhitespaceValue(collapsed, input, EWhiteSpace::Normal);
   TestWhitespaceValue(collapsed, input, EWhiteSpace::Nowrap);
-  TestWhitespaceValue(collapsed, input, EWhiteSpace::KhtmlNowrap);
+  TestWhitespaceValue(collapsed, input, EWhiteSpace::WebkitNowrap);
   TestWhitespaceValue(collapsed, input, EWhiteSpace::PreLine);
   TestWhitespaceValue(input, input, EWhiteSpace::Pre);
   TestWhitespaceValue(input, input, EWhiteSpace::PreWrap);
@@ -70,7 +70,7 @@
   String collapsed("text text text text");
   TestWhitespaceValue(collapsed, input, EWhiteSpace::Normal);
   TestWhitespaceValue(collapsed, input, EWhiteSpace::Nowrap);
-  TestWhitespaceValue(collapsed, input, EWhiteSpace::KhtmlNowrap);
+  TestWhitespaceValue(collapsed, input, EWhiteSpace::WebkitNowrap);
   TestWhitespaceValue(collapsed, input, EWhiteSpace::PreLine);
   TestWhitespaceValue(input, input, EWhiteSpace::Pre);
   TestWhitespaceValue(input, input, EWhiteSpace::PreWrap);
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
index cfc5c091..56077911 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGBlock.h
@@ -80,6 +80,11 @@
                    const HitTestLocation& locationInContainer,
                    const LayoutPoint& accumulatedOffset,
                    HitTestAction) override;
+
+  // The inherited version doesn't check for SVG effects.
+  bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override {
+    return false;
+  }
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
index 9ed7c72..3179cad7 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp
@@ -22,20 +22,83 @@
 
 #include "core/layout/svg/LayoutSVGResourceClipper.h"
 
-#include "core/SVGNames.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/layout/HitTestResult.h"
 #include "core/layout/svg/SVGLayoutSupport.h"
 #include "core/paint/PaintInfo.h"
 #include "core/svg/SVGGeometryElement.h"
 #include "core/svg/SVGUseElement.h"
-#include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/paint/SkPictureBuilder.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/pathops/SkPathOps.h"
 
 namespace blink {
 
+namespace {
+
+bool requiresMask(const LayoutObject& layoutObject) {
+  // Only basic shapes or paths are supported for direct clipping. We need to
+  // fallback to masking for texts.
+  if (layoutObject.isSVGText())
+    return true;
+  // Current shape in clip-path gets clipped too. Fallback to masking.
+  return layoutObject.styleRef().clipPath();
+}
+
+bool requiresMask(const SVGElement& element) {
+  const LayoutObject* layoutObject = element.layoutObject();
+  DCHECK(layoutObject);
+  if (isSVGUseElement(element)) {
+    if (layoutObject->styleRef().clipPath())
+      return true;
+    const SVGGraphicsElement* clippingElement =
+        toSVGUseElement(element).visibleTargetGraphicsElementForClipping();
+    DCHECK(clippingElement);
+    layoutObject = clippingElement->layoutObject();
+  }
+  return requiresMask(*layoutObject);
+}
+
+bool contributesToClip(const SVGGraphicsElement& element) {
+  const LayoutObject* layoutObject = element.layoutObject();
+  if (!layoutObject)
+    return false;
+  const ComputedStyle& style = layoutObject->styleRef();
+  if (style.display() == EDisplay::None ||
+      style.visibility() != EVisibility::Visible)
+    return false;
+  // Only shapes, paths and texts are allowed for clipping.
+  return layoutObject->isSVGShape() || layoutObject->isSVGText();
+}
+
+bool contributesToClip(const SVGElement& element) {
+  // <use> within <clipPath> have a restricted content model.
+  // (https://drafts.fxtf.org/css-masking-1/#ClipPathElement)
+  if (isSVGUseElement(element)) {
+    const LayoutObject* useLayoutObject = element.layoutObject();
+    if (!useLayoutObject ||
+        useLayoutObject->styleRef().display() == EDisplay::None)
+      return false;
+    const SVGGraphicsElement* clippingElement =
+        toSVGUseElement(element).visibleTargetGraphicsElementForClipping();
+    if (!clippingElement)
+      return false;
+    return contributesToClip(*clippingElement);
+  }
+  if (!element.isSVGGraphicsElement())
+    return false;
+  return contributesToClip(toSVGGraphicsElement(element));
+}
+
+void pathFromElement(const SVGElement& element, Path& clipPath) {
+  if (isSVGGeometryElement(element))
+    toSVGGeometryElement(element).toClipPath(clipPath);
+  else if (isSVGUseElement(element))
+    toSVGUseElement(element).toClipPath(clipPath);
+}
+
+}  // namespace
+
 LayoutSVGResourceClipper::LayoutSVGResourceClipper(SVGClipPathElement* node)
     : LayoutSVGResourceContainer(node), m_inClipExpansion(false) {}
 
@@ -72,40 +135,19 @@
   bool usingBuilder = false;
   SkOpBuilder clipPathBuilder;
 
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* childLayoutObject = childElement->layoutObject();
-    if (!childLayoutObject)
-      continue;
-    // Only shapes or paths are supported for direct clipping. We need to
-    // fallback to masking for texts.
-    if (childLayoutObject->isSVGText()) {
-      m_clipContentPath.clear();
-      return false;
-    }
-    if (!childElement->isSVGGraphicsElement())
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    if (!contributesToClip(childElement))
       continue;
 
-    const ComputedStyle* style = childLayoutObject->style();
-    if (!style || style->display() == EDisplay::None ||
-        (style->visibility() != EVisibility::Visible &&
-         !isSVGUseElement(*childElement)))
-      continue;
-
-    // Current shape in clip-path gets clipped too. Fallback to masking.
-    if (style->clipPath()) {
+    if (requiresMask(childElement)) {
       m_clipContentPath.clear();
       return false;
     }
 
     // First clip shape.
     if (m_clipContentPath.isEmpty()) {
-      if (isSVGGeometryElement(childElement))
-        toSVGGeometryElement(childElement)->toClipPath(m_clipContentPath);
-      else if (isSVGUseElement(childElement))
-        toSVGUseElement(childElement)->toClipPath(m_clipContentPath);
-
+      pathFromElement(childElement, m_clipContentPath);
       continue;
     }
 
@@ -125,10 +167,7 @@
     }
 
     Path subPath;
-    if (isSVGGeometryElement(childElement))
-      toSVGGeometryElement(childElement)->toClipPath(subPath);
-    else if (isSVGUseElement(childElement))
-      toSVGUseElement(childElement)->toClipPath(subPath);
+    pathFromElement(childElement, subPath);
 
     clipPathBuilder.add(subPath.getSkPath(), kUnion_SkPathOp);
   }
@@ -177,49 +216,23 @@
   FloatRect bounds = strokeBoundingBox();
 
   SkPictureBuilder pictureBuilder(bounds, nullptr, nullptr);
+  // Switch to a paint behavior where all children of this <clipPath> will be
+  // laid out using special constraints:
+  // - fill-opacity/stroke-opacity/opacity set to 1
+  // - masker/filter not applied when laying out the children
+  // - fill is set to the initial fill paint server (solid, black)
+  // - stroke is set to the initial stroke paint server (none)
+  PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(),
+                 PaintPhaseForeground, GlobalPaintNormalPhase,
+                 PaintLayerPaintingRenderingClipPathAsMask);
 
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* layoutObject = childElement->layoutObject();
-    if (!layoutObject)
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    if (!contributesToClip(childElement))
       continue;
-
-    const ComputedStyle* style = layoutObject->style();
-    if (!style || style->display() == EDisplay::None ||
-        (style->visibility() != EVisibility::Visible &&
-         !isSVGUseElement(*childElement)))
-      continue;
-
-    bool isUseElement = isSVGUseElement(*childElement);
-    if (isUseElement) {
-      const SVGGraphicsElement* clippingElement =
-          toSVGUseElement(*childElement)
-              .visibleTargetGraphicsElementForClipping();
-      if (!clippingElement)
-        continue;
-
-      layoutObject = clippingElement->layoutObject();
-      if (!layoutObject)
-        continue;
-    }
-
-    // Only shapes, paths and texts are allowed for clipping.
-    if (!layoutObject->isSVGShape() && !layoutObject->isSVGText())
-      continue;
-
-    if (isUseElement)
-      layoutObject = childElement->layoutObject();
-
-    // Switch to a paint behavior where all children of this <clipPath> will be
-    // laid out using special constraints:
-    // - fill-opacity/stroke-opacity/opacity set to 1
-    // - masker/filter not applied when laying out the children
-    // - fill is set to the initial fill paint server (solid, black)
-    // - stroke is set to the initial stroke paint server (none)
-    PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(),
-                   PaintPhaseForeground, GlobalPaintNormalPhase,
-                   PaintLayerPaintingRenderingClipPathAsMask);
+    // Use the LayoutObject of the direct child even if it is a <use>. In that
+    // case, we will paint the targeted element indirectly.
+    const LayoutObject* layoutObject = childElement.layoutObject();
     layoutObject->paint(info, IntPoint());
   }
 
@@ -230,25 +243,11 @@
 void LayoutSVGResourceClipper::calculateLocalClipBounds() {
   // This is a rough heuristic to appraise the clip size and doesn't consider
   // clip on clip.
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* layoutObject = childElement->layoutObject();
-    if (!layoutObject)
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    if (!contributesToClip(childElement))
       continue;
-    if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() &&
-        !isSVGUseElement(*childElement))
-      continue;
-    const ComputedStyle* style = layoutObject->style();
-    if (!style || style->display() == EDisplay::None ||
-        (style->visibility() != EVisibility::Visible &&
-         !isSVGUseElement(*childElement)))
-      continue;
-    if (isSVGUseElement(*childElement) &&
-        !toSVGUseElement(*childElement)
-             .visibleTargetGraphicsElementForClipping())
-      continue;
-
+    const LayoutObject* layoutObject = childElement.layoutObject();
     m_localClipBounds.unite(layoutObject->localToSVGParentTransform().mapRect(
         layoutObject->visualRectInLocalSVGCoordinates()));
   }
@@ -277,21 +276,16 @@
 
   point = animatedLocalTransform.inverse().mapPoint(point);
 
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* layoutObject = childElement->layoutObject();
-    if (!layoutObject)
-      continue;
-    if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() &&
-        !isSVGUseElement(*childElement))
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    if (!contributesToClip(childElement))
       continue;
     IntPoint hitPoint;
     HitTestResult result(HitTestRequest::SVGClipContent, hitPoint);
+    LayoutObject* layoutObject = childElement.layoutObject();
     if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground))
       return true;
   }
-
   return false;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
index 68e53aa..54da89b6 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceMasker.cpp
@@ -85,17 +85,11 @@
           : ColorFilterNone;
   pictureBuilder.context().setColorFilter(maskContentFilter);
 
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* layoutObject = childElement->layoutObject();
-    if (!layoutObject)
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    const LayoutObject* layoutObject = childElement.layoutObject();
+    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::None)
       continue;
-    const ComputedStyle* style = layoutObject->style();
-    if (!style || style->display() == EDisplay::None ||
-        style->visibility() != EVisibility::Visible)
-      continue;
-
     SVGPaintContext::paintSubtree(pictureBuilder.context(), layoutObject);
   }
 
@@ -104,15 +98,10 @@
 }
 
 void LayoutSVGResourceMasker::calculateMaskContentVisualRect() {
-  for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element());
-       childElement;
-       childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
-    LayoutObject* layoutObject = childElement->layoutObject();
-    if (!layoutObject)
-      continue;
-    const ComputedStyle* style = layoutObject->style();
-    if (!style || style->display() == EDisplay::None ||
-        style->visibility() != EVisibility::Visible)
+  for (const SVGElement& childElement :
+       Traversal<SVGElement>::childrenOf(*element())) {
+    const LayoutObject* layoutObject = childElement.layoutObject();
+    if (!layoutObject || layoutObject->styleRef().display() == EDisplay::None)
       continue;
     m_maskContentBoundaries.unite(
         layoutObject->localToSVGParentTransform().mapRect(
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
index 80e7f06e..7cd271a0 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.cpp
@@ -161,8 +161,11 @@
   // content may have been exposed, so mark the entire subtree as needing paint
   // invalidation checking. (It is only somewhat by coincidence that this
   // condition happens to be the same as the one for viewport changes.)
-  if (viewportMayHaveChanged)
+  if (viewportMayHaveChanged) {
     setMayNeedPaintInvalidationSubtree();
+    if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
+      setNeedsPaintPropertyUpdate();
+  }
 
   SVGSVGElement* svg = toSVGSVGElement(node());
   ASSERT(svg);
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
index 37e2d38..a329781 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGRoot.h
@@ -68,9 +68,10 @@
     // SVGImage::draw() does a view layout prior to painting,
     // and we need that layout to know of the new size otherwise
     // the layout may be incorrectly using the old size.
-    if (m_containerSize != containerSize)
+    if (m_containerSize != containerSize) {
       setNeedsLayoutAndFullPaintInvalidation(
           LayoutInvalidationReason::SizeChanged);
+    }
     m_containerSize = containerSize;
   }
 
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGTransformableContainer.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGTransformableContainer.cpp
index 9ce3b01..8d1af96c 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGTransformableContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGTransformableContainer.cpp
@@ -67,6 +67,11 @@
 
 void LayoutSVGTransformableContainer::setNeedsTransformUpdate() {
   setMayNeedPaintInvalidationSubtree();
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    // The transform paint property relies on the SVG transform being up-to-date
+    // (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
+    setNeedsPaintPropertyUpdate();
+  }
   m_needsTransformUpdate = true;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGViewportContainer.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGViewportContainer.cpp
index ca7a99a..a7438c2 100644
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGViewportContainer.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGViewportContainer.cpp
@@ -66,6 +66,11 @@
 
 void LayoutSVGViewportContainer::setNeedsTransformUpdate() {
   setMayNeedPaintInvalidationSubtree();
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    // The transform paint property relies on the SVG transform being up-to-date
+    // (see: PaintPropertyTreeBuilder::updateTransformForNonRootSVG).
+    setNeedsPaintPropertyUpdate();
+  }
   m_needsTransformUpdate = true;
 }
 
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index 04bb0c42..cd0ad37 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -61,11 +61,11 @@
 #include "core/page/FrameTree.h"
 #include "core/page/Page.h"
 #include "platform/HTTPNames.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/UserGestureIndicator.h"
 #include "platform/mhtml/ArchiveResource.h"
 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
 #include "platform/network/HTTPParsers.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/plugins/PluginData.h"
 #include "platform/weborigin/SchemeRegistry.h"
 #include "platform/weborigin/SecurityPolicy.h"
diff --git a/third_party/WebKit/Source/core/loader/EmptyClients.h b/third_party/WebKit/Source/core/loader/EmptyClients.h
index 0252027..25c26a7 100644
--- a/third_party/WebKit/Source/core/loader/EmptyClients.h
+++ b/third_party/WebKit/Source/core/loader/EmptyClients.h
@@ -38,6 +38,7 @@
 #include "core/page/Page.h"
 #include "core/page/SpellCheckerClient.h"
 #include "platform/DragImage.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/geometry/FloatPoint.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/geometry/IntRect.h"
@@ -45,7 +46,6 @@
 #include "platform/network/ResourceError.h"
 #include "platform/text/TextCheckerClient.h"
 #include "public/platform/WebFocusType.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebScreenInfo.h"
 #include "wtf/Forward.h"
 #include <memory>
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index d5787a3..54838d53 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -62,6 +62,7 @@
 #include "core/svg/graphics/SVGImageChromeClient.h"
 #include "core/timing/DOMWindowPerformance.h"
 #include "core/timing/Performance.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/mhtml/MHTMLArchive.h"
 #include "platform/network/NetworkUtils.h"
 #include "platform/network/ResourceLoadPriority.h"
@@ -71,7 +72,6 @@
 #include "platform/weborigin/SecurityPolicy.h"
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebDocumentSubresourceFilter.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebInsecureRequestPolicy.h"
 #include "public/platform/WebViewScheduler.h"
 #include <algorithm>
@@ -423,14 +423,19 @@
 
 void FrameFetchContext::dispatchDidReceiveData(unsigned long identifier,
                                                const char* data,
-                                               int dataLength,
-                                               int encodedDataLength) {
+                                               int dataLength) {
+  frame()->loader().progress().incrementProgress(identifier, dataLength);
+  InspectorInstrumentation::didReceiveData(frame(), identifier, data,
+                                           dataLength);
+}
+
+void FrameFetchContext::dispatchDidReceiveEncodedData(unsigned long identifier,
+                                                      int encodedDataLength) {
   TRACE_EVENT1(
       "devtools.timeline", "ResourceReceivedData", "data",
       InspectorReceiveDataEvent::data(identifier, frame(), encodedDataLength));
-  frame()->loader().progress().incrementProgress(identifier, dataLength);
-  InspectorInstrumentation::didReceiveData(frame(), identifier, data,
-                                           dataLength, encodedDataLength);
+  InspectorInstrumentation::didReceiveEncodedDataLength(frame(), identifier,
+                                                        encodedDataLength);
 }
 
 void FrameFetchContext::dispatchDidDownloadData(unsigned long identifier,
@@ -440,8 +445,9 @@
       "devtools.timeline", "ResourceReceivedData", "data",
       InspectorReceiveDataEvent::data(identifier, frame(), encodedDataLength));
   frame()->loader().progress().incrementProgress(identifier, dataLength);
-  InspectorInstrumentation::didReceiveData(frame(), identifier, 0, dataLength,
-                                           encodedDataLength);
+  InspectorInstrumentation::didReceiveData(frame(), identifier, 0, dataLength);
+  InspectorInstrumentation::didReceiveEncodedDataLength(frame(), identifier,
+                                                        encodedDataLength);
 }
 
 void FrameFetchContext::dispatchDidFinishLoading(unsigned long identifier,
@@ -459,6 +465,7 @@
 
 void FrameFetchContext::dispatchDidFail(unsigned long identifier,
                                         const ResourceError& error,
+                                        int64_t encodedDataLength,
                                         bool isInternalRequest) {
   TRACE_EVENT1("devtools.timeline", "ResourceFinish", "data",
                InspectorResourceFinishEvent::data(identifier, 0, true));
@@ -493,7 +500,7 @@
   }
 
   if (resource->encodedSize() > 0)
-    dispatchDidReceiveData(identifier, 0, resource->encodedSize(), 0);
+    dispatchDidReceiveData(identifier, 0, resource->encodedSize());
 
   dispatchDidFinishLoading(identifier, 0, 0);
 }
@@ -580,43 +587,39 @@
       SecurityMessageSource, ErrorMessageLevel, message));
 }
 
-bool FrameFetchContext::canRequest(
+ResourceRequestBlockedReason FrameFetchContext::canRequest(
     Resource::Type type,
     const ResourceRequest& resourceRequest,
     const KURL& url,
     const ResourceLoaderOptions& options,
     bool forPreload,
     FetchRequest::OriginRestriction originRestriction) const {
-  ResourceRequestBlockedReason reason =
+  ResourceRequestBlockedReason blockedReason =
       canRequestInternal(type, resourceRequest, url, options, forPreload,
                          originRestriction, resourceRequest.redirectStatus());
-  if (reason != ResourceRequestBlockedReasonNone) {
-    if (!forPreload) {
-      InspectorInstrumentation::didBlockRequest(frame(), resourceRequest,
-                                                masterDocumentLoader(),
-                                                options.initiatorInfo, reason);
-    }
-    return false;
+  if (blockedReason != ResourceRequestBlockedReason::None && !forPreload) {
+    InspectorInstrumentation::didBlockRequest(
+        frame(), resourceRequest, masterDocumentLoader(), options.initiatorInfo,
+        blockedReason);
   }
-  return true;
+  return blockedReason;
 }
 
-bool FrameFetchContext::allowResponse(
+ResourceRequestBlockedReason FrameFetchContext::allowResponse(
     Resource::Type type,
     const ResourceRequest& resourceRequest,
     const KURL& url,
     const ResourceLoaderOptions& options) const {
-  ResourceRequestBlockedReason reason =
+  ResourceRequestBlockedReason blockedReason =
       canRequestInternal(type, resourceRequest, url, options, false,
                          FetchRequest::UseDefaultOriginRestrictionForType,
                          RedirectStatus::FollowedRedirect);
-  if (reason != ResourceRequestBlockedReasonNone) {
-    InspectorInstrumentation::didBlockRequest(frame(), resourceRequest,
-                                              masterDocumentLoader(),
-                                              options.initiatorInfo, reason);
-    return false;
+  if (blockedReason != ResourceRequestBlockedReason::None) {
+    InspectorInstrumentation::didBlockRequest(
+        frame(), resourceRequest, masterDocumentLoader(), options.initiatorInfo,
+        blockedReason);
   }
-  return true;
+  return blockedReason;
 }
 
 ResourceRequestBlockedReason FrameFetchContext::canRequestInternal(
@@ -628,7 +631,7 @@
     FetchRequest::OriginRestriction originRestriction,
     ResourceRequest::RedirectStatus redirectStatus) const {
   if (InspectorInstrumentation::shouldBlockRequest(frame(), resourceRequest))
-    return ResourceRequestBlockedReasonInspector;
+    return ResourceRequestBlockedReason::Inspector;
 
   SecurityOrigin* securityOrigin = options.securityOrigin.get();
   if (!securityOrigin && m_document)
@@ -640,7 +643,7 @@
       FrameLoader::reportLocalLoadFailed(frame(), url.elidedString());
     RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::requestResource URL was not "
                                  "allowed by SecurityOrigin::canDisplay";
-    return ResourceRequestBlockedReasonOther;
+    return ResourceRequestBlockedReason::Other;
   }
 
   // Some types of resources can be loaded only from the same origin. Other
@@ -663,7 +666,7 @@
       if (originRestriction == FetchRequest::RestrictToSameOrigin &&
           !securityOrigin->canRequest(url)) {
         printAccessDeniedMessage(url);
-        return ResourceRequestBlockedReasonOrigin;
+        return ResourceRequestBlockedReason::Origin;
       }
       break;
     case Resource::XSLStyleSheet:
@@ -671,7 +674,7 @@
     case Resource::SVGDocument:
       if (!securityOrigin->canRequest(url)) {
         printAccessDeniedMessage(url);
-        return ResourceRequestBlockedReasonOrigin;
+        return ResourceRequestBlockedReason::Origin;
       }
       break;
   }
@@ -695,7 +698,7 @@
             resourceRequest.requestContext(), url,
             options.contentSecurityPolicyNonce, options.integrityMetadata,
             options.parserDisposition, redirectStatus, cspReporting))
-      return ResourceRequestBlockedReasonCSP;
+      return ResourceRequestBlockedReason::CSP;
   }
 
   if (type == Resource::Script || type == Resource::ImportResource) {
@@ -706,19 +709,19 @@
       frame()->loader().client()->didNotAllowScript();
       // TODO(estark): Use a different ResourceRequestBlockedReason here, since
       // this check has nothing to do with CSP. https://crbug.com/600795
-      return ResourceRequestBlockedReasonCSP;
+      return ResourceRequestBlockedReason::CSP;
     }
   } else if (type == Resource::Media || type == Resource::TextTrack) {
     DCHECK(frame());
     if (!frame()->loader().client()->allowMedia(url))
-      return ResourceRequestBlockedReasonOther;
+      return ResourceRequestBlockedReason::Other;
   }
 
   // SVG Images have unique security rules that prevent all subresource requests
   // except for data urls.
   if (type != Resource::MainResource &&
       frame()->chromeClient().isSVGImageChromeClient() && !url.protocolIsData())
-    return ResourceRequestBlockedReasonOrigin;
+    return ResourceRequestBlockedReason::Origin;
 
   // Measure the number of legacy URL schemes ('ftp://') and the number of
   // embedded-credential ('http://user:password@...') resources embedded as
@@ -747,7 +750,7 @@
                  : MixedContentChecker::SendReport;
   if (MixedContentChecker::shouldBlockFetch(frame(), resourceRequest, url,
                                             mixedContentReporting))
-    return ResourceRequestBlockedReasonMixedContent;
+    return ResourceRequestBlockedReason::MixedContent;
 
   // Let the client have the final say into whether or not the load should
   // proceed.
@@ -756,9 +759,9 @@
       type != Resource::MainResource && type != Resource::ImportResource &&
       !documentLoader->subresourceFilter()->allowLoad(
           url, resourceRequest.requestContext()))
-    return ResourceRequestBlockedReasonSubresourceFilter;
+    return ResourceRequestBlockedReason::SubresourceFilter;
 
-  return ResourceRequestBlockedReasonNone;
+  return ResourceRequestBlockedReason::None;
 }
 
 bool FrameFetchContext::isControlledByServiceWorker() const {
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.h b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
index b35a7fb..3df40818 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.h
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
@@ -46,7 +46,6 @@
 class LocalFrame;
 class ResourceError;
 class ResourceResponse;
-class ResourceRequest;
 
 class CORE_EXPORT FrameFetchContext final : public FetchContext {
  public:
@@ -96,8 +95,9 @@
                                   Resource*) override;
   void dispatchDidReceiveData(unsigned long identifier,
                               const char* data,
-                              int dataLength,
-                              int encodedDataLength) override;
+                              int dataLength) override;
+  void dispatchDidReceiveEncodedData(unsigned long identifier,
+                                     int encodedDataLength) override;
   void dispatchDidDownloadData(unsigned long identifier,
                                int dataLength,
                                int encodedDataLength) override;
@@ -106,6 +106,7 @@
                                 int64_t encodedDataLength) override;
   void dispatchDidFail(unsigned long identifier,
                        const ResourceError&,
+                       int64_t encodedDataLength,
                        bool isInternalRequest) override;
 
   bool shouldLoadNewResource(Resource::Type) const override;
@@ -116,16 +117,18 @@
 
   void addResourceTiming(const ResourceTimingInfo&) override;
   bool allowImage(bool imagesEnabled, const KURL&) const override;
-  bool canRequest(Resource::Type,
-                  const ResourceRequest&,
-                  const KURL&,
-                  const ResourceLoaderOptions&,
-                  bool forPreload,
-                  FetchRequest::OriginRestriction) const override;
-  bool allowResponse(Resource::Type,
-                     const ResourceRequest&,
-                     const KURL&,
-                     const ResourceLoaderOptions&) const override;
+  ResourceRequestBlockedReason canRequest(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&,
+      bool forPreload,
+      FetchRequest::OriginRestriction) const override;
+  ResourceRequestBlockedReason allowResponse(
+      Resource::Type,
+      const ResourceRequest&,
+      const KURL&,
+      const ResourceLoaderOptions&) const override;
 
   bool isControlledByServiceWorker() const override;
   int64_t serviceWorkerID() const override;
diff --git a/third_party/WebKit/Source/core/loader/LinkLoader.cpp b/third_party/WebKit/Source/core/loader/LinkLoader.cpp
index f80e0da7..5abb54d4 100644
--- a/third_party/WebKit/Source/core/loader/LinkLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/LinkLoader.cpp
@@ -47,11 +47,11 @@
 #include "core/loader/NetworkHintsInterface.h"
 #include "core/loader/PrerenderHandle.h"
 #include "core/loader/resource/LinkFetchResource.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/Prerender.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/network/LinkHeader.h"
 #include "platform/network/NetworkHints.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "public/platform/WebPrerender.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/core/loader/PingLoader.cpp b/third_party/WebKit/Source/core/loader/PingLoader.cpp
index accfcbc..76652d4 100644
--- a/third_party/WebKit/Source/core/loader/PingLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/PingLoader.cpp
@@ -52,6 +52,7 @@
 #include "core/loader/FrameLoaderClient.h"
 #include "core/loader/MixedContentChecker.h"
 #include "core/page/Page.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/exported/WrappedResourceResponse.h"
 #include "platform/network/EncodedFormData.h"
@@ -62,7 +63,6 @@
 #include "platform/weborigin/SecurityOrigin.h"
 #include "platform/weborigin/SecurityPolicy.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebURLLoader.h"
 #include "public/platform/WebURLRequest.h"
 #include "public/platform/WebURLResponse.h"
diff --git a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
index 5cd2cdc9..28bb404 100644
--- a/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
+++ b/third_party/WebKit/Source/core/loader/resource/ScriptResource.cpp
@@ -30,8 +30,8 @@
 #include "core/fetch/IntegrityMetadata.h"
 #include "core/fetch/ResourceClientWalker.h"
 #include "core/fetch/ResourceFetcher.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/SharedBuffer.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/tracing/web_memory_allocator_dump.h"
 #include "platform/tracing/web_process_memory_dump.h"
 
diff --git a/third_party/WebKit/Source/core/page/AutoscrollController.cpp b/third_party/WebKit/Source/core/page/AutoscrollController.cpp
index 32b5c38..c907c94 100644
--- a/third_party/WebKit/Source/core/page/AutoscrollController.cpp
+++ b/third_party/WebKit/Source/core/page/AutoscrollController.cpp
@@ -77,6 +77,8 @@
   // it's already active.
   if (m_autoscrollType != NoAutoscroll)
     return;
+  if (layoutObject)
+    layoutObject->frameView()->updateAllLifecyclePhasesExceptPaint();
   LayoutBox* scrollable = LayoutBox::findAutoscrollable(layoutObject);
   if (!scrollable)
     scrollable =
@@ -163,6 +165,10 @@
           dropTargetNode->layoutObject()->frame())
     return;
 
+  dropTargetNode->layoutObject()
+      ->frameView()
+      ->updateAllLifecyclePhasesExceptPaint();
+
   LayoutBox* scrollable =
       LayoutBox::findAutoscrollable(dropTargetNode->layoutObject());
   if (!scrollable) {
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index bcc4c794..0fb7492 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -53,10 +53,10 @@
 #include "core/page/ValidationMessageClient.h"
 #include "core/page/scrolling/ScrollingCoordinator.h"
 #include "core/paint/PaintLayer.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/plugins/PluginData.h"
 #include "public/platform/Platform.h"
-#include "public/platform/WebFrameScheduler.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
index e7926c9..1abf9c82 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.cpp
@@ -73,26 +73,15 @@
   return m_rootScroller;
 }
 
-Node* RootScrollerController::effectiveRootScroller() const {
-  return m_effectiveRootScroller;
+Node& RootScrollerController::effectiveRootScroller() const {
+  DCHECK(m_effectiveRootScroller);
+  return *m_effectiveRootScroller;
 }
 
 void RootScrollerController::didUpdateLayout() {
   recomputeEffectiveRootScroller();
 }
 
-void RootScrollerController::didDisposePaintLayerScrollableArea(
-    PaintLayerScrollableArea& area) {
-  // If the document is being torn down we'll skip a bunch of notifications
-  // so recomputing the effective root scroller could touch dead objects.
-  // (e.g. ScrollAnchor keeps a pointer to dead LayoutObjects).
-  if (!m_effectiveRootScroller || area.box().documentBeingDestroyed())
-    return;
-
-  if (&area.box() == m_effectiveRootScroller->layoutObject())
-    recomputeEffectiveRootScroller();
-}
-
 void RootScrollerController::recomputeEffectiveRootScroller() {
   bool rootScrollerValid =
       m_rootScroller && isValidRootScroller(*m_rootScroller);
@@ -155,7 +144,6 @@
 }
 
 bool RootScrollerController::scrollsViewport(const Element& element) const {
-  DCHECK(m_effectiveRootScroller);
   if (m_effectiveRootScroller->isDocumentNode())
     return element.isSameNode(m_document->documentElement());
 
diff --git a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
index 27ab2f3..167db97 100644
--- a/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
+++ b/third_party/WebKit/Source/core/page/scrolling/RootScrollerController.h
@@ -13,7 +13,6 @@
 class Document;
 class Element;
 class PaintLayer;
-class PaintLayerScrollableArea;
 
 // Manages the root scroller associated with a given document. The root
 // scroller causes browser controls movement, overscroll effects and prevents
@@ -59,18 +58,13 @@
   // This returns the Element that's actually being used to control viewport
   // actions right now. This is different from get() if a root scroller hasn't
   // been set, or if the set root scroller isn't currently a valid scroller.
-  // TODO(bokan): Make this a ref.
-  Node* effectiveRootScroller() const;
+  Node& effectiveRootScroller() const;
 
   // This class needs to be informed of changes in layout so that it can
   // determine if the current root scroller is still valid or if it must be
   // replaced by the default root scroller.
   void didUpdateLayout();
 
-  // PaintLayerScrollableAreas need to notify this class when they're being
-  // disposed so that we can remove them as the root scroller.
-  void didDisposePaintLayerScrollableArea(PaintLayerScrollableArea&);
-
   // Returns the PaintLayer associated with the currently effective root
   // scroller.
   PaintLayer* rootScrollerPaintLayer() const;
@@ -104,7 +98,8 @@
 
   // The Node currently being used as the root scroller in this Document.
   // If the m_rootScroller is valid this will point to it. Otherwise, it'll
-  // use the document Node.
+  // use the document Node. It'll never be nullptr since the Document owns the
+  // RootScrollerController.
   Member<Node> m_effectiveRootScroller;
 
   bool m_documentHasDocumentElement;
diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
index 6fde443..e0d276c 100644
--- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp
@@ -1114,10 +1114,10 @@
            layoutObject->style()->position() == StickyPosition);
     PaintLayer* layer = toLayoutBoxModelObject(layoutObject)->layer();
 
-    // Whether the Layer scrolls with the viewport is a tree-depenent
+    // Whether the Layer sticks to the viewport is a tree-depenent
     // property and our viewportConstrainedObjects collection is maintained
     // with only LayoutObject-level information.
-    if (!layer->scrollsWithViewport())
+    if (!layer->sticksToViewport())
       continue;
 
     // If the whole subtree is invisible, there's no reason to scroll on
diff --git a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
index acda2ed..1f05192 100644
--- a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
+++ b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.cpp
@@ -64,11 +64,9 @@
   if (!topDocument())
     return nullptr;
 
-  DCHECK(topDocument()->rootScrollerController());
   Node* effectiveRootScroller =
-      topDocument()->rootScrollerController()->effectiveRootScroller();
+      &topDocument()->rootScrollerController().effectiveRootScroller();
 
-  DCHECK(effectiveRootScroller);
   if (effectiveRootScroller->isDocumentNode())
     return topDocument()->documentElement();
 
@@ -83,10 +81,8 @@
     if (!iframeDocument)
       return element;
 
-    DCHECK(iframeDocument->rootScrollerController());
     effectiveRootScroller =
-        iframeDocument->rootScrollerController()->effectiveRootScroller();
-    DCHECK(effectiveRootScroller);
+        &iframeDocument->rootScrollerController().effectiveRootScroller();
     if (effectiveRootScroller->isDocumentNode())
       return iframeDocument->documentElement();
 
@@ -167,7 +163,7 @@
 
   PaintLayer* layer = m_globalRootScroller->document()
                           .rootScrollerController()
-                          ->rootScrollerPaintLayer();
+                          .rootScrollerPaintLayer();
 
   if (layer)
     layer->setNeedsCompositingInputsUpdate();
@@ -185,6 +181,26 @@
   m_frameHost->chromeClient().registerViewportLayers();
 }
 
+void TopDocumentRootScrollerController::didDisposeScrollableArea(
+    ScrollableArea& area) {
+  if (!topDocument() || !topDocument()->view())
+    return;
+
+  // If the document is tearing down, we may no longer have a layoutViewport to
+  // fallback to.
+  if (topDocument()->lifecycle().state() >= DocumentLifecycle::Stopping)
+    return;
+
+  FrameView* frameView = topDocument()->view();
+
+  RootFrameViewport* rfv = frameView->getRootFrameViewport();
+
+  if (&area == &rfv->layoutViewport()) {
+    DCHECK(frameView->layoutViewportScrollableArea());
+    rfv->setLayoutViewport(*frameView->layoutViewportScrollableArea());
+  }
+}
+
 void TopDocumentRootScrollerController::initializeViewportScrollCallback(
     RootFrameViewport& rootFrameViewport) {
   DCHECK(m_frameHost);
diff --git a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.h b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.h
index de3752c..c0b5681b 100644
--- a/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.h
+++ b/third_party/WebKit/Source/core/page/scrolling/TopDocumentRootScrollerController.h
@@ -17,6 +17,7 @@
 class PaintLayer;
 class RootFrameViewport;
 class ScrollStateCallback;
+class ScrollableArea;
 class ViewportScrollCallback;
 
 // This class manages the the page level aspects of the root scroller.  That
@@ -36,6 +37,10 @@
   // update the compositor when the effective root scroller changes.
   void didUpdateCompositing();
 
+  // PaintLayerScrollableAreas need to notify this class when they're being
+  // disposed so that we can remove them as the root scroller.
+  void didDisposeScrollableArea(ScrollableArea&);
+
   // This method needs to be called to create a ViewportScrollCallback that
   // will be used to apply viewport scrolling actions like browser controls
   // movement and overscroll glow.
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
index 5b4303bd..e819921 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -198,7 +198,7 @@
     // FIXME: Should eventually give the theme control over whether the box
     // shadow should paint, since controls could have custom shadows of their
     // own.
-    paintBoxShadow(paintInfo, paintRect, style, Normal);
+    paintNormalBoxShadow(paintInfo, paintRect, style);
 
     if (bleedAvoidanceIsClipping(boxDecorationData.bleedAvoidance)) {
       stateSaver.save();
@@ -230,7 +230,7 @@
   }
 
   if (!paintingOverflowContents) {
-    paintBoxShadow(paintInfo, paintRect, style, Inset);
+    paintInsetBoxShadow(paintInfo, paintRect, style);
 
     // The theme will tell us whether or not we should also paint the CSS
     // border.
@@ -912,26 +912,18 @@
   borderPainter.paintBorder(info, rect);
 }
 
-void BoxPainter::paintBoxShadow(const PaintInfo& info,
-                                const LayoutRect& paintRect,
-                                const ComputedStyle& style,
-                                ShadowStyle shadowStyle,
-                                bool includeLogicalLeftEdge,
-                                bool includeLogicalRightEdge) {
-  // FIXME: Deal with border-image. Would be great to use border-image as a
-  // mask.
-  GraphicsContext& context = info.context;
+void BoxPainter::paintNormalBoxShadow(const PaintInfo& info,
+                                      const LayoutRect& paintRect,
+                                      const ComputedStyle& style,
+                                      bool includeLogicalLeftEdge,
+                                      bool includeLogicalRightEdge) {
   if (!style.boxShadow())
     return;
-  FloatRoundedRect border =
-      (shadowStyle == Inset)
-          ? style.getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge,
-                                           includeLogicalRightEdge)
-          : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge,
-                                      includeLogicalRightEdge);
+  GraphicsContext& context = info.context;
+  FloatRoundedRect border = style.getRoundedBorderFor(
+      paintRect, includeLogicalLeftEdge, includeLogicalRightEdge);
 
   bool hasBorderRadius = style.hasBorderRadius();
-  bool isHorizontal = style.isHorizontalWritingMode();
   bool hasOpaqueBackground =
       style.visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
 
@@ -940,7 +932,7 @@
   const ShadowList* shadowList = style.boxShadow();
   for (size_t i = shadowList->shadows().size(); i--;) {
     const ShadowData& shadow = shadowList->shadows()[i];
-    if (shadow.style() != shadowStyle)
+    if (shadow.style() != Normal)
       continue;
 
     FloatSize shadowOffset(shadow.x(), shadow.y());
@@ -953,97 +945,138 @@
     const Color& shadowColor =
         shadow.color().resolve(style.visitedDependentColor(CSSPropertyColor));
 
-    if (shadow.style() == Normal) {
-      FloatRect fillRect = border.rect();
-      fillRect.inflate(shadowSpread);
-      if (fillRect.isEmpty())
-        continue;
+    FloatRect fillRect = border.rect();
+    fillRect.inflate(shadowSpread);
+    if (fillRect.isEmpty())
+      continue;
 
-      FloatRect shadowRect(border.rect());
-      shadowRect.inflate(shadowBlur + shadowSpread);
-      shadowRect.move(shadowOffset);
+    FloatRect shadowRect(border.rect());
+    shadowRect.inflate(shadowBlur + shadowSpread);
+    shadowRect.move(shadowOffset);
 
-      // Save the state and clip, if not already done.
-      // The clip does not depend on any shadow-specific properties.
-      if (!stateSaver.saved()) {
-        stateSaver.save();
-        if (hasBorderRadius) {
-          FloatRoundedRect rectToClipOut = border;
-
-          // If the box is opaque, it is unnecessary to clip it out. However,
-          // doing so saves time when painting the shadow. On the other hand, it
-          // introduces subpixel gaps along the corners. Those are avoided by
-          // insetting the clipping path by one CSS pixel.
-          if (hasOpaqueBackground)
-            rectToClipOut.inflateWithRadii(-1);
-
-          if (!rectToClipOut.isEmpty())
-            context.clipOutRoundedRect(rectToClipOut);
-        } else {
-          // This IntRect is correct even with fractional shadows, because it is
-          // used for the rectangle of the box itself, which is always
-          // pixel-aligned.
-          FloatRect rectToClipOut = border.rect();
-
-          // If the box is opaque, it is unnecessary to clip it out. However,
-          // doing so saves time when painting the shadow. On the other hand, it
-          // introduces subpixel gaps along the edges if they are not
-          // pixel-aligned. Those are avoided by insetting the clipping path by
-          // one CSS pixel.
-          if (hasOpaqueBackground)
-            rectToClipOut.inflate(-1);
-
-          if (!rectToClipOut.isEmpty())
-            context.clipOut(rectToClipOut);
-        }
-      }
-
-      // Draw only the shadow.
-      context.setShadow(shadowOffset, shadowBlur, shadowColor,
-                        DrawLooperBuilder::ShadowRespectsTransforms,
-                        DrawLooperBuilder::ShadowIgnoresAlpha, DrawShadowOnly);
-
+    // Save the state and clip, if not already done.
+    // The clip does not depend on any shadow-specific properties.
+    if (!stateSaver.saved()) {
+      stateSaver.save();
       if (hasBorderRadius) {
-        FloatRoundedRect influenceRect(
-            pixelSnappedIntRect(LayoutRect(shadowRect)), border.getRadii());
-        float changeAmount = 2 * shadowBlur + shadowSpread;
-        if (changeAmount >= 0)
-          influenceRect.expandRadii(changeAmount);
-        else
-          influenceRect.shrinkRadii(-changeAmount);
+        FloatRoundedRect rectToClipOut = border;
 
-        FloatRoundedRect roundedFillRect = border;
-        roundedFillRect.inflate(shadowSpread);
+        // If the box is opaque, it is unnecessary to clip it out. However,
+        // doing so saves time when painting the shadow. On the other hand, it
+        // introduces subpixel gaps along the corners. Those are avoided by
+        // insetting the clipping path by one CSS pixel.
+        if (hasOpaqueBackground)
+          rectToClipOut.inflateWithRadii(-1);
 
-        if (shadowSpread >= 0)
-          roundedFillRect.expandRadii(shadowSpread);
-        else
-          roundedFillRect.shrinkRadii(-shadowSpread);
-        if (!roundedFillRect.isRenderable())
-          roundedFillRect.adjustRadii();
-        roundedFillRect.constrainRadii();
-        context.fillRoundedRect(roundedFillRect, Color::black);
+        if (!rectToClipOut.isEmpty())
+          context.clipOutRoundedRect(rectToClipOut);
       } else {
-        context.fillRect(fillRect, Color::black);
+        // This IntRect is correct even with fractional shadows, because it is
+        // used for the rectangle of the box itself, which is always
+        // pixel-aligned.
+        FloatRect rectToClipOut = border.rect();
+
+        // If the box is opaque, it is unnecessary to clip it out. However,
+        // doing so saves time when painting the shadow. On the other hand, it
+        // introduces subpixel gaps along the edges if they are not
+        // pixel-aligned. Those are avoided by insetting the clipping path by
+        // one CSS pixel.
+        if (hasOpaqueBackground)
+          rectToClipOut.inflate(-1);
+
+        if (!rectToClipOut.isEmpty())
+          context.clipOut(rectToClipOut);
       }
-    } else {
-      // The inset shadow case.
-      GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge;
-      if (!includeLogicalLeftEdge) {
-        if (isHorizontal)
-          clippedEdges |= GraphicsContext::LeftEdge;
-        else
-          clippedEdges |= GraphicsContext::TopEdge;
-      }
-      if (!includeLogicalRightEdge) {
-        if (isHorizontal)
-          clippedEdges |= GraphicsContext::RightEdge;
-        else
-          clippedEdges |= GraphicsContext::BottomEdge;
-      }
-      context.drawInnerShadow(border, shadowColor, shadowOffset, shadowBlur,
-                              shadowSpread, clippedEdges);
     }
+
+    // Draw only the shadow.
+    context.setShadow(shadowOffset, shadowBlur, shadowColor,
+                      DrawLooperBuilder::ShadowRespectsTransforms,
+                      DrawLooperBuilder::ShadowIgnoresAlpha, DrawShadowOnly);
+
+    if (hasBorderRadius) {
+      FloatRoundedRect influenceRect(
+          pixelSnappedIntRect(LayoutRect(shadowRect)), border.getRadii());
+      float changeAmount = 2 * shadowBlur + shadowSpread;
+      if (changeAmount >= 0)
+        influenceRect.expandRadii(changeAmount);
+      else
+        influenceRect.shrinkRadii(-changeAmount);
+
+      FloatRoundedRect roundedFillRect = border;
+      roundedFillRect.inflate(shadowSpread);
+
+      if (shadowSpread >= 0)
+        roundedFillRect.expandRadii(shadowSpread);
+      else
+        roundedFillRect.shrinkRadii(-shadowSpread);
+      if (!roundedFillRect.isRenderable())
+        roundedFillRect.adjustRadii();
+      roundedFillRect.constrainRadii();
+      context.fillRoundedRect(roundedFillRect, Color::black);
+    } else {
+      context.fillRect(fillRect, Color::black);
+    }
+  }
+}
+
+void BoxPainter::paintInsetBoxShadow(const PaintInfo& info,
+                                     const LayoutRect& paintRect,
+                                     const ComputedStyle& style,
+                                     bool includeLogicalLeftEdge,
+                                     bool includeLogicalRightEdge) {
+  if (!style.boxShadow())
+    return;
+  FloatRoundedRect bounds = style.getRoundedInnerBorderFor(
+      paintRect, includeLogicalLeftEdge, includeLogicalRightEdge);
+  paintInsetBoxShadowInBounds(info, bounds, style, includeLogicalLeftEdge,
+                              includeLogicalRightEdge);
+}
+
+void BoxPainter::paintInsetBoxShadowInBounds(const PaintInfo& info,
+                                             const FloatRoundedRect& bounds,
+                                             const ComputedStyle& style,
+                                             bool includeLogicalLeftEdge,
+                                             bool includeLogicalRightEdge) {
+  // The caller should have checked style.boxShadow() when computing bounds.
+  DCHECK(style.boxShadow());
+  GraphicsContext& context = info.context;
+
+  bool isHorizontal = style.isHorizontalWritingMode();
+  GraphicsContextStateSaver stateSaver(context, false);
+
+  const ShadowList* shadowList = style.boxShadow();
+  for (size_t i = shadowList->shadows().size(); i--;) {
+    const ShadowData& shadow = shadowList->shadows()[i];
+    if (shadow.style() != Inset)
+      continue;
+
+    FloatSize shadowOffset(shadow.x(), shadow.y());
+    float shadowBlur = shadow.blur();
+    float shadowSpread = shadow.spread();
+
+    if (shadowOffset.isZero() && !shadowBlur && !shadowSpread)
+      continue;
+
+    const Color& shadowColor =
+        shadow.color().resolve(style.visitedDependentColor(CSSPropertyColor));
+
+    // The inset shadow case.
+    GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge;
+    if (!includeLogicalLeftEdge) {
+      if (isHorizontal)
+        clippedEdges |= GraphicsContext::LeftEdge;
+      else
+        clippedEdges |= GraphicsContext::TopEdge;
+    }
+    if (!includeLogicalRightEdge) {
+      if (isHorizontal)
+        clippedEdges |= GraphicsContext::RightEdge;
+      else
+        clippedEdges |= GraphicsContext::BottomEdge;
+    }
+    context.drawInnerShadow(bounds, shadowColor, shadowOffset, shadowBlur,
+                            shadowSpread, clippedEdges);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.h b/third_party/WebKit/Source/core/paint/BoxPainter.h
index 331a6351..5b03844 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainter.h
+++ b/third_party/WebKit/Source/core/paint/BoxPainter.h
@@ -16,6 +16,7 @@
 
 class ComputedStyle;
 class FillLayer;
+class FloatRoundedRect;
 class GraphicsContext;
 class Image;
 class InlineFlowBox;
@@ -90,12 +91,26 @@
                           BackgroundBleedAvoidance = BackgroundBleedNone,
                           bool includeLogicalLeftEdge = true,
                           bool includeLogicalRightEdge = true);
-  static void paintBoxShadow(const PaintInfo&,
-                             const LayoutRect&,
-                             const ComputedStyle&,
-                             ShadowStyle,
-                             bool includeLogicalLeftEdge = true,
-                             bool includeLogicalRightEdge = true);
+  static void paintNormalBoxShadow(const PaintInfo&,
+                                   const LayoutRect&,
+                                   const ComputedStyle&,
+                                   bool includeLogicalLeftEdge = true,
+                                   bool includeLogicalRightEdge = true);
+  // The input rect should be the border rect. The outer bounds of the shadow
+  // will be inset by border widths.
+  static void paintInsetBoxShadow(const PaintInfo&,
+                                  const LayoutRect&,
+                                  const ComputedStyle&,
+                                  bool includeLogicalLeftEdge = true,
+                                  bool includeLogicalRightEdge = true);
+  // This form is used by callers requiring special computation of the outer
+  // bounds of the shadow. For example, TableCellPainter insets the bounds by
+  // half widths of collapsed borders instead of the default whole widths.
+  static void paintInsetBoxShadowInBounds(const PaintInfo&,
+                                          const FloatRoundedRect& bounds,
+                                          const ComputedStyle&,
+                                          bool includeLogicalLeftEdge = true,
+                                          bool includeLogicalRightEdge = true);
   static bool shouldForceWhiteBackgroundForPrintEconomy(const ComputedStyle&,
                                                         const Document&);
 
diff --git a/third_party/WebKit/Source/core/paint/FieldsetPainter.cpp b/third_party/WebKit/Source/core/paint/FieldsetPainter.cpp
index 26162b3..f2b826f 100644
--- a/third_party/WebKit/Source/core/paint/FieldsetPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/FieldsetPainter.cpp
@@ -31,18 +31,16 @@
   // https://bugs.webkit.org/show_bug.cgi?id=47236
   if (m_layoutFieldset.style()->isHorizontalWritingMode()) {
     LayoutUnit yOff =
-        (legend->location().y() +
-         (legend->size().height() - m_layoutFieldset.borderTop()) / 2)
-            .clampNegativeToZero();
-
+        (legend->location().y() > 0)
+            ? LayoutUnit()
+            : (legend->size().height() - m_layoutFieldset.borderTop()) / 2;
     paintRect.setHeight(paintRect.height() - yOff);
     paintRect.setY(paintRect.y() + yOff);
   } else {
     LayoutUnit xOff =
-        (legend->location().x() +
-         (legend->size().width() - m_layoutFieldset.borderLeft()) / 2)
-            .clampNegativeToZero();
-
+        (legend->location().x() > 0)
+            ? LayoutUnit()
+            : (legend->size().width() - m_layoutFieldset.borderLeft()) / 2;
     paintRect.setWidth(paintRect.width() - xOff);
     paintRect.setX(paintRect.x() + xOff);
   }
@@ -51,14 +49,15 @@
                                        paintInfo.phase, paintRect);
   BoxDecorationData boxDecorationData(m_layoutFieldset);
 
-  if (boxDecorationData.bleedAvoidance == BackgroundBleedNone)
-    BoxPainter::paintBoxShadow(paintInfo, paintRect,
-                               m_layoutFieldset.styleRef(), Normal);
+  if (boxDecorationData.bleedAvoidance == BackgroundBleedNone) {
+    BoxPainter::paintNormalBoxShadow(paintInfo, paintRect,
+                                     m_layoutFieldset.styleRef());
+  }
   BoxPainter(m_layoutFieldset)
       .paintFillLayers(paintInfo, boxDecorationData.backgroundColor,
                        m_layoutFieldset.style()->backgroundLayers(), paintRect);
-  BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutFieldset.styleRef(),
-                             Inset);
+  BoxPainter::paintInsetBoxShadow(paintInfo, paintRect,
+                                  m_layoutFieldset.styleRef());
 
   if (!boxDecorationData.hasBorderDecoration)
     return;
@@ -113,18 +112,16 @@
   // https://bugs.webkit.org/show_bug.cgi?id=47236
   if (m_layoutFieldset.style()->isHorizontalWritingMode()) {
     LayoutUnit yOff =
-        (legend->location().y() +
-         (legend->size().height() - m_layoutFieldset.borderTop()) / 2)
-            .clampNegativeToZero();
-
+        (legend->location().y() > LayoutUnit())
+            ? LayoutUnit()
+            : (legend->size().height() - m_layoutFieldset.borderTop()) / 2;
     paintRect.expand(LayoutUnit(), -yOff);
     paintRect.move(LayoutUnit(), yOff);
   } else {
     LayoutUnit xOff =
-        (legend->location().x() +
-         (legend->size().width() - m_layoutFieldset.borderLeft()) / 2)
-            .clampNegativeToZero();
-
+        (legend->location().x() > LayoutUnit())
+            ? LayoutUnit()
+            : (legend->size().width() - m_layoutFieldset.borderLeft()) / 2;
     paintRect.expand(-xOff, LayoutUnit());
     paintRect.move(xOff, LayoutUnit());
   }
diff --git a/third_party/WebKit/Source/core/paint/FramePainter.cpp b/third_party/WebKit/Source/core/paint/FramePainter.cpp
index 6fcc221..dc25b19 100644
--- a/third_party/WebKit/Source/core/paint/FramePainter.cpp
+++ b/third_party/WebKit/Source/core/paint/FramePainter.cpp
@@ -166,7 +166,7 @@
 
   PaintLayer* rootLayer = layoutView->layer();
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   layoutView->assertSubtreeIsLaidOut();
   LayoutObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(
       *rootLayer->layoutObject());
diff --git a/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.cpp b/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.cpp
index 9e59672..e43cd13 100644
--- a/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.cpp
@@ -109,21 +109,35 @@
   }
 }
 
-void InlineFlowBoxPainter::paintBoxShadow(const PaintInfo& info,
-                                          const ComputedStyle& s,
-                                          ShadowStyle shadowStyle,
-                                          const LayoutRect& paintRect) {
-  if ((!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) ||
-      !m_inlineFlowBox.parent()) {
-    BoxPainter::paintBoxShadow(info, paintRect, s, shadowStyle);
-  } else {
-    // FIXME: We can do better here in the multi-line case. We want to push a
-    // clip so that the shadow doesn't protrude incorrectly at the edges, and we
-    // want to possibly include shadows cast from the previous/following lines
-    BoxPainter::paintBoxShadow(info, paintRect, s, shadowStyle,
-                               m_inlineFlowBox.includeLogicalLeftEdge(),
-                               m_inlineFlowBox.includeLogicalRightEdge());
-  }
+inline bool InlineFlowBoxPainter::shouldForceIncludeLogicalEdges() const {
+  return (!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) ||
+         !m_inlineFlowBox.parent();
+}
+
+inline bool InlineFlowBoxPainter::includeLogicalLeftEdgeForBoxShadow() const {
+  return shouldForceIncludeLogicalEdges() ||
+         m_inlineFlowBox.includeLogicalLeftEdge();
+}
+
+inline bool InlineFlowBoxPainter::includeLogicalRightEdgeForBoxShadow() const {
+  return shouldForceIncludeLogicalEdges() ||
+         m_inlineFlowBox.includeLogicalRightEdge();
+}
+
+void InlineFlowBoxPainter::paintNormalBoxShadow(const PaintInfo& info,
+                                                const ComputedStyle& s,
+                                                const LayoutRect& paintRect) {
+  BoxPainter::paintNormalBoxShadow(info, paintRect, s,
+                                   includeLogicalLeftEdgeForBoxShadow(),
+                                   includeLogicalRightEdgeForBoxShadow());
+}
+
+void InlineFlowBoxPainter::paintInsetBoxShadow(const PaintInfo& info,
+                                               const ComputedStyle& s,
+                                               const LayoutRect& paintRect) {
+  BoxPainter::paintInsetBoxShadow(info, paintRect, s,
+                                  includeLogicalLeftEdgeForBoxShadow(),
+                                  includeLogicalRightEdgeForBoxShadow());
 }
 
 static LayoutRect clipRectForNinePieceImageStrip(const InlineFlowBox& box,
@@ -276,13 +290,13 @@
       getBorderPaintType(adjustedFrameRect, adjustedClipRect);
 
   // Shadow comes first and is behind the background and border.
-  paintBoxShadow(paintInfo, *styleToUse, Normal, adjustedFrameRect);
+  paintNormalBoxShadow(paintInfo, *styleToUse, adjustedFrameRect);
 
   Color backgroundColor = inlineFlowBoxLayoutObject->resolveColor(
       *styleToUse, CSSPropertyBackgroundColor);
   paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(),
                   adjustedFrameRect);
-  paintBoxShadow(paintInfo, *styleToUse, Inset, adjustedFrameRect);
+  paintInsetBoxShadow(paintInfo, *styleToUse, adjustedFrameRect);
 
   switch (borderPaintingType) {
     case DontPaintBorders:
diff --git a/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.h b/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.h
index 21ac8976..9658a99 100644
--- a/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.h
+++ b/third_party/WebKit/Source/core/paint/InlineFlowBoxPainter.h
@@ -51,10 +51,15 @@
                       const FillLayer&,
                       const LayoutRect&,
                       SkBlendMode op);
-  void paintBoxShadow(const PaintInfo&,
-                      const ComputedStyle&,
-                      ShadowStyle,
-                      const LayoutRect& paintRect);
+  inline bool shouldForceIncludeLogicalEdges() const;
+  inline bool includeLogicalLeftEdgeForBoxShadow() const;
+  inline bool includeLogicalRightEdgeForBoxShadow() const;
+  void paintNormalBoxShadow(const PaintInfo&,
+                            const ComputedStyle&,
+                            const LayoutRect& paintRect);
+  void paintInsetBoxShadow(const PaintInfo&,
+                           const ComputedStyle&,
+                           const LayoutRect& paintRect);
   LayoutRect paintRectForImageStrip(const LayoutPoint& paintOffset,
                                     const LayoutSize& frameSize,
                                     TextDirection) const;
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.cpp b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.cpp
index 9f8f334..54740da2 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPaintProperties.cpp
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintProperties.cpp
@@ -14,6 +14,9 @@
   if (scrollTranslation())
     propertiesWithOffset.propertyTreeState.setTransform(scrollTranslation());
 
+  if (scroll())
+    propertiesWithOffset.propertyTreeState.setScroll(scroll());
+
   if (overflowClip())
     propertiesWithOffset.propertyTreeState.setClip(overflowClip());
   else if (cssClip())
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 9982e8a0..16d3f4c 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -71,7 +71,10 @@
 
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
     // In SPv2, visual rects are in the space of their local transform node.
-    rect.moveBy(FloatPoint(context.treeBuilderContext.current.paintOffset));
+    // For SVG, the input rect is in local SVG coordinates in which paint
+    // offset doesn't apply.
+    if (!object.isSVG() || object.isSVGRoot())
+      rect.moveBy(FloatPoint(context.treeBuilderContext.current.paintOffset));
     // Use enclosingIntRect to ensure the final visual rect will cover the
     // rect in source coordinates no matter if the painting will use pixel
     // snapping.
@@ -86,8 +89,10 @@
   } else if (object == context.paintInvalidationContainer) {
     result = LayoutRect(rect);
   } else {
-    rect.moveBy(FloatPoint(context.treeBuilderContext.current.paintOffset));
-    if ((!object.isSVG() || object.isSVGRoot()) && !rect.isEmpty()) {
+    // For non-root SVG, the input rect is in local SVG coordinates in which
+    // paint offset doesn't apply.
+    if (!object.isSVG() || object.isSVGRoot()) {
+      rect.moveBy(FloatPoint(context.treeBuilderContext.current.paintOffset));
       // Use enclosingIntRect to ensure the final visual rect will cover the
       // rect in source coordinates no matter if the painting will use pixel
       // snapping.
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 13ad90d..8a2ceaf 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -338,16 +338,42 @@
   }
 }
 
-bool PaintLayer::scrollsWithViewport() const {
+bool PaintLayer::sticksToViewport() const {
+  if (layoutObject()->style()->position() != FixedPosition &&
+      layoutObject()->style()->position() != StickyPosition)
+    return false;
+
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
+    const ScrollPaintPropertyNode* ancestorTargetScrollNode;
+    if (layoutObject()->style()->position() == FixedPosition) {
+      ancestorTargetScrollNode = layoutObject()
+                                     ->view()
+                                     ->paintProperties()
+                                     ->localBorderBoxProperties()
+                                     ->propertyTreeState.scroll();
+    } else {
+      ancestorTargetScrollNode = layoutObject()
+                                     ->view()
+                                     ->paintProperties()
+                                     ->contentsProperties()
+                                     .propertyTreeState.scroll();
+    }
+
+    return layoutObject()
+               ->paintProperties()
+               ->localBorderBoxProperties()
+               ->propertyTreeState.scroll() == ancestorTargetScrollNode;
+  }
+
   return (layoutObject()->style()->position() == FixedPosition &&
           layoutObject()->containerForFixedPosition() ==
               layoutObject()->view()) ||
          (layoutObject()->style()->position() == StickyPosition &&
-          !ancestorScrollingLayer());
+          (!ancestorScrollingLayer() || ancestorScrollingLayer() == root()));
 }
 
 bool PaintLayer::scrollsWithRespectTo(const PaintLayer* other) const {
-  if (scrollsWithViewport() != other->scrollsWithViewport())
+  if (sticksToViewport() != other->sticksToViewport())
     return true;
   return ancestorScrollingLayer() != other->ancestorScrollingLayer();
 }
@@ -698,7 +724,7 @@
                                         child->layoutObject()
                                             ->document()
                                             .rootScrollerController()
-                                            ->rootScrollerPaintLayer());
+                                            .rootScrollerPaintLayer());
     }
 
     m_needsDescendantDependentFlagsUpdate = false;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index cd8aafc..f1ad9e2 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -600,7 +600,7 @@
 
   bool isInTopLayer() const;
 
-  bool scrollsWithViewport() const;
+  bool sticksToViewport() const;
   bool scrollsWithRespectTo(const PaintLayer*) const;
 
   void addLayerHitTestRects(LayerHitTestRects&) const;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index be91e8d..a6adbe3 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -163,9 +163,11 @@
       frameView->removeResizerArea(box());
   }
 
-  if (RootScrollerController* controller =
-          box().document().rootScrollerController())
-    controller->didDisposePaintLayerScrollableArea(*this);
+  box()
+      .document()
+      .frameHost()
+      ->globalRootScrollerController()
+      .didDisposeScrollableArea(*this);
 
   m_scrollbarManager.dispose();
 
@@ -442,6 +444,18 @@
     box().setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
   }
 
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    // The scrollOffsetTranslation paint property depends on the scroll offset.
+    // (see: PaintPropertyTreeBuilder.updateProperties(FrameView&,...) and
+    // PaintPropertyTreeBuilder.updateScrollAndScrollTranslation).
+    if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
+        layer()->isRootLayer()) {
+      frameView->setNeedsPaintPropertyUpdate();
+    } else {
+      box().setNeedsPaintPropertyUpdate();
+    }
+  }
+
   // Schedule the scroll DOM event.
   if (box().node())
     box().node()->document().enqueueScrollEventForNode(box().node());
@@ -1671,6 +1685,12 @@
   if (didScrollOverflow == scrollsOverflow())
     return;
 
+  if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled()) {
+    // The scroll and scroll offset properties depend on |scrollsOverflow| (see:
+    // PaintPropertyTreeBuilder::updateScrollAndScrollTranslation).
+    box().setNeedsPaintPropertyUpdate();
+  }
+
   if (m_scrollsOverflow) {
     DCHECK(canHaveOverflowScrollbars(box()));
     frameView->addScrollableArea(this);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
index bdcf181..f137b1c 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
@@ -8,16 +8,37 @@
 #include "core/layout/LayoutBoxModelObject.h"
 #include "core/layout/LayoutTestHelper.h"
 #include "core/layout/LayoutView.h"
+#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
 #include "platform/testing/UnitTestHelpers.h"
 
 namespace blink {
 
-class PaintLayerTest : public RenderingTest {
+typedef std::pair<bool, bool> SlimmingPaintAndRootLayerScrolling;
+class PaintLayerTest
+    : public ::testing::WithParamInterface<SlimmingPaintAndRootLayerScrolling>,
+      private ScopedSlimmingPaintV2ForTest,
+      private ScopedRootLayerScrollingForTest,
+      public RenderingTest {
  public:
-  PaintLayerTest() : RenderingTest(SingleChildFrameLoaderClient::create()) {}
+  PaintLayerTest()
+      : ScopedSlimmingPaintV2ForTest(GetParam().first),
+        ScopedRootLayerScrollingForTest(GetParam().second),
+        RenderingTest(SingleChildFrameLoaderClient::create()) {}
 };
 
-TEST_F(PaintLayerTest, CompositedBoundsAbsPosGrandchild) {
+SlimmingPaintAndRootLayerScrolling foo[] = {
+    SlimmingPaintAndRootLayerScrolling(false, false),
+    SlimmingPaintAndRootLayerScrolling(true, false),
+    SlimmingPaintAndRootLayerScrolling(false, true),
+    SlimmingPaintAndRootLayerScrolling(true, true)};
+
+INSTANTIATE_TEST_CASE_P(All, PaintLayerTest, ::testing::ValuesIn(foo));
+
+TEST_P(PaintLayerTest, CompositedBoundsAbsPosGrandchild) {
+  // TODO(chrishtr): fix this test for SPv2
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    return;
+
   setBodyInnerHTML(
       " <div id='parent'><div id='absposparent'><div id='absposchild'>"
       " </div></div></div>"
@@ -37,7 +58,7 @@
             parentLayer->boundingBoxForCompositing());
 }
 
-TEST_F(PaintLayerTest, PaintingExtentReflection) {
+TEST_P(PaintLayerTest, PaintingExtentReflection) {
   setBodyInnerHTML(
       "<div id='target' style='background-color: blue; position: absolute;"
       "    width: 110px; height: 120px; top: 40px; left: 60px;"
@@ -51,7 +72,7 @@
       layer->paintingExtent(document().layoutView()->layer(), LayoutSize(), 0));
 }
 
-TEST_F(PaintLayerTest, PaintingExtentReflectionWithTransform) {
+TEST_P(PaintLayerTest, PaintingExtentReflectionWithTransform) {
   setBodyInnerHTML(
       "<div id='target' style='background-color: blue; position: absolute;"
       "    width: 110px; height: 120px; top: 40px; left: 60px;"
@@ -65,7 +86,94 @@
       layer->paintingExtent(document().layoutView()->layer(), LayoutSize(), 0));
 }
 
-TEST_F(PaintLayerTest, CompositedScrollingNoNeedsRepaint) {
+TEST_P(PaintLayerTest, ScrollsWithViewportRelativePosition) {
+  setBodyInnerHTML("<div id='target' style='position: relative'></div>");
+
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_FALSE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, ScrollsWithViewportFixedPosition) {
+  setBodyInnerHTML("<div id='target' style='position: fixed'></div>");
+
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_TRUE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, ScrollsWithViewportFixedPositionInsideTransform) {
+  // We don't intend to launch SPv2 without root layer scrolling, so skip this
+  // test in that configuration because it's broken.
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() &&
+      !RuntimeEnabledFeatures::rootLayerScrollingEnabled())
+    return;
+  setBodyInnerHTML(
+      "<div style='transform: translateZ(0)'>"
+      "  <div id='target' style='position: fixed'></div>"
+      "</div>"
+      "<div style='width: 10px; height: 1000px'></div>");
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_FALSE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest,
+       ScrollsWithViewportFixedPositionInsideTransformNoScroll) {
+  setBodyInnerHTML(
+      "<div style='transform: translateZ(0)'>"
+      "  <div id='target' style='position: fixed'></div>"
+      "</div>");
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+
+  // In SPv2 mode, we correctly determine that the frame doesn't scroll at all,
+  // and so return true.
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    EXPECT_TRUE(layer->sticksToViewport());
+  else
+    EXPECT_FALSE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, ScrollsWithViewportStickyPosition) {
+  setBodyInnerHTML(
+      "<div style='transform: translateZ(0)'>"
+      "  <div id='target' style='position: sticky'></div>"
+      "</div>"
+      "<div style='width: 10px; height: 1000px'></div>");
+
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_TRUE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, ScrollsWithViewportStickyPositionNoScroll) {
+  setBodyInnerHTML(
+      "<div style='transform: translateZ(0)'>"
+      "  <div id='target' style='position: sticky'></div>"
+      "</div>");
+
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_TRUE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, ScrollsWithViewportStickyPositionInsideScroller) {
+  setBodyInnerHTML(
+      "<div style='overflow:scroll; width: 100px; height: 100px;'>"
+      "  <div id='target' style='position: sticky'></div>"
+      "  <div style='width: 50px; height: 1000px;'></div>"
+      "</div>");
+
+  PaintLayer* layer =
+      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
+  EXPECT_FALSE(layer->sticksToViewport());
+}
+
+TEST_P(PaintLayerTest, CompositedScrollingNoNeedsRepaint) {
+  if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+    return;
+
   enableCompositing();
   setBodyInnerHTML(
       "<div id='scroll' style='width: 100px; height: 100px; overflow: scroll;"
@@ -92,7 +200,7 @@
   document().view()->updateAllLifecyclePhases();
 }
 
-TEST_F(PaintLayerTest, NonCompositedScrollingNeedsRepaint) {
+TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) {
   setBodyInnerHTML(
       "<div id='scroll' style='width: 100px; height: 100px; overflow: scroll'>"
       "  <div id='content' style='position: relative; background: blue;"
@@ -117,7 +225,7 @@
   document().view()->updateAllLifecyclePhases();
 }
 
-TEST_F(PaintLayerTest, HasNonIsolatedDescendantWithBlendMode) {
+TEST_P(PaintLayerTest, HasNonIsolatedDescendantWithBlendMode) {
   setBodyInnerHTML(
       "<div id='stacking-grandparent' style='isolation: isolate'>"
       "  <div id='stacking-parent' style='isolation: isolate'>"
@@ -145,7 +253,7 @@
   EXPECT_TRUE(parent->hasVisibleDescendant());
 }
 
-TEST_F(PaintLayerTest, HasDescendantWithClipPath) {
+TEST_P(PaintLayerTest, HasDescendantWithClipPath) {
   setBodyInnerHTML(
       "<div id='parent' style='position:relative'>"
       "  <div id='clip-path' style='clip-path: circle(50px at 0 100px)'>"
@@ -163,7 +271,7 @@
   EXPECT_TRUE(parent->hasVisibleDescendant());
 }
 
-TEST_F(PaintLayerTest, HasVisibleDescendant) {
+TEST_P(PaintLayerTest, HasVisibleDescendant) {
   enableCompositing();
   setBodyInnerHTML(
       "<div id='invisible' style='position:relative'>"
@@ -182,7 +290,7 @@
   EXPECT_FALSE(invisible->hasDescendantWithClipPath());
 }
 
-TEST_F(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) {
+TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) {
   enableCompositing();
   setBodyInnerHTML(
       "<style>body { margin: 0; }</style>"
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index a74a9cf..a0a17d3 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -640,15 +640,34 @@
   context.current.paintOffset = LayoutPoint();
 }
 
+MainThreadScrollingReasons mainScrollingReasons(const LayoutObject& object) {
+  MainThreadScrollingReasons reasons = 0;
+  if (!object.document().settings()->threadedScrollingEnabled())
+    reasons |= MainThreadScrollingReason::kThreadedScrollingDisabled;
+  // Checking for descendants in the layout tree has two downsides:
+  // 1) There can be more descendants in layout order than in paint order (e.g.,
+  //    fixed position objects).
+  // 2) Iterating overall all background attachment fixed objects for every
+  //    scroll node can be slow, though there will be none in the common case.
+  const FrameView& frameView = *object.frameView();
+  if (frameView.hasBackgroundAttachmentFixedDescendants(object))
+    reasons |= MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
+  return reasons;
+}
+
 void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
   if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
+    bool needsScrollProperties = false;
     if (object.hasOverflowClip()) {
+      auto mainThreadScrollingReasons = mainScrollingReasons(object);
       const LayoutBox& box = toLayoutBox(object);
-      const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea();
+      const auto* scrollableArea = box.getScrollableArea();
       IntSize scrollOffset = box.scrolledContentOffset();
-      if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) {
+      if (mainThreadScrollingReasons || !scrollOffset.isZero() ||
+          scrollableArea->scrollsOverflow()) {
+        needsScrollProperties = true;
         auto& properties =
             object.getMutableForPainting().ensurePaintProperties();
         TransformationMatrix matrix = TransformationMatrix().translate(
@@ -664,32 +683,18 @@
             scrollableArea->userInputScrollable(HorizontalScrollbar);
         bool userScrollableVertical =
             scrollableArea->userInputScrollable(VerticalScrollbar);
-        MainThreadScrollingReasons reasons = 0;
-        if (!object.document().settings()->threadedScrollingEnabled())
-          reasons |= MainThreadScrollingReason::kThreadedScrollingDisabled;
-        // Checking for descendants in the layout tree has two downsides:
-        // 1) There can be more descendants in layout order than in paint
-        //    order (e.g., fixed position objects).
-        // 2) Iterating overall all background attachment fixed objects for
-        //    every scroll node can be slow, though there will be no objects
-        //    in the common case.
-        const FrameView& frameView = *object.frameView();
-        if (frameView.hasBackgroundAttachmentFixedDescendants(object)) {
-          reasons |=
-              MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
-        }
         context.forceSubtreeUpdate |= properties.updateScroll(
             context.current.scroll, properties.scrollTranslation(), scrollClip,
             scrollBounds, userScrollableHorizontal, userScrollableVertical,
-            reasons);
-      } else {
-        // Ensure pre-existing properties are cleared when there is no
-        // scrolling.
-        auto* properties = object.getMutableForPainting().paintProperties();
-        if (properties) {
-          context.forceSubtreeUpdate |= properties->clearScrollTranslation();
-          context.forceSubtreeUpdate |= properties->clearScroll();
-        }
+            mainThreadScrollingReasons);
+      }
+    }
+
+    if (!needsScrollProperties) {
+      // Ensure pre-existing properties are cleared.
+      if (auto* properties = object.getMutableForPainting().paintProperties()) {
+        context.forceSubtreeUpdate |= properties->clearScrollTranslation();
+        context.forceSubtreeUpdate |= properties->clearScroll();
       }
     }
   }
@@ -791,14 +796,13 @@
   context.scroll = properties->propertyTreeState.scroll();
 }
 
-static void deriveBorderBoxFromContainerContext(
+void PaintPropertyTreeBuilder::updateContextForBoxPosition(
     const LayoutObject& object,
     PaintPropertyTreeBuilderContext& context) {
   if (!object.isBoxModelObject())
     return;
 
   const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);
-
   switch (object.styleRef().position()) {
     case StaticPosition:
       break;
@@ -884,8 +888,6 @@
   FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context);
 #endif
 
-  deriveBorderBoxFromContainerContext(object, context);
-
   updatePaintOffsetTranslation(object, context);
   updateTransform(object, context);
   updateEffect(object, context);
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
index e1f5e391..350073f4 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.h
@@ -96,6 +96,11 @@
   // date.
   void updateProperties(FrameView&, PaintPropertyTreeBuilderContext&);
 
+  // Update the context to account for positioning. This can affect the
+  // current paint offset, for example, but no paint properties are updated.
+  void updateContextForBoxPosition(const LayoutObject&,
+                                   PaintPropertyTreeBuilderContext&);
+
   // Update the paint properties that affect this object (e.g., properties like
   // paint offset translation) and ensure the context is up to date.
   void updatePropertiesForSelf(const LayoutObject&,
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index f891580..d54efdb 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -2696,6 +2696,36 @@
                    ->hasBackgroundAttachmentFixedDescendants());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, MainThreadScrollReasonsWithoutScrolling) {
+  setBodyInnerHTML(
+      "<style>"
+      "  #overflow {"
+      "    overflow: scroll;"
+      "    width: 100px;"
+      "    height: 100px;"
+      "  }"
+      "  .backgroundAttachmentFixed {"
+      "    background-image: url('foo');"
+      "    background-attachment: fixed;"
+      "    width: 10px;"
+      "    height: 10px;"
+      "  }"
+      "  .forceScroll {"
+      "    height: 4000px;"
+      "  }"
+      "</style>"
+      "<div id='overflow'>"
+      "  <div class='backgroundAttachmentFixed'></div>"
+      "</div>"
+      "<div class='forceScroll'></div>");
+  Element* overflow = document().getElementById("overflow");
+  EXPECT_TRUE(frameScroll()->hasBackgroundAttachmentFixedDescendants());
+  EXPECT_TRUE(overflow->layoutObject()
+                  ->paintProperties()
+                  ->scroll()
+                  ->hasBackgroundAttachmentFixedDescendants());
+}
+
 TEST_P(PaintPropertyTreeBuilderTest,
        BackgroundAttachmentFixedMainThreadScrollReasonsWithFixedScroller) {
   setBodyInnerHTML(
@@ -3029,4 +3059,83 @@
   EXPECT_FALSE(iframeTransform->descendantNeedsPaintPropertyUpdate());
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, ClipChangesUpdateOverflowClip) {
+  setBodyInnerHTML(
+      "<style>"
+      "  body { margin:0 }"
+      "  #div { overflow:hidden; height:0px; }"
+      "</style>"
+      "<div id='div'></div>");
+  auto* div = document().getElementById("div");
+  div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:7px;");
+  document().view()->updateAllLifecyclePhases();
+  auto* clipProperties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 7, 0), clipProperties->clipRect().rect());
+
+  // Width changes should update the overflow clip.
+  div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:7px;");
+  document().view()->updateAllLifecyclePhases();
+  clipProperties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 7, 0), clipProperties->clipRect().rect());
+  div->setAttribute(HTMLNames::styleAttr, "display:inline-block; width:9px;");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(FloatRect(0, 0, 9, 0), clipProperties->clipRect().rect());
+
+  // An inline block's overflow clip should be updated when padding changes,
+  // even if the border box remains unchanged.
+  div->setAttribute(HTMLNames::styleAttr,
+                    "display:inline-block; width:7px; padding-right:3px;");
+  document().view()->updateAllLifecyclePhases();
+  clipProperties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 10, 0), clipProperties->clipRect().rect());
+  div->setAttribute(HTMLNames::styleAttr,
+                    "display:inline-block; width:8px; padding-right:2px;");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(FloatRect(0, 0, 10, 0), clipProperties->clipRect().rect());
+  div->setAttribute(HTMLNames::styleAttr,
+                    "display:inline-block; width:8px;"
+                    "padding-right:1px; padding-left:1px;");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(FloatRect(0, 0, 10, 0), clipProperties->clipRect().rect());
+
+  // An block's overflow clip should be updated when borders change.
+  div->setAttribute(HTMLNames::styleAttr, "border-right:3px solid red;");
+  document().view()->updateAllLifecyclePhases();
+  clipProperties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 797, 0), clipProperties->clipRect().rect());
+  div->setAttribute(HTMLNames::styleAttr, "border-right:5px solid red;");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_EQ(FloatRect(0, 0, 795, 0), clipProperties->clipRect().rect());
+
+  // Removing overflow clip should remove the property.
+  div->setAttribute(HTMLNames::styleAttr, "overflow:hidden;");
+  document().view()->updateAllLifecyclePhases();
+  clipProperties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 800, 0), clipProperties->clipRect().rect());
+  div->setAttribute(HTMLNames::styleAttr, "overflow:visible;");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_TRUE(!div->layoutObject()->paintProperties() ||
+              !div->layoutObject()->paintProperties()->overflowClip());
+}
+
+// crbug.com:645667: Contain paint changes fail to update paint properties.
+TEST_P(PaintPropertyTreeBuilderTest,
+       DISABLED_ContainPaintChangesUpdateOverflowClip) {
+  setBodyInnerHTML(
+      "<style>"
+      "  body { margin:0 }"
+      "  #div { will-change:transform; width:7px; height:6px; }"
+      "</style>"
+      "<div id='div' style='contain:paint;'></div>");
+  document().view()->updateAllLifecyclePhases();
+  auto* div = document().getElementById("div");
+  auto* properties = div->layoutObject()->paintProperties()->overflowClip();
+  EXPECT_EQ(FloatRect(0, 0, 7, 6), properties->clipRect().rect());
+
+  div->setAttribute(HTMLNames::styleAttr, "");
+  document().view()->updateAllLifecyclePhases();
+  EXPECT_TRUE(!div->layoutObject()->paintProperties() ||
+              !div->layoutObject()->paintProperties()->overflowClip());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/PaintTiming.cpp b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
index b5964500..2536ce0 100644
--- a/third_party/WebKit/Source/core/paint/PaintTiming.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintTiming.cpp
@@ -8,8 +8,8 @@
 #include "core/frame/FrameView.h"
 #include "core/frame/LocalFrame.h"
 #include "core/loader/DocumentLoader.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/tracing/TraceEvent.h"
-#include "public/platform/WebFrameScheduler.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
index 265fd62..f122464 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -64,37 +64,8 @@
 
 bool PrePaintTreeWalk::walk(const LayoutObject& object,
                             const PrePaintTreeWalkContext& context) {
-  // Early out from the treewalk if possible.
-  if (!object.needsPaintPropertyUpdate() &&
-      !object.descendantNeedsPaintPropertyUpdate() &&
-      !context.treeBuilderContext.forceSubtreeUpdate &&
-      !context.paintInvalidatorContext.forcedSubtreeInvalidationFlags &&
-      !object
-           .shouldCheckForPaintInvalidationRegardlessOfPaintInvalidationState()) {
-    // Even though the subtree was not walked, we know that a walk will not
-    // change anything and can return true as if the subtree was fully updated.
-    return true;
-  }
-
   PrePaintTreeWalkContext localContext(context);
 
-  // TODO(pdr): These should be removable once paint offset changes mark an
-  // object as needing a paint property update. Below, we temporarily re-use
-  // paint invalidation flags to detect paint offset changes.
-  if (localContext.paintInvalidatorContext.forcedSubtreeInvalidationFlags) {
-    // forcedSubtreeInvalidationFlags will be true if locations have changed
-    // which will affect paint properties (e.g., PaintOffset).
-    localContext.treeBuilderContext.forceSubtreeUpdate = true;
-  } else if (object.shouldDoFullPaintInvalidation()) {
-    // shouldDoFullPaintInvalidation will be true when locations or overflow
-    // changes which will affect paint properties (e.g., PaintOffset, scroll).
-    object.getMutableForPainting().setNeedsPaintPropertyUpdate();
-  } else if (object.mayNeedPaintInvalidation()) {
-    // mayNeedpaintInvalidation will be true when locations change which will
-    // affect paint properties (e.g., PaintOffset).
-    object.getMutableForPainting().setNeedsPaintPropertyUpdate();
-  }
-
   // TODO(pdr): Ensure multi column works with incremental property tree
   // construction.
   if (object.isLayoutMultiColumnSpannerPlaceholder()) {
@@ -103,10 +74,9 @@
     // positioned descendants if their containers are between the multi-column
     // container and the spanner. See PaintPropertyTreeBuilder for details.
     localContext.treeBuilderContext.isUnderMultiColumnSpanner = true;
+    const auto& placeholder = toLayoutMultiColumnSpannerPlaceholder(object);
     bool descendantsFullyUpdated =
-        walk(*toLayoutMultiColumnSpannerPlaceholder(object)
-                  .layoutObjectInFlowThread(),
-             localContext);
+        walk(*placeholder.layoutObjectInFlowThread(), localContext);
     if (descendantsFullyUpdated) {
       // If descendants were not fully updated, do not clear flags. During the
       // next PrePaintTreeWalk, these flags will be used again.
@@ -117,6 +87,30 @@
     return descendantsFullyUpdated;
   }
 
+  // Ensure the current context takes into account the box position. This can
+  // change the current context's paint offset so it must proceed the paint
+  // offset property update check.
+  m_propertyTreeBuilder.updateContextForBoxPosition(
+      object, localContext.treeBuilderContext);
+  // Many paint properties depend on paint offset so we force an update of
+  // properties if the paint offset changes.
+  if (object.previousPaintOffset() !=
+      localContext.treeBuilderContext.current.paintOffset) {
+    object.getMutableForPainting().setNeedsPaintPropertyUpdate();
+  }
+
+  // Early out from the treewalk if possible.
+  if (!object.needsPaintPropertyUpdate() &&
+      !object.descendantNeedsPaintPropertyUpdate() &&
+      !localContext.treeBuilderContext.forceSubtreeUpdate &&
+      !localContext.paintInvalidatorContext.forcedSubtreeInvalidationFlags &&
+      !object
+           .shouldCheckForPaintInvalidationRegardlessOfPaintInvalidationState()) {
+    // Even though the subtree was not walked, we know that a walk will not
+    // change anything and can return true as if the subtree was fully updated.
+    return true;
+  }
+
   m_propertyTreeBuilder.updatePropertiesForSelf(
       object, localContext.treeBuilderContext);
   m_paintInvalidator.invalidatePaintIfNeeded(
diff --git a/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp b/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
index e3103a9..ceee315 100644
--- a/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/SVGClipPainter.cpp
@@ -48,7 +48,7 @@
 
   m_clip.clearInvalidationMask();
 
-  if (visualRect.isEmpty() || m_clip.hasCycle())
+  if (m_clip.hasCycle())
     return false;
 
   SVGClipExpansionCycleHelper inClipExpansionChange(m_clip);
diff --git a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
index 856d8ecc..3ac8ce1d 100644
--- a/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableCellPainter.cpp
@@ -239,16 +239,14 @@
     const PaintInfo& paintInfo,
     const LayoutPoint& paintOffset) {
   LayoutTable* table = m_layoutTableCell.table();
-  if (!table->collapseBorders() &&
-      m_layoutTableCell.style()->emptyCells() == EEmptyCells::Hide &&
+  const ComputedStyle& style = m_layoutTableCell.styleRef();
+  if (!table->collapseBorders() && style.emptyCells() == EEmptyCells::Hide &&
       !m_layoutTableCell.firstChild())
     return;
 
   bool needsToPaintBorder =
-      m_layoutTableCell.styleRef().hasBorderDecoration() &&
-      !table->collapseBorders();
-  if (!m_layoutTableCell.styleRef().hasBackground() &&
-      !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder)
+      style.hasBorderDecoration() && !table->collapseBorders();
+  if (!style.hasBackground() && !style.boxShadow() && !needsToPaintBorder)
     return;
 
   if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
@@ -265,17 +263,16 @@
 
   LayoutRect paintRect = paintRectNotIncludingVisualOverflow(paintOffset);
 
-  BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(),
-                             Normal);
+  BoxPainter::paintNormalBoxShadow(paintInfo, paintRect, style);
   paintBackground(paintInfo, paintRect, m_layoutTableCell);
-  BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(),
-                             Inset);
+  // TODO(wangxianzhu): Calculate the inset shadow bounds by insetting paintRect
+  // by half widths of collapsed borders.
+  BoxPainter::paintInsetBoxShadow(paintInfo, paintRect, style);
 
   if (!needsToPaintBorder)
     return;
 
-  BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect,
-                          m_layoutTableCell.styleRef());
+  BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, style);
 }
 
 void TableCellPainter::paintMask(const PaintInfo& paintInfo,
diff --git a/third_party/WebKit/Source/core/paint/TableRowPainter.cpp b/third_party/WebKit/Source/core/paint/TableRowPainter.cpp
index c00953b..cada3bc 100644
--- a/third_party/WebKit/Source/core/paint/TableRowPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableRowPainter.cpp
@@ -77,9 +77,16 @@
           .boundsForDrawingRecorder(paintInfo, adjustedPaintOffset);
   LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableRow,
                                        type, bounds);
-  BoxPainter::paintBoxShadow(
-      paintInfo, LayoutRect(adjustedPaintOffset, m_layoutTableRow.size()),
-      m_layoutTableRow.styleRef(), shadowStyle);
+  LayoutRect paintRect(adjustedPaintOffset, m_layoutTableRow.size());
+  if (shadowStyle == Normal) {
+    BoxPainter::paintNormalBoxShadow(paintInfo, paintRect,
+                                     m_layoutTableRow.styleRef());
+  } else {
+    // TODO(wangxianzhu): Calculate the inset shadow bounds by insetting
+    // paintRect by half widths of collapsed borders.
+    BoxPainter::paintInsetBoxShadow(paintInfo, paintRect,
+                                    m_layoutTableRow.styleRef());
+  }
 }
 
 void TableRowPainter::paintBackgroundBehindCell(
diff --git a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
index 685ba682..877fbd9b 100644
--- a/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/TableSectionPainter.cpp
@@ -400,9 +400,16 @@
                           .boundsForDrawingRecorder(paintInfo, paintOffset);
   LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableSection,
                                        type, bounds);
-  BoxPainter::paintBoxShadow(
-      paintInfo, LayoutRect(paintOffset, m_layoutTableSection.size()),
-      m_layoutTableSection.styleRef(), shadowStyle);
+  LayoutRect paintRect(paintOffset, m_layoutTableSection.size());
+  if (shadowStyle == Normal) {
+    BoxPainter::paintNormalBoxShadow(paintInfo, paintRect,
+                                     m_layoutTableSection.styleRef());
+  } else {
+    // TODO(wangxianzhu): Calculate the inset shadow bounds by insetting
+    // paintRect by half widths of collapsed borders.
+    BoxPainter::paintInsetBoxShadow(paintInfo, paintRect,
+                                    m_layoutTableSection.styleRef());
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/streams/CommonStrings.js b/third_party/WebKit/Source/core/streams/CommonStrings.js
new file mode 100644
index 0000000..01fc130
--- /dev/null
+++ b/third_party/WebKit/Source/core/streams/CommonStrings.js
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// User-visible strings shared between ReadableStream and WritableStream.
+
+(function(global, binding, v8) {
+  'use strict';
+
+  binding.streamErrors = {
+    illegalInvocation: 'Illegal invocation',
+    illegalConstructor: 'Illegal constructor',
+    invalidType: 'Invalid type is specified',
+    invalidSize: 'The return value of a queuing strategy\'s size function must be a finite, non-NaN, non-negative number',
+    sizeNotAFunction: 'A queuing strategy\'s size property must be a function',
+    invalidHWM: 'A queueing strategy\'s highWaterMark property must be a nonnegative, non-NaN number',
+  };
+});
diff --git a/third_party/WebKit/Source/core/streams/ReadableStream.js b/third_party/WebKit/Source/core/streams/ReadableStream.js
index 90056e99..c410099f 100644
--- a/third_party/WebKit/Source/core/streams/ReadableStream.js
+++ b/third_party/WebKit/Source/core/streams/ReadableStream.js
@@ -62,8 +62,7 @@
   const Promise_resolve = v8.simpleBind(Promise.resolve, Promise);
   const Promise_reject = v8.simpleBind(Promise.reject, Promise);
 
-  const errIllegalInvocation = 'Illegal invocation';
-  const errIllegalConstructor = 'Illegal constructor';
+  const streamErrors = binding.streamErrors;
   const errCancelLockedStream =
       'Cannot cancel a readable stream that is locked to a reader';
   const errEnqueueCloseRequestedStream =
@@ -91,12 +90,7 @@
       'Cannot release a readable stream reader when it still has outstanding read() calls that have not yet settled';
   const errReleasedReaderClosedPromise =
       'This readable stream reader has been released and cannot be used to monitor the stream\'s state';
-  const errInvalidSize =
-      'The return value of a queuing strategy\'s size function must be a finite, non-NaN, non-negative number';
-  const errSizeNotAFunction =
-      'A queuing strategy\'s size property must be a function';
-  const errInvalidHWM =
-      'A queueing strategy\'s highWaterMark property must be a nonnegative, non-NaN number';
+
   const errTmplMustBeFunctionOrUndefined = name =>
       `${name} must be a function or undefined`;
 
@@ -129,7 +123,7 @@
       if (typeString === 'bytes') {
         throw new RangeError('bytes type is not yet implemented');
       } else if (type !== undefined) {
-        throw new RangeError('Invalid type is specified');
+        throw new RangeError(streamErrors.invalidType);
       }
 
       this[_controller] =
@@ -138,7 +132,7 @@
 
     get locked() {
       if (IsReadableStream(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       return IsReadableStreamLocked(this);
@@ -146,7 +140,7 @@
 
     cancel(reason) {
       if (IsReadableStream(this) === false) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
 
       if (IsReadableStreamLocked(this) === true) {
@@ -158,7 +152,7 @@
 
     getReader({ mode } = {}) {
       if (IsReadableStream(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       if (mode === 'byob') {
@@ -180,7 +174,7 @@
 
     tee() {
       if (IsReadableStream(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       return ReadableStreamTee(this);
@@ -190,11 +184,11 @@
   class ReadableStreamDefaultController {
     constructor(stream, underlyingSource, size, highWaterMark, isExternallyControlled) {
       if (IsReadableStream(stream) === false) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
 
       if (stream[_controller] !== undefined) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
 
       this[_controlledReadableStream] = stream;
@@ -232,7 +226,7 @@
 
     get desiredSize() {
       if (IsReadableStreamDefaultController(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       return ReadableStreamDefaultControllerGetDesiredSize(this);
@@ -240,7 +234,7 @@
 
     close() {
       if (IsReadableStreamDefaultController(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       const stream = this[_controlledReadableStream];
@@ -262,7 +256,7 @@
 
     enqueue(chunk) {
       if (IsReadableStreamDefaultController(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       const stream = this[_controlledReadableStream];
@@ -284,7 +278,7 @@
 
     error(e) {
       if (IsReadableStreamDefaultController(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       const stream = this[_controlledReadableStream];
@@ -351,7 +345,7 @@
 
     get closed() {
       if (IsReadableStreamDefaultReader(this) === false) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
 
       return this[_closedPromise];
@@ -359,7 +353,7 @@
 
     cancel(reason) {
       if (IsReadableStreamDefaultReader(this) === false) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
 
       const stream = this[_ownerReadableStream];
@@ -372,7 +366,7 @@
 
     read() {
       if (IsReadableStreamDefaultReader(this) === false) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
 
       if (this[_ownerReadableStream] === undefined) {
@@ -384,7 +378,7 @@
 
     releaseLock() {
       if (IsReadableStreamDefaultReader(this) === false) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
 
       const stream = this[_ownerReadableStream];
@@ -808,7 +802,7 @@
   function EnqueueValueWithSize(controller, value, size) {
     size = Number(size);
     if (Number_isNaN(size) || size === +Infinity || size < 0) {
-      throw new RangeError(errInvalidSize);
+      throw new RangeError(streamErrors.invalidSize);
     }
 
     controller[_totalQueuedSize] += size;
@@ -823,12 +817,15 @@
 
   function ValidateAndNormalizeQueuingStrategy(size, highWaterMark) {
     if (size !== undefined && typeof size !== 'function') {
-      throw new TypeError(errSizeNotAFunction);
+      throw new TypeError(streamErrors.sizeNotAFunction);
     }
 
     highWaterMark = Number(highWaterMark);
-    if (Number_isNaN(highWaterMark) || highWaterMark < 0) {
-      throw new RangeError(errInvalidHWM);
+    if (Number_isNaN(highWaterMark)) {
+      throw new RangeError(streamErrors.errInvalidHWM);
+    }
+    if (highWaterMark < 0) {
+      throw new RangeError(streamErrors.invalidHWM);
     }
 
     return {size, highWaterMark};
diff --git a/third_party/WebKit/Source/core/streams/WritableStream.js b/third_party/WebKit/Source/core/streams/WritableStream.js
index 7e132d9..dcffef6 100644
--- a/third_party/WebKit/Source/core/streams/WritableStream.js
+++ b/third_party/WebKit/Source/core/streams/WritableStream.js
@@ -70,11 +70,8 @@
   const Promise_reject = v8.simpleBind(Promise.reject, Promise);
 
   // User-visible strings.
-  // TODO(ricea): Share strings with ReadableStream that are identical in both.
-  const errIllegalInvocation = 'Illegal invocation';
-  const errIllegalConstructor = 'Illegal constructor';
-  const errInvalidType = 'Invalid type is specified';
-  const errAbortLockedStream =  'Cannot abort a writable stream that is locked to a writer';
+  const streamErrors = binding.streamErrors;
+  const errAbortLockedStream = 'Cannot abort a writable stream that is locked to a writer';
   const errStreamAborted = 'The stream has been aborted';
   const errWriterLockReleasedPrefix = 'This writable stream writer has been released and cannot be ';
   const errCloseCloseRequestedStream =
@@ -86,12 +83,6 @@
   const errReleasedWriterClosedPromise =
       'This writable stream writer has been released and cannot be used to monitor the stream\'s state';
   const templateErrorIsNotAFunction = f => `${f} is not a function`;
-  const errSizeNotAFunction =
-      'A queuing strategy\'s size property must be a function';
-  const errInvalidHWM =
-      'A queuing strategy\'s highWaterMark property must be a non-negative, non-NaN number';
-  const errInvalidSize =
-      'The return value of a queuing strategy\'s size function must be a finite, non-NaN, non-negative number';
 
   // These verbs are used after errWriterLockReleasedPrefix
   const verbUsedToGetTheDesiredSize = 'used to get the desiredSize';
@@ -172,7 +163,7 @@
       this[_writeRequests] = new v8.InternalPackedArray();
       const type = underlyingSink.type;
       if (type !== undefined) {
-        throw new RangeError(errInvalidType);
+        throw new RangeError(streamErrors.invalidType);
       }
       this[_writableStreamController] =
           new WritableStreamDefaultController(this, underlyingSink, size,
@@ -181,14 +172,14 @@
 
     get locked() {
       if (!IsWritableStream(this)) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
       return IsWritableStreamLocked(this);
     }
 
     abort(reason) {
       if (!IsWritableStream(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       if (IsWritableStreamLocked(this)) {
         return Promise_reject(new TypeError(errAbortLockedStream));
@@ -198,7 +189,7 @@
 
     getWriter() {
       if (!IsWritableStream(this)) {
-         throw new TypeError(errIllegalInvocation);
+         throw new TypeError(streamErrors.illegalInvocation);
       }
       return AcquireWritableStreamDefaultWriter(this);
     }
@@ -304,10 +295,10 @@
   class WritableStreamDefaultWriter {
     constructor(stream) {
       if (!IsWritableStream(stream)) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
       if (IsWritableStreamLocked(stream)) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
       this[_ownerWritableStream] = stream;
       stream[_writer] = this;
@@ -332,14 +323,14 @@
 
     get closed() {
       if (!IsWritableStreamDefaultWriter(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       return this[_closedPromise];
     }
 
     get desiredSize() {
       if (!IsWritableStreamDefaultWriter(this)) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
       if (this[_ownerWritableStream] === undefined) {
         throw createWriterLockReleasedError(verbUsedToGetTheDesiredSize);
@@ -349,14 +340,14 @@
 
     get ready() {
       if (!IsWritableStreamDefaultWriter(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       return this[_readyPromise];
     }
 
     abort(reason) {
      if (!IsWritableStreamDefaultWriter(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       if (this[_ownerWritableStream] === undefined) {
         return Promise_reject(createWriterLockReleasedError(verbAborted));
@@ -366,7 +357,7 @@
 
     close() {
       if (!IsWritableStreamDefaultWriter(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       const stream = this[_ownerWritableStream];
       if (stream === undefined) {
@@ -380,7 +371,7 @@
 
     releaseLock() {
       if (!IsWritableStreamDefaultWriter(this)) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
       const stream = this[_ownerWritableStream];
       if (stream === undefined) {
@@ -393,7 +384,7 @@
 
     write(chunk) {
       if (!IsWritableStreamDefaultWriter(this)) {
-        return Promise_reject(new TypeError(errIllegalInvocation));
+        return Promise_reject(new TypeError(streamErrors.illegalInvocation));
       }
       const stream = this[_ownerWritableStream];
       if (stream === undefined) {
@@ -497,10 +488,10 @@
   class WritableStreamDefaultController {
     constructor(stream, underlyingSink, size, highWaterMark) {
       if (!IsWritableStream(stream)) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
       if (stream[_writableStreamController] !== undefined) {
-        throw new TypeError(errIllegalConstructor);
+        throw new TypeError(streamErrors.illegalConstructor);
       }
       this[_controlledWritableStream] = stream;
       this[_underlyingSink] = underlyingSink;
@@ -529,7 +520,7 @@
 
     error(e) {
       if (!IsWritableStreamDefaultController(this)) {
-        throw new TypeError(errIllegalInvocation);
+        throw new TypeError(streamErrors.illegalInvocation);
       }
       const state = this[_controlledWritableStream][_state];
       if (state === CLOSED || state === ERRORED) {
@@ -713,7 +704,7 @@
   function EnqueueValueWithSizeForController(controller, value, size) {
     size = Number(size);
     if (!IsFiniteNonNegativeNumber(size)) {
-      throw new RangeError(errInvalidSize);
+      throw new RangeError(streamErrors.invalidSize);
     }
 
     controller[_queueSize] += size;
@@ -787,12 +778,15 @@
   // TODO(ricea): Share this operation with ReadableStream.js.
   function ValidateAndNormalizeQueuingStrategy(size, highWaterMark) {
     if (size !== undefined && typeof size !== 'function') {
-      throw new TypeError(errSizeNotAFunction);
+      throw new TypeError(streamErrors.sizeNotAFunction);
     }
 
     highWaterMark = Number(highWaterMark);
-    if (Number_isNaN(highWaterMark) || highWaterMark < 0) {
-      throw new RangeError(errInvalidHWM);
+    if (Number_isNaN(highWaterMark)) {
+      throw new RangeError(streamErrors.errInvalidHWM);
+    }
+    if (highWaterMark < 0) {
+      throw new RangeError(streamErrors.invalidHWM);
     }
 
     return {size, highWaterMark};
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 83931c9..2d4959b 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -1521,7 +1521,7 @@
 
 FloatRoundedRect ComputedStyle::getRoundedInnerBorderFor(
     const LayoutRect& borderRect,
-    const LayoutRectOutsets insets,
+    const LayoutRectOutsets& insets,
     bool includeLogicalLeftEdge,
     bool includeLogicalRightEdge) const {
   LayoutRect innerRect(borderRect);
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 4914d2fa..251ee5b 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -209,7 +209,9 @@
     inline bool compareEqualIndependent(const InheritedData& other) const {
       // These must match the properties tagged 'independent' in
       // CSSProperties.in.
-      // TODO(sashab): Generate this function.
+      // TODO(napper): Remove this once all independent properties are
+      // generated and replace with a private function used only in
+      // stylePropagationDiff().
       return (m_pointerEvents == other.m_pointerEvents) &&
              (m_whiteSpace == other.m_whiteSpace);
     }
@@ -1462,6 +1464,15 @@
                    offsetPosition);
   }
 
+  // offset-rotate
+  static StyleOffsetRotation initialOffsetRotate() {
+    return initialOffsetRotation();
+  }
+  const StyleOffsetRotation& offsetRotate() const { return offsetRotation(); }
+  void setOffsetRotate(const StyleOffsetRotation& offsetRotate) {
+    setOffsetRotation(offsetRotate);
+  }
+
   // offset-rotation
   static StyleOffsetRotation initialOffsetRotation() {
     return StyleOffsetRotation(0, OffsetRotationAuto);
@@ -3393,7 +3404,7 @@
       bool includeLogicalLeftEdge = true,
       bool includeLogicalRightEdge = true) const;
   FloatRoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect,
-                                            const LayoutRectOutsets insets,
+                                            const LayoutRectOutsets& insets,
                                             bool includeLogicalLeftEdge,
                                             bool includeLogicalRightEdge) const;
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index d5cb0b1f8..e6a2bbd9 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -334,7 +334,7 @@
   PreWrap,
   PreLine,
   Nowrap,
-  KhtmlNowrap
+  WebkitNowrap
 };
 
 // The order of this enum must match the order of the text align values in
@@ -465,8 +465,6 @@
   None
 };
 
-// The order of this enum must match the order of the display values in
-// CSSValueKeywords.in.
 enum class EDisplay : unsigned {
   Inline,
   Block,
diff --git a/third_party/WebKit/Source/core/svg/SVGSVGElement.cpp b/third_party/WebKit/Source/core/svg/SVGSVGElement.cpp
index f162a17..1f85592 100644
--- a/third_party/WebKit/Source/core/svg/SVGSVGElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGSVGElement.cpp
@@ -39,7 +39,6 @@
 #include "core/layout/svg/LayoutSVGModelObject.h"
 #include "core/layout/svg/LayoutSVGRoot.h"
 #include "core/layout/svg/LayoutSVGViewportContainer.h"
-#include "core/page/FrameTree.h"
 #include "core/svg/SVGAngleTearOff.h"
 #include "core/svg/SVGDocumentExtensions.h"
 #include "core/svg/SVGLengthTearOff.h"
@@ -82,7 +81,6 @@
                                     SVGNames::heightAttr,
                                     SVGLength::create(SVGLengthMode::Height),
                                     CSSPropertyHeight)),
-      m_useCurrentView(false),
       m_timeContainer(SMILTimeContainer::create(*this)),
       m_translation(SVGPoint::create()),
       m_currentScale(1) {
@@ -101,12 +99,6 @@
 
 SVGSVGElement::~SVGSVGElement() {}
 
-SVGViewSpec& SVGSVGElement::ensureViewSpec() {
-  if (!m_viewSpec)
-    m_viewSpec = SVGViewSpec::create();
-  return *m_viewSpec;
-}
-
 float SVGSVGElement::currentScale() const {
   if (!isConnected() || !isOutermostSVGSVGElement())
     return 1;
@@ -158,7 +150,7 @@
 
 bool SVGSVGElement::zoomAndPanEnabled() const {
   SVGZoomAndPanType zoomAndPan = this->zoomAndPan();
-  if (m_useCurrentView)
+  if (m_viewSpec)
     zoomAndPan = m_viewSpec->zoomAndPan();
   return zoomAndPan == SVGZoomAndPanMagnify;
 }
@@ -597,10 +589,8 @@
 }
 
 FloatRect SVGSVGElement::currentViewBoxRect() const {
-  if (m_useCurrentView) {
-    DCHECK(m_viewSpec);
+  if (m_viewSpec)
     return m_viewSpec->viewBox()->value();
-  }
 
   FloatRect useViewBox = viewBox()->currentValue()->value();
   if (!useViewBox.isEmpty())
@@ -622,10 +612,9 @@
 }
 
 SVGPreserveAspectRatio* SVGSVGElement::currentPreserveAspectRatio() const {
-  if (m_useCurrentView) {
-    DCHECK(m_viewSpec);
+  if (m_viewSpec)
     return m_viewSpec->preserveAspectRatio();
-  }
+
   if (!viewBox()->currentValue()->isValid() && shouldSynthesizeViewBox()) {
     // If no viewBox is specified and we're embedded through SVGImage, then
     // synthesize a pAR with the value 'none'.
@@ -687,9 +676,8 @@
   AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(
       currentViewBoxRect(), currentPreserveAspectRatio(), viewWidth,
       viewHeight);
-  if (!m_useCurrentView)
+  if (!m_viewSpec)
     return ctm;
-  DCHECK(m_viewSpec);
 
   SVGTransformList* transformList = m_viewSpec->transform();
   if (transformList->isEmpty())
@@ -702,72 +690,54 @@
   return ctm;
 }
 
+void SVGSVGElement::setViewSpec(SVGViewSpec* viewSpec) {
+  // Even if the viewspec object itself doesn't change, it could still
+  // have been mutated, so only treat a "no viewspec" -> "no viewspec"
+  // transition as a no-op.
+  if (!m_viewSpec && !viewSpec)
+    return;
+  m_viewSpec = viewSpec;
+  if (LayoutObject* layoutObject = this->layoutObject())
+    markForLayoutAndParentResourceInvalidation(layoutObject);
+}
+
 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier,
                                      Element* anchorNode) {
-  if (m_viewSpec)
-    m_viewSpec->reset();
-
-  // If we previously had a view, we need to layout again, regardless of the
-  // state after setting.
-  bool needsViewUpdate = m_useCurrentView;
-  m_useCurrentView = false;
-
   if (fragmentIdentifier.startsWith("svgView(")) {
-    // Ensure the SVGViewSpec has been created.
-    SVGViewSpec& view = ensureViewSpec();
-    view.inheritViewAttributesFromElement(this);
-
-    if (view.parseViewSpec(fragmentIdentifier)) {
+    SVGViewSpec* viewSpec = SVGViewSpec::createForElement(*this);
+    if (viewSpec->parseViewSpec(fragmentIdentifier)) {
       UseCounter::count(document(), UseCounter::SVGSVGElementFragmentSVGView);
-      m_useCurrentView = true;
-      needsViewUpdate = true;
-    } else {
-      view.reset();
-    }
-  } else if (isSVGViewElement(anchorNode)) {
-    // Spec: If the SVG fragment identifier addresses a 'view' element
-    // within an SVG document (e.g., MyDrawing.svg#MyView or
-    // MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor
-    // 'svg' element is displayed in the viewport.  Any view specification
-    // attributes included on the given 'view' element override the
-    // corresponding view specification attributes on the closest ancestor
-    // 'svg' element.
-    // TODO(ed): The spec text above is a bit unclear.
-    // Should the transform from outermost svg to nested svg be applied to
-    // "display" the inner svg in the viewport, then let the view element
-    // override the inner svg's view specification attributes. Should it
-    // fill/override the outer viewport?
-    SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
-
-    if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
-      svg->inheritViewAttributes(&viewElement);
-
-      if (LayoutObject* layoutObject = svg->layoutObject())
-        markForLayoutAndParentResourceInvalidation(layoutObject);
-
+      setViewSpec(viewSpec);
       return;
     }
   }
 
-  LayoutObject* layoutObject = this->layoutObject();
-  if (layoutObject && needsViewUpdate)
-    markForLayoutAndParentResourceInvalidation(layoutObject);
+  setViewSpec(nullptr);
 
-  // If m_useCurrentView is true we should have a view-spec.
-  DCHECK(!m_useCurrentView || m_viewSpec);
+  if (!isSVGViewElement(anchorNode))
+    return;
 
-  // FIXME: We need to decide which <svg> to focus on, and zoom to it.
-  // FIXME: We need to actually "highlight" the viewTarget(s).
-}
+  SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
 
-void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) {
-  SVGViewSpec& view = ensureViewSpec();
-  m_useCurrentView = true;
-  UseCounter::count(document(),
+  // Spec: If the SVG fragment identifier addresses a 'view' element
+  // within an SVG document (e.g., MyDrawing.svg#MyView) then the
+  // closest ancestor 'svg' element is displayed in the
+  // viewport. Any view specification attributes included on the
+  // given 'view' element override the corresponding view
+  // specification attributes on the closest ancestor 'svg' element.
+  // TODO(ed): The spec text above is a bit unclear.
+  // Should the transform from outermost svg to nested svg be applied to
+  // "display" the inner svg in the viewport, then let the view element
+  // override the inner svg's view specification attributes. Should it
+  // fill/override the outer viewport?
+  SVGSVGElement* svg = viewElement.ownerSVGElement();
+  if (!svg)
+    return;
+  SVGViewSpec* viewSpec = SVGViewSpec::createForElement(*svg);
+  viewSpec->inheritViewAttributesFromElement(viewElement);
+  UseCounter::count(svg->document(),
                     UseCounter::SVGSVGElementFragmentSVGViewElement);
-  view.inheritViewAttributesFromElement(this);
-  view.inheritViewAttributesFromElement(viewElement);
-  DCHECK(!m_useCurrentView || m_viewSpec);
+  svg->setViewSpec(viewSpec);
 }
 
 void SVGSVGElement::finishParsingChildren() {
diff --git a/third_party/WebKit/Source/core/svg/SVGSVGElement.h b/third_party/WebKit/Source/core/svg/SVGSVGElement.h
index 0f4e7a8d..24c3b36 100644
--- a/third_party/WebKit/Source/core/svg/SVGSVGElement.h
+++ b/third_party/WebKit/Source/core/svg/SVGSVGElement.h
@@ -38,7 +38,6 @@
 class SVGNumberTearOff;
 class SVGPointTearOff;
 class SVGTransformTearOff;
-class SVGViewElement;
 class SVGViewSpec;
 
 class SVGSVGElement final : public SVGGraphicsElement,
@@ -111,6 +110,9 @@
 
   DECLARE_VIRTUAL_TRACE();
 
+  SVGViewSpec* viewSpec() const { return m_viewSpec; }
+  void setViewSpec(SVGViewSpec*);
+
  private:
   explicit SVGSVGElement(Document&);
   ~SVGSVGElement() override;
@@ -136,8 +138,6 @@
 
   bool selfHasRelativeLengths() const override;
 
-  void inheritViewAttributes(SVGViewElement*);
-
   bool shouldSynthesizeViewBox() const;
   void updateUserTransform();
 
@@ -161,7 +161,6 @@
   AffineTransform localCoordinateSpaceTransform(
       SVGElement::CTMScope) const override;
 
-  bool m_useCurrentView;
   Member<SMILTimeContainer> m_timeContainer;
   Member<SVGPoint> m_translation;
   Member<SVGViewSpec> m_viewSpec;
diff --git a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
index 6922049..68d1e65 100644
--- a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
@@ -501,13 +501,6 @@
   if (!element.isSVGGraphicsElement())
     return nullptr;
 
-  if (!element.layoutObject())
-    return nullptr;
-
-  const ComputedStyle* style = element.layoutObject()->style();
-  if (!style || style->visibility() != EVisibility::Visible)
-    return nullptr;
-
   // Spec: "If a <use> element is a child of a clipPath element, it must
   // directly reference <path>, <text> or basic shapes elements. Indirect
   // references are an error and the clipPath element must be ignored."
diff --git a/third_party/WebKit/Source/core/svg/SVGViewSpec.cpp b/third_party/WebKit/Source/core/svg/SVGViewSpec.cpp
index 23fdd2a..a8098ef4 100644
--- a/third_party/WebKit/Source/core/svg/SVGViewSpec.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGViewSpec.cpp
@@ -22,6 +22,7 @@
 #include "core/svg/SVGParserUtilities.h"
 #include "core/svg/SVGPreserveAspectRatio.h"
 #include "core/svg/SVGRect.h"
+#include "core/svg/SVGSVGElement.h"
 #include "core/svg/SVGTransformList.h"
 #include "wtf/text/ParsingUtilities.h"
 
@@ -38,6 +39,16 @@
   visitor->trace(m_transform);
 }
 
+SVGViewSpec* SVGViewSpec::createForElement(SVGSVGElement& rootElement) {
+  SVGViewSpec* viewSpec = rootElement.viewSpec();
+  if (!viewSpec)
+    viewSpec = new SVGViewSpec();
+  else
+    viewSpec->reset();
+  viewSpec->inheritViewAttributesFromElement(rootElement);
+  return viewSpec;
+}
+
 bool SVGViewSpec::parseViewSpec(const String& spec) {
   if (spec.isEmpty())
     return false;
diff --git a/third_party/WebKit/Source/core/svg/SVGViewSpec.h b/third_party/WebKit/Source/core/svg/SVGViewSpec.h
index ba1ca69..3d7ea03 100644
--- a/third_party/WebKit/Source/core/svg/SVGViewSpec.h
+++ b/third_party/WebKit/Source/core/svg/SVGViewSpec.h
@@ -28,17 +28,18 @@
 class FloatRect;
 class SVGPreserveAspectRatio;
 class SVGRect;
+class SVGSVGElement;
 class SVGTransformList;
 
 class SVGViewSpec final : public GarbageCollectedFinalized<SVGViewSpec>,
                           public SVGZoomAndPan {
  public:
-  static SVGViewSpec* create() { return new SVGViewSpec(); }
+  static SVGViewSpec* createForElement(SVGSVGElement&);
 
   bool parseViewSpec(const String&);
   void reset();
   template <typename T>
-  void inheritViewAttributesFromElement(T*);
+  void inheritViewAttributesFromElement(T&);
 
   SVGRect* viewBox() { return m_viewBox; }
   SVGPreserveAspectRatio* preserveAspectRatio() {
@@ -63,17 +64,17 @@
 };
 
 template <typename T>
-void SVGViewSpec::inheritViewAttributesFromElement(T* inheritFromElement) {
-  if (!inheritFromElement->hasEmptyViewBox())
-    setViewBox(inheritFromElement->viewBox()->currentValue()->value());
+void SVGViewSpec::inheritViewAttributesFromElement(T& inheritFromElement) {
+  if (!inheritFromElement.hasEmptyViewBox())
+    setViewBox(inheritFromElement.viewBox()->currentValue()->value());
 
-  if (inheritFromElement->preserveAspectRatio()->isSpecified()) {
+  if (inheritFromElement.preserveAspectRatio()->isSpecified()) {
     setPreserveAspectRatio(
-        *inheritFromElement->preserveAspectRatio()->currentValue());
+        *inheritFromElement.preserveAspectRatio()->currentValue());
   }
 
-  if (inheritFromElement->hasAttribute(SVGNames::zoomAndPanAttr))
-    setZoomAndPan(inheritFromElement->zoomAndPan());
+  if (inheritFromElement.hasAttribute(SVGNames::zoomAndPanAttr))
+    setZoomAndPan(inheritFromElement.zoomAndPan());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index 8bfa3e1..d30de854 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -218,8 +218,6 @@
   return toLayoutBox(layoutObject)->getScrollableArea();
 }
 
-Internals::~Internals() {}
-
 static RuntimeEnabledFeatures::Backup* sFeaturesBackup = nullptr;
 
 void Internals::resetToConsistentState(Page* page) {
@@ -252,26 +250,21 @@
 }
 
 Internals::Internals(ExecutionContext* context)
-    : ContextLifecycleObserver(context),
-      m_runtimeFlags(InternalRuntimeFlags::create()) {
-  contextDocument()->fetcher()->enableIsPreloadedForTest();
-}
-
-Document* Internals::contextDocument() const {
-  return toDocument(getExecutionContext());
+    : m_runtimeFlags(InternalRuntimeFlags::create()),
+      m_document(toDocument(context)) {
+  m_document->fetcher()->enableIsPreloadedForTest();
 }
 
 LocalFrame* Internals::frame() const {
-  if (!contextDocument())
-    return 0;
-  return contextDocument()->frame();
+  if (!m_document)
+    return nullptr;
+  return m_document->frame();
 }
 
 InternalSettings* Internals::settings() const {
-  Document* document = contextDocument();
-  if (!document)
+  if (!m_document)
     return 0;
-  Page* page = document->page();
+  Page* page = m_document->page();
   if (!page)
     return 0;
   return InternalSettings::from(*page);
@@ -306,16 +299,15 @@
 
 unsigned Internals::updateStyleAndReturnAffectedElementCount(
     ExceptionState& exceptionState) const {
-  Document* document = contextDocument();
-  if (!document) {
+  if (!m_document) {
     exceptionState.throwDOMException(InvalidAccessError,
                                      "No context document is available.");
     return 0;
   }
 
-  unsigned beforeCount = document->styleEngine().styleForElementCount();
-  document->updateStyleAndLayoutTree();
-  return document->styleEngine().styleForElementCount() - beforeCount;
+  unsigned beforeCount = m_document->styleEngine().styleForElementCount();
+  m_document->updateStyleAndLayoutTree();
+  return m_document->styleEngine().styleForElementCount() - beforeCount;
 }
 
 unsigned Internals::needsLayoutCount(ExceptionState& exceptionState) const {
@@ -398,7 +390,7 @@
 }
 
 bool Internals::isPreloaded(const String& url) {
-  return isPreloadedBy(url, contextDocument());
+  return isPreloadedBy(url, m_document);
 }
 
 bool Internals::isPreloadedBy(const String& url, Document* document) {
@@ -408,24 +400,22 @@
 }
 
 bool Internals::isLoading(const String& url) {
-  if (!contextDocument())
+  if (!m_document)
     return false;
-  const String cacheIdentifier =
-      contextDocument()->fetcher()->getCacheIdentifier();
+  const String cacheIdentifier = m_document->fetcher()->getCacheIdentifier();
   Resource* resource = memoryCache()->resourceForURL(
-      contextDocument()->completeURL(url), cacheIdentifier);
+      m_document->completeURL(url), cacheIdentifier);
   // We check loader() here instead of isLoading(), because a multipart
   // ImageResource lies isLoading() == false after the first part is loaded.
   return resource && resource->loader();
 }
 
 bool Internals::isLoadingFromMemoryCache(const String& url) {
-  if (!contextDocument())
+  if (!m_document)
     return false;
-  const String cacheIdentifier =
-      contextDocument()->fetcher()->getCacheIdentifier();
+  const String cacheIdentifier = m_document->fetcher()->getCacheIdentifier();
   Resource* resource = memoryCache()->resourceForURL(
-      contextDocument()->completeURL(url), cacheIdentifier);
+      m_document->completeURL(url), cacheIdentifier);
   return resource && resource->getStatus() == Resource::Cached;
 }
 
@@ -861,7 +851,7 @@
 
 bool Internals::hasAutofocusRequest(Document* document) {
   if (!document)
-    document = contextDocument();
+    document = m_document;
   return document->autofocusElement();
 }
 
@@ -898,27 +888,22 @@
 }
 
 DOMWindow* Internals::pagePopupWindow() const {
-  Document* document = contextDocument();
-  if (!document)
+  if (!m_document)
     return nullptr;
-  if (Page* page = document->page())
+  if (Page* page = m_document->page())
     return page->chromeClient().pagePopupWindowForTesting();
   return nullptr;
 }
 
 ClientRect* Internals::absoluteCaretBounds(ExceptionState& exceptionState) {
-  Document* document = contextDocument();
-  if (!document || !document->frame()) {
+  if (!frame()) {
     exceptionState.throwDOMException(
-        InvalidAccessError, document
-                                ? "The document's frame cannot be retrieved."
-                                : "No context document can be obtained.");
+        InvalidAccessError, "The document's frame cannot be retrieved.");
     return ClientRect::create();
   }
 
-  document->updateStyleAndLayoutIgnorePendingStylesheets();
-  return ClientRect::create(
-      document->frame()->selection().absoluteCaretBounds());
+  m_document->updateStyleAndLayoutIgnorePendingStylesheets();
+  return ClientRect::create(frame()->selection().absoluteCaretBounds());
 }
 
 ClientRect* Internals::boundingBox(Element* element) {
@@ -1722,7 +1707,7 @@
     Document* document,
     ExceptionState& exceptionState) {
   ASSERT(document);
-  if (!document->view() || !document->page() || document != contextDocument()) {
+  if (!document->view() || !document->page() || document != m_document) {
     exceptionState.throwDOMException(InvalidAccessError,
                                      "The document provided is invalid.");
     return nullptr;
@@ -1861,16 +1846,15 @@
 
 void Internals::setSpellCheckingEnabled(bool enabled,
                                         ExceptionState& exceptionState) {
-  if (!contextDocument() || !contextDocument()->frame()) {
+  if (!frame()) {
     exceptionState.throwDOMException(
         InvalidAccessError,
         "No frame can be obtained from the provided document.");
     return;
   }
 
-  if (enabled !=
-      contextDocument()->frame()->spellChecker().isSpellCheckingEnabled())
-    contextDocument()->frame()->spellChecker().toggleSpellCheckingEnabled();
+  if (enabled != frame()->spellChecker().isSpellCheckingEnabled())
+    frame()->spellChecker().toggleSpellCheckingEnabled();
 }
 
 void Internals::replaceMisspelled(Document* document,
@@ -2195,15 +2179,12 @@
 }
 
 float Internals::pageScaleFactor(ExceptionState& exceptionState) {
-  Document* document = contextDocument();
-  if (!document || !document->page()) {
+  if (!m_document->page()) {
     exceptionState.throwDOMException(
-        InvalidAccessError, document
-                                ? "The document's page cannot be retrieved."
-                                : "No context document can be obtained.");
+        InvalidAccessError, "The document's page cannot be retrieved.");
     return 0;
   }
-  Page* page = document->page();
+  Page* page = m_document->page();
   return page->frameHost().visualViewport().pageScale();
 }
 
@@ -2211,31 +2192,25 @@
                                    ExceptionState& exceptionState) {
   if (scaleFactor <= 0)
     return;
-  Document* document = contextDocument();
-  if (!document || !document->page()) {
+  if (!m_document->page()) {
     exceptionState.throwDOMException(
-        InvalidAccessError, document
-                                ? "The document's page cannot be retrieved."
-                                : "No context document can be obtained.");
+        InvalidAccessError, "The document's page cannot be retrieved.");
     return;
   }
-  Page* page = document->page();
+  Page* page = m_document->page();
   page->frameHost().visualViewport().setScale(scaleFactor);
 }
 
 void Internals::setPageScaleFactorLimits(float minScaleFactor,
                                          float maxScaleFactor,
                                          ExceptionState& exceptionState) {
-  Document* document = contextDocument();
-  if (!document || !document->page()) {
+  if (!m_document->page()) {
     exceptionState.throwDOMException(
-        InvalidAccessError, document
-                                ? "The document's page cannot be retrieved."
-                                : "No context document can be obtained.");
+        InvalidAccessError, "The document's page cannot be retrieved.");
     return;
   }
 
-  Page* page = document->page();
+  Page* page = m_document->page();
   page->frameHost().setDefaultPageScaleLimits(minScaleFactor, maxScaleFactor);
 }
 
@@ -2375,7 +2350,7 @@
     ExceptionState& exceptionState) {
   Document* document = nullptr;
   if (!node) {
-    document = contextDocument();
+    document = m_document;
   } else if (node->isDocumentNode()) {
     document = toDocument(node);
   } else if (isHTMLIFrameElement(*node)) {
@@ -2595,16 +2570,13 @@
 }
 
 ClientRect* Internals::selectionBounds(ExceptionState& exceptionState) {
-  Document* document = contextDocument();
-  if (!document || !document->frame()) {
+  if (!frame()) {
     exceptionState.throwDOMException(
-        InvalidAccessError, document
-                                ? "The document's frame cannot be retrieved."
-                                : "No context document can be obtained.");
+        InvalidAccessError, "The document's frame cannot be retrieved.");
     return nullptr;
   }
 
-  return ClientRect::create(FloatRect(document->frame()->selection().bounds()));
+  return ClientRect::create(FloatRect(frame()->selection().bounds()));
 }
 
 String Internals::markerTextForListItem(Element* element) {
@@ -2817,7 +2789,7 @@
 
 DEFINE_TRACE(Internals) {
   visitor->trace(m_runtimeFlags);
-  ContextLifecycleObserver::trace(visitor);
+  visitor->trace(m_document);
 }
 
 void Internals::setValueForUser(HTMLInputElement* element,
@@ -3052,14 +3024,7 @@
 double Internals::monotonicTimeToZeroBasedDocumentTime(
     double platformTime,
     ExceptionState& exceptionState) {
-  Document* document = contextDocument();
-  if (!document) {
-    exceptionState.throwDOMException(InvalidAccessError,
-                                     "No context document is available.");
-    return 0;
-  }
-
-  return document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(
+  return m_document->loader()->timing().monotonicTimeToZeroBasedDocumentTime(
       platformTime);
 }
 
diff --git a/third_party/WebKit/Source/core/testing/Internals.h b/third_party/WebKit/Source/core/testing/Internals.h
index 8edd2c9..add6d5e 100644
--- a/third_party/WebKit/Source/core/testing/Internals.h
+++ b/third_party/WebKit/Source/core/testing/Internals.h
@@ -34,7 +34,6 @@
 #include "bindings/core/v8/ScriptValue.h"
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "core/css/CSSComputedStyleDeclaration.h"
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/page/scrolling/ScrollingCoordinator.h"
 #include "platform/heap/Handle.h"
 #include "wtf/Forward.h"
@@ -78,18 +77,15 @@
 class StaticNodeTypeList;
 using StaticNodeList = StaticNodeTypeList<Node>;
 
-class Internals final : public GarbageCollectedFinalized<Internals>,
+class Internals final : public GarbageCollected<Internals>,
                         public ScriptWrappable,
-                        public ContextLifecycleObserver,
                         public ValueIterable<int> {
   DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(Internals);
 
  public:
   static Internals* create(ExecutionContext* context) {
     return new Internals(context);
   }
-  virtual ~Internals();
 
   static void resetToConsistentState(Page*);
 
@@ -543,6 +539,7 @@
                            unsigned index,
                            ExceptionState&);
   Member<InternalRuntimeFlags> m_runtimeFlags;
+  Member<Document> m_document;
 
   IterationSource* startIteration(ScriptState*, ExceptionState&) override;
 };
diff --git a/third_party/WebKit/Source/core/timing/PerformanceTiming.cpp b/third_party/WebKit/Source/core/timing/PerformanceTiming.cpp
index 2fe8acb..aa6fd3c 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceTiming.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceTiming.cpp
@@ -414,6 +414,14 @@
       timing->authorStyleSheetParseDurationBeforeFCP());
 }
 
+unsigned long long PerformanceTiming::updateStyleDurationBeforeFCP() const {
+  const CSSTiming* timing = cssTiming();
+  if (!timing)
+    return 0;
+
+  return toIntegerMilliseconds(timing->updateDurationBeforeFCP());
+}
+
 DocumentLoader* PerformanceTiming::documentLoader() const {
   if (!frame())
     return nullptr;
diff --git a/third_party/WebKit/Source/core/timing/PerformanceTiming.h b/third_party/WebKit/Source/core/timing/PerformanceTiming.h
index 9eab0e3..472c94a 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceTiming.h
+++ b/third_party/WebKit/Source/core/timing/PerformanceTiming.h
@@ -108,10 +108,8 @@
   unsigned long long parseBlockedOnScriptExecutionDuration() const;
   unsigned long long parseBlockedOnScriptExecutionFromDocumentWriteDuration()
       const;
-
-  // Microseconds spend parsing author style sheets before the first contentful
-  // paint.
   unsigned long long authorStyleSheetParseDurationBeforeFCP() const;
+  unsigned long long updateStyleDurationBeforeFCP() const;
 
   ScriptValue toJSONForBinding(ScriptState*) const;
 
diff --git a/third_party/WebKit/Source/core/timing/WorkerPerformance.cpp b/third_party/WebKit/Source/core/timing/WorkerPerformance.cpp
index fdb0c18..d729e7eb 100644
--- a/third_party/WebKit/Source/core/timing/WorkerPerformance.cpp
+++ b/third_party/WebKit/Source/core/timing/WorkerPerformance.cpp
@@ -38,16 +38,11 @@
 namespace blink {
 
 WorkerPerformance::WorkerPerformance(WorkerGlobalScope* context)
-    : PerformanceBase(context->timeOrigin()),
-      ContextLifecycleObserver(context) {}
-
-ExecutionContext* WorkerPerformance::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
-}
+    : PerformanceBase(context->timeOrigin()), m_executionContext(context) {}
 
 DEFINE_TRACE(WorkerPerformance) {
+  visitor->trace(m_executionContext);
   PerformanceBase::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
 }
 
 MemoryInfo* WorkerPerformance::memory() {
diff --git a/third_party/WebKit/Source/core/timing/WorkerPerformance.h b/third_party/WebKit/Source/core/timing/WorkerPerformance.h
index e7b3cc1..75d3976c 100644
--- a/third_party/WebKit/Source/core/timing/WorkerPerformance.h
+++ b/third_party/WebKit/Source/core/timing/WorkerPerformance.h
@@ -32,7 +32,6 @@
 #define WorkerPerformance_h
 
 #include "bindings/core/v8/ScriptWrappable.h"
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/timing/PerformanceBase.h"
 #include "platform/heap/Handle.h"
 #include "wtf/Forward.h"
@@ -43,17 +42,17 @@
 class MemoryInfo;
 class WorkerGlobalScope;
 
-class WorkerPerformance final : public PerformanceBase,
-                                public ContextLifecycleObserver {
+class WorkerPerformance final : public PerformanceBase {
   DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(WorkerPerformance);
 
  public:
   static WorkerPerformance* create(WorkerGlobalScope* context) {
     return new WorkerPerformance(context);
   }
 
-  ExecutionContext* getExecutionContext() const override;
+  ExecutionContext* getExecutionContext() const override {
+    return m_executionContext;
+  }
 
   MemoryInfo* memory();
 
@@ -61,6 +60,8 @@
 
  private:
   explicit WorkerPerformance(WorkerGlobalScope*);
+
+  Member<ExecutionContext> m_executionContext;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h
index 4374ce3..f74077e 100644
--- a/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerGlobalScope.h
@@ -63,7 +63,7 @@
                    const MessagePortArray&,
                    ExceptionState&);
 
-  static bool canTransferArrayBuffer() { return true; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return true; }
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
 
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h
index 5c02891..da27b03d 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerBase.h
@@ -34,7 +34,7 @@
                    PassRefPtr<SerializedScriptValue> message,
                    const MessagePortArray&,
                    ExceptionState&);
-  static bool canTransferArrayBuffer() { return true; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return true; }
   void terminate();
 
   // ActiveDOMObject
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerMessagingProxy.cpp b/third_party/WebKit/Source/core/workers/InProcessWorkerMessagingProxy.cpp
index 51171c0..7f39c1b 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerMessagingProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerMessagingProxy.cpp
@@ -90,8 +90,8 @@
       m_workerObject(workerObject),
       m_workerClients(workerClients),
       m_weakPtrFactory(this) {
-  m_workerObjectProxy =
-      InProcessWorkerObjectProxy::create(m_weakPtrFactory.createWeakPtr());
+  m_workerObjectProxy = InProcessWorkerObjectProxy::create(
+      m_weakPtrFactory.createWeakPtr(), getParentFrameTaskRunners());
 }
 
 InProcessWorkerMessagingProxy::~InProcessWorkerMessagingProxy() {
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.cpp b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.cpp
index bc60b0f..4fd495cdc 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.cpp
@@ -53,9 +53,11 @@
 const double kMaxIntervalInSec = 30;
 
 std::unique_ptr<InProcessWorkerObjectProxy> InProcessWorkerObjectProxy::create(
-    const WeakPtr<InProcessWorkerMessagingProxy>& messagingProxy) {
-  DCHECK(messagingProxy);
-  return wrapUnique(new InProcessWorkerObjectProxy(messagingProxy));
+    const WeakPtr<InProcessWorkerMessagingProxy>& messagingProxyWeakPtr,
+    ParentFrameTaskRunners* parentFrameTaskRunners) {
+  DCHECK(messagingProxyWeakPtr);
+  return wrapUnique(new InProcessWorkerObjectProxy(messagingProxyWeakPtr,
+                                                   parentFrameTaskRunners));
 }
 
 InProcessWorkerObjectProxy::~InProcessWorkerObjectProxy() {}
@@ -140,7 +142,6 @@
 
 void InProcessWorkerObjectProxy::postMessageToPageInspector(
     const String& message) {
-  DCHECK(getExecutionContext()->isDocument());
   // The TaskType of Inspector tasks need to be Unthrottled because they need to
   // run even on a suspended page.
   getParentFrameTaskRunners()
@@ -153,8 +154,7 @@
 
 ParentFrameTaskRunners*
 InProcessWorkerObjectProxy::getParentFrameTaskRunners() {
-  DCHECK(m_messagingProxy);
-  return m_messagingProxy->getParentFrameTaskRunners();
+  return m_parentFrameTaskRunners.get();
 }
 
 void InProcessWorkerObjectProxy::didCreateWorkerGlobalScope(
@@ -194,18 +194,14 @@
 }
 
 InProcessWorkerObjectProxy::InProcessWorkerObjectProxy(
-    const WeakPtr<InProcessWorkerMessagingProxy>& messagingProxy)
-    : m_messagingProxy(messagingProxy.get()),
-      m_messagingProxyWeakPtr(messagingProxy),
+    const WeakPtr<InProcessWorkerMessagingProxy>& messagingProxyWeakPtr,
+    ParentFrameTaskRunners* parentFrameTaskRunners)
+    : m_messagingProxyWeakPtr(messagingProxyWeakPtr),
+      m_parentFrameTaskRunners(parentFrameTaskRunners),
       m_defaultIntervalInSec(kDefaultIntervalInSec),
       m_nextIntervalInSec(kDefaultIntervalInSec),
       m_maxIntervalInSec(kMaxIntervalInSec) {}
 
-ExecutionContext* InProcessWorkerObjectProxy::getExecutionContext() {
-  DCHECK(m_messagingProxy);
-  return m_messagingProxy->getExecutionContext();
-}
-
 void InProcessWorkerObjectProxy::checkPendingActivity(TimerBase*) {
   bool hasPendingActivity = V8GCController::hasPendingActivity(
       m_workerGlobalScope->thread()->isolate(), m_workerGlobalScope);
diff --git a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
index d4fff66..280f32f9 100644
--- a/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
+++ b/third_party/WebKit/Source/core/workers/InProcessWorkerObjectProxy.h
@@ -37,31 +37,32 @@
 #include "platform/Timer.h"
 #include "platform/heap/Handle.h"
 #include "wtf/PassRefPtr.h"
+#include "wtf/WeakPtr.h"
 #include <memory>
 
 namespace blink {
 
-class ExecutionContext;
 class InProcessWorkerMessagingProxy;
+class ParentFrameTaskRunners;
 class WorkerGlobalScope;
 class WorkerOrWorkletGlobalScope;
 
-// A proxy to talk to the worker object. This object is created on the
-// parent context thread (i.e. usually the main thread), passed on to
-// the worker thread, and used to proxy messages to the
+// A proxy to talk to the parent worker object. This object is created and
+// destroyed on the parent context thread (i.e. usually the main thread), and
+// used on the worker thread for proxying messages to the
 // InProcessWorkerMessagingProxy on the parent context thread.
+// InProcessWorkerMessagingProxy always outlives this proxy.
 //
 // This also checks pending activities on WorkerGlobalScope and reports a result
 // to the message proxy when an exponential backoff timer is fired.
-//
-// Used only by in-process workers (DedicatedWorker and CompositorWorker.)
 class CORE_EXPORT InProcessWorkerObjectProxy : public WorkerReportingProxy {
   USING_FAST_MALLOC(InProcessWorkerObjectProxy);
   WTF_MAKE_NONCOPYABLE(InProcessWorkerObjectProxy);
 
  public:
   static std::unique_ptr<InProcessWorkerObjectProxy> create(
-      const WeakPtr<InProcessWorkerMessagingProxy>&);
+      const WeakPtr<InProcessWorkerMessagingProxy>&,
+      ParentFrameTaskRunners*);
   ~InProcessWorkerObjectProxy() override;
 
   void postMessageToWorkerObject(PassRefPtr<SerializedScriptValue>,
@@ -88,22 +89,23 @@
   void didTerminateWorkerThread() override;
 
  protected:
-  InProcessWorkerObjectProxy(const WeakPtr<InProcessWorkerMessagingProxy>&);
-  virtual ExecutionContext* getExecutionContext();
+  InProcessWorkerObjectProxy(const WeakPtr<InProcessWorkerMessagingProxy>&,
+                             ParentFrameTaskRunners*);
 
  private:
   friend class InProcessWorkerMessagingProxyForTest;
 
   void checkPendingActivity(TimerBase*);
 
-  // This object always outlives this proxy.
-  InProcessWorkerMessagingProxy* m_messagingProxy;
-
   // No guarantees about the lifetimes of tasks posted by this proxy wrt the
   // InProcessWorkerMessagingProxy so a weak pointer must be used when posting
   // the tasks.
   WeakPtr<InProcessWorkerMessagingProxy> m_messagingProxyWeakPtr;
 
+  // Used to post a task to InProcessWorkerMessagingProxy on the parent context
+  // thread.
+  CrossThreadPersistent<ParentFrameTaskRunners> m_parentFrameTaskRunners;
+
   // Used for checking pending activities on the worker global scope. This is
   // cancelled when the worker global scope is destroyed.
   std::unique_ptr<Timer<InProcessWorkerObjectProxy>> m_timer;
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
index f8e919b7..bb17ac4 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
@@ -34,8 +34,8 @@
 ThreadedWorkletMessagingProxy::ThreadedWorkletMessagingProxy(
     ExecutionContext* executionContext)
     : ThreadedMessagingProxyBase(executionContext), m_weakPtrFactory(this) {
-  m_workletObjectProxy =
-      ThreadedWorkletObjectProxy::create(m_weakPtrFactory.createWeakPtr());
+  m_workletObjectProxy = ThreadedWorkletObjectProxy::create(
+      m_weakPtrFactory.createWeakPtr(), getParentFrameTaskRunners());
 }
 
 void ThreadedWorkletMessagingProxy::initialize() {
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.cpp
index 3db954f0..2b8b3e3 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.cpp
@@ -17,9 +17,11 @@
 namespace blink {
 
 std::unique_ptr<ThreadedWorkletObjectProxy> ThreadedWorkletObjectProxy::create(
-    const WeakPtr<ThreadedWorkletMessagingProxy>& messagingProxyWeakPtr) {
+    const WeakPtr<ThreadedWorkletMessagingProxy>& messagingProxyWeakPtr,
+    ParentFrameTaskRunners* parentFrameTaskRunners) {
   DCHECK(messagingProxyWeakPtr);
-  return wrapUnique(new ThreadedWorkletObjectProxy(messagingProxyWeakPtr));
+  return wrapUnique(new ThreadedWorkletObjectProxy(messagingProxyWeakPtr,
+                                                   parentFrameTaskRunners));
 }
 
 ThreadedWorkletObjectProxy::~ThreadedWorkletObjectProxy() {
@@ -67,7 +69,7 @@
 
 ParentFrameTaskRunners*
 ThreadedWorkletObjectProxy::getParentFrameTaskRunners() {
-  return m_messagingProxyWeakPtr->getParentFrameTaskRunners();
+  return m_parentFrameTaskRunners.get();
 }
 
 void ThreadedWorkletObjectProxy::didCloseWorkerGlobalScope() {
@@ -90,7 +92,9 @@
 }
 
 ThreadedWorkletObjectProxy::ThreadedWorkletObjectProxy(
-    const WeakPtr<ThreadedWorkletMessagingProxy>& messagingProxyWeakPtr)
-    : m_messagingProxyWeakPtr(messagingProxyWeakPtr) {}
+    const WeakPtr<ThreadedWorkletMessagingProxy>& messagingProxyWeakPtr,
+    ParentFrameTaskRunners* parentFrameTaskRunners)
+    : m_messagingProxyWeakPtr(messagingProxyWeakPtr),
+      m_parentFrameTaskRunners(parentFrameTaskRunners) {}
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.h
index 71e434c..4c04c47 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.h
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletObjectProxy.h
@@ -14,8 +14,8 @@
 
 class ThreadedWorkletMessagingProxy;
 
-// A proxy to talk to the parent worklet object. This object is created on the
-// main thread, passed on to the worklet thread, and used just to proxy
+// A proxy to talk to the parent worklet object. This object is created and
+// destroyed on the main thread, and used on the worklet thread for proxying
 // messages to the ThreadedWorkletMessagingProxy on the main thread.
 // ThreadedWorkletMessagingProxy always outlives this proxy.
 class CORE_EXPORT ThreadedWorkletObjectProxy : public WorkerReportingProxy {
@@ -24,7 +24,8 @@
 
  public:
   static std::unique_ptr<ThreadedWorkletObjectProxy> create(
-      const WeakPtr<ThreadedWorkletMessagingProxy>&);
+      const WeakPtr<ThreadedWorkletMessagingProxy>&,
+      ParentFrameTaskRunners*);
   ~ThreadedWorkletObjectProxy() override;
 
   void reportPendingActivity(bool hasPendingActivity);
@@ -47,13 +48,18 @@
   void didTerminateWorkerThread() override;
 
  protected:
-  ThreadedWorkletObjectProxy(const WeakPtr<ThreadedWorkletMessagingProxy>&);
+  ThreadedWorkletObjectProxy(const WeakPtr<ThreadedWorkletMessagingProxy>&,
+                             ParentFrameTaskRunners*);
 
  private:
   // No guarantees about the lifetimes of tasks posted by this proxy wrt the
   // ThreadedWorkletMessagingProxy so a weak pointer must be used when posting
   // the tasks.
   WeakPtr<ThreadedWorkletMessagingProxy> m_messagingProxyWeakPtr;
+
+  // Used to post a task to ThreadedWorkletMessagingProxy on the parent context
+  // thread.
+  CrossThreadPersistent<ParentFrameTaskRunners> m_parentFrameTaskRunners;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/.eslintrc.js b/third_party/WebKit/Source/devtools/.eslintrc.js
index f9a41f0..7eafe914 100644
--- a/third_party/WebKit/Source/devtools/.eslintrc.js
+++ b/third_party/WebKit/Source/devtools/.eslintrc.js
@@ -40,6 +40,8 @@
             "setWithoutGet": false
         }],
         "curly": [2, "multi-or-nest", "consistent"],
+        "new-parens": 2,
+        "func-call-spacing": 2,
 
         // anti-patterns
         "no-with": 2,
@@ -51,6 +53,15 @@
         "no-octal-escape": 2,
         "no-self-compare": 2,
         "no-shadow-restricted-names": 2,
+        "no-cond-assign": 2,
+        "no-debugger": 2,
+        "no-dupe-keys": 2,
+        "no-duplicate-case": 2,
+        "no-empty-character-class": 2,
+        "no-unreachable": 2,
+        "no-unsafe-negation": 2,
+        "radix": 2,
+        "valid-typeof": 2,
 
         // es2015 features
         "require-yield": 2,
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 392c5eb..afc5844 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -1175,6 +1175,7 @@
     "scripts/closure/",
     "scripts/compile_frontend.py",
     "scripts/jsdoc_validator/",
+    "scripts/utils.py",
     "//testing/scripts/run_devtools_closure_compile.py",
     "//testing/scripts/common.py",
     "//testing/xvfb.py",
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js b/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js
index 6eabe87..6b9e290 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js
@@ -58,6 +58,11 @@
    * @param {!SDK.Target} target
    */
   targetRemoved(target) {
+    var debuggerModel = SDK.DebuggerModel.fromTarget(target);
+    if (debuggerModel) {
+      this._debuggerModelData.delete(debuggerModel);
+      this._isBlackboxedURLCache.clear();
+    }
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/CompilerScriptMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/CompilerScriptMapping.js
index 085c4a46..c1aeb93b 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/CompilerScriptMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/CompilerScriptMapping.js
@@ -197,7 +197,7 @@
       return;
     // Create stub UISourceCode for the time source mapping is being loaded.
     var stubUISourceCode = this._stubProject.addContentProvider(
-        script.sourceURL,
+        script.sourceURL + ':sourcemap',
         Common.StaticContentProvider.fromString(
             script.sourceURL, Common.resourceTypes.Script,
             '\n\n\n\n\n// Please wait a bit.\n// Compiled script is not shown while source map is being loaded!'));
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
index 29e7dcd..bb311be 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/ResourceScriptMapping.js
@@ -296,6 +296,8 @@
     if (typeof this._scriptSource === 'undefined')
       return false;
     var workingCopy = this._uiSourceCode.workingCopy();
+    if (!workingCopy)
+      return false;
 
     // Match ignoring sourceURL.
     if (!workingCopy.startsWith(this._scriptSource.trimRight()))
diff --git a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
index 6a71a77..b17ac6f 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
@@ -65,6 +65,10 @@
   if ((expressionString && !isNaN(expressionString)) || (!expressionString && query && !isNaN(query)))
     return Promise.resolve([]);
 
+  // User is creating an array, do not suggest anything.
+  if (bracketNotation && !expressionString)
+    return Promise.resolve([]);
+
   if (!query && !expressionString && !force)
     return Promise.resolve([]);
 
@@ -252,7 +256,7 @@
     var property = properties[i];
 
     // Assume that all non-ASCII characters are letters and thus can be used as part of identifier.
-    if (dotNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.test(property))
+    if (!bracketNotation && !/^[a-zA-Z_$\u008F-\uFFFF][a-zA-Z0-9_$\u008F-\uFFFF]*$/.test(property))
       continue;
 
     if (bracketNotation) {
diff --git a/third_party/WebKit/Source/devtools/front_end/components/RemoteObjectPreviewFormatter.js b/third_party/WebKit/Source/devtools/front_end/components/RemoteObjectPreviewFormatter.js
index 1fc9f74..54b36b05 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/RemoteObjectPreviewFormatter.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/RemoteObjectPreviewFormatter.js
@@ -15,12 +15,12 @@
       parentElement.appendChild(this.renderPropertyPreview(preview.type, preview.subtype, description));
       return;
     }
-    var isArray = preview.subtype === 'array' || preview.subtype === 'typedarray';
-    if (description && !isArray) {
+    if (description && preview.subtype !== 'array') {
       var text = preview.subtype ? description : this._abbreviateFullQualifiedClassName(description);
       parentElement.createTextChildren(text, ' ');
     }
 
+    var isArray = preview.subtype === 'array' || preview.subtype === 'typedarray';
     parentElement.createTextChild(isArray ? '[' : '{');
     if (preview.entries)
       this._appendEntriesPreview(parentElement, preview);
@@ -49,7 +49,7 @@
    * @param {!Protocol.Runtime.ObjectPreview} preview
    */
   _appendObjectPropertiesPreview(parentElement, preview) {
-    var properties = preview.properties.slice().stableSort(compareFunctionsLast);
+    var properties = preview.properties.filter(p => p.type !== 'accessor').stableSort(compareFunctionsLast);
 
     /**
      * @param {!Protocol.Runtime.PropertyPreview} a
@@ -167,6 +167,12 @@
     var span = createElementWithClass('span', 'object-value-' + (subtype || type));
     description = description || '';
 
+    if (type === 'accessor') {
+      span.textContent = '(...)';
+      span.title = Common.UIString('The property is computed with a getter');
+      return span;
+    }
+
     if (type === 'function') {
       span.textContent = 'function';
       return span;
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
index d8de5df..73aedc4 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
@@ -639,6 +639,8 @@
       }
 
       var titleElement = createElementWithClass('span', 'console-object-preview');
+      if (array.subtype === 'typedarray')
+        titleElement.createTextChild(array.description + ' ');
       var elements = {};
       for (var i = 0; i < properties.length; ++i) {
         var property = properties[i];
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index 77571ef..35d9ded 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -648,13 +648,10 @@
       return createTextNode('');
 
     var ruleLocation;
-    if (rule instanceof SDK.CSSStyleRule) {
-      var matchingSelectors = matchedStyles.matchingSelectors(rule);
-      var firstMatchingIndex = matchingSelectors.length ? matchingSelectors[0] : 0;
-      ruleLocation = rule.selectors[firstMatchingIndex].range;
-    } else if (rule instanceof SDK.CSSKeyframeRule) {
+    if (rule instanceof SDK.CSSStyleRule)
+      ruleLocation = rule.style.range;
+    else if (rule instanceof SDK.CSSKeyframeRule)
       ruleLocation = rule.key().range;
-    }
 
     var header = rule.styleSheetId ? matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId) : null;
     if (ruleLocation && rule.styleSheetId && header && !header.isAnonymousInlineStyleSheet()) {
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
index ae39313..c306fb6 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/AcornTokenizer.js
@@ -11,7 +11,7 @@
   constructor(content) {
     this._content = content;
     this._comments = [];
-    this._tokenizer = acorn.tokenizer(this._content, {ecmaVersion: 6, onComment: this._comments});
+    this._tokenizer = acorn.tokenizer(this._content, {ecmaVersion: 7, onComment: this._comments});
     this._lineEndings = this._content.computeLineEndings();
     this._lineNumber = 0;
     this._tokenLineStart = 0;
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
index 8c26e62..9fa7adc9 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/FormatterWorker.js
@@ -97,7 +97,7 @@
  * @param {string} content
  */
 FormatterWorker.evaluatableJavaScriptSubstring = function(content) {
-  var tokenizer = acorn.tokenizer(content, {ecmaVersion: 6});
+  var tokenizer = acorn.tokenizer(content, {ecmaVersion: 7});
   var result = '';
   try {
     var token = tokenizer.getToken();
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
index 3c9e296f..85ed624f 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptFormatter.js
@@ -51,7 +51,7 @@
     this._content = text.substring(this._fromOffset, this._toOffset);
     this._lastLineNumber = 0;
     this._tokenizer = new FormatterWorker.AcornTokenizer(this._content);
-    var ast = acorn.parse(this._content, {ranges: false, ecmaVersion: 6});
+    var ast = acorn.parse(this._content, {ranges: false, ecmaVersion: 7});
     var walker = new FormatterWorker.ESTreeWalker(this._beforeVisit.bind(this), this._afterVisit.bind(this));
     walker.walk(ast);
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/network/FilterSuggestionBuilder.js b/third_party/WebKit/Source/devtools/front_end/network/FilterSuggestionBuilder.js
index eed67b4..a3ea2f8 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/FilterSuggestionBuilder.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/FilterSuggestionBuilder.js
@@ -27,10 +27,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-/**
- * @implements {UI.TextFilterUI.SuggestionBuilder}
- * @unrestricted
- */
+
 Network.FilterSuggestionBuilder = class {
   /**
    * @param {!Array.<string>} keys
@@ -42,23 +39,14 @@
   }
 
   /**
-   * @override
-   * @param {!HTMLInputElement} input
-   * @return {?Array.<string>}
+   * @param {string} expression
+   * @param {string} prefix
+   * @param {boolean=} force
+   * @return {!Promise<!UI.SuggestBox.Suggestions>}
    */
-  buildSuggestions(input) {
-    var text = input.value;
-    var end = input.selectionEnd;
-    if (end !== text.length)
-      return null;
-
-    var start = input.selectionStart;
-    text = text.substring(0, start);
-    var prefixIndex = text.lastIndexOf(' ') + 1;
-
-    var prefix = text.substring(prefixIndex);
-    if (!prefix)
-      return [];
+  completions(expression, prefix, force) {
+    if (!prefix && !force)
+      return Promise.resolve([]);
 
     var negative = prefix.startsWith('-');
     if (negative)
@@ -71,7 +59,7 @@
       var matcher = new RegExp('^' + prefix.escapeForRegExp(), 'i');
       for (var j = 0; j < this._keys.length; ++j) {
         if (this._keys[j].match(matcher))
-          suggestions.push(modifier + this._keys[j] + ':');
+          suggestions.push({title: modifier + this._keys[j] + ':'});
       }
     } else {
       var key = prefix.substring(0, valueDelimiterIndex).toLowerCase();
@@ -80,46 +68,10 @@
       var items = this._values(key);
       for (var i = 0; i < items.length; ++i) {
         if (items[i].match(matcher) && (items[i] !== value))
-          suggestions.push(modifier + key + ':' + items[i]);
+          suggestions.push({title: modifier + key + ':' + items[i]});
       }
     }
-    return suggestions;
-  }
-
-  /**
-   * @override
-   * @param {!HTMLInputElement} input
-   * @param {string} suggestion
-   * @param {boolean} isIntermediate
-   */
-  applySuggestion(input, suggestion, isIntermediate) {
-    var text = input.value;
-
-    var start = input.selectionStart;
-    text = text.substring(0, start);
-    var prefixIndex = text.lastIndexOf(' ') + 1;
-
-    if (isIntermediate) {
-      text = text + suggestion.substring(text.length - prefixIndex);
-      input.value = text;
-    } else {
-      text = text.substring(0, prefixIndex) + suggestion;
-      input.value = text;
-      start = text.length;
-    }
-    input.setSelectionRange(start, text.length);
-  }
-
-  /**
-   * @override
-   * @param {!HTMLInputElement} input
-   */
-  unapplySuggestion(input) {
-    var start = input.selectionStart;
-    var end = input.selectionEnd;
-    var text = input.value;
-    if (start !== end && end === text.length)
-      input.value = text.substring(0, start);
+    return Promise.resolve(suggestions);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
index 94e5a12..3865db60 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -443,7 +443,7 @@
     this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '100');
     this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '10k');
     this._suggestionBuilder.addItem(Network.NetworkLogView.FilterType.LargerThan, '1M');
-    this._textFilterUI.setSuggestionBuilder(this._suggestionBuilder);
+    this._textFilterUI.setSuggestionProvider(this._suggestionBuilder.completions.bind(this._suggestionBuilder));
   }
 
   /**
@@ -504,7 +504,6 @@
   }
 
   _setupDataGrid() {
-    /** @type {!UI.SortableDataGrid} */
     this._dataGrid = this._columns.dataGrid();
     this._dataGrid.setRowContextMenuCallback(
         (contextMenu, node) => this.handleContextMenuForRequest(contextMenu, node.request()));
@@ -521,8 +520,7 @@
    * @param {!Event} event
    */
   _dataGridMouseMove(event) {
-    var node = /** @type {?Network.NetworkDataGridNode} */ (
-        this._dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.target)));
+    var node = this._dataGrid.dataGridNodeFromNode(event.target);
     var highlightInitiatorChain = event.shiftKey;
     this._setHoveredNode(node, highlightInitiatorChain);
     this._highlightInitiatorChain((highlightInitiatorChain && node) ? node.request() : null);
@@ -785,7 +783,7 @@
    * @return {!Array<!Network.NetworkDataGridNode>}
    */
   flatNodesList() {
-    return this._dataGrid.rootNode().flattenChildren();
+    return this._dataGrid.flatNodesList();
   }
 
   _refresh() {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index 6fb1f137..9ccfdcb 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -287,7 +287,6 @@
       this._waterfallRequestsAreStale = true;
       var sortFunction = Network.NetworkDataGridNode.RequestPropertyComparator.bind(null, this._activeWaterfallSortId);
       this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending());
-      this._networkLogView.dataGridSorted();
       return;
     }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css b/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css
index 8da02f5..1d041e4 100644
--- a/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css
+++ b/third_party/WebKit/Source/devtools/front_end/settings/settingsScreen.css
@@ -31,31 +31,6 @@
     padding: 12px;
 }
 
-.help-window-main .help-container-wrapper::-webkit-scrollbar {
-    width: 11px;
-}
-
-.help-window-main .help-container-wrapper::-webkit-scrollbar-corner,
-.help-window-main .help-container-wrapper::-webkit-resizer {
-    display: none;
-}
-
-.help-window-main .help-container-wrapper::-webkit-scrollbar-thumb:vertical {
-    background: linear-gradient(to right, rgb(128, 128, 128), rgb(96, 96, 96) 40%, rgb(128, 128, 128));
-    border-radius: 5px;
-    min-height: 20px;
-}
-
-.help-window-main .help-container-wrapper::-webkit-scrollbar-thumb:vertical:hover,
-.help-window-main .help-container-wrapper::-webkit-scrollbar-thumb:vertical:active {
-    background: linear-gradient(to right, rgb(176, 176, 176), rgb(144, 144, 144) 40%, rgb(176, 176, 176));
-}
-
-.help-window-main .help-container-wrapper::-webkit-scrollbar-track:vertical {
-    background: linear-gradient(to right, rgb(10, 10, 10), rgb(32, 32, 32) 25%, rgb(32, 32, 32));
-    border-radius: 5px;
-}
-
 .help-container {
     width: 100%;
     -webkit-user-select: auto;
diff --git a/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js b/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
index 8f88cd1..749a87f 100644
--- a/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
+++ b/third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js
@@ -275,7 +275,8 @@
   _contextMenu(event) {
     var contextMenu = new UI.ContextMenu(event);
     event.consume(true);  // Consume event now to prevent document from handling the async menu
-    var target = event.target.enclosingNodeOrSelfWithClass('CodeMirror-gutter-elt');
+    var wrapper = event.target.enclosingNodeOrSelfWithClass('CodeMirror-gutter-wrapper');
+    var target = wrapper ? wrapper.querySelector('.CodeMirror-linenumber') : null;
     var promise;
     if (target) {
       promise = this._delegate.populateLineGutterContextMenu(contextMenu, parseInt(target.textContent, 10) - 1);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
index 9d15ae67..3711cf5 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
@@ -52,7 +52,7 @@
    */
   _filterUISourceCode(uiSourceCode) {
     var binding = Persistence.persistence.binding(uiSourceCode);
-    return !binding || binding.network === uiSourceCode;
+    return !binding || binding.fileSystem === uiSourceCode;
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
index 0c82744..c75d9ad 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js
@@ -37,6 +37,7 @@
    */
   constructor(uiSourceCode) {
     super(uiSourceCode);
+    this._debuggerSourceCode = uiSourceCode;
 
     this._scriptsPanel = Sources.SourcesPanel.instance();
     this._breakpointManager = Bindings.breakpointManager;
@@ -59,8 +60,6 @@
         Bindings.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
 
     this.uiSourceCode().addEventListener(
-        Workspace.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
-    this.uiSourceCode().addEventListener(
         Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
     this.uiSourceCode().addEventListener(
         Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
@@ -74,22 +73,13 @@
 
     /** @type {!Map.<!SDK.Target, !Bindings.ResourceScriptFile>}*/
     this._scriptFileForTarget = new Map();
-    var targets = SDK.targetManager.targets();
-    for (var i = 0; i < targets.length; ++i) {
-      var scriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(uiSourceCode, targets[i]);
-      if (scriptFile)
-        this._updateScriptFile(targets[i]);
-    }
-
-    if (this._scriptFileForTarget.size || uiSourceCode.extension() === 'js' ||
-        uiSourceCode.project().type() === Workspace.projectTypes.Snippets)
-      this._compiler = new Sources.JavaScriptCompiler(this);
 
     Common.moduleSetting('skipStackFramesPattern').addChangeListener(this._showBlackboxInfobarIfNeeded, this);
     Common.moduleSetting('skipContentScripts').addChangeListener(this._showBlackboxInfobarIfNeeded, this);
-    this._showBlackboxInfobarIfNeeded();
+
     /** @type {!Map.<number, !Element>} */
     this._valueWidgets = new Map();
+    this.onBindingChanged();
   }
 
   /**
@@ -98,7 +88,7 @@
    */
   syncToolbarItems() {
     var result = super.syncToolbarItems();
-    var originURL = Bindings.CompilerScriptMapping.uiSourceCodeOrigin(this.uiSourceCode());
+    var originURL = Bindings.CompilerScriptMapping.uiSourceCodeOrigin(this._debuggerSourceCode);
     if (originURL) {
       var parsedURL = originURL.asParsedURL();
       if (parsedURL)
@@ -116,7 +106,7 @@
   }
 
   _showBlackboxInfobarIfNeeded() {
-    var uiSourceCode = this.uiSourceCode();
+    var uiSourceCode = this._debuggerSourceCode;
     if (!uiSourceCode.contentType().hasScripts())
       return;
     var projectType = uiSourceCode.project().type();
@@ -185,8 +175,10 @@
    * @override
    */
   onUISourceCodeContentChanged() {
-    for (var decoration of this._breakpointDecorations)
-      decoration.breakpoint.remove();
+    for (var decoration of this._breakpointDecorations) {
+      if (decoration.breakpoint)
+        decoration.breakpoint.remove();
+    }
     super.onUISourceCodeContentChanged();
   }
 
@@ -209,7 +201,7 @@
      * @this {Sources.JavaScriptSourceFrame}
      */
     function populate(resolve, reject) {
-      var uiLocation = new Workspace.UILocation(this.uiSourceCode(), lineNumber, 0);
+      var uiLocation = new Workspace.UILocation(this._debuggerSourceCode, lineNumber, 0);
       this._scriptsPanel.appendUILocationItems(contextMenu, uiLocation);
       var breakpoints = this._lineBreakpointDecorations(lineNumber)
                             .map(decoration => decoration.breakpoint)
@@ -276,9 +268,9 @@
      * @this {Sources.JavaScriptSourceFrame}
      */
     function populateSourceMapMembers() {
-      if (this.uiSourceCode().project().type() === Workspace.projectTypes.Network &&
+      if (this._debuggerSourceCode.project().type() === Workspace.projectTypes.Network &&
           Common.moduleSetting('jsSourceMapsEnabled').get() &&
-          !Bindings.blackboxManager.isBlackboxedUISourceCode(this.uiSourceCode())) {
+          !Bindings.blackboxManager.isBlackboxedUISourceCode(this._debuggerSourceCode)) {
         if (this._scriptFileForTarget.size) {
           var scriptFile = this._scriptFileForTarget.valuesArray()[0];
           var addSourceMapURLLabel = Common.UIString.capitalize('Add ^source ^map\u2026');
@@ -442,7 +434,7 @@
     var evaluationText = line.substring(startHighlight, endHighlight + 1);
     Sources.SourceMapNamesResolver
         .resolveExpression(
-            selectedCallFrame, evaluationText, this.uiSourceCode(), lineNumber, startHighlight, endHighlight)
+            selectedCallFrame, evaluationText, this._debuggerSourceCode, lineNumber, startHighlight, endHighlight)
         .then(onResolve.bind(this));
 
     /**
@@ -595,8 +587,8 @@
     var functionUILocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(
         /** @type {!SDK.DebuggerModel.Location} */ (callFrame.functionLocation()));
     var executionUILocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(callFrame.location());
-    if (functionUILocation.uiSourceCode !== this.uiSourceCode() ||
-        executionUILocation.uiSourceCode !== this.uiSourceCode()) {
+    if (functionUILocation.uiSourceCode !== this._debuggerSourceCode ||
+        executionUILocation.uiSourceCode !== this._debuggerSourceCode) {
       this._clearValueWidgets();
       return;
     }
@@ -876,7 +868,7 @@
    */
   _shouldIgnoreExternalBreakpointEvents(event) {
     var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation);
-    if (uiLocation.uiSourceCode !== this.uiSourceCode() || !this.loaded)
+    if (uiLocation.uiSourceCode !== this._debuggerSourceCode || !this.loaded)
       return true;
     if (this._supportsEnabledBreakpointsWhileEditing())
       return false;
@@ -897,9 +889,16 @@
     if (this._shouldIgnoreExternalBreakpointEvents(event))
       return;
     var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation);
-    var lineDecorations = this._lineBreakpointDecorations(uiLocation.lineNumber);
     var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
+    this._addBreakpoint(uiLocation, breakpoint);
+  }
 
+  /**
+   * @param {!Workspace.UILocation} uiLocation
+   * @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
+   */
+  _addBreakpoint(uiLocation, breakpoint) {
+    var lineDecorations = this._lineBreakpointDecorations(uiLocation.lineNumber);
     var decoration = this._breakpointDecoration(uiLocation.lineNumber, uiLocation.columnNumber);
     if (decoration) {
       decoration.breakpoint = breakpoint;
@@ -920,7 +919,7 @@
       this._willAddInlineDecorationsForTest();
       this._breakpointManager
           .possibleBreakpoints(
-              this.uiSourceCode(), new Common.TextRange(uiLocation.lineNumber, 0, uiLocation.lineNumber + 1, 0))
+              this._debuggerSourceCode, new Common.TextRange(uiLocation.lineNumber, 0, uiLocation.lineNumber + 1, 0))
           .then(addInlineDecorations.bind(this, uiLocation.lineNumber));
     }
 
@@ -998,6 +997,47 @@
   }
 
   /**
+   * @override
+   */
+  onBindingChanged() {
+    this._updateDebuggerSourceCode();
+    this._updateScriptFiles();
+    this._refreshBreakpoints();
+
+    var canLiveCompileJavascript = this._scriptFileForTarget.size || this._debuggerSourceCode.extension() === 'js' ||
+        this._debuggerSourceCode.project().type() === Workspace.projectTypes.Snippets;
+    if (!!canLiveCompileJavascript !== !!this._compiler)
+      this._compiler = canLiveCompileJavascript ? new Sources.JavaScriptCompiler(this) : null;
+
+    this._showBlackboxInfobarIfNeeded();
+    this._updateLinesWithoutMappingHighlight();
+  }
+
+  _refreshBreakpoints() {
+    if (!this.loaded)
+      return;
+    for (var lineDecoration of this._breakpointDecorations.valuesArray()) {
+      this._breakpointDecorations.delete(lineDecoration);
+      this._updateBreakpointDecoration(lineDecoration);
+    }
+    var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._debuggerSourceCode);
+    for (var breakpointLocation of breakpointLocations)
+      this._addBreakpoint(breakpointLocation.uiLocation, breakpointLocation.breakpoint);
+  }
+
+  _updateDebuggerSourceCode() {
+    if (this._debuggerSourceCode) {
+      this._debuggerSourceCode.removeEventListener(
+          Workspace.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
+    }
+
+    var binding = Persistence.persistence.binding(this.uiSourceCode());
+    this._debuggerSourceCode = binding ? binding.network : this.uiSourceCode();
+    this._debuggerSourceCode.addEventListener(
+        Workspace.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
+  }
+
+  /**
    * @param {!Common.Event} event
    */
   _onSourceMappingChanged(event) {
@@ -1009,7 +1049,7 @@
   _updateLinesWithoutMappingHighlight() {
     var linesCount = this.textEditor.linesCount;
     for (var i = 0; i < linesCount; ++i) {
-      var lineHasMapping = Bindings.debuggerWorkspaceBinding.uiLineHasMapping(this.uiSourceCode(), i);
+      var lineHasMapping = Bindings.debuggerWorkspaceBinding.uiLineHasMapping(this._debuggerSourceCode, i);
       if (!lineHasMapping)
         this._hasLineWithoutMapping = true;
       if (this._hasLineWithoutMapping)
@@ -1017,12 +1057,20 @@
     }
   }
 
+  _updateScriptFiles() {
+    for (var target of SDK.targetManager.targets()) {
+      var scriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(this._debuggerSourceCode, target);
+      if (scriptFile)
+        this._updateScriptFile(target);
+    }
+  }
+
   /**
    * @param {!SDK.Target} target
    */
   _updateScriptFile(target) {
     var oldScriptFile = this._scriptFileForTarget.get(target);
-    var newScriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(this.uiSourceCode(), target);
+    var newScriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(this._debuggerSourceCode, target);
     this._scriptFileForTarget.remove(target);
     if (oldScriptFile) {
       oldScriptFile.removeEventListener(Bindings.ResourceScriptFile.Events.DidMergeToVM, this._didMergeToVM, this);
@@ -1063,9 +1111,9 @@
     if (this._executionLocation)
       this.setExecutionLocation(this._executionLocation);
 
-    var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this.uiSourceCode());
-    for (var i = 0; i < breakpointLocations.length; ++i)
-      this._breakpointAdded(/** @type {!Common.Event} */ ({data: breakpointLocations[i]}));
+    var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._debuggerSourceCode);
+    for (var breakpointLocation of breakpointLocations)
+      this._addBreakpoint(breakpointLocation.uiLocation, breakpointLocation.breakpoint);
 
     var scriptFiles = this._scriptFileForTarget.valuesArray();
     for (var i = 0; i < scriptFiles.length; ++i)
@@ -1173,7 +1221,7 @@
       if (this._textEditor.line(lineNumber).length >= maxLengthToCheck)
         return Promise.resolve(/** @type {?Array<!Workspace.UILocation>} */ ([]));
       return this._breakpointManager
-          .possibleBreakpoints(this.uiSourceCode(), new Common.TextRange(lineNumber, 0, lineNumber + 1, 0))
+          .possibleBreakpoints(this._debuggerSourceCode, new Common.TextRange(lineNumber, 0, lineNumber + 1, 0))
           .then(locations => locations.length ? locations : null);
     }
 
@@ -1223,10 +1271,10 @@
    * @param {boolean} enabled
    */
   _setBreakpoint(lineNumber, columnNumber, condition, enabled) {
-    if (!Bindings.debuggerWorkspaceBinding.uiLineHasMapping(this.uiSourceCode(), lineNumber))
+    if (!Bindings.debuggerWorkspaceBinding.uiLineHasMapping(this._debuggerSourceCode, lineNumber))
       return;
 
-    this._breakpointManager.setBreakpoint(this.uiSourceCode(), lineNumber, columnNumber, condition, enabled);
+    this._breakpointManager.setBreakpoint(this._debuggerSourceCode, lineNumber, columnNumber, condition, enabled);
     this._breakpointWasSetForTest(lineNumber, columnNumber, condition, enabled);
   }
 
@@ -1248,13 +1296,16 @@
     this._breakpointManager.removeEventListener(
         Bindings.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
     this.uiSourceCode().removeEventListener(
-        Workspace.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
-    this.uiSourceCode().removeEventListener(
         Workspace.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
     this.uiSourceCode().removeEventListener(
         Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
     this.uiSourceCode().removeEventListener(
         Workspace.UISourceCode.Events.TitleChanged, this._showBlackboxInfobarIfNeeded, this);
+    if (this._debuggerSourceCode) {
+      this._debuggerSourceCode.removeEventListener(
+          Workspace.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
+    }
+
     Common.moduleSetting('skipStackFramesPattern').removeChangeListener(this._showBlackboxInfobarIfNeeded, this);
     Common.moduleSetting('skipContentScripts').removeChangeListener(this._showBlackboxInfobarIfNeeded, this);
     super.dispose();
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
index 371c4e40..9e93907c 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -387,10 +387,6 @@
    * @param {boolean=} skipReveal
    */
   _revealInNavigator(uiSourceCode, skipReveal) {
-    var binding = Persistence.persistence.binding(uiSourceCode);
-    if (binding && binding.network === uiSourceCode)
-      uiSourceCode = binding.fileSystem;
-
     var extensions = self.runtime.extensions(Sources.NavigatorView);
     Promise.all(extensions.map(extension => extension.instance())).then(filterNavigators.bind(this));
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesSearchScope.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesSearchScope.js
index abfc54f..261e24b 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesSearchScope.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesSearchScope.js
@@ -140,7 +140,7 @@
     for (var i = 0; i < uiSourceCodes.length; ++i) {
       var uiSourceCode = uiSourceCodes[i];
       var binding = Persistence.persistence.binding(uiSourceCode);
-      if (binding && binding.fileSystem === uiSourceCode)
+      if (binding && binding.network === uiSourceCode)
         continue;
       if (dirtyOnly && !uiSourceCode.isDirty())
         continue;
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesView.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesView.js
index b6f8816..949e9ea 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesView.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesView.js
@@ -89,6 +89,11 @@
 
     this._shortcuts = {};
     this.element.addEventListener('keydown', this._handleKeyDown.bind(this), false);
+
+    Persistence.persistence.addEventListener(
+        Persistence.Persistence.Events.BindingCreated, this._onBindingChanged, this);
+    Persistence.persistence.addEventListener(
+        Persistence.Persistence.Events.BindingRemoved, this._onBindingChanged, this);
   }
 
   /**
@@ -394,21 +399,42 @@
       /** @type {!Sources.UISourceCodeFrame} */ (sourceView).dispose();
   }
 
+  /**
+   * @param {!Common.Event} event
+   */
+  _onBindingChanged(event) {
+    if (!this._executionLocation)
+      return;
+    var binding = /** @type {!Persistence.PersistenceBinding} */ (event.data);
+    var uiSourceCode = this._executionLocation.uiSourceCode;
+    if (binding.network !== uiSourceCode)
+      return;
+    this.setExecutionLocation(this._executionLocation);
+    this.showSourceLocation(
+        this._executionLocation.uiSourceCode, this._executionLocation.lineNumber, this._executionLocation.columnNumber);
+  }
+
   clearCurrentExecutionLine() {
     if (this._executionSourceFrame)
       this._executionSourceFrame.clearExecutionLine();
-    delete this._executionSourceFrame;
+    this._executionSourceFrame = null;
+    this._executionLocation = null;
   }
 
   /**
    * @param {!Workspace.UILocation} uiLocation
    */
   setExecutionLocation(uiLocation) {
-    var sourceView = this._getOrCreateSourceView(uiLocation.uiSourceCode);
+    this.clearCurrentExecutionLine();
+    var binding = Persistence.persistence.binding(uiLocation.uiSourceCode);
+    var uiSourceCode = binding ? binding.fileSystem : uiLocation.uiSourceCode;
+    var sourceView = this._getOrCreateSourceView(uiSourceCode);
     if (sourceView instanceof Sources.UISourceCodeFrame) {
       var sourceFrame = /** @type {!Sources.UISourceCodeFrame} */ (sourceView);
-      sourceFrame.setExecutionLocation(uiLocation);
+      sourceFrame.setExecutionLocation(
+          new Workspace.UILocation(uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber));
       this._executionSourceFrame = sourceFrame;
+      this._executionLocation = uiLocation;
     }
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js b/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
index 7ecc659..4a669a4 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/TabbedEditorContainer.js
@@ -78,30 +78,32 @@
    */
   _onBindingCreated(event) {
     var binding = /** @type {!Persistence.PersistenceBinding} */ (event.data);
-    this._updateFileTitle(binding.network);
+    this._updateFileTitle(binding.fileSystem);
 
     var networkTabId = this._tabIds.get(binding.network);
     var fileSystemTabId = this._tabIds.get(binding.fileSystem);
-    if (!fileSystemTabId)
+
+    var wasSelectedInNetwork = this._currentFile === binding.network;
+    var currentSelectionRange = this._history.selectionRange(binding.network.url());
+    var currentScrollLineNumber = this._history.scrollLineNumber(binding.network.url());
+    this._history.remove(binding.network.url());
+
+    if (!networkTabId)
       return;
 
-    var wasSelectedInFileSystem = this._currentFile === binding.fileSystem;
-    var currentSelectionRange = this._history.selectionRange(binding.fileSystem.url());
-    var currentScrollLineNumber = this._history.scrollLineNumber(binding.fileSystem.url());
-
-    var tabIndex = this._tabbedPane.tabIndex(fileSystemTabId);
-    var tabsToClose = [fileSystemTabId];
-    if (networkTabId)
-      tabsToClose.push(networkTabId);
+    var tabIndex = this._tabbedPane.tabIndex(networkTabId);
+    var tabsToClose = [networkTabId];
+    if (fileSystemTabId)
+      tabsToClose.push(fileSystemTabId);
     this._closeTabs(tabsToClose, true);
-    networkTabId = this._appendFileTab(binding.network, false, tabIndex);
+    fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tabIndex);
     this._updateHistory();
 
-    if (wasSelectedInFileSystem)
-      this._tabbedPane.selectTab(networkTabId, false);
+    if (wasSelectedInNetwork)
+      this._tabbedPane.selectTab(fileSystemTabId, false);
 
-    var networkTabView = /** @type {!UI.Widget} */ (this._tabbedPane.tabView(networkTabId));
-    this._restoreEditorProperties(networkTabView, currentSelectionRange, currentScrollLineNumber);
+    var fileSystemTabView = /** @type {!UI.Widget} */ (this._tabbedPane.tabView(fileSystemTabId));
+    this._restoreEditorProperties(fileSystemTabView, currentSelectionRange, currentScrollLineNumber);
   }
 
   /**
@@ -109,24 +111,7 @@
    */
   _onBindingRemoved(event) {
     var binding = /** @type {!Persistence.PersistenceBinding} */ (event.data);
-    this._updateFileTitle(binding.network);
-
-    var networkTabId = this._tabIds.get(binding.network);
-    if (!networkTabId)
-      return;
-
-    var tabIndex = this._tabbedPane.tabIndex(networkTabId);
-    var wasSelected = this._currentFile === binding.network;
-    var fileSystemTabId = this._appendFileTab(binding.fileSystem, false, tabIndex);
-    this._updateHistory();
-
-    if (wasSelected)
-      this._tabbedPane.selectTab(fileSystemTabId, false);
-
-    var fileSystemTabView = /** @type {!UI.Widget} */ (this._tabbedPane.tabView(fileSystemTabId));
-    var savedSelectionRange = this._history.selectionRange(binding.network.url());
-    var savedScrollLineNumber = this._history.scrollLineNumber(binding.network.url());
-    this._restoreEditorProperties(fileSystemTabView, savedSelectionRange, savedScrollLineNumber);
+    this._updateFileTitle(binding.fileSystem);
   }
 
   /**
@@ -264,7 +249,7 @@
    */
   _innerShowFile(uiSourceCode, userGesture) {
     var binding = Persistence.persistence.binding(uiSourceCode);
-    uiSourceCode = binding ? binding.network : uiSourceCode;
+    uiSourceCode = binding ? binding.fileSystem : uiSourceCode;
     if (this._currentFile === uiSourceCode)
       return;
 
@@ -295,10 +280,8 @@
    * @return {string}
    */
   _titleForFile(uiSourceCode) {
-    var binding = Persistence.persistence.binding(uiSourceCode);
-    var titleUISourceCode = binding ? binding.fileSystem : uiSourceCode;
     var maxDisplayNameLength = 30;
-    var title = titleUISourceCode.displayName(true).trimMiddle(maxDisplayNameLength);
+    var title = uiSourceCode.displayName(true).trimMiddle(maxDisplayNameLength);
     if (uiSourceCode.isDirty() || Persistence.persistence.hasUnsavedCommittedChanges(uiSourceCode))
       title += '*';
     return title;
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js
index 1693e31c..3b6246f 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js
@@ -258,6 +258,14 @@
     this._installMessageAndDecorationListeners();
     this._updateStyle();
     this._decorateAllTypes();
+    this.onBindingChanged();
+  }
+
+  /**
+   * @protected
+   */
+  onBindingChanged() {
+    // Overriden in subclasses.
   }
 
   _updateStyle() {
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/module.json b/third_party/WebKit/Source/devtools/front_end/sources/module.json
index 6c5e2a9..35b1128 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sources/module.json
@@ -156,7 +156,7 @@
             "contextTypes": ["Sources.UISourceCodeFrame"],
             "bindings": [
                 {
-                    "shortcut": "Ctrl+Shift+A'"
+                    "shortcut": "Ctrl+Shift+A"
                 }
             ]
         },
@@ -173,7 +173,7 @@
             "contextTypes": ["Sources.UISourceCodeFrame"],
             "bindings": [
                 {
-                    "shortcut": "Ctrl+E'"
+                    "shortcut": "Ctrl+Shift+E"
                 }
             ]
         },
diff --git a/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorTextEditor.js b/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorTextEditor.js
index ae224e5..ee0ec96 100644
--- a/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorTextEditor.js
+++ b/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorTextEditor.js
@@ -65,8 +65,8 @@
       'Down': 'goLineDown',
       'End': 'goLineEnd',
       'Home': 'goLineStartSmart',
-      'PageUp': 'smartPageUp',
-      'PageDown': 'smartPageDown',
+      'PageUp': 'goSmartPageUp',
+      'PageDown': 'goSmartPageDown',
       'Delete': 'delCharAfter',
       'Backspace': 'delCharBefore',
       'Tab': 'defaultTab',
@@ -1306,7 +1306,7 @@
 /**
  * @return {!Object|undefined}
  */
-CodeMirror.commands.smartPageUp = function(codemirror) {
+CodeMirror.commands.goSmartPageUp = function(codemirror) {
   if (codemirror._codeMirrorTextEditor.selection().equal(Common.TextRange.createFromLocation(0, 0)))
     return CodeMirror.Pass;
   codemirror.execCommand('goPageUp');
@@ -1315,7 +1315,7 @@
 /**
  * @return {!Object|undefined}
  */
-CodeMirror.commands.smartPageDown = function(codemirror) {
+CodeMirror.commands.goSmartPageDown = function(codemirror) {
   if (codemirror._codeMirrorTextEditor.selection().equal(codemirror._codeMirrorTextEditor.fullRange().collapseToEnd()))
     return CodeMirror.Pass;
   codemirror.execCommand('goPageDown');
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
index 8ddf4d3..cc640277 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
@@ -527,7 +527,6 @@
         var frame = this._model.pageFrameById(node.id);
         var frameName = frame ? Timeline.TimelineUIUtils.displayNameForFrame(frame, 80) : Common.UIString('Page');
         return {name: frameName, color: color};
-        break;
 
       default:
         console.assert(false, 'Unexpected aggregation type');
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
index f4b312d..d94c188 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
@@ -595,7 +595,7 @@
     background-color: inherit;
 }
 
-.timeline-details #filter-input-field {
+.timeline-details .filter-input-field {
     width: 120px;
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/FilterBar.js b/third_party/WebKit/Source/devtools/front_end/ui/FilterBar.js
index d9b38aa..34afd71e 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/FilterBar.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/FilterBar.js
@@ -187,7 +187,6 @@
 
 /**
  * @implements {UI.FilterUI}
- * @implements {UI.SuggestBoxDelegate}
  * @unrestricted
  */
 UI.TextFilterUI = class extends Common.Object {
@@ -202,26 +201,27 @@
     this._filterElement = createElement('div');
     this._filterElement.className = 'filter-text-filter';
 
-    this._filterInputElement =
-        /** @type {!HTMLInputElement} */ (this._filterElement.createChild('input', 'filter-input-field'));
-    this._filterInputElement.placeholder = Common.UIString('Filter');
-    this._filterInputElement.id = 'filter-input-field';
-    this._filterInputElement.addEventListener('input', this._onInput.bind(this), false);
-    this._filterInputElement.addEventListener('change', this._onChange.bind(this), false);
-    this._filterInputElement.addEventListener('keydown', this._onInputKeyDown.bind(this), true);
-    this._filterInputElement.addEventListener('blur', this._onBlur.bind(this), true);
+    this._filterInputElement = this._filterElement.createChild('span', 'filter-input-field');
 
-    /** @type {?UI.TextFilterUI.SuggestionBuilder} */
-    this._suggestionBuilder = null;
+    this._prompt = new UI.TextPrompt();
+    this._prompt.initialize(this._completions.bind(this), ' ');
+    this._proxyElement = this._prompt.attach(this._filterInputElement);
+    this._prompt.setPlaceholder(Common.UIString('Filter'));
 
-    this._suggestBox = new UI.SuggestBox(this);
+    this._proxyElement.addEventListener('input', this._valueChanged.bind(this), false);
+    this._proxyElement.addEventListener('keydown', this._onInputKeyDown.bind(this), false);
+    this._prompt.addEventListener(UI.TextPrompt.Events.ItemAccepted, this._valueChanged.bind(this));
+    this._prompt.addEventListener(UI.TextPrompt.Events.ItemApplied, this._valueChanged.bind(this));
+
+    /** @type {?function(string, string, boolean=):!Promise<!UI.SuggestBox.Suggestions>} */
+    this._suggestionProvider = null;
 
     if (this._supportRegex) {
       this._filterElement.classList.add('supports-regex');
       var label = createCheckboxLabel(Common.UIString('Regex'));
       this._regexCheckBox = label.checkboxElement;
       this._regexCheckBox.id = 'text-filter-regex';
-      this._regexCheckBox.addEventListener('change', this._onInput.bind(this), false);
+      this._regexCheckBox.addEventListener('change', this._valueChanged.bind(this), false);
       this._filterElement.appendChild(label);
 
       this._regexLabel = this._filterElement.textElement;
@@ -229,11 +229,22 @@
   }
 
   /**
+   * @param {string} expression
+   * @param {string} prefix
+   * @param {boolean=} force
+   * @return {!Promise<!UI.SuggestBox.Suggestions>}
+   */
+  _completions(expression, prefix, force) {
+    if (this._suggestionProvider && !this.isRegexChecked())
+      return this._suggestionProvider(expression, prefix, force);
+    return Promise.resolve([]);
+  }
+  /**
    * @override
    * @return {boolean}
    */
   isActive() {
-    return !!this._filterInputElement.value;
+    return !!this._prompt.text();
   }
 
   /**
@@ -241,7 +252,7 @@
    * @return {!Element}
    */
   element() {
-    return this._filterElement;
+    return this._proxyElement;
   }
 
   /**
@@ -255,15 +266,15 @@
    * @return {string}
    */
   value() {
-    return this._filterInputElement.value;
+    return this._prompt.textWithCurrentSuggestion();
   }
 
   /**
    * @param {string} value
    */
   setValue(value) {
-    this._filterInputElement.value = value;
-    this._valueChanged(false);
+    this._prompt.setText(value);
+    this._valueChanged();
   }
 
   /**
@@ -281,70 +292,19 @@
     return this._regex;
   }
 
-  /**
-   * @param {!Event} event
-   */
-  _onBlur(event) {
-    this._cancelSuggestion();
-  }
-
-  _cancelSuggestion() {
-    if (!this._suggestionBuilder || !this._suggestBox.visible())
-      return;
-    this._suggestionBuilder.unapplySuggestion(this._filterInputElement);
-    this._suggestBox.hide();
-  }
-
-  _onInput() {
-    this._valueChanged(true);
-  }
-
-  _onChange() {
-    this._valueChanged(false);
-  }
-
   focus() {
     this._filterInputElement.focus();
   }
 
   /**
-   * @param {?UI.TextFilterUI.SuggestionBuilder} suggestionBuilder
+   * @param {(function(string, string, boolean=):!Promise<!UI.SuggestBox.Suggestions>)} suggestionProvider
    */
-  setSuggestionBuilder(suggestionBuilder) {
-    this._cancelSuggestion();
-    this._suggestionBuilder = suggestionBuilder;
+  setSuggestionProvider(suggestionProvider) {
+    this._prompt.clearAutocomplete();
+    this._suggestionProvider = suggestionProvider;
   }
 
-  _updateSuggestions() {
-    if (!this._suggestionBuilder)
-      return;
-    if (this.isRegexChecked()) {
-      if (this._suggestBox.visible())
-        this._suggestBox.hide();
-      return;
-    }
-    var suggestions = this._suggestionBuilder.buildSuggestions(this._filterInputElement);
-    if (suggestions && suggestions.length) {
-      if (this._suppressSuggestion)
-        delete this._suppressSuggestion;
-      else
-        this._suggestionBuilder.applySuggestion(this._filterInputElement, suggestions[0], true);
-      var anchorBox = this._filterInputElement.boxInWindow().relativeTo(new AnchorBox(-3, 0));
-      this._suggestBox.updateSuggestions(anchorBox, suggestions.map(item => ({title: item})), true, true, '');
-    } else {
-      this._suggestBox.hide();
-    }
-  }
-
-  /**
-   * @param {boolean} showSuggestions
-   */
-  _valueChanged(showSuggestions) {
-    if (showSuggestions)
-      this._updateSuggestions();
-    else
-      this._suggestBox.hide();
-
+  _valueChanged() {
     var filterQuery = this.value();
 
     this._regex = null;
@@ -370,72 +330,11 @@
 
   /**
    * @param {!Event} event
-   * @return {boolean}
    */
   _onInputKeyDown(event) {
-    var handled = false;
-    if (event.key === 'Backspace') {
-      this._suppressSuggestion = true;
-    } else if (this._suggestBox.visible()) {
-      if (event.key === 'Escape') {
-        this._cancelSuggestion();
-        handled = true;
-      } else if (event.key === 'Tab') {
-        this._suggestBox.acceptSuggestion();
-        this._valueChanged(true);
-        handled = true;
-      } else {
-        handled = this._suggestBox.keyPressed(/** @type {!KeyboardEvent} */ (event));
-      }
-    }
-    if (handled)
+    if (isEnterKey(event))
       event.consume(true);
-    return handled;
   }
-
-  /**
-   * @override
-   * @param {string} suggestion
-   * @param {boolean=} isIntermediateSuggestion
-   */
-  applySuggestion(suggestion, isIntermediateSuggestion) {
-    if (!this._suggestionBuilder)
-      return;
-    this._suggestionBuilder.applySuggestion(this._filterInputElement, suggestion, !!isIntermediateSuggestion);
-    if (isIntermediateSuggestion)
-      this._dispatchFilterChanged();
-  }
-
-  /** @override */
-  acceptSuggestion() {
-    this._filterInputElement.scrollLeft = this._filterInputElement.scrollWidth;
-    this._valueChanged(true);
-  }
-};
-
-/**
- * @interface
- */
-UI.TextFilterUI.SuggestionBuilder = function() {};
-
-UI.TextFilterUI.SuggestionBuilder.prototype = {
-  /**
-   * @param {!HTMLInputElement} input
-   * @return {?Array.<string>}
-   */
-  buildSuggestions(input) {},
-
-  /**
-   * @param {!HTMLInputElement} input
-   * @param {string} suggestion
-   * @param {boolean} isIntermediate
-   */
-  applySuggestion(input, suggestion, isIntermediate) {},
-
-  /**
-   * @param {!HTMLInputElement} input
-   */
-  unapplySuggestion(input) {}
 };
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
index 23cd78d..cc55b44f 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -126,7 +126,8 @@
 
     var spacer = 6;
     var maxHeight = Math.min(
-        Math.max(underHeight, aboveHeight) - spacer, this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : 0);
+        Math.max(underHeight, aboveHeight) - spacer,
+        this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : Infinity);
     var height = this._rowHeight * this._items.length;
     this._hasVerticalScroll = height > maxHeight;
     this._element.style.height = Math.min(maxHeight, height) + 'px';
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
index a661c55b..8113627 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -196,6 +196,13 @@
       this._proxyElement.title = title;
   }
 
+  /**
+   * @param {string} placeholder
+   */
+  setPlaceholder(placeholder) {
+    this._element.setAttribute('data-placeholder', placeholder);
+  }
+
   _removeFromElement() {
     this.clearAutocomplete();
     this._element.removeEventListener('keydown', this._boundOnKeyDown, false);
@@ -317,11 +324,16 @@
   }
 
   clearAutocomplete() {
+    var beforeText = this.textWithCurrentSuggestion();
+
     if (this._isSuggestBoxVisible())
       this._suggestBox.hide();
     this._clearAutocompleteTimeout();
     this._queryRange = null;
     this._refreshGhostText();
+
+    if (beforeText !== this.textWithCurrentSuggestion())
+      this.dispatchEventToListeners(UI.TextPrompt.Events.ItemApplied);
   }
 
   _refreshGhostText() {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js
index 4e41b82..047eefe 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js
@@ -30,12 +30,12 @@
 
   /**
    * @param {!Element} element
-   * @param {!Element|string} tooltipContent
+   * @param {?Element|string} tooltipContent
    * @param {string=} actionId
    * @param {!Object=} options
    */
   static install(element, tooltipContent, actionId, options) {
-    if (typeof tooltipContent === 'string' && tooltipContent === '') {
+    if (!tooltipContent) {
       delete element[UI.Tooltip._symbol];
       return;
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/filter.css b/third_party/WebKit/Source/devtools/front_end/ui/filter.css
index d741dfe..2de512b5 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/filter.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/filter.css
@@ -139,13 +139,18 @@
 }
 
 .filter-input-field {
-    -webkit-appearance: none;
     border: 1px solid rgb(163, 163, 163);
     border-radius: 2px;
     padding: 1px 3px 0;
     margin: 0 0 0 1px;
-    width: 253px;
+    width: 143px;
     height: 20px;
     line-height: 17px;
-    flex: 1;
+    -webkit-user-modify: read-write-plaintext-only;
+    -webkit-user-select: text;
+    display: inline-block;
+    background: #FFF;
+    overflow: hidden;
+    white-space: nowrap;
+    cursor: auto;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
index 5d7c7c2..46c5ad6 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/inspectorCommon.css
@@ -283,7 +283,7 @@
 }
 
 :not(.platform-mac).-theme-with-dark-background ::-webkit-scrollbar-thumb,
-:host-context(:not(.platform-mac).-theme-with-dark-background) .-theme-with-dark-background ::-webkit-scrollbar-thumb {
+:host-context(:not(.platform-mac).-theme-with-dark-background) ::-webkit-scrollbar-thumb {
     border-radius: 2px;
     background-color: #333;
     -webkit-box-shadow: inset 0 0 1px rgba(255,255,255,0.5);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css b/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
index e0f93cef..25602a25 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/textPrompt.css
@@ -29,13 +29,17 @@
     white-space: pre;
 }
 
-::content .auto-complete-text,
-.text-prompt-editing ::content .auto-complete-text {
+::content .auto-complete-text {
     color: rgb(128, 128, 128) !important;
     -webkit-user-select: none;
     -webkit-user-modify: read-only;
 }
 
+::content .text-prompt:empty::before {
+    content: attr(data-placeholder);
+    color: rgb(128, 128, 128);
+}
+
 .text-prompt-editing ::content br {
     display: none;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/SortableDataGrid.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/SortableDataGrid.js
index ecf1e48..94db8da0 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/SortableDataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/SortableDataGrid.js
@@ -134,7 +134,6 @@
    */
   sortNodes(comparator, reverseMode) {
     this._sortingFunction = UI.SortableDataGrid.Comparator.bind(null, comparator, reverseMode);
-    this._rootNode.recalculateSiblings(0);
     this._rootNode._sortChildren(reverseMode);
     this.scheduleUpdateStructure();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ViewportDataGrid.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ViewportDataGrid.js
index d75c69b9..4ffffd88 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/ViewportDataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/ViewportDataGrid.js
@@ -21,6 +21,8 @@
     this._scrollContainer.addEventListener('mousewheel', this._onWheel.bind(this), true);
     /** @type {!Array.<!UI.ViewportDataGridNode>} */
     this._visibleNodes = [];
+    /** @type {?Array.<!UI.ViewportDataGridNode>} */
+    this._flatNodes = null;
     /** @type {boolean} */
     this._inline = false;
 
@@ -88,6 +90,7 @@
    * @protected
    */
   scheduleUpdateStructure() {
+    this._flatNodes = null;
     this.scheduleUpdate();
   }
 
@@ -117,12 +120,40 @@
   }
 
   /**
+   * @return {!Array.<!UI.ViewportDataGridNode>}
+   */
+  flatNodesList() {
+    if (this._flatNodes)
+      return this._flatNodes;
+    var flatNodes = [];
+    var children = [this._rootNode.children];
+    var counters = [0];
+    var depth = 0;
+    while (depth >= 0) {
+      var node = children[depth][counters[depth]++];
+      if (!node) {
+        depth--;
+        continue;
+      }
+      flatNodes.push(node);
+      node.setDepth(depth);
+      if (node._expanded && node.children.length) {
+        depth++;
+        children[depth] = node.children;
+        counters[depth] = 0;
+      }
+    }
+    this._flatNodes = flatNodes;
+    return this._flatNodes;
+  }
+
+  /**
    * @param {number} clientHeight
    * @param {number} scrollTop
    * @return {{topPadding: number, bottomPadding: number, contentHeight: number, visibleNodes: !Array.<!UI.ViewportDataGridNode>, offset: number}}
    */
   _calculateVisibleNodes(clientHeight, scrollTop) {
-    var nodes = this.rootNode().flattenChildren();
+    var nodes = this.flatNodesList();
     if (this._inline)
       return {topPadding: 0, bottomPadding: 0, contentHeight: 0, visibleNodes: nodes, offset: 0};
 
@@ -156,7 +187,7 @@
    * @return {number}
    */
   _contentHeight() {
-    var nodes = this.rootNode().flattenChildren();
+    var nodes = this.flatNodesList();
     var result = 0;
     for (var i = 0, size = nodes.length; i < size; ++i)
       result += nodes[i].nodeSelfHeight();
@@ -232,7 +263,7 @@
    * @param {!UI.ViewportDataGridNode} node
    */
   _revealViewportNode(node) {
-    var nodes = this.rootNode().flattenChildren();
+    var nodes = this.flatNodesList();
     var index = nodes.indexOf(node);
     if (index === -1)
       return;
@@ -268,8 +299,6 @@
     super(data, hasChildren);
     /** @type {boolean} */
     this._stale = false;
-    /** @type {?Array<!UI.ViewportDataGridNode>} */
-    this._flatNodes = null;
   }
 
   /**
@@ -291,42 +320,6 @@
     return /** @type {!Element} */ (this._element);
   }
 
-  _clearFlatNodes() {
-    this._flatNodes = null;
-    var parent = /** @type {!UI.ViewportDataGridNode} */ (this.parent);
-    if (parent)
-      parent._clearFlatNodes();
-  }
-
-  /**
-   * @return {!Array<!UI.ViewportDataGridNode>}
-   */
-  flattenChildren() {
-    if (this._flatNodes)
-      return this._flatNodes;
-    var flatNodes = [];
-    var children = [this.children];
-    var counters = [0];
-    var depth = 0;
-    while (depth >= 0) {
-      var node = children[depth][counters[depth]++];
-      if (!node) {
-        depth--;
-        continue;
-      }
-      flatNodes.push(node);
-      node.setDepth(depth);
-      if (node._expanded && node.children.length) {
-        depth++;
-        children[depth] = node.children;
-        counters[depth] = 0;
-      }
-    }
-
-    this._flatNodes = flatNodes;
-    return flatNodes;
-  }
-
   /**
    * @param {number} depth
    */
@@ -340,7 +333,6 @@
    * @param {number} index
    */
   insertChild(child, index) {
-    this._clearFlatNodes();
     if (child.parent === this) {
       var currentIndex = this.children.indexOf(child);
       if (currentIndex < 0)
@@ -366,7 +358,6 @@
    * @param {!UI.DataGridNode} child
    */
   removeChild(child) {
-    this._clearFlatNodes();
     if (this.dataGrid)
       this.dataGrid.updateSelectionBeforeRemoval(child, false);
     if (child.previousSibling)
@@ -388,7 +379,6 @@
    * @override
    */
   removeChildren() {
-    this._clearFlatNodes();
     if (this.dataGrid)
       this.dataGrid.updateSelectionBeforeRemoval(this, true);
     for (var i = 0; i < this.children.length; ++i)
@@ -475,13 +465,4 @@
   reveal() {
     this.dataGrid._revealViewportNode(this);
   }
-
-  /**
-   * @override
-   * @param {number} index
-   */
-  recalculateSiblings(index) {
-    this._clearFlatNodes();
-    super.recalculateSiblings(index);
-  }
 };
diff --git a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
index afc5667..911570d 100755
--- a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
+++ b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
@@ -38,6 +38,8 @@
 from build import modular_build
 from build import generate_protocol_externs
 
+import utils
+
 try:
     import simplejson as json
 except ImportError:
@@ -97,24 +99,6 @@
 generate_protocol_externs.generate_protocol_externs(protocol_externs_file, path.join(inspector_path, 'browser_protocol.json'), path.join(v8_inspector_path, 'js_protocol.json'))
 
 
-# Based on http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python.
-def which(program):
-    def is_exe(fpath):
-        return path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-    fpath, fname = path.split(program)
-    if fpath:
-        if is_exe(program):
-            return program
-    else:
-        for part in os.environ["PATH"].split(os.pathsep):
-            part = part.strip('"')
-            exe_file = path.join(part, program)
-            if is_exe(exe_file):
-                return exe_file
-    return None
-
-
 def log_error(message):
     print 'ERROR: ' + message
 
@@ -207,9 +191,7 @@
     required_minor = 7
     exec_command = None
     has_server_jvm = True
-    java_path = which('java')
-    if not java_path:
-        java_path = which('java.exe')
+    java_path = utils.which('java')
 
     if not java_path:
         print 'NOTE: No Java executable found in $PATH.'
diff --git a/third_party/WebKit/Source/devtools/scripts/install_node_deps.py b/third_party/WebKit/Source/devtools/scripts/install_node_deps.py
index ab8eeab..6dd474eb 100755
--- a/third_party/WebKit/Source/devtools/scripts/install_node_deps.py
+++ b/third_party/WebKit/Source/devtools/scripts/install_node_deps.py
@@ -14,6 +14,8 @@
 import subprocess
 import sys
 
+import utils
+
 MIN_NODE_VERSION = 4
 LOCAL_NODE_VERSION = '4.5.0'
 
@@ -31,7 +33,9 @@
 
 def resolve_node_paths():
     if has_valid_global_node():
-        return (which('node'), which('npm'))
+        if sys.platform == "win32":
+            return (utils.which('node'), utils.which('npm.cmd'))
+        return (utils.which('node'), utils.which('npm'))
     has_installed_local_node = path.isfile(local_node_binary_path)
     if has_installed_local_node:
         return (local_node_binary_path, local_npm_binary_path)
@@ -46,7 +50,7 @@
 
 
 def has_valid_global_node():
-    node_path = which('node')
+    node_path = utils.which('node')
     if not node_path:
         return False
     node_process = popen([node_path, '--version'])
@@ -79,24 +83,6 @@
     print(npm_process_out)
 
 
-# Based on http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python.
-def which(program):
-    def is_exe(fpath):
-        return path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-    fpath, fname = path.split(program)
-    if fpath:
-        if is_exe(program):
-            return program
-    else:
-        for part in os.environ['PATH'].split(os.pathsep):
-            part = part.strip('\"')
-            exe_file = path.join(part, program)
-            if is_exe(exe_file):
-                return exe_file
-    return None
-
-
 def popen(arguments, env=None):
     return subprocess.Popen(
         arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
diff --git a/third_party/WebKit/Source/devtools/scripts/lint_javascript.py b/third_party/WebKit/Source/devtools/scripts/lint_javascript.py
index 3f1e234..3390441 100755
--- a/third_party/WebKit/Source/devtools/scripts/lint_javascript.py
+++ b/third_party/WebKit/Source/devtools/scripts/lint_javascript.py
@@ -29,8 +29,8 @@
 is_cygwin = sys.platform == "cygwin"
 
 
-def popen(arguments):
-    return subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+def popen(arguments, cwd=None):
+    return subprocess.Popen(arguments, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
 
 def to_platform_path(filepath):
@@ -49,7 +49,7 @@
 scripts_path = path.dirname(path.abspath(__file__))
 devtools_path = path.dirname(scripts_path)
 devtools_frontend_path = path.join(devtools_path, "front_end")
-eslint_path = path.join(devtools_path, "node_modules", ".bin", "eslint")
+eslint_path = path.join(devtools_path, "node_modules", "eslint", "bin", "eslint.js")
 
 print("Linting JavaScript with eslint...\n")
 
@@ -75,7 +75,7 @@
         "--ignore-path", to_platform_path_exact(eslintignore_path),
     ] + files_list
 
-    eslint_proc = popen(exec_command)
+    eslint_proc = popen(exec_command, cwd=devtools_path)
     (eslint_proc_out, _) = eslint_proc.communicate()
     if eslint_proc.returncode != 0:
         eslint_errors_found = True
diff --git a/third_party/WebKit/Source/devtools/scripts/utils.py b/third_party/WebKit/Source/devtools/scripts/utils.py
new file mode 100644
index 0000000..4ff392b
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/scripts/utils.py
@@ -0,0 +1,42 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+from os import path
+import sys
+
+
+# Based on http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python.
+def which(program):
+    def is_executable(fpath):
+        return path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+    fpath, fname = path.split(program)
+    if fpath:
+        if is_executable(program):
+            return program
+        return None
+    env_paths = os.environ["PATH"].split(os.pathsep)
+    if sys.platform == "win32":
+        env_paths = get_windows_path(env_paths)
+    for part in env_paths:
+        part = part.strip('\"')
+        file = path.join(part, program)
+        if is_executable(file):
+            return file
+        if sys.platform == "win32" and not file.endswith(".exe"):
+            file_exe = file + ".exe"
+            if is_executable(file_exe):
+                return file_exe
+    return None
+
+
+# Use to find 64-bit programs (e.g. Java) when using 32-bit python in Windows
+def get_windows_path(env_paths):
+    new_env_paths = env_paths[:]
+    for env_path in env_paths:
+        env_path = env_path.lower()
+        if "system32" in env_path:
+            new_env_paths.append(env_path.replace("system32", "sysnative"))
+    return new_env_paths
diff --git a/third_party/WebKit/Source/modules/EventTargetModulesFactory.in b/third_party/WebKit/Source/modules/EventTargetModulesFactory.in
index 5dab681..3f6e790 100644
--- a/third_party/WebKit/Source/modules/EventTargetModulesFactory.in
+++ b/third_party/WebKit/Source/modules/EventTargetModulesFactory.in
@@ -42,6 +42,7 @@
 modules/speech/SpeechRecognition
 modules/speech/SpeechSynthesis
 modules/speech/SpeechSynthesisUtterance
+modules/vr/VRDisplay
 modules/webaudio/AudioContext
 modules/webaudio/AudioNode
 modules/webmidi/MIDIAccess
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
index eef1ab9..ef53fd5 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -194,7 +194,7 @@
                                AXObjectCacheImpl& axObjectCache)
     : AXNodeObject(layoutObject->node(), axObjectCache),
       m_layoutObject(layoutObject) {
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   m_layoutObject->setHasAXObject(true);
 #endif
 }
@@ -314,7 +314,7 @@
 
   detachRemoteSVGRoot();
 
-#if ENABLE(ASSERT)
+#if DCHECK_IS_ON()
   if (m_layoutObject)
     m_layoutObject->setHasAXObject(false);
 #endif
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h
index f369d64..6ac85ccd 100644
--- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerGlobalScope.h
@@ -38,7 +38,7 @@
                    PassRefPtr<SerializedScriptValue>,
                    const MessagePortArray&,
                    ExceptionState&);
-  static bool canTransferArrayBuffer() { return true; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return true; }
   DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
 
   int requestAnimationFrame(FrameRequestCallback*);
diff --git a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
index 0e30a532..6d0ca72d 100644
--- a/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/CompositorWorkerThreadTest.cpp
@@ -10,7 +10,6 @@
 #include "bindings/core/v8/WorkerOrWorkletScriptController.h"
 #include "core/dom/CompositorProxyClient.h"
 #include "core/inspector/ConsoleMessage.h"
-#include "core/testing/DummyPageHolder.h"
 #include "core/workers/InProcessWorkerObjectProxy.h"
 #include "core/workers/ParentFrameTaskRunners.h"
 #include "core/workers/WorkerBackingThread.h"
@@ -36,9 +35,8 @@
 // CompositorWorkerThreads.
 class TestCompositorWorkerObjectProxy : public InProcessWorkerObjectProxy {
  public:
-  static std::unique_ptr<TestCompositorWorkerObjectProxy> create(
-      ExecutionContext* context) {
-    return wrapUnique(new TestCompositorWorkerObjectProxy(context));
+  static std::unique_ptr<TestCompositorWorkerObjectProxy> create() {
+    return wrapUnique(new TestCompositorWorkerObjectProxy());
   }
 
   // (Empty) WorkerReportingProxy implementation:
@@ -50,28 +48,16 @@
                             const String& message,
                             SourceLocation*) override {}
   void postMessageToPageInspector(const String&) override {}
-  ParentFrameTaskRunners* getParentFrameTaskRunners() override {
-    return m_parentFrameTaskRunners.get();
-  }
-
   void didCreateWorkerGlobalScope(WorkerOrWorkletGlobalScope*) override {}
   void didEvaluateWorkerScript(bool success) override {}
   void didCloseWorkerGlobalScope() override {}
   void willDestroyWorkerGlobalScope() override {}
   void didTerminateWorkerThread() override {}
 
-  ExecutionContext* getExecutionContext() override {
-    return m_executionContext.get();
-  }
-
  private:
-  TestCompositorWorkerObjectProxy(ExecutionContext* context)
-      : InProcessWorkerObjectProxy(nullptr),
-        m_executionContext(context),
-        m_parentFrameTaskRunners(ParentFrameTaskRunners::create(nullptr)) {}
-
-  Persistent<ExecutionContext> m_executionContext;
-  Persistent<ParentFrameTaskRunners> m_parentFrameTaskRunners;
+  TestCompositorWorkerObjectProxy()
+      : InProcessWorkerObjectProxy(nullptr,
+                                   ParentFrameTaskRunners::create(nullptr)) {}
 };
 
 class TestCompositorProxyClient
@@ -111,15 +97,12 @@
  public:
   void SetUp() override {
     CompositorWorkerThread::createSharedBackingThreadForTest();
-    m_page = DummyPageHolder::create();
-    m_objectProxy =
-        TestCompositorWorkerObjectProxy::create(&m_page->document());
+    m_objectProxy = TestCompositorWorkerObjectProxy::create();
     m_securityOrigin =
         SecurityOrigin::create(KURL(ParsedURLString, "http://fake.url/"));
   }
 
   void TearDown() override {
-    m_page.reset();
     CompositorWorkerThread::clearSharedBackingThread();
   }
 
@@ -158,7 +141,6 @@
     waitEvent->signal();
   }
 
-  std::unique_ptr<DummyPageHolder> m_page;
   RefPtr<SecurityOrigin> m_securityOrigin;
   std::unique_ptr<InProcessWorkerObjectProxy> m_objectProxy;
   CompositorWorkerTestPlatform m_testPlatform;
diff --git a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
index 86a5f8b..cafba93 100644
--- a/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
+++ b/third_party/WebKit/Source/modules/encryptedmedia/MediaKeySession.cpp
@@ -40,8 +40,8 @@
 #include "modules/encryptedmedia/MediaKeyMessageEvent.h"
 #include "modules/encryptedmedia/MediaKeys.h"
 #include "platform/ContentDecryptionModuleResult.h"
-#include "platform/ContentType.h"
 #include "platform/Timer.h"
+#include "platform/network/mime/ContentType.h"
 #include "public/platform/WebContentDecryptionModule.h"
 #include "public/platform/WebContentDecryptionModuleException.h"
 #include "public/platform/WebContentDecryptionModuleSession.h"
diff --git a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
index 198aacf5f..07fa3f9 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchManager.cpp
@@ -832,14 +832,6 @@
 void FetchManager::Loader::performDataFetch() {
   ASSERT(m_request->url().protocolIsData());
 
-  // Spec: https://fetch.spec.whatwg.org/#concept-basic-fetch
-  // If |request|'s method is `GET` .... Otherwise, return a network error.
-  if (m_request->method() != HTTPNames::GET) {
-    performNetworkError(
-        "Only 'GET' method is allowed for data URLs in Fetch API.");
-    return;
-  }
-
   ResourceRequest request(m_request->url());
   request.setRequestContext(m_request->context());
   request.setUseStreamOnResponse(true);
@@ -895,7 +887,7 @@
 }
 
 FetchManager::FetchManager(ExecutionContext* executionContext)
-    : ContextLifecycleObserver(executionContext), m_isStopped(false) {}
+    : ContextLifecycleObserver(executionContext) {}
 
 ScriptPromise FetchManager::fetch(ScriptState* scriptState,
                                   FetchRequestData* request) {
@@ -913,8 +905,6 @@
 }
 
 void FetchManager::contextDestroyed() {
-  ASSERT(!m_isStopped);
-  m_isStopped = true;
   for (auto& loader : m_loaders)
     loader->dispose();
 }
diff --git a/third_party/WebKit/Source/modules/fetch/FetchManager.h b/third_party/WebKit/Source/modules/fetch/FetchManager.h
index 845ddf30..ac06ecb4 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchManager.h
+++ b/third_party/WebKit/Source/modules/fetch/FetchManager.h
@@ -24,7 +24,6 @@
   explicit FetchManager(ExecutionContext*);
   ScriptPromise fetch(ScriptState*, FetchRequestData*);
   void contextDestroyed() override;
-  bool isStopped() const { return m_isStopped; }
 
   DECLARE_TRACE();
 
@@ -35,7 +34,6 @@
   void onLoaderFinished(Loader*);
 
   HeapHashSet<Member<Loader>> m_loaders;
-  bool m_isStopped;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/fetch/GlobalFetch.cpp b/third_party/WebKit/Source/modules/fetch/GlobalFetch.cpp
index 258978c..25c7a74 100644
--- a/third_party/WebKit/Source/modules/fetch/GlobalFetch.cpp
+++ b/third_party/WebKit/Source/modules/fetch/GlobalFetch.cpp
@@ -45,10 +45,6 @@
       exceptionState.throwTypeError("The global scope is shutting down.");
       return ScriptPromise();
     }
-    if (m_fetchManager->isStopped()) {
-      exceptionState.throwTypeError("The global scope is shutting down.");
-      return ScriptPromise();
-    }
 
     // "Let |r| be the associated request of the result of invoking the
     // initial value of Request as constructor with |input| and |init| as
diff --git a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
index 77ddf82..5a7f140 100644
--- a/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
+++ b/third_party/WebKit/Source/modules/mediarecorder/MediaRecorder.cpp
@@ -10,8 +10,8 @@
 #include "core/inspector/ConsoleMessage.h"
 #include "modules/EventTargetModules.h"
 #include "modules/mediarecorder/BlobEvent.h"
-#include "platform/ContentType.h"
 #include "platform/blob/BlobData.h"
+#include "platform/network/mime/ContentType.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebMediaStream.h"
 #include "wtf/PtrUtil.h"
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
index b2fc748..d079eee 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaMetadataSanitizer.cpp
@@ -39,10 +39,10 @@
     return false;
 
   if (!src.protocolIs(url::kHttpScheme) && !src.protocolIs(url::kHttpsScheme) &&
-      !src.protocolIs(url::kDataScheme)) {
+      !src.protocolIs(url::kDataScheme) && !src.protocolIs(url::kBlobScheme)) {
     context->addConsoleMessage(ConsoleMessage::create(
         JSMessageSource, WarningMessageLevel,
-        "MediaImage src can only be of http/https/data scheme: " +
+        "MediaImage src can only be of http/https/data/blob scheme: " +
             src.getString()));
     return false;
   }
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
index 6f85142..b723323 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
@@ -4,7 +4,6 @@
 
 #include "modules/mediasession/MediaSession.h"
 
-#include "bindings/core/v8/ScriptState.h"
 #include "core/dom/Document.h"
 #include "core/dom/DocumentUserGestureToken.h"
 #include "core/dom/ExecutionContext.h"
@@ -69,11 +68,11 @@
 
 }  // anonymous namespace
 
-MediaSession::MediaSession(ScriptState* scriptState)
-    : m_scriptState(scriptState), m_clientBinding(this) {}
+MediaSession::MediaSession(ExecutionContext* executionContext)
+    : ContextLifecycleObserver(executionContext), m_clientBinding(this) {}
 
-MediaSession* MediaSession::create(ScriptState* scriptState) {
-  return new MediaSession(scriptState);
+MediaSession* MediaSession::create(ExecutionContext* executionContext) {
+  return new MediaSession(executionContext);
 }
 
 void MediaSession::dispose() {
@@ -81,8 +80,7 @@
 }
 
 void MediaSession::setMetadata(MediaMetadata* metadata) {
-  if (mojom::blink::MediaSessionService* service =
-          getService(m_scriptState.get())) {
+  if (mojom::blink::MediaSessionService* service = getService()) {
     service->SetMetadata(MediaMetadataSanitizer::sanitizeAndConvertToMojo(
         metadata, getExecutionContext()));
   }
@@ -97,17 +95,18 @@
 }
 
 ExecutionContext* MediaSession::getExecutionContext() const {
-  return m_scriptState->getExecutionContext();
+  return ContextLifecycleObserver::getExecutionContext();
 }
 
-mojom::blink::MediaSessionService* MediaSession::getService(
-    ScriptState* scriptState) {
+mojom::blink::MediaSessionService* MediaSession::getService() {
   if (m_service)
     return m_service.get();
+  if (!getExecutionContext())
+    return nullptr;
 
-  DCHECK(scriptState->getExecutionContext()->isDocument())
+  DCHECK(getExecutionContext()->isDocument())
       << "MediaSession::getService() is only available from a frame";
-  Document* document = toDocument(scriptState->getExecutionContext());
+  Document* document = toDocument(getExecutionContext());
   if (!document->frame())
     return nullptr;
 
@@ -126,8 +125,7 @@
     const AtomicString& eventType,
     EventListener* listener,
     const AddEventListenerOptionsResolved& options) {
-  if (mojom::blink::MediaSessionService* service =
-          getService(m_scriptState.get())) {
+  if (mojom::blink::MediaSessionService* service = getService()) {
     auto mojomAction = eventNameToMojomAction(eventType);
     DCHECK(mojomAction.has_value());
     service->EnableAction(mojomAction.value());
@@ -139,8 +137,7 @@
     const AtomicString& eventType,
     const EventListener* listener,
     const EventListenerOptions& options) {
-  if (mojom::blink::MediaSessionService* service =
-          getService(m_scriptState.get())) {
+  if (mojom::blink::MediaSessionService* service = getService()) {
     auto mojomAction = eventNameToMojomAction(eventType);
     DCHECK(mojomAction.has_value());
     service->DisableAction(mojomAction.value());
@@ -160,6 +157,7 @@
 DEFINE_TRACE(MediaSession) {
   visitor->trace(m_metadata);
   EventTargetWithInlineData::trace(visitor);
+  ContextLifecycleObserver::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaSession.h b/third_party/WebKit/Source/modules/mediasession/MediaSession.h
index 08bca66..bf7fb75 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaSession.h
+++ b/third_party/WebKit/Source/modules/mediasession/MediaSession.h
@@ -6,6 +6,7 @@
 #define MediaSession_h
 
 #include "bindings/core/v8/ScriptWrappable.h"
+#include "core/dom/ContextLifecycleObserver.h"
 #include "core/events/EventTarget.h"
 #include "modules/ModulesExport.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -15,17 +16,19 @@
 
 namespace blink {
 
+class ExecutionContext;
 class MediaMetadata;
-class ScriptState;
 
 class MODULES_EXPORT MediaSession final
     : public EventTargetWithInlineData,
+      public ContextLifecycleObserver,
       blink::mojom::blink::MediaSessionClient {
+  USING_GARBAGE_COLLECTED_MIXIN(MediaSession);
   DEFINE_WRAPPERTYPEINFO();
   USING_PRE_FINALIZER(MediaSession, dispose);
 
  public:
-  static MediaSession* create(ScriptState*);
+  static MediaSession* create(ExecutionContext*);
 
   void dispose();
 
@@ -49,7 +52,7 @@
  private:
   friend class MediaSessionTest;
 
-  MediaSession(ScriptState*);
+  explicit MediaSession(ExecutionContext*);
 
   // EventTarget overrides
   bool addEventListenerInternal(
@@ -64,9 +67,8 @@
   void DidReceiveAction(blink::mojom::blink::MediaSessionAction) override;
 
   // Returns null when the ExecutionContext is not document.
-  mojom::blink::MediaSessionService* getService(ScriptState*);
+  mojom::blink::MediaSessionService* getService();
 
-  RefPtr<ScriptState> m_scriptState;
   Member<MediaMetadata> m_metadata;
   mojom::blink::MediaSessionServicePtr m_service;
   mojo::Binding<blink::mojom::blink::MediaSessionClient> m_clientBinding;
diff --git a/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp b/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
index c172ff9..2489112 100644
--- a/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/NavigatorMediaSession.cpp
@@ -35,7 +35,7 @@
                                                   Navigator& navigator) {
   NavigatorMediaSession& self = NavigatorMediaSession::from(navigator);
   if (!self.m_session)
-    self.m_session = MediaSession::create(scriptState);
+    self.m_session = MediaSession::create(scriptState->getExecutionContext());
   return self.m_session.get();
 }
 
diff --git a/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp b/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
index 8e446eb..adee5f4 100644
--- a/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/MediaSource.cpp
@@ -43,9 +43,9 @@
 #include "core/html/track/VideoTrackList.h"
 #include "modules/mediasource/MediaSourceRegistry.h"
 #include "modules/mediasource/SourceBufferTrackBaseSupplement.h"
-#include "platform/ContentType.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/RuntimeEnabledFeatures.h"
+#include "platform/network/mime/ContentType.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/tracing/TraceEvent.h"
 #include "public/platform/WebMediaSource.h"
 #include "public/platform/WebSourceBuffer.h"
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp b/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
index 2fcf28ba..bb279db 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStream.cpp
@@ -96,10 +96,9 @@
 
 MediaStream::MediaStream(ExecutionContext* context,
                          MediaStreamDescriptor* streamDescriptor)
-    : ContextLifecycleObserver(context),
-      m_stopped(false),
-      m_descriptor(streamDescriptor),
-      m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired) {
+    : m_descriptor(streamDescriptor),
+      m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired),
+      m_executionContext(context) {
   m_descriptor->setClient(this);
 
   size_t numberOfAudioTracks = m_descriptor->numberOfAudioComponents();
@@ -128,9 +127,8 @@
 MediaStream::MediaStream(ExecutionContext* context,
                          const MediaStreamTrackVector& audioTracks,
                          const MediaStreamTrackVector& videoTracks)
-    : ContextLifecycleObserver(context),
-      m_stopped(false),
-      m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired) {
+    : m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired),
+      m_executionContext(context) {
   MediaStreamComponentVector audioComponents;
   MediaStreamComponentVector videoComponents;
 
@@ -297,7 +295,7 @@
 }
 
 void MediaStream::streamEnded() {
-  if (m_stopped)
+  if (m_executionContext->isContextDestroyed())
     return;
 
   if (active()) {
@@ -311,34 +309,25 @@
     EventListener* listener,
     const AddEventListenerOptionsResolved& options) {
   if (eventType == EventTypeNames::active)
-    UseCounter::count(getExecutionContext(), UseCounter::MediaStreamOnActive);
+    UseCounter::count(m_executionContext, UseCounter::MediaStreamOnActive);
   else if (eventType == EventTypeNames::inactive)
-    UseCounter::count(getExecutionContext(), UseCounter::MediaStreamOnInactive);
+    UseCounter::count(m_executionContext, UseCounter::MediaStreamOnInactive);
 
   return EventTargetWithInlineData::addEventListenerInternal(eventType,
                                                              listener, options);
 }
 
-void MediaStream::contextDestroyed() {
-  ContextLifecycleObserver::contextDestroyed();
-  m_stopped = true;
-}
-
 const AtomicString& MediaStream::interfaceName() const {
   return EventTargetNames::MediaStream;
 }
 
-ExecutionContext* MediaStream::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
-}
-
 void MediaStream::addRemoteTrack(MediaStreamComponent* component) {
   DCHECK(component);
-  if (m_stopped)
+  if (m_executionContext->isContextDestroyed())
     return;
 
   MediaStreamTrack* track =
-      MediaStreamTrack::create(getExecutionContext(), component);
+      MediaStreamTrack::create(m_executionContext, component);
   switch (component->source()->type()) {
     case MediaStreamSource::TypeAudio:
       m_audioTracks.append(track);
@@ -361,7 +350,7 @@
 
 void MediaStream::removeRemoteTrack(MediaStreamComponent* component) {
   DCHECK(component);
-  if (m_stopped)
+  if (m_executionContext->isContextDestroyed())
     return;
 
   MediaStreamTrackVector* tracks = 0;
@@ -406,7 +395,7 @@
 }
 
 void MediaStream::scheduledEventTimerFired(TimerBase*) {
-  if (m_stopped)
+  if (m_executionContext->isContextDestroyed())
     return;
 
   HeapVector<Member<Event>> events;
@@ -428,8 +417,8 @@
   visitor->trace(m_videoTracks);
   visitor->trace(m_descriptor);
   visitor->trace(m_scheduledEvents);
+  visitor->trace(m_executionContext);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
   MediaStreamDescriptorClient::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStream.h b/third_party/WebKit/Source/modules/mediastream/MediaStream.h
index e3bca0af..d04cd2c 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStream.h
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStream.h
@@ -26,7 +26,7 @@
 #ifndef MediaStream_h
 #define MediaStream_h
 
-#include "core/dom/ContextLifecycleObserver.h"
+#include "core/dom/ExecutionContext.h"
 #include "core/html/URLRegistry.h"
 #include "modules/EventTargetModules.h"
 #include "modules/ModulesExport.h"
@@ -40,8 +40,7 @@
 
 class MODULES_EXPORT MediaStream final : public EventTargetWithInlineData,
                                          public URLRegistrable,
-                                         public MediaStreamDescriptorClient,
-                                         public ContextLifecycleObserver {
+                                         public MediaStreamDescriptorClient {
   USING_GARBAGE_COLLECTED_MIXIN(MediaStream);
   DEFINE_WRAPPERTYPEINFO();
 
@@ -79,7 +78,9 @@
 
   // EventTarget
   const AtomicString& interfaceName() const override;
-  ExecutionContext* getExecutionContext() const override;
+  ExecutionContext* getExecutionContext() const override {
+    return m_executionContext;
+  }
 
   // URLRegistrable
   URLRegistry& registry() const override;
@@ -98,9 +99,6 @@
               const MediaStreamTrackVector& audioTracks,
               const MediaStreamTrackVector& videoTracks);
 
-  // ContextLifecycleObserver
-  void contextDestroyed() override;
-
   // MediaStreamDescriptorClient
   void addRemoteTrack(MediaStreamComponent*) override;
   void removeRemoteTrack(MediaStreamComponent*) override;
@@ -110,14 +108,13 @@
   void scheduleDispatchEvent(Event*);
   void scheduledEventTimerFired(TimerBase*);
 
-  bool m_stopped;
-
   MediaStreamTrackVector m_audioTracks;
   MediaStreamTrackVector m_videoTracks;
   Member<MediaStreamDescriptor> m_descriptor;
 
   Timer<MediaStream> m_scheduledEventTimer;
   HeapVector<Member<Event>> m_scheduledEvents;
+  Member<ExecutionContext> m_executionContext;
 };
 
 typedef HeapVector<Member<MediaStream>> MediaStreamVector;
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 58786c170..8985d76 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -503,6 +503,7 @@
                     "webaudio/AudioBufferOptions.idl",
                     "webaudio/AudioBufferSourceOptions.idl",
                     "webaudio/AudioNodeOptions.idl",
+                    "webaudio/AudioProcessingEventInit.idl",
                     "webaudio/BiquadFilterOptions.idl",
                     "webaudio/ChannelMergerOptions.idl",
                     "webaudio/ChannelSplitterOptions.idl",
@@ -518,6 +519,7 @@
                     "webaudio/PeriodicWaveConstraints.idl",
                     "webaudio/PeriodicWaveOptions.idl",
                     "webaudio/OscillatorOptions.idl",
+                    "webaudio/OfflineAudioCompletionEventInit.idl",
                     "webaudio/StereoPannerOptions.idl",
                     "webaudio/WaveShaperOptions.idl",
                     "webgl/WebGLContextAttributes.idl",
@@ -887,6 +889,8 @@
   "$blink_modules_output_dir/webaudio/AudioBufferSourceOptions.h",
   "$blink_modules_output_dir/webaudio/AudioNodeOptions.cpp",
   "$blink_modules_output_dir/webaudio/AudioNodeOptions.h",
+  "$blink_modules_output_dir/webaudio/AudioProcessingEventInit.cpp",
+  "$blink_modules_output_dir/webaudio/AudioProcessingEventInit.h",
   "$blink_modules_output_dir/webaudio/BiquadFilterOptions.cpp",
   "$blink_modules_output_dir/webaudio/BiquadFilterOptions.h",
   "$blink_modules_output_dir/webaudio/ChannelMergerOptions.cpp",
@@ -915,6 +919,8 @@
   "$blink_modules_output_dir/webaudio/PeriodicWaveConstraints.h",
   "$blink_modules_output_dir/webaudio/PeriodicWaveOptions.cpp",
   "$blink_modules_output_dir/webaudio/PeriodicWaveOptions.h",
+  "$blink_modules_output_dir/webaudio/OfflineAudioCompletionEventInit.cpp",
+  "$blink_modules_output_dir/webaudio/OfflineAudioCompletionEventInit.h",
   "$blink_modules_output_dir/webaudio/OscillatorOptions.cpp",
   "$blink_modules_output_dir/webaudio/OscillatorOptions.h",
   "$blink_modules_output_dir/webaudio/StereoPannerOptions.cpp",
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.cpp b/third_party/WebKit/Source/modules/notifications/Notification.cpp
index 980b5ac..2090e9a0 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.cpp
+++ b/third_party/WebKit/Source/modules/notifications/Notification.cpp
@@ -155,7 +155,8 @@
 
 void Notification::prepareShow() {
   DCHECK_EQ(m_state, State::Loading);
-  if (NotificationManager::from(getExecutionContext())->permissionStatus() !=
+  if (NotificationManager::from(getExecutionContext())
+          ->permissionStatus(getExecutionContext()) !=
       mojom::blink::PermissionStatus::GRANTED) {
     dispatchErrorEvent();
     return;
@@ -355,7 +356,7 @@
 
 String Notification::permission(ExecutionContext* context) {
   return permissionString(
-      NotificationManager::from(context)->permissionStatus());
+      NotificationManager::from(context)->permissionStatus(context));
 }
 
 ScriptPromise Notification::requestPermission(
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp b/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
index 0042689..00ba58c 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationImageLoader.cpp
@@ -160,8 +160,7 @@
 
     std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
         m_data, true /* dataComplete */, ImageDecoder::AlphaPremultiplied,
-        ImageDecoder::ColorSpaceTransformed,
-        ImageDecoder::globalTargetColorSpace());
+        ColorBehavior::transformToGlobalTarget());
     if (decoder) {
       // The |ImageFrame*| is owned by the decoder.
       ImageFrame* imageFrame = decoder->frameBufferAtIndex(0);
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
index 66d2a76a..07dd280f 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.cpp
@@ -28,7 +28,7 @@
   NotificationManager* manager = static_cast<NotificationManager*>(
       Supplement<ExecutionContext>::from(executionContext, supplementName()));
   if (!manager) {
-    manager = new NotificationManager(executionContext);
+    manager = new NotificationManager();
     Supplement<ExecutionContext>::provideTo(*executionContext, supplementName(),
                                             manager);
   }
@@ -41,20 +41,19 @@
   return "NotificationManager";
 }
 
-NotificationManager::NotificationManager(ExecutionContext* executionContext)
-    : ContextLifecycleObserver(executionContext) {}
+NotificationManager::NotificationManager() {}
 
 NotificationManager::~NotificationManager() {}
 
-mojom::blink::PermissionStatus NotificationManager::permissionStatus() {
+mojom::blink::PermissionStatus NotificationManager::permissionStatus(
+    ExecutionContext* executionContext) {
   if (!m_notificationService)
     Platform::current()->interfaceProvider()->getInterface(
         mojo::GetProxy(&m_notificationService));
 
   mojom::blink::PermissionStatus permissionStatus;
   const bool result = m_notificationService->GetPermissionStatus(
-      getExecutionContext()->getSecurityOrigin()->toString(),
-      &permissionStatus);
+      executionContext->getSecurityOrigin()->toString(), &permissionStatus);
   DCHECK(result);
 
   return permissionStatus;
@@ -87,11 +86,6 @@
   return promise;
 }
 
-void NotificationManager::contextDestroyed() {
-  m_notificationService.reset();
-  m_permissionService.reset();
-}
-
 void NotificationManager::onPermissionRequestComplete(
     ScriptPromiseResolver* resolver,
     NotificationPermissionCallback* deprecatedCallback,
@@ -112,7 +106,6 @@
 }
 
 DEFINE_TRACE(NotificationManager) {
-  ContextLifecycleObserver::trace(visitor);
   Supplement<ExecutionContext>::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationManager.h b/third_party/WebKit/Source/modules/notifications/NotificationManager.h
index 6b4042c..37bafc9 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationManager.h
+++ b/third_party/WebKit/Source/modules/notifications/NotificationManager.h
@@ -5,7 +5,6 @@
 #ifndef NotificationManager_h
 #define NotificationManager_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/dom/ExecutionContext.h"
 #include "public/platform/modules/notifications/notification_service.mojom-blink.h"
 #include "public/platform/modules/permissions/permission.mojom-blink.h"
@@ -25,7 +24,6 @@
 // TODO(peter): Make the NotificationManager responsible for resource loading.
 class NotificationManager final
     : public GarbageCollectedFinalized<NotificationManager>,
-      public ContextLifecycleObserver,
       public Supplement<ExecutionContext> {
   USING_GARBAGE_COLLECTED_MIXIN(NotificationManager);
   WTF_MAKE_NONCOPYABLE(NotificationManager);
@@ -38,19 +36,16 @@
 
   // Returns the notification permission status of the current origin. This
   // method is synchronous to support the Notification.permission getter.
-  mojom::blink::PermissionStatus permissionStatus();
+  mojom::blink::PermissionStatus permissionStatus(ExecutionContext*);
 
   ScriptPromise requestPermission(
       ScriptState*,
       NotificationPermissionCallback* deprecatedCallback);
 
-  // ContextLifecycleObserver interface.
-  void contextDestroyed() override;
-
   DECLARE_VIRTUAL_TRACE();
 
  private:
-  explicit NotificationManager(ExecutionContext*);
+  NotificationManager();
 
   void onPermissionRequestComplete(ScriptPromiseResolver*,
                                    NotificationPermissionCallback*,
diff --git a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
index 482eaf42..7b6b993 100644
--- a/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
+++ b/third_party/WebKit/Source/modules/notifications/ServiceWorkerRegistrationNotifications.cpp
@@ -79,7 +79,8 @@
 
   // If permission for notification's origin is not "granted", reject the
   // promise with a TypeError exception, and terminate these substeps.
-  if (NotificationManager::from(executionContext)->permissionStatus() !=
+  if (NotificationManager::from(executionContext)
+          ->permissionStatus(executionContext) !=
       mojom::blink::PermissionStatus::GRANTED)
     return ScriptPromise::reject(
         scriptState,
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppManager.cpp b/third_party/WebKit/Source/modules/payments/PaymentAppManager.cpp
index e066fab7..629cc30 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppManager.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentAppManager.cpp
@@ -84,13 +84,8 @@
 namespace blink {
 
 PaymentAppManager* PaymentAppManager::create(
-    ExecutionContext* executionContext,
     ServiceWorkerRegistration* registration) {
-  return new PaymentAppManager(executionContext, registration);
-}
-
-void PaymentAppManager::contextDestroyed() {
-  m_manager.reset();
+  return new PaymentAppManager(registration);
 }
 
 ScriptPromise PaymentAppManager::setManifest(
@@ -135,12 +130,10 @@
 
 DEFINE_TRACE(PaymentAppManager) {
   visitor->trace(m_registration);
-  ContextLifecycleObserver::trace(visitor);
 }
 
-PaymentAppManager::PaymentAppManager(ExecutionContext* executionContext,
-                                     ServiceWorkerRegistration* registration)
-    : ContextLifecycleObserver(executionContext), m_registration(registration) {
+PaymentAppManager::PaymentAppManager(ServiceWorkerRegistration* registration)
+    : m_registration(registration) {
   DCHECK(registration);
   Platform::current()->interfaceProvider()->getInterface(
       mojo::GetProxy(&m_manager));
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppManager.h b/third_party/WebKit/Source/modules/payments/PaymentAppManager.h
index d311af4..7e9b3d9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppManager.h
+++ b/third_party/WebKit/Source/modules/payments/PaymentAppManager.h
@@ -8,13 +8,11 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "components/payments/payment_app.mojom-blink.h"
-#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
-class ExecutionContext;
 class PaymentAppManifest;
 class ScriptPromiseResolver;
 class ScriptState;
@@ -22,17 +20,12 @@
 
 class MODULES_EXPORT PaymentAppManager final
     : public GarbageCollectedFinalized<PaymentAppManager>,
-      public ScriptWrappable,
-      public ContextLifecycleObserver {
+      public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(PaymentAppManager);
   WTF_MAKE_NONCOPYABLE(PaymentAppManager);
 
  public:
-  static PaymentAppManager* create(ExecutionContext*,
-                                   ServiceWorkerRegistration*);
-
-  void contextDestroyed() override;
+  static PaymentAppManager* create(ServiceWorkerRegistration*);
 
   ScriptPromise setManifest(ScriptState*, const PaymentAppManifest&);
   ScriptPromise getManifest(ScriptState*);
@@ -40,7 +33,7 @@
   DECLARE_TRACE();
 
  private:
-  PaymentAppManager(ExecutionContext*, ServiceWorkerRegistration*);
+  explicit PaymentAppManager(ServiceWorkerRegistration*);
 
   void onSetManifest(ScriptPromiseResolver*,
                      payments::mojom::blink::PaymentAppManifestError);
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAppServiceWorkerRegistration.cpp b/third_party/WebKit/Source/modules/payments/PaymentAppServiceWorkerRegistration.cpp
index 940f5a4a..292b711 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAppServiceWorkerRegistration.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentAppServiceWorkerRegistration.cpp
@@ -40,8 +40,7 @@
 PaymentAppManager* PaymentAppServiceWorkerRegistration::paymentAppManager(
     ScriptState* scriptState) {
   if (!m_paymentAppManager) {
-    m_paymentAppManager = PaymentAppManager::create(
-        scriptState->getExecutionContext(), m_registration);
+    m_paymentAppManager = PaymentAppManager::create(m_registration);
   }
   return m_paymentAppManager.get();
 }
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.idl b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.idl
index 4c2cd0f..802545db 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestEvent.idl
@@ -9,19 +9,5 @@
     Exposed=ServiceWorker
 ] interface PaymentRequestEvent : ExtendableEvent {
     readonly attribute PaymentAppRequestData data;
-    // The payment app api spec says that respondWith() should take a union
-    // type as follows:
-    //  - respondWith((Promise<PaymentResponse> or PaymentResponse));
-    //
-    // But the web-idl spec says that Promise types are not distinguishable
-    // with any other type. Also, if we pass PaymentResponse to respondWith(),
-    // then it will just be cast to a promise immediately via Promise.resolve().
-    //
-    // We found existing case here:
-    //  - https://github.com/w3c/ServiceWorker/issues/724
-    //
-    // So, we should just use Promise<PaymentResponse> instead of union type.
-    // Related spec bug:
-    //  - https://github.com/w3c/webpayments-payment-apps-api/pull/71
     void respondWith(Promise<PaymentResponse> response);
 };
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.cpp b/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.cpp
index 669f976..dc215aa 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.cpp
@@ -13,16 +13,12 @@
 
 PresentationConnectionList::PresentationConnectionList(
     ExecutionContext* context)
-    : ContextLifecycleObserver(context) {}
+    : m_executionContext(context) {}
 
 const AtomicString& PresentationConnectionList::interfaceName() const {
   return EventTargetNames::PresentationConnectionList;
 }
 
-ExecutionContext* PresentationConnectionList::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
-}
-
 const HeapVector<Member<PresentationConnection>>&
 PresentationConnectionList::connections() const {
   return m_connections;
@@ -55,7 +51,7 @@
 
 DEFINE_TRACE(PresentationConnectionList) {
   visitor->trace(m_connections);
-  ContextLifecycleObserver::trace(visitor);
+  visitor->trace(m_executionContext);
   EventTargetWithInlineData::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.h b/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.h
index 44a37c0..7d8f8d78 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnectionList.h
@@ -5,7 +5,6 @@
 #ifndef PresentationConnectionList_h
 #define PresentationConnectionList_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/events/EventTarget.h"
 #include "modules/ModulesExport.h"
 #include "modules/presentation/PresentationConnection.h"
@@ -18,9 +17,7 @@
 // from which represents set of presentation connections in the set of
 // presentation controllers.
 class MODULES_EXPORT PresentationConnectionList final
-    : public EventTargetWithInlineData,
-      public ContextLifecycleObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(PresentationConnectionList);
+    : public EventTargetWithInlineData {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -29,7 +26,9 @@
 
   // EventTarget implementation.
   const AtomicString& interfaceName() const override;
-  ExecutionContext* getExecutionContext() const override;
+  ExecutionContext* getExecutionContext() const override {
+    return m_executionContext;
+  }
 
   // PresentationConnectionList.idl implementation.
   const HeapVector<Member<PresentationConnection>>& connections() const;
@@ -50,6 +49,7 @@
   friend class PresentationReceiverTest;
 
   HeapVector<Member<PresentationConnection>> m_connections;
+  Member<ExecutionContext> m_executionContext;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
index 95db13e0..064031f 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
@@ -47,7 +47,6 @@
 
 RemotePlayback::RemotePlayback(HTMLMediaElement& element)
     : ActiveScriptWrappable(this),
-      ContextLifecycleObserver(&element.document()),
       m_state(element.isPlayingRemotely()
                   ? WebRemotePlaybackState::Connected
                   : WebRemotePlaybackState::Disconnected),
@@ -202,15 +201,13 @@
 }
 
 bool RemotePlayback::hasPendingActivity() const {
+  // TODO(haraken): This check should be moved to ActiveScriptWrappable.
+  if (getExecutionContext()->isContextDestroyed())
+    return false;
   return hasEventListeners() || !m_availabilityCallbacks.isEmpty() ||
          m_promptPromiseResolver;
 }
 
-void RemotePlayback::contextDestroyed() {
-  m_availabilityCallbacks.clear();
-  m_promptPromiseResolver = nullptr;
-}
-
 void RemotePlayback::notifyInitialAvailability(int callbackId) {
   // May not find the callback if the website cancels it fast enough.
   auto iter = m_availabilityCallbacks.find(callbackId);
@@ -310,7 +307,6 @@
   visitor->trace(m_promptPromiseResolver);
   visitor->trace(m_mediaElement);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
 }
 
 DEFINE_TRACE_WRAPPERS(RemotePlayback) {
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
index 0a9e6e8..856ed7da 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.h
@@ -8,7 +8,6 @@
 #include "bindings/core/v8/ActiveScriptWrappable.h"
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/TraceWrapperMember.h"
-#include "core/dom/ContextLifecycleObserver.h"
 #include "core/events/EventTarget.h"
 #include "modules/ModulesExport.h"
 #include "platform/heap/Handle.h"
@@ -30,7 +29,6 @@
 class MODULES_EXPORT RemotePlayback final
     : public EventTargetWithInlineData,
       public ActiveScriptWrappable,
-      public ContextLifecycleObserver,
       NON_EXPORTED_BASE(public WebRemotePlaybackClient) {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(RemotePlayback);
@@ -68,8 +66,6 @@
   // ScriptWrappable implementation.
   bool hasPendingActivity() const final;
 
-  void contextDestroyed() override;
-
   DEFINE_ATTRIBUTE_EVENT_LISTENER(connecting);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(connect);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(disconnect);
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h
index 8e6d683..5802710 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorker.h
@@ -63,7 +63,7 @@
                    PassRefPtr<SerializedScriptValue> message,
                    const MessagePortArray&,
                    ExceptionState&);
-  static bool canTransferArrayBuffer() { return false; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return false; }
 
   String scriptURL() const;
   String state() const;
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h
index d4f99e3..1da5eba 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerClient.h
@@ -43,7 +43,7 @@
                    const MessagePortArray&,
                    ExceptionState&);
 
-  static bool canTransferArrayBuffer() { return false; }
+  static bool canTransferArrayBuffersAndImageBitmaps() { return false; }
 
   DEFINE_INLINE_VIRTUAL_TRACE() {}
 
diff --git a/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.cpp b/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.cpp
index 332362a..e4839a17 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.cpp
@@ -91,7 +91,7 @@
   // waitUntil() isn't called, that means between willDispatchEvent() and
   // didDispatchEvent().
   if (m_type == NotificationClick)
-    getExecutionContext()->allowWindowInteraction();
+    m_executionContext->allowWindowInteraction();
 
   incrementPendingActivity();
 }
@@ -112,7 +112,7 @@
     return;
   }
 
-  if (!getExecutionContext())
+  if (!m_executionContext)
     return;
 
   // When handling a notificationclick event, we want to allow one window to
@@ -133,7 +133,7 @@
 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
                                      EventType type,
                                      int eventID)
-    : ContextLifecycleObserver(context),
+    : m_executionContext(context),
       m_type(type),
       m_eventID(eventID),
       m_consumeWindowInteractionTimer(
@@ -153,11 +153,11 @@
 
 void WaitUntilObserver::decrementPendingActivity() {
   ASSERT(m_pendingActivity > 0);
-  if (!getExecutionContext() || (!m_hasError && --m_pendingActivity))
+  if (!m_executionContext || (!m_hasError && --m_pendingActivity))
     return;
 
   ServiceWorkerGlobalScopeClient* client =
-      ServiceWorkerGlobalScopeClient::from(getExecutionContext());
+      ServiceWorkerGlobalScopeClient::from(m_executionContext);
   WebServiceWorkerEventResult result =
       m_hasError ? WebServiceWorkerEventResultRejected
                  : WebServiceWorkerEventResultCompleted;
@@ -196,17 +196,17 @@
                                            m_eventDispatchTime);
       break;
   }
-  setContext(nullptr);
+  m_executionContext = nullptr;
 }
 
 void WaitUntilObserver::consumeWindowInteraction(TimerBase*) {
-  if (!getExecutionContext())
+  if (!m_executionContext)
     return;
-  getExecutionContext()->consumeWindowInteraction();
+  m_executionContext->consumeWindowInteraction();
 }
 
 DEFINE_TRACE(WaitUntilObserver) {
-  ContextLifecycleObserver::trace(visitor);
+  visitor->trace(m_executionContext);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h b/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h
index 1acc6d8..6d5b415 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h
@@ -5,7 +5,6 @@
 #ifndef WaitUntilObserver_h
 #define WaitUntilObserver_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/ModulesExport.h"
 #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
 #include "platform/Timer.h"
@@ -21,10 +20,7 @@
 
 // Created for each ExtendableEvent instance.
 class MODULES_EXPORT WaitUntilObserver final
-    : public GarbageCollectedFinalized<WaitUntilObserver>,
-      public ContextLifecycleObserver {
-  USING_GARBAGE_COLLECTED_MIXIN(WaitUntilObserver);
-
+    : public GarbageCollectedFinalized<WaitUntilObserver> {
  public:
   enum EventType {
     Activate,
@@ -68,6 +64,7 @@
 
   void consumeWindowInteraction(TimerBase*);
 
+  Member<ExecutionContext> m_executionContext;
   EventType m_type;
   int m_eventID;
   int m_pendingActivity = 0;
diff --git a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
index 969ff22..b76da09 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
@@ -5,9 +5,12 @@
 #include "modules/shapedetection/BarcodeDetector.h"
 
 #include "core/dom/DOMException.h"
+#include "core/dom/DOMRect.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/html/canvas/CanvasImageSource.h"
+#include "modules/shapedetection/DetectedBarcode.h"
+#include "public/platform/InterfaceProvider.h"
 
 namespace blink {
 
@@ -15,17 +18,63 @@
   return new BarcodeDetector(*document.frame());
 }
 
-BarcodeDetector::BarcodeDetector(LocalFrame& frame) : ShapeDetector(frame) {}
+BarcodeDetector::BarcodeDetector(LocalFrame& frame) : ShapeDetector(frame) {
+  frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_barcodeService));
+  m_barcodeService.set_connection_error_handler(convertToBaseCallback(
+      WTF::bind(&BarcodeDetector::onBarcodeServiceConnectionError,
+                wrapWeakPersistent(this))));
+}
 
-ScriptPromise BarcodeDetector::detect(
-    ScriptState* scriptState,
-    const CanvasImageSourceUnion& imageSource) {
-  return detectShapes(scriptState, ShapeDetector::DetectorType::Barcode,
-                      imageSource);
+ScriptPromise BarcodeDetector::doDetect(
+    ScriptPromiseResolver* resolver,
+    mojo::ScopedSharedBufferHandle sharedBufferHandle,
+    int imageWidth,
+    int imageHeight) {
+  ScriptPromise promise = resolver->promise();
+  if (!m_barcodeService) {
+    resolver->reject(DOMException::create(
+        NotSupportedError, "Barcode detection service unavailable."));
+    return promise;
+  }
+  m_barcodeServiceRequests.add(resolver);
+  m_barcodeService->Detect(
+      std::move(sharedBufferHandle), imageWidth, imageHeight,
+      convertToBaseCallback(WTF::bind(&BarcodeDetector::onDetectBarcodes,
+                                      wrapPersistent(this),
+                                      wrapPersistent(resolver))));
+  return promise;
+}
+
+void BarcodeDetector::onDetectBarcodes(
+    ScriptPromiseResolver* resolver,
+    Vector<mojom::blink::BarcodeDetectionResultPtr> barcodeDetectionResults) {
+  DCHECK(m_barcodeServiceRequests.contains(resolver));
+  m_barcodeServiceRequests.remove(resolver);
+
+  HeapVector<Member<DetectedBarcode>> detectedBarcodes;
+  for (const auto& barcode : barcodeDetectionResults) {
+    detectedBarcodes.append(DetectedBarcode::create(
+        barcode->raw_value,
+        DOMRect::create(barcode->bounding_box->x, barcode->bounding_box->y,
+                        barcode->bounding_box->width,
+                        barcode->bounding_box->height)));
+  }
+
+  resolver->resolve(detectedBarcodes);
+}
+
+void BarcodeDetector::onBarcodeServiceConnectionError() {
+  for (const auto& request : m_barcodeServiceRequests) {
+    request->reject(DOMException::create(NotSupportedError,
+                                         "Barcode Detection not implemented."));
+  }
+  m_barcodeServiceRequests.clear();
+  m_barcodeService.reset();
 }
 
 DEFINE_TRACE(BarcodeDetector) {
   ShapeDetector::trace(visitor);
+  visitor->trace(m_barcodeServiceRequests);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
index e876745..126559c2 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.h
@@ -11,6 +11,7 @@
 #include "modules/ModulesExport.h"
 #include "modules/canvas2d/CanvasRenderingContext2D.h"
 #include "modules/shapedetection/ShapeDetector.h"
+#include "public/platform/modules/shapedetection/barcodedetection.mojom-blink.h"
 
 namespace blink {
 
@@ -23,12 +24,23 @@
  public:
   static BarcodeDetector* create(Document&);
 
-  ScriptPromise detect(ScriptState*, const CanvasImageSourceUnion&);
   DECLARE_VIRTUAL_TRACE();
 
  private:
   explicit BarcodeDetector(LocalFrame&);
   ~BarcodeDetector() override = default;
+
+  ScriptPromise doDetect(ScriptPromiseResolver*,
+                         mojo::ScopedSharedBufferHandle,
+                         int imageWidth,
+                         int imageHeight) override;
+  void onDetectBarcodes(ScriptPromiseResolver*,
+                        Vector<mojom::blink::BarcodeDetectionResultPtr>);
+  void onBarcodeServiceConnectionError();
+
+  mojom::blink::BarcodeDetectionPtr m_barcodeService;
+
+  HeapHashSet<Member<ScriptPromiseResolver>> m_barcodeServiceRequests;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
index 4a6c857..fd0952b2 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
@@ -5,9 +5,11 @@
 #include "modules/shapedetection/FaceDetector.h"
 
 #include "core/dom/DOMException.h"
+#include "core/dom/DOMRect.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/html/canvas/CanvasImageSource.h"
+#include "public/platform/InterfaceProvider.h"
 
 namespace blink {
 
@@ -18,16 +20,63 @@
 
 FaceDetector::FaceDetector(LocalFrame& frame,
                            const FaceDetectorOptions& options)
-    : ShapeDetector(frame, options) {}
+    : ShapeDetector(frame),
+      m_faceDetectorOptions(mojom::blink::FaceDetectorOptions::New()) {
+  frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_faceService));
+  m_faceService.set_connection_error_handler(convertToBaseCallback(WTF::bind(
+      &FaceDetector::onFaceServiceConnectionError, wrapWeakPersistent(this))));
+  m_faceDetectorOptions->max_detected_faces = options.maxDetectedFaces();
+  m_faceDetectorOptions->fast_mode = options.fastMode();
+}
 
-ScriptPromise FaceDetector::detect(ScriptState* scriptState,
-                                   const CanvasImageSourceUnion& imageSource) {
-  return detectShapes(scriptState, ShapeDetector::DetectorType::Face,
-                      imageSource);
+ScriptPromise FaceDetector::doDetect(
+    ScriptPromiseResolver* resolver,
+    mojo::ScopedSharedBufferHandle sharedBufferHandle,
+    int imageWidth,
+    int imageHeight) {
+  ScriptPromise promise = resolver->promise();
+  if (!m_faceService) {
+    resolver->reject(DOMException::create(
+        NotSupportedError, "Face detection service unavailable."));
+    return promise;
+  }
+  m_faceServiceRequests.add(resolver);
+  m_faceService->Detect(std::move(sharedBufferHandle), imageWidth, imageHeight,
+                        m_faceDetectorOptions.Clone(),
+                        convertToBaseCallback(WTF::bind(
+                            &FaceDetector::onDetectFaces, wrapPersistent(this),
+                            wrapPersistent(resolver))));
+  return promise;
+}
+
+void FaceDetector::onDetectFaces(
+    ScriptPromiseResolver* resolver,
+    mojom::blink::FaceDetectionResultPtr faceDetectionResult) {
+  DCHECK(m_faceServiceRequests.contains(resolver));
+  m_faceServiceRequests.remove(resolver);
+
+  HeapVector<Member<DOMRect>> detectedFaces;
+  for (const auto& boundingBox : faceDetectionResult->bounding_boxes) {
+    detectedFaces.append(DOMRect::create(boundingBox->x, boundingBox->y,
+                                         boundingBox->width,
+                                         boundingBox->height));
+  }
+
+  resolver->resolve(detectedFaces);
+}
+
+void FaceDetector::onFaceServiceConnectionError() {
+  for (const auto& request : m_faceServiceRequests) {
+    request->reject(DOMException::create(NotSupportedError,
+                                         "Face Detection not implemented."));
+  }
+  m_faceServiceRequests.clear();
+  m_faceService.reset();
 }
 
 DEFINE_TRACE(FaceDetector) {
   ShapeDetector::trace(visitor);
+  visitor->trace(m_faceServiceRequests);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
index 83b6054..5372762 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.h
@@ -10,7 +10,9 @@
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "modules/ModulesExport.h"
 #include "modules/canvas2d/CanvasRenderingContext2D.h"
+#include "modules/shapedetection/FaceDetectorOptions.h"
 #include "modules/shapedetection/ShapeDetector.h"
+#include "public/platform/modules/shapedetection/facedetection.mojom-blink.h"
 
 namespace blink {
 
@@ -23,12 +25,24 @@
  public:
   static FaceDetector* create(Document&, const FaceDetectorOptions&);
 
-  ScriptPromise detect(ScriptState*, const CanvasImageSourceUnion&);
   DECLARE_VIRTUAL_TRACE();
 
  private:
   FaceDetector(LocalFrame&, const FaceDetectorOptions&);
   ~FaceDetector() override = default;
+
+  ScriptPromise doDetect(ScriptPromiseResolver*,
+                         mojo::ScopedSharedBufferHandle,
+                         int imageWidth,
+                         int imageHeight) override;
+  void onDetectFaces(ScriptPromiseResolver*,
+                     mojom::blink::FaceDetectionResultPtr);
+  void onFaceServiceConnectionError();
+
+  mojom::blink::FaceDetectionPtr m_faceService;
+  mojom::blink::FaceDetectorOptionsPtr m_faceDetectorOptions;
+
+  HeapHashSet<Member<ScriptPromiseResolver>> m_faceServiceRequests;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
index abfa2e3d..4da8d7c0 100644
--- a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.cpp
@@ -9,14 +9,11 @@
 #include "core/dom/Document.h"
 #include "core/fetch/ImageResource.h"
 #include "core/frame/ImageBitmap.h"
-#include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/html/HTMLImageElement.h"
 #include "core/html/HTMLVideoElement.h"
 #include "core/html/canvas/CanvasImageSource.h"
-#include "modules/shapedetection/DetectedBarcode.h"
 #include "platform/graphics/Image.h"
-#include "public/platform/InterfaceProvider.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "wtf/CheckedNumeric.h"
@@ -25,63 +22,60 @@
 
 namespace {
 
-static CanvasImageSource* toImageSourceInternal(
-    const CanvasImageSourceUnion& value) {
-  if (value.isHTMLImageElement())
-    return value.getAsHTMLImageElement();
+mojo::ScopedSharedBufferHandle getSharedBufferOnData(
+    ScriptPromiseResolver* resolver,
+    uint8_t* data,
+    int size) {
+  DCHECK(data);
+  DCHECK(size);
+  ScriptPromise promise = resolver->promise();
 
-  if (value.isImageBitmap() &&
-      !static_cast<ImageBitmap*>(value.getAsImageBitmap())->isNeutered()) {
-    return value.getAsImageBitmap();
+  mojo::ScopedSharedBufferHandle sharedBufferHandle =
+      mojo::SharedBufferHandle::Create(size);
+  if (!sharedBufferHandle->is_valid()) {
+    resolver->reject(
+        DOMException::create(InvalidStateError, "Internal allocation error"));
+    return sharedBufferHandle;
   }
 
-  if (value.isHTMLVideoElement())
-    return value.getAsHTMLVideoElement();
+  const mojo::ScopedSharedBufferMapping mappedBuffer =
+      sharedBufferHandle->Map(size);
+  DCHECK(mappedBuffer.get());
+  memcpy(mappedBuffer.get(), data, size);
 
-  return nullptr;
+  return sharedBufferHandle;
 }
 
 }  // anonymous namespace
 
 ShapeDetector::ShapeDetector(LocalFrame& frame) {
-  DCHECK(!m_faceService.is_bound());
-  DCHECK(!m_barcodeService.is_bound());
   DCHECK(frame.interfaceProvider());
-  frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_faceService));
-  frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_barcodeService));
-  m_faceService.set_connection_error_handler(convertToBaseCallback(WTF::bind(
-      &ShapeDetector::onFaceServiceConnectionError, wrapWeakPersistent(this))));
-  m_barcodeService.set_connection_error_handler(convertToBaseCallback(
-      WTF::bind(&ShapeDetector::onBarcodeServiceConnectionError,
-                wrapWeakPersistent(this))));
 }
 
-ShapeDetector::ShapeDetector(LocalFrame& frame,
-                             const FaceDetectorOptions& options)
-    : ShapeDetector(frame) {
-  m_faceDetectorOptions = mojom::blink::FaceDetectorOptions::New();
-  m_faceDetectorOptions->max_detected_faces = options.maxDetectedFaces();
-  m_faceDetectorOptions->fast_mode = options.fastMode();
-}
-
-ScriptPromise ShapeDetector::detectShapes(
-    ScriptState* scriptState,
-    DetectorType detectorType,
-    const CanvasImageSourceUnion& imageSource) {
-  CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
-
+ScriptPromise ShapeDetector::detect(ScriptState* scriptState,
+                                    const CanvasImageSourceUnion& imageSource) {
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
   ScriptPromise promise = resolver->promise();
 
-  if (!imageSourceInternal) {
-    // TODO(mcasas): Implement more CanvasImageSources, https://crbug.com/659138
-    NOTIMPLEMENTED() << "Unsupported CanvasImageSource";
+  CanvasImageSource* canvasImageSource;
+  if (imageSource.isHTMLImageElement()) {
+    canvasImageSource = imageSource.getAsHTMLImageElement();
+  } else if (imageSource.isImageBitmap()) {
+    canvasImageSource = imageSource.getAsImageBitmap();
+  } else if (imageSource.isHTMLVideoElement()) {
+    canvasImageSource = imageSource.getAsHTMLVideoElement();
+  } else if (imageSource.isHTMLCanvasElement()) {
+    canvasImageSource = imageSource.getAsHTMLCanvasElement();
+  } else if (imageSource.isOffscreenCanvas()) {
+    canvasImageSource = imageSource.getAsOffscreenCanvas();
+  } else {
+    NOTREACHED() << "Unsupported CanvasImageSource";
     resolver->reject(
-        DOMException::create(NotFoundError, "Unsupported source."));
+        DOMException::create(NotSupportedError, "Unsupported source."));
     return promise;
   }
 
-  if (imageSourceInternal->wouldTaintOrigin(
+  if (canvasImageSource->wouldTaintOrigin(
           scriptState->getExecutionContext()->getSecurityOrigin())) {
     resolver->reject(
         DOMException::create(SecurityError, "Source would taint origin."));
@@ -89,29 +83,67 @@
   }
 
   if (imageSource.isHTMLImageElement()) {
-    return detectShapesOnImageElement(
-        detectorType, resolver,
-        static_cast<HTMLImageElement*>(imageSourceInternal));
-  }
-  if (imageSourceInternal->isImageBitmap()) {
-    return detectShapesOnImageBitmap(
-        detectorType, resolver, static_cast<ImageBitmap*>(imageSourceInternal));
-  }
-  if (imageSourceInternal->isVideoElement()) {
-    return detectShapesOnVideoElement(
-        detectorType, resolver,
-        static_cast<HTMLVideoElement*>(imageSourceInternal));
+    return detectShapesOnImageElement(resolver,
+                                      imageSource.getAsHTMLImageElement());
   }
 
-  NOTREACHED();
-  return promise;
+  // TODO(mcasas): Check if |video| is actually playing a MediaStream by using
+  // HTMLMediaElement::isMediaStreamURL(video->currentSrc().getString()); if
+  // there is a local WebCam associated, there might be sophisticated ways to
+  // detect faces on it. Until then, treat as a normal <video> element.
+
+  const FloatSize size(canvasImageSource->sourceWidth(),
+                       canvasImageSource->sourceHeight());
+
+  SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
+  RefPtr<Image> image = canvasImageSource->getSourceImageForCanvas(
+      &sourceImageStatus, PreferNoAcceleration, SnapshotReasonDrawImage, size);
+  if (!image || sourceImageStatus != NormalSourceImageStatus) {
+    resolver->reject(
+        DOMException::create(InvalidStateError, "Invalid element or state."));
+    return promise;
+  }
+
+  SkPixmap pixmap;
+  RefPtr<Uint8Array> pixelData;
+  uint8_t* pixelDataPtr = nullptr;
+  WTF::CheckedNumeric<int> allocationSize = 0;
+
+  sk_sp<SkImage> skImage = image->imageForCurrentFrame();
+  // Use |skImage|'s pixels if it has direct access to them.
+  if (skImage->peekPixels(&pixmap)) {
+    pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
+    allocationSize = pixmap.getSafeSize();
+  } else if (imageSource.isImageBitmap()) {
+    ImageBitmap* imageBitmap = imageSource.getAsImageBitmap();
+    pixelData = imageBitmap->copyBitmapData(imageBitmap->isPremultiplied()
+                                                ? PremultiplyAlpha
+                                                : DontPremultiplyAlpha,
+                                            N32ColorType);
+    pixelDataPtr = pixelData->data();
+    allocationSize = imageBitmap->size().area() * 4 /* bytes per pixel */;
+  } else {
+    // TODO(mcasas): retrieve the pixels from elsewhere.
+    NOTREACHED();
+    resolver->reject(DOMException::create(
+        InvalidStateError, "Failed to get pixels for current frame."));
+    return promise;
+  }
+
+  mojo::ScopedSharedBufferHandle sharedBufferHandle = getSharedBufferOnData(
+      resolver, pixelDataPtr, allocationSize.ValueOrDefault(0));
+  if (!sharedBufferHandle->is_valid())
+    return promise;
+
+  return doDetect(resolver, std::move(sharedBufferHandle), image->width(),
+                  image->height());
 }
 
 ScriptPromise ShapeDetector::detectShapesOnImageElement(
-    DetectorType detectorType,
     ScriptPromiseResolver* resolver,
     const HTMLImageElement* img) {
   ScriptPromise promise = resolver->promise();
+
   if (img->bitmapSourceSize().isZero()) {
     resolver->resolve(HeapVector<Member<DOMRect>>());
     return promise;
@@ -170,236 +202,8 @@
     return promise;
   }
 
-  if (detectorType == DetectorType::Face) {
-    if (!m_faceService) {
-      resolver->reject(DOMException::create(
-          NotSupportedError, "Face detection service unavailable."));
-      return promise;
-    }
-    m_faceServiceRequests.add(resolver);
-    m_faceService->Detect(std::move(sharedBufferHandle), img->naturalWidth(),
-                          img->naturalHeight(), m_faceDetectorOptions.Clone(),
-                          convertToBaseCallback(WTF::bind(
-                              &ShapeDetector::onDetectFaces,
-                              wrapPersistent(this), wrapPersistent(resolver))));
-  } else if (detectorType == DetectorType::Barcode) {
-    if (!m_barcodeService) {
-      resolver->reject(DOMException::create(
-          NotSupportedError, "Barcode detection service unavailable."));
-      return promise;
-    }
-    m_barcodeServiceRequests.add(resolver);
-    m_barcodeService->Detect(
-        std::move(sharedBufferHandle), img->naturalWidth(),
-        img->naturalHeight(),
-        convertToBaseCallback(WTF::bind(&ShapeDetector::onDetectBarcodes,
-                                        wrapPersistent(this),
-                                        wrapPersistent(resolver))));
-  } else {
-    NOTREACHED() << "Unsupported detector type";
-  }
-
-  return promise;
-}
-
-ScriptPromise ShapeDetector::detectShapesOnImageBitmap(
-    DetectorType detectorType,
-    ScriptPromiseResolver* resolver,
-    ImageBitmap* imageBitmap) {
-  ScriptPromise promise = resolver->promise();
-  if (!imageBitmap->originClean()) {
-    resolver->reject(
-        DOMException::create(SecurityError, "ImageBitmap is not origin clean"));
-    return promise;
-  }
-
-  if (imageBitmap->size().area() == 0) {
-    resolver->resolve(HeapVector<Member<DOMRect>>());
-    return promise;
-  }
-
-  SkPixmap pixmap;
-  RefPtr<Uint8Array> pixelData;
-  uint8_t* pixelDataPtr = nullptr;
-  WTF::CheckedNumeric<int> allocationSize = 0;
-  // Use |skImage|'s pixels if it has direct access to them, otherwise retrieve
-  // them from elsewhere via copyBitmapData().
-  sk_sp<SkImage> skImage = imageBitmap->bitmapImage()->imageForCurrentFrame();
-  if (skImage->peekPixels(&pixmap)) {
-    pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
-    allocationSize = pixmap.getSafeSize();
-  } else {
-    pixelData = imageBitmap->copyBitmapData(imageBitmap->isPremultiplied()
-                                                ? PremultiplyAlpha
-                                                : DontPremultiplyAlpha,
-                                            N32ColorType);
-    pixelDataPtr = pixelData->data();
-    allocationSize = imageBitmap->size().area() * 4 /* bytes per pixel */;
-  }
-
-  return detectShapesOnData(detectorType, resolver, pixelDataPtr,
-                            allocationSize.ValueOrDefault(0),
-                            imageBitmap->width(), imageBitmap->height());
-}
-
-ScriptPromise ShapeDetector::detectShapesOnVideoElement(
-    DetectorType detectorType,
-    ScriptPromiseResolver* resolver,
-    const HTMLVideoElement* video) {
-  ScriptPromise promise = resolver->promise();
-
-  // TODO(mcasas): Check if |video| is actually playing a MediaStream by using
-  // HTMLMediaElement::isMediaStreamURL(video->currentSrc().getString()); if
-  // there is a local WebCam associated, there might be sophisticated ways to
-  // detect faces on it. Until then, treat as a normal <video> element.
-
-  // !hasAvailableVideoFrame() is a bundle of invalid states.
-  if (!video->hasAvailableVideoFrame()) {
-    resolver->reject(DOMException::create(
-        InvalidStateError, "Invalid HTMLVideoElement or state."));
-    return promise;
-  }
-
-  const FloatSize videoSize(video->videoWidth(), video->videoHeight());
-  SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
-  RefPtr<Image> image =
-      video->getSourceImageForCanvas(&sourceImageStatus, PreferNoAcceleration,
-                                     SnapshotReasonDrawImage, videoSize);
-
-  DCHECK_EQ(NormalSourceImageStatus, sourceImageStatus);
-
-  SkPixmap pixmap;
-  RefPtr<Uint8Array> pixelData;
-  uint8_t* pixelDataPtr = nullptr;
-  WTF::CheckedNumeric<int> allocationSize = 0;
-  // Use |skImage|'s pixels if it has direct access to them.
-  sk_sp<SkImage> skImage = image->imageForCurrentFrame();
-  if (skImage->peekPixels(&pixmap)) {
-    pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
-    allocationSize = pixmap.getSafeSize();
-  } else {
-    // TODO(mcasas): retrieve the pixels from elsewhere.
-    NOTREACHED();
-    resolver->reject(DOMException::create(
-        InvalidStateError, "Failed to get pixels for current frame."));
-    return promise;
-  }
-
-  return detectShapesOnData(detectorType, resolver, pixelDataPtr,
-                            allocationSize.ValueOrDefault(0), image->width(),
-                            image->height());
-}
-
-ScriptPromise ShapeDetector::detectShapesOnData(DetectorType detectorType,
-                                                ScriptPromiseResolver* resolver,
-                                                uint8_t* data,
-                                                int size,
-                                                int width,
-                                                int height) {
-  DCHECK(data);
-  DCHECK(size);
-  ScriptPromise promise = resolver->promise();
-
-  mojo::ScopedSharedBufferHandle sharedBufferHandle =
-      mojo::SharedBufferHandle::Create(size);
-  if (!sharedBufferHandle->is_valid()) {
-    resolver->reject(
-        DOMException::create(InvalidStateError, "Internal allocation error"));
-    return promise;
-  }
-
-  const mojo::ScopedSharedBufferMapping mappedBuffer =
-      sharedBufferHandle->Map(size);
-  DCHECK(mappedBuffer.get());
-
-  memcpy(mappedBuffer.get(), data, size);
-
-  if (detectorType == DetectorType::Face) {
-    if (!m_faceService) {
-      resolver->reject(DOMException::create(
-          NotSupportedError, "Face detection service unavailable."));
-      return promise;
-    }
-    m_faceServiceRequests.add(resolver);
-    m_faceService->Detect(std::move(sharedBufferHandle), width, height,
-                          m_faceDetectorOptions.Clone(),
-                          convertToBaseCallback(WTF::bind(
-                              &ShapeDetector::onDetectFaces,
-                              wrapPersistent(this), wrapPersistent(resolver))));
-  } else if (detectorType == DetectorType::Barcode) {
-    if (!m_barcodeService) {
-      resolver->reject(DOMException::create(
-          NotSupportedError, "Barcode detection service unavailable."));
-      return promise;
-    }
-    m_barcodeServiceRequests.add(resolver);
-    m_barcodeService->Detect(
-        std::move(sharedBufferHandle), width, height,
-        convertToBaseCallback(WTF::bind(&ShapeDetector::onDetectBarcodes,
-                                        wrapPersistent(this),
-                                        wrapPersistent(resolver))));
-  } else {
-    NOTREACHED() << "Unsupported detector type";
-  }
-  sharedBufferHandle.reset();
-  return promise;
-}
-
-void ShapeDetector::onDetectFaces(
-    ScriptPromiseResolver* resolver,
-    mojom::blink::FaceDetectionResultPtr faceDetectionResult) {
-  DCHECK(m_faceServiceRequests.contains(resolver));
-  m_faceServiceRequests.remove(resolver);
-
-  HeapVector<Member<DOMRect>> detectedFaces;
-  for (const auto& boundingBox : faceDetectionResult->bounding_boxes) {
-    detectedFaces.append(DOMRect::create(boundingBox->x, boundingBox->y,
-                                         boundingBox->width,
-                                         boundingBox->height));
-  }
-
-  resolver->resolve(detectedFaces);
-}
-
-void ShapeDetector::onDetectBarcodes(
-    ScriptPromiseResolver* resolver,
-    Vector<mojom::blink::BarcodeDetectionResultPtr> barcodeDetectionResults) {
-  DCHECK(m_barcodeServiceRequests.contains(resolver));
-  m_barcodeServiceRequests.remove(resolver);
-
-  HeapVector<Member<DetectedBarcode>> detectedBarcodes;
-  for (const auto& barcode : barcodeDetectionResults) {
-    detectedBarcodes.append(DetectedBarcode::create(
-        barcode->raw_value,
-        DOMRect::create(barcode->bounding_box->x, barcode->bounding_box->y,
-                        barcode->bounding_box->width,
-                        barcode->bounding_box->height)));
-  }
-
-  resolver->resolve(detectedBarcodes);
-}
-
-void ShapeDetector::onFaceServiceConnectionError() {
-  for (const auto& request : m_faceServiceRequests) {
-    request->reject(DOMException::create(NotSupportedError,
-                                         "Face Detection not implemented."));
-  }
-  m_faceServiceRequests.clear();
-  m_faceService.reset();
-}
-
-void ShapeDetector::onBarcodeServiceConnectionError() {
-  for (const auto& request : m_barcodeServiceRequests) {
-    request->reject(DOMException::create(NotSupportedError,
-                                         "Barcode Detection not implemented."));
-  }
-  m_barcodeServiceRequests.clear();
-  m_barcodeService.reset();
-}
-
-DEFINE_TRACE(ShapeDetector) {
-  visitor->trace(m_faceServiceRequests);
-  visitor->trace(m_barcodeServiceRequests);
+  return doDetect(resolver, std::move(sharedBufferHandle), img->naturalWidth(),
+                  img->naturalHeight());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
index 744007a..3a1cc10 100644
--- a/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
+++ b/third_party/WebKit/Source/modules/shapedetection/ShapeDetector.h
@@ -9,9 +9,6 @@
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "modules/ModulesExport.h"
 #include "modules/canvas2d/CanvasRenderingContext2D.h"
-#include "modules/shapedetection/FaceDetectorOptions.h"
-#include "public/platform/modules/shapedetection/barcodedetection.mojom-blink.h"
-#include "public/platform/modules/shapedetection/facedetection.mojom-blink.h"
 
 namespace blink {
 
@@ -20,55 +17,22 @@
 class MODULES_EXPORT ShapeDetector
     : public GarbageCollectedFinalized<ShapeDetector> {
  public:
-  enum class DetectorType {
-    Face,
-    Barcode
-    // TODO(mcasas): Implement TextDetector after
-    // https://github.com/WICG/shape-detection-api/issues/6
-  };
+  // TODO(mcasas): Implement TextDetector after
+  // https://github.com/WICG/shape-detection-api/issues/6
   explicit ShapeDetector(LocalFrame&);
-  ShapeDetector(LocalFrame&, const FaceDetectorOptions&);
   virtual ~ShapeDetector() = default;
 
-  ScriptPromise detectShapes(ScriptState*,
-                             DetectorType,
-                             const CanvasImageSourceUnion&);
-  DECLARE_VIRTUAL_TRACE();
+  ScriptPromise detect(ScriptState*, const CanvasImageSourceUnion&);
+  DEFINE_INLINE_VIRTUAL_TRACE() {}
 
  private:
-  ScriptPromise detectShapesOnImageElement(DetectorType,
-                                           ScriptPromiseResolver*,
+  ScriptPromise detectShapesOnImageElement(ScriptPromiseResolver*,
                                            const HTMLImageElement*);
-  ScriptPromise detectShapesOnImageBitmap(DetectorType,
-                                          ScriptPromiseResolver*,
-                                          ImageBitmap*);
-  ScriptPromise detectShapesOnVideoElement(DetectorType,
-                                           ScriptPromiseResolver*,
-                                           const HTMLVideoElement*);
 
-  ScriptPromise detectShapesOnData(DetectorType,
-                                   ScriptPromiseResolver*,
-                                   uint8_t* data,
-                                   int size,
-                                   int width,
-                                   int height);
-  void onDetectFaces(ScriptPromiseResolver*,
-                     mojom::blink::FaceDetectionResultPtr);
-  void onDetectBarcodes(ScriptPromiseResolver*,
-                        Vector<mojom::blink::BarcodeDetectionResultPtr>);
-
-  // Error handlers for use if mojo service doesn't connect.
-  void onFaceServiceConnectionError();
-  void onBarcodeServiceConnectionError();
-
-  mojom::blink::FaceDetectionPtr m_faceService;
-  mojom::blink::BarcodeDetectionPtr m_barcodeService;
-
-  HeapHashSet<Member<ScriptPromiseResolver>> m_faceServiceRequests;
-  HeapHashSet<Member<ScriptPromiseResolver>> m_barcodeServiceRequests;
-
- protected:
-  mojom::blink::FaceDetectorOptionsPtr m_faceDetectorOptions;
+  virtual ScriptPromise doDetect(ScriptPromiseResolver*,
+                                 mojo::ScopedSharedBufferHandle,
+                                 int imageWidth,
+                                 int imageHeight) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
index de3c677f..89d723d 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.cpp
@@ -37,7 +37,7 @@
 }
 
 SpeechSynthesis::SpeechSynthesis(ExecutionContext* context)
-    : ContextLifecycleObserver(context),
+    : m_executionContext(context),
       m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this)),
       m_isPaused(false) {}
 
@@ -46,13 +46,9 @@
   m_platformSpeechSynthesizer = synthesizer;
 }
 
-ExecutionContext* SpeechSynthesis::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
-}
-
 void SpeechSynthesis::voicesDidChange() {
   m_voiceList.clear();
-  if (getExecutionContext() && !getExecutionContext()->isContextDestroyed())
+  if (!m_executionContext->isContextDestroyed())
     dispatchEvent(Event::create(EventTypeNames::voiceschanged));
 }
 
@@ -130,7 +126,7 @@
                                 SpeechSynthesisUtterance* utterance,
                                 unsigned long charIndex,
                                 const String& name) {
-  if (getExecutionContext() && !getExecutionContext()->isContextDestroyed()) {
+  if (!m_executionContext->isContextDestroyed()) {
     double elapsedTimeMillis =
         (monotonicallyIncreasingTime() - utterance->startTime()) * 1000.0;
     utterance->dispatchEvent(SpeechSynthesisEvent::create(
@@ -238,12 +234,12 @@
 }
 
 DEFINE_TRACE(SpeechSynthesis) {
+  visitor->trace(m_executionContext);
   visitor->trace(m_platformSpeechSynthesizer);
   visitor->trace(m_voiceList);
   visitor->trace(m_utteranceQueue);
   PlatformSpeechSynthesizerClient::trace(visitor);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
index 3094a4cf..6363225 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesis.h
@@ -26,7 +26,6 @@
 #ifndef SpeechSynthesis_h
 #define SpeechSynthesis_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/EventTargetModules.h"
 #include "modules/ModulesExport.h"
 #include "modules/speech/SpeechSynthesisUtterance.h"
@@ -41,8 +40,7 @@
 
 class MODULES_EXPORT SpeechSynthesis final
     : public EventTargetWithInlineData,
-      public PlatformSpeechSynthesizerClient,
-      public ContextLifecycleObserver {
+      public PlatformSpeechSynthesizerClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesis);
 
@@ -65,7 +63,9 @@
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(voiceschanged);
 
-  ExecutionContext* getExecutionContext() const override;
+  ExecutionContext* getExecutionContext() const override {
+    return m_executionContext;
+  }
 
   DECLARE_VIRTUAL_TRACE();
 
@@ -93,6 +93,7 @@
   // Returns the utterance at the front of the queue.
   SpeechSynthesisUtterance* currentSpeechUtterance() const;
 
+  Member<ExecutionContext> m_executionContext;
   Member<PlatformSpeechSynthesizer> m_platformSpeechSynthesizer;
   HeapVector<Member<SpeechSynthesisVoice>> m_voiceList;
   HeapDeque<Member<SpeechSynthesisUtterance>> m_utteranceQueue;
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
index 14ec0006..68a8339 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.cpp
@@ -25,6 +25,8 @@
 
 #include "modules/speech/SpeechSynthesisUtterance.h"
 
+#include "core/dom/ExecutionContext.h"
+
 namespace blink {
 
 SpeechSynthesisUtterance* SpeechSynthesisUtterance::create(
@@ -35,7 +37,7 @@
 
 SpeechSynthesisUtterance::SpeechSynthesisUtterance(ExecutionContext* context,
                                                    const String& text)
-    : ContextLifecycleObserver(context),
+    : m_executionContext(context),
       m_platformUtterance(PlatformSpeechSynthesisUtterance::create(this)) {
   m_platformUtterance->setText(text);
 }
@@ -43,7 +45,7 @@
 SpeechSynthesisUtterance::~SpeechSynthesisUtterance() {}
 
 ExecutionContext* SpeechSynthesisUtterance::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
+  return m_executionContext;
 }
 
 const AtomicString& SpeechSynthesisUtterance::interfaceName() const {
@@ -65,10 +67,10 @@
 }
 
 DEFINE_TRACE(SpeechSynthesisUtterance) {
+  visitor->trace(m_executionContext);
   visitor->trace(m_platformUtterance);
   visitor->trace(m_voice);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
index 39f4b23..bad1e60 100644
--- a/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
+++ b/third_party/WebKit/Source/modules/speech/SpeechSynthesisUtterance.h
@@ -26,7 +26,6 @@
 #ifndef SpeechSynthesisUtterance_h
 #define SpeechSynthesisUtterance_h
 
-#include "core/dom/ContextLifecycleObserver.h"
 #include "modules/EventTargetModules.h"
 #include "modules/speech/SpeechSynthesisVoice.h"
 #include "platform/heap/Handle.h"
@@ -36,8 +35,7 @@
 
 class SpeechSynthesisUtterance final
     : public EventTargetWithInlineData,
-      public PlatformSpeechSynthesisUtteranceClient,
-      public ContextLifecycleObserver {
+      public PlatformSpeechSynthesisUtteranceClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(SpeechSynthesisUtterance);
 
@@ -91,6 +89,7 @@
   // EventTarget
   const AtomicString& interfaceName() const override;
 
+  Member<ExecutionContext> m_executionContext;
   Member<PlatformSpeechSynthesisUtterance> m_platformUtterance;
   Member<SpeechSynthesisVoice> m_voice;
 };
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index c87e0694..3884e13 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -14,6 +14,7 @@
 #include "core/inspector/ConsoleMessage.h"
 #include "core/loader/DocumentLoader.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "modules/EventTargetModules.h"
 #include "modules/vr/NavigatorVR.h"
 #include "modules/vr/VRController.h"
 #include "modules/vr/VRDisplayCapabilities.h"
@@ -78,7 +79,9 @@
 VRDisplay::VRDisplay(NavigatorVR* navigatorVR,
                      device::mojom::blink::VRDisplayPtr display,
                      device::mojom::blink::VRDisplayClientRequest request)
-    : m_navigatorVR(navigatorVR),
+    : ActiveScriptWrappable(this),
+      ContextLifecycleObserver(navigatorVR->document()),
+      m_navigatorVR(navigatorVR),
       m_isConnected(false),
       m_isPresenting(false),
       m_isValidDeviceForPresenting(true),
@@ -206,7 +209,7 @@
 }
 
 int VRDisplay::requestAnimationFrame(FrameRequestCallback* callback) {
-  Document* doc = m_navigatorVR->document();
+  Document* doc = this->document();
   if (!doc)
     return 0;
 
@@ -240,7 +243,7 @@
   // frames should be tied to the presenting VR display (e.g. should be serviced
   // by GVR library callbacks on Android), and not the doc frame rate.
   if (!m_animationCallbackRequested) {
-    Document* doc = m_navigatorVR->document();
+    Document* doc = this->document();
     if (!doc)
       return;
     doc->requestAnimationFrame(new VRDisplayFrameRequestCallback(this));
@@ -436,7 +439,7 @@
 }
 
 void VRDisplay::beginPresent() {
-  Document* doc = m_navigatorVR->document();
+  Document* doc = this->document();
   std::unique_ptr<UserGestureIndicator> gestureIndicator;
   if (m_capabilities->hasExternalDisplay()) {
     forceExitPresent();
@@ -591,7 +594,7 @@
   if (!m_display)
     return;
 
-  Document* doc = m_navigatorVR->document();
+  Document* doc = this->document();
   if (!m_isPresenting) {
     if (doc) {
       doc->addConsoleMessage(ConsoleMessage::create(
@@ -718,7 +721,7 @@
     }
     m_reenteredFullscreen = true;
     auto canvas = m_layer.source();
-    Document* doc = m_navigatorVR->document();
+    Document* doc = this->document();
     std::unique_ptr<UserGestureIndicator> gestureIndicator;
     if (doc) {
       gestureIndicator =
@@ -741,7 +744,27 @@
   m_binding.Close();
 }
 
+ExecutionContext* VRDisplay::getExecutionContext() const {
+  return ContextLifecycleObserver::getExecutionContext();
+}
+
+const AtomicString& VRDisplay::interfaceName() const {
+  return EventTargetNames::VRDisplay;
+}
+
+void VRDisplay::contextDestroyed() {
+  forceExitPresent();
+}
+
+bool VRDisplay::hasPendingActivity() const {
+  // Prevent V8 from garbage collecting the wrapper object if there are
+  // event listeners attached to it.
+  return getExecutionContext() && hasEventListeners();
+}
+
 DEFINE_TRACE(VRDisplay) {
+  EventTargetWithInlineData::trace(visitor);
+  ContextLifecycleObserver::trace(visitor);
   visitor->trace(m_navigatorVR);
   visitor->trace(m_capabilities);
   visitor->trace(m_stageParameters);
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.h b/third_party/WebKit/Source/modules/vr/VRDisplay.h
index 45fa373..6f903bf0 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.h
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.h
@@ -5,9 +5,9 @@
 #ifndef VRDisplay_h
 #define VRDisplay_h
 
-#include "bindings/core/v8/ScriptWrappable.h"
 #include "core/dom/Document.h"
 #include "core/dom/FrameRequestCallback.h"
+#include "core/events/EventTarget.h"
 #include "device/vr/vr_service.mojom-blink.h"
 #include "modules/vr/VRDisplayCapabilities.h"
 #include "modules/vr/VRLayer.h"
@@ -38,10 +38,12 @@
 
 enum VREye { VREyeNone, VREyeLeft, VREyeRight };
 
-class VRDisplay final : public GarbageCollectedFinalized<VRDisplay>,
-                        public device::mojom::blink::VRDisplayClient,
-                        public ScriptWrappable {
+class VRDisplay final : public EventTargetWithInlineData,
+                        public ActiveScriptWrappable,
+                        public ContextLifecycleObserver,
+                        public device::mojom::blink::VRDisplayClient {
   DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(VRDisplay);
   USING_PRE_FINALIZER(VRDisplay, dispose);
 
  public:
@@ -81,6 +83,16 @@
 
   Document* document();
 
+  // EventTarget overrides:
+  ExecutionContext* getExecutionContext() const override;
+  const AtomicString& interfaceName() const override;
+
+  // ContextLifecycleObserver implementation.
+  void contextDestroyed() override;
+
+  // ScriptWrappable implementation.
+  bool hasPendingActivity() const final;
+
   DECLARE_VIRTUAL_TRACE();
 
  protected:
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.idl b/third_party/WebKit/Source/modules/vr/VRDisplay.idl
index 7f38e2f..2a00e744 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.idl
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.idl
@@ -9,8 +9,10 @@
 
 // https://w3c.github.io/webvr/#interface-vrdisplay
 [
+    ActiveScriptWrappable,
+    DependentLifetime,
     OriginTrialEnabled=WebVR
-] interface VRDisplay {
+] interface VRDisplay : EventTarget {
     // An identifier for this device unique across VRDisplays.
     readonly attribute unsigned long displayId;
 
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.cpp b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.cpp
index dd9562c6..326c156b 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "modules/webaudio/AudioProcessingEvent.h"
+#include "modules/webaudio/AudioProcessingEventInit.h"
 
 namespace blink {
 
@@ -37,6 +38,12 @@
   return new AudioProcessingEvent(inputBuffer, outputBuffer, playbackTime);
 }
 
+AudioProcessingEvent* AudioProcessingEvent::create(
+    const AtomicString& type,
+    const AudioProcessingEventInit& initializer) {
+  return new AudioProcessingEvent(type, initializer);
+}
+
 AudioProcessingEvent::AudioProcessingEvent() {}
 
 AudioProcessingEvent::AudioProcessingEvent(AudioBuffer* inputBuffer,
@@ -47,6 +54,15 @@
       m_outputBuffer(outputBuffer),
       m_playbackTime(playbackTime) {}
 
+AudioProcessingEvent::AudioProcessingEvent(
+    const AtomicString& type,
+    const AudioProcessingEventInit& initializer)
+    : Event(type, initializer) {
+  m_inputBuffer = initializer.inputBuffer();
+  m_outputBuffer = initializer.outputBuffer();
+  m_playbackTime = initializer.playbackTime();
+}
+
 AudioProcessingEvent::~AudioProcessingEvent() {}
 
 const AtomicString& AudioProcessingEvent::interfaceName() const {
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.h b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.h
index a85b8de..d8407ec8 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.h
@@ -28,12 +28,14 @@
 
 #include "modules/EventModules.h"
 #include "modules/webaudio/AudioBuffer.h"
+#include "modules/webaudio/AudioProcessingEventInit.h"
 #include "wtf/PassRefPtr.h"
 #include "wtf/RefPtr.h"
 
 namespace blink {
 
 class AudioBuffer;
+class AudioProcessingEventInit;
 
 class AudioProcessingEvent final : public Event {
   DEFINE_WRAPPERTYPEINFO();
@@ -44,6 +46,9 @@
                                       AudioBuffer* outputBuffer,
                                       double playbackTime);
 
+  static AudioProcessingEvent* create(const AtomicString& type,
+                                      const AudioProcessingEventInit&);
+
   ~AudioProcessingEvent() override;
 
   AudioBuffer* inputBuffer() { return m_inputBuffer.get(); }
@@ -59,6 +64,8 @@
   AudioProcessingEvent(AudioBuffer* inputBuffer,
                        AudioBuffer* outputBuffer,
                        double playbackTime);
+  AudioProcessingEvent(const AtomicString& type,
+                       const AudioProcessingEventInit&);
 
   Member<AudioBuffer> m_inputBuffer;
   Member<AudioBuffer> m_outputBuffer;
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.idl b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.idl
index 41fc880ff..ad82edd 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.idl
+++ b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEvent.idl
@@ -24,6 +24,9 @@
  */
 
 // See https://webaudio.github.io/web-audio-api/#the-audioprocessingevent-interface---deprecated
+[
+    Constructor(DOMString type, AudioProcessingEventInit eventInitDict)
+]
 interface AudioProcessingEvent : Event {
     readonly attribute double playbackTime;
     readonly attribute AudioBuffer inputBuffer;
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioProcessingEventInit.idl b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEventInit.idl
new file mode 100644
index 0000000..eda9d939
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/AudioProcessingEventInit.idl
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// See https://webaudio.github.io/web-audio-api/#audioprocessingeventinit
+dictionary AudioProcessingEventInit : EventInit {
+    required double playbackTime;
+    required AudioBuffer inputBuffer;
+    required AudioBuffer outputBuffer;
+};
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.cpp b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.cpp
index 951eea3..2742ffd 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.cpp
@@ -36,6 +36,12 @@
   return new OfflineAudioCompletionEvent(renderedBuffer);
 }
 
+OfflineAudioCompletionEvent* OfflineAudioCompletionEvent::create(
+    const AtomicString& eventType,
+    const OfflineAudioCompletionEventInit& eventInit) {
+  return new OfflineAudioCompletionEvent(eventType, eventInit);
+}
+
 OfflineAudioCompletionEvent::OfflineAudioCompletionEvent() {}
 
 OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(
@@ -43,6 +49,13 @@
     : Event(EventTypeNames::complete, true, false),
       m_renderedBuffer(renderedBuffer) {}
 
+OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(
+    const AtomicString& eventType,
+    const OfflineAudioCompletionEventInit& eventInit)
+    : Event(eventType, eventInit) {
+  m_renderedBuffer = eventInit.renderedBuffer();
+}
+
 OfflineAudioCompletionEvent::~OfflineAudioCompletionEvent() {}
 
 const AtomicString& OfflineAudioCompletionEvent::interfaceName() const {
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.h b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.h
index 8353caef..7722d430 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.h
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.h
@@ -28,12 +28,14 @@
 
 #include "modules/EventModules.h"
 #include "modules/webaudio/AudioBuffer.h"
+#include "modules/webaudio/OfflineAudioCompletionEventInit.h"
 #include "wtf/PassRefPtr.h"
 #include "wtf/RefPtr.h"
 
 namespace blink {
 
 class AudioBuffer;
+class OfflineAudioCompletionEventInit;
 
 class OfflineAudioCompletionEvent final : public Event {
   DEFINE_WRAPPERTYPEINFO();
@@ -41,6 +43,9 @@
  public:
   static OfflineAudioCompletionEvent* create();
   static OfflineAudioCompletionEvent* create(AudioBuffer* renderedBuffer);
+  static OfflineAudioCompletionEvent* create(
+      const AtomicString& type,
+      const OfflineAudioCompletionEventInit&);
 
   ~OfflineAudioCompletionEvent() override;
 
@@ -53,6 +58,8 @@
  private:
   OfflineAudioCompletionEvent();
   explicit OfflineAudioCompletionEvent(AudioBuffer* renderedBuffer);
+  explicit OfflineAudioCompletionEvent(const AtomicString& type,
+                                       const OfflineAudioCompletionEventInit&);
 
   Member<AudioBuffer> m_renderedBuffer;
 };
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.idl b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.idl
index f2bd91e..4aed122 100644
--- a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.idl
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEvent.idl
@@ -24,6 +24,9 @@
  */
 
 // See https://webaudio.github.io/web-audio-api/#OfflineAudioCompletionEvent 
+[
+    Constructor(DOMString type, OfflineAudioCompletionEventInit eventInitDict)
+]
 interface OfflineAudioCompletionEvent : Event {
     readonly attribute AudioBuffer renderedBuffer;
 };
diff --git a/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEventInit.idl b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEventInit.idl
new file mode 100644
index 0000000..c70ba7c
--- /dev/null
+++ b/third_party/WebKit/Source/modules/webaudio/OfflineAudioCompletionEventInit.idl
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// See https://webaudio.github.io/web-audio-api/#offlineaudiocompletioneventinit
+dictionary OfflineAudioCompletionEventInit : EventInit {
+    required AudioBuffer renderedBuffer;
+};
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
index e2406d71..5d88165 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
@@ -30,24 +30,12 @@
 WebGLContextGroup::WebGLContextGroup() : m_numberOfContextLosses(0) {}
 
 gpu::gles2::GLES2Interface* WebGLContextGroup::getAGLInterface() {
-  // It's possible that all of the WebGLRenderingContextBases (currently
-  // only one) referenced weakly by this WebGLContextGroup are GC'd before
-  // the (shared) WebGLObjects that it created. Since the calling code
-  // handles this gracefully, and since the graphical resources will have
-  // been implicitly reclaimed by the deletion of the context, explicitly
-  // test for this possibility.
-  if (m_contexts.isEmpty())
-    return nullptr;
-
-  // Weak processing removes dead entries from the HeapHashSet, so it's
-  // guaranteed that this will not return null for the reason that a
-  // WeakMember was nulled out.
+  DCHECK(!m_contexts.isEmpty());
   return (*m_contexts.begin())->contextGL();
 }
 
 void WebGLContextGroup::addContext(WebGLRenderingContextBase* context) {
-  m_contexts.add(context);
-  ScriptWrappableVisitor::writeBarrier(this, context);
+  m_contexts.add(TraceWrapperMember<WebGLRenderingContextBase>(this, context));
 }
 
 void WebGLContextGroup::loseContextGroup(
@@ -63,11 +51,8 @@
 }
 
 DEFINE_TRACE_WRAPPERS(WebGLContextGroup) {
-  // TODO(kbr, mlippautz): need to use the manual write barrier since we
-  // need to use weak members to get the desired semantics in Oilpan, but
-  // no such TraceWrapperMember (like TraceWrapperWeakMember) exists yet.
   for (auto context : m_contexts) {
-    visitor->traceWrappersWithManualWriteBarrier(context);
+    visitor->traceWrappers(context);
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
index 4986a45d..8884a063 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.h
@@ -72,9 +72,7 @@
 
   uint32_t m_numberOfContextLosses;
 
-  // Note: ideally this would use a TraceWrapperMember subclass, but
-  // that doesn't seem to exist for weak members yet.
-  HeapHashSet<WeakMember<WebGLRenderingContextBase>> m_contexts;
+  HeapHashSet<TraceWrapperMember<WebGLRenderingContextBase>> m_contexts;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 9240884..7ea80ad 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -7676,9 +7676,7 @@
 }
 
 DEFINE_TRACE_WRAPPERS(WebGLRenderingContextBase) {
-  if (isContextLost()) {
-    return;
-  }
+  visitor->traceWrappers(m_contextGroup);
   visitor->traceWrappers(m_boundArrayBuffer);
   visitor->traceWrappers(m_renderbufferBinding);
   visitor->traceWrappers(m_framebufferBinding);
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index ec4be49..d43ec77 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -249,8 +249,6 @@
     "ContentDecryptionModuleResult.h",
     "ContentSettingCallbacks.cpp",
     "ContentSettingCallbacks.h",
-    "ContentType.cpp",
-    "ContentType.h",
     "ContextMenu.cpp",
     "ContextMenu.h",
     "ContextMenuItem.cpp",
@@ -303,9 +301,6 @@
     "LifecycleObserver.h",
     "LinkHash.cpp",
     "LinkHash.h",
-    "MIMETypeFromURL.cpp",
-    "MIMETypeFromURL.h",
-    "MIMETypeRegistry.h",
     "MemoryCoordinator.cpp",
     "MemoryCoordinator.h",
     "PODArena.h",
@@ -357,6 +352,7 @@
     "UserGestureIndicator.h",
     "WaitableEvent.cpp",
     "WaitableEvent.h",
+    "WebFrameScheduler.h",
     "WebIconSizesParser.cpp",
     "WebScheduler.cpp",
     "WebTaskRunner.cpp",
@@ -639,8 +635,6 @@
     "fonts/FontFallbackPriority.h",
     "fonts/FontFamily.cpp",
     "fonts/FontFamily.h",
-    "fonts/FontFeatureSettings.cpp",
-    "fonts/FontFeatureSettings.h",
     "fonts/FontPlatformData.cpp",
     "fonts/FontPlatformData.h",
     "fonts/FontVariantNumeric.h",
@@ -678,6 +672,7 @@
     "fonts/mac/FontFamilyMatcherMac.h",
     "fonts/mac/FontFamilyMatcherMac.mm",
     "fonts/mac/FontPlatformDataMac.mm",
+    "fonts/opentype/FontSettings.h",
     "fonts/opentype/OpenTypeCapsSupport.cpp",
     "fonts/opentype/OpenTypeCapsSupport.h",
     "fonts/opentype/OpenTypeCapsSupportMPL.cpp",
@@ -785,6 +780,8 @@
     "graphics/CanvasSurfaceLayerBridge.h",
     "graphics/Color.cpp",
     "graphics/Color.h",
+    "graphics/ColorBehavior.cpp",
+    "graphics/ColorBehavior.h",
     "graphics/ColorSpace.cpp",
     "graphics/ColorSpace.h",
     "graphics/ColorSpaceProfileData.cpp",
@@ -1159,7 +1156,12 @@
     "network/WebSocketHandshakeRequest.h",
     "network/WebSocketHandshakeResponse.cpp",
     "network/WebSocketHandshakeResponse.h",
+    "network/mime/ContentType.cpp",
+    "network/mime/ContentType.h",
+    "network/mime/MIMETypeFromURL.cpp",
+    "network/mime/MIMETypeFromURL.h",
     "network/mime/MIMETypeRegistry.cpp",
+    "network/mime/MIMETypeRegistry.h",
     "peerconnection/RTCAnswerOptionsPlatform.h",
     "peerconnection/RTCOfferOptionsPlatform.h",
     "peerconnection/RTCSessionDescriptionRequest.h",
diff --git a/third_party/WebKit/Source/platform/ContentType.cpp b/third_party/WebKit/Source/platform/ContentType.cpp
deleted file mode 100644
index 8edb5c5..0000000
--- a/third_party/WebKit/Source/platform/ContentType.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2006, 2008 Apple Inc.  All rights reserved.
- * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
- * (http://www.torchmobile.com/)
- * Copyright (C) 2009 Google Inc.  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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#include "platform/ContentType.h"
-
-namespace blink {
-
-ContentType::ContentType(const String& contentType) : m_type(contentType) {}
-
-String ContentType::parameter(const String& parameterName) const {
-  String parameterValue;
-  String strippedType = m_type.stripWhiteSpace();
-
-  // a MIME type can have one or more "param=value" after a semi-colon, and
-  // separated from each other by semi-colons
-  size_t semi = strippedType.find(';');
-  if (semi != kNotFound) {
-    size_t start =
-        strippedType.find(parameterName, semi + 1, TextCaseASCIIInsensitive);
-    if (start != kNotFound) {
-      start = strippedType.find('=', start + parameterName.length());
-      if (start != kNotFound) {
-        size_t quote = strippedType.find('\"', start + 1);
-        size_t end = strippedType.find('\"', start + 2);
-        if (quote != kNotFound && end != kNotFound) {
-          start = quote;
-        } else {
-          end = strippedType.find(';', start + 1);
-          if (end == kNotFound)
-            end = strippedType.length();
-        }
-        parameterValue = strippedType.substring(start + 1, end - (start + 1))
-                             .stripWhiteSpace();
-      }
-    }
-  }
-
-  return parameterValue;
-}
-
-String ContentType::type() const {
-  String strippedType = m_type.stripWhiteSpace();
-
-  // "type" can have parameters after a semi-colon, strip them
-  size_t semi = strippedType.find(';');
-  if (semi != kNotFound)
-    strippedType = strippedType.left(semi).stripWhiteSpace();
-
-  return strippedType;
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/ContentType.h b/third_party/WebKit/Source/platform/ContentType.h
deleted file mode 100644
index ccd966c8..0000000
--- a/third_party/WebKit/Source/platform/ContentType.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
- * Copyright (C) 2009 Google Inc.  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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#ifndef ContentType_h
-#define ContentType_h
-
-#include "platform/PlatformExport.h"
-#include "wtf/Allocator.h"
-#include "wtf/text/WTFString.h"
-
-namespace blink {
-
-class PLATFORM_EXPORT ContentType {
-  STACK_ALLOCATED();
-
- public:
-  explicit ContentType(const String& type);
-
-  String parameter(const String& parameterName) const;
-  String type() const;
-  const String& raw() const { return m_type; }
-
- private:
-  String m_type;
-};
-
-}  // namespace blink
-
-#endif  // ContentType_h
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.h b/third_party/WebKit/Source/platform/LayoutUnit.h
index c0341001..9ee4c7e 100644
--- a/third_party/WebKit/Source/platform/LayoutUnit.h
+++ b/third_party/WebKit/Source/platform/LayoutUnit.h
@@ -108,14 +108,6 @@
   double toDouble() const {
     return static_cast<double>(m_value) / kFixedPointDenominator;
   }
-  float ceilToFloat() const {
-    float floatValue = toFloat();
-    if (static_cast<int>(floatValue * kFixedPointDenominator) == m_value)
-      return floatValue;
-    if (floatValue > 0)
-      return nextafterf(floatValue, std::numeric_limits<float>::max());
-    return nextafterf(floatValue, std::numeric_limits<float>::min());
-  }
   unsigned toUnsigned() const {
     REPORT_OVERFLOW(m_value >= 0);
     return toInt();
diff --git a/third_party/WebKit/Source/platform/MIMETypeFromURL.cpp b/third_party/WebKit/Source/platform/MIMETypeFromURL.cpp
deleted file mode 100644
index 0ebfc2d..0000000
--- a/third_party/WebKit/Source/platform/MIMETypeFromURL.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2004, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
- * Copyright (C) 2012 Research In Motion Limited. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#include "platform/MIMETypeFromURL.h"
-
-#include "platform/MIMETypeRegistry.h"
-#include "platform/weborigin/KURL.h"
-#include "wtf/text/WTFString.h"
-
-namespace blink {
-
-String mimeTypeFromDataURL(const String& url) {
-  ASSERT(protocolIs(url, "data"));
-  size_t index = url.find(';');
-  if (index == kNotFound)
-    index = url.find(',');
-  if (index != kNotFound) {
-    if (index > 5)
-      return url.substring(5, index - 5).lower();
-    // Data URLs with no MIME type are considered text/plain.
-    return "text/plain";
-  }
-  return "";
-}
-
-String mimeTypeFromURL(const KURL& url) {
-  String decodedPath = decodeURLEscapeSequences(url.path());
-  String extension = decodedPath.substring(decodedPath.reverseFind('.') + 1);
-
-  // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns
-  // "application/octet-stream" upon failure
-  return MIMETypeRegistry::getMIMETypeForExtension(extension);
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/MIMETypeRegistry.h b/third_party/WebKit/Source/platform/MIMETypeRegistry.h
deleted file mode 100644
index 95b9adc3..0000000
--- a/third_party/WebKit/Source/platform/MIMETypeRegistry.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#ifndef MIMETypeRegistry_h
-#define MIMETypeRegistry_h
-
-#include "platform/PlatformExport.h"
-#include "wtf/Allocator.h"
-#include "wtf/HashSet.h"
-#include "wtf/text/StringHash.h"
-#include "wtf/text/WTFString.h"
-
-namespace blink {
-
-// Note/reminder: MIME type and parameter names are per-RFC case
-// insensitive (https://www.ietf.org/rfc/rfc2045.txt , section 5.1).
-// The MIMETypeRegistry predicates are all case-insensitive.
-
-// TODO(kinuko): Move this file under platform/network/mime.
-class PLATFORM_EXPORT MIMETypeRegistry {
-  STATIC_ONLY(MIMETypeRegistry);
-
- public:
-  // For Media MIME type checks.
-  enum SupportsType { IsNotSupported, IsSupported, MayBeSupported };
-
-  static String getMIMETypeForExtension(const String& extension);
-  static String getWellKnownMIMETypeForExtension(const String& extension);
-  static String getMIMETypeForPath(const String& path);
-
-  // Checks to see if the given mime type is supported.
-  static bool isSupportedMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded inline as an
-  // image (e.g., <img> tags).
-  static bool isSupportedImageMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded as an image
-  // document in a frame.
-  static bool isSupportedImageResourceMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being displayed as an image.
-  static bool isSupportedImagePrefixedMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being encoded.
-  static bool isSupportedImageMIMETypeForEncoding(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded as a JavaScript
-  // resource.
-  static bool isSupportedJavaScriptMIMEType(const String& mimeType);
-
-  // Checks to see if a non-image mime type is suitable for being loaded as a
-  // document in a frame. Includes supported JavaScript MIME types.
-  static bool isSupportedNonImageMIMEType(const String& mimeType);
-
-  // Checks to see if the mime type and codecs are supported media MIME types.
-  static bool isSupportedMediaMIMEType(const String& mimeType,
-                                       const String& codecs);
-
-  // Does similar to isSupportedMediaMIMEType, but returns a little more
-  // detailed information in SupportsType enum.
-  static SupportsType supportsMediaMIMEType(const String& mimeType,
-                                            const String& codecs);
-
-  // Checks to see if the mime type and codecs are supported by the MediaSource
-  // implementation.
-  static bool isSupportedMediaSourceMIMEType(const String& mimeType,
-                                             const String& codecs);
-
-  // Checks to see if a mime type is a valid Java applet mime type
-  static bool isJavaAppletMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded as a stylesheet.
-  static bool isSupportedStyleSheetMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded as a font.
-  static bool isSupportedFontMIMEType(const String& mimeType);
-
-  // Checks to see if a mime type is suitable for being loaded as a text track.
-  static bool isSupportedTextTrackMIMEType(const String& mimeType);
-};
-
-}  // namespace blink
-
-#endif  // MIMETypeRegistry_h
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 10f067a..14818ba 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -67,6 +67,8 @@
 CSSMaskSourceType status=experimental
 CSSOMSmoothScroll status=experimental
 CSSOffsetPositionAnchor status=experimental
+CSSOffsetRotate status=stable
+CSSOffsetRotation status=stable
 CSSPaintAPI status=experimental, depends_on=CSSTypedOM, depends_on=Worklet
 CSSSnapSize status=experimental
 CSSStickyPosition status=stable
@@ -140,6 +142,7 @@
 MediaSourceExperimental status=experimental
 MediaSourceNewAbortAndDuration status=experimental
 MediaStreamSpeech status=experimental
+MediaQueryShape status=experimental
 MemoryInfoInWorkers status=experimental
 // This is enabled by default on Windows only. The only part that's
 // "experimental" is the support on other platforms.
diff --git a/third_party/WebKit/Source/platform/WebFrameScheduler.h b/third_party/WebKit/Source/platform/WebFrameScheduler.h
new file mode 100644
index 0000000..f6b2d5bc
--- /dev/null
+++ b/third_party/WebKit/Source/platform/WebFrameScheduler.h
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebFrameScheduler_h
+#define WebFrameScheduler_h
+
+namespace blink {
+
+class WebTaskRunner;
+class WebViewScheduler;
+
+class WebFrameScheduler {
+ public:
+  virtual ~WebFrameScheduler() {}
+
+  // The scheduler may throttle tasks associated with offscreen frames.
+  virtual void setFrameVisible(bool) {}
+
+  // Tells the scheduler that the page this frame belongs to is not visible.
+  // The scheduler may throttle tasks associated with pages that are not
+  // visible.
+  virtual void setPageVisible(bool) {}
+
+  // Set whether this frame is suspended. Only unthrottledTaskRunner tasks are
+  // allowed to run on a suspended frame.
+  virtual void setSuspended(bool) {}
+
+  // Set whether this frame is cross origin w.r.t. the top level frame. Cross
+  // origin frames may use a different scheduling policy from same origin
+  // frames.
+  virtual void setCrossOrigin(bool) {}
+
+  // Returns the WebTaskRunner for loading tasks.
+  // WebFrameScheduler owns the returned WebTaskRunner.
+  virtual WebTaskRunner* loadingTaskRunner() { return nullptr; }
+
+  // Returns the WebTaskRunner for timer tasks.
+  // WebFrameScheduler owns the returned WebTaskRunner.
+  virtual WebTaskRunner* timerTaskRunner() { return nullptr; }
+
+  // Returns the WebTaskRunner for tasks which should never get throttled.
+  // This is generally used for executing internal browser tasks which should
+  // never be throttled. Ideally only tasks whose performance characteristics
+  // are known should be posted to this task runner; for example user
+  // JavaScript is discouraged. WebFrameScheduler owns the returned
+  // WebTaskRunner.
+  virtual WebTaskRunner* unthrottledTaskRunner() { return nullptr; }
+
+  // Returns the parent WebViewScheduler.
+  virtual WebViewScheduler* webViewScheduler() { return nullptr; }
+
+  // Tells the scheduler a resource load has started. The scheduler may make
+  // policy decisions based on this.
+  virtual void didStartLoading(unsigned long identifier) {}
+
+  // Tells the scheduler a resource load has stopped. The scheduler may make
+  // policy decisions based on this.
+  virtual void didStopLoading(unsigned long identifier) {}
+
+  // Tells the scheduler if we are parsing a document on another thread. This
+  // tells the scheduler not to advance virtual time if it's using the
+  // DETERMINISTIC_LOADING policy.
+  virtual void setDocumentParsingInBackground(bool) {}
+
+  // Tells the scheduler that the first meaningful paint has occured for this
+  // frame.
+  virtual void onFirstMeaningfulPaint() {}
+};
+
+}  // namespace blink
+
+#endif  // WebFrameScheduler_h
diff --git a/third_party/WebKit/Source/platform/WebScheduler.cpp b/third_party/WebKit/Source/platform/WebScheduler.cpp
index 04e27d793..6f80af7 100644
--- a/third_party/WebKit/Source/platform/WebScheduler.cpp
+++ b/third_party/WebKit/Source/platform/WebScheduler.cpp
@@ -4,7 +4,7 @@
 
 #include "public/platform/WebScheduler.h"
 
-#include "public/platform/WebFrameScheduler.h"
+#include "platform/WebFrameScheduler.h"
 #include "public/platform/WebTraceLocation.h"
 #include "wtf/Assertions.h"
 
diff --git a/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp b/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
index 3e034cc..217a7e5f 100644
--- a/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
+++ b/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
@@ -29,8 +29,8 @@
     WebThread* thread,
     BlinkGC::ThreadHeapMode threadHeapMode)
     : m_thread(thread), m_threadHeapMode(threadHeapMode) {
-#if ENABLE(ASSERT)
-  ASSERT(!name || !thread);
+  DCHECK(!name || !thread);
+#if DCHECK_IS_ON()
   // We call this regardless of whether an existing thread is given or not,
   // as it means that blink is going to run with more than one thread.
   WTF::willCreateThread();
diff --git a/third_party/WebKit/Source/platform/audio/resources/Composite.flac b/third_party/WebKit/Source/platform/audio/resources/Composite.flac
new file mode 100644
index 0000000..d67abe48
--- /dev/null
+++ b/third_party/WebKit/Source/platform/audio/resources/Composite.flac
Binary files differ
diff --git a/third_party/WebKit/Source/platform/audio/resources/README b/third_party/WebKit/Source/platform/audio/resources/README
new file mode 100644
index 0000000..90d3fd6
--- /dev/null
+++ b/third_party/WebKit/Source/platform/audio/resources/README
@@ -0,0 +1,20 @@
+See https://bugs.webkit.org/show_bug.cgi?id=98294#c9.
+
+Here is how to create Composite.wav from the indidividual responses:
+
+AUDIO_DIR="WebKit/Source/platform/audio/resources"
+sox $( \
+  for azimuth in {0..23}; do \
+    for elevation in 0 15 30 45 60 75 90 315 330 345; do \
+      printf "$AUDIO_DIR/IRC_Composite_C_R0195_T%03d_P%03d.wav\n" "$(expr $azimuth \* 15)" "$elevation"; \
+    done; \
+  done;) \
+Composite.wav
+
+To create the FLAC-encoded version:
+
+avconv -i Composite.wav Composite.flac
+
+If you change Composite.flac (or Composite.wav), be sure to update
+LayoutTests/webaudio/resources/hrtf with the updated files!
+
diff --git a/third_party/WebKit/Source/platform/exported/WebImage.cpp b/third_party/WebKit/Source/platform/exported/WebImage.cpp
index 755c959d..86c6835 100644
--- a/third_party/WebKit/Source/platform/exported/WebImage.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebImage.cpp
@@ -45,9 +45,8 @@
 
 WebImage WebImage::fromData(const WebData& data, const WebSize& desiredSize) {
   RefPtr<SharedBuffer> buffer = PassRefPtr<SharedBuffer>(data);
-  std::unique_ptr<ImageDecoder> decoder(
-      ImageDecoder::create(buffer, true, ImageDecoder::AlphaPremultiplied,
-                           ImageDecoder::ColorSpaceIgnored, nullptr));
+  std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
+      buffer, true, ImageDecoder::AlphaPremultiplied, ColorBehavior::ignore()));
   if (!decoder || !decoder->isSizeAvailable())
     return WebImage();
 
@@ -84,9 +83,8 @@
   const size_t maxFrameCount = 8;
 
   RefPtr<SharedBuffer> buffer = PassRefPtr<SharedBuffer>(data);
-  std::unique_ptr<ImageDecoder> decoder(
-      ImageDecoder::create(buffer, true, ImageDecoder::AlphaPremultiplied,
-                           ImageDecoder::ColorSpaceIgnored, nullptr));
+  std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
+      buffer, true, ImageDecoder::AlphaPremultiplied, ColorBehavior::ignore()));
   if (!decoder || !decoder->isSizeAvailable())
     return WebVector<WebImage>();
 
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
index be49368..31b3967 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.cpp
@@ -36,6 +36,8 @@
     "domain", FeaturePolicy::FeatureDefault::EnableForAll};
 const FeaturePolicy::Feature kDocumentWrite{
     "docwrite", FeaturePolicy::FeatureDefault::EnableForAll};
+const FeaturePolicy::Feature kFullscreenFeature{
+    "fullscreen", FeaturePolicy::FeatureDefault::EnableForSelf};
 const FeaturePolicy::Feature kGeolocationFeature{
     "geolocation", FeaturePolicy::FeatureDefault::EnableForSelf};
 const FeaturePolicy::Feature kMidiFeature{
@@ -112,9 +114,9 @@
   DEFINE_STATIC_LOCAL(
       Vector<const FeaturePolicy::Feature*>, defaultFeatureList,
       ({&kDocumentCookie, &kDocumentDomain, &kDocumentWrite,
-        &kGeolocationFeature, &kMidiFeature, &kNotificationsFeature,
-        &kPaymentFeature, &kPushFeature, &kSyncScript, &kSyncXHR, &kUsermedia,
-        &kVibrateFeature, &kWebRTC}));
+        &kGeolocationFeature, &kFullscreenFeature, &kMidiFeature,
+        &kNotificationsFeature, &kPaymentFeature, &kPushFeature, &kSyncScript,
+        &kSyncXHR, &kUsermedia, &kVibrateFeature, &kWebRTC}));
   return defaultFeatureList;
 }
 
diff --git a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
index 062d6e5..47b22e6b7 100644
--- a/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
+++ b/third_party/WebKit/Source/platform/feature_policy/FeaturePolicy.h
@@ -191,6 +191,7 @@
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kDocumentDomain;
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kDocumentWrite;
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kGeolocationFeature;
+extern const PLATFORM_EXPORT FeaturePolicy::Feature kFullscreenFeature;
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kMidiFeature;
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kNotificationsFeature;
 extern const PLATFORM_EXPORT FeaturePolicy::Feature kPaymentFeature;
diff --git a/third_party/WebKit/Source/platform/fonts/FontDescription.h b/third_party/WebKit/Source/platform/fonts/FontDescription.h
index 025301c..594164f 100644
--- a/third_party/WebKit/Source/platform/fonts/FontDescription.h
+++ b/third_party/WebKit/Source/platform/fonts/FontDescription.h
@@ -31,7 +31,6 @@
 #include "platform/LayoutLocale.h"
 #include "platform/fonts/FontCacheKey.h"
 #include "platform/fonts/FontFamily.h"
-#include "platform/fonts/FontFeatureSettings.h"
 #include "platform/fonts/FontOrientation.h"
 #include "platform/fonts/FontSmoothingMode.h"
 #include "platform/fonts/FontTraits.h"
@@ -39,6 +38,7 @@
 #include "platform/fonts/FontWidthVariant.h"
 #include "platform/fonts/TextRenderingMode.h"
 #include "platform/fonts/TypesettingFeatures.h"
+#include "platform/fonts/opentype/FontSettings.h"
 #include "wtf/Allocator.h"
 #include "wtf/MathExtras.h"
 
diff --git a/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.cpp b/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.cpp
deleted file mode 100644
index ab4c63f..0000000
--- a/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#include "platform/fonts/FontFeatureSettings.h"
-
-namespace blink {
-
-FontFeature::FontFeature(const AtomicString& tag, int value)
-    : m_tag(tag), m_value(value) {}
-
-bool FontFeature::operator==(const FontFeature& other) const {
-  return m_tag == other.m_tag && m_value == other.m_value;
-}
-
-FontFeatureSettings::FontFeatureSettings() {}
-
-bool FontFeatureSettings::operator==(const FontFeatureSettings& other) const {
-  return m_list == other.m_list;
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.h b/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.h
deleted file mode 100644
index ea0bd32..0000000
--- a/third_party/WebKit/Source/platform/fonts/FontFeatureSettings.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
- */
-
-#ifndef FontFeatureSettings_h
-#define FontFeatureSettings_h
-
-#include "platform/PlatformExport.h"
-#include "wtf/Allocator.h"
-#include "wtf/PassRefPtr.h"
-#include "wtf/RefCounted.h"
-#include "wtf/RefPtr.h"
-#include "wtf/Vector.h"
-#include "wtf/text/AtomicString.h"
-
-namespace blink {
-
-class PLATFORM_EXPORT FontFeature {
-  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
-
- public:
-  FontFeature(const AtomicString& tag, int value);
-  bool operator==(const FontFeature&) const;
-
-  const AtomicString& tag() const { return m_tag; }
-  int value() const { return m_value; }
-
- private:
-  AtomicString m_tag;
-  const int m_value;
-};
-
-class PLATFORM_EXPORT FontFeatureSettings
-    : public RefCounted<FontFeatureSettings> {
-  WTF_MAKE_NONCOPYABLE(FontFeatureSettings);
-
- public:
-  static PassRefPtr<FontFeatureSettings> create() {
-    return adoptRef(new FontFeatureSettings());
-  }
-  void append(const FontFeature& feature) { m_list.append(feature); }
-  size_t size() const { return m_list.size(); }
-  const FontFeature& operator[](int index) const { return m_list[index]; }
-  const FontFeature& at(size_t index) const { return m_list.at(index); }
-  bool operator==(const FontFeatureSettings&) const;
-
- private:
-  FontFeatureSettings();
-  Vector<FontFeature> m_list;
-};
-
-}  // namespace blink
-
-#endif  // FontFeatureSettings_h
diff --git a/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
new file mode 100644
index 0000000..c6469352
--- /dev/null
+++ b/third_party/WebKit/Source/platform/fonts/opentype/FontSettings.h
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FontSettings_h
+#define FontSettings_h
+
+#include "platform/PlatformExport.h"
+#include "wtf/Allocator.h"
+#include "wtf/PassRefPtr.h"
+#include "wtf/RefCounted.h"
+#include "wtf/RefPtr.h"
+#include "wtf/Vector.h"
+#include "wtf/text/AtomicString.h"
+
+namespace blink {
+
+template <typename T>
+class FontTagValuePair {
+  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+  FontTagValuePair(const AtomicString& tag, T value)
+      : m_tag(tag), m_value(value){};
+  bool operator==(const FontTagValuePair& other) const {
+    return m_tag == other.m_tag && m_value == other.m_value;
+  };
+
+  const AtomicString& tag() const { return m_tag; }
+  T value() const { return m_value; }
+
+ private:
+  AtomicString m_tag;
+  const T m_value;
+};
+
+template <typename T>
+class FontSettings {
+  WTF_MAKE_NONCOPYABLE(FontSettings);
+
+ public:
+  void append(const T& feature) { m_list.append(feature); }
+  size_t size() const { return m_list.size(); }
+  const T& operator[](int index) const { return m_list[index]; }
+  const T& at(size_t index) const { return m_list.at(index); }
+  bool operator==(const FontSettings& other) const {
+    return m_list == other.m_list;
+  };
+
+ protected:
+  FontSettings(){};
+
+ private:
+  Vector<T> m_list;
+};
+
+using FontFeature = FontTagValuePair<int>;
+using FontVariationAxis = FontTagValuePair<float>;
+
+class PLATFORM_EXPORT FontFeatureSettings
+    : public FontSettings<FontFeature>,
+      public RefCounted<FontFeatureSettings> {
+  WTF_MAKE_NONCOPYABLE(FontFeatureSettings);
+
+ public:
+  static PassRefPtr<FontFeatureSettings> create() {
+    return adoptRef(new FontFeatureSettings);
+  }
+
+ private:
+  FontFeatureSettings() = default;
+};
+
+class PLATFORM_EXPORT FontVariationSettings
+    : public FontSettings<FontVariationAxis>,
+      public RefCounted<FontVariationSettings> {
+  WTF_MAKE_NONCOPYABLE(FontVariationSettings);
+
+ public:
+  static PassRefPtr<FontVariationSettings> create() {
+    return adoptRef(new FontVariationSettings());
+  };
+
+ private:
+  FontVariationSettings() = default;
+};
+
+}  // namespace blink
+
+#endif  // FontSettings_h
diff --git a/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp b/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp
new file mode 100644
index 0000000..781ccc1f
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/ColorBehavior.cpp
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/graphics/ColorBehavior.h"
+
+#include "platform/graphics/BitmapImageMetrics.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "wtf/SpinLock.h"
+
+namespace blink {
+
+namespace {
+
+// The output device color space is global and shared across multiple threads.
+SpinLock gTargetColorSpaceLock;
+SkColorSpace* gTargetColorSpace = nullptr;
+
+}  // namespace
+
+// static
+void ColorBehavior::setGlobalTargetColorProfile(
+    const WebVector<char>& profile) {
+  if (profile.isEmpty())
+    return;
+
+  // Take a lock around initializing and accessing the global device color
+  // profile.
+  SpinLock::Guard guard(gTargetColorSpaceLock);
+
+  // Layout tests expect that only the first call will take effect.
+  if (gTargetColorSpace)
+    return;
+
+  gTargetColorSpace =
+      SkColorSpace::MakeICC(profile.data(), profile.size()).release();
+
+  // UMA statistics.
+  BitmapImageMetrics::countOutputGamma(gTargetColorSpace);
+}
+
+// static
+sk_sp<SkColorSpace> ColorBehavior::globalTargetColorSpace() {
+  // Take a lock around initializing and accessing the global device color
+  // profile.
+  SpinLock::Guard guard(gTargetColorSpaceLock);
+
+  // Initialize the output device profile to sRGB if it has not yet been
+  // initialized.
+  if (!gTargetColorSpace) {
+    gTargetColorSpace =
+        SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).release();
+  }
+
+  gTargetColorSpace->ref();
+  return sk_sp<SkColorSpace>(gTargetColorSpace);
+}
+
+// static
+ColorBehavior ColorBehavior::transformToGlobalTarget() {
+  return ColorBehavior(Type::TransformTo, globalTargetColorSpace());
+}
+
+// static
+ColorBehavior ColorBehavior::transformToTargetForTesting() {
+  return transformToGlobalTarget();
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/ColorBehavior.h b/third_party/WebKit/Source/platform/graphics/ColorBehavior.h
new file mode 100644
index 0000000..b2de9e47a
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/ColorBehavior.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ColorBehavior_h
+#define ColorBehavior_h
+
+#include "platform/PlatformExport.h"
+#include "public/platform/WebVector.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkColorSpace;
+
+namespace blink {
+
+class PLATFORM_EXPORT ColorBehavior {
+ public:
+  // This specifies to ignore color profiles embedded in images entirely. No
+  // transformations will be applied to any pixel data, and no SkImages will be
+  // tagged with an SkColorSpace.
+  static inline ColorBehavior ignore() {
+    return ColorBehavior(Type::Ignore, nullptr);
+  }
+  bool isIgnore() const { return m_type == Type::Ignore; }
+
+  // This specifies that images will not be transformed (to the extent
+  // possible), but that SkImages will be tagged with the embedded SkColorSpace
+  // (or sRGB if there was no embedded color profile).
+  static inline ColorBehavior tag() {
+    return ColorBehavior(Type::Tag, nullptr);
+  }
+  bool isTag() const { return m_type == Type::Tag; }
+
+  // This specifies that images will be transformed to the specified target
+  // color space, and that SkImages will not be tagged with any SkColorSpace.
+  static inline ColorBehavior transformTo(sk_sp<SkColorSpace> target) {
+    return ColorBehavior(Type::TransformTo, std::move(target));
+  }
+  bool isTransformToTargetColorSpace() const {
+    return m_type == Type::TransformTo;
+  }
+  sk_sp<SkColorSpace> targetColorSpace() const {
+    DCHECK(m_type == Type::TransformTo);
+    return m_target;
+  }
+
+  // Set the target color profile into which all images with embedded color
+  // profiles should be converted. Note that only the first call to this
+  // function in this process has any effect.
+  static void setGlobalTargetColorProfile(const WebVector<char>&);
+  static sk_sp<SkColorSpace> globalTargetColorSpace();
+
+  // Return the behavior of transforming to the color space specified above, or
+  // sRGB, if the above has not yet been called.
+  static ColorBehavior transformToGlobalTarget();
+
+  // Transform to a target color space to be used by tests.
+  static ColorBehavior transformToTargetForTesting();
+
+ private:
+  enum class Type {
+    Ignore,
+    Tag,
+    TransformTo,
+  };
+  ColorBehavior(Type type, sk_sp<SkColorSpace> target)
+      : m_type(type), m_target(std::move(target)) {}
+  Type m_type;
+  sk_sp<SkColorSpace> m_target;
+};
+
+}  // namespace blink
+
+#endif  // ColorBehavior_h
diff --git a/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp b/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp
index aaa6e09c..a2b49cf3 100644
--- a/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp
@@ -94,7 +94,7 @@
     // TODO(ccameron): This color space should come from the GraphicsLayer.
     // https://crbug.com/667411
     webDisplayItemList->setImpliedColorSpace(gfx::ColorSpace::FromSkColorSpace(
-        ImageDecoder::globalTargetColorSpace()));
+        ColorBehavior::globalTargetColorSpace()));
   }
   paintController.setDisplayItemConstructionIsDisabled(false);
   paintController.setSubsequenceCachingIsDisabled(false);
diff --git a/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp b/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
index c158bad..29eefb7 100644
--- a/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DecodingImageGenerator.cpp
@@ -142,8 +142,7 @@
   // don't really matter.
   std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
       segmentReader, true, ImageDecoder::AlphaPremultiplied,
-      ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::globalTargetColorSpace());
+      ColorBehavior::transformToGlobalTarget());
   if (!decoder || !decoder->isSizeAvailable())
     return nullptr;
 
@@ -152,9 +151,9 @@
       SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType,
                            decoder->colorSpaceForSkImages());
 
-  RefPtr<ImageFrameGenerator> frame = ImageFrameGenerator::create(
-      SkISize::Make(size.width(), size.height()), false,
-      decoder->colorSpaceOption(), decoder->targetColorSpace());
+  RefPtr<ImageFrameGenerator> frame =
+      ImageFrameGenerator::create(SkISize::Make(size.width(), size.height()),
+                                  false, decoder->colorBehavior());
   if (!frame)
     return nullptr;
 
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
index d7dbfaf4..c706acb 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.cpp
@@ -61,13 +61,11 @@
     PassRefPtr<SharedBuffer> passData,
     bool dataComplete,
     ImageDecoder::AlphaOption alphaOption,
-    ImageDecoder::ColorSpaceOption colorOptions,
-    sk_sp<SkColorSpace> targetColorSpace) {
+    const ColorBehavior& colorBehavior) {
   RefPtr<SharedBuffer> data = passData;
 
   std::unique_ptr<ImageDecoder> actualDecoder =
-      ImageDecoder::create(data, dataComplete, alphaOption, colorOptions,
-                           std::move(targetColorSpace));
+      ImageDecoder::create(data, dataComplete, alphaOption, colorBehavior);
   if (!actualDecoder)
     return nullptr;
 
@@ -277,8 +275,7 @@
       SkISize::Make(m_actualDecoder->decodedSize().width(),
                     m_actualDecoder->decodedSize().height());
   m_frameGenerator = ImageFrameGenerator::create(
-      decodedSize, !isSingleFrame, m_actualDecoder->colorSpaceOption(),
-      m_actualDecoder->targetColorSpace());
+      decodedSize, !isSingleFrame, m_actualDecoder->colorBehavior());
 }
 
 void DeferredImageDecoder::prepareLazyDecodedFrames() {
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
index 338360a..443c399 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoder.h
@@ -53,8 +53,7 @@
       PassRefPtr<SharedBuffer> data,
       bool dataComplete,
       ImageDecoder::AlphaOption,
-      ImageDecoder::ColorSpaceOption,
-      sk_sp<SkColorSpace> targetColorSpace);
+      const ColorBehavior&);
 
   static std::unique_ptr<DeferredImageDecoder> createForTesting(
       std::unique_ptr<ImageDecoder>);
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
index 8be1eb71..d455d12 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTest.cpp
@@ -330,8 +330,7 @@
 TEST_F(DeferredImageDecoderTest, frameOpacity) {
   std::unique_ptr<DeferredImageDecoder> decoder = DeferredImageDecoder::create(
       m_data, true, ImageDecoder::AlphaPremultiplied,
-      ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting());
+      ColorBehavior::transformToTargetForTesting());
 
   SkImageInfo pixInfo = SkImageInfo::MakeN32Premul(1, 1);
 
diff --git a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTestWoPlatform.cpp b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTestWoPlatform.cpp
index 578d7980..5540d70 100644
--- a/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTestWoPlatform.cpp
+++ b/third_party/WebKit/Source/platform/graphics/DeferredImageDecoderTestWoPlatform.cpp
@@ -40,7 +40,7 @@
       SharedBuffer::create(file->data(), bytesForFirstFrame);
   std::unique_ptr<DeferredImageDecoder> decoder = DeferredImageDecoder::create(
       partialFile, false, ImageDecoder::AlphaPremultiplied,
-      ImageDecoder::ColorSpaceIgnored, nullptr);
+      ColorBehavior::ignore());
   ASSERT_NE(decoder, nullptr);
   sk_sp<SkImage> partialImage = decoder->createFrameAtIndex(0);
 
@@ -101,7 +101,7 @@
     EXPECT_FALSE(ImageDecoder::hasSufficientDataToSniffImageType(*buffer));
     EXPECT_EQ(nullptr, DeferredImageDecoder::create(
                            buffer, false, ImageDecoder::AlphaPremultiplied,
-                           ImageDecoder::ColorSpaceIgnored, nullptr));
+                           ColorBehavior::ignore()));
 
     // Append the rest of the data.  We should be able to sniff the signature
     // now, even if segmented.
@@ -110,7 +110,7 @@
     std::unique_ptr<DeferredImageDecoder> decoder =
         DeferredImageDecoder::create(buffer, false,
                                      ImageDecoder::AlphaPremultiplied,
-                                     ImageDecoder::ColorSpaceIgnored, nullptr);
+                                     ColorBehavior::ignore());
     ASSERT_NE(decoder, nullptr);
     EXPECT_TRUE(String(testFiles[i]).endsWith(decoder->filenameExtension()));
   }
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
index d10f0c8fa7..3be93aa 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayerTest.cpp
@@ -102,7 +102,7 @@
 };
 
 TEST_F(GraphicsLayerTest, updateLayerShouldFlattenTransformWithAnimations) {
-  ASSERT_FALSE(m_platformLayer->hasActiveAnimationForTesting());
+  ASSERT_FALSE(m_platformLayer->hasTickingAnimationForTesting());
 
   std::unique_ptr<CompositorFloatAnimationCurve> curve =
       CompositorFloatAnimationCurve::create();
@@ -129,23 +129,23 @@
 
   player.compositorPlayer()->addAnimation(std::move(floatAnimation));
 
-  ASSERT_TRUE(m_platformLayer->hasActiveAnimationForTesting());
+  ASSERT_TRUE(m_platformLayer->hasTickingAnimationForTesting());
 
   m_graphicsLayer->setShouldFlattenTransform(false);
 
   m_platformLayer = m_graphicsLayer->platformLayer();
   ASSERT_TRUE(m_platformLayer);
 
-  ASSERT_TRUE(m_platformLayer->hasActiveAnimationForTesting());
+  ASSERT_TRUE(m_platformLayer->hasTickingAnimationForTesting());
   player.compositorPlayer()->removeAnimation(animationId);
-  ASSERT_FALSE(m_platformLayer->hasActiveAnimationForTesting());
+  ASSERT_FALSE(m_platformLayer->hasTickingAnimationForTesting());
 
   m_graphicsLayer->setShouldFlattenTransform(true);
 
   m_platformLayer = m_graphicsLayer->platformLayer();
   ASSERT_TRUE(m_platformLayer);
 
-  ASSERT_FALSE(m_platformLayer->hasActiveAnimationForTesting());
+  ASSERT_FALSE(m_platformLayer->hasTickingAnimationForTesting());
 
   player.compositorPlayer()->detachElement();
   ASSERT_FALSE(player.compositorPlayer()->isElementAttached());
diff --git a/third_party/WebKit/Source/platform/graphics/Image.cpp b/third_party/WebKit/Source/platform/graphics/Image.cpp
index 2e7cbef9..b04a53b 100644
--- a/third_party/WebKit/Source/platform/graphics/Image.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Image.cpp
@@ -27,7 +27,6 @@
 #include "platform/graphics/Image.h"
 
 #include "platform/Length.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/PlatformInstrumentation.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/SharedBuffer.h"
@@ -37,6 +36,7 @@
 #include "platform/graphics/BitmapImage.h"
 #include "platform/graphics/DeferredImageDecoder.h"
 #include "platform/graphics/GraphicsContext.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/tracing/TraceEvent.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebData.h"
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
index 8a95991..f812c2c1 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
@@ -35,7 +35,6 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/sync_token.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/geometry/IntRect.h"
 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
@@ -50,6 +49,7 @@
 #include "platform/image-encoders/JPEGImageEncoder.h"
 #include "platform/image-encoders/PNGImageEncoder.h"
 #include "platform/image-encoders/WEBPImageEncoder.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebGraphicsContext3DProvider.h"
 #include "skia/ext/texture_handle.h"
diff --git a/third_party/WebKit/Source/platform/graphics/ImageDecodingStoreTest.cpp b/third_party/WebKit/Source/platform/graphics/ImageDecodingStoreTest.cpp
index 9cda1a3..5682367 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageDecodingStoreTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageDecodingStoreTest.cpp
@@ -37,9 +37,8 @@
  public:
   void SetUp() override {
     ImageDecodingStore::instance().setCacheLimitInBytes(1024 * 1024);
-    m_generator =
-        ImageFrameGenerator::create(SkISize::Make(100, 100), true,
-                                    ImageDecoder::ColorSpaceIgnored, nullptr);
+    m_generator = ImageFrameGenerator::create(SkISize::Make(100, 100), true,
+                                              ColorBehavior::ignore());
     m_decodersDestroyed = 0;
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
index 2b665bb7..f76ea742 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp
@@ -103,14 +103,11 @@
   return true;
 }
 
-ImageFrameGenerator::ImageFrameGenerator(
-    const SkISize& fullSize,
-    bool isMultiFrame,
-    ImageDecoder::ColorSpaceOption decoderColorSpaceOption,
-    sk_sp<SkColorSpace> decoderTargetColorSpace)
+ImageFrameGenerator::ImageFrameGenerator(const SkISize& fullSize,
+                                         bool isMultiFrame,
+                                         const ColorBehavior& colorBehavior)
     : m_fullSize(fullSize),
-      m_decoderColorSpaceOption(decoderColorSpaceOption),
-      m_decoderTargetColorSpace(std::move(decoderTargetColorSpace)),
+      m_decoderColorBehavior(colorBehavior),
       m_isMultiFrame(isMultiFrame),
       m_decodeFailed(false),
       m_yuvDecodingFailed(false),
@@ -178,8 +175,7 @@
   }
 
   std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
-      data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorSpaceOption,
-      m_decoderTargetColorSpace);
+      data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorBehavior);
   // getYUVComponentSizes was already called and was successful, so
   // ImageDecoder::create must succeed.
   ASSERT(decoder);
@@ -299,9 +295,9 @@
       *decoder = m_imageDecoderFactory->create().release();
 
     if (!*decoder) {
-      *decoder = ImageDecoder::create(
-                     data, allDataReceived, ImageDecoder::AlphaPremultiplied,
-                     m_decoderColorSpaceOption, m_decoderTargetColorSpace)
+      *decoder = ImageDecoder::create(data, allDataReceived,
+                                      ImageDecoder::AlphaPremultiplied,
+                                      m_decoderColorBehavior)
                      .release();
       // The newly created decoder just grabbed the data.  No need to reset it.
       shouldCallSetData = false;
@@ -370,8 +366,7 @@
     return false;
 
   std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
-      data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorSpaceOption,
-      m_decoderTargetColorSpace);
+      data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorBehavior);
   if (!decoder)
     return false;
 
diff --git a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.h b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.h
index 7945132..65ce816 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.h
@@ -66,11 +66,9 @@
   static PassRefPtr<ImageFrameGenerator> create(
       const SkISize& fullSize,
       bool isMultiFrame,
-      ImageDecoder::ColorSpaceOption decoderColorSpaceOption,
-      sk_sp<SkColorSpace> decoderTargetColorSpace) {
+      const ColorBehavior& colorBehavior) {
     return adoptRef(
-        new ImageFrameGenerator(fullSize, isMultiFrame, decoderColorSpaceOption,
-                                std::move(decoderTargetColorSpace)));
+        new ImageFrameGenerator(fullSize, isMultiFrame, colorBehavior));
   }
 
   ~ImageFrameGenerator();
@@ -112,8 +110,7 @@
  private:
   ImageFrameGenerator(const SkISize& fullSize,
                       bool isMultiFrame,
-                      ImageDecoder::ColorSpaceOption decoderColorSpaceOption,
-                      sk_sp<SkColorSpace> decoderTargetColorSpace);
+                      const ColorBehavior&);
 
   friend class ImageFrameGeneratorTest;
   friend class DeferredImageDecoderTest;
@@ -141,8 +138,7 @@
   const SkISize m_fullSize;
 
   // Parameters used to create internal ImageDecoder objects.
-  const ImageDecoder::ColorSpaceOption m_decoderColorSpaceOption;
-  const sk_sp<SkColorSpace> m_decoderTargetColorSpace;
+  const ColorBehavior m_decoderColorBehavior;
 
   const bool m_isMultiFrame;
   bool m_decodeFailed;
diff --git a/third_party/WebKit/Source/platform/graphics/ImageFrameGeneratorTest.cpp b/third_party/WebKit/Source/platform/graphics/ImageFrameGeneratorTest.cpp
index 14b7534b..761deaa 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageFrameGeneratorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageFrameGeneratorTest.cpp
@@ -59,8 +59,8 @@
  public:
   void SetUp() override {
     ImageDecodingStore::instance().setCacheLimitInBytes(1024 * 1024);
-    m_generator = ImageFrameGenerator::create(
-        fullSize(), false, ImageDecoder::ColorSpaceIgnored, nullptr);
+    m_generator =
+        ImageFrameGenerator::create(fullSize(), false, ColorBehavior::ignore());
     m_data = SharedBuffer::create();
     m_segmentReader = SegmentReader::createFromSharedBuffer(m_data);
     useMockImageDecoderFactory();
@@ -111,8 +111,8 @@
     m_frameCount = count;
     if (count > 1) {
       m_generator.clear();
-      m_generator = ImageFrameGenerator::create(
-          fullSize(), true, ImageDecoder::ColorSpaceIgnored, nullptr);
+      m_generator = ImageFrameGenerator::create(fullSize(), true,
+                                                ColorBehavior::ignore());
       useMockImageDecoderFactory();
     }
   }
diff --git a/third_party/WebKit/Source/platform/graphics/ImageSource.cpp b/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
index 17ba96b..232b986 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageSource.cpp
@@ -58,14 +58,13 @@
   }
 
   if (RuntimeEnabledFeatures::colorCorrectRenderingEnabled()) {
-    m_decoder = DeferredImageDecoder::create(
-        data, allDataReceived, ImageDecoder::AlphaPremultiplied,
-        ImageDecoder::ColorSpaceTagged, nullptr);
+    m_decoder = DeferredImageDecoder::create(data, allDataReceived,
+                                             ImageDecoder::AlphaPremultiplied,
+                                             ColorBehavior::tag());
   } else {
     m_decoder = DeferredImageDecoder::create(
         data, allDataReceived, ImageDecoder::AlphaPremultiplied,
-        ImageDecoder::ColorSpaceTransformed,
-        ImageDecoder::globalTargetColorSpace());
+        ColorBehavior::transformToGlobalTarget());
   }
 
   // Insufficient data is not a failure.
diff --git a/third_party/WebKit/Source/platform/graphics/OffscreenCanvasFrameDispatcherImpl.cpp b/third_party/WebKit/Source/platform/graphics/OffscreenCanvasFrameDispatcherImpl.cpp
index 6abe6ea..cf252bf58 100644
--- a/third_party/WebKit/Source/platform/graphics/OffscreenCanvasFrameDispatcherImpl.cpp
+++ b/third_party/WebKit/Source/platform/graphics/OffscreenCanvasFrameDispatcherImpl.cpp
@@ -188,7 +188,7 @@
   frame.metadata.device_scale_factor = 1.0f;
 
   const gfx::Rect bounds(m_width, m_height);
-  const cc::RenderPassId renderPassId(1, 1);
+  const int renderPassId = 1;
   std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
   pass->SetAll(renderPassId, bounds, bounds, gfx::Transform(), false);
 
diff --git a/third_party/WebKit/Source/platform/graphics/PictureSnapshot.cpp b/third_party/WebKit/Source/platform/graphics/PictureSnapshot.cpp
index 1a48146..9786c30 100644
--- a/third_party/WebKit/Source/platform/graphics/PictureSnapshot.cpp
+++ b/third_party/WebKit/Source/platform/graphics/PictureSnapshot.cpp
@@ -67,7 +67,7 @@
         SegmentReader::createFromSkData(SkData::MakeWithoutCopy(data, length));
     std::unique_ptr<ImageDecoder> imageDecoder = ImageDecoder::create(
         segmentReader.release(), true, ImageDecoder::AlphaPremultiplied,
-        ImageDecoder::ColorSpaceIgnored, nullptr);
+        ColorBehavior::ignore());
     if (!imageDecoder)
       return nullptr;
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
index 06f0a0d..3e005441 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp
@@ -2751,9 +2751,8 @@
     // Attempt to get raw unpremultiplied image data.
     std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
         m_image->data(), true, ImageDecoder::AlphaNotPremultiplied,
-        ignoreColorSpace ? ImageDecoder::ColorSpaceIgnored
-                         : ImageDecoder::ColorSpaceTransformed,
-        ignoreColorSpace ? nullptr : ImageDecoder::globalTargetColorSpace()));
+        ignoreColorSpace ? ColorBehavior::ignore()
+                         : ColorBehavior::transformToGlobalTarget()));
     if (!decoder || !decoder->frameCount())
       return;
     ImageFrame* frame = decoder->frameBufferAtIndex(0);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
index 25a5bcd9f..fb47d824 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemClient.h
@@ -55,6 +55,20 @@
   // offsetFromLayoutObjectWithSubpixelAccumulation().
   virtual LayoutRect visualRect() const = 0;
 
+  // This is declared here instead of in LayoutObject for verifying the
+  // condition in DrawingRecorder.
+  // Returns true if the object itself will not generate any effective painted
+  // output no matter what size the object is. For example, this function can
+  // return false for an object whose size is currently 0x0 but would have
+  // effective painted output if it was set a non-empty size. It's used to skip
+  // unforced paint invalidation of LayoutObjects (which is when
+  // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or
+  // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint
+  // invalidations of empty areas covered by such objects.
+  virtual bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
+    return false;
+  }
+
   void setDisplayItemsUncached(
       PaintInvalidationReason reason = PaintInvalidationFull) const {
     m_cacheGenerationOrInvalidationReason.invalidate(reason);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
index 6781f1c..a6011c9 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingRecorder.cpp
@@ -79,9 +79,19 @@
          m_context.getPaintController().newDisplayItemList().size());
 #endif
 
+  sk_sp<const SkPicture> picture = m_context.endRecording();
+
+#if DCHECK_IS_ON()
+  if (!RuntimeEnabledFeatures::slimmingPaintStrictCullRectClippingEnabled() &&
+      !m_context.getPaintController().isForSkPictureBuilder() &&
+      m_displayItemClient.paintedOutputOfObjectHasNoEffectRegardlessOfSize()) {
+    DCHECK_EQ(0, picture->approximateOpCount())
+        << m_displayItemClient.debugName();
+  }
+#endif
+
   m_context.getPaintController().createAndAppend<DrawingDisplayItem>(
-      m_displayItemClient, m_displayItemType, m_context.endRecording(),
-      m_knownToBeOpaque);
+      m_displayItemClient, m_displayItemType, picture, m_knownToBeOpaque);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
index 8aaebc3..cde4c2d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
@@ -187,6 +187,10 @@
 
 #if DCHECK_IS_ON()
   void assertDisplayItemClientsAreLive();
+
+  enum Usage { ForNormalUsage, ForSkPictureBuilder };
+  void setUsage(Usage usage) { m_usage = usage; }
+  bool isForSkPictureBuilder() const { return m_usage == ForSkPictureBuilder; }
 #endif
 
   void setTracksRasterInvalidations(bool value);
@@ -351,6 +355,8 @@
 #if DCHECK_IS_ON()
   // This is used to check duplicated ids during createAndAppend().
   IndicesByClientMap m_newDisplayItemIndicesByClient;
+
+  Usage m_usage;
 #endif
 
   // These are set in useCachedDrawingIfPossible() and
diff --git a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
index cdc23e9b..3905add 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/SkPictureBuilder.cpp
@@ -26,6 +26,10 @@
     m_paintControllerPtr = PaintController::create();
     m_paintController = m_paintControllerPtr.get();
   }
+#if DCHECK_IS_ON()
+  m_paintController->setUsage(PaintController::ForSkPictureBuilder);
+#endif
+
   m_context = wrapUnique(
       new GraphicsContext(*m_paintController, disabledMode, metaData));
 
@@ -35,7 +39,11 @@
   }
 }
 
-SkPictureBuilder::~SkPictureBuilder() {}
+SkPictureBuilder::~SkPictureBuilder() {
+#if DCHECK_IS_ON()
+  m_paintController->setUsage(PaintController::ForNormalUsage);
+#endif
+}
 
 sk_sp<SkPicture> SkPictureBuilder::endRecording() {
   m_context->beginRecording(m_bounds);
diff --git a/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h b/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
index 0de03fc..8c64315 100644
--- a/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
+++ b/third_party/WebKit/Source/platform/graphics/test/MockImageDecoder.h
@@ -67,8 +67,7 @@
 
   MockImageDecoder(MockImageDecoderClient* client)
       : ImageDecoder(AlphaPremultiplied,
-                     ColorSpaceTransformed,
-                     targetColorSpaceForTesting(),
+                     ColorBehavior::transformToTargetForTesting(),
                      noDecodedImageByteLimit),
         m_client(client) {}
 
diff --git a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
index b999d30..f85668a 100644
--- a/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
+++ b/third_party/WebKit/Source/platform/heap/WrapperVisitor.h
@@ -163,8 +163,14 @@
   }
 
   virtual bool markWrapperHeader(HeapObjectHeader*) const = 0;
+
   virtual void markWrappersInAllWorlds(const ScriptWrappable*) const = 0;
-  virtual void markWrappersInAllWorlds(const void*) const = 0;
+
+  void markWrappersInAllWorlds(const void*) const {
+    // Empty handler used for WRAPPER_VISITOR_SPECIAL_CLASSES. These types
+    // don't require marking wrappers in all worlds, so just nop on those.
+  }
+
   virtual void pushToMarkingDeque(
       void (*traceWrappersCallback)(const WrapperVisitor*, const void*),
       HeapObjectHeader* (*heapObjectHeaderCallback)(const void*),
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index 99960457..a254a09 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -71,16 +71,9 @@
     PassRefPtr<SegmentReader> passData,
     bool dataComplete,
     AlphaOption alphaOption,
-    ColorSpaceOption colorOptions,
-    sk_sp<SkColorSpace> targetColorSpace) {
+    const ColorBehavior& colorBehavior) {
   RefPtr<SegmentReader> data = passData;
 
-  // Ensure that the color space options are consistent.
-  if (colorOptions == ColorSpaceTransformed)
-    DCHECK(targetColorSpace);
-  else
-    DCHECK(!targetColorSpace);
-
   // We need at least kLongestSignatureLength bytes to run the signature
   // matcher.
   if (data->size() < kLongestSignatureLength)
@@ -101,34 +94,28 @@
   std::unique_ptr<ImageDecoder> decoder;
   switch (sniffResult) {
     case SniffResult::JPEG:
-      decoder.reset(new JPEGImageDecoder(alphaOption, colorOptions,
-                                         std::move(targetColorSpace),
-                                         maxDecodedBytes));
+      decoder.reset(
+          new JPEGImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::PNG:
-      decoder.reset(new PNGImageDecoder(alphaOption, colorOptions,
-                                        std::move(targetColorSpace),
-                                        maxDecodedBytes));
+      decoder.reset(
+          new PNGImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::GIF:
-      decoder.reset(new GIFImageDecoder(alphaOption, colorOptions,
-                                        std::move(targetColorSpace),
-                                        maxDecodedBytes));
+      decoder.reset(
+          new GIFImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::WEBP:
-      decoder.reset(new WEBPImageDecoder(alphaOption, colorOptions,
-                                         std::move(targetColorSpace),
-                                         maxDecodedBytes));
+      decoder.reset(
+          new WEBPImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::ICO:
-      decoder.reset(new ICOImageDecoder(alphaOption, colorOptions,
-                                        std::move(targetColorSpace),
-                                        maxDecodedBytes));
+      decoder.reset(
+          new ICOImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::BMP:
-      decoder.reset(new BMPImageDecoder(alphaOption, colorOptions,
-                                        std::move(targetColorSpace),
-                                        maxDecodedBytes));
+      decoder.reset(
+          new BMPImageDecoder(alphaOption, colorBehavior, maxDecodedBytes));
       break;
     case SniffResult::Invalid:
       break;
@@ -509,65 +496,6 @@
   return m_rowBytes[i];
 }
 
-namespace {
-
-// The output device color space is global and shared across multiple threads.
-SpinLock gTargetColorSpaceLock;
-SkColorSpace* gTargetColorSpace = nullptr;
-
-}  // namespace
-
-// static
-void ImageDecoder::setGlobalTargetColorProfile(const WebVector<char>& profile) {
-  if (profile.isEmpty())
-    return;
-
-  // Take a lock around initializing and accessing the global device color
-  // profile.
-  SpinLock::Guard guard(gTargetColorSpaceLock);
-
-  // Layout tests expect that only the first call will take effect.
-  if (gTargetColorSpace)
-    return;
-
-  gTargetColorSpace =
-      SkColorSpace::MakeICC(profile.data(), profile.size()).release();
-
-  // UMA statistics.
-  BitmapImageMetrics::countOutputGamma(gTargetColorSpace);
-}
-
-// static
-sk_sp<SkColorSpace> ImageDecoder::globalTargetColorSpace() {
-  // Take a lock around initializing and accessing the global device color
-  // profile.
-  SpinLock::Guard guard(gTargetColorSpaceLock);
-
-  // Initialize the output device profile to sRGB if it has not yet been
-  // initialized.
-  if (!gTargetColorSpace) {
-    gTargetColorSpace =
-        SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named).release();
-  }
-
-  gTargetColorSpace->ref();
-  return sk_sp<SkColorSpace>(gTargetColorSpace);
-}
-
-// static
-sk_sp<SkColorSpace> ImageDecoder::targetColorSpaceForTesting() {
-  return globalTargetColorSpace();
-}
-
-sk_sp<SkColorSpace> ImageDecoder::colorSpaceForSkImages() const {
-  if (m_colorSpaceOption != ColorSpaceTagged)
-    return nullptr;
-
-  if (m_embeddedColorSpace)
-    return m_embeddedColorSpace;
-  return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
-}
-
 void ImageDecoder::setEmbeddedColorProfile(const char* iccData,
                                            unsigned iccLength) {
   sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(iccData, iccLength);
@@ -590,23 +518,34 @@
   m_sourceToTargetColorTransformNeedsUpdate = false;
   m_sourceToTargetColorTransform = nullptr;
 
-  // With color correct rendering, we do not transform to the output color space
-  // at decode time.  Instead, we tag the raw image pixels and pass the tagged
-  // SkImage to Skia.
-  if (RuntimeEnabledFeatures::colorCorrectRenderingEnabled())
+  if (!m_colorBehavior.isTransformToTargetColorSpace())
     return nullptr;
 
-  if (!m_embeddedColorSpace)
-    return nullptr;
+  sk_sp<SkColorSpace> srcColorSpace = m_embeddedColorSpace;
+  if (!srcColorSpace) {
+    if (RuntimeEnabledFeatures::colorCorrectRenderingEnabled())
+      srcColorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+    else
+      return nullptr;
+  }
 
   if (SkColorSpace::Equals(m_embeddedColorSpace.get(),
-                           m_targetColorSpace.get())) {
+                           m_colorBehavior.targetColorSpace().get())) {
     return nullptr;
   }
 
   m_sourceToTargetColorTransform = SkColorSpaceXform::New(
-      m_embeddedColorSpace.get(), m_targetColorSpace.get());
+      m_embeddedColorSpace.get(), m_colorBehavior.targetColorSpace().get());
   return m_sourceToTargetColorTransform.get();
 }
 
+sk_sp<SkColorSpace> ImageDecoder::colorSpaceForSkImages() const {
+  if (!m_colorBehavior.isTag())
+    return nullptr;
+
+  if (m_embeddedColorSpace)
+    return m_embeddedColorSpace;
+  return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
index 815c946c..85e1649 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
@@ -31,6 +31,7 @@
 #include "SkColorSpaceXform.h"
 #include "platform/PlatformExport.h"
 #include "platform/SharedBuffer.h"
+#include "platform/graphics/ColorBehavior.h"
 #include "platform/graphics/ImageOrientation.h"
 #include "platform/image-decoders/ImageAnimation.h"
 #include "platform/image-decoders/ImageFrame.h"
@@ -86,23 +87,6 @@
 
   enum AlphaOption { AlphaPremultiplied, AlphaNotPremultiplied };
 
-  enum ColorSpaceOption {
-    // The embedded color profile is ignored entirely. The hasEmbeddedColorSpace
-    // method will always return false. No transformations are applied to the
-    // pixel data. SkImages created will have no assocated SkColorSpace.
-    ColorSpaceIgnored,
-    // The image will be transformed to the specified target color space. The
-    // hasEmbeddedColorSpace method will return the truth. SkImages created by
-    // this decoder will have no associated SkColorSpace.
-    // TODO(ccameron): This transform is applied only if the image has an
-    // embedded color profile, but should be applied always.
-    ColorSpaceTransformed,
-    // The image will not be transformed (to the extent possible). SkImages
-    // created by this decoder will have the SkColorSpace of the embedded
-    // color profile (or sRGB if there was no embedded color profile).
-    ColorSpaceTagged,
-  };
-
   virtual ~ImageDecoder() {}
 
   // Returns a caller-owned decoder of the appropriate type.  Returns nullptr if
@@ -112,16 +96,14 @@
   static std::unique_ptr<ImageDecoder> create(PassRefPtr<SegmentReader> data,
                                               bool dataComplete,
                                               AlphaOption,
-                                              ColorSpaceOption,
-                                              sk_sp<SkColorSpace>);
+                                              const ColorBehavior&);
   static std::unique_ptr<ImageDecoder> create(
       PassRefPtr<SharedBuffer> data,
       bool dataComplete,
       AlphaOption alphaoption,
-      ColorSpaceOption colorOptions,
-      sk_sp<SkColorSpace> targetColorSpace) {
+      const ColorBehavior& colorBehavior) {
     return create(SegmentReader::createFromSharedBuffer(std::move(data)),
-                  dataComplete, alphaoption, colorOptions, targetColorSpace);
+                  dataComplete, alphaoption, colorBehavior);
   }
 
   virtual String filenameExtension() const = 0;
@@ -224,20 +206,8 @@
 
   ImageOrientation orientation() const { return m_orientation; }
 
-  bool ignoresColorSpace() const {
-    return m_colorSpaceOption == ColorSpaceIgnored;
-  }
-  ColorSpaceOption colorSpaceOption() const { return m_colorSpaceOption; }
-  sk_sp<SkColorSpace> targetColorSpace() const { return m_targetColorSpace; }
-
-  // Set the target color profile into which all images with embedded color
-  // profiles should be converted. Note that only the first call to this
-  // function in this process has any effect.
-  static void setGlobalTargetColorProfile(const WebVector<char>&);
-  static sk_sp<SkColorSpace> globalTargetColorSpace();
-
-  // A target color space to be used by tests.
-  static sk_sp<SkColorSpace> targetColorSpaceForTesting();
+  bool ignoresColorSpace() const { return m_colorBehavior.isIgnore(); }
+  const ColorBehavior& colorBehavior() const { return m_colorBehavior; }
 
   // This returns the color space that will be included in the SkImageInfo of
   // SkImages created from this decoder. This will be nullptr unless the
@@ -304,12 +274,10 @@
 
  protected:
   ImageDecoder(AlphaOption alphaOption,
-               ColorSpaceOption colorOptions,
-               sk_sp<SkColorSpace> targetColorSpace,
+               const ColorBehavior& colorBehavior,
                size_t maxDecodedBytes)
       : m_premultiplyAlpha(alphaOption == AlphaPremultiplied),
-        m_colorSpaceOption(colorOptions),
-        m_targetColorSpace(std::move(targetColorSpace)),
+        m_colorBehavior(colorBehavior),
         m_maxDecodedBytes(maxDecodedBytes),
         m_purgeAggressively(false) {}
 
@@ -385,8 +353,7 @@
   RefPtr<SegmentReader> m_data;  // The encoded data.
   Vector<ImageFrame, 1> m_frameBufferCache;
   const bool m_premultiplyAlpha;
-  const ColorSpaceOption m_colorSpaceOption;
-  const sk_sp<SkColorSpace> m_targetColorSpace;
+  const ColorBehavior m_colorBehavior;
   ImageOrientation m_orientation;
 
   // The maximum amount of memory a decoded image should require. Ideally,
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoderTest.cpp
index bcfbf54..870a9a6 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoderTest.cpp
@@ -42,8 +42,7 @@
  public:
   TestImageDecoder()
       : ImageDecoder(AlphaNotPremultiplied,
-                     ColorSpaceTransformed,
-                     targetColorSpaceForTesting(),
+                     ColorBehavior::transformToTargetForTesting(),
                      noDecodedImageByteLimit) {}
 
   String filenameExtension() const override { return ""; }
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
index b710b7a8..3b90c5d 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
@@ -41,13 +41,9 @@
 static const size_t sizeOfFileHeader = 14;
 
 BMPImageDecoder::BMPImageDecoder(AlphaOption alphaOption,
-                                 ColorSpaceOption colorOptions,
-                                 sk_sp<SkColorSpace> targetColorSpace,
+                                 const ColorBehavior& colorBehavior,
                                  size_t maxDecodedBytes)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes),
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
       m_decodedOffset(0) {}
 
 void BMPImageDecoder::onSetData(SegmentReader* data) {
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
index aec7045..9f38fc1 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.h
@@ -39,10 +39,7 @@
 // This class decodes the BMP image format.
 class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
  public:
-  BMPImageDecoder(AlphaOption,
-                  ColorSpaceOption,
-                  sk_sp<SkColorSpace>,
-                  size_t maxDecodedBytes);
+  BMPImageDecoder(AlphaOption, const ColorBehavior&, size_t maxDecodedBytes);
 
   // ImageDecoder:
   String filenameExtension() const override { return "bmp"; }
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoderTest.cpp
index 75d48efb..1d3c05f6 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoderTest.cpp
@@ -15,10 +15,10 @@
 namespace {
 
 std::unique_ptr<ImageDecoder> createDecoder() {
-  return wrapUnique(new BMPImageDecoder(
-      ImageDecoder::AlphaNotPremultiplied, ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting(),
-      ImageDecoder::noDecodedImageByteLimit));
+  return wrapUnique(
+      new BMPImageDecoder(ImageDecoder::AlphaNotPremultiplied,
+                          ColorBehavior::transformToTargetForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
 }
 
 }  // anonymous namespace
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
index 54399fc..fbb791e 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -33,13 +33,9 @@
 namespace blink {
 
 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption,
-                                 ColorSpaceOption colorOptions,
-                                 sk_sp<SkColorSpace> targetColorSpace,
+                                 const ColorBehavior& colorBehavior,
                                  size_t maxDecodedBytes)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes),
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
       m_repetitionCount(cAnimationLoopOnce) {}
 
 GIFImageDecoder::~GIFImageDecoder() {}
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
index 0f68eb0..e84b1b4 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h
@@ -41,10 +41,7 @@
   WTF_MAKE_NONCOPYABLE(GIFImageDecoder);
 
  public:
-  GIFImageDecoder(AlphaOption,
-                  ColorSpaceOption,
-                  sk_sp<SkColorSpace>,
-                  size_t maxDecodedBytes);
+  GIFImageDecoder(AlphaOption, const ColorBehavior&, size_t maxDecodedBytes);
   ~GIFImageDecoder() override;
 
   enum GIFParseQuery { GIFSizeQuery, GIFFrameCountQuery };
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
index da11d072..a2233b50 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
@@ -46,10 +46,10 @@
 const char layoutTestResourcesDir[] = "LayoutTests/images/resources";
 
 std::unique_ptr<ImageDecoder> createDecoder() {
-  return wrapUnique(new GIFImageDecoder(
-      ImageDecoder::AlphaNotPremultiplied, ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting(),
-      ImageDecoder::noDecodedImageByteLimit));
+  return wrapUnique(
+      new GIFImageDecoder(ImageDecoder::AlphaNotPremultiplied,
+                          ColorBehavior::transformToTargetForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
 }
 
 void testRepetitionCount(const char* dir,
@@ -367,15 +367,14 @@
   RefPtr<SharedBuffer> partialData =
       SharedBuffer::create(fullData->data(), kTruncateSize);
 
-  std::unique_ptr<ImageDecoder> premulDecoder = wrapUnique(new GIFImageDecoder(
-      ImageDecoder::AlphaPremultiplied, ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting(),
-      ImageDecoder::noDecodedImageByteLimit));
-  std::unique_ptr<ImageDecoder> unpremulDecoder =
-      wrapUnique(new GIFImageDecoder(ImageDecoder::AlphaNotPremultiplied,
-                                     ImageDecoder::ColorSpaceTransformed,
-                                     ImageDecoder::targetColorSpaceForTesting(),
-                                     ImageDecoder::noDecodedImageByteLimit));
+  std::unique_ptr<ImageDecoder> premulDecoder = wrapUnique(
+      new GIFImageDecoder(ImageDecoder::AlphaPremultiplied,
+                          ColorBehavior::transformToTargetForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
+  std::unique_ptr<ImageDecoder> unpremulDecoder = wrapUnique(
+      new GIFImageDecoder(ImageDecoder::AlphaNotPremultiplied,
+                          ColorBehavior::transformToTargetForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
 
   // Partially decoded frame => the frame alpha type is unknown and should
   // reflect the requested format.
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
index 80501882..7a6a188 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
@@ -43,17 +43,13 @@
 static const size_t sizeOfDirEntry = 16;
 
 ICOImageDecoder::ICOImageDecoder(AlphaOption alphaOption,
-                                 ColorSpaceOption colorOptions,
-                                 sk_sp<SkColorSpace> targetColorSpace,
+                                 const ColorBehavior& colorBehavior,
                                  size_t maxDecodedBytes)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes),
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
       m_fastReader(nullptr),
       m_decodedOffset(0),
       m_dirEntriesCount(0),
-      m_colorSpaceOption(colorOptions) {}
+      m_colorBehavior(colorBehavior) {}
 
 ICOImageDecoder::~ICOImageDecoder() {}
 
@@ -216,8 +212,8 @@
     AlphaOption alphaOption =
         m_premultiplyAlpha ? AlphaPremultiplied : AlphaNotPremultiplied;
     m_pngDecoders[index] = wrapUnique(
-        new PNGImageDecoder(alphaOption, m_colorSpaceOption, m_targetColorSpace,
-                            m_maxDecodedBytes, dirEntry.m_imageOffset));
+        new PNGImageDecoder(alphaOption, m_colorBehavior, m_maxDecodedBytes,
+                            dirEntry.m_imageOffset));
     setDataForPNGDecoderAtIndex(index);
   }
   // Fail if the size the PNGImageDecoder calculated does not match the size
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
index 1b93fab..a24bdece4 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.h
@@ -44,10 +44,7 @@
   WTF_MAKE_NONCOPYABLE(ICOImageDecoder);
 
  public:
-  ICOImageDecoder(AlphaOption,
-                  ColorSpaceOption,
-                  sk_sp<SkColorSpace>,
-                  size_t maxDecodedBytes);
+  ICOImageDecoder(AlphaOption, const ColorBehavior&, size_t maxDecodedBytes);
   ~ICOImageDecoder() override;
 
   // ImageDecoder:
@@ -181,7 +178,7 @@
   IntSize m_frameSize;
 
   // Used to pass on to an internally created PNG decoder.
-  const ColorSpaceOption m_colorSpaceOption;
+  const ColorBehavior m_colorBehavior;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoderTest.cpp
index 9b83e69..a219161 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoderTest.cpp
@@ -14,10 +14,10 @@
 namespace {
 
 std::unique_ptr<ImageDecoder> createDecoder() {
-  return wrapUnique(new ICOImageDecoder(
-      ImageDecoder::AlphaNotPremultiplied, ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting(),
-      ImageDecoder::noDecodedImageByteLimit));
+  return wrapUnique(
+      new ICOImageDecoder(ImageDecoder::AlphaNotPremultiplied,
+                          ColorBehavior::transformToTargetForTesting(),
+                          ImageDecoder::noDecodedImageByteLimit));
 }
 }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
index ef2397f..e49a211d 100644
--- a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
@@ -710,13 +710,9 @@
 }
 
 JPEGImageDecoder::JPEGImageDecoder(AlphaOption alphaOption,
-                                   ColorSpaceOption colorOptions,
-                                   sk_sp<SkColorSpace> targetColorSpace,
+                                   const ColorBehavior& colorBehavior,
                                    size_t maxDecodedBytes)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes) {}
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes) {}
 
 JPEGImageDecoder::~JPEGImageDecoder() {}
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.h
index 8eef42b..d65014a 100644
--- a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.h
@@ -37,10 +37,7 @@
   WTF_MAKE_NONCOPYABLE(JPEGImageDecoder);
 
  public:
-  JPEGImageDecoder(AlphaOption,
-                   ColorSpaceOption,
-                   sk_sp<SkColorSpace>,
-                   size_t maxDecodedBytes);
+  JPEGImageDecoder(AlphaOption, const ColorBehavior&, size_t maxDecodedBytes);
   ~JPEGImageDecoder() override;
 
   // ImageDecoder:
diff --git a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoderTest.cpp
index 6454b041..21c6bb4 100644
--- a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoderTest.cpp
@@ -48,8 +48,8 @@
 
 std::unique_ptr<ImageDecoder> createDecoder(size_t maxDecodedBytes) {
   return wrapUnique(new JPEGImageDecoder(
-      ImageDecoder::AlphaNotPremultiplied, ImageDecoder::ColorSpaceTransformed,
-      ImageDecoder::targetColorSpaceForTesting(), maxDecodedBytes));
+      ImageDecoder::AlphaNotPremultiplied,
+      ColorBehavior::transformToTargetForTesting(), maxDecodedBytes));
 }
 
 std::unique_ptr<ImageDecoder> createDecoder() {
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
index 8e6e19d01..0163a70 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp
@@ -46,14 +46,10 @@
 namespace blink {
 
 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption,
-                                 ColorSpaceOption colorOptions,
-                                 sk_sp<SkColorSpace> targetColorSpace,
+                                 const ColorBehavior& colorBehavior,
                                  size_t maxDecodedBytes,
                                  size_t offset)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes),
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
       m_offset(offset) {}
 
 PNGImageDecoder::~PNGImageDecoder() {}
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
index dacc938..a2e3d681 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.h
@@ -38,8 +38,7 @@
 
  public:
   PNGImageDecoder(AlphaOption,
-                  ColorSpaceOption,
-                  sk_sp<SkColorSpace>,
+                  const ColorBehavior&,
                   size_t maxDecodedBytes,
                   size_t offset = 0);
   ~PNGImageDecoder() override;
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
index f03406d7..8b6b2160 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp
@@ -14,10 +14,9 @@
 
 std::unique_ptr<ImageDecoder> createDecoder(
     ImageDecoder::AlphaOption alphaOption) {
-  return wrapUnique(
-      new PNGImageDecoder(alphaOption, ImageDecoder::ColorSpaceTransformed,
-                          ImageDecoder::targetColorSpaceForTesting(),
-                          ImageDecoder::noDecodedImageByteLimit));
+  return wrapUnique(new PNGImageDecoder(
+      alphaOption, ColorBehavior::transformToTargetForTesting(),
+      ImageDecoder::noDecodedImageByteLimit));
 }
 
 std::unique_ptr<ImageDecoder> createDecoder() {
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
index bbfec45..c9045baf 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
@@ -117,13 +117,9 @@
 namespace blink {
 
 WEBPImageDecoder::WEBPImageDecoder(AlphaOption alphaOption,
-                                   ColorSpaceOption colorOptions,
-                                   sk_sp<SkColorSpace> targetColorSpace,
+                                   const ColorBehavior& colorBehavior,
                                    size_t maxDecodedBytes)
-    : ImageDecoder(alphaOption,
-                   colorOptions,
-                   std::move(targetColorSpace),
-                   maxDecodedBytes),
+    : ImageDecoder(alphaOption, colorBehavior, maxDecodedBytes),
       m_decoder(0),
       m_formatFlags(0),
       m_frameBackgroundHasAlpha(false),
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
index 946d7cc..ee1e2a9 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.h
@@ -41,10 +41,7 @@
   WTF_MAKE_NONCOPYABLE(WEBPImageDecoder);
 
  public:
-  WEBPImageDecoder(AlphaOption,
-                   ColorSpaceOption,
-                   sk_sp<SkColorSpace>,
-                   size_t maxDecodedBytes);
+  WEBPImageDecoder(AlphaOption, const ColorBehavior&, size_t maxDecodedBytes);
   ~WEBPImageDecoder() override;
 
   // ImageDecoder:
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
index 1744ca2a..4b185c6 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoderTest.cpp
@@ -47,10 +47,9 @@
 
 std::unique_ptr<ImageDecoder> createDecoder(
     ImageDecoder::AlphaOption alphaOption) {
-  return wrapUnique(
-      new WEBPImageDecoder(alphaOption, ImageDecoder::ColorSpaceTransformed,
-                           ImageDecoder::targetColorSpaceForTesting(),
-                           ImageDecoder::noDecodedImageByteLimit));
+  return wrapUnique(new WEBPImageDecoder(
+      alphaOption, ColorBehavior::transformToTargetForTesting(),
+      ImageDecoder::noDecodedImageByteLimit));
 }
 
 std::unique_ptr<ImageDecoder> createDecoder() {
diff --git a/third_party/WebKit/Source/platform/image-encoders/ImageEncoderUtils.cpp b/third_party/WebKit/Source/platform/image-encoders/ImageEncoderUtils.cpp
index 4c758a91..b7411bd 100644
--- a/third_party/WebKit/Source/platform/image-encoders/ImageEncoderUtils.cpp
+++ b/third_party/WebKit/Source/platform/image-encoders/ImageEncoderUtils.cpp
@@ -5,7 +5,7 @@
 #include "platform/image-encoders/ImageEncoderUtils.h"
 
 #include "platform/Histogram.h"
-#include "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "wtf/Threading.h"
 
 namespace blink {
diff --git a/third_party/WebKit/Source/platform/mhtml/MHTMLArchive.cpp b/third_party/WebKit/Source/platform/mhtml/MHTMLArchive.cpp
index 6a852fe..29b7817 100644
--- a/third_party/WebKit/Source/platform/mhtml/MHTMLArchive.cpp
+++ b/third_party/WebKit/Source/platform/mhtml/MHTMLArchive.cpp
@@ -31,11 +31,11 @@
 #include "platform/mhtml/MHTMLArchive.h"
 
 #include "platform/DateComponents.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/SerializedResource.h"
 #include "platform/SharedBuffer.h"
 #include "platform/mhtml/ArchiveResource.h"
 #include "platform/mhtml/MHTMLParser.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/text/QuotedPrintable.h"
 #include "platform/weborigin/SchemeRegistry.h"
 #include "wtf/Assertions.h"
diff --git a/third_party/WebKit/Source/platform/mojo/CommonCustomTypes.typemap b/third_party/WebKit/Source/platform/mojo/CommonCustomTypes.typemap
deleted file mode 100644
index 3c3b1bb..0000000
--- a/third_party/WebKit/Source/platform/mojo/CommonCustomTypes.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//mojo/common/common_custom_types.mojom"
-public_headers = [ "//third_party/WebKit/Source/wtf/text/WTFString.h" ]
-traits_headers = [ "//third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h" ]
-deps = [
-  "//mojo/public/cpp/bindings",
-]
-type_mappings =
-    [ "mojo.common.mojom.String16=WTF::String[nullable_is_same_type]" ]
diff --git a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
index 40595811..0b4bc5c 100644
--- a/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h
@@ -6,7 +6,7 @@
 #define CommonCustomTypesStructTraits_h
 
 #include "base/strings/string16.h"
-#include "mojo/common/common_custom_types.mojom-blink.h"
+#include "mojo/common/string16.mojom-blink.h"
 #include "platform/PlatformExport.h"
 
 namespace mojo {
diff --git a/third_party/WebKit/Source/platform/mojo/DEPS b/third_party/WebKit/Source/platform/mojo/DEPS
index e7afb601..ef35a2c 100644
--- a/third_party/WebKit/Source/platform/mojo/DEPS
+++ b/third_party/WebKit/Source/platform/mojo/DEPS
@@ -6,6 +6,6 @@
     "+base/strings/string16.h",
     "+mojo/public/cpp/bindings/binding.h",
     "+mojo/public/cpp/bindings/wtf_array.h",
-    "+mojo/common/common_custom_types.mojom-blink.h",
+    "+mojo/common/string16.mojom-blink.h",
     "+mojo/common/test_common_custom_types.mojom-blink.h",
 ]
diff --git a/third_party/WebKit/Source/platform/mojo/File.typemap b/third_party/WebKit/Source/platform/mojo/File.typemap
new file mode 100644
index 0000000..bc9b5d8f
--- /dev/null
+++ b/third_party/WebKit/Source/platform/mojo/File.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/file.mojom"
+public_headers = [ "//base/files/file.h" ]
+traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
+deps = [
+  "//mojo/common:struct_traits",
+]
+type_mappings =
+    [ "mojo.common.mojom.File=base::File[move_only,nullable_is_same_type]" ]
diff --git a/third_party/WebKit/Source/platform/mojo/String.typemap b/third_party/WebKit/Source/platform/mojo/String.typemap
new file mode 100644
index 0000000..91204738
--- /dev/null
+++ b/third_party/WebKit/Source/platform/mojo/String.typemap
@@ -0,0 +1,9 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/string16.mojom"
+public_headers = [ "//third_party/WebKit/Source/wtf/text/WTFString.h" ]
+traits_headers = [ "//third_party/WebKit/Source/platform/mojo/CommonCustomTypesStructTraits.h" ]
+type_mappings =
+    [ "mojo.common.mojom.String16=WTF::String[nullable_is_same_type]" ]
diff --git a/third_party/WebKit/Source/platform/mojo/blink_typemaps.gni b/third_party/WebKit/Source/platform/mojo/blink_typemaps.gni
index a7a0320..af47d17 100644
--- a/third_party/WebKit/Source/platform/mojo/blink_typemaps.gni
+++ b/third_party/WebKit/Source/platform/mojo/blink_typemaps.gni
@@ -3,8 +3,9 @@
 # found in the LICENSE file.
 
 typemaps = [
-  "//third_party/WebKit/Source/platform/mojo/CommonCustomTypes.typemap",
+  "//third_party/WebKit/Source/platform/mojo/File.typemap",
+  "//third_party/WebKit/Source/platform/mojo/Geometry.typemap",
   "//third_party/WebKit/Source/platform/mojo/KURL.typemap",
   "//third_party/WebKit/Source/platform/mojo/SecurityOrigin.typemap",
-  "//third_party/WebKit/Source/platform/mojo/Geometry.typemap",
+  "//third_party/WebKit/Source/platform/mojo/String.typemap",
 ]
diff --git a/third_party/WebKit/Source/platform/network/ResourceError.cpp b/third_party/WebKit/Source/platform/network/ResourceError.cpp
index 6cd3966..7c6e304 100644
--- a/third_party/WebKit/Source/platform/network/ResourceError.cpp
+++ b/third_party/WebKit/Source/platform/network/ResourceError.cpp
@@ -26,6 +26,7 @@
 
 #include "platform/network/ResourceError.h"
 
+#include "platform/network/ResourceRequest.h"
 #include "platform/weborigin/KURL.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebURL.h"
@@ -40,9 +41,12 @@
 }
 
 ResourceError ResourceError::cancelledDueToAccessCheckError(
-    const String& failingURL) {
+    const String& failingURL,
+    ResourceRequestBlockedReason blockedReason) {
   ResourceError error = cancelledError(failingURL);
   error.setIsAccessCheck(true);
+  if (blockedReason == ResourceRequestBlockedReason::SubresourceFilter)
+    error.setShouldCollapseInitiator(true);
   return error;
 }
 
diff --git a/third_party/WebKit/Source/platform/network/ResourceError.h b/third_party/WebKit/Source/platform/network/ResourceError.h
index 1d5715d..a359851 100644
--- a/third_party/WebKit/Source/platform/network/ResourceError.h
+++ b/third_party/WebKit/Source/platform/network/ResourceError.h
@@ -35,6 +35,8 @@
 
 namespace blink {
 
+enum class ResourceRequestBlockedReason;
+
 // Used for errors that won't be exposed to clients.
 PLATFORM_EXPORT extern const char errorDomainBlinkInternal[];
 
@@ -48,7 +50,9 @@
   };
 
   static ResourceError cancelledError(const String& failingURL);
-  static ResourceError cancelledDueToAccessCheckError(const String& failingURL);
+  static ResourceError cancelledDueToAccessCheckError(
+      const String& failingURL,
+      ResourceRequestBlockedReason);
 
   // Only for Blink internal usage.
   static ResourceError cacheMissError(const String& failingURL);
@@ -61,7 +65,8 @@
         m_isTimeout(false),
         m_staleCopyInCache(false),
         m_wasIgnoredByHandler(false),
-        m_isCacheMiss(false) {}
+        m_isCacheMiss(false),
+        m_shouldCollapseInitiator(false) {}
 
   ResourceError(const String& domain,
                 int errorCode,
@@ -77,7 +82,8 @@
         m_isTimeout(false),
         m_staleCopyInCache(false),
         m_wasIgnoredByHandler(false),
-        m_isCacheMiss(false) {}
+        m_isCacheMiss(false),
+        m_shouldCollapseInitiator(false) {}
 
   // Makes a deep copy. Useful for when you need to use a ResourceError on
   // another thread.
@@ -113,6 +119,11 @@
   void setIsCacheMiss(bool isCacheMiss) { m_isCacheMiss = isCacheMiss; }
   bool isCacheMiss() const { return m_isCacheMiss; }
 
+  void setShouldCollapseInitiator(bool shouldCollapseInitiator) {
+    m_shouldCollapseInitiator = shouldCollapseInitiator;
+  }
+  bool shouldCollapseInitiator() const { return m_shouldCollapseInitiator; }
+
   static bool compare(const ResourceError&, const ResourceError&);
 
  private:
@@ -127,6 +138,7 @@
   bool m_staleCopyInCache;
   bool m_wasIgnoredByHandler;
   bool m_isCacheMiss;
+  bool m_shouldCollapseInitiator;
 };
 
 inline bool operator==(const ResourceError& a, const ResourceError& b) {
diff --git a/third_party/WebKit/Source/platform/network/ResourceRequest.h b/third_party/WebKit/Source/platform/network/ResourceRequest.h
index c395911..4a2ecc5 100644
--- a/third_party/WebKit/Source/platform/network/ResourceRequest.h
+++ b/third_party/WebKit/Source/platform/network/ResourceRequest.h
@@ -45,14 +45,14 @@
 
 enum class WebCachePolicy;
 
-enum ResourceRequestBlockedReason {
-  ResourceRequestBlockedReasonCSP,
-  ResourceRequestBlockedReasonMixedContent,
-  ResourceRequestBlockedReasonOrigin,
-  ResourceRequestBlockedReasonInspector,
-  ResourceRequestBlockedReasonSubresourceFilter,
-  ResourceRequestBlockedReasonOther,
-  ResourceRequestBlockedReasonNone
+enum class ResourceRequestBlockedReason {
+  CSP,
+  MixedContent,
+  Origin,
+  Inspector,
+  SubresourceFilter,
+  Other,
+  None
 };
 
 enum InputToLoadPerfMetricReportPolicy {
diff --git a/third_party/WebKit/Source/platform/network/mime/ContentType.cpp b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
new file mode 100644
index 0000000..cc1fa9dc0
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc.  All rights reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ * Copyright (C) 2009 Google Inc.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
+ */
+
+#include "platform/network/mime/ContentType.h"
+
+namespace blink {
+
+ContentType::ContentType(const String& contentType) : m_type(contentType) {}
+
+String ContentType::parameter(const String& parameterName) const {
+  String parameterValue;
+  String strippedType = m_type.stripWhiteSpace();
+
+  // a MIME type can have one or more "param=value" after a semi-colon, and
+  // separated from each other by semi-colons
+  size_t semi = strippedType.find(';');
+  if (semi != kNotFound) {
+    size_t start =
+        strippedType.find(parameterName, semi + 1, TextCaseASCIIInsensitive);
+    if (start != kNotFound) {
+      start = strippedType.find('=', start + parameterName.length());
+      if (start != kNotFound) {
+        size_t quote = strippedType.find('\"', start + 1);
+        size_t end = strippedType.find('\"', start + 2);
+        if (quote != kNotFound && end != kNotFound) {
+          start = quote;
+        } else {
+          end = strippedType.find(';', start + 1);
+          if (end == kNotFound)
+            end = strippedType.length();
+        }
+        parameterValue = strippedType.substring(start + 1, end - (start + 1))
+                             .stripWhiteSpace();
+      }
+    }
+  }
+
+  return parameterValue;
+}
+
+String ContentType::type() const {
+  String strippedType = m_type.stripWhiteSpace();
+
+  // "type" can have parameters after a semi-colon, strip them
+  size_t semi = strippedType.find(';');
+  if (semi != kNotFound)
+    strippedType = strippedType.left(semi).stripWhiteSpace();
+
+  return strippedType;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/network/mime/ContentType.h b/third_party/WebKit/Source/platform/network/mime/ContentType.h
new file mode 100644
index 0000000..5af71092
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/mime/ContentType.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 Apple Inc.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
+ */
+
+#ifndef ContentType_h
+#define ContentType_h
+
+#include "platform/PlatformExport.h"
+#include "wtf/Allocator.h"
+#include "wtf/text/WTFString.h"
+
+namespace blink {
+
+// Parses the MIME type and parameter values based on RFC 2231.
+class PLATFORM_EXPORT ContentType {
+  STACK_ALLOCATED();
+
+ public:
+  explicit ContentType(const String& type);
+
+  String parameter(const String& parameterName) const;
+  String type() const;
+  const String& raw() const { return m_type; }
+
+ private:
+  String m_type;
+};
+
+}  // namespace blink
+
+#endif  // ContentType_h
diff --git a/third_party/WebKit/Source/platform/network/mime/MIMETypeFromURL.cpp b/third_party/WebKit/Source/platform/network/mime/MIMETypeFromURL.cpp
new file mode 100644
index 0000000..2532aa7
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/mime/MIMETypeFromURL.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
+ */
+
+#include "platform/network/mime/MIMETypeFromURL.h"
+
+#include "platform/network/mime/MIMETypeRegistry.h"
+#include "platform/weborigin/KURL.h"
+#include "wtf/text/WTFString.h"
+
+namespace blink {
+
+String mimeTypeFromDataURL(const String& url) {
+  DCHECK(protocolIs(url, "data"));
+  size_t index = url.find(';');
+  if (index == kNotFound)
+    index = url.find(',');
+  if (index != kNotFound) {
+    if (index > 5)
+      return url.substring(5, index - 5).lower();
+    // Data URLs with no MIME type are considered text/plain.
+    return "text/plain";
+  }
+  return "";
+}
+
+String mimeTypeFromURL(const KURL& url) {
+  String decodedPath = decodeURLEscapeSequences(url.path());
+  String extension = decodedPath.substring(decodedPath.reverseFind('.') + 1);
+
+  // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns
+  // "application/octet-stream" upon failure
+  return MIMETypeRegistry::getMIMETypeForExtension(extension);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/MIMETypeFromURL.h b/third_party/WebKit/Source/platform/network/mime/MIMETypeFromURL.h
similarity index 100%
rename from third_party/WebKit/Source/platform/MIMETypeFromURL.h
rename to third_party/WebKit/Source/platform/network/mime/MIMETypeFromURL.h
diff --git a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
index 28526a3..5a21f4ed 100644
--- a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
+++ b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.cpp
@@ -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 "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 
 #include "base/files/file_path.h"
 #include "base/strings/string_util.h"
diff --git a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.h b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.h
new file mode 100644
index 0000000..66d049b
--- /dev/null
+++ b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistry.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2006 Apple Inc.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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.
+ */
+
+#ifndef MIMETypeRegistry_h
+#define MIMETypeRegistry_h
+
+#include "platform/PlatformExport.h"
+#include "wtf/Allocator.h"
+#include "wtf/HashSet.h"
+#include "wtf/text/StringHash.h"
+#include "wtf/text/WTFString.h"
+
+namespace blink {
+
+// Note/reminder: MIME type and parameter names are per-RFC case
+// insensitive (https://www.ietf.org/rfc/rfc2045.txt , section 5.1).
+// The MIMETypeRegistry predicates are all case-insensitive.
+class PLATFORM_EXPORT MIMETypeRegistry {
+  STATIC_ONLY(MIMETypeRegistry);
+
+ public:
+  // For Media MIME type checks.
+  enum SupportsType { IsNotSupported, IsSupported, MayBeSupported };
+
+  static String getMIMETypeForExtension(const String& extension);
+  static String getWellKnownMIMETypeForExtension(const String& extension);
+  static String getMIMETypeForPath(const String& path);
+
+  // Checks to see if the given mime type is supported.
+  static bool isSupportedMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded inline as an
+  // image (e.g., <img> tags).
+  static bool isSupportedImageMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded as an image
+  // document in a frame.
+  static bool isSupportedImageResourceMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being displayed as an image.
+  static bool isSupportedImagePrefixedMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being encoded.
+  static bool isSupportedImageMIMETypeForEncoding(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded as a JavaScript
+  // resource.
+  static bool isSupportedJavaScriptMIMEType(const String& mimeType);
+
+  // Checks to see if a non-image mime type is suitable for being loaded as a
+  // document in a frame. Includes supported JavaScript MIME types.
+  static bool isSupportedNonImageMIMEType(const String& mimeType);
+
+  // Checks to see if the mime type and codecs are supported media MIME types.
+  static bool isSupportedMediaMIMEType(const String& mimeType,
+                                       const String& codecs);
+
+  // Does similar to isSupportedMediaMIMEType, but returns a little more
+  // detailed information in SupportsType enum.
+  static SupportsType supportsMediaMIMEType(const String& mimeType,
+                                            const String& codecs);
+
+  // Checks to see if the mime type and codecs are supported by the MediaSource
+  // implementation.
+  static bool isSupportedMediaSourceMIMEType(const String& mimeType,
+                                             const String& codecs);
+
+  // Checks to see if a mime type is a valid Java applet mime type
+  static bool isJavaAppletMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded as a stylesheet.
+  static bool isSupportedStyleSheetMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded as a font.
+  static bool isSupportedFontMIMEType(const String& mimeType);
+
+  // Checks to see if a mime type is suitable for being loaded as a text track.
+  static bool isSupportedTextTrackMIMEType(const String& mimeType);
+};
+
+}  // namespace blink
+
+#endif  // MIMETypeRegistry_h
diff --git a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistryTest.cpp b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistryTest.cpp
index 412ebd19..c7c98df1 100644
--- a/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistryTest.cpp
+++ b/third_party/WebKit/Source/platform/network/mime/MIMETypeRegistryTest.cpp
@@ -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 "platform/MIMETypeRegistry.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/third_party/WebKit/Source/platform/scheduler/DEPS b/third_party/WebKit/Source/platform/scheduler/DEPS
index 97fd186..b2f8c40 100644
--- a/third_party/WebKit/Source/platform/scheduler/DEPS
+++ b/third_party/WebKit/Source/platform/scheduler/DEPS
@@ -11,6 +11,7 @@
   "+base/debug/task_annotator.h",
   "+base/feature_list.h",
   "+base/format_macros.h",
+  "+base/gtest_prod_util.h",
   "+base/logging.h",
   "+base/message_loop/message_loop.h",
   "+base/metrics/field_trial.h",
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
index 752e9a1..9b15c9cc 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -487,6 +487,8 @@
 
   MainThreadOnly().renderer_backgrounded = true;
 
+  UpdatePolicy();
+
   base::TimeTicks now = tick_clock()->NowTicks();
   MainThreadOnly().foreground_main_thread_load_tracker.Pause(now);
   MainThreadOnly().background_main_thread_load_tracker.Resume(now);
@@ -513,6 +515,8 @@
   MainThreadOnly().renderer_backgrounded = false;
   MainThreadOnly().renderer_suspended = false;
 
+  UpdatePolicy();
+
   base::TimeTicks now = tick_clock()->NowTicks();
   MainThreadOnly().foreground_main_thread_load_tracker.Resume(now);
   MainThreadOnly().background_main_thread_load_tracker.Pause(now);
@@ -1073,6 +1077,11 @@
       ShouldDisableThrottlingBecauseOfAudio(now) ||
       MainThreadOnly().use_virtual_time;
 
+  // TODO(altimin): Consider adding default timer tq to background time
+  // budget pool.
+  if (MainThreadOnly().renderer_backgrounded)
+    new_policy.timer_queue_policy.time_domain_type = TimeDomainType::THROTTLED;
+
   // Tracing is done before the early out check, because it's quite possible we
   // will otherwise miss this information in traces.
   CreateTraceEventObjectSnapshotLocked();
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
index a9286e04..c305223 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_SCHEDULER_IMPL_H_
 
 #include "base/atomicops.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/trace_log.h"
@@ -201,6 +202,7 @@
   friend class RendererSchedulerImplTest;
   friend class RendererSchedulerImplForTest;
   friend class RenderWidgetSchedulingState;
+  FRIEND_TEST_ALL_PREFIXES(RendererSchedulerImplTest, Tracing);
 
   enum class ExpensiveTaskPolicy { RUN, BLOCK, THROTTLE };
 
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
index 9fd5973..bd05e458 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -13,10 +13,12 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "cc/output/begin_frame_args.h"
 #include "cc/test/ordered_simple_task_runner.h"
+#include "platform/WebTaskRunner.h"
 #include "platform/scheduler/base/test_time_source.h"
 #include "platform/scheduler/child/scheduler_tqm_delegate_for_test.h"
 #include "platform/scheduler/child/scheduler_tqm_delegate_impl.h"
 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -176,6 +178,26 @@
   *is_anticipated_after = scheduler->IsHighPriorityWorkAnticipated();
 }
 
+// RAII helper class to enable auto advancing of time inside mock task runner.
+// Automatically disables auto-advancement when destroyed.
+class ScopedAutoAdvanceNowEnabler {
+ public:
+  ScopedAutoAdvanceNowEnabler(
+      scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner)
+      : task_runner_(task_runner) {
+    task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  }
+
+  ~ScopedAutoAdvanceNowEnabler() {
+    task_runner_->SetAutoAdvanceNowToPendingTasks(false);
+  }
+
+ private:
+  scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedAutoAdvanceNowEnabler);
+};
+
 };  // namespace
 
 class RendererSchedulerImplForTest : public RendererSchedulerImpl {
@@ -319,7 +341,7 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
     // RunUntilIdle won't actually run all of the SimpleTestTickClock::Advance
     // tasks unless we set AutoAdvanceNow to true :/
-    mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+    ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
 
     // Simulate a bunch of expensive tasks
     for (int i = 0; i < 10; i++) {
@@ -330,10 +352,6 @@
     }
 
     RunUntilIdle();
-
-    // Switch back to not auto-advancing because we want to be in control of
-    // when time advances.
-    mock_task_runner_->SetAutoAdvanceNowToPendingTasks(false);
   }
 
   enum class TouchEventPolicy {
@@ -719,7 +737,7 @@
 }
 
 TEST_F(RendererSchedulerImplTest, TestIdleTaskExceedsDeadline) {
-  mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
   int run_count = 0;
 
   // Post two UpdateClockToDeadlineIdleTestTask tasks.
@@ -1694,7 +1712,7 @@
 
 TEST_F(RendererSchedulerImplWithMockSchedulerTest,
        OnePendingDelayedAndOneUrgentUpdatePolicy) {
-  mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
 
   mock_scheduler_->ScheduleDelayedPolicyUpdate(
       clock_->NowTicks(), base::TimeDelta::FromMilliseconds(1));
@@ -1708,7 +1726,7 @@
 
 TEST_F(RendererSchedulerImplWithMockSchedulerTest,
        OneUrgentAndOnePendingDelayedUpdatePolicy) {
-  mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
 
   mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
   mock_scheduler_->ScheduleDelayedPolicyUpdate(
@@ -1833,7 +1851,7 @@
 
 TEST_F(RendererSchedulerImplWithMockSchedulerTest,
        EnsureUpdatePolicyNotTriggeredTooOften) {
-  mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
 
   EXPECT_EQ(0, mock_scheduler_->update_policy_count_);
   scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
@@ -2010,7 +2028,7 @@
 }
 
 TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodRepeating) {
-  mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
   std::vector<base::TimeTicks> actual_deadlines;
   int run_count = 0;
 
@@ -2251,6 +2269,8 @@
 }
 
 TEST_F(RendererSchedulerImplTest, ResumeRenderer) {
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
   // Assume that the renderer is backgrounded.
   scheduler_->OnRendererBackgrounded();
 
@@ -2340,26 +2360,35 @@
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "T1 T2");
 
+  base::TimeTicks now;
+
   // The background signal will not immediately suspend the timer queue.
   scheduler_->OnRendererBackgrounded();
+  now += base::TimeDelta::FromMilliseconds(1100);
+  clock_->SetNowTicks(now);
   RunUntilIdle();
   EXPECT_THAT(run_order,
               testing::ElementsAre(std::string("T1"), std::string("T2")));
 
   run_order.clear();
   PostTestTasks(&run_order, "T3");
+
+  now += base::TimeDelta::FromSeconds(1);
+  clock_->SetNowTicks(now);
   RunUntilIdle();
   EXPECT_THAT(run_order, testing::ElementsAre(std::string("T3")));
 
   // Advance the time until after the scheduled timer queue suspension.
+  now = base::TimeTicks() + suspend_timers_when_backgrounded_delay() +
+        base::TimeDelta::FromMilliseconds(10);
   run_order.clear();
-  clock_->Advance(suspend_timers_when_backgrounded_delay() +
-                  base::TimeDelta::FromMilliseconds(10));
+  clock_->SetNowTicks(now);
   RunUntilIdle();
   ASSERT_TRUE(run_order.empty());
 
   // Timer tasks should be suspended until the foregrounded signal.
   PostTestTasks(&run_order, "T4 T5");
+  now += base::TimeDelta::FromSeconds(10);
   RunUntilIdle();
   EXPECT_TRUE(run_order.empty());
 
@@ -3518,5 +3547,79 @@
             scheduler_->GetVirtualTimeDomain());
 }
 
+TEST_F(RendererSchedulerImplTest, Tracing) {
+  // This test sets renderer scheduler to some non-trivial state
+  // (by posting tasks, creating child schedulers, etc) and converts it into a
+  // traced value. This test checks that no internal checks fire during this.
+
+  std::unique_ptr<WebViewSchedulerImpl> web_view_scheduler1 = base::WrapUnique(
+      new WebViewSchedulerImpl(nullptr, nullptr, scheduler_.get(), false));
+  scheduler_->AddWebViewScheduler(web_view_scheduler1.get());
+
+  std::unique_ptr<WebFrameSchedulerImpl> web_frame_scheduler =
+      web_view_scheduler1->createWebFrameSchedulerImpl(nullptr);
+
+  std::unique_ptr<WebViewSchedulerImpl> web_view_scheduler2 = base::WrapUnique(
+      new WebViewSchedulerImpl(nullptr, nullptr, scheduler_.get(), false));
+  scheduler_->AddWebViewScheduler(web_view_scheduler2.get());
+
+  TaskQueueThrottler::TimeBudgetPool* time_budget_pool =
+      scheduler_->task_queue_throttler()->CreateTimeBudgetPool(
+          "test", base::nullopt, base::nullopt);
+
+  time_budget_pool->AddQueue(base::TimeTicks(),
+                             scheduler_->TimerTaskRunner().get());
+
+  scheduler_->TimerTaskRunner()->PostTask(FROM_HERE, base::Bind(NullTask));
+
+  web_frame_scheduler->loadingTaskRunner()->postDelayedTask(
+      FROM_HERE, base::Bind(NullTask), 10);
+
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat> value =
+      scheduler_->AsValue(base::TimeTicks());
+  EXPECT_TRUE(value);
+}
+
+namespace {
+
+void RecordingTimeTestTask(std::vector<base::TimeTicks>* run_times,
+                           base::SimpleTestTickClock* clock) {
+  run_times->push_back(clock->NowTicks());
+}
+
+}  // namespace
+
+TEST_F(RendererSchedulerImplTest,
+       DefaultTimerTasksAreThrottledWhenBackgrounded) {
+  ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+  scheduler_->OnRendererBackgrounded();
+
+  std::vector<base::TimeTicks> run_times;
+
+  timer_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&RecordingTimeTestTask, &run_times, clock_.get()));
+
+  mock_task_runner_->RunUntilTime(base::TimeTicks() +
+                                  base::TimeDelta::FromMilliseconds(1100));
+
+  EXPECT_THAT(run_times, testing::ElementsAre(base::TimeTicks() +
+                                              base::TimeDelta::FromSeconds(1)));
+  run_times.clear();
+
+  timer_task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&RecordingTimeTestTask, &run_times, clock_.get()),
+      base::TimeDelta::FromMilliseconds(200));
+
+  scheduler_->OnRendererForegrounded();
+
+  mock_task_runner_->RunUntilTime(base::TimeTicks() +
+                                  base::TimeDelta::FromMilliseconds(1500));
+
+  EXPECT_THAT(run_times,
+              testing::ElementsAre(base::TimeTicks() +
+                                   base::TimeDelta::FromMilliseconds(1300)));
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
index ce1c5a6..c64fbd46 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
@@ -11,12 +11,12 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/strings/stringprintf.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/scheduler/base/real_time_domain.h"
 #include "platform/scheduler/child/scheduler_tqm_delegate.h"
 #include "platform/scheduler/renderer/renderer_scheduler_impl.h"
 #include "platform/scheduler/renderer/throttled_time_domain.h"
 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
-#include "public/platform/WebFrameScheduler.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
index 5aac895..9e3a050 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_frame_scheduler_impl.h
@@ -10,9 +10,9 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/trace_event/trace_event.h"
+#include "platform/WebFrameScheduler.h"
 #include "public/platform/scheduler/base/task_queue.h"
 #include "public/platform/WebCommon.h"
-#include "public/platform/WebFrameScheduler.h"
 
 namespace base {
 namespace trace_event {
@@ -29,7 +29,7 @@
 class WebTaskRunnerImpl;
 class WebViewSchedulerImpl;
 
-class BLINK_PLATFORM_EXPORT WebFrameSchedulerImpl : public WebFrameScheduler {
+class WebFrameSchedulerImpl : public WebFrameScheduler {
  public:
   WebFrameSchedulerImpl(RendererSchedulerImpl* renderer_scheduler,
                         WebViewSchedulerImpl* parent_web_view_scheduler,
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl.cc
index fa44994..6f491fc 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_view_scheduler_impl.cc
@@ -7,12 +7,12 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "platform/RuntimeEnabledFeatures.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/scheduler/base/virtual_time_domain.h"
 #include "platform/scheduler/child/scheduler_tqm_delegate.h"
 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 #include "platform/scheduler/renderer/renderer_scheduler_impl.h"
 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
-#include "public/platform/WebFrameScheduler.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
index 8aede23..338a27b8 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -274,10 +274,6 @@
   virtual bool shouldPlaceVerticalScrollbarOnLeft() const = 0;
 
   // Convenience functions
-  float scrollOffset(ScrollbarOrientation orientation) {
-    return orientation == HorizontalScrollbar ? scrollOffsetInt().width()
-                                              : scrollOffsetInt().height();
-  }
   float minimumScrollOffset(ScrollbarOrientation orientation) {
     return orientation == HorizontalScrollbar ? minimumScrollOffset().width()
                                               : minimumScrollOffset().height();
diff --git a/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp b/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp
index 3e4dc8e..7c38c7d2 100644
--- a/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp
+++ b/third_party/WebKit/Source/platform/testing/ImageDecodeBench.cpp
@@ -260,9 +260,8 @@
                      size_t packetSize) {
   std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
       data, true, ImageDecoder::AlphaPremultiplied,
-      colorCorrection ? ImageDecoder::ColorSpaceTransformed
-                      : ImageDecoder::ColorSpaceIgnored,
-      colorCorrection ? ImageDecoder::targetColorSpaceForTesting() : nullptr);
+      colorCorrection ? ColorBehavior::transformToTargetForTesting()
+                      : ColorBehavior::ignore());
   if (!packetSize) {
     bool allDataReceived = true;
     decoder->setData(data, allDataReceived);
@@ -313,7 +312,7 @@
     applyColorCorrection = (--argc, ++argv, true);
     WebVector<char> profile;
     getScreenColorProfile(&profile);  // Returns a color spin color profile.
-    ImageDecoder::setGlobalTargetColorProfile(profile);
+    ColorBehavior::setGlobalTargetColorProfile(profile);
   }
 
   if (argc < 2) {
diff --git a/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
index 2b709af..b73a34c 100644
--- a/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
+++ b/third_party/WebKit/Source/platform/text/hyphenation/HyphenationMinikin.cpp
@@ -8,7 +8,6 @@
 #include "base/files/memory_mapped_file.h"
 #include "base/metrics/histogram.h"
 #include "base/timer/elapsed_timer.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 #include "platform/LayoutLocale.h"
 #include "platform/text/hyphenation/HyphenatorAOSP.h"
 #include "public/platform/InterfaceProvider.h"
@@ -28,7 +27,7 @@
   Vector<size_t, 8> hyphenLocations(const StringView&) const override;
 
  private:
-  static base::PlatformFile openDictionaryFile(const AtomicString& locale);
+  static base::File openDictionaryFile(const AtomicString& locale);
 
   std::vector<uint8_t> hyphenate(const StringView&) const;
 
@@ -49,30 +48,20 @@
   return service;
 }
 
-base::PlatformFile HyphenationMinikin::openDictionaryFile(
-    const AtomicString& locale) {
+base::File HyphenationMinikin::openDictionaryFile(const AtomicString& locale) {
   const mojom::blink::HyphenationPtr& service = getService();
-  mojo::ScopedHandle handle;
+  base::File file;
   base::ElapsedTimer timer;
-  service->OpenDictionary(locale, &handle);
+  service->OpenDictionary(locale, &file);
   UMA_HISTOGRAM_TIMES("Hyphenation.Open", timer.Elapsed());
-  if (!handle.is_valid())
-    return base::kInvalidPlatformFile;
-
-  base::PlatformFile file;
-  MojoResult result = mojo::UnwrapPlatformFile(std::move(handle), &file);
-  if (result != MOJO_RESULT_OK) {
-    DLOG(ERROR) << "UnwrapPlatformFile failed";
-    return base::kInvalidPlatformFile;
-  }
   return file;
 }
 
 bool HyphenationMinikin::openDictionary(const AtomicString& locale) {
-  base::PlatformFile file = openDictionaryFile(locale);
-  if (file == base::kInvalidPlatformFile)
+  base::File file = openDictionaryFile(locale);
+  if (!file.IsValid())
     return false;
-  if (!m_file.Initialize(base::File(file))) {
+  if (!m_file.Initialize(std::move(file))) {
     DLOG(ERROR) << "mmap failed";
     return false;
   }
diff --git a/third_party/WebKit/Source/platform/transforms/RotateTransformOperation.h b/third_party/WebKit/Source/platform/transforms/RotateTransformOperation.h
index e1f0cc4..1d2e4508 100644
--- a/third_party/WebKit/Source/platform/transforms/RotateTransformOperation.h
+++ b/third_party/WebKit/Source/platform/transforms/RotateTransformOperation.h
@@ -66,6 +66,7 @@
 
   virtual bool canBlendWith(const TransformOperation& other) const;
   OperationType type() const override { return m_type; }
+  OperationType primitiveType() const final { return Rotate3D; }
 
   void apply(TransformationMatrix& transform,
              const FloatSize& /*borderBoxSize*/) const override {
diff --git a/third_party/WebKit/Source/platform/transforms/ScaleTransformOperation.h b/third_party/WebKit/Source/platform/transforms/ScaleTransformOperation.h
index b136986..8ffb2eb 100644
--- a/third_party/WebKit/Source/platform/transforms/ScaleTransformOperation.h
+++ b/third_party/WebKit/Source/platform/transforms/ScaleTransformOperation.h
@@ -65,6 +65,7 @@
 
  private:
   OperationType type() const override { return m_type; }
+  OperationType primitiveType() const final { return Scale3D; }
 
   bool operator==(const TransformOperation& o) const override {
     if (!isSameType(o))
diff --git a/third_party/WebKit/Source/platform/transforms/TransformOperation.h b/third_party/WebKit/Source/platform/transforms/TransformOperation.h
index 1453ae4..004226e4 100644
--- a/third_party/WebKit/Source/platform/transforms/TransformOperation.h
+++ b/third_party/WebKit/Source/platform/transforms/TransformOperation.h
@@ -83,6 +83,10 @@
   virtual PassRefPtr<TransformOperation> zoom(double factor) = 0;
 
   virtual OperationType type() const = 0;
+
+  // https://drafts.csswg.org/css-transforms/#transform-primitives
+  virtual OperationType primitiveType() const { return type(); }
+
   bool isSameType(const TransformOperation& other) const {
     return other.type() == type();
   }
diff --git a/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp b/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp
index ee27533c..b94746c22 100644
--- a/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp
+++ b/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp
@@ -52,14 +52,14 @@
 bool TransformOperations::operationsMatch(
     const TransformOperations& other) const {
   size_t numOperations = operations().size();
-  // If the sizes of the function lists don't match, the lists don't match
   if (numOperations != other.operations().size())
     return false;
 
-  // If the types of each function are not the same, the lists don't match
   for (size_t i = 0; i < numOperations; ++i) {
-    if (!operations()[i]->isSameType(*other.operations()[i]))
+    if (operations()[i]->primitiveType() !=
+        other.operations()[i]->primitiveType()) {
       return false;
+    }
   }
   return true;
 }
@@ -115,6 +115,7 @@
   return Matrix3DTransformOperation::create(toTransform);
 }
 
+// https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms
 TransformOperations TransformOperations::blend(const TransformOperations& from,
                                                double progress) const {
   if (from == *this || (!from.size() && !size()))
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp
index 6b7411b..9ac95bb 100644
--- a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp
+++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp
@@ -497,10 +497,12 @@
   // Calculate the adjoint matrix
   adjoint(matrix, result);
 
+  double rdet = 1 / det;
+
   // Scale the adjoint matrix to get the inverse
   for (int i = 0; i < 4; i++)
     for (int j = 0; j < 4; j++)
-      result[i][j] = result[i][j] / det;
+      result[i][j] = result[i][j] * rdet;
 #endif
   return true;
 }
diff --git a/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.h b/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.h
index 93f6657..124ff01d 100644
--- a/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.h
+++ b/third_party/WebKit/Source/platform/transforms/TranslateTransformOperation.h
@@ -74,6 +74,7 @@
 
  private:
   OperationType type() const override { return m_type; }
+  OperationType primitiveType() const final { return Translate3D; }
 
   bool operator==(const TransformOperation& o) const override {
     if (!isSameType(o))
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
index 32bf96b..93c0bd69 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
@@ -193,8 +193,10 @@
 
 void SecurityPolicy::addOriginTrustworthyWhiteList(
     PassRefPtr<SecurityOrigin> origin) {
+#if DCHECK_IS_ON()
   // Must be called before we start other threads.
-  ASSERT(WTF::isBeforeThreadCreated());
+  DCHECK(WTF::isBeforeThreadCreated());
+#endif
   if (origin->isUnique())
     return;
   trustworthyOriginSet().add(origin->toRawString());
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.cpp b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
index ad184bfc..9be955d 100644
--- a/third_party/WebKit/Source/web/ChromeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ChromeClientImpl.cpp
@@ -67,13 +67,13 @@
 #include "platform/Histogram.h"
 #include "platform/KeyboardCodes.h"
 #include "platform/RuntimeEnabledFeatures.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/geometry/IntRect.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/weborigin/SecurityOrigin.h"
 #include "public/platform/WebCursorInfo.h"
 #include "public/platform/WebFloatRect.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/platform/WebInputEvent.h"
 #include "public/platform/WebRect.h"
 #include "public/platform/WebURLRequest.h"
diff --git a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
index 362cd93..48ef654 100644
--- a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
+++ b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
@@ -66,12 +66,12 @@
 #include "modules/storage/DOMWindowStorageController.h"
 #include "modules/vr/NavigatorVR.h"
 #include "platform/Histogram.h"
-#include "platform/MIMETypeRegistry.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/UserGestureIndicator.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/exported/WrappedResourceResponse.h"
 #include "platform/network/HTTPParsers.h"
+#include "platform/network/mime/MIMETypeRegistry.h"
 #include "platform/plugins/PluginData.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebApplicationCacheHost.h"
diff --git a/third_party/WebKit/Source/web/WebDocument.cpp b/third_party/WebKit/Source/web/WebDocument.cpp
index 1ae80b7..78d64db8 100644
--- a/third_party/WebKit/Source/web/WebDocument.cpp
+++ b/third_party/WebKit/Source/web/WebDocument.cpp
@@ -206,14 +206,6 @@
   CSSSelectorWatch::from(*document).watchCSSSelectors(selectors);
 }
 
-bool WebDocument::unloadStartedDoNotUse() const {
-  return constUnwrap<Document>()->unloadStarted();
-}
-
-bool WebDocument::processingBeforeUnloadDoNotUse() const {
-  return constUnwrap<Document>()->processingBeforeUnload();
-}
-
 WebReferrerPolicy WebDocument::getReferrerPolicy() const {
   return static_cast<WebReferrerPolicy>(
       constUnwrap<Document>()->getReferrerPolicy());
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index e5a1a5f..88911af 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -51,8 +51,8 @@
 #include "core/page/Page.h"
 #include "core/page/PointerLockController.h"
 #include "platform/KeyboardCodes.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/graphics/CompositorMutatorClient.h"
-#include "public/platform/WebFrameScheduler.h"
 #include "public/web/WebAutofillClient.h"
 #include "public/web/WebPlugin.h"
 #include "public/web/WebRange.h"
diff --git a/third_party/WebKit/Source/web/WebImageDecoder.cpp b/third_party/WebKit/Source/web/WebImageDecoder.cpp
index ee78776..0c8428f 100644
--- a/third_party/WebKit/Source/web/WebImageDecoder.cpp
+++ b/third_party/WebKit/Source/web/WebImageDecoder.cpp
@@ -51,13 +51,13 @@
   switch (type) {
     case TypeBMP:
       m_private = new BMPImageDecoder(
-          ImageDecoder::AlphaPremultiplied, ImageDecoder::ColorSpaceTransformed,
-          ImageDecoder::targetColorSpaceForTesting(), maxDecodedBytes);
+          ImageDecoder::AlphaPremultiplied,
+          ColorBehavior::transformToTargetForTesting(), maxDecodedBytes);
       break;
     case TypeICO:
       m_private = new ICOImageDecoder(
-          ImageDecoder::AlphaPremultiplied, ImageDecoder::ColorSpaceTransformed,
-          ImageDecoder::targetColorSpaceForTesting(), maxDecodedBytes);
+          ImageDecoder::AlphaPremultiplied,
+          ColorBehavior::transformToTargetForTesting(), maxDecodedBytes);
       break;
   }
 }
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index c120291..d49378a 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -160,6 +160,7 @@
 #include "modules/screen_orientation/ScreenOrientationControllerImpl.h"
 #include "platform/ScriptForbiddenScope.h"
 #include "platform/UserGestureIndicator.h"
+#include "platform/WebFrameScheduler.h"
 #include "platform/clipboard/ClipboardUtilities.h"
 #include "platform/fonts/FontCache.h"
 #include "platform/graphics/GraphicsContext.h"
@@ -2337,6 +2338,27 @@
   UseCounter::count(frame(), feature);
 }
 
+base::SingleThreadTaskRunner* WebLocalFrameImpl::timerTaskRunner() {
+  return frame()
+      ->frameScheduler()
+      ->timerTaskRunner()
+      ->toSingleThreadTaskRunner();
+}
+
+base::SingleThreadTaskRunner* WebLocalFrameImpl::loadingTaskRunner() {
+  return frame()
+      ->frameScheduler()
+      ->loadingTaskRunner()
+      ->toSingleThreadTaskRunner();
+}
+
+base::SingleThreadTaskRunner* WebLocalFrameImpl::unthrottledTaskRunner() {
+  return frame()
+      ->frameScheduler()
+      ->unthrottledTaskRunner()
+      ->toSingleThreadTaskRunner();
+}
+
 WebInputMethodControllerImpl* WebLocalFrameImpl::inputMethodController() const {
   return m_inputMethodController.get();
 }
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.h b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
index 2d4b1d59..8cdfda6c 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.h
@@ -292,6 +292,9 @@
   void saveImageAt(const WebPoint&) override;
   void clearActiveFindMatch() override;
   void usageCountChromeLoadTimes(const WebString& metric) override;
+  base::SingleThreadTaskRunner* timerTaskRunner() override;
+  base::SingleThreadTaskRunner* loadingTaskRunner() override;
+  base::SingleThreadTaskRunner* unthrottledTaskRunner() override;
 
   // WebFrameImplBase methods:
   void initializeCoreFrame(FrameHost*,
diff --git a/third_party/WebKit/Source/web/WebPerformance.cpp b/third_party/WebKit/Source/web/WebPerformance.cpp
index 3b3bee45..6336020 100644
--- a/third_party/WebKit/Source/web/WebPerformance.cpp
+++ b/third_party/WebKit/Source/web/WebPerformance.cpp
@@ -202,6 +202,11 @@
       m_private->timing()->authorStyleSheetParseDurationBeforeFCP());
 }
 
+double WebPerformance::updateStyleDurationBeforeFCP() const {
+  return millisecondsToSeconds(
+      m_private->timing()->updateStyleDurationBeforeFCP());
+}
+
 WebPerformance::WebPerformance(Performance* performance)
     : m_private(performance) {}
 
diff --git a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
index cdfd51e..284ccfd4 100644
--- a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
@@ -234,6 +234,10 @@
   RuntimeEnabledFeatures::setSlimmingPaintV2Enabled(enable);
 }
 
+void WebRuntimeFeatures::enableSlimmingPaintInvalidation(bool enable) {
+  RuntimeEnabledFeatures::setSlimmingPaintInvalidationEnabled(enable);
+}
+
 void WebRuntimeFeatures::enableSpeculativeLaunchServiceWorker(bool enable) {
   RuntimeEnabledFeatures::setSpeculativeLaunchServiceWorkerEnabled(enable);
 }
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index df6b5c3..9c99761 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -3032,7 +3032,7 @@
 }
 
 void WebViewImpl::setDeviceColorProfile(const WebVector<char>& colorProfile) {
-  ImageDecoder::setGlobalTargetColorProfile(colorProfile);
+  ColorBehavior::setGlobalTargetColorProfile(colorProfile);
 }
 
 void WebViewImpl::enableAutoResizeMode(const WebSize& minSize,
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
index 2fdf07d..c0d503d 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
@@ -138,11 +138,11 @@
 }
 
 void reloadFrame(WebFrame* frame) {
-  frame->reload(WebFrameLoadType::Reload);
+  frame->reload(WebFrameLoadType::ReloadMainResource);
   pumpPendingRequestsForFrameToLoad(frame);
 }
 
-void reloadFrameIgnoringCache(WebFrame* frame) {
+void reloadFrameBypassingCache(WebFrame* frame) {
   frame->reload(WebFrameLoadType::ReloadBypassingCache);
   pumpPendingRequestsForFrameToLoad(frame);
 }
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
index 7b932d0..de2683e3 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
@@ -75,7 +75,7 @@
                      WebCachePolicy);
 // Same as above, but for WebFrame::reload().
 void reloadFrame(WebFrame*);
-void reloadFrameIgnoringCache(WebFrame*);
+void reloadFrameBypassingCache(WebFrame*);
 
 // Pumps pending resource requests while waiting for a frame to load. Consider
 // using one of the above helper methods whenever possible.
diff --git a/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
index 21502e19..0d80dda0 100644
--- a/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/RootScrollerTest.cpp
@@ -128,7 +128,7 @@
   }
 
   Node* effectiveRootScroller(Document* doc) const {
-    return doc->rootScrollerController()->effectiveRootScroller();
+    return &doc->rootScrollerController().effectiveRootScroller();
   }
 
   WebGestureEvent generateTouchGestureEvent(WebInputEvent::Type type,
@@ -173,7 +173,7 @@
 TEST_F(RootScrollerTest, TestDefaultRootScroller) {
   initialize("overflow-scrolling.html");
 
-  RootScrollerController* controller =
+  RootScrollerController& controller =
       mainFrame()->document()->rootScrollerController();
 
   ASSERT_EQ(nullptr, mainFrame()->document()->rootScroller());
@@ -182,7 +182,7 @@
             effectiveRootScroller(mainFrame()->document()));
 
   Element* htmlElement = mainFrame()->document()->documentElement();
-  EXPECT_TRUE(controller->scrollsViewport(*htmlElement));
+  EXPECT_TRUE(controller.scrollsViewport(*htmlElement));
 }
 
 // Make sure that replacing the documentElement doesn't change the effective
@@ -738,9 +738,9 @@
       mainFrame()->document()->getElementById("iframe"));
   Element* container = iframe->contentDocument()->getElementById("container");
 
-  RootScrollerController* mainController =
+  RootScrollerController& mainController =
       mainFrame()->document()->rootScrollerController();
-  RootScrollerController* childController =
+  RootScrollerController& childController =
       iframe->contentDocument()->rootScrollerController();
   TopDocumentRootScrollerController& globalController =
       frameHost().globalRootScrollerController();
@@ -779,8 +779,8 @@
     mainFrame()->document()->setRootScroller(iframe, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    ASSERT_EQ(iframe, mainController->effectiveRootScroller());
-    ASSERT_EQ(container, childController->effectiveRootScroller());
+    ASSERT_EQ(iframe, &mainController.effectiveRootScroller());
+    ASSERT_EQ(container, &childController.effectiveRootScroller());
 
     EXPECT_FALSE(
         mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
@@ -804,9 +804,9 @@
     iframe->contentDocument()->setRootScroller(nullptr, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    ASSERT_EQ(iframe, mainController->effectiveRootScroller());
+    ASSERT_EQ(iframe, &mainController.effectiveRootScroller());
     ASSERT_EQ(iframe->contentDocument(),
-              childController->effectiveRootScroller());
+              &childController.effectiveRootScroller());
     ASSERT_EQ(iframe->contentDocument()->documentElement(),
               globalController.globalRootScroller());
 
@@ -835,9 +835,9 @@
     mainFrame()->document()->setRootScroller(nullptr, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    ASSERT_EQ(mainFrame()->document(), mainController->effectiveRootScroller());
+    ASSERT_EQ(mainFrame()->document(), &mainController.effectiveRootScroller());
     ASSERT_EQ(iframe->contentDocument(),
-              childController->effectiveRootScroller());
+              &childController.effectiveRootScroller());
     ASSERT_EQ(mainFrame()->document()->documentElement(),
               globalController.globalRootScroller());
 
@@ -864,9 +864,9 @@
     mainFrame()->document()->setRootScroller(iframe, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    ASSERT_EQ(iframe, mainController->effectiveRootScroller());
+    ASSERT_EQ(iframe, &mainController.effectiveRootScroller());
     ASSERT_EQ(iframe->contentDocument(),
-              childController->effectiveRootScroller());
+              &childController.effectiveRootScroller());
     ASSERT_EQ(iframe->contentDocument()->documentElement(),
               globalController.globalRootScroller());
 
@@ -892,8 +892,8 @@
     iframe->contentDocument()->setRootScroller(container, nonThrow);
     mainFrameView()->updateAllLifecyclePhases();
 
-    ASSERT_EQ(mainFrame()->document(), mainController->effectiveRootScroller());
-    ASSERT_EQ(container, childController->effectiveRootScroller());
+    ASSERT_EQ(mainFrame()->document(), &mainController.effectiveRootScroller());
+    ASSERT_EQ(container, &childController.effectiveRootScroller());
 
     EXPECT_TRUE(
         mainCompositor->rootContentLayer()->platformLayer()->masksToBounds());
@@ -1059,9 +1059,6 @@
     // If the root scroller wasn't updated by the DOM removal above, this
     // will touch the disposed root scroller's ScrollableArea.
     mainFrameView()->getRootFrameViewport()->serviceScrollAnimations(0);
-
-    EXPECT_EQ(iframe->contentDocument(),
-              effectiveRootScroller(iframe->contentDocument()));
   }
 }
 
@@ -1224,17 +1221,16 @@
   document->setRootScroller(iframe, exceptionState);
   mainFrameView()->updateAllLifecyclePhases();
 
-  RootScrollerController* mainController =
+  RootScrollerController& mainController =
       mainFrame()->document()->rootScrollerController();
 
   LocalFrame* iframeLocalFrame = toLocalFrame(iframe->contentFrame());
-  EXPECT_EQ(iframe, mainController->effectiveRootScroller());
+  EXPECT_EQ(iframe, &mainController.effectiveRootScroller());
   EXPECT_EQ(iframeLocalFrame->view()->layoutViewportScrollableArea(),
             &mainFrameView()->getRootFrameViewport()->layoutViewport());
 
   // Remove the <iframe> and make sure the layout viewport reverts to the
-  // FrameView
-  // without a layout.
+  // FrameView without a layout.
   iframe->remove();
 
   EXPECT_EQ(mainFrameView()->layoutViewportScrollableArea(),
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 04287ea..76e28a5 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -4089,7 +4089,7 @@
   webViewHelper.webView()->mainFrame()->reload(
       WebFrameLoadType::ReloadBypassingCache);
   // start another reload before request is delivered.
-  FrameTestHelpers::reloadFrameIgnoringCache(
+  FrameTestHelpers::reloadFrameBypassingCache(
       webViewHelper.webView()->mainFrame());
 }
 
@@ -4147,7 +4147,7 @@
   EXPECT_EQ(0, webViewHelper.webView()->mainFrame()->getScrollOffset().height);
   EXPECT_EQ(1.0f, webViewHelper.webView()->pageScaleFactor());
 
-  // Reload the page while ignoring the cache. State should not be propagated.
+  // Reload the page while bypassing the cache. State should not be propagated.
   webViewHelper.webView()->mainFrame()->reloadWithOverrideURL(
       toKURL(m_baseURL + thirdURL), WebFrameLoadType::ReloadBypassingCache);
   FrameTestHelpers::pumpPendingRequestsForFrameToLoad(
@@ -4167,7 +4167,7 @@
   WebURLRequest request(toKURL(m_baseURL + "fixed_layout.html"));
   webViewHelper.webView()->mainFrame()->loadRequest(request);
   // start reload before first request is delivered.
-  FrameTestHelpers::reloadFrameIgnoringCache(
+  FrameTestHelpers::reloadFrameBypassingCache(
       webViewHelper.webView()->mainFrame());
 
   WebDataSource* dataSource =
@@ -7088,7 +7088,12 @@
 
   EXPECT_EQ(mainClient.childFrameCreationCount(), 2);
   EXPECT_EQ(childClient.willSendRequestCallCount(), 2);
-  EXPECT_EQ(childClient.getCachePolicy(), WebCachePolicy::ValidatingCacheData);
+
+  // Sub-frames should not be forcibly revalidated.
+  // TODO(toyoshim): Will consider to revalidate main resources in sub-frames
+  // on reloads. Or will do only for bypassingCache.
+  EXPECT_EQ(childClient.getCachePolicy(),
+            WebCachePolicy::UseProtocolCachePolicy);
 }
 
 class TestSameDocumentWebFrameClient
@@ -8248,13 +8253,13 @@
 }
 
 TEST_P(ParameterizedWebFrameTest, ReloadBypassingCache) {
-  // Check that a reload ignoring cache on a frame will result in the cache
+  // Check that a reload bypassing cache on a frame will result in the cache
   // policy of the request being set to ReloadBypassingCache.
   registerMockedHttpURLLoad("foo.html");
   FrameTestHelpers::WebViewHelper webViewHelper;
   webViewHelper.initializeAndLoad(m_baseURL + "foo.html", true);
   WebFrame* frame = webViewHelper.webView()->mainFrame();
-  FrameTestHelpers::reloadFrameIgnoringCache(frame);
+  FrameTestHelpers::reloadFrameBypassingCache(frame);
   EXPECT_EQ(WebCachePolicy::BypassingCache,
             frame->dataSource()->request().getCachePolicy());
 }
diff --git a/third_party/WebKit/Source/wtf/MathExtras.h b/third_party/WebKit/Source/wtf/MathExtras.h
index 36ad812..6ff82028 100644
--- a/third_party/WebKit/Source/wtf/MathExtras.h
+++ b/third_party/WebKit/Source/wtf/MathExtras.h
@@ -59,8 +59,8 @@
 const double twoPiDouble = piDouble * 2.0;
 const float twoPiFloat = piFloat * 2.0f;
 
-#if OS(ANDROID) || COMPILER(MSVC)
-// ANDROID and MSVC's math.h does not currently supply log2 or log2f.
+#if OS(ANDROID)
+// ANDROID's math.h does not currently supply log2 or log2f.
 inline double log2(double num) {
   // This constant is roughly M_LN2, which is not provided by default on Windows
   // and Android.
diff --git a/third_party/WebKit/Source/wtf/Vector.h b/third_party/WebKit/Source/wtf/Vector.h
index e020acea..f069d93 100644
--- a/third_party/WebKit/Source/wtf/Vector.h
+++ b/third_party/WebKit/Source/wtf/Vector.h
@@ -945,6 +945,8 @@
   void append(const U*, size_t);
   template <typename U>
   void append(U&&);
+  template <typename U>
+  void push_back(U&&);
   template <typename... Args>
   T& emplace_back(Args&&...);
   template <typename U>
@@ -1382,6 +1384,12 @@
 
 template <typename T, size_t inlineCapacity, typename Allocator>
 template <typename U>
+ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::push_back(U&& val) {
+  return append(std::forward<U>(val));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
 ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::append(U&& val) {
   ASSERT(Allocator::isAllocationAllowed());
   if (LIKELY(size() != capacity())) {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot_mock.py
index 4239b624..159af39d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot_mock.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/buildbot_mock.py
@@ -27,31 +27,20 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from webkitpy.common.net.buildbot import BuildBot
-from webkitpy.common.net.layouttestresults import LayoutTestResults
-from webkitpy.common.net.layouttestresults_unittest import LayoutTestResultsTest
 
 
-# TODO(qyearsley): Instead of canned results from another module, other unit
-# tests may be a little easier to understand if this returned None by default
-# when there are no canned results to return.
 class MockBuildBot(BuildBot):
 
     def __init__(self):
         super(MockBuildBot, self).__init__()
-        # Dict of Build to canned LayoutTestResults.
         self._canned_results = {}
         self._canned_retry_summary_json = {}
 
-    def fetch_layout_test_results(self, _):
-        return LayoutTestResults.results_from_string(LayoutTestResultsTest.example_full_results_json)
-
     def set_results(self, build, results):
         self._canned_results[build] = results
 
     def fetch_results(self, build):
-        return self._canned_results.get(
-            build,
-            LayoutTestResults.results_from_string(LayoutTestResultsTest.example_full_results_json))
+        return self._canned_results.get(build)
 
     def set_retry_sumary_json(self, build, content):
         self._canned_retry_summary_json[build] = content
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
index 6e4cf9f1..a679b915 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
@@ -86,7 +86,7 @@
         """Creates a LayoutTestResults object from a test result JSON string.
 
         Args:
-            string: JSON string containg layout test result.
+            string: JSON string containing layout test result.
             chromium_revision: If given, it will override the chromium_revision
                 field in json, to indicate the last revision that has completed
                 uploading onto the storage server. chromium_revision can be a
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py
index 1fd61c3..601aa1a 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py
@@ -57,6 +57,7 @@
             return
 
         builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots())
+
         if options.trigger_jobs:
             if self.trigger_jobs_for_missing_builds(builds):
                 _log.info('Please re-run webkit-patch rebaseline-cl once all pending try jobs have finished.')
@@ -64,13 +65,20 @@
         if not builds:
             _log.info('No builds to download baselines from.')
 
+        _log.debug('Getting results for Rietveld issue %d.', issue_number)
+        builds_to_results = self._fetch_results(builds)
+        if builds_to_results is None:
+            return
+
+        test_prefix_list = {}
         if args:
-            test_prefix_list = {}
             for test in args:
                 test_prefix_list[test] = {b: BASELINE_SUFFIX_LIST for b in builds}
         else:
             test_prefix_list = self._test_prefix_list(
-                issue_number, only_changed_tests=options.only_changed_tests)
+                issue_number,
+                builds_to_results,
+                only_changed_tests=options.only_changed_tests)
 
         self._log_test_prefix_list(test_prefix_list)
 
@@ -124,11 +132,44 @@
 
         return bool(builders_with_pending_builds or builders_without_builds)
 
-    def _test_prefix_list(self, issue_number, only_changed_tests):
-        """Returns a collection of test, builder and file extensions to get new baselines for.
+    def _try_bots(self):
+        """Returns a collection of try bot builders to fetch results for."""
+        return self._tool.builders.all_try_builder_names()
+
+    def _fetch_results(self, builds):
+        """Fetches results for each build.
+
+        There should be a one-to-one correspondence between Builds, supported
+        platforms, and try bots. If not all of the builds can be fetched, then
+        continuing with rebaselining may yield incorrect results, when the new
+        baselines are deduped, an old baseline may be kept for the platform
+        that's missing results.
+
+        Returns:
+            A dict mapping Build to LayoutTestResults, or None if any results
+            were not available.
+        """
+        buildbot = self._tool.buildbot
+        results = {}
+        for build in builds:
+            results_url = buildbot.results_url(build.builder_name, build.build_number)
+            layout_test_results = buildbot.fetch_results(build)
+            if layout_test_results is None:
+                _log.error(
+                    'Failed to fetch results from "%s".\n'
+                    'Try starting a new job for %s by running :\n'
+                    '  git cl try -b %s',
+                    results_url, build.builder_name, build.builder_name)
+                return None
+            results[build] = layout_test_results
+        return results
+
+    def _test_prefix_list(self, issue_number, builds_to_results, only_changed_tests):
+        """Returns a collection of tests, builders and file extensions to get new baselines for.
 
         Args:
             issue_number: The CL number of the change which needs new baselines.
+            builds_to_results: A dict mapping Builds to LayoutTestResults.
             only_changed_tests: Whether to only include baselines for tests that
                are changed in this CL. If False, all new baselines for failing
                tests will be downloaded, even for tests that were not modified.
@@ -136,7 +177,9 @@
         Returns:
             A dict containing information about which new baselines to download.
         """
-        builds_to_tests = self._builds_to_tests(issue_number)
+        builds_to_tests = {}
+        for build, results in builds_to_results.iteritems():
+            builds_to_tests[build] = self._tests_to_rebaseline(build, results)
         if only_changed_tests:
             files_in_cl = self.rietveld.changed_files(issue_number)
             # Note, in the changed files list from Rietveld, paths always
@@ -154,28 +197,8 @@
                 result[test][build] = BASELINE_SUFFIX_LIST
         return result
 
-    def _builds_to_tests(self, issue_number):
-        """Fetches a list of try bots, and for each, fetches tests with new baselines."""
-        _log.debug('Getting results for Rietveld issue %d.', issue_number)
-        builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots())
-        if not builds:
-            _log.debug('No try job results for builders in: %r.', self._try_bots())
-        return {build: self._tests_to_rebaseline(build) for build in builds}
-
-    def _try_bots(self):
-        """Returns a collection of try bot builders to fetch results for."""
-        return self._tool.builders.all_try_builder_names()
-
-    def _tests_to_rebaseline(self, build):
-        """Fetches a list of tests that should be rebaselined."""
-        buildbot = self._tool.buildbot
-        results_url = buildbot.results_url(build.builder_name, build.build_number)
-
-        layout_test_results = buildbot.fetch_layout_test_results(results_url)
-        if layout_test_results is None:
-            _log.warning('Failed to request layout test results from "%s".', results_url)
-            return []
-
+    def _tests_to_rebaseline(self, build, layout_test_results):
+        """Fetches a list of tests that should be rebaselined for some build ."""
         unexpected_results = layout_test_results.didnt_run_as_expected_results()
         tests = sorted(r.test_name() for r in unexpected_results
                        if r.is_missing_baseline() or r.has_mismatch_result())
@@ -197,7 +220,7 @@
             retry_summary = json.loads(content)
             return retry_summary['failures']
         except (ValueError, KeyError):
-            _log.warning('Unexepected retry summary content:\n%s', content)
+            _log.warning('Unexpected retry summary content:\n%s', content)
             return None
 
     @staticmethod
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
index 874130a..afb61286 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
@@ -7,6 +7,7 @@
 
 from webkitpy.common.net.buildbot import Build
 from webkitpy.common.net.git_cl import GitCL
+from webkitpy.common.net.layouttestresults import LayoutTestResults
 from webkitpy.common.net.rietveld import Rietveld
 from webkitpy.common.net.web_mock import MockWeb
 from webkitpy.common.system.logtesting import LoggingTestCase
@@ -51,13 +52,64 @@
                 "is_try_builder": True,
             },
             "MOCK Try Linux": {
-                "port_name": "test-mac-mac10.10",
-                "specifiers": ["Mac10.10", "Release"],
+                "port_name": "test-linux-trusty",
+                "specifiers": ["Trusty", "Release"],
                 "is_try_builder": True,
             },
         })
         self.command.rietveld = Rietveld(web)
 
+        layout_test_results = LayoutTestResults({
+            'tests': {
+                'fast': {
+                    'dom': {
+                        'prototype-inheritance.html': {
+                            'expected': 'PASS',
+                            'actual': 'TEXT',
+                            'is_unexpected': True,
+                        },
+                        'prototype-banana.html': {
+                            'expected': 'FAIL',
+                            'actual': 'PASS',
+                            'is_unexpected': True,
+                        },
+                        'prototype-taco.html': {
+                            'expected': 'PASS',
+                            'actual': 'PASS TEXT',
+                            'is_unexpected': True,
+                        },
+                        'prototype-chocolate.html': {
+                            'expected': 'FAIL',
+                            'actual': 'IMAGE+TEXT'
+                        },
+                        'prototype-crashy.html': {
+                            'expected': 'PASS',
+                            'actual': 'CRASH',
+                            'is_unexpected': True,
+                        },
+                        'prototype-newtest.html': {
+                            'expected': 'PASS',
+                            'actual': 'MISSING',
+                            'is_unexpected': True,
+                            'is_missing_text': True,
+                        }
+                    }
+                },
+                'svg': {
+                    'dynamic-updates': {
+                        'SVGFEDropShadowElement-dom-stdDeviation-attr.html': {
+                            'expected': 'PASS',
+                            'actual': 'IMAGE',
+                            'has_stderr': True,
+                            'is_unexpected': True,
+                        }
+                    }
+                }
+            }
+        })
+        for build in [Build('MOCK Try Win', 5000), Build('MOCK Try Mac', 4000)]:
+            self.tool.buildbot.set_results(build, layout_test_results)
+
         self.tool.buildbot.set_retry_sumary_json(Build('MOCK Try Win', 5000), json.dumps({
             'failures': [
                 'fast/dom/prototype-newtest.html',
@@ -237,3 +289,13 @@
             'INFO: Triggering try jobs for:\n',
             'INFO:   MOCK Try Win\n',
         ])
+
+    def test_bails_when_one_build_is_missing_results(self):
+        self.tool.buildbot.set_results(Build("MOCK Try Win", 5000), None)
+        self.command.execute(self.command_options(issue=11112222), [], self.tool)
+        self.assertLog([
+            'ERROR: Failed to fetch results from '
+            '"https://storage.googleapis.com/chromium-layout-test-archives/MOCK_Try_Win/5000/layout-test-results".\n'
+            'Try starting a new job for MOCK Try Win by running :\n'
+            '  git cl try -b MOCK Try Win\n'
+        ])
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
index d7f5b4d..d23f73b2 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
@@ -92,7 +92,9 @@
 
         local_branch_name = local_wpt.create_branch_with_patch(message, patch)
 
-        self.wpt_github.create_pr(
+        response_data = self.wpt_github.create_pr(
             local_branch_name=local_branch_name,
             desc_title=outbound_commit.subject(),
             body=outbound_commit.body())
+
+        _log.info('Create PR response: %s', response_data)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
index b9ea558..655c214 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
@@ -7,18 +7,11 @@
 import logging
 import os
 import sys
+import urllib2
 
 from webkitpy.common.system.filesystem import FileSystem
 from webkitpy.common.webkit_finder import WebKitFinder
 
-# httplib2 is part of the hermetic depot_tools install,
-# but we first need to find a path to it.
-# TODO(dglazkov): libhttp2 needs to be imported into thirdparty dir.
-# See http://crbug.com/670987 for details.
-_DEPOT_TOOLS_ROOT = WebKitFinder(FileSystem()).depot_tools_base()
-sys.path.append(os.path.join(_DEPOT_TOOLS_ROOT, 'third_party'))
-
-import httplib2
 
 _log = logging.getLogger(__name__)
 API_BASE = 'https://api.github.com'
@@ -35,7 +28,7 @@
         assert self.user and self.token, 'must have GH_USER and GH_TOKEN env vars'
 
     def auth_token(self):
-        return base64.encodestring('{}:{}'.format(self.user, self.token))
+        return base64.encodestring('{}:{}'.format(self.user, self.token)).strip()
 
     def create_pr(self, local_branch_name, desc_title, body):
         """Creates a PR on GitHub.
@@ -53,11 +46,7 @@
 
         # TODO(jeffcarp): CC foolip and qyearsley on all PRs for now
         # TODO(jeffcarp): add HTTP to Host and use that here
-        conn = httplib2.Http()
-        headers = {
-            "Accept": "application/vnd.github.v3+json",
-            "Authorization": "Basic " + self.auth_token()
-        }
+        path = '/repos/w3c/web-platform-tests/pulls'
         body = {
             "title": desc_title,
             "body": body,
@@ -65,39 +54,34 @@
             "base": 'master',
             "labels": [EXPORT_LABEL]
         }
-        resp, content = conn.request("https://api.github.com/repos/w3c/web-platform-tests/pulls",
-                                     "POST", body=json.JSONEncoder().encode(body), headers=headers)
-        _log.info("GitHub response: %s", content)
-        if resp["status"] != "201":
+        data, status_code = self.request(path, body)
+
+        if status_code != 201:
             return None
-        return json.loads(content)
+
+        return data
 
     def in_flight_pull_requests(self):
         url_encoded_label = EXPORT_LABEL.replace(' ', '%20')
         path = '/search/issues?q=repo:w3c/web-platform-tests%20is:open%20type:pr%20labels:{}'.format(url_encoded_label)
-        response, content = self.request(path)
-        if response['status'] == '200':
-            data = json.loads(content)
+        data, status_code = self.request(path)
+        if status_code == 200:
             return data['items']
         else:
-            raise Exception('Non-200 status code (%s): %s' % (response['status'], content))
+            raise Exception('Non-200 status code (%s): %s' % (status_code, data))
 
     def request(self, path, body=None):
         assert path.startswith('/')
 
-        # Not used yet since only hitting public API
-        # headers = {
-        #     "Accept": "application/vnd.github.v3+json",
-        #     "Authorization": "Basic " + self.auth_token()
-        # }
-
         if body:
-            json_body = json.dumps(body)
-        else:
-            json_body = None
+            body = json.dumps(body)
 
-        conn = httplib2.Http()
-        return conn.request(API_BASE + path, body=json_body)
+        req = urllib2.Request(url=API_BASE + path, data=body)
+        req.add_header('Accept', 'application/vnd.github.v3+json')
+        req.add_header('Authorization', 'Basic {}'.format(self.auth_token()))
+        res = urllib2.urlopen(req)
+        status_code = res.getcode()
+        return json.load(res), status_code
 
     def merge_pull_request(self, pull_request_number):
         path = '/repos/w3c/web-platform-tests/pulls/%d/merge' % pull_request_number
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
index aebcc45..34025ac 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
@@ -38,5 +38,5 @@
         self.assertEqual(self.wpt_github.token, 'deadbeefcafe')
 
     def test_auth_token(self):
-        expected = base64.encodestring('rutabaga:deadbeefcafe')
+        expected = base64.encodestring('rutabaga:deadbeefcafe').strip()
         self.assertEqual(self.wpt_github.auth_token(), expected)
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 660d2fb8..40fb9877 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -185,7 +185,6 @@
     "platform/WebFocusType.h",
     "platform/WebFont.h",
     "platform/WebFontDescription.h",
-    "platform/WebFrameScheduler.h",
     "platform/WebGamepad.h",
     "platform/WebGamepadListener.h",
     "platform/WebGamepads.h",
diff --git a/third_party/WebKit/public/blink_resources.grd b/third_party/WebKit/public/blink_resources.grd
index 41fdee8..cfcac10 100644
--- a/third_party/WebKit/public/blink_resources.grd
+++ b/third_party/WebKit/public/blink_resources.grd
@@ -34,7 +34,6 @@
       <include name="IDR_PRIVATE_SCRIPT_DOCUMENTEXECCOMMAND_JS" file="../Source/core/editing/js/DocumentExecCommand.js" type="BINDATA"/>
       <include name="IDR_PRIVATE_SCRIPT_DOCUMENTXMLTREEVIEWER_CSS" file="../Source/core/xml/DocumentXMLTreeViewer.css" type="BINDATA"/>
       <include name="IDR_PRIVATE_SCRIPT_DOCUMENTXMLTREEVIEWER_JS" file="../Source/core/xml/DocumentXMLTreeViewer.js" type="BINDATA"/>
-      <include name="IDR_PRIVATE_SCRIPT_HTMLMARQUEEELEMENT_JS" file="../Source/core/html/HTMLMarqueeElement.js" type="BINDATA"/>
       <include name="IDR_PRIVATE_SCRIPT_PRIVATESCRIPTRUNNER_JS" file="../Source/bindings/core/v8/PrivateScriptRunner.js" type="BINDATA"/>
       <if expr="not is_android">
         <include name="IDR_PICKER_COMMON_JS" file="../Source/web/resources/pickerCommon.js" type="BINDATA"/>
@@ -50,7 +49,7 @@
         <include name="IDR_LIST_PICKER_JS" file="../Source/web/resources/listPicker.js" type="BINDATA"/>
       </if>
       <if expr="use_concatenated_impulse_responses">
-        <include name="IDR_AUDIO_SPATIALIZATION_COMPOSITE" file="../Source/platform/audio/resources/Composite.wav" type="BINDATA"/>
+        <include name="IDR_AUDIO_SPATIALIZATION_COMPOSITE" file="../Source/platform/audio/resources/Composite.flac" type="BINDATA"/>
       </if>
       <if expr="not use_concatenated_impulse_responses">
         <include name="IDR_AUDIO_SPATIALIZATION_T000_P000" file="..\Source\platform\audio\resources\IRC_Composite_C_R0195_T000_P000.wav" type="BINDATA"/>
diff --git a/third_party/WebKit/public/platform/ShapeProperties.h b/third_party/WebKit/public/platform/ShapeProperties.h
new file mode 100644
index 0000000..fecdc40f
--- /dev/null
+++ b/third_party/WebKit/public/platform/ShapeProperties.h
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ShapeProperties_h
+#define ShapeProperties_h
+
+namespace blink {
+
+// Bit field values indicating available display shapes.
+enum DisplayShape {
+  DisplayShapeRect = 1 << 0,
+  DisplayShapeRound = 1 << 1,
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/WebKit/public/platform/WebFrameScheduler.h b/third_party/WebKit/public/platform/WebFrameScheduler.h
deleted file mode 100644
index ec075a2..0000000
--- a/third_party/WebKit/public/platform/WebFrameScheduler.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WebFrameScheduler_h
-#define WebFrameScheduler_h
-
-#include "WebCommon.h"
-
-#include <string>
-
-namespace blink {
-
-class WebTaskRunner;
-class WebViewScheduler;
-
-class BLINK_PLATFORM_EXPORT WebFrameScheduler {
- public:
-  virtual ~WebFrameScheduler() {}
-
-  // The scheduler may throttle tasks associated with offscreen frames.
-  virtual void setFrameVisible(bool) {}
-
-  // Tells the scheduler that the page this frame belongs to is not visible.
-  // The scheduler may throttle tasks associated with pages that are not
-  // visible.
-  virtual void setPageVisible(bool) {}
-
-  // Set whether this frame is suspended. Only unthrottledTaskRunner tasks are
-  // allowed to run on a suspended frame.
-  virtual void setSuspended(bool) {}
-
-  // Set whether this frame is cross origin w.r.t. the top level frame. Cross
-  // origin frames may use a different scheduling policy from same origin
-  // frames.
-  virtual void setCrossOrigin(bool) {}
-
-  // Returns the WebTaskRunner for loading tasks.
-  // WebFrameScheduler owns the returned WebTaskRunner.
-  virtual WebTaskRunner* loadingTaskRunner() { return nullptr; }
-
-  // Returns the WebTaskRunner for timer tasks.
-  // WebFrameScheduler owns the returned WebTaskRunner.
-  virtual WebTaskRunner* timerTaskRunner() { return nullptr; }
-
-  // Returns the WebTaskRunner for tasks which should never get throttled.
-  // This is generally used for executing internal browser tasks which should
-  // never be throttled. Ideally only tasks whose performance characteristics
-  // are known should be posted to this task runner; for example user
-  // JavaScript is discouraged. WebFrameScheduler owns the returned
-  // WebTaskRunner.
-  virtual WebTaskRunner* unthrottledTaskRunner() { return nullptr; }
-
-  // Returns the parent WebViewScheduler.
-  virtual WebViewScheduler* webViewScheduler() { return nullptr; }
-
-  // Tells the scheduler a resource load has started. The scheduler may make
-  // policy decisions based on this.
-  virtual void didStartLoading(unsigned long identifier) {}
-
-  // Tells the scheduler a resource load has stopped. The scheduler may make
-  // policy decisions based on this.
-  virtual void didStopLoading(unsigned long identifier) {}
-
-  // Tells the scheduler if we are parsing a document on another thread. This
-  // tells the scheduler not to advance virtual time if it's using the
-  // DETERMINISTIC_LOADING policy.
-  virtual void setDocumentParsingInBackground(bool) {}
-
-  // Tells the scheduler that the first meaningful paint has occured for this
-  // frame.
-  virtual void onFirstMeaningfulPaint() {}
-};
-
-}  // namespace blink
-
-#endif  // WebFrameScheduler_h
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index 73e51c4..e51ae30 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -145,7 +145,7 @@
   virtual void setBackgroundFilters(const cc::FilterOperations&) = 0;
 
   // Returns true if this layer has any active animations - useful for tests.
-  virtual bool hasActiveAnimationForTesting() = 0;
+  virtual bool hasTickingAnimationForTesting() = 0;
 
   // If a scroll parent is set, this layer will inherit its parent's scroll
   // delta and offset even though it will not be a descendant of the scroll
diff --git a/third_party/WebKit/public/platform/WebScreenInfo.h b/third_party/WebKit/public/platform/WebScreenInfo.h
index 3c89412..f51f78b 100644
--- a/third_party/WebKit/public/platform/WebScreenInfo.h
+++ b/third_party/WebKit/public/platform/WebScreenInfo.h
@@ -32,6 +32,7 @@
 #define WebScreenInfo_h
 
 #include "WebRect.h"
+#include "public/platform/ShapeProperties.h"
 #include "public/platform/modules/screen_orientation/WebScreenOrientationType.h"
 
 namespace blink {
@@ -77,13 +78,17 @@
   // It is the opposite of the physical rotation.
   uint16_t orientationAngle;
 
+  // This is the shape of display.
+  DisplayShape displayShape;
+
   WebScreenInfo()
       : deviceScaleFactor(1),
         depth(0),
         depthPerComponent(0),
         isMonochrome(false),
         orientationType(WebScreenOrientationUndefined),
-        orientationAngle(0) {}
+        orientationAngle(0),
+        displayShape(DisplayShapeRect) {}
 
   bool operator==(const WebScreenInfo& other) const {
     return this->deviceScaleFactor == other.deviceScaleFactor &&
@@ -93,7 +98,8 @@
            this->rect == other.rect &&
            this->availableRect == other.availableRect &&
            this->orientationType == other.orientationType &&
-           this->orientationAngle == other.orientationAngle;
+           this->orientationAngle == other.orientationAngle &&
+           this->displayShape == other.displayShape;
   }
 
   bool operator!=(const WebScreenInfo& other) const {
diff --git a/third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom b/third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom
index 32dc8c5e..d87bd0d 100644
--- a/third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom
+++ b/third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom
@@ -4,8 +4,6 @@
 
 module blink.mojom;
 
-import "mojo/common/common_custom_types.mojom";
-
 enum BackgroundSyncNetworkState {
   ANY,
   AVOID_CELLULAR,
diff --git a/third_party/WebKit/public/platform/modules/hyphenation/hyphenation.mojom b/third_party/WebKit/public/platform/modules/hyphenation/hyphenation.mojom
index 65c3d3c8..349076bd 100644
--- a/third_party/WebKit/public/platform/modules/hyphenation/hyphenation.mojom
+++ b/third_party/WebKit/public/platform/modules/hyphenation/hyphenation.mojom
@@ -4,9 +4,12 @@
 
 module blink.mojom;
 
+import "mojo/common/file.mojom";
+
 // Loads hyphenation dictionary.
 interface Hyphenation {
   // Returns a handle to the raw hyphneation dictionary.
   [Sync]
-  OpenDictionary(string locale) => (handle? hyphenation_dictionary_handle);
+  OpenDictionary(string locale) => (
+      mojo.common.mojom.File? hyphenation_dictionary_handle);
 };
diff --git a/third_party/WebKit/public/platform/modules/mediasession/media_session.mojom b/third_party/WebKit/public/platform/modules/mediasession/media_session.mojom
index b55e95a..3d4df05a 100644
--- a/third_party/WebKit/public/platform/modules/mediasession/media_session.mojom
+++ b/third_party/WebKit/public/platform/modules/mediasession/media_session.mojom
@@ -4,7 +4,7 @@
 
 module blink.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojo/url.mojom";
 
diff --git a/third_party/WebKit/public/web/WebDocument.h b/third_party/WebKit/public/web/WebDocument.h
index b126371..4c72430 100644
--- a/third_party/WebKit/public/web/WebDocument.h
+++ b/third_party/WebKit/public/web/WebDocument.h
@@ -128,12 +128,6 @@
   // Each call to this method overrides any previous calls.
   BLINK_EXPORT void watchCSSSelectors(const WebVector<WebString>& selectors);
 
-  // These methods are exposed only as a temporary fix for a stable-blocker
-  // regression (crbug.com/660706), and should be removed pronto.
-  // TODO(devlin): Make it happen!
-  BLINK_EXPORT bool unloadStartedDoNotUse() const;
-  BLINK_EXPORT bool processingBeforeUnloadDoNotUse() const;
-
   BLINK_EXPORT WebVector<WebDraggableRegion> draggableRegions() const;
 
   BLINK_EXPORT v8::Local<v8::Value> registerEmbedderCustomElement(
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index a650b6f..a6dde3392 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -12,6 +12,10 @@
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebURLError.h"
 
+namespace base {
+class SingleThreadTaskRunner;
+}
+
 namespace blink {
 
 class WebAutofillClient;
@@ -437,6 +441,14 @@
   // This will be removed following the deprecation.
   virtual void usageCountChromeLoadTimes(const WebString& metric) = 0;
 
+  // Task queues --------------------------------------------------------------
+
+  // Returns frame-specific task runner to run tasks of this type on.
+  // They have the same lifetime as the frame.
+  virtual base::SingleThreadTaskRunner* timerTaskRunner() = 0;
+  virtual base::SingleThreadTaskRunner* loadingTaskRunner() = 0;
+  virtual base::SingleThreadTaskRunner* unthrottledTaskRunner() = 0;
+
  protected:
   explicit WebLocalFrame(WebTreeScopeType scope) : WebFrame(scope) {}
 
diff --git a/third_party/WebKit/public/web/WebPerformance.h b/third_party/WebKit/public/web/WebPerformance.h
index a3bfba3..59e94f945 100644
--- a/third_party/WebKit/public/web/WebPerformance.h
+++ b/third_party/WebKit/public/web/WebPerformance.h
@@ -98,6 +98,7 @@
   BLINK_EXPORT double parseBlockedOnScriptExecutionFromDocumentWriteDuration()
       const;
   BLINK_EXPORT double authorStyleSheetParseDurationBeforeFCP() const;
+  BLINK_EXPORT double updateStyleDurationBeforeFCP() const;
 
 #if BLINK_IMPLEMENTATION
   WebPerformance(Performance*);
diff --git a/third_party/WebKit/public/web/WebRuntimeFeatures.h b/third_party/WebKit/public/web/WebRuntimeFeatures.h
index 2f448c9..c95e4aa 100644
--- a/third_party/WebKit/public/web/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/web/WebRuntimeFeatures.h
@@ -115,6 +115,7 @@
   BLINK_EXPORT static void enableSharedArrayBuffer(bool);
   BLINK_EXPORT static void enableSharedWorker(bool);
   BLINK_EXPORT static void enableSlimmingPaintV2(bool);
+  BLINK_EXPORT static void enableSlimmingPaintInvalidation(bool);
   BLINK_EXPORT static void enableSpeculativeLaunchServiceWorker(bool);
   BLINK_EXPORT static void enableTouchEventAPI(bool);
   BLINK_EXPORT static void enableV8IdleTasks(bool);
diff --git a/third_party/android_swipe_refresh/BUILD.gn b/third_party/android_swipe_refresh/BUILD.gn
index 8775e75..c2aa2e5e 100644
--- a/third_party/android_swipe_refresh/BUILD.gn
+++ b/third_party/android_swipe_refresh/BUILD.gn
@@ -14,6 +14,6 @@
     "java/src/org/chromium/third_party/android/swiperefresh/SwipeRefreshLayout.java",
   ]
   deps = [
-    "//third_party/android_tools:android_support_v4_java",
+    "//third_party/android_tools:android_support_core_ui_java",
   ]
 }
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index f42c424..5615d14 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -692,6 +692,9 @@
 /** @type {!ChromeEvent} */
 chrome.fileManagerPrivate.onDriveSyncError;
 
+/** @type {!ChromeEvent} */
+chrome.fileManagerPrivate.onAppsUpdated;
+
 /** @enum {string} */
 chrome.fileManagerPrivate.Verb = {
   OPEN_WITH: 'open_with',
diff --git a/third_party/custom_tabs_client/BUILD.gn b/third_party/custom_tabs_client/BUILD.gn
index 29f5dfd7..a53bd6b 100644
--- a/third_party/custom_tabs_client/BUILD.gn
+++ b/third_party/custom_tabs_client/BUILD.gn
@@ -53,7 +53,7 @@
   ]
   deps = [
     "//third_party/android_tools:android_support_annotations_java",
-    "//third_party/android_tools:android_support_v4_java",
+    "//third_party/android_tools:android_support_compat_java",
   ]
   srcjar_deps = [ ":chrome_custom_tabs_service_aidl" ]
   chromium_code = true
diff --git a/third_party/libaddressinput/chromium/json.cc b/third_party/libaddressinput/chromium/json.cc
index 887d55d..fbfcb3a 100644
--- a/third_party/libaddressinput/chromium/json.cc
+++ b/third_party/libaddressinput/chromium/json.cc
@@ -29,7 +29,7 @@
   // of the standalone library use char* rather than std::string.
   ::std::unique_ptr<const base::Value> parsed(
       base::JSONReader::Read(json.c_str()));
-  *parser_error = !parsed || !parsed->IsType(base::Value::TYPE_DICTIONARY);
+  *parser_error = !parsed || !parsed->IsType(base::Value::Type::DICTIONARY);
 
   if (*parser_error)
     result.reset(new base::DictionaryValue);
@@ -57,7 +57,7 @@
     if (sub_dicts_.empty()) {
       for (base::DictionaryValue::Iterator it(dict_); !it.IsAtEnd();
            it.Advance()) {
-        if (it.value().IsType(base::Value::TYPE_DICTIONARY)) {
+        if (it.value().IsType(base::Value::Type::DICTIONARY)) {
           const base::DictionaryValue* sub_dict = NULL;
           it.value().GetAsDictionary(&sub_dict);
           owned_sub_dicts_.push_back(
diff --git a/third_party/libjingle/webrtc/BUILD.gn b/third_party/libjingle/webrtc/BUILD.gn
index 00104d9..413d103 100644
--- a/third_party/libjingle/webrtc/BUILD.gn
+++ b/third_party/libjingle/webrtc/BUILD.gn
@@ -44,6 +44,7 @@
     "//third_party/usrsctp",
     "//third_party/webrtc/api:libjingle_peerconnection",
     "//third_party/webrtc/media:rtc_media",
+    "//third_party/webrtc/media:rtc_media_base",
     "//third_party/webrtc/modules/media_file",
     "//third_party/webrtc/modules/video_capture",
     "//third_party/webrtc/pc:rtc_pc",
diff --git a/third_party/libxml/README.chromium b/third_party/libxml/README.chromium
index 24b749b..cdd41e5 100644
--- a/third_party/libxml/README.chromium
+++ b/third_party/libxml/README.chromium
@@ -16,6 +16,7 @@
 - Add second workaround for VS 2015 Update 2 code-gen bug - crbug.com/599427
 - Apply patch contributed here: https://crbug.com/623378#c7
 - Apply patch contributed here: https://crbug.com/624011
+- Apply patch contributed here: https://crbug.com/628581#c18
 
 This import was generated by this script: https://goo.gl/72CTWf
 
diff --git a/third_party/libxml/src/entities.c b/third_party/libxml/src/entities.c
index 64808ff6..2851e2d 100644
--- a/third_party/libxml/src/entities.c
+++ b/third_party/libxml/src/entities.c
@@ -159,6 +159,7 @@
     memset(ret, 0, sizeof(xmlEntity));
     ret->type = XML_ENTITY_DECL;
     ret->checked = 0;
+    ret->guard = XML_ENTITY_NOT_BEING_CHECKED;
 
     /*
      * fill the structure.
@@ -931,6 +932,7 @@
 	cur->orig = xmlStrdup(ent->orig);
     if (ent->URI != NULL)
 	cur->URI = xmlStrdup(ent->URI);
+    cur->guard = 0;
     return(cur);
 }
 
diff --git a/third_party/libxml/src/include/libxml/entities.h b/third_party/libxml/src/include/libxml/entities.h
index 47b4573..012efab2 100644
--- a/third_party/libxml/src/include/libxml/entities.h
+++ b/third_party/libxml/src/include/libxml/entities.h
@@ -35,8 +35,13 @@
  * and the linkind data needed for the linking in the hash table.
  */
 
+typedef enum {
+  XML_ENTITY_NOT_BEING_CHECKED,
+  XML_ENTITY_BEING_CHECKED              /* entity check is in progress */
+} xmlEntityRecursionGuard;
+
 struct _xmlEntity {
-    void           *_private;	        /* application data */
+    void               *_private;	/* application data */
     xmlElementType          type;       /* XML_ENTITY_DECL, must be second ! */
     const xmlChar          *name;	/* Entity name */
     struct _xmlNode    *children;	/* First child link */
@@ -56,10 +61,11 @@
     struct _xmlEntity     *nexte;	/* unused */
     const xmlChar           *URI;	/* the full URI as computed */
     int                    owner;	/* does the entity own the childrens */
-    int			 checked;	/* was the entity content checked */
-					/* this is also used to count entities
-					 * references done from that entity
-					 * and if it contains '<' */
+    int                  checked;	/* was the entity content checked and */
+					/* l.o. bit: replacement contains '<' */
+					/* remaining bits: one plus count of */
+                                        /* entity references from this entity */
+    xmlEntityRecursionGuard guard;
 };
 
 /*
diff --git a/third_party/libxml/src/parser.c b/third_party/libxml/src/parser.c
index 53a6b7f..e313612 100644
--- a/third_party/libxml/src/parser.c
+++ b/third_party/libxml/src/parser.c
@@ -137,18 +137,24 @@
      * This may look absurd but is needed to detect
      * entities problems
      */
+    if ((ent != NULL) && (ent->guard == XML_ENTITY_BEING_CHECKED)) {
+        xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
+        return (1);
+    }
     if ((ent != NULL) && (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) &&
 	(ent->content != NULL) && (ent->checked == 0) &&
 	(ctxt->errNo != XML_ERR_ENTITY_LOOP)) {
 	unsigned long oldnbent = ctxt->nbentities;
 	xmlChar *rep;
 
+        ent->guard = XML_ENTITY_BEING_CHECKED;
 	ent->checked = 1;
 
         ++ctxt->depth;
 	rep = xmlStringDecodeEntities(ctxt, ent->content,
 				  XML_SUBSTITUTE_REF, 0, 0, 0);
         --ctxt->depth;
+        ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
 	if (ctxt->errNo == XML_ERR_ENTITY_LOOP) {
 	    ent->content[0] = 0;
 	}
@@ -7329,23 +7335,28 @@
 	 * if its replacement text matches the production labeled
 	 * content.
 	 */
-	if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
-	    ctxt->depth++;
-	    ret = xmlParseBalancedChunkMemoryInternal(ctxt, ent->content,
-	                                              user_data, &list);
-	    ctxt->depth--;
-
-	} else if (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
-	    ctxt->depth++;
-	    ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt, ctxt->sax,
-	                                   user_data, ctxt->depth, ent->URI,
-					   ent->ExternalID, &list);
-	    ctxt->depth--;
-	} else {
-	    ret = XML_ERR_ENTITY_PE_INTERNAL;
-	    xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
-			 "invalid entity type found\n", NULL);
-	}
+        if (ent->guard == XML_ENTITY_BEING_CHECKED) {
+            ret = XML_ERR_ENTITY_LOOP;
+        } else {
+            ent->guard = XML_ENTITY_BEING_CHECKED;
+            if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
+                ctxt->depth++;
+                ret = xmlParseBalancedChunkMemoryInternal(ctxt, ent->content,
+                                                          user_data, &list);
+                ctxt->depth--;
+            } else if (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
+                ctxt->depth++;
+                ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt, ctxt->sax,
+                                               user_data, ctxt->depth, ent->URI,
+                                               ent->ExternalID, &list);
+                ctxt->depth--;
+            } else {
+                ret = XML_ERR_ENTITY_PE_INTERNAL;
+                xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
+                             "invalid entity type found\n", NULL);
+            }
+            ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
+        }
 
 	/*
 	 * Store the number of entities needing parsing for this entity
@@ -7448,23 +7459,29 @@
 	    else
 		user_data = ctxt->userData;
 
-	    if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
-		ctxt->depth++;
-		ret = xmlParseBalancedChunkMemoryInternal(ctxt,
-				   ent->content, user_data, NULL);
-		ctxt->depth--;
-	    } else if (ent->etype ==
-		       XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
-		ctxt->depth++;
-		ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt,
-			   ctxt->sax, user_data, ctxt->depth,
-			   ent->URI, ent->ExternalID, NULL);
-		ctxt->depth--;
-	    } else {
-		ret = XML_ERR_ENTITY_PE_INTERNAL;
-		xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
-			     "invalid entity type found\n", NULL);
-	    }
+            if (ent->guard == XML_ENTITY_BEING_CHECKED) {
+                ret = XML_ERR_ENTITY_LOOP;
+            } else {
+                ent->guard = XML_ENTITY_BEING_CHECKED;
+                if (ent->etype == XML_INTERNAL_GENERAL_ENTITY) {
+                    ctxt->depth++;
+                    ret = xmlParseBalancedChunkMemoryInternal(ctxt,
+                                       ent->content, user_data, NULL);
+                    ctxt->depth--;
+                } else if (ent->etype ==
+                           XML_EXTERNAL_GENERAL_PARSED_ENTITY) {
+                    ctxt->depth++;
+                    ret = xmlParseExternalEntityPrivate(ctxt->myDoc, ctxt,
+                               ctxt->sax, user_data, ctxt->depth,
+                               ent->URI, ent->ExternalID, NULL);
+                    ctxt->depth--;
+                } else {
+                    ret = XML_ERR_ENTITY_PE_INTERNAL;
+                    xmlErrMsgStr(ctxt, XML_ERR_INTERNAL_ERROR,
+                                 "invalid entity type found\n", NULL);
+                }
+                ent->guard = XML_ENTITY_NOT_BEING_CHECKED;
+            }
 	    if (ret == XML_ERR_ENTITY_LOOP) {
 		xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL);
 		return;
diff --git a/ios/third_party/material_text_accessibility_ios/LICENSE b/third_party/retrolambda/LICENSE.txt
similarity index 100%
rename from ios/third_party/material_text_accessibility_ios/LICENSE
rename to third_party/retrolambda/LICENSE.txt
diff --git a/third_party/retrolambda/OWNERS b/third_party/retrolambda/OWNERS
new file mode 100644
index 0000000..e96e054
--- /dev/null
+++ b/third_party/retrolambda/OWNERS
@@ -0,0 +1,2 @@
+agrieve@chromium.org
+zpeng@chromium.org
diff --git a/third_party/retrolambda/README.chromium b/third_party/retrolambda/README.chromium
new file mode 100644
index 0000000..5fc43cf
--- /dev/null
+++ b/third_party/retrolambda/README.chromium
@@ -0,0 +1,20 @@
+Name: Retrolambda, backport of Java 8's lambda expressions to Java 7, 6, 5
+Short Name: retrolambda
+URL: https://github.com/orfjackal/retrolambda/
+Version: 2.3.0
+License: Apache 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Retrolambda is a Java library used by the build process to transform Java 8's
+bytecode to Java 7's bytecode. It is used to enable Java 8 features such as
+lambda expressions for Chrome on Android without switching from javac to Jack.
+
+Local Modifications:
+None
+
+Source of the .jar:
+Downloaded from Maven Central. Link:
+http://search.maven.org/remotecontent?filepath=net/orfjackal/retrolambda/retrolambda/2.3.0/retrolambda-2.3.0.jar
+
diff --git a/third_party/retrolambda/retrolambda-2.3.0.jar.sha1 b/third_party/retrolambda/retrolambda-2.3.0.jar.sha1
new file mode 100644
index 0000000..4aa8d1d
--- /dev/null
+++ b/third_party/retrolambda/retrolambda-2.3.0.jar.sha1
@@ -0,0 +1 @@
+f35f58f7946d2f8be3dcff78aa3114f60aa40051
\ No newline at end of file
diff --git a/third_party/tcmalloc/README.chromium b/third_party/tcmalloc/README.chromium
index 7fa86454..e018d13 100644
--- a/third_party/tcmalloc/README.chromium
+++ b/third_party/tcmalloc/README.chromium
@@ -103,3 +103,4 @@
 - Add TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC mallopt() arg
 - Added tc_malloc_skip_new_handler.
 - Added TCMALLOC_DONT_REPLACE_SYSTEM_ALLOC which bypasses the libc_override logic.
+- Backported 7df7f14 "issue-693: enable futex usage on arm" from upstream.
diff --git a/third_party/tcmalloc/chromium/src/base/linux_syscall_support.h b/third_party/tcmalloc/chromium/src/base/linux_syscall_support.h
index bdbc4b7e..b29ec2d 100644
--- a/third_party/tcmalloc/chromium/src/base/linux_syscall_support.h
+++ b/third_party/tcmalloc/chromium/src/base/linux_syscall_support.h
@@ -82,7 +82,6 @@
  *      sys_fcntl(
  *      sys_fstat(
  *      sys_futex(
- *      sys_futex1(
  *      sys_getcpu(
  *      sys_getdents(
  *      sys_getppid(
diff --git a/third_party/tcmalloc/chromium/src/base/spinlock_linux-inl.h b/third_party/tcmalloc/chromium/src/base/spinlock_linux-inl.h
index 4a1498d..2f2ca473 100644
--- a/third_party/tcmalloc/chromium/src/base/spinlock_linux-inl.h
+++ b/third_party/tcmalloc/chromium/src/base/spinlock_linux-inl.h
@@ -61,13 +61,8 @@
     int x = 0;
     // futexes are ints, so we can use them only when
     // that's the same size as the lockword_ in SpinLock.
-#ifdef __arm__
-    // ARM linux doesn't support sys_futex1(void*, int, int, struct timespec*);
-    have_futex = 0;
-#else
     have_futex = (sizeof (Atomic32) == sizeof (int) && 
                   syscall(__NR_futex, &x, FUTEX_WAKE, 1, 0) >= 0);
-#endif
     if (have_futex &&
         syscall(__NR_futex, &x, FUTEX_WAKE | futex_private_flag, 1, 0) < 0) {
       futex_private_flag = 0;
diff --git a/tools/android/audio_focus_grabber/BUILD.gn b/tools/android/audio_focus_grabber/BUILD.gn
index 89e19d2a..7e394b3c 100644
--- a/tools/android/audio_focus_grabber/BUILD.gn
+++ b/tools/android/audio_focus_grabber/BUILD.gn
@@ -12,7 +12,7 @@
   deps = [
     ":audio_focus_grabber_apk_resources",
     "//base:base_java",
-    "//third_party/android_tools:android_support_v4_java",
+    "//third_party/android_tools:android_support_compat_java",
   ]
 
   java_files = [
diff --git a/tools/chrome_proxy/webdriver/common.py b/tools/chrome_proxy/webdriver/common.py
index 29c7e58..be5ba9d 100644
--- a/tools/chrome_proxy/webdriver/common.py
+++ b/tools/chrome_proxy/webdriver/common.py
@@ -5,6 +5,8 @@
 import argparse
 import json
 import os
+import re
+import socket
 import shlex
 import sys
 import time
@@ -15,13 +17,15 @@
 from selenium import webdriver
 from selenium.webdriver.chrome.options import Options
 
-# TODO(robertogden) add logging
+# TODO(robertogden): Add logging.
 
 
 def ParseFlags():
-  """
-  Parses the given command line arguments and returns an object with the flags
-  as properties.
+  """Parses the given command line arguments.
+
+  Returns:
+    A new Namespace object with class properties for each argument added below.
+    See pydoc for argparse.
   """
   parser = argparse.ArgumentParser()
   parser.add_argument('--browser_args', nargs=1, type=str, help='Override '
@@ -29,18 +33,23 @@
   parser.add_argument('--via_header_value', metavar='via_header', nargs=1,
     default='1.1 Chrome-Compression-Proxy', help='What the via should match to '
     'be considered valid')
+  parser.add_argument('--android', help='If given, attempts to run the test on '
+    'Android via adb. Ignores usage of --chrome_exec', action='store_true')
+  parser.add_argument('--android_package', nargs=1,
+    default='com.android.chrome', help='Set the android package for Chrome')
   parser.add_argument('--chrome_exec', nargs=1, type=str, help='The path to '
     'the Chrome or Chromium executable')
   parser.add_argument('chrome_driver', nargs=1, type=str, help='The path to '
     'the ChromeDriver executable. If not given, the default system chrome '
     'will be used.')
-  # TODO(robertogden) make this a logging statement
-  print 'DEBUG: Args=', json.dumps(vars(parser.parse_args(sys.argv[1:])))
+  # TODO(robertogden): Log sys.argv here.
   return parser.parse_args(sys.argv[1:])
 
 def HandleException(test_name=None):
-  """
-  Writes the exception being handled and a stack trace to stderr.
+  """Writes the exception being handled and a stack trace to stderr.
+
+  Args:
+    test_name: The string name of the test that led to this exception.
   """
   sys.stderr.write("**************************************\n")
   sys.stderr.write("**************************************\n")
@@ -55,11 +64,18 @@
   sys.exit(1)
 
 class TestDriver:
-  """
+  """The main driver for an integration test.
+
   This class is the tool that is used by every integration test to interact with
   the Chromium browser and validate proper functionality. This class sits on top
   of the Selenium Chrome Webdriver with added utility and helper functions for
   Chrome-Proxy. This class should be used with Python's 'with' operator.
+
+  Attributes:
+    _flags: A Namespace object from the call to ParseFlags()
+    _driver: A reference to the driver object from the Chrome Driver library.
+    _chrome_args: A set of string arguments to start Chrome with.
+    _url: The string URL that Chrome will navigate to for this test.
   """
 
   def __init__(self):
@@ -76,9 +92,10 @@
       self._StopDriver()
 
   def _OverrideChromeArgs(self):
-    """
-    Overrides any given arguments in the code with those given on the command
-    line. Arguments that need to be overridden may contain different values for
+    """Overrides any given arguments in the code with those given on the command
+    line.
+
+    Arguments that need to be overridden may contain different values for
     a flag given in the code. In that case, check by the flag whether to
     override the argument.
     """
@@ -89,75 +106,92 @@
       original_args = {}
       for arg in self._chrome_args:
           original_args[GetDictKey(arg)] = arg
-      # Override by flag.
+      # Override flags given in code with any command line arguments.
       for override_arg in shlex.split(self._flags.browser_args[0]):
         arg_key = GetDictKey(override_arg)
         if arg_key in original_args:
           self._chrome_args.remove(original_args[arg_key])
         self._chrome_args.add(override_arg)
+    # Always add the flag that allows histograms to be queried in javascript.
+    self._chrome_args.add('--enable-stats-collection-bindings')
 
   def _StartDriver(self):
-    """
-    Parses the flags to pass to Chromium, then starts the ChromeDriver.
+    """Parses the flags to pass to Chromium, then starts the ChromeDriver.
+
+    If running Android, the Android package name is passed to ChromeDriver here.
     """
     self._OverrideChromeArgs()
-    options = Options()
-    for arg in self._chrome_args:
-      options.add_argument(arg)
-    capabilities = {'loggingPrefs': {'performance': 'INFO'}}
-    if self._flags.chrome_exec:
+    capabilities = {
+      'loggingPrefs': {'performance': 'INFO'},
+      'chromeOptions': {
+        'args': list(self._chrome_args)
+      }
+    }
+    if self._flags.android:
+      capabilities['chromeOptions'].update({
+        'androidPackage': self._flags.android_package,
+      })
+    elif self._flags.chrome_exec:
       capabilities['chrome.binary'] = self._flags.chrome_exec
     driver = webdriver.Chrome(executable_path=self._flags.chrome_driver[0],
-      chrome_options=options, desired_capabilities=capabilities)
+      desired_capabilities=capabilities)
     driver.command_executor._commands.update({
       'getAvailableLogTypes': ('GET', '/session/$sessionId/log/types'),
       'getLog': ('POST', '/session/$sessionId/log')})
     self._driver = driver
 
   def _StopDriver(self):
-    """
-    Nicely stops the ChromeDriver.
+    """Nicely stops the ChromeDriver.
     """
     self._driver.quit()
-    del self._driver
+    self._driver = None
 
   def AddChromeArgs(self, args):
-    """
-    Adds multiple arguments that will be passed to Chromium at start.
+    """Adds multiple arguments that will be passed to Chromium at start.
+
+    Args:
+      args: An iterable of strings, each an argument to pass to Chrome at start.
     """
     for arg in args:
       self._chrome_args.add(arg)
 
   def AddChromeArg(self, arg):
-    """
-    Adds a single argument that will be passed to Chromium at start.
+    """Adds a single argument that will be passed to Chromium at start.
+
+    Args:
+      arg: a string argument to pass to Chrome at start
     """
     self._chrome_args.add(arg)
 
   def RemoveChromeArgs(self, args):
-    """
-    Removes multiple arguments that will no longer be passed to Chromium at
+    """Removes multiple arguments that will no longer be passed to Chromium at
     start.
+
+    Args:
+        args: An iterable of strings to no longer use the next time Chrome
+          starts.
     """
     for arg in args:
       self._chrome_args.discard(arg)
 
   def RemoveChromeArg(self, arg):
-    """
-    Removes a single argument that will no longer be passed to Chromium at
+    """Removes a single argument that will no longer be passed to Chromium at
     start.
+
+    Args:
+      arg: A string flag to no longer use the next time Chrome starts.
     """
     self._chrome_args.discard(arg)
 
   def ClearChromeArgs(self):
-    """
-    Removes all arguments from Chromium at start.
+    """Removes all arguments from Chromium at start.
     """
     self._chrome_args.clear()
 
   def ClearCache(self):
-    """
-    Clears the browser cache. Important note: ChromeDriver automatically starts
+    """Clears the browser cache.
+
+    Important note: ChromeDriver automatically starts
     a clean copy of Chrome on every instantiation.
     """
     self.ExecuteJavascript('if(window.chrome && chrome.benchmarking && '
@@ -166,41 +200,186 @@
       'clearHostResolverCache();}')
 
   def SetURL(self, url):
-    """
-    Sets the URL that the browser will navigate to during the test.
+    """Sets the URL that the browser will navigate to during the test.
+
+    Args:
+      url: The string URL to navigate to
     """
     self._url = url
 
-  # TODO(robertogden) add timeout
-  def LoadPage(self):
-    """
-    Starts Chromium with any arguments previously given and navigates to the
-    previously given URL.
+  def LoadPage(self, timeout=30):
+    """Starts Chromium with any arguments previously given and navigates to the
+    given URL.
+
+    Args:
+      timeout: Page load timeout in seconds.
     """
     if not self._driver:
       self._StartDriver()
+    self._driver.set_page_load_timeout(timeout)
     self._driver.get(self._url)
 
-  # TODO(robertogden) add timeout
-  def ExecuteJavascript(self, script):
-    """
-    Executes the given javascript in the browser's current page as if it were on
-    the console. Returns a string of whatever the evaluation was.
+  def ExecuteJavascript(self, script, timeout=30):
+    """Executes the given javascript in the browser's current page in an
+    anonymous function.
+
+    If you expect a result and don't get one, try adding a return statement or
+    using ExecuteJavascriptStatement() below.
+
+    Args:
+      script: A string of Javascript code.
+      timeout: Timeout for the Javascript code to return in seconds.
+    Returns:
+      A string of the verbatim output from the Javascript execution.
     """
     if not self._driver:
       self._StartDriver()
-    return self._driver.execute_script("return " + script)
+    # TODO(robertogden): Use 'driver.set_script_timeout(timeout)' instead after
+    # crbug/672114 is fixed.
+    default_timeout = socket.getdefaulttimeout()
+    socket.setdefaulttimeout(timeout)
+    script_result = self._driver.execute_script(script)
+    socket.setdefaulttimeout(default_timeout)
+    return script_result
 
+  def ExecuteJavascriptStatement(self, script, timeout=30):
+    """Wraps ExecuteJavascript() for use with a single statement.
+
+    Behavior is analogous to 'function(){ return <script> }();'
+
+    Args:
+      script: A string of Javascript code.
+      timeout: Timeout for the Javascript code to return in seconds.
+    Returns:
+      A string of the verbatim output from the Javascript execution.
+    """
+    return self.ExecuteJavascript("return " + script, timeout)
+
+  def GetHistogram(self, histogram):
+    js_query = 'statsCollectionController.getBrowserHistogram("%s")' % histogram
+    string_response = self.ExecuteJavascriptStatement(js_query)
+    return json.loads(string_response)
+
+  def GetPerformanceLogs(self, method_filter=r'Network\.responseReceived'):
+    """Returns all logged Performance events from Chrome.
+
+    Args:
+      method_filter: A regex expression to match the method of logged events
+        against. Only logs who's method matches the regex will be returned.
+    Returns:
+      Performance logs as a list of dicts, since the last time this function was
+      called.
+    """
+    all_messages = []
+    for log in self._driver.execute('getLog', {'type': 'performance'})['value']:
+      message = json.loads(log['message'])['message']
+      if re.match(method_filter, message['method']):
+        all_messages.append(message)
+    return all_messages
+
+  def GetHTTPResponses(self, include_favicon=False):
+    """Parses the Performance Logs and returns a list of HTTPResponse objects.
+
+    This function should be called exactly once after every page load.
+
+    Args:
+      include_favicon: A bool that if True will include responses for favicons.
+    Returns:
+      A list of HTTPResponse objects, each representing a single completed HTTP
+      transaction by Chrome.
+    """
+    def MakeHTTPResponse(log_dict):
+      params = log_dict['params']
+      response_dict = params['response']
+      http_response_dict = {
+        'response_headers': response_dict['headers'],
+        'request_headers': response_dict['requestHeaders'],
+        'url': response_dict['url'],
+        'status': response_dict['status'],
+        'request_type': params['type']
+      }
+      return HTTPResponse(**http_response_dict)
+    all_responses = []
+    for message in self.GetPerformanceLogs():
+      response = MakeHTTPResponse(message)
+      is_favicon = response.url.endswith('favicon.ico')
+      if not is_favicon or include_favicon:
+        all_responses.append(response)
+    return all_responses
+
+class HTTPResponse:
+  """This class represents a single HTTP transaction (request and response) by
+  Chrome.
+
+  This class also includes several convenience functions for ChromeProxy
+  specific assertions.
+
+  Attributes:
+    _response_headers: A dict of response headers.
+    _request_headers: A dict of request headers.
+    _url: the fetched url
+    _status: The integer status code of the response
+    _request_type: What caused this request (Document, XHR, etc)
+    _flags: A Namespace object from ParseFlags()
+  """
+
+  def __init__(self, response_headers, request_headers, url, status,
+      request_type):
+    self._response_headers = response_headers
+    self._request_headers = request_headers
+    self._url = url
+    self._status = status
+    self._request_type = request_type
+    self._flags = ParseFlags()
+
+  def __str__(self):
+    self_dict = {
+      'response_headers': self._response_headers,
+      'request_headers': self._request_headers,
+      'url': self._url,
+      'status': self._status,
+      'request_type': self._request_type
+    }
+    return json.dumps(self_dict)
+
+  @property
+  def response_headers(self):
+    return self._response_headers
+
+  @property
+  def request_headers(self):
+    return self._request_headers
+
+  @property
+  def url(self):
+    return self._url
+
+  @property
+  def status(self):
+    return self._status
+
+  @property
+  def request_type(self):
+    return self._request_type
+
+  def ResponseHasViaHeader(self):
+    return 'via' in self._response_headers and (self._response_headers['via'] ==
+      self._flags.via_header_value)
+
+  def WasXHR(self):
+    return self.request_type == 'XHR'
 
 class IntegrationTest:
-  """
-  A parent class for all integration tests with utility and assertion methods.
+  """A parent class for all integration tests with utility and assertion
+  methods.
+
   All methods starting with the word 'test' (ignoring case) will be called with
   the RunAllTests() method which can be used in place of a main method.
   """
   def RunAllTests(self):
-    """
-    Runs all methods starting with the word 'test' (ignoring case) in the class.
+    """Runs all methods starting with the word 'test' (ignoring case) in the
+    class.
+
     Can be used in place of a main method to run all tests in a class.
     """
     methodList = [method for method in dir(self) if callable(getattr(self,
@@ -209,12 +388,17 @@
       try:
         getattr(self, method)()
       except Exception as e:
-        # Uses the Exception tuple from sys.exec_info()
+        # Uses the Exception tuple from sys.exec_info().
         HandleException(method)
 
-  # TODO(robertogden) add some nice assertion functions
+  # TODO(robertogden): Add some nice assertion functions.
 
   def Fail(self, msg):
+    """Called when a test fails an assertion.
+
+    Args:
+      msg: The string message to print to stderr
+    """
     sys.stderr.write("**************************************\n")
     sys.stderr.write("**************************************\n")
     sys.stderr.write("**                                  **\n")
diff --git a/tools/chrome_proxy/webdriver/simple_smoke.py b/tools/chrome_proxy/webdriver/simple_smoke.py
index c07b447..5513be9e 100644
--- a/tools/chrome_proxy/webdriver/simple_smoke.py
+++ b/tools/chrome_proxy/webdriver/simple_smoke.py
@@ -13,11 +13,25 @@
   # Simple example integration test.
   def TestCheckPageWithProxy(self):
     with TestDriver() as t:
-      t.AddChromeArgs(['--enable-spdy-proxy-auth'])
+      t.AddChromeArg('--enable-spdy-proxy-auth')
       t.SetURL('http://check.googlezip.net/test.html')
       t.LoadPage()
-      print 'Document Title: ', t.ExecuteJavascript('document.title')
+      print 'Document Title: ', t.ExecuteJavascriptStatement('document.title',
+        timeout=1)
       time.sleep(5)
+      responses = t.GetHTTPResponses()
+      for response in responses:
+        print "URL: %s, ViaHeader: %s, XHR: %s" % (response.url,
+          response.ResponseHasViaHeader(), response.WasXHR())
+
+  # Show how to get a histogram.
+  def TestPingbackHistogram(self):
+    with TestDriver() as t:
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      t.SetURL('http://check.googlezip.net/test.html')
+      t.LoadPage()
+      t.LoadPage()
+      print t.GetHistogram('DataReductionProxy.Pingback.Attempted')
 
 if __name__ == '__main__':
   test = SimpleSmoke()
diff --git a/tools/clang/blink_gc_plugin/CheckFieldsVisitor.h b/tools/clang/blink_gc_plugin/CheckFieldsVisitor.h
index ef806f2..18a791df 100644
--- a/tools/clang/blink_gc_plugin/CheckFieldsVisitor.h
+++ b/tools/clang/blink_gc_plugin/CheckFieldsVisitor.h
@@ -9,7 +9,6 @@
 
 #include "Edge.h"
 
-struct BlinkGCPluginOptions;
 class FieldPoint;
 
 // This visitor checks that the fields of a class are "well formed".
diff --git a/tools/gn/builder.h b/tools/gn/builder.h
index 573d8ed..e10df4d8 100644
--- a/tools/gn/builder.h
+++ b/tools/gn/builder.h
@@ -14,7 +14,6 @@
 #include "tools/gn/label_ptr.h"
 #include "tools/gn/unique_vector.h"
 
-class Config;
 class Err;
 class Loader;
 class ParseNode;
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 9f7f903..1de9fbb 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -56,7 +56,7 @@
       PrintValue(&iter.value(), indentLevel + 1);
       iter.Advance();
     }
-  } else if (value->IsType(base::Value::TYPE_NULL)) {
+  } else if (value->IsType(base::Value::Type::NONE)) {
     OutputString(indent + "<null>\n");
   }
 }
diff --git a/tools/gn/config_values_generator.h b/tools/gn/config_values_generator.h
index 60878786..7fd803c 100644
--- a/tools/gn/config_values_generator.h
+++ b/tools/gn/config_values_generator.h
@@ -11,7 +11,6 @@
 class ConfigValues;
 class Err;
 class Scope;
-class Token;
 
 // This class fills in the config values from a given scope. It's shared
 // between the "config" function call and all the different binary target types
diff --git a/tools/gn/deps_iterator.h b/tools/gn/deps_iterator.h
index 58f2b34..28062ae2 100644
--- a/tools/gn/deps_iterator.h
+++ b/tools/gn/deps_iterator.h
@@ -9,8 +9,6 @@
 
 #include "tools/gn/label_ptr.h"
 
-class Target;
-
 // Provides an iterator for iterating over multiple LabelTargetVectors to
 // make it convenient to iterate over all deps of a target.
 //
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index c17ab39d..bc14eb92 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -15,8 +15,6 @@
 #include "tools/gn/target.h"
 
 class Err;
-class Location;
-class Value;
 
 std::string FilePathToUTF8(const base::FilePath::StringType& str);
 inline std::string FilePathToUTF8(const base::FilePath& path) {
diff --git a/tools/gn/functions.h b/tools/gn/functions.h
index 0c97700..401384acb 100644
--- a/tools/gn/functions.h
+++ b/tools/gn/functions.h
@@ -18,7 +18,6 @@
 class ListNode;
 class ParseNode;
 class Scope;
-class Token;
 class Value;
 
 // -----------------------------------------------------------------------------
diff --git a/tools/gn/header_checker.h b/tools/gn/header_checker.h
index 8f9212f..0c0eb4a9 100644
--- a/tools/gn/header_checker.h
+++ b/tools/gn/header_checker.h
@@ -19,7 +19,6 @@
 
 class BuildSettings;
 class InputFile;
-class Label;
 class LocationRange;
 class SourceFile;
 class Target;
diff --git a/tools/gn/ninja_build_writer.h b/tools/gn/ninja_build_writer.h
index 42bbb84..df6d1c8 100644
--- a/tools/gn/ninja_build_writer.h
+++ b/tools/gn/ninja_build_writer.h
@@ -15,7 +15,6 @@
 class Builder;
 class BuildSettings;
 class Err;
-class Pool;
 class Settings;
 class Target;
 class Toolchain;
diff --git a/tools/gn/ninja_copy_target_writer.h b/tools/gn/ninja_copy_target_writer.h
index 9e1746e..a45a470 100644
--- a/tools/gn/ninja_copy_target_writer.h
+++ b/tools/gn/ninja_copy_target_writer.h
@@ -8,8 +8,6 @@
 #include "base/macros.h"
 #include "tools/gn/ninja_target_writer.h"
 
-class Tool;
-
 // Writes a .ninja file for a copy target type.
 class NinjaCopyTargetWriter : public NinjaTargetWriter {
  public:
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
index 72ecbca..5a3af18 100644
--- a/tools/gn/ninja_target_writer.h
+++ b/tools/gn/ninja_target_writer.h
@@ -11,7 +11,6 @@
 #include "tools/gn/path_output.h"
 #include "tools/gn/substitution_type.h"
 
-class FileTemplate;
 class OutputFile;
 class Settings;
 class Target;
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
index 9ddde849..9c6ad24 100644
--- a/tools/gn/ninja_toolchain_writer.h
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -16,10 +16,8 @@
 #include "tools/gn/path_output.h"
 #include "tools/gn/toolchain.h"
 
-class BuildSettings;
 struct EscapeOptions;
 class Settings;
-class Target;
 class Tool;
 
 class NinjaToolchainWriter {
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
index 4bcbf366..6372f0cf 100644
--- a/tools/gn/ninja_writer.h
+++ b/tools/gn/ninja_writer.h
@@ -15,8 +15,6 @@
 class Builder;
 class BuildSettings;
 class Err;
-class Pool;
-class Settings;
 class Target;
 class Toolchain;
 
diff --git a/tools/gn/operators.cc b/tools/gn/operators.cc
index 72b2dd1..350fef4 100644
--- a/tools/gn/operators.cc
+++ b/tools/gn/operators.cc
@@ -419,7 +419,7 @@
                    const Value& right,
                    Err* err) {
   // Left-hand-side int. The only thing to do is subtract another int.
-  if (left.type() == Value::INTEGER && right.type() != Value::INTEGER) {
+  if (left.type() == Value::INTEGER && right.type() == Value::INTEGER) {
     // Int - int -> subtraction.
     return Value(op_node, left.int_value() - right.int_value());
   }
diff --git a/tools/gn/operators_unittest.cc b/tools/gn/operators_unittest.cc
index fa163a1..4c9c670 100644
--- a/tools/gn/operators_unittest.cc
+++ b/tools/gn/operators_unittest.cc
@@ -237,6 +237,34 @@
   EXPECT_EQ("bar", new_value->list_value()[0].string_value());
 }
 
+TEST(Operators, IntegerAdd) {
+  Err err;
+  TestWithScope setup;
+
+  TestBinaryOpNode node(Token::PLUS, "+");
+  node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
+  node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
+  Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+                                    node.right(), &err);
+  ASSERT_FALSE(err.has_error());
+  ASSERT_EQ(Value::INTEGER, ret.type());
+  EXPECT_EQ(579, ret.int_value());
+}
+
+TEST(Operators, IntegerSubtract) {
+  Err err;
+  TestWithScope setup;
+
+  TestBinaryOpNode node(Token::MINUS, "-");
+  node.SetLeftToValue(Value(nullptr, static_cast<int64_t>(123)));
+  node.SetRightToValue(Value(nullptr, static_cast<int64_t>(456)));
+  Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
+                                    node.right(), &err);
+  ASSERT_FALSE(err.has_error());
+  ASSERT_EQ(Value::INTEGER, ret.type());
+  EXPECT_EQ(-333, ret.int_value());
+}
+
 TEST(Operators, ShortCircuitAnd) {
   Err err;
   TestWithScope setup;
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index 18e1ea12..447dfcf5 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -851,8 +851,8 @@
     if ((*i)->AsFunctionCall() || (*i)->AsList() || (*i)->AsBlock())
       continue;
 
-    const Location& start = (*i)->GetRange().begin();
-    const Location& end = (*i)->GetRange().end();
+    Location start = (*i)->GetRange().begin();
+    Location end = (*i)->GetRange().end();
 
     // Don't assign suffix comments to something that starts on an earlier
     // line, so that in:
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
index 36de0884..31bac626 100644
--- a/tools/gn/scope.h
+++ b/tools/gn/scope.h
@@ -19,12 +19,9 @@
 #include "tools/gn/source_dir.h"
 #include "tools/gn/value.h"
 
-class FunctionCallNode;
-class ImportManager;
 class Item;
 class ParseNode;
 class Settings;
-class TargetManager;
 class Template;
 
 // Scope for the script execution.
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 747b7b9e..1890a535 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -27,9 +27,7 @@
 #include "tools/gn/unique_vector.h"
 
 class DepsIteratorRange;
-class InputFile;
 class Settings;
-class Token;
 class Toolchain;
 
 class Target : public Item {
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index 6e50428..b6c0bc6 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -36,10 +36,15 @@
   bool capture_at_generation;
 };
 
+// clang-format off
 SafeEnvironmentVariableInfo kSafeEnvironmentVariables[] = {
-    {"HOME", true}, {"LANG", true},    {"PATH", true},
-    {"USER", true}, {"TMPDIR", false},
+    {"HOME", true},
+    {"LANG", true},
+    {"PATH", true},
+    {"USER", true},
+    {"TMPDIR", false},
 };
+// clang-format on
 
 XcodeWriter::TargetOsType GetTargetOs(const Args& args) {
   const Value* target_os_value = args.GetArgOverride(variables::kTargetOs);
@@ -63,8 +68,7 @@
   // variable overridding settings, including the SDK, thus breaking hermetic
   // build).
   script << "env -i ";
-  for (size_t i = 0; i < arraysize(kSafeEnvironmentVariables); ++i) {
-    const auto& variable = kSafeEnvironmentVariables[i];
+  for (const auto& variable : kSafeEnvironmentVariables) {
     script << variable.name << "=\"";
 
     std::string value;
diff --git a/tools/gn/xcode_writer.h b/tools/gn/xcode_writer.h
index 87a7d58..7373cff0 100644
--- a/tools/gn/xcode_writer.h
+++ b/tools/gn/xcode_writer.h
@@ -22,10 +22,6 @@
 using PBXAttributes = std::map<std::string, std::string>;
 class PBXProject;
 
-namespace base {
-class FilePath;
-}
-
 class XcodeWriter {
  public:
   enum TargetOsType {
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index e9e497d..479bdc8d 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -252,12 +252,6 @@
     "enable_task_manager",
   ]
 }
-if (enable_notifications) {
-  grit_defines += [
-    "-D",
-    "enable_notifications",
-  ]
-}
 
 # TODO(aberent): Enable for other platforms once the build machines have
 #                Java on them (and hence can run the closure compiler).
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index 868d4ff..04810dc 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -504,36 +504,36 @@
     if (fuzzer->ShouldGenerate())
       list_length = g_depth > 3 ? 0 : RandInRange(8);
     for (size_t index = 0; index < list_length; ++index) {
-      switch (RandInRange(8)) {
-        case base::Value::TYPE_BOOLEAN: {
+      switch (static_cast<base::Value::Type>(RandInRange(8))) {
+        case base::Value::Type::BOOLEAN: {
           bool tmp;
           p->GetBoolean(index, &tmp);
           fuzzer->FuzzBool(&tmp);
           p->Set(index, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_INTEGER: {
+        case base::Value::Type::INTEGER: {
           int tmp;
           p->GetInteger(index, &tmp);
           fuzzer->FuzzInt(&tmp);
           p->Set(index, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_DOUBLE: {
+        case base::Value::Type::DOUBLE: {
           double tmp;
           p->GetDouble(index, &tmp);
           fuzzer->FuzzDouble(&tmp);
           p->Set(index, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_STRING: {
+        case base::Value::Type::STRING: {
           std::string tmp;
           p->GetString(index, &tmp);
           fuzzer->FuzzString(&tmp);
           p->Set(index, new base::StringValue(tmp));
           break;
         }
-        case base::Value::TYPE_BINARY: {
+        case base::Value::Type::BINARY: {
           char tmp[200];
           size_t bin_length = RandInRange(sizeof(tmp));
           fuzzer->FuzzData(tmp, bin_length);
@@ -541,21 +541,21 @@
                  base::BinaryValue::CreateWithCopiedBuffer(tmp, bin_length));
           break;
         }
-        case base::Value::TYPE_DICTIONARY: {
+        case base::Value::Type::DICTIONARY: {
           base::DictionaryValue* tmp = new base::DictionaryValue();
           p->GetDictionary(index, &tmp);
           FuzzParam(tmp, fuzzer);
           p->Set(index, tmp);
           break;
         }
-        case base::Value::TYPE_LIST: {
+        case base::Value::Type::LIST: {
           base::ListValue* tmp = new base::ListValue();
           p->GetList(index, &tmp);
           FuzzParam(tmp, fuzzer);
           p->Set(index, tmp);
           break;
         }
-        case base::Value::TYPE_NULL:
+        case base::Value::Type::NONE:
         default:
           break;
       }
@@ -577,32 +577,32 @@
     for (size_t index = 0; index < dict_length; ++index) {
       std::string property;
       fuzzer->FuzzString(&property);
-      switch (RandInRange(8)) {
-        case base::Value::TYPE_BOOLEAN: {
+      switch (static_cast<base::Value::Type>(RandInRange(8))) {
+        case base::Value::Type::BOOLEAN: {
           bool tmp;
           fuzzer->FuzzBool(&tmp);
           p->SetWithoutPathExpansion(property, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_INTEGER: {
+        case base::Value::Type::INTEGER: {
           int tmp;
           fuzzer->FuzzInt(&tmp);
           p->SetWithoutPathExpansion(property, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_DOUBLE: {
+        case base::Value::Type::DOUBLE: {
           double tmp;
           fuzzer->FuzzDouble(&tmp);
           p->SetWithoutPathExpansion(property, new base::FundamentalValue(tmp));
           break;
         }
-        case base::Value::TYPE_STRING: {
+        case base::Value::Type::STRING: {
           std::string tmp;
           fuzzer->FuzzString(&tmp);
           p->SetWithoutPathExpansion(property, new base::StringValue(tmp));
           break;
         }
-        case base::Value::TYPE_BINARY: {
+        case base::Value::Type::BINARY: {
           char tmp[200];
           size_t bin_length = RandInRange(sizeof(tmp));
           fuzzer->FuzzData(tmp, bin_length);
@@ -611,19 +611,19 @@
               base::BinaryValue::CreateWithCopiedBuffer(tmp, bin_length));
           break;
         }
-        case base::Value::TYPE_DICTIONARY: {
+        case base::Value::Type::DICTIONARY: {
           base::DictionaryValue* tmp = new base::DictionaryValue();
           FuzzParam(tmp, fuzzer);
           p->SetWithoutPathExpansion(property, tmp);
           break;
         }
-        case base::Value::TYPE_LIST: {
+        case base::Value::Type::LIST: {
           base::ListValue* tmp = new base::ListValue();
           FuzzParam(tmp, fuzzer);
           p->SetWithoutPathExpansion(property, tmp);
           break;
         }
-        case base::Value::TYPE_NULL:
+        case base::Value::Type::NONE:
         default:
           break;
       }
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index dcd28ce..5710467 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -307,7 +307,7 @@
               self._util_cc_helper.GetValueTypeString('value'))))
         .Append('return false;'))
     elif type_.property_type == PropertyType.OBJECT:
-      (c.Sblock('if (!value.IsType(base::Value::TYPE_DICTIONARY)) {')
+      (c.Sblock('if (!value.IsType(base::Value::Type::DICTIONARY)) {')
         .Concat(self._GenerateError(
           '"expected dictionary, got " + ' +
           self._util_cc_helper.GetValueTypeString('value')))
@@ -713,7 +713,7 @@
       value_var = param.unix_name + '_value'
       (c.Append('const base::Value* %(value_var)s = NULL;')
         .Append('if (args.Get(%(i)s, &%(value_var)s) &&')
-        .Sblock('    !%(value_var)s->IsType(base::Value::TYPE_NULL)) {')
+        .Sblock('    !%(value_var)s->IsType(base::Value::Type::NONE)) {')
         .Concat(self._GeneratePopulatePropertyFromValue(
             param, value_var, 'params', failure_value))
         .Eblock('}')
@@ -888,7 +888,7 @@
                                                     failure_value))
     elif underlying_type.property_type == PropertyType.BINARY:
       (c.Append('const base::BinaryValue* binary_value = NULL;')
-        .Sblock('if (!%(src_var)s->IsType(base::Value::TYPE_BINARY)) {')
+        .Sblock('if (!%(src_var)s->IsType(base::Value::Type::BINARY)) {')
         .Concat(self._GenerateError(
           '"\'%%(key)s\': expected binary, got " + ' +
           self._util_cc_helper.GetValueTypeString('%%(src_var)s', True)))
diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py
index c535021..881a6c73 100644
--- a/tools/json_schema_compiler/cpp_util.py
+++ b/tools/json_schema_compiler/cpp_util.py
@@ -60,16 +60,16 @@
   """Returns the Value::Type corresponding to the model.Type.
   """
   return {
-      PropertyType.ARRAY: 'base::Value::TYPE_LIST',
-      PropertyType.BINARY: 'base::Value::TYPE_BINARY',
-      PropertyType.BOOLEAN: 'base::Value::TYPE_BOOLEAN',
+      PropertyType.ARRAY: 'base::Value::Type::LIST',
+      PropertyType.BINARY: 'base::Value::Type::BINARY',
+      PropertyType.BOOLEAN: 'base::Value::Type::BOOLEAN',
       # PropertyType.CHOICES can be any combination of types.
-      PropertyType.DOUBLE: 'base::Value::TYPE_DOUBLE',
-      PropertyType.ENUM: 'base::Value::TYPE_STRING',
-      PropertyType.FUNCTION: 'base::Value::TYPE_DICTIONARY',
-      PropertyType.INTEGER: 'base::Value::TYPE_INTEGER',
-      PropertyType.OBJECT: 'base::Value::TYPE_DICTIONARY',
-      PropertyType.STRING: 'base::Value::TYPE_STRING',
+      PropertyType.DOUBLE: 'base::Value::Type::DOUBLE',
+      PropertyType.ENUM: 'base::Value::Type::STRING',
+      PropertyType.FUNCTION: 'base::Value::Type::DICTIONARY',
+      PropertyType.INTEGER: 'base::Value::Type::INTEGER',
+      PropertyType.OBJECT: 'base::Value::Type::DICTIONARY',
+      PropertyType.STRING: 'base::Value::Type::STRING',
   }[type_.property_type]
 
 
diff --git a/tools/json_schema_compiler/feature_compiler.py b/tools/json_schema_compiler/feature_compiler.py
index eddbb96..1f272d6 100644
--- a/tools/json_schema_compiler/feature_compiler.py
+++ b/tools/json_schema_compiler/feature_compiler.py
@@ -55,8 +55,6 @@
 
 #include "%(header_file_path)s"
 
-#include "extensions/common/features/api_feature.h"
-#include "extensions/common/features/behavior_feature.h"
 #include "extensions/common/features/complex_feature.h"
 #include "extensions/common/features/manifest_feature.h"
 #include "extensions/common/features/permission_feature.h"
@@ -215,8 +213,8 @@
     },
   })
 
-FEATURE_CLASSES = ['APIFeature', 'BehaviorFeature',
-                   'ManifestFeature', 'PermissionFeature']
+FEATURE_TYPES = ['APIFeature', 'BehaviorFeature',
+                 'ManifestFeature', 'PermissionFeature']
 
 def HasProperty(property_name, value):
   return property_name in value
@@ -259,6 +257,13 @@
     return True
   return reverse_reference_value == ('"%s"' % feature.name)
 
+SIMPLE_FEATURE_CPP_CLASSES = ({
+  'APIFeature': 'SimpleFeature',
+  'ManifestFeature': 'ManifestFeature',
+  'PermissionFeature': 'PermissionFeature',
+  'BehaviorFeature': 'SimpleFeature',
+})
+
 VALIDATION = ({
   'all': [
     (partial(HasAtLeastOneProperty, ['channel', 'dependencies']),
@@ -500,17 +505,18 @@
     for key, key_grammar in FEATURE_GRAMMAR.iteritems():
       self._ParseKey(key, parsed_json, shared_values, key_grammar)
 
-  def Validate(self, feature_class, shared_values):
+  def Validate(self, feature_type, shared_values):
     feature_values = self.feature_values.copy()
     feature_values.update(shared_values)
-    for validator, error in (VALIDATION[feature_class] + VALIDATION['all']):
+    for validator, error in (VALIDATION[feature_type] + VALIDATION['all']):
       if not validator(feature_values):
         self.AddError(error)
 
-  def GetCode(self, feature_class):
+  def GetCode(self, feature_type):
     """Returns the Code object for generating this feature."""
     c = Code()
-    c.Append('%s* feature = new %s();' % (feature_class, feature_class))
+    cpp_feature_class = SIMPLE_FEATURE_CPP_CLASSES[feature_type]
+    c.Append('%s* feature = new %s();' % (cpp_feature_class, cpp_feature_class))
     c.Append('feature->set_name("%s");' % self.name)
     c.Concat(GetCodeForFeatureValues(self.GetAllFeatureValues()))
     return c
@@ -544,7 +550,7 @@
     Feature.__init__(self, name)
     self.feature_list = []
 
-  def GetCode(self, feature_class):
+  def GetCode(self, feature_type):
     c = Code()
     c.Append('std::vector<Feature*> features;')
     for f in self.feature_list:
@@ -552,7 +558,7 @@
       # set.
       assert not f.shared_values
       c.Sblock('{')
-      c.Concat(f.GetCode(feature_class))
+      c.Concat(f.GetCode(feature_type))
       c.Append('features.push_back(feature);')
       c.Eblock('}')
     c.Append('ComplexFeature* feature(new ComplexFeature(&features));')
@@ -578,12 +584,12 @@
 class FeatureCompiler(object):
   """A compiler to load, parse, and generate C++ code for a number of
   features.json files."""
-  def __init__(self, chrome_root, source_files, feature_class,
+  def __init__(self, chrome_root, source_files, feature_type,
                provider_class, out_root, out_base_filename):
     # See __main__'s ArgumentParser for documentation on these properties.
     self._chrome_root = chrome_root
     self._source_files = source_files
-    self._feature_class = feature_class
+    self._feature_type = feature_type
     self._provider_class = provider_class
     self._out_root = out_root
     self._out_base_filename = out_base_filename
@@ -657,7 +663,7 @@
         if parent:
           feature.SetParent(parent)
         feature.Parse(value, shared_values)
-        feature.Validate(self._feature_class, shared_values)
+        feature.Validate(self._feature_type, shared_values)
         return feature
       except:
         print('Failure to parse feature "%s"' % feature_name)
@@ -690,7 +696,7 @@
     self._features[feature_name].SetSharedValues(final_shared_values)
 
   def _FinalValidation(self):
-    validators = FINAL_VALIDATION['all'] + FINAL_VALIDATION[self._feature_class]
+    validators = FINAL_VALIDATION['all'] + FINAL_VALIDATION[self._feature_type]
     for name, feature in self._features.items():
       for validator, error in validators:
         if not validator(feature, self._features):
@@ -712,7 +718,7 @@
     for k in sorted(self._features.keys()):
       c.Sblock('{')
       feature = self._features[k]
-      c.Concat(feature.GetCode(self._feature_class))
+      c.Concat(feature.GetCode(self._feature_type))
       c.Append('AddFeature("%s", feature);' % k)
       c.Eblock('}')
     c.Eblock('}')
@@ -755,7 +761,7 @@
   parser.add_argument('chrome_root', type=str,
                       help='The root directory of the chrome checkout')
   parser.add_argument(
-      'feature_class', type=str,
+      'feature_type', type=str,
       help='The name of the class to use in feature generation ' +
                '(e.g. APIFeature, PermissionFeature)')
   parser.add_argument('provider_class', type=str,
@@ -768,9 +774,9 @@
   parser.add_argument('source_files', type=str, nargs='+',
                       help='The source features.json files')
   args = parser.parse_args()
-  if args.feature_class not in FEATURE_CLASSES:
-    raise NameError('Unknown feature class: %s' % args.feature_class)
-  c = FeatureCompiler(args.chrome_root, args.source_files, args.feature_class,
+  if args.feature_type not in FEATURE_TYPES:
+    raise NameError('Unknown feature type: %s' % args.feature_type)
+  c = FeatureCompiler(args.chrome_root, args.source_files, args.feature_type,
                       args.provider_class, args.out_root,
                       args.out_base_filename)
   c.Load()
diff --git a/tools/json_schema_compiler/json_features.gni b/tools/json_schema_compiler/json_features.gni
index ab940f7..7746b3b 100644
--- a/tools/json_schema_compiler/json_features.gni
+++ b/tools/json_schema_compiler/json_features.gni
@@ -5,18 +5,18 @@
 # Generates the FeatureProviders files for extension features files.
 # The following variables are required:
 #   sources: The features.json files to use.
-#   feature_class: The name of the feature class to generate, e.g. APIFeature.
+#   feature_type: The type of the features to generate, e.g. APIFeature.
 #   provider_class: The name of the provider class to generate, e.g.
 #                   APIFeatureProvider.
 #   deps/public_deps/visibility: normal meaning
 template("json_features") {
   assert(defined(invoker.sources),
          "\"sources\" must be defined for the $target_name template.")
-  assert(defined(invoker.feature_class),
-         "\"feature_class\" must be defined for the $target_name template.")
+  assert(defined(invoker.feature_type),
+         "\"feature_type\" must be defined for the $target_name template.")
   assert(defined(invoker.provider_class),
          "\"provider_class\" must be defined for the $target_name template.")
-  feature_class = invoker.feature_class
+  feature_type = invoker.feature_type
   provider_class = invoker.provider_class
 
   compiler_root = "//tools/json_schema_compiler"
@@ -40,7 +40,7 @@
     rebased = rebase_path(sources, root_build_dir)
     args = [
              ".",
-             "$feature_class",
+             "$feature_type",
              "$provider_class",
              rebase_path(target_gen_dir, root_build_dir),
              "$base_filename",
diff --git a/tools/json_schema_compiler/test/BUILD.gn b/tools/json_schema_compiler/test/BUILD.gn
index db8bceb..dd08558 100644
--- a/tools/json_schema_compiler/test/BUILD.gn
+++ b/tools/json_schema_compiler/test/BUILD.gn
@@ -42,7 +42,7 @@
 }
 
 json_features("features_compiler_test") {
-  feature_class = "APIFeature"
+  feature_type = "APIFeature"
   provider_class = "CompilerTestFeatureProvider"
   sources = [
     "features_test.json",
diff --git a/tools/json_schema_compiler/test/features_generation_unittest.cc b/tools/json_schema_compiler/test/features_generation_unittest.cc
index 79f76387..18f2f14 100644
--- a/tools/json_schema_compiler/test/features_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/features_generation_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/common/features/api_feature.h"
 #include "extensions/common/features/complex_feature.h"
 #include "extensions/common/features/feature.h"
 #include "extensions/common/features/simple_feature.h"
@@ -33,7 +32,7 @@
 // A utility object for comparing a feature with its expected value.
 struct FeatureComparator {
  public:
-  FeatureComparator(const std::string& name);
+  explicit FeatureComparator(const std::string& name);
   ~FeatureComparator();
 
   void CompareFeature(SimpleFeature* feature);
@@ -95,16 +94,23 @@
 TEST(FeaturesGenerationTest, FeaturesTest) {
   CompilerTestFeatureProvider provider;
 
-  auto GetAPIFeature = [&provider](const std::string& name) {
+  auto GetAsSimpleFeature = [&provider](const std::string& name) {
     Feature* feature = provider.GetFeature(name);
     // Shame we can't test this more safely, but if our feature is declared as
     // the wrong class, things should blow up in a spectacular fashion.
-    return static_cast<APIFeature*>(feature);
+    return static_cast<SimpleFeature*>(feature);
+  };
+
+  auto GetAsComplexFeature = [&provider](const std::string& name) {
+    Feature* feature = provider.GetFeature(name);
+    // Shame we can't test this more safely, but if our feature is declared as
+    // the wrong class, things should blow up in a spectacular fashion.
+    return static_cast<ComplexFeature*>(feature);
   };
 
   // Check some simple features for accuracy.
   {
-    APIFeature* feature = GetAPIFeature("alpha");
+    SimpleFeature* feature = GetAsSimpleFeature("alpha");
     FeatureComparator comparator("alpha");
     comparator.dependencies = {"permission:alpha"};
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
@@ -114,7 +120,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    APIFeature* feature = GetAPIFeature("beta");
+    SimpleFeature* feature = GetAsSimpleFeature("beta");
     FeatureComparator comparator("beta");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -128,7 +134,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    APIFeature* feature = GetAPIFeature("gamma");
+    SimpleFeature* feature = GetAsSimpleFeature("gamma");
     FeatureComparator comparator("gamma");
     comparator.channel.reset(
         new version_info::Channel(version_info::Channel::BETA));
@@ -141,7 +147,7 @@
 
     // A child feature should inherit all fields from its parent, except in the
     // case that it specifies its own value. Thus, we reuse |comparator|.
-    feature = GetAPIFeature("gamma.child");
+    feature = GetAsSimpleFeature("gamma.child");
     comparator.name = "gamma.child";
     comparator.whitelist = {"ccc"};
     comparator.platforms = {Feature::LINUX_PLATFORM};
@@ -151,7 +157,7 @@
   {
     // Features that specify 'noparent' should not inherit features from any
     // other feature.
-    APIFeature* feature = GetAPIFeature("gamma.unparented");
+    SimpleFeature* feature = GetAsSimpleFeature("gamma.unparented");
     FeatureComparator comparator("gamma.unparented");
     comparator.blacklist = {"ddd"};
     comparator.contexts = {Feature::UNBLESSED_EXTENSION_CONTEXT};
@@ -160,8 +166,8 @@
     comparator.CompareFeature(feature);
   }
   {
-    ComplexFeature* complex_feature = static_cast<ComplexFeature*>(
-        provider.GetFeature("gamma.complex_unparented"));
+    ComplexFeature* complex_feature =
+        GetAsComplexFeature("gamma.complex_unparented");
     FeatureComparator comparator("gamma.complex_unparented");
     comparator.contexts = {Feature::UNBLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -172,7 +178,7 @@
       comparator.CompareFeature(static_cast<SimpleFeature*>(feature.get()));
   }
   {
-    APIFeature* feature = GetAPIFeature("delta");
+    SimpleFeature* feature = GetAsSimpleFeature("delta");
     FeatureComparator comparator("delta");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT,
                            Feature::WEBUI_CONTEXT};
@@ -184,7 +190,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    APIFeature* feature = GetAPIFeature("allEnum");
+    SimpleFeature* feature = GetAsSimpleFeature("allEnum");
     FeatureComparator comparator("allEnum");
     comparator.contexts = {
         Feature::BLESSED_EXTENSION_CONTEXT,  Feature::BLESSED_WEB_PAGE_CONTEXT,
@@ -201,7 +207,7 @@
   }
   {
     // Omega is imported from a second .json file.
-    APIFeature* feature = GetAPIFeature("omega");
+    SimpleFeature* feature = GetAsSimpleFeature("omega");
     FeatureComparator comparator("omega");
     comparator.contexts = {Feature::WEB_PAGE_CONTEXT};
     comparator.channel.reset(
@@ -211,14 +217,13 @@
   }
   {
     // Features specifying 'nocompile' should not be generated at all.
-    APIFeature* feature = GetAPIFeature("uncompiled");
+    SimpleFeature* feature = GetAsSimpleFeature("uncompiled");
     EXPECT_FALSE(feature);
   }
 
   // Test complex features.
   {
-    ComplexFeature* feature =
-        static_cast<ComplexFeature*>(provider.GetFeature("complex"));
+    ComplexFeature* feature = GetAsComplexFeature("complex");
     ASSERT_TRUE(feature);
     EXPECT_EQ(2u, feature->features_.size());
     // Find the default parent. This is a little tedious because it might not
@@ -249,7 +254,7 @@
       comparator.CompareFeature(default_parent);
       // Check the child of the complex feature. It should inherit its
       // properties from the default parent.
-      APIFeature* child_feature = GetAPIFeature("complex.child");
+      SimpleFeature* child_feature = GetAsSimpleFeature("complex.child");
       comparator.name = "complex.child";
       comparator.platforms = {Feature::WIN_PLATFORM};
       comparator.dependencies = {"permission:complex.child"};
@@ -269,7 +274,7 @@
 
   // Test API aliases.
   {
-    APIFeature* feature = GetAPIFeature("alias");
+    SimpleFeature* feature = GetAsSimpleFeature("alias");
     FeatureComparator comparator("alias");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -278,7 +283,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    APIFeature* feature = GetAPIFeature("alias_source");
+    SimpleFeature* feature = GetAsSimpleFeature("alias_source");
     FeatureComparator comparator("alias_source");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -297,27 +302,27 @@
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = GetAPIFeature("parent_source");
+    Feature* feature = provider.GetFeature("parent_source");
     ASSERT_EQ("parent_source_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = GetAPIFeature("parent_source.child");
+    Feature* feature = provider.GetFeature("parent_source.child");
     ASSERT_EQ("parent_source_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = GetAPIFeature("parent_source.child_source");
+    Feature* feature = provider.GetFeature("parent_source.child_source");
     ASSERT_EQ("parent_source_child_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = GetAPIFeature("alias_parent");
+    Feature* feature = provider.GetFeature("alias_parent");
     ASSERT_EQ("", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = GetAPIFeature("alias_parent.child");
+    Feature* feature = provider.GetFeature("alias_parent.child");
     ASSERT_EQ("", feature->alias());
     ASSERT_EQ("child_source", feature->source());
   }
diff --git a/tools/json_schema_compiler/test/idl_schemas_unittest.cc b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
index 07df4fb5..41d42afd 100644
--- a/tools/json_schema_compiler/test/idl_schemas_unittest.cc
+++ b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
@@ -69,7 +69,7 @@
   std::unique_ptr<base::ListValue> f5_results(Function5::Results::Create(13));
   base::Value* f5_result_int = NULL;
   ASSERT_TRUE(f5_results->Get(0, &f5_result_int));
-  EXPECT_TRUE(f5_result_int->IsType(base::Value::TYPE_INTEGER));
+  EXPECT_TRUE(f5_result_int->IsType(base::Value::Type::INTEGER));
 
   std::unique_ptr<base::ListValue> f6_results(Function6::Results::Create(a));
   base::Value* f6_result_dict = NULL;
diff --git a/tools/json_schema_compiler/util.cc b/tools/json_schema_compiler/util.cc
index a15c9f5..9f4286a7 100644
--- a/tools/json_schema_compiler/util.cc
+++ b/tools/json_schema_compiler/util.cc
@@ -32,7 +32,7 @@
 
 bool PopulateItem(const base::Value& from, int* out, base::string16* error) {
   if (!from.GetAsInteger(out))
-    return ReportError(from, base::Value::TYPE_INTEGER, error);
+    return ReportError(from, base::Value::Type::INTEGER, error);
   return true;
 }
 
@@ -42,7 +42,7 @@
 
 bool PopulateItem(const base::Value& from, bool* out, base::string16* error) {
   if (!from.GetAsBoolean(out))
-    return ReportError(from, base::Value::TYPE_BOOLEAN, error);
+    return ReportError(from, base::Value::Type::BOOLEAN, error);
   return true;
 }
 
@@ -52,7 +52,7 @@
 
 bool PopulateItem(const base::Value& from, double* out, base::string16* error) {
   if (!from.GetAsDouble(out))
-    return ReportError(from, base::Value::TYPE_DOUBLE, error);
+    return ReportError(from, base::Value::Type::DOUBLE, error);
   return true;
 }
 
@@ -64,7 +64,7 @@
                   std::string* out,
                   base::string16* error) {
   if (!from.GetAsString(out))
-    return ReportError(from, base::Value::TYPE_STRING, error);
+    return ReportError(from, base::Value::Type::STRING, error);
   return true;
 }
 
@@ -81,7 +81,7 @@
                   base::string16* error) {
   const base::BinaryValue* binary = nullptr;
   if (!from.GetAsBinary(&binary))
-    return ReportError(from, base::Value::TYPE_BINARY, error);
+    return ReportError(from, base::Value::Type::BINARY, error);
   out->assign(binary->GetBuffer(), binary->GetBuffer() + binary->GetSize());
   return true;
 }
@@ -112,7 +112,7 @@
                   base::string16* error) {
   const base::DictionaryValue* dict = nullptr;
   if (!from.GetAsDictionary(&dict))
-    return ReportError(from, base::Value::TYPE_DICTIONARY, error);
+    return ReportError(from, base::Value::Type::DICTIONARY, error);
   *out = dict->CreateDeepCopy();
   return true;
 }
diff --git a/tools/luci-go/linux64/isolate.sha1 b/tools/luci-go/linux64/isolate.sha1
index e9ee5e1d..61d45e2 100644
--- a/tools/luci-go/linux64/isolate.sha1
+++ b/tools/luci-go/linux64/isolate.sha1
@@ -1 +1 @@
-bc6671bfa3721d4be00f74436c916c6487c2f2c8
+b7468b6e00ca43ca009b98e79bf4583311b483e6
diff --git a/tools/luci-go/mac64/isolate.sha1 b/tools/luci-go/mac64/isolate.sha1
index c40ba122..fa24d6a 100644
--- a/tools/luci-go/mac64/isolate.sha1
+++ b/tools/luci-go/mac64/isolate.sha1
@@ -1 +1 @@
-c8e7c0ca4b64377679f98480bb04820c4ac360b8
+9a893e89be9a4fc932ba04365ba0df5b81bda765
diff --git a/tools/luci-go/win64/isolate.exe.sha1 b/tools/luci-go/win64/isolate.exe.sha1
index 055bad03..69e8c4bf 100644
--- a/tools/luci-go/win64/isolate.exe.sha1
+++ b/tools/luci-go/win64/isolate.exe.sha1
@@ -1 +1 @@
-da849f39377f2bf89600f1f6f33b4dce04a19d45
+53de6456d5b1c15ed9151454008e6af26922b255
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 07ad4a2..59b820ad 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1105,13 +1105,11 @@
                  + logdog_command + test_cmdline)
     elif use_x11 and test_type == 'windowed_test_launcher':
       extra_files = [
-          'xdisplaycheck',
           '../../testing/test_env.py',
           '../../testing/xvfb.py',
       ]
       cmdline = [
         '../../testing/xvfb.py',
-        '.',
         './' + str(executable) + executable_suffix,
         '--brave-new-test-launcher',
         '--test-launcher-bot-mode',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 2ea561a..3cf2be53 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -159,6 +159,7 @@
       'CrWinClngLLDdbg': 'clang_tot_minimal_symbols_shared_debug_use_lld_x86',
       'EarlGreyiOS': 'ios',
       'GomaCanaryiOS': 'ios',
+      'ios-simulator': 'ios',
       'Headless Linux (dbg)': '//build/args/bots/chromium.fyi/headless_linux_dbg.gn',
       'MD Top Chrome ChromeOS material-hybrid': 'chromeos_with_codecs_debug_bot',
       'MD Top Chrome ChromeOS non-material': 'chromeos_with_codecs_debug_bot',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 209ca4f..11709401 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -13684,6 +13684,16 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="ScreenOrientationChange">
+  <owner>avayvod@chromium.org</owner>
+  <owner>mlamouri@chromium.org</owner>
+  <owner>zqzhang@chromium.org</owner>
+  <description>
+    The use rotates the device triggering a screen orientation change. This
+    action is Android-specific.
+  </description>
+</action>
+
 <action name="Screenshot_Annotate">
   <owner>derat@chromium.org</owner>
   <owner>jdufault@chromium.org</owner>
@@ -13843,6 +13853,14 @@
   <description>The Windows Settings app became focused.</description>
 </action>
 
+<action name="SettingsAppMonitor.CheckItOut">
+  <owner>grt@chromium.org</owner>
+  <owner>pmonette@chromium.org</owner>
+  <description>
+    The user clicked the &quot;Check it out&quot; button in the Edge promo.
+  </description>
+</action>
+
 <action name="SettingsAppMonitor.ChooserInvoked">
   <owner>grt@chromium.org</owner>
   <description>
@@ -13874,6 +13892,20 @@
   </description>
 </action>
 
+<action name="SettingsAppMonitor.PromoFocused">
+  <owner>grt@chromium.org</owner>
+  <owner>pmonette@chromium.org</owner>
+  <description>The Edge promo became focused.</description>
+</action>
+
+<action name="SettingsAppMonitor.SwitchAnyway">
+  <owner>grt@chromium.org</owner>
+  <owner>pmonette@chromium.org</owner>
+  <description>
+    The user clicked the &quot;Switch Anyway&quot; button in the Edge promo.
+  </description>
+</action>
+
 <action name="SettingsResetBubble.LearnMore">
   <owner>mad@chromium.org</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 487b1ab..35be8bcd 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -465,6 +465,46 @@
   <summary>Recorded when a download is opened.</summary>
 </histogram>
 
+<histogram name="Android.DownloadManager.OtherExtensions.InitialCount"
+    enum="AndroidDownloadExtensionType">
+  <owner>dfalcantara@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The extension type for non-incognito download items that match the
+    &quot;other&quot; filter type. Recorded when the download UI is initialized.
+  </summary>
+</histogram>
+
+<histogram name="Android.DownloadManager.OtherExtensions.OpenFailed"
+    enum="AndroidDownloadExtensionType">
+  <owner>dfalcantara@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The extension type for downloads that match the &quot;other&quot; filter
+    type. Recorded when a download fails to open.
+  </summary>
+</histogram>
+
+<histogram name="Android.DownloadManager.OtherExtensions.OpenSucceeded"
+    enum="AndroidDownloadExtensionType">
+  <owner>dfalcantara@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The extension type for downloads that match the &quot;other&quot; filter
+    type. Recorded when a download is opened.
+  </summary>
+</histogram>
+
+<histogram name="Android.DownloadManager.OtherExtensions.Share"
+    enum="AndroidDownloadExtensionType">
+  <owner>dfalcantara@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The extension type for downloads that match the &quot;other&quot; filter
+    type. Recorded when downloads are shared through the download manager.
+  </summary>
+</histogram>
+
 <histogram name="Android.DownloadManager.Share.Count">
   <owner>dfalcantara@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
@@ -491,6 +531,46 @@
   <summary>The percentage of total storage downloads consume.</summary>
 </histogram>
 
+<histogram name="Android.InstantApps.ApiCallDuration2" units="ms">
+  <owner>mariakhomenko@chromium.org</owner>
+  <summary>
+    Measures the amount of time spent in the getInstantAppIntent() API call.
+  </summary>
+</histogram>
+
+<histogram name="Android.InstantApps.ApiCallDurationWithApp" units="ms">
+  <owner>mariakhomenko@chromium.org</owner>
+  <summary>
+    Measures the amount of time spent in the getInstantAppIntent() API call when
+    the API was able to find an Instant App for the URL.
+  </summary>
+</histogram>
+
+<histogram name="Android.InstantApps.ApiCallDurationWithoutApp" units="ms">
+  <owner>mariakhomenko@chromium.org</owner>
+  <summary>
+    Measures the amount of time spent in the getInstantAppIntent() API call when
+    the API was not able to find an Instant App for the URL.
+  </summary>
+</histogram>
+
+<histogram name="Android.InstantApps.FallbackDuration" units="ms">
+  <owner>mariakhomenko@chromium.org</owner>
+  <summary>
+    Measures the time from when we first received an eligible intent for Instant
+    Apps to the time we processed it in the case where the  Instant Apps
+    activity had to invoke a fallback intent.
+  </summary>
+</histogram>
+
+<histogram name="Android.InstantApps.HandleIntentDuration" units="ms">
+  <owner>mariakhomenko@chromium.org</owner>
+  <summary>
+    Measures the amount of time spent triaging an incoming event to decide
+    whether it needs to be routed to Instant Apps.
+  </summary>
+</histogram>
+
 <histogram name="Android.IsLastSharedAppInfoRetrieved"
     enum="BooleanIsLastSharedAppInfoRetrieved">
   <owner>jaekyun@chromium.org</owner>
@@ -8463,6 +8543,17 @@
   </summary>
 </histogram>
 
+<histogram name="DataReductionProxy.CaptivePortalDetected.Platform"
+    enum="BooleanPresent">
+  <owner>tbansal@chromium.org</owner>
+  <owner>bengr@chromium.org</owner>
+  <summary>
+    Records if the platform detected that a captive portal is present on the
+    current network. Recorded at the time of Chrome startup and on IP change
+    event. Recorded only for users that have data saver enabled.
+  </summary>
+</histogram>
+
 <histogram name="DataReductionProxy.ConfigFetchLostBytesCL">
   <obsolete>
     Removed in Feb. 2016
@@ -17256,6 +17347,15 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.Messaging.SetPortIdTime" units="ms">
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <summary>
+    The amount of time for the renderer to inform the browser process of a new
+    port being created with a certain id. This replaces the flow for
+    Extensions.Messaging.GetPortId[A]SyncTime.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.NetworkDelay" units="ms">
   <owner>battre@chromium.org</owner>
   <summary>Time that network requests were blocked due to extensions.</summary>
@@ -23862,6 +23962,15 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Audio.Render.OutputDeviceStatus"
+    enum="OutputDeviceStatus">
+  <owner>olka@chromium.org</owner>
+  <owner>dalecurtis@chromium.org</owner>
+  <summary>
+    Device status received in response to device authorization request.
+  </summary>
+</histogram>
+
 <histogram
     name="Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization"
     enum="GetOutputDeviceInfoCacheHit">
@@ -25689,6 +25798,16 @@
   </summary>
 </histogram>
 
+<histogram name="Media.WebAudioSourceProvider.SinkStatus"
+    enum="OutputDeviceStatus">
+  <owner>olka@chromium.org</owner>
+  <owner>dalecurtis@chromium.org</owner>
+  <summary>
+    Status of audio sink provided to WebMediaPlayer. If not OK, null sink will
+    be used for audio output instead.
+  </summary>
+</histogram>
+
 <histogram name="Media.WindowsCoreAudioInput" enum="BooleanSuccess">
   <owner>henrika@chromium.org</owner>
   <summary>
@@ -31167,6 +31286,11 @@
   </summary>
 </histogram>
 
+<histogram name="Net.Http2ResponseStatusHeader" enum="StatusHeader">
+  <owner>bnc@chromium.org</owner>
+  <summary>Format of :status header value in HTTP/2 response.</summary>
+</histogram>
+
 <histogram name="Net.Http2SSLCipherSuite" enum="SSLCipherSuite">
   <owner>davidben@chromium.org</owner>
   <summary>
@@ -34690,6 +34814,9 @@
 <histogram
     name="Net.SSL_Connection_Latency_PostQuantumSupported_Full_Handshake"
     units="ms">
+  <obsolete>
+    Deprecated as of 2016-12-01.
+  </obsolete>
   <owner>mab@chromium.org</owner>
   <summary>
     Time from when the Connect() starts until it completes (full handshakes
@@ -34708,6 +34835,9 @@
 
 <histogram name="Net.SSL_Connection_PostQuantum_Negotiated"
     enum="BooleanSupported">
+  <obsolete>
+    Deprecated as of 2016-12-01.
+  </obsolete>
   <owner>mab@chromium.org</owner>
   <summary>
     For only browsers in the post-quantum (CECPQ1) ciphersuite experiment,
@@ -38331,6 +38461,16 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.LoadType" enum="NTPLoadType">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Android: the type of load for the NTP, such as cold or warm start. It's a
+    warm start if the native library is already loaded and initialized at the
+    time the activity is created. This might happen if for example a service was
+    already running.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.LogoClick" enum="NewTabPageLogoClick">
   <owner>ianwen@chromium.org</owner>
   <summary>
@@ -38676,6 +38816,25 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.SearchAvailableLoadTime.ColdStart" units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Android: the time until the search box became available on the NTP in a cold
+    start. It's a cold start if the native library is not already loaded and
+    initialized at the time the activity is created.
+  </summary>
+</histogram>
+
+<histogram name="NewTabPage.SearchAvailableLoadTime.WarmStart" units="ms">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <summary>
+    Android: the time until the search box became available on the NTP in a warm
+    start. It's a warm start if the native library is already loaded and
+    initialized at the time the activity is created. This might happen if for
+    example a service was already running.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.SearchURLs.Total">
   <obsolete>
     Deprecated 2016-02 (and not recorded for some time before that).
@@ -41956,6 +42115,22 @@
   </summary>
 </histogram>
 
+<histogram name="PageLoad.CSSTiming.ParseAndUpdate.BeforeFirstContentfulPaint"
+    units="ms">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    The sum of CSSTiming.Update and CSSTiming.Parse variants for this page load.
+  </summary>
+</histogram>
+
+<histogram name="PageLoad.CSSTiming.Update.BeforeFirstContentfulPaint"
+    units="ms">
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    The time spent in Document::updateStyle before the first contentful paint.
+  </summary>
+</histogram>
+
 <histogram
     name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"
     units="ms">
@@ -42144,6 +42319,10 @@
 <histogram
     name="PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintSignalStatus"
     enum="FirstMeaningfulPaintSignalStatus">
+  <obsolete>
+    Deprecated in favor of
+    PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintSignalStatus2.
+  </obsolete>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
     Whether the user had any interaction on the page (except mouse move) after
@@ -42152,6 +42331,17 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintSignalStatus2"
+    enum="FirstMeaningfulPaintSignalStatus">
+  <owner>ksakamoto@chromium.org</owner>
+  <summary>
+    Whether the user had any interaction on the page (except mouse move) after
+    first paint, and whether the user left the page before network stable or
+    not. Not logged if page load was aborted before first paint.
+  </summary>
+</histogram>
+
 <histogram name="PageLoad.Experimental.PaintTiming.FirstMeaningfulPaintStatus"
     enum="FirstMeaningfulPaintStatus">
   <owner>ksakamoto@chromium.org</owner>
@@ -48422,6 +48612,9 @@
 </histogram>
 
 <histogram name="PreloadScanner.DocumentWrite.ScriptLength" units="characters">
+  <obsolete>
+    No longer needed
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The length of the inline script that is being considered for document write
@@ -51035,7 +51228,7 @@
   <summary>Final status of the download of a reading list entry.</summary>
 </histogram>
 
-<histogram name="ReadingList.FirstReadAgeOnDeletion" units="ms">
+<histogram name="ReadingList.FirstReadAgeOnDeletion" units="minutes">
   <owner>gambard@chromium.org</owner>
   <summary>
     Time since the first read of the reading list entry getting deleted. 0 if it
@@ -51048,7 +51241,7 @@
   <summary>Whether the displayed version is the offline one.</summary>
 </histogram>
 
-<histogram name="ReadingList.Read.AgeOnDeletion" units="ms">
+<histogram name="ReadingList.Read.AgeOnDeletion" units="hours">
   <owner>gambard@chromium.org</owner>
   <summary>
     Time since the creation of the read reading list entry getting deleted.
@@ -51060,7 +51253,7 @@
   <summary>Number of read entries in reading list.</summary>
 </histogram>
 
-<histogram name="ReadingList.Unread.AgeOnDeletion" units="ms">
+<histogram name="ReadingList.Unread.AgeOnDeletion" units="hours">
   <owner>gmabard@chromium.org</owner>
   <summary>
     Time since the creation of the unread reading list entry getting deleted.
@@ -53290,6 +53483,16 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.Pref.Scout.Transition"
+    enum="ScoutTransitionReason">
+  <owner>lpz@chromium.org</owner>
+  <summary>
+    Tracks reasons for the Extended Reporting preference transition, such as a
+    user entering an experiment group or seeing a security interstitial for the
+    first time. Recorded for all non-Incognito profiles on profile startup.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.UnverifiedDownloads.Allowed"
     enum="SBClientDownloadExtensions">
   <owner>asanka@chromium.org</owner>
@@ -67426,6 +67629,15 @@
   </summary>
 </histogram>
 
+<histogram name="TrackedObjects.GetRetiredOrCreateThreadData" units="ms">
+  <owner>fdoray@chromium.org</owner>
+  <summary>
+    Time spent in base::ThreadData::GetRetiredOrCreateThreadData(). This method
+    is called at most once per thread, when its name is set, when it posts a
+    task or when it runs a task (whichever comes first).
+  </summary>
+</histogram>
+
 <histogram name="TrafficStatsAmortizer.AmortizationDelay" units="ms">
   <owner>sclittle@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
@@ -75050,6 +75262,24 @@
   <int value="1" label="NEON support"/>
 </enum>
 
+<enum name="AndroidDownloadExtensionType" type="int">
+  <int value="0" label="Other"/>
+  <int value="1" label="APK"/>
+  <int value="2" label="CSV"/>
+  <int value="3" label="DOC"/>
+  <int value="4" label="DOCX"/>
+  <int value="5" label="EXE"/>
+  <int value="6" label="PDF"/>
+  <int value="7" label="PPT"/>
+  <int value="8" label="PPTX"/>
+  <int value="9" label="PSD"/>
+  <int value="10" label="RTF"/>
+  <int value="11" label="TXT"/>
+  <int value="12" label="XLS"/>
+  <int value="13" label="XLSX"/>
+  <int value="14" label="ZIP"/>
+</enum>
+
 <enum name="AndroidDownloadFilterType" type="int">
   <int value="0" label="All"/>
   <int value="1" label="Page"/>
@@ -76563,6 +76793,7 @@
   <int value="156" label="MDDH_INVALID_UNSUBSCRIPTION_REQUEST"/>
   <int value="157" label="AOAH_NONSENSE_DEVICE_ID"/>
   <int value="158" label="BDH_INVALID_OPTIONS"/>
+  <int value="159" label="RFH_DID_ADD_CONSOLE_MESSAGE_BAD_SEVERITY"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions" type="int">
@@ -87172,6 +87403,7 @@
   <int value="1693" label="CSSGridLayout"/>
   <int value="1694" label="V8BarcodeDetector_Detect_Method"/>
   <int value="1695" label="V8FaceDetector_Detect_Method"/>
+  <int value="1696" label="FullscreenAllowedByOrientationChange"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -92184,6 +92416,7 @@
   <int value="-2099035488" label="enable-data-reduction-proxy-bypass-warning"/>
   <int value="-2098610409" label="disable-lcd-text"/>
   <int value="-2097515669" label="disable-cast"/>
+  <int value="-2084492219" label="ComponentFlashOnly:disabled"/>
   <int value="-2083195884" label="enable-firewall-hole-punching"/>
   <int value="-2077268643" label="disable-device-enumeration"/>
   <int value="-2075870708" label="MediaRemotingEncrypted:disabled"/>
@@ -92335,6 +92568,7 @@
   <int value="-1473668019" label="token-binding:disabled"/>
   <int value="-1473136627" label="enable-web-payments"/>
   <int value="-1467332609" label="tab-management-experiment-type-anise"/>
+  <int value="-1466990325" label="CrosCompUpdates:enabled"/>
   <int value="-1460462432" label="disable-media-source"/>
   <int value="-1456004000" label="VrShell:disabled"/>
   <int value="-1443796945" label="OfflinePagesSharing:disabled"/>
@@ -92378,6 +92612,7 @@
   <int value="-1269084216" label="ash-md"/>
   <int value="-1268836676" label="disable-out-of-process-pdf"/>
   <int value="-1267958145" label="disable-pdf-material-ui"/>
+  <int value="-1254070521" label="enable-slimming-paint-invalidation"/>
   <int value="-1251411236" label="disable-new-md-input-view"/>
   <int value="-1246840031" label="OptInImeMenu:disabled"/>
   <int value="-1241747717" label="enable-android-password-link"/>
@@ -92503,6 +92738,7 @@
   <int value="-684900739" label="disable-merge-key-char-events"/>
   <int value="-667517406" label="overscroll-history-navigation"/>
   <int value="-661978438" label="enable-data-reduction-proxy-lo-fi"/>
+  <int value="-660182316" label="ComponentFlashOnly:enabled"/>
   <int value="-660160292" label="enable-apps-show-on-first-paint"/>
   <int value="-650504533" label="enable-speculative-launch-service-worker"/>
   <int value="-650176557" label="OfflinePagesSvelteConcurrentLoading:enabled"/>
@@ -92511,6 +92747,7 @@
   <int value="-641719457" label="disable-compositor-touch-hit-testing"/>
   <int value="-631740127" label="inert-visual-viewport"/>
   <int value="-622685174" label="enable-pdf-material-ui"/>
+  <int value="-620030047" label="CrosCompUpdates:disabled"/>
   <int value="-617452890" label="media-router"/>
   <int value="-610411643" label="enable-printer-app-search"/>
   <int value="-606898702" label="MaterialDesignSettings:disabled"/>
@@ -95889,6 +96126,12 @@
   <int value="4" label="Fits with field trial"/>
 </enum>
 
+<enum name="NTPLoadType" type="int">
+  <int value="0" label="Cold startup"/>
+  <int value="1" label="Warm startup"/>
+  <int value="2" label="Other, not at startup"/>
+</enum>
+
 <enum name="NtpMostVisitedScheme" type="int">
   <obsolete>
     Deprecated 2016-05.
@@ -96630,6 +96873,14 @@
   <int value="10" label="Hide for now"/>
 </enum>
 
+<enum name="OutputDeviceStatus" type="int">
+  <int value="0" label="Ok"/>
+  <int value="1" label="Not found"/>
+  <int value="2" label="Not authorized"/>
+  <int value="3" label="Authorization timed out"/>
+  <int value="4" label="Internal error"/>
+</enum>
+
 <enum name="OverscrollMode" type="int">
   <summary>Direction of the overscroll gesture.</summary>
   <int value="1" label="North">Scrolled from bottom towards top</int>
@@ -99424,6 +99675,7 @@
   <int value="3" label="Incoming message app id was unknown"/>
   <int value="4" label="Incoming message origin no longer has permission"/>
   <int value="5" label="Incoming message Service Worker not found"/>
+  <int value="6" label="GCM Store reset due to corruption"/>
 </enum>
 
 <enum name="PushUnregistrationStatus" type="int">
@@ -100969,6 +101221,24 @@
   <int value="11" label="(gesture) ScheduledPageBlock"/>
 </enum>
 
+<enum name="ScoutTransitionReason" type="int">
+  <int value="0" label="Flag forced Scout Group to true"/>
+  <int value="1" label="Flag forced Scout Group to false"/>
+  <int value="2" label="User in OnlyShowScout group, enters Scout Group"/>
+  <int value="3"
+      label="User in CanShowScout group, enters Scout Group immediately"/>
+  <int value="4"
+      label="User in CanShowScout group, waiting for interstitial to enter
+             Scout Group"/>
+  <int value="5"
+      label="User in CanShowScout group saw first interstitial and entered
+             Scout Group"/>
+  <int value="6" label="User in Control group"/>
+  <int value="7" label="Rollback: SBER2 on on implies SBER1 can turn on"/>
+  <int value="8" label="Rollback: SBER2 off so SBER1 must be turned off"/>
+  <int value="9" label="Rollback: SBER2 absent so SBER1 must be cleared"/>
+</enum>
+
 <enum name="ScrollThread" type="int">
   <int value="0" label="Scroll on impl-thread"/>
   <int value="1" label="Scroll on main-thread"/>
@@ -103299,6 +103569,13 @@
   <int value="3" label="No pref change: was equal to platform-specific store"/>
 </enum>
 
+<enum name="StatusHeader" type="int">
+  <int value="0" label="Headers do not include :status header field."/>
+  <int value="1" label=":status header does not start with a number."/>
+  <int value="2" label=":status header is an integer."/>
+  <int value="3" label=":status header is an integer followed by text."/>
+</enum>
+
 <enum name="StayVsLeave" type="int">
   <int value="0" label="Stay on the current page"/>
   <int value="1" label="Leave the current page"/>
@@ -108736,12 +109013,25 @@
 
 <histogram_suffixes name="ExtensionMessagingPortCreationTime" separator=".">
   <suffix name="Normal"
-      label="Created during any time other than the 'unload' or
-             'beforeunload' handlers."/>
+      label="Created during any time other than the 'unload' or 'beforeunload'
+             handlers.">
+    <obsolete>
+      Deprecated and removed from code as of 05/2015.
+    </obsolete>
+  </suffix>
   <suffix name="InBeforeUnload"
-      label="Created during an event handler for the 'beforeunload' event."/>
+      label="Created during an event handler for the 'beforeunload' event.">
+    <obsolete>
+      Deprecated and removed from code as of 05/2015.
+    </obsolete>
+  </suffix>
   <suffix name="InUnload"
-      label="Created during an event handler for the 'unload' event."/>
+      label="Created during an event handler for the 'unload' event.">
+    <obsolete>
+      Deprecated and removed from code as of 05/2015.
+    </obsolete>
+  </suffix>
+  <suffix name="Total" label="The total number of ports created."/>
   <affected-histogram name="Extensions.Messaging.ExtensionPortsCreated"/>
 </histogram_suffixes>
 
@@ -108751,6 +109041,7 @@
   <suffix name="Tab" label="A port opened to a tab context."/>
   <affected-histogram name="Extensions.Messaging.GetPortIdAsyncTime"/>
   <affected-histogram name="Extensions.Messaging.GetPortIdSyncTime"/>
+  <affected-histogram name="Extensions.Messaging.SetPortIdTime"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="ExtensionsDatabaseOpen" separator=".">
@@ -111966,15 +112257,47 @@
   <affected-histogram name="PageLoad.AbortTiming.NewNavigation.BeforeCommit"/>
   <affected-histogram name="PageLoad.AbortTiming.Reload.BeforeCommit"/>
   <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.ForwardBackNavigation.BeforeCommit"/>
+      name="PageLoad.Experimental.AbortTiming.ForwardBackNavigation.BeforeCommit">
+    <obsolete>
+      Deprecated in favor of UserGesture/UserInputEvent/BrowserInitiated.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit"/>
+      name="PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit">
+    <obsolete>
+      Deprecated in favor of UserGesture/UserInputEvent/BrowserInitiated.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.Reload.BeforeCommit"/>
+      name="PageLoad.Experimental.AbortTiming.Reload.BeforeCommit">
+    <obsolete>
+      Deprecated in favor of UserGesture/UserInputEvent/BrowserInitiated.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="PageLoadMetricsUserInitiated2" separator=".">
+  <suffix name="UserGesture"
+      label="(experimental) Page load that has a user gesture"/>
+  <suffix name="UserInputEvent"
+      label="(experimental) Page load that has a user input event"/>
+  <suffix name="BrowserInitiated"
+      label="(experimental) Page load that was initiated from the browser
+             process"/>
+  <affected-histogram
+      name="PageLoad.AbortTiming.ForwardBackNavigation.AfterCommit.BeforePaint"/>
+  <affected-histogram
+      name="PageLoad.AbortTiming.ForwardBackNavigation.BeforeCommit"/>
+  <affected-histogram
+      name="PageLoad.AbortTiming.NewNavigation.AfterCommit.BeforePaint"/>
+  <affected-histogram name="PageLoad.AbortTiming.NewNavigation.BeforeCommit"/>
+  <affected-histogram
+      name="PageLoad.AbortTiming.Reload.AfterCommit.BeforePaint"/>
+  <affected-histogram name="PageLoad.AbortTiming.Reload.BeforeCommit"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="PageLoadType">
   <suffix name="HistoryLoad"
       label="but only for user pressing back or forward"/>
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 0cfd03a..a96a3ae 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -119,6 +119,29 @@
     print log
 
 
+class _BlinkPerfMeasurementSlimmingPaintInvalidation(_BlinkPerfMeasurement):
+  """Measures blink perf with the new paint invalidation system (see:
+  https://goo.gl/eQczQW). The benchmarks using this measurement should be
+  removed when slimming paint invalidation ships."""
+  def CustomizeBrowserOptions(self, options):
+    _BlinkPerfMeasurement.CustomizeBrowserOptions(self, options)
+    options.AppendExtraBrowserArgs([
+        '--enable-blink-features=SlimmingPaintInvalidation'
+    ])
+
+
+class _BlinkPerfBenchmark(perf_benchmark.PerfBenchmark):
+  test = _BlinkPerfMeasurement
+
+  @classmethod
+  def Name(cls):
+    return 'blink_perf.' + cls.tag
+
+  def CreateStorySet(self, options):
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
+    return CreateStorySetFromPath(path, SKIPPED_FILE)
+
+
 class _SharedPywebsocketPageState(shared_page_state.SharedPageState):
   """Runs a pywebsocket server."""
 
@@ -129,17 +152,9 @@
 
 
 @benchmark.Disabled('all') # http://crbug.com/670069
-class BlinkPerfBindings(perf_benchmark.PerfBenchmark):
+class BlinkPerfBindings(_BlinkPerfBenchmark):
   tag = 'bindings'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.bindings'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Bindings')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'Bindings'
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
@@ -151,48 +166,28 @@
 
 
 @benchmark.Enabled('content-shell')
-class BlinkPerfBlinkGC(perf_benchmark.PerfBenchmark):
+class BlinkPerfBlinkGC(_BlinkPerfBenchmark):
   tag = 'blink_gc'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.blink_gc'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'BlinkGC')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'BlinkGC'
 
 
-class BlinkPerfCSS(perf_benchmark.PerfBenchmark):
+class BlinkPerfCSS(_BlinkPerfBenchmark):
   tag = 'css'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.css'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'CSS')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'CSS'
 
 
 @benchmark.Disabled('android-webview', # http://crbug.com/593200
                     'reference')  # http://crbug.com/576779
-class BlinkPerfCanvas(perf_benchmark.PerfBenchmark):
+class BlinkPerfCanvas(_BlinkPerfBenchmark):
   tag = 'canvas'
-  test = _BlinkPerfMeasurement
+  subdir = 'Canvas'
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
     return cls.IsSvelte(possible_browser)  # http://crbug.com/593973.
 
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.canvas'
-
   def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Canvas')
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
     story_set = CreateStorySetFromPath(
         path, SKIPPED_FILE,
         shared_page_state_class=(
@@ -204,143 +199,85 @@
     return story_set
 
 
-class BlinkPerfDOM(perf_benchmark.PerfBenchmark):
+class BlinkPerfDOM(_BlinkPerfBenchmark):
   tag = 'dom'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.dom'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'DOM')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'DOM'
 
 
 @benchmark.Disabled('win')  # http://crbug.com/588819
-class BlinkPerfEvents(perf_benchmark.PerfBenchmark):
+class BlinkPerfEvents(_BlinkPerfBenchmark):
   tag = 'events'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.events'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Events')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'Events'
 
 
 @benchmark.Disabled('win8')  # http://crbug.com/462350
 @benchmark.Disabled('win-reference')  # http://crbug.com/642884
-class BlinkPerfLayout(perf_benchmark.PerfBenchmark):
+class BlinkPerfLayout(_BlinkPerfBenchmark):
   tag = 'layout'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.layout'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Layout')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'Layout'
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
     return cls.IsSvelte(possible_browser)  # http://crbug.com/551950
 
 
-class BlinkPerfPaint(perf_benchmark.PerfBenchmark):
+class BlinkPerfPaint(_BlinkPerfBenchmark):
   tag = 'paint'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.paint'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Paint')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'Paint'
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
     return cls.IsSvelte(possible_browser)  # http://crbug.com/574483
 
 
+class BlinkPerfPaintSlimmingPaintInvalidation(BlinkPerfPaint):
+  tag = 'paint_slimmingpaintinvalidation'
+  test = _BlinkPerfMeasurementSlimmingPaintInvalidation
+
+
 @benchmark.Disabled('win')  # crbug.com/488493
-class BlinkPerfParser(perf_benchmark.PerfBenchmark):
+class BlinkPerfParser(_BlinkPerfBenchmark):
   tag = 'parser'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.parser'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Parser')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'Parser'
 
 
-class BlinkPerfSVG(perf_benchmark.PerfBenchmark):
+class BlinkPerfSVG(_BlinkPerfBenchmark):
   tag = 'svg'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.svg'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'SVG')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'SVG'
 
 
-class BlinkPerfShadowDOM(perf_benchmark.PerfBenchmark):
+class BlinkPerfSVGSlimmingPaintInvalidation(BlinkPerfSVG):
+  tag = 'svg_slimmingpaintinvalidation'
+  test = _BlinkPerfMeasurementSlimmingPaintInvalidation
+
+
+class BlinkPerfShadowDOM(_BlinkPerfBenchmark):
   tag = 'shadow_dom'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.shadow_dom'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'ShadowDOM')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'ShadowDOM'
 
 
 # This benchmark is for local testing, doesn't need to run on bots.
 @benchmark.Disabled('all')
-class BlinkPerfXMLHttpRequest(perf_benchmark.PerfBenchmark):
+class BlinkPerfXMLHttpRequest(_BlinkPerfBenchmark):
   tag = 'xml_http_request'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.xml_http_request'
-
-  def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'XMLHttpRequest')
-    return CreateStorySetFromPath(path, SKIPPED_FILE)
+  subdir = 'XMLHttpRequest'
 
 
 # Disabled on Windows and ChromeOS due to https://crbug.com/521887
 #@benchmark.Disabled('win', 'chromeos')
 # Disabling on remaining platforms due to heavy flake https://crbug.com/646938
 @benchmark.Disabled('all')
-class BlinkPerfPywebsocket(perf_benchmark.PerfBenchmark):
+class BlinkPerfPywebsocket(_BlinkPerfBenchmark):
   """The blink_perf.pywebsocket tests measure turn-around-time of 10MB
   send/receive for XHR, Fetch API and WebSocket. We might ignore < 10%
   regressions, because the tests are noisy and such regressions are
   often unreproducible (https://crbug.com/549017).
   """
   tag = 'pywebsocket'
-  test = _BlinkPerfMeasurement
-
-  @classmethod
-  def Name(cls):
-    return 'blink_perf.pywebsocket'
+  subdir = 'Pywebsocket'
 
   def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, 'Pywebsocket')
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
     return CreateStorySetFromPath(
         path, SKIPPED_FILE,
         shared_page_state_class=_SharedPywebsocketPageState)
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index 1dc5e7d..f1675a4 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -74,8 +74,7 @@
     return 'media.media_cns_cases'
 
 
-@benchmark.Enabled('android')
-@benchmark.Disabled('l', 'android-webview')  # WebView: crbug.com/419689
+@benchmark.Disabled('android')  # crbug.com/671628, WebView: crbug.com/419689.
 class MediaAndroid(perf_benchmark.PerfBenchmark):
   """Obtains media metrics for key user scenarios on Android."""
   test = media.Media
@@ -86,6 +85,9 @@
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
+    # crbug.com/672059
+    if possible_browser.platform.GetOSName() != "android":
+      return True
     # crbug.com/448092
     if cls.IsSvelte(possible_browser):
         return True
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index 0c4f353..4eb83b90 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -227,7 +227,10 @@
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
-    return cls.IsSvelte(possible_browser)  # http://crbug.com/611167
+    return (cls.IsSvelte(possible_browser) or  # http://crbug.com/611167
+            # http://crbug.com/671650
+            ((possible_browser.browser_type == 'reference' and
+              possible_browser.platform.GetDeviceTypeName() == 'Nexus 5')))
 
 
 @benchmark.Enabled('has tabs')  # http://crbug.com/612210
diff --git a/tools/perf/benchmarks/oortonline.py b/tools/perf/benchmarks/oortonline.py
index 810a1eb..e7c4317 100644
--- a/tools/perf/benchmarks/oortonline.py
+++ b/tools/perf/benchmarks/oortonline.py
@@ -95,6 +95,10 @@
     return options
 
   @classmethod
+  def ShouldDisable(cls, possible_browser):
+    return possible_browser.platform.GetDeviceTypeName() == 'Nexus 9'
+
+  @classmethod
   def Name(cls):
     return 'oortonline_tbmv2'
 
diff --git a/tools/perf/benchmarks/power.py b/tools/perf/benchmarks/power.py
index 06f7313..1047cab 100644
--- a/tools/perf/benchmarks/power.py
+++ b/tools/perf/benchmarks/power.py
@@ -34,9 +34,14 @@
     options.full_performance_mode = False
 
   @classmethod
-  def ShouldDisable(cls, possible_browser):  # http://crbug.com/597656
-    return (possible_browser.browser_type == 'reference' and
-            possible_browser.platform.GetDeviceTypeName() == 'Nexus 5X')
+  def ShouldDisable(cls, possible_browser):
+    # http://crbug.com/597656
+    if (possible_browser.browser_type == 'reference' and
+        possible_browser.platform.GetDeviceTypeName() == 'Nexus 5X'):
+      return True
+
+    # crbug.com/671631
+    return possible_browser.platform.GetDeviceTypeName() == 'Nexus 9'
 
   @classmethod
   def Name(cls):
diff --git a/tools/perf/benchmarks/spaceport.py b/tools/perf/benchmarks/spaceport.py
index c1a8f5cc..66e0678 100644
--- a/tools/perf/benchmarks/spaceport.py
+++ b/tools/perf/benchmarks/spaceport.py
@@ -76,6 +76,7 @@
     num_results = 0
     num_tests_in_spaceport = 24
     while num_results < num_tests_in_spaceport:
+      # TODO(catapult:#3028): Fix interpolation of JavaScript values.
       tab.WaitForJavaScriptExpression(
           'Object.keys(window.__results).length > %d' % num_results, 180)
       num_results = tab.EvaluateJavaScript(
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py
index ca75434..b6d82cf 100644
--- a/tools/perf/benchmarks/speedometer.py
+++ b/tools/perf/benchmarks/speedometer.py
@@ -56,6 +56,7 @@
     if tab.browser.platform.GetOSName() == 'android':
       iterationCount = 3
 
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     tab.ExecuteJavaScript("""
         // Store all the results in the benchmarkClient
         benchmarkClient._measuredValues = []
@@ -76,6 +77,7 @@
     for suite_name in self.enabled_suites:
       results.AddValue(list_of_scalar_values.ListOfScalarValues(
           page, suite_name, 'ms',
+          # TODO(catapult:#3028): Fix interpolation of JavaScript values.
           tab.EvaluateJavaScript("""
               var suite_times = [];
               for(var i = 0; i < benchmarkClient.iterationCount; i++) {
diff --git a/tools/perf/benchmarks/start_with_url.py b/tools/perf/benchmarks/start_with_url.py
index 45989ad..cb42df9 100644
--- a/tools/perf/benchmarks/start_with_url.py
+++ b/tools/perf/benchmarks/start_with_url.py
@@ -44,6 +44,11 @@
     super(StartWithUrlColdTBM, self).SetExtraBrowserOptions(options)
 
   @classmethod
+  def ShouldDisable(cls, possible_browser):  # http://crbug.com/667470
+    return (possible_browser.platform.GetDeviceTypeName() in
+            ['Nexus 7v2', 'Nexus 9'])
+
+  @classmethod
   def Name(cls):
     return 'start_with_url.cold.startup_pages'
 
diff --git a/tools/perf/chrome_telemetry_build/BUILD.gn b/tools/perf/chrome_telemetry_build/BUILD.gn
index f344185..8914f3c 100644
--- a/tools/perf/chrome_telemetry_build/BUILD.gn
+++ b/tools/perf/chrome_telemetry_build/BUILD.gn
@@ -94,10 +94,7 @@
   }
 
   if (is_linux) {
-    data_deps += [
-      "//tools/xdisplaycheck",
-      "//breakpad:dump_syms($host_toolchain)",
-    ]
+    data_deps += [ "//breakpad:dump_syms($host_toolchain)" ]
   }
 
   if (is_mac) {
diff --git a/tools/perf/measurements/multipage_skpicture_printer.py b/tools/perf/measurements/multipage_skpicture_printer.py
index 30b7afc..85e31b2e 100644
--- a/tools/perf/measurements/multipage_skpicture_printer.py
+++ b/tools/perf/measurements/multipage_skpicture_printer.py
@@ -28,5 +28,6 @@
     # Replace win32 path separator char '\' with '\\'.
     outpath = os.path.abspath(
         os.path.join(self._mskp_outdir, page.file_safe_name + '.mskp'))
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     js = _JS.format(outpath.replace('\\', '\\\\'))
     tab.EvaluateJavaScript(js)
diff --git a/tools/perf/measurements/rasterize_and_record_micro.py b/tools/perf/measurements/rasterize_and_record_micro.py
index 3fc818d..270f528 100644
--- a/tools/perf/measurements/rasterize_and_record_micro.py
+++ b/tools/perf/measurements/rasterize_and_record_micro.py
@@ -35,6 +35,7 @@
     time.sleep(self._start_wait_time)
 
     # Enqueue benchmark
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     tab.ExecuteJavaScript("""
         window.benchmark_results = {};
         window.benchmark_results.done = false;
diff --git a/tools/perf/measurements/skpicture_printer.py b/tools/perf/measurements/skpicture_printer.py
index 499aa48..0890fc0 100644
--- a/tools/perf/measurements/skpicture_printer.py
+++ b/tools/perf/measurements/skpicture_printer.py
@@ -30,6 +30,7 @@
     # Replace win32 path separator char '\' with '\\'.
     outpath = os.path.abspath(
         os.path.join(self._skp_outdir, page.file_safe_name))
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     js = _JS.format(outpath.replace('\\', '\\\\'))
     tab.EvaluateJavaScript(js)
     pictures = glob.glob(os.path.join(outpath, '*.skp'))
diff --git a/tools/perf/metrics/startup_metric.py b/tools/perf/metrics/startup_metric.py
index b0c47ca8..64aa03e5 100644
--- a/tools/perf/metrics/startup_metric.py
+++ b/tools/perf/metrics/startup_metric.py
@@ -107,6 +107,7 @@
     get_histogram_js = 'statsCollectionController.getBrowserHistogram("%s")'
 
     for display_name, histogram_name in self.HISTOGRAMS_TO_RECORD.iteritems():
+      # TODO(catapult:#3028): Fix interpolation of JavaScript values.
       result = tab.EvaluateJavaScript(get_histogram_js % histogram_name)
       result = json.loads(result)
       measured_time = 0
diff --git a/tools/perf/page_sets/blob_workshop.py b/tools/perf/page_sets/blob_workshop.py
index c6e6da6..9778b313 100644
--- a/tools/perf/page_sets/blob_workshop.py
+++ b/tools/perf/page_sets/blob_workshop.py
@@ -25,6 +25,7 @@
     for size_bytes in self._blob_sizes:
       with action_runner.CreateInteraction('Action_CreateAndReadBlob',
                                            repeatable=True):
+        # TODO(catapult:#3028): Fix interpolation of JavaScript values.
         action_runner.ExecuteJavaScript(
             'createAndRead(' + str(size_bytes) + ');')
         action_runner.WaitForJavaScriptCondition(
@@ -50,6 +51,7 @@
     for size_bytes in self._blob_sizes:
       with action_runner.CreateInteraction('Action_CreateBlob',
                                            repeatable=True):
+        # TODO(catapult:#3028): Fix interpolation of JavaScript values.
         action_runner.ExecuteJavaScript('createBlob(' + str(size_bytes) + ');')
 
     # Read blobs
diff --git a/tools/perf/page_sets/google_pages.py b/tools/perf/page_sets/google_pages.py
index 2a20f9d..d782a47 100644
--- a/tools/perf/page_sets/google_pages.py
+++ b/tools/perf/page_sets/google_pages.py
@@ -77,4 +77,5 @@
 
   def RunPageInteractions(self, action_runner):
     action_runner.WaitForElement(text='Welcome to AdWords!')
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript('console.timeEnd("%s");' % INTERACTION_NAME)
diff --git a/tools/perf/page_sets/indexeddb_endure_page.py b/tools/perf/page_sets/indexeddb_endure_page.py
index 4910782..eff676c 100644
--- a/tools/perf/page_sets/indexeddb_endure_page.py
+++ b/tools/perf/page_sets/indexeddb_endure_page.py
@@ -15,6 +15,7 @@
     self._subtest = subtest
 
   def RunPageInteractions(self, action_runner):
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript('window.testFilter = "' +
                                     self._subtest + '";')
     with action_runner.CreateInteraction('Action_Test'):
diff --git a/tools/perf/page_sets/key_silk_cases.py b/tools/perf/page_sets/key_silk_cases.py
index f6657eb3..2023f2b 100644
--- a/tools/perf/page_sets/key_silk_cases.py
+++ b/tools/perf/page_sets/key_silk_cases.py
@@ -418,6 +418,7 @@
 
   def ScrollKnowledgeCardToTop(self, action_runner, card_id):
     # scroll until the knowledge card is at the top
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript(
         "document.getElementById('%s').scrollIntoView()" % card_id)
 
@@ -647,17 +648,20 @@
     first_name = profile + 'paper-input#first /deep/ input'
     action_runner.WaitForElement(selector=first_name)
     # Input First Name:
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript('''
         var fn = document.querySelector('%s');
         fn.value = 'Chrome';
         fn.fire('input');''' % first_name)
     # Input Last Initial:
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript('''
         var li = document.querySelector('%s paper-input#last /deep/ input');
         li.value = 'E';
         li.fire('input');''' % profile)
     with action_runner.CreateInteraction('animation_interaction'):
       # Click the check-mark to login:
+      # TODO(catapult:#3028): Fix interpolation of JavaScript values.
       action_runner.ExecuteJavaScript('''
           window.topeka_page_transitions = 0;
           [].forEach.call(document.querySelectorAll(
diff --git a/tools/perf/page_sets/login_helpers/dropbox_login.py b/tools/perf/page_sets/login_helpers/dropbox_login.py
index 8e84981f..fda3a3a 100644
--- a/tools/perf/page_sets/login_helpers/dropbox_login.py
+++ b/tools/perf/page_sets/login_helpers/dropbox_login.py
@@ -32,6 +32,7 @@
 
   # Wait until the "Sign in" button is enabled and then click it.
   login_button_selector = '.login-form .login-button'
+  # TODO(catapult:#3028): Fix interpolation of JavaScript values.
   action_runner.WaitForJavaScriptCondition('''
       (function() {
         var loginButton = document.querySelector("%s");
diff --git a/tools/perf/page_sets/login_helpers/google_login.py b/tools/perf/page_sets/login_helpers/google_login.py
index 27fef3e..9c5a14e 100644
--- a/tools/perf/page_sets/login_helpers/google_login.py
+++ b/tools/perf/page_sets/login_helpers/google_login.py
@@ -43,6 +43,7 @@
        'https%3A%2F%2Faccounts.google.com%2FManageAccount')
 
   # Wait until either the email or password input is visible.
+  # TODO(catapult:#3028): Fix interpolation of JavaScript values.
   action_runner.WaitForJavaScriptCondition('%s || %s' % (
       _EMAIL_INPUT_VISIBLE_CONDITION, _PASSWORD_INPUT_VISIBLE_CONDITION))
 
diff --git a/tools/perf/page_sets/login_helpers/login_utils.py b/tools/perf/page_sets/login_helpers/login_utils.py
index 5e9bbe07..296ed20 100644
--- a/tools/perf/page_sets/login_helpers/login_utils.py
+++ b/tools/perf/page_sets/login_helpers/login_utils.py
@@ -45,6 +45,7 @@
       possible exceptions.
   """
   action_runner.WaitForElement(selector=input_selector)
+  # TODO(catapult:#3028): Fix interpolation of JavaScript values.
   action_runner.ExecuteJavaScript(
       'document.querySelector("%s").value = "%s";' %
       (input_selector, input_text))
@@ -73,4 +74,3 @@
   else:
     raise ValueError("Input ID can not be None or empty.")
   InputWithSelector(action_runner, input_text, element_selector)
-
diff --git a/tools/perf/page_sets/polymer.py b/tools/perf/page_sets/polymer.py
index ec3316f5..01e003a 100644
--- a/tools/perf/page_sets/polymer.py
+++ b/tools/perf/page_sets/polymer.py
@@ -111,6 +111,7 @@
 
   def AnimateShadow(self, action_runner, eid):
     for i in range(1, 6):
+      # TODO(catapult:#3028): Fix interpolation of JavaScript values.
       action_runner.ExecuteJavaScript(
           'document.getElementById("{0}").z = {1}'.format(eid, i))
       action_runner.Wait(1)
@@ -137,6 +138,7 @@
 
   def RunNavigateSteps(self, action_runner):
     super(PolymerSampler, self).RunNavigateSteps(action_runner)
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     waitForLoadJS = """
       window.Polymer.whenPolymerReady(function() {
         %s.contentWindow.Polymer.whenPolymerReady(function() {
@@ -190,11 +192,13 @@
   def DoActionOnWidgetType(self, action_runner, widget_type, action_function):
     # Find all widgets of this type, but skip any that are disabled or are
     # currently active as they typically don't produce animation frames.
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     element_list_query = (self.iframe_js +
         ('.querySelectorAll("body %s:not([disabled]):'
          'not([active])")' % widget_type))
     roles_count_query = element_list_query + '.length'
     for i in range(action_runner.EvaluateJavaScript(roles_count_query)):
+      # TODO(catapult:#3028): Fix interpolation of JavaScript values.
       element_query = element_list_query + ("[%d]" % i)
       if action_runner.EvaluateJavaScript(
           element_query + '.offsetParent != null'):
diff --git a/tools/perf/page_sets/repaint_helpers.py b/tools/perf/page_sets/repaint_helpers.py
index 24f138d2..93f2b9e 100644
--- a/tools/perf/page_sets/repaint_helpers.py
+++ b/tools/perf/page_sets/repaint_helpers.py
@@ -39,6 +39,7 @@
   with action_runner.CreateInteraction('Repaint'):
     action_runner.RepaintContinuously(seconds=5)
 
+  # TODO(catapult:#3028): Fix interpolation of JavaScript values.
   action_runner.ExecuteJavaScript("""
       window.benchmark_results.message_handled =
           chrome.gpuBenchmarking.sendMessageToMicroBenchmark(
diff --git a/tools/perf/page_sets/system_health/background_stories.py b/tools/perf/page_sets/system_health/background_stories.py
index a30cbf0..31df314 100644
--- a/tools/perf/page_sets/system_health/background_stories.py
+++ b/tools/perf/page_sets/system_health/background_stories.py
@@ -6,6 +6,8 @@
 from page_sets.system_health import system_health_story
 from page_sets.system_health.loading_stories import LoadGmailMobileStory
 
+from telemetry import decorators
+
 _WAIT_FOR_VIDEO_SECONDS = 5
 
 class _BackgroundStory(system_health_story.SystemHealthStory):
@@ -76,6 +78,7 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
 
 
+@decorators.Disabled('android')  # crbug.com.com/664505
 class BackgroundGmailMobileStory(LoadGmailMobileStory):
   NAME = 'background:tools:gmail'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index 62b843d..10f97613 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -225,6 +225,7 @@
     # window does not have a "Close" button, instead it has only a "Send link
     # to phone" button. So on tablets we run with the popup window open. The
     # popup is transparent, so this is mostly an aesthetical issue.
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     has_button = action_runner.EvaluateJavaScript(
         '!!document.querySelector("%s")' % self._CLOSE_BUTTON_SELECTOR)
     if has_button:
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index 3b0532d..b2e3828 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -187,6 +187,7 @@
     # window does not have a "Close" button, instead it has only a "Send link
     # to phone" button. So on tablets we run with the popup window open. The
     # popup is transparent, so this is mostly an aesthetical issue.
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     has_button = action_runner.EvaluateJavaScript(
         '!!document.querySelector("%s")' % self._CLOSE_BUTTON_SELECTOR)
     if has_button:
@@ -278,6 +279,7 @@
       'https://docs.google.com/document/d/1GvzDP-tTLmJ0myRhUAfTYWs3ZUFilUICg8psNHyccwQ/edit?usp=sharing')
 
 
+@decorators.Disabled('android')  # crbug.com.com/664505
 class _LoadGmailBaseStory(_LoadingStory):
   NAME = 'load:tools:gmail'
   URL = 'https://mail.google.com/mail/'
diff --git a/tools/perf/page_sets/system_health/long_running_stories.py b/tools/perf/page_sets/system_health/long_running_stories.py
index 417faae..2eed0ef 100644
--- a/tools/perf/page_sets/system_health/long_running_stories.py
+++ b/tools/perf/page_sets/system_health/long_running_stories.py
@@ -6,7 +6,7 @@
 from page_sets.system_health import platforms
 from page_sets.system_health import system_health_story
 
-from telemetry import benchmark
+from telemetry import decorators
 
 
 IDLE_TIME_IN_SECONDS = 100
@@ -83,6 +83,7 @@
         'document.getElementById("loading").style.display === "none"')
 
 
+@decorators.Disabled('android')  # crbug.com.com/664505
 class LongRunningGmailMobileForegroundStory(_LongRunningGmailMobileBase):
   NAME = 'long_running:tools:gmail-foreground'
 
@@ -91,7 +92,8 @@
   NAME = 'long_running:tools:gmail-foreground'
 
 
-@benchmark.Disabled('android-webview')  # Webview does not have tabs.
+@decorators.Disabled('android-webview'  # Weview does not have tabs.
+                  , 'android')  # crbug.com/664505
 class LongRunningGmailMobileBackgroundStory(_LongRunningGmailMobileBase):
   BACKGROUND = True
   NAME = 'long_running:tools:gmail-background'
diff --git a/tools/perf/page_sets/system_health/media_stories.py b/tools/perf/page_sets/system_health/media_stories.py
index 991df46..6a8501f 100644
--- a/tools/perf/page_sets/system_health/media_stories.py
+++ b/tools/perf/page_sets/system_health/media_stories.py
@@ -46,6 +46,7 @@
           self.PLAY_DURATION - self._GetTimeInSeconds(action_runner))
 
   def _GetTimeInSeconds(self, action_runner):
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     time_func = (
         'document.querySelector("%s").textContent' % self.TIME_SELECTOR)
     minutes, seconds = action_runner.EvaluateJavaScript(time_func).split(':')
diff --git a/tools/perf/page_sets/system_health/searching_stories.py b/tools/perf/page_sets/system_health/searching_stories.py
index 19c64a68..d63cf0740 100644
--- a/tools/perf/page_sets/system_health/searching_stories.py
+++ b/tools/perf/page_sets/system_health/searching_stories.py
@@ -31,6 +31,7 @@
     action_runner.WaitForElement(selector=self._RESULT_SELECTOR)
     action_runner.Wait(1)
     # TODO(petrcermak): Turn this into a proper Telemetry API (ScrollToElement).
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     result_visible_expression = ('''
         (function() {
           var resultElem = document.querySelector(\'%s\');
diff --git a/tools/perf/page_sets/todomvc.py b/tools/perf/page_sets/todomvc.py
index 4f095ec..35daccb 100644
--- a/tools/perf/page_sets/todomvc.py
+++ b/tools/perf/page_sets/todomvc.py
@@ -48,6 +48,7 @@
         """
     )
     action_runner.WaitForJavaScriptCondition('this.becameIdle === true')
+    # TODO(catapult:#3028): Fix interpolation of JavaScript values.
     action_runner.ExecuteJavaScript('console.timeEnd("%s");' % INTERACTION_NAME)
 
 
diff --git a/tools/perf/page_sets/top_7_stress.py b/tools/perf/page_sets/top_7_stress.py
index 6f29777..ee228d9 100644
--- a/tools/perf/page_sets/top_7_stress.py
+++ b/tools/perf/page_sets/top_7_stress.py
@@ -11,6 +11,7 @@
 
 
 def _WaitForLocationChange(action_runner, old_href):
+  # TODO(catapult:#3028): Fix interpolation of JavaScript values.
   action_runner.WaitForJavaScriptCondition(
       'document.location.href != "%s"' % old_href)
 
diff --git a/tools/perf/page_sets/tough_layout_cases.py b/tools/perf/page_sets/tough_layout_cases.py
index c5358d0..b5aac16 100644
--- a/tools/perf/page_sets/tough_layout_cases.py
+++ b/tools/perf/page_sets/tough_layout_cases.py
@@ -34,7 +34,8 @@
       'http://oilevent.com',
       'http://www.muzoboss.ru',
       'http://natunkantha.com',
-      'http://www.mossiella.com',
+      # http://crbug.com/671645
+      #'http://www.mossiella.com',
       'http://bookish.com',
       'http://mydiyclub.com',
       'http://amarchoti.blogspot.com',
diff --git a/ui/android/screen_android.h b/ui/android/screen_android.h
index 44ffb64..4352c0e3 100644
--- a/ui/android/screen_android.h
+++ b/ui/android/screen_android.h
@@ -8,10 +8,6 @@
 #include <jni.h>
 #include "ui/android/ui_android_export.h"
 
-namespace display {
-class Screen;
-}
-
 namespace ui {
 
 bool RegisterScreenAndroid(JNIEnv* env);
diff --git a/ui/arc/notification/arc_custom_notification_item.cc b/ui/arc/notification/arc_custom_notification_item.cc
index 78ecfa4..827e8cb 100644
--- a/ui/arc/notification/arc_custom_notification_item.cc
+++ b/ui/arc/notification/arc_custom_notification_item.cc
@@ -14,6 +14,7 @@
 #include "ui/arc/notification/arc_custom_notification_view.h"
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_types.h"
+#include "ui/message_center/views/custom_notification_content_view_delegate.h"
 
 namespace arc {
 
@@ -26,8 +27,12 @@
   explicit ArcNotificationDelegate(ArcCustomNotificationItem* item)
       : item_(item) {}
 
-  std::unique_ptr<views::View> CreateCustomContent() override {
-    return base::MakeUnique<ArcCustomNotificationView>(item_);
+  std::unique_ptr<message_center::CustomContent> CreateCustomContent()
+      override {
+    auto view = base::MakeUnique<ArcCustomNotificationView>(item_);
+    auto content_view_delegate = view->CreateContentViewDelegate();
+    return base::MakeUnique<message_center::CustomContent>(
+        std::move(view), std::move(content_view_delegate));
   }
 
  private:
diff --git a/ui/arc/notification/arc_custom_notification_view.cc b/ui/arc/notification/arc_custom_notification_view.cc
index 80623f2a..ed6d8fd4 100644
--- a/ui/arc/notification/arc_custom_notification_view.cc
+++ b/ui/arc/notification/arc_custom_notification_view.cc
@@ -22,6 +22,7 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/painter.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/window_util.h"
 
@@ -144,6 +145,34 @@
   DISALLOW_COPY_AND_ASSIGN(SlideHelper);
 };
 
+class ArcCustomNotificationView::ContentViewDelegate
+    : public message_center::CustomNotificationContentViewDelegate {
+ public:
+  explicit ContentViewDelegate(ArcCustomNotificationView* owner)
+      : owner_(owner) {}
+
+  bool IsCloseButtonFocused() const override {
+    if (owner_->floating_close_button_ == nullptr)
+      return false;
+    return owner_->floating_close_button_->HasFocus();
+  }
+
+  void RequestFocusOnCloseButton() override {
+    if (owner_->floating_close_button_)
+      owner_->floating_close_button_->RequestFocus();
+    owner_->UpdateCloseButtonVisiblity();
+  }
+
+  bool IsPinned() const override {
+    return owner_->floating_close_button_ == nullptr;
+  }
+
+ private:
+  ArcCustomNotificationView* const owner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentViewDelegate);
+};
+
 ArcCustomNotificationView::ArcCustomNotificationView(
     ArcCustomNotificationItem* item)
     : item_(item),
@@ -174,13 +203,24 @@
     ArcNotificationSurfaceManager::Get()->RemoveObserver(this);
 }
 
+std::unique_ptr<message_center::CustomNotificationContentViewDelegate>
+ArcCustomNotificationView::CreateContentViewDelegate() {
+  return base::MakeUnique<ArcCustomNotificationView::ContentViewDelegate>(this);
+}
+
 void ArcCustomNotificationView::CreateFloatingCloseButton() {
   if (!surface_)
     return;
 
+  // TODO(yhanada): Make the close button get focus after the entire
+  // notification
   floating_close_button_ = new views::ImageButton(this);
   floating_close_button_->set_background(
       views::Background::CreateSolidBackground(SK_ColorTRANSPARENT));
+  floating_close_button_->SetFocusForPlatform();
+  floating_close_button_->SetFocusPainter(
+      views::Painter::CreateSolidFocusPainter(message_center::kFocusBorderColor,
+                                              gfx::Insets(1, 2, 2, 2)));
 
   // The sizes below are in DIPs.
   constexpr int kPaddingFromBorder = 4;
@@ -262,7 +302,8 @@
 
   const bool target_visiblity =
       surface_->window()->GetBoundsInScreen().Contains(
-          display::Screen::GetScreen()->GetCursorScreenPoint());
+          display::Screen::GetScreen()->GetCursorScreenPoint()) ||
+      floating_close_button_->HasFocus();
   if (target_visiblity == floating_close_button_widget_->IsVisible())
     return;
 
diff --git a/ui/arc/notification/arc_custom_notification_view.h b/ui/arc/notification/arc_custom_notification_view.h
index 5c29831..949cdee8 100644
--- a/ui/arc/notification/arc_custom_notification_view.h
+++ b/ui/arc/notification/arc_custom_notification_view.h
@@ -12,6 +12,7 @@
 #include "ui/arc/notification/arc_custom_notification_item.h"
 #include "ui/arc/notification/arc_notification_surface_manager.h"
 #include "ui/aura/window_observer.h"
+#include "ui/message_center/views/custom_notification_content_view_delegate.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/native/native_view_host.h"
 
@@ -36,7 +37,11 @@
   explicit ArcCustomNotificationView(ArcCustomNotificationItem* item);
   ~ArcCustomNotificationView() override;
 
+  std::unique_ptr<message_center::CustomNotificationContentViewDelegate>
+  CreateContentViewDelegate();
+
  private:
+  class ContentViewDelegate;
   class EventForwarder;
   class SlideHelper;
 
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index d318eab..03c333e 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -123,7 +123,7 @@
     Window* window,
     const std::unordered_map<std::string, std::vector<uint8_t>>& properties) {
   auto type_iter =
-      properties.find(ui::mojom::WindowManager::kWindowType_Property);
+      properties.find(ui::mojom::WindowManager::kWindowType_InitProperty);
   if (type_iter == properties.end())
     return;
 
@@ -1353,7 +1353,7 @@
       mojo::UnorderedMapToMap(transport_properties);
   ui::mojom::WindowType window_type = ui::mojom::WindowType::UNKNOWN;
   auto type_iter =
-      properties.find(ui::mojom::WindowManager::kWindowType_Property);
+      properties.find(ui::mojom::WindowManager::kWindowType_InitProperty);
   if (type_iter != properties.end()) {
     // TODO: validation! http://crbug.com/654924.
     window_type = static_cast<ui::mojom::WindowType>(
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 4671a454..a83763d 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -144,7 +144,7 @@
   const uint8_t server_test_property1_value = 91;
   data->properties[kTestPropertyServerKey1] =
       ConvertToPropertyTransportValue(server_test_property1_value);
-  data->properties[ui::mojom::WindowManager::kWindowType_Property] =
+  data->properties[ui::mojom::WindowManager::kWindowType_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           static_cast<int32_t>(ui::mojom::WindowType::BUBBLE));
   data->parent_id = server_id(root_window());
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index cbb826e..add2617 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -892,8 +892,6 @@
       "//ui/events/platform/x11",
       "//ui/gfx/x",
     ]
-
-    data_deps += [ "//tools/xdisplaycheck" ]
   }
 
   if (is_mac) {
diff --git a/ui/base/cursor/cursor_loader_x11.cc b/ui/base/cursor/cursor_loader_x11.cc
index 293bba65..1929d9f 100644
--- a/ui/base/cursor/cursor_loader_x11.cc
+++ b/ui/base/cursor/cursor_loader_x11.cc
@@ -87,7 +87,11 @@
     case ui::kCursorPointer:
       return "left_ptr";
     case ui::kCursorMove:
-      return "move";
+      // Returning "move" is the correct thing here, but Blink doesn't
+      // make a distinction between move and all-scroll.  Other
+      // platforms use a cursor more consistent with all-scroll, so
+      // use that.
+      return "all-scroll";
     case ui::kCursorCross:
       return "crosshair";
     case ui::kCursorHand:
@@ -172,6 +176,7 @@
     { "progress",    "left_ptr_watch",  XC_watch },
     { "wait",        nullptr,           XC_watch },
     { "cell",        nullptr,           XC_plus },
+    { "all-scroll",  nullptr,           XC_fleur},
     { "crosshair",   nullptr,           XC_cross },
     { "text",        nullptr,           XC_xterm },
     { "not-allowed", "crossed_circle",  None },
diff --git a/ui/base/ime/dummy_text_input_client.cc b/ui/base/ime/dummy_text_input_client.cc
index 4547d7ce..60ae481 100644
--- a/ui/base/ime/dummy_text_input_client.cc
+++ b/ui/base/ime/dummy_text_input_client.cc
@@ -116,8 +116,7 @@
                                                     size_t after) {
 }
 
-void DummyTextInputClient::EnsureCaretInRect(const gfx::Rect& rect)  {
-}
+void DummyTextInputClient::EnsureCaretNotInRect(const gfx::Rect& rect) {}
 
 bool DummyTextInputClient::IsTextEditCommandEnabled(
     TextEditCommand command) const {
diff --git a/ui/base/ime/dummy_text_input_client.h b/ui/base/ime/dummy_text_input_client.h
index d1546aa..41c65e4 100644
--- a/ui/base/ime/dummy_text_input_client.h
+++ b/ui/base/ime/dummy_text_input_client.h
@@ -46,7 +46,7 @@
   bool ChangeTextDirectionAndLayoutAlignment(
       base::i18n::TextDirection direction) override;
   void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretInRect(const gfx::Rect& rect) override;
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override;
   bool IsTextEditCommandEnabled(TextEditCommand command) const override;
   void SetTextEditCommandForNextKeyEvent(TextEditCommand command) override;
 
diff --git a/ui/base/ime/text_input_client.h b/ui/base/ime/text_input_client.h
index 3d032b0..d76c00a 100644
--- a/ui/base/ime/text_input_client.h
+++ b/ui/base/ime/text_input_client.h
@@ -162,13 +162,13 @@
   // between browser and renderer.
   virtual void ExtendSelectionAndDelete(size_t before, size_t after) = 0;
 
-  // Ensure the caret is within |rect|.  |rect| is in screen coordinates and
+  // Ensure the caret is not in |rect|.  |rect| is in screen coordinates and
   // may extend beyond the bounds of this TextInputClient.
   // Note: On Windows, the returned value is supposed to be DIP (Density
   // Independent Pixel).
   // TODO(ime): Have a clear spec whether the returned value is DIP or not.
   // http://crbug.com/360334
-  virtual void EnsureCaretInRect(const gfx::Rect& rect) = 0;
+  virtual void EnsureCaretNotInRect(const gfx::Rect& rect) = 0;
 
   // Returns true if |command| is currently allowed to be executed.
   virtual bool IsTextEditCommandEnabled(TextEditCommand command) const = 0;
diff --git a/ui/base/resource/resource_bundle_android.cc b/ui/base/resource/resource_bundle_android.cc
index 21ef9ff..47a9820 100644
--- a/ui/base/resource/resource_bundle_android.cc
+++ b/ui/base/resource/resource_bundle_android.cc
@@ -113,7 +113,7 @@
     return std::string();
   }
 
-  locale_resources_data_.reset(data_pack.release());
+  locale_resources_data_ = std::move(data_pack);
   return app_locale;
 }
 
diff --git a/ui/compositor/layer_animator_unittest.cc b/ui/compositor/layer_animator_unittest.cc
index 1d7f074..51f883b4 100644
--- a/ui/compositor/layer_animator_unittest.cc
+++ b/ui/compositor/layer_animator_unittest.cc
@@ -2632,7 +2632,7 @@
   Layer layer;
   root_1.Add(&layer);
   LayerAnimator* animator = layer.GetAnimator();
-  EXPECT_FALSE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_FALSE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   double target_opacity = 1.0;
   base::TimeDelta time_delta = base::TimeDelta::FromSeconds(1);
@@ -2641,12 +2641,12 @@
       LayerAnimationElement::CreateOpacityElement(target_opacity, time_delta)));
   EXPECT_TRUE(compositor_1->layer_animator_collection()->HasActiveAnimators());
   EXPECT_FALSE(compositor_2->layer_animator_collection()->HasActiveAnimators());
-  EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_TRUE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   root_2.Add(&layer);
   EXPECT_FALSE(compositor_1->layer_animator_collection()->HasActiveAnimators());
   EXPECT_TRUE(compositor_2->layer_animator_collection()->HasActiveAnimators());
-  EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_TRUE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   host_2.reset();
   host_1.reset();
@@ -2676,13 +2676,13 @@
 
   animator->ScheduleAnimation(new LayerAnimationSequence(
       LayerAnimationElement::CreateOpacityElement(target_opacity, time_delta)));
-  EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_TRUE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   root.Remove(&layer);
-  EXPECT_FALSE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_FALSE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   root.Add(&layer);
-  EXPECT_TRUE(layer.cc_layer_for_testing()->HasActiveAnimationForTesting());
+  EXPECT_TRUE(layer.cc_layer_for_testing()->HasTickingAnimationForTesting());
 
   host.reset();
   TerminateContextFactoryForTests();
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 6af9ee7..75f29657 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -924,7 +924,7 @@
   base::JSONReader json_reader;
   std::unique_ptr<base::Value> debug_info_value(json_reader.ReadToValue(json));
   EXPECT_TRUE(debug_info_value);
-  EXPECT_TRUE(debug_info_value->IsType(base::Value::TYPE_DICTIONARY));
+  EXPECT_TRUE(debug_info_value->IsType(base::Value::Type::DICTIONARY));
   base::DictionaryValue* dictionary = 0;
   EXPECT_TRUE(debug_info_value->GetAsDictionary(&dictionary));
   std::string roundtrip;
diff --git a/ui/events/blink/compositor_thread_event_queue.cc b/ui/events/blink/compositor_thread_event_queue.cc
index 58290fd..9eec24e 100644
--- a/ui/events/blink/compositor_thread_event_queue.cc
+++ b/ui/events/blink/compositor_thread_event_queue.cc
@@ -25,7 +25,7 @@
 std::unique_ptr<EventWithCallback> CompositorThreadEventQueue::Pop() {
   std::unique_ptr<EventWithCallback> result;
   if (!queue_.empty()) {
-    result.reset(queue_.front().release());
+    result = std::move(queue_.front());
     queue_.pop_front();
   }
   return result;
diff --git a/ui/events/event_source.cc b/ui/events/event_source.cc
index a93b6d2..2736696 100644
--- a/ui/events/event_source.cc
+++ b/ui/events/event_source.cc
@@ -62,7 +62,7 @@
     details = DeliverEventToProcessor(new_event.get());
     if (details.dispatcher_destroyed)
       return details;
-    rewritten_event.reset(new_event.release());
+    rewritten_event = std::move(new_event);
   }
   return EventDispatchDetails();
 }
diff --git a/ui/events/mojo/latency_info.mojom b/ui/events/mojo/latency_info.mojom
index 034896b..82cead5 100644
--- a/ui/events/mojo/latency_info.mojom
+++ b/ui/events/mojo/latency_info.mojom
@@ -4,7 +4,7 @@
 
 module ui.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/time.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 enum LatencyComponentType {
diff --git a/ui/events/ozone/layout/keyboard_layout_engine_manager.cc b/ui/events/ozone/layout/keyboard_layout_engine_manager.cc
index 7baf7a6..735aac1 100644
--- a/ui/events/ozone/layout/keyboard_layout_engine_manager.cc
+++ b/ui/events/ozone/layout/keyboard_layout_engine_manager.cc
@@ -27,7 +27,7 @@
 void KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
     std::unique_ptr<KeyboardLayoutEngine> engine) {
   if (instance_)
-    instance_->keyboard_layout_engine_.reset(engine.release());
+    instance_->keyboard_layout_engine_ = std::move(engine);
   else
     new KeyboardLayoutEngineManager(engine.release());
 }
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index f94a4a8..ae775ff 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -60,50 +60,6 @@
   opacity: 0.8;
 }
 
-.dialog-container ::-webkit-scrollbar {
-  height: 0;
-  width: 0;
-}
-
-/* TODO(mtomasz): Flip scrollbars to the opposite side for RTL languages. */
-.scrollbar-vertical {
-  bottom: 0;
-  flex: none;
-  pointer-events: none;
-  position: absolute;
-  right: 0;
-  top: 0;
-  width: 10px;
-  z-index: 500;  /* Must be below the contextmenu (600). */
-}
-
-.scrollbar-button {
-  background-color: black;
-  border-radius: 3px;
-  border: 1px solid #ccc;
-  box-sizing: border-box;
-  height: 50%;
-  margin-right: 2px;
-  opacity: 0;
-  pointer-events: auto;
-  position: absolute;
-  transition: opacity 100ms;
-  width: 8px;
-}
-
-:hover > .scrollbar-vertical > .scrollbar-button,
-.scrollbar-vertical > .scrollbar-button.scrolling {
-  opacity: 0.3;
-}
-
-.scrollbar-vertical > .scrollbar-button:hover {
-  opacity: 0.4;
-}
-
-.scrollbar-vertical > .scrollbar-button.pressed {
-  opacity: 0.5;
-}
-
 /* Main part of the dialog between header and footer. */
 .dialog-container {
   align-items: stretch;
@@ -295,6 +251,10 @@
   position: absolute;
   right: 0;
   top: 0;
+
+  /* TODO(yamaguchi): Remove these when crbug.com/671644 is resolved. */
+  background-color: rgb(250, 250, 250);
+  contain: paint;
 }
 
 #directory-tree .tree-row {
@@ -1145,9 +1105,9 @@
 }
 
 #file-list {
-  /* Override overflow specifying by table_list.js to use the original scroll
-     bar. crbug.com/391698 */
-  overflow: scroll !important;
+  /* TODO(yamaguchi): Remove these when crbug.com/671644 is resolved. */
+  background-color: rgb(250, 250, 250);
+  contain: paint;
 }
 
 #file-list .drag-selection-border {
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
index 325c016..42a8306 100644
--- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
+++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
@@ -154,7 +154,6 @@
           './ui/multi_profile_share_dialog.js',
           './ui/progress_center_panel.js',
           './ui/providers_menu.js',
-          './ui/scrollbar.js',
           './ui/search_box.js',
           './ui/share_dialog.js',
           './ui/single_file_details.js',
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index 71439bf..c731a57 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -168,7 +168,6 @@
 //<include src="ui/multi_profile_share_dialog.js">
 //<include src="ui/progress_center_panel.js">
 //<include src="ui/providers_menu.js">
-//<include src="ui/scrollbar.js">
 //<include src="ui/search_box.js">
 //<include src="ui/share_dialog.js">
 //<include src="ui/single_file_details.js">
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller.js b/ui/file_manager/file_manager/foreground/js/task_controller.js
index 0ecdef30..a703d14c 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller.js
@@ -97,7 +97,9 @@
       this.onSelectionChanged_.bind(this));
   this.selectionHandler_.addEventListener(
       FileSelectionHandler.EventType.CHANGE_THROTTLED,
-      this.onSelectionChangeThrottled_.bind(this));
+      this.updateTasks_.bind(this));
+  chrome.fileManagerPrivate.onAppsUpdated.addListener(
+      this.updateTasks_.bind(this));
 }
 
 /**
@@ -277,10 +279,10 @@
 };
 
 /**
- * Handles change of selection asynchronously and updates context menu.
+ * Updates available tasks opened from context menu or the open button.
  * @private
  */
-TaskController.prototype.onSelectionChangeThrottled_ = function() {
+TaskController.prototype.updateTasks_ = function() {
   var selection = this.selectionHandler_.selection;
   if (this.dialogType_ === DialogType.FULL_PAGE &&
       (selection.directoryCount > 0 || selection.fileCount > 0)) {
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
index b93e58d2..34abed0 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
@@ -34,7 +34,10 @@
         {taskId:'handler-extension-id|file|open', isDefault: false},
         {taskId:'handler-extension-id|file|play', isDefault: true}
       ]), 0);
-    }
+    },
+    onAppsUpdated: {
+      addListener: function() {},
+    },
   };
 
   var fileSystem = new MockFileSystem('volumeId');
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 317e979..e51bae72 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -1247,9 +1247,6 @@
   chrome.fileManagerPrivate.onDirectoryChanged.addListener(
       this.privateOnDirectoryChangedBound_);
 
-  this.scrollBar_ = new ScrollBar();
-  this.scrollBar_.initialize(this.parentElement, this);
-
   /**
    * Flag to show fake entries in the tree.
    * @type {boolean}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
index debe979..e1f0e19 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
@@ -29,7 +29,6 @@
   <script src="../mock_navigation_list_model.js"></script>
   <script src="../navigation_list_model.js"></script>
   <script src="directory_tree.js"></script>
-  <script src="scrollbar.js"></script>
 
   <script src="directory_tree_unittest.js"></script>
 </body>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index dca897e..758df34 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -82,9 +82,6 @@
   /** @private {function(!Event)} */
   self.onThumbnailLoadedBound_ = self.onThumbnailLoaded_.bind(self);
 
-  self.scrollBar_ = new ScrollBar();
-  self.scrollBar_.initialize(self.parentElement, self);
-
   self.itemConstructor = function(entry) {
     var item = self.ownerDocument.createElement('li');
     FileGrid.Item.decorate(
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index e5ef783..d2f85f1 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -433,9 +433,6 @@
   self.setRenderFunction(self.renderTableRow_.bind(self,
       self.getRenderFunction()));
 
-  self.scrollBar_ = new ScrollBar();
-  self.scrollBar_.initialize(self, self.list);
-
   // Keep focus on the file list when clicking on the header.
   self.header.addEventListener('mousedown', function(e) {
     self.list.focus();
diff --git a/ui/file_manager/file_manager/foreground/js/ui/scrollbar.js b/ui/file_manager/file_manager/foreground/js/ui/scrollbar.js
deleted file mode 100644
index a5f38986..0000000
--- a/ui/file_manager/file_manager/foreground/js/ui/scrollbar.js
+++ /dev/null
@@ -1,256 +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.
-
-/**
- * Creates a new scroll bar element.
- * @extends {HTMLDivElement}
- * @constructor
- */
-var ScrollBar = cr.ui.define('div');
-
-/**
- * Mode of the scrollbar. As for now, only vertical scrollbars are supported.
- * @enum {number}
- */
-ScrollBar.Mode = {
-  VERTICAL: 0,
-  HORIZONTAL: 1
-};
-
-ScrollBar.prototype = {
-  set mode(value) {
-    this.mode_ = value;
-    if (this.mode_ == ScrollBar.Mode.VERTICAL) {
-      this.classList.remove('scrollbar-horizontal');
-      this.classList.add('scrollbar-vertical');
-    } else {
-      this.classList.remove('scrollbar-vertical');
-      this.classList.add('scrollbar-horizontal');
-    }
-    this.redraw_();
-  },
-  get mode() {
-    return this.mode_;
-  }
-};
-
-/**
- * Inherits after HTMLDivElement.
- */
-ScrollBar.prototype.__proto__ = HTMLDivElement.prototype;
-
-/**
- * Initializes the DOM structure of the scrollbar.
- */
-ScrollBar.prototype.decorate = function() {
-  this.classList.add('scrollbar');
-  this.button_ = util.createChild(this, 'scrollbar-button', 'div');
-  this.mode = ScrollBar.Mode.VERTICAL;
-  this.idleTimerId_ = 0;
-
-  this.button_.addEventListener('mousedown',
-                                this.onButtonPressed_.bind(this));
-  this.button_.addEventListener('wheel', this.onWheel_.bind(this));
-  window.addEventListener('mouseup', this.onMouseUp_.bind(this));
-  window.addEventListener('mousemove', this.onMouseMove_.bind(this));
-};
-
-/**
- * Initialize a scrollbar.
- *
- * @param {Element} parent Parent element, must have a relative or absolute
- *     positioning.
- * @param {Element=} opt_scrollableArea Element with scrollable contents.
- *     If not passed, then call attachToView manually when the scrollable
- *     element becomes available.
- */
-ScrollBar.prototype.initialize = function(parent, opt_scrollableArea) {
-  parent.appendChild(this);
-  if (opt_scrollableArea)
-    this.attachToView(opt_scrollableArea);
-};
-
-/**
- * Attaches the scrollbar to a scrollable element and attaches handlers.
- * @param {Element} view Scrollable element.
- */
-ScrollBar.prototype.attachToView = function(view) {
-  this.view_ = view;
-  this.view_.addEventListener('scroll', this.onScroll_.bind(this));
-  this.view_.addEventListener('relayout', this.onRelayout_.bind(this));
-  this.domObserver_ = new MutationObserver(this.onDomChanged_.bind(this));
-  this.domObserver_.observe(
-      this.view_,
-      /** @type {MutationObserverInit} */ ({subtree: true, attributes: true}));
-  this.onRelayout_();
-};
-
-/**
- * Handles scroll by a mouse wheel.
- *
- * @param {Event} event Mouse event.
- * @private
- */
-ScrollBar.prototype.onWheel_ = function(event) {
-  var scrollPosition = this.view_.scrollTop + event.deltaY;
-  // Ensure the scrollbar is in the view.
-  var scrollBottom =
-      Math.max(this.view_.scrollHeight - this.view_.clientHeight, 0);
-  scrollPosition = Math.max(Math.min(scrollPosition, scrollBottom), 0);
-
-  // TODO(yamaguchi): Invoke native scroll instead of setting scrollTop.
-  // This implementation will bypass the smooth scroll animation seen with
-  // native scroll.
-  this.scrollTop_ = scrollPosition;
-  this.view_.scrollTop = scrollPosition;
-  this.redraw_();
-}
-
-/**
- * Scroll handler.
- * @private
- */
-ScrollBar.prototype.onScroll_ = function() {
-  this.scrollTop_ = this.view_.scrollTop;
-  this.redraw_();
-
-  // Add class 'scrolling' to scrollbar to make it visible while scrolling.
-  this.button_.classList.add('scrolling');
-
-  // Set timer to remove class 'scrolling' after scrolling becomes idle.
-  if (this.idleTimerId_)
-    clearTimeout(this.idleTimerId_);
-  this.idleTimerId_ = setTimeout(function() {
-    this.idleTimerId_ = 0;
-    this.button_.classList.remove('scrolling');
-  }.bind(this), 1000);
-};
-
-/**
- * Relayout handler.
- * @private
- */
-ScrollBar.prototype.onRelayout_ = function() {
-  this.scrollHeight_ = this.view_.scrollHeight;
-  this.clientHeight_ = this.view_.clientHeight;
-  this.offsetTop_ = this.view_.offsetTop;
-  this.scrollTop_ = this.view_.scrollTop;
-  this.redraw_();
-};
-
-/**
- * Pressing on the scrollbar's button handler.
- *
- * @param {Event} event Pressing event.
- * @private
- */
-ScrollBar.prototype.onButtonPressed_ = function(event) {
-  this.buttonPressed_ = true;
-  this.buttonPressedEvent_ = event;
-  this.buttonPressedPosition_ = this.button_.offsetTop - this.view_.offsetTop;
-  this.button_.classList.add('pressed');
-
-  event.preventDefault();
-};
-
-/**
- * Releasing the button handler. Note, that it may not be called when releasing
- * outside of the window. Therefore this is also called from onMouseMove_.
- *
- * @param {Event} event Mouse event.
- * @private
- */
-ScrollBar.prototype.onMouseUp_ = function(event) {
-  this.buttonPressed_ = false;
-  this.button_.classList.remove('pressed');
-};
-
-/**
- * Mouse move handler. Updates the scroll position.
- *
- * @param {Event} event Mouse event.
- * @private
- */
-ScrollBar.prototype.onMouseMove_ = function(event) {
-  if (!this.buttonPressed_)
-    return;
-  if (!event.which) {
-    this.onMouseUp_(event);
-    return;
-  }
-  var clientSize = this.clientHeight_;
-  var totalSize = this.scrollHeight_;
-  // TODO(hirono): Fix the geometric calculation.  crbug.com/253779
-  var buttonSize = Math.max(50, clientSize / totalSize * clientSize);
-  var buttonPosition = this.buttonPressedPosition_ +
-      (event.screenY - this.buttonPressedEvent_.screenY);
-  // Ensures the scrollbar is in the view.
-  buttonPosition =
-      Math.max(0, Math.min(buttonPosition, clientSize - buttonSize));
-  var scrollPosition;
-  if (clientSize > buttonSize) {
-    scrollPosition = Math.max(totalSize - clientSize, 0) *
-        buttonPosition / (clientSize - buttonSize);
-  } else {
-    scrollPosition = 0;
-  }
-
-  this.scrollTop_ = scrollPosition;
-  this.view_.scrollTop = scrollPosition;
-  this.redraw_();
-};
-
-/**
- * Handles changed in Dom by redrawing the scrollbar. Ignores consecutive calls.
- * @private
- */
-ScrollBar.prototype.onDomChanged_ = function() {
-  if (this.domChangedTimer_) {
-    clearTimeout(this.domChangedTimer_);
-    this.domChangedTimer_ = null;
-  }
-  this.domChangedTimer_ = setTimeout(function() {
-    this.onRelayout_();
-    this.domChangedTimer_ = null;
-  }.bind(this), 50);
-};
-
-/**
- * Redraws the scrollbar.
- * @private
- */
-ScrollBar.prototype.redraw_ = function() {
-  if (!this.view_)
-    return;
-
-  var clientSize = this.clientHeight_;
-  var clientTop = this.offsetTop_;
-  var scrollPosition = this.scrollTop_;
-  var totalSize = this.scrollHeight_;
-  var hidden = totalSize <= clientSize;
-
-  var buttonSize = Math.max(50, clientSize / totalSize * clientSize);
-  var buttonPosition;
-  if (clientSize - buttonSize > 0) {
-    buttonPosition = scrollPosition / (totalSize - clientSize) *
-        (clientSize - buttonSize);
-  } else {
-    buttonPosition = 0;
-  }
-  var buttonTop = buttonPosition + clientTop;
-
-  var time = Date.now();
-  if (this.hidden != hidden ||
-      this.lastButtonTop_ != buttonTop ||
-      this.lastButtonSize_ != buttonSize) {
-    requestAnimationFrame(function() {
-      this.hidden = hidden;
-      this.button_.style.top = buttonTop + 'px';
-      this.button_.style.height = buttonSize + 'px';
-    }.bind(this));
-  }
-
-  this.lastButtonTop_ = buttonTop;
-  this.lastButtonSize_ = buttonSize;
-};
diff --git a/ui/gfx/image/image_skia_operations.cc b/ui/gfx/image/image_skia_operations.cc
index 3836235..e2b67d3 100644
--- a/ui/gfx/image/image_skia_operations.cc
+++ b/ui/gfx/image/image_skia_operations.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkClipOp.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
@@ -370,11 +371,8 @@
 // |source| that represent requested scale factors.
 class DropShadowSource : public ImageSkiaSource {
  public:
-  DropShadowSource(const ImageSkia& source,
-                   const ShadowValues& shadows_in_dip)
-      : source_(source),
-        shaodws_in_dip_(shadows_in_dip) {
-  }
+  DropShadowSource(const ImageSkia& source, const ShadowValues& shadows_in_dip)
+      : source_(source), shadows_in_dip_(shadows_in_dip) {}
   ~DropShadowSource() override {}
 
   // gfx::ImageSkiaSource overrides:
@@ -382,8 +380,8 @@
     const ImageSkiaRep& image_rep = source_.GetRepresentation(scale);
 
     ShadowValues shadows_in_pixel;
-    for (size_t i = 0; i < shaodws_in_dip_.size(); ++i)
-      shadows_in_pixel.push_back(shaodws_in_dip_[i].Scale(scale));
+    for (size_t i = 0; i < shadows_in_dip_.size(); ++i)
+      shadows_in_pixel.push_back(shadows_in_dip_[i].Scale(scale));
 
     const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow(
         image_rep.sk_bitmap(),
@@ -393,12 +391,61 @@
 
  private:
   const ImageSkia source_;
-  const ShadowValues shaodws_in_dip_;
+  const ShadowValues shadows_in_dip_;
 
   DISALLOW_COPY_AND_ASSIGN(DropShadowSource);
 };
 
-// An image source that is 1dp wide, suitable for tiling horizontally.
+class ShadowNineboxSource : public CanvasImageSource {
+ public:
+  ShadowNineboxSource(const std::vector<ShadowValue>& shadows,
+                      float corner_radius)
+      : CanvasImageSource(CalculateSize(shadows, corner_radius), false),
+        shadows_(shadows),
+        corner_radius_(corner_radius) {
+    DCHECK(!shadows.empty());
+  }
+  ~ShadowNineboxSource() override {}
+
+  // CanvasImageSource overrides:
+  void Draw(Canvas* canvas) override {
+    SkPaint paint;
+    paint.setLooper(CreateShadowDrawLooperCorrectBlur(shadows_));
+    Insets insets = -ShadowValue::GetMargin(shadows_);
+    gfx::Rect bounds(size());
+    bounds.Inset(insets);
+    SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectToSkRect(bounds),
+                                         corner_radius_, corner_radius_);
+
+    // Clip out the center so it's not painted with the shadow.
+    canvas->sk_canvas()->clipRRect(r_rect, kDifference_SkClipOp, true);
+    // Clipping alone is not enough --- due to anti aliasing there will still be
+    // some of the fill color in the rounded corners. We must make the fill
+    // color transparent.
+    paint.setColor(SK_ColorTRANSPARENT);
+    canvas->sk_canvas()->drawRRect(r_rect, paint);
+  }
+
+ private:
+  static Size CalculateSize(const std::vector<ShadowValue>& shadows,
+                            float corner_radius) {
+    // The "content" area (the middle tile in the 3x3 grid) is a single pixel.
+    gfx::Rect bounds(0, 0, 1, 1);
+    // We need enough space to render the full range of blur.
+    bounds.Inset(-ShadowValue::GetBlurRegion(shadows));
+    // We also need space for the full roundrect corner rounding.
+    bounds.Inset(-gfx::Insets(corner_radius));
+    return bounds.size();
+  }
+
+  const std::vector<ShadowValue> shadows_;
+
+  const float corner_radius_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShadowNineboxSource);
+};
+
+// An image source that is 1px wide, suitable for tiling horizontally.
 class HorizontalShadowSource : public CanvasImageSource {
  public:
   HorizontalShadowSource(const std::vector<ShadowValue>& shadows,
@@ -592,11 +639,17 @@
 }
 
 // static
+ImageSkia ImageSkiaOperations::CreateShadowNinebox(const ShadowValues& shadows,
+                                                   float corner_radius) {
+  auto source = new ShadowNineboxSource(shadows, corner_radius);
+  return ImageSkia(source, source->size());
+}
+
+// static
 ImageSkia ImageSkiaOperations::CreateHorizontalShadow(
     const std::vector<ShadowValue>& shadows,
     bool fades_down) {
-  HorizontalShadowSource* source =
-      new HorizontalShadowSource(shadows, fades_down);
+  auto source = new HorizontalShadowSource(shadows, fades_down);
   return ImageSkia(source, source->size());
 }
 
diff --git a/ui/gfx/image/image_skia_operations.h b/ui/gfx/image/image_skia_operations.h
index f3829a2..1d7d36c 100644
--- a/ui/gfx/image/image_skia_operations.h
+++ b/ui/gfx/image/image_skia_operations.h
@@ -6,6 +6,9 @@
 #define UI_GFX_IMAGE_IMAGE_SKIA_OPERATIONS_H_
 
 #include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkDrawLooper.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkRRect.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/shadow_value.h"
@@ -87,10 +90,16 @@
   static ImageSkia CreateImageWithDropShadow(const ImageSkia& source,
                                              const ShadowValues& shadows);
 
+  // Creates an image with the given shadows painted around a round rect with
+  // the given corner radius. The image will be just large enough to paint the
+  // shadows appropriately with a 1px square region reserved for "content".
+  static ImageSkia CreateShadowNinebox(const ShadowValues& shadows,
+                                       float corner_radius);
+
   // Creates an image that is 1dp wide, suitable for tiling horizontally to
   // create a drop shadow effect. The purpose of tiling a static image is to
   // avoid repeatedly asking Skia to draw a shadow.
-  static gfx::ImageSkia CreateHorizontalShadow(
+  static ImageSkia CreateHorizontalShadow(
       const std::vector<ShadowValue>& shadows,
       bool fades_down);
 
diff --git a/ui/gfx/interpolated_transform.cc b/ui/gfx/interpolated_transform.cc
index f6561d1..f86c1f82 100644
--- a/ui/gfx/interpolated_transform.cc
+++ b/ui/gfx/interpolated_transform.cc
@@ -337,7 +337,7 @@
 
   pre_transform->SetChild(xform);
   xform->SetChild(post_transform.release());
-  transform_.reset(pre_transform.release());
+  transform_ = std::move(pre_transform);
 }
 
 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
diff --git a/ui/gfx/shadow_value.cc b/ui/gfx/shadow_value.cc
index 2599fdb..a8ab3f8 100644
--- a/ui/gfx/shadow_value.cc
+++ b/ui/gfx/shadow_value.cc
@@ -14,19 +14,43 @@
 
 namespace gfx {
 
-ShadowValue::ShadowValue()
-    : blur_(0),
-      color_(0) {
+namespace {
+
+Insets GetInsets(const ShadowValues& shadows, bool include_inner_blur) {
+  int left = 0;
+  int top = 0;
+  int right = 0;
+  int bottom = 0;
+
+  for (size_t i = 0; i < shadows.size(); ++i) {
+    const ShadowValue& shadow = shadows[i];
+
+    double blur = shadow.blur();
+    if (!include_inner_blur)
+      blur /= 2;
+    // Add 0.5 to round up to the next integer.
+    int blur_length = static_cast<int>(blur + 0.5);
+
+    left = std::max(left, blur_length - shadow.x());
+    top = std::max(top, blur_length - shadow.y());
+    right = std::max(right, blur_length + shadow.x());
+    bottom = std::max(bottom, blur_length + shadow.y());
+  }
+
+  return Insets(top, left, bottom, right);
 }
 
+}  // namespace
+
+ShadowValue::ShadowValue() : blur_(0), color_(0) {}
+
 ShadowValue::ShadowValue(const gfx::Vector2d& offset,
                          double blur,
                          SkColor color)
     : offset_(offset), blur_(blur), color_(color) {
 }
 
-ShadowValue::~ShadowValue() {
-}
+ShadowValue::~ShadowValue() {}
 
 ShadowValue ShadowValue::Scale(float scale) const {
   gfx::Vector2d scaled_offset =
@@ -47,24 +71,13 @@
 
 // static
 Insets ShadowValue::GetMargin(const ShadowValues& shadows) {
-  int left = 0;
-  int top = 0;
-  int right = 0;
-  int bottom = 0;
+  gfx::Insets margins = GetInsets(shadows, false);
+  return -margins;
+}
 
-  for (size_t i = 0; i < shadows.size(); ++i) {
-    const ShadowValue& shadow = shadows[i];
-
-    // Add 0.5 to round up to the next integer.
-    int blur = static_cast<int>(shadow.blur() / 2 + 0.5);
-
-    left = std::max(left, blur - shadow.x());
-    top = std::max(top, blur - shadow.y());
-    right = std::max(right, blur + shadow.x());
-    bottom = std::max(bottom, blur + shadow.y());
-  }
-
-  return Insets(-top, -left, -bottom, -right);
+// static
+Insets ShadowValue::GetBlurRegion(const ShadowValues& shadows) {
+  return GetInsets(shadows, true);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/shadow_value.h b/ui/gfx/shadow_value.h
index c0623f3..bf5d98d 100644
--- a/ui/gfx/shadow_value.h
+++ b/ui/gfx/shadow_value.h
@@ -41,6 +41,13 @@
   // are negative because shadow margins are outside a boundary.
   static Insets GetMargin(const ShadowValues& shadows);
 
+  // Gets the area inside a rectangle that would be affected by shadow blur.
+  // This is similar to the margin except it's positive (the blur region is
+  // inside a hypothetical rectangle) and it accounts for the blur both inside
+  // and outside the bounding box. The region inside the "blur region" would be
+  // a uniform color.
+  static Insets GetBlurRegion(const ShadowValues& shadows);
+
  private:
   gfx::Vector2d offset_;
 
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 5050c891..219a9e6 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -154,6 +154,8 @@
 
   if (use_egl) {
     sources += [
+      "angle_platform_impl.cc",
+      "angle_platform_impl.h",
       "egl_util.cc",
       "egl_util.h",
       "gl_bindings_autogen_egl.cc",
@@ -219,8 +221,6 @@
   }
   if (is_win) {
     sources += [
-      "angle_platform_impl.cc",
-      "angle_platform_impl.h",
       "gl_bindings_autogen_wgl.cc",
       "gl_bindings_autogen_wgl.h",
       "gl_context_wgl.cc",
@@ -411,7 +411,6 @@
     testonly = true
     data_deps = [
       ":gl_unittests",
-      "//tools/xdisplaycheck",
     ]
   }
 
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc
index b9dbd15a..772cf06 100644
--- a/ui/gl/egl_api_unittest.cc
+++ b/ui/gl/egl_api_unittest.cc
@@ -25,6 +25,7 @@
     g_driver_egl.fn.eglGetCurrentDisplayFn = &FakeGetCurrentDisplay;
     g_driver_egl.fn.eglGetDisplayFn = &FakeGetDisplay;
     g_driver_egl.fn.eglGetErrorFn = &FakeGetError;
+    g_driver_egl.fn.eglGetProcAddressFn = &FakeGetProcAddress;
   }
 
   void TearDown() override {
@@ -82,6 +83,11 @@
     return EGL_SUCCESS;
   }
 
+  static __eglMustCastToProperFunctionPointerType GL_BINDING_CALL
+  FakeGetProcAddress(const char* procname) {
+    return nullptr;
+  }
+
   std::pair<const char*, const char*> GetExtensions() {
     return std::make_pair(
         api_->eglQueryStringFn(EGL_NO_DISPLAY, EGL_EXTENSIONS),
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 54ffb7dd..56032ca9 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -21,6 +22,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_context_egl.h"
@@ -134,6 +136,10 @@
 bool g_egl_surface_orientation_supported = false;
 bool g_use_direct_composition = false;
 
+base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
+    LAZY_INSTANCE_INITIALIZER;
+ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
+
 EGLDisplay GetPlatformANGLEDisplay(EGLNativeDisplayType native_display,
                                    EGLenum platform_type,
                                    bool warpDevice) {
@@ -533,7 +539,11 @@
 }
 
 // static
-void GLSurfaceEGL::ResetForTesting() {
+void GLSurfaceEGL::ShutdownOneOff() {
+  if (g_angle_platform_shutdown) {
+    g_angle_platform_shutdown();
+  }
+
   if (g_display != EGL_NO_DISPLAY)
     eglTerminate(g_display);
   g_display = EGL_NO_DISPLAY;
@@ -607,6 +617,17 @@
 
   g_native_display = native_display;
 
+  // Init ANGLE platform here, before we call GetPlatformDisplay().
+  ANGLEPlatformInitializeFunc angle_platform_init =
+      reinterpret_cast<ANGLEPlatformInitializeFunc>(
+          eglGetProcAddress("ANGLEPlatformInitialize"));
+  if (angle_platform_init) {
+    angle_platform_init(&g_angle_platform_impl.Get());
+
+    g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
+        eglGetProcAddress("ANGLEPlatformShutdown"));
+  }
+
   // If EGL_EXT_client_extensions not supported this call to eglQueryString
   // will return NULL.
   const char* client_extensions =
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 1a0e7055..dc000eba 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -78,7 +78,7 @@
   GLSurface::Format GetFormat() override;
 
   static bool InitializeOneOff(EGLNativeDisplayType native_display);
-  static void ResetForTesting();
+  static void ShutdownOneOff();
   static EGLDisplay GetHardwareDisplay();
   static EGLDisplay InitializeDisplay(EGLNativeDisplayType native_display);
   static EGLNativeDisplayType GetNativeDisplay();
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index d528ad4..f338cc0 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -97,6 +97,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 624e43b..213ef8b 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -92,6 +92,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   if (HasGLOzone()) {
     GetGLOzone()->ShutdownGL();
     return;
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index 5b0fc28..89a3586 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "base/path_service.h"
@@ -19,8 +18,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/windows_version.h"
-// TODO(jmadill): Apply to all platforms eventually
-#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_features.h"
@@ -38,12 +35,6 @@
 
 const wchar_t kD3DCompiler[] = L"D3DCompiler_47.dll";
 
-// TODO(jmadill): Apply to all platforms eventually
-base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
-    LAZY_INSTANCE_INITIALIZER;
-
-ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
-
 bool LoadD3DXLibrary(const base::FilePath& module_path,
                      const base::FilePath::StringType& name) {
   base::NativeLibrary library =
@@ -152,22 +143,6 @@
   }
 #endif
 
-  if (!using_swift_shader) {
-    // Init ANGLE platform here, before we call GetPlatformDisplay().
-    // TODO(jmadill): Apply to all platforms eventually
-    ANGLEPlatformInitializeFunc angle_platform_init =
-        reinterpret_cast<ANGLEPlatformInitializeFunc>(
-            base::GetFunctionPointerFromNativeLibrary(
-                gles_library, "ANGLEPlatformInitialize"));
-    if (angle_platform_init) {
-      angle_platform_init(&g_angle_platform_impl.Get());
-
-      g_angle_platform_shutdown = reinterpret_cast<ANGLEPlatformShutdownFunc>(
-          base::GetFunctionPointerFromNativeLibrary(gles_library,
-                                                    "ANGLEPlatformShutdown"));
-    }
-  }
-
   GLGetProcAddressProc get_proc_address =
       reinterpret_cast<GLGetProcAddressProc>(
           base::GetFunctionPointerFromNativeLibrary(egl_library,
@@ -320,11 +295,7 @@
 }
 
 void ShutdownGLPlatform() {
-  // TODO(jmadill): Apply to all platforms eventually
-  if (g_angle_platform_shutdown) {
-    g_angle_platform_shutdown();
-  }
-
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsOSMESA();
diff --git a/ui/gl/init/gl_initializer_x11.cc b/ui/gl/init/gl_initializer_x11.cc
index a41e5ce..27fca039 100644
--- a/ui/gl/init/gl_initializer_x11.cc
+++ b/ui/gl/init/gl_initializer_x11.cc
@@ -186,6 +186,7 @@
 }
 
 void ShutdownGLPlatform() {
+  GLSurfaceEGL::ShutdownOneOff();
   ClearBindingsEGL();
   ClearBindingsGL();
   ClearBindingsGLX();
diff --git a/ui/keyboard/keyboard_ui.cc b/ui/keyboard/keyboard_ui.cc
index 816933bf..896add64 100644
--- a/ui/keyboard/keyboard_ui.cc
+++ b/ui/keyboard/keyboard_ui.cc
@@ -31,12 +31,8 @@
 void KeyboardUI::EnsureCaretInWorkArea() {
   if (GetInputMethod()->GetTextInputClient()) {
     aura::Window* keyboard_window = GetKeyboardWindow();
-    aura::Window* root_window = keyboard_window->GetRootWindow();
-    gfx::Rect available_bounds = root_window->bounds();
-    gfx::Rect keyboard_bounds = keyboard_window->bounds();
-    available_bounds.set_height(available_bounds.height() -
-        keyboard_bounds.height());
-    GetInputMethod()->GetTextInputClient()->EnsureCaretInRect(available_bounds);
+    GetInputMethod()->GetTextInputClient()->EnsureCaretNotInRect(
+        keyboard_window->GetBoundsInScreen());
   }
 }
 
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index ea3cf13..69e04c40 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -16,7 +16,7 @@
 
   defines = [ "MESSAGE_CENTER_IMPLEMENTATION" ]
 
-  if (enable_notifications && !is_android) {
+  if (!is_ios && !is_android) {
     deps += [
       "//base:i18n",
       "//base/third_party/dynamic_annotations",
@@ -98,6 +98,8 @@
         "views/bounded_label.cc",
         "views/bounded_label.h",
         "views/constants.h",
+        "views/custom_notification_content_view_delegate.cc",
+        "views/custom_notification_content_view_delegate.h",
         "views/custom_notification_view.cc",
         "views/custom_notification_view.h",
         "views/desktop_popup_alignment_delegate.cc",
@@ -167,7 +169,7 @@
 static_library("test_support") {
   testonly = true
 
-  if (enable_notifications && !is_android) {
+  if (!is_ios && !is_android) {
     sources = [
       "fake_message_center.cc",
       "fake_message_center.h",
@@ -218,7 +220,7 @@
     "//ui/resources:ui_test_pak_data",
   ]
 
-  if (enable_notifications && !is_android) {
+  if (!is_ios && !is_android) {
     sources += [
       "cocoa/notification_controller_unittest.mm",
       "cocoa/popup_collection_unittest.mm",
@@ -260,5 +262,5 @@
         "//ui/message_center/mojo:test_interfaces",
       ]
     }
-  }  # enable_notifications && !is_android
+  }  # !is_ios && !is_android
 }
diff --git a/ui/message_center/mojo/notification.mojom b/ui/message_center/mojo/notification.mojom
index ce67c17..a9651b3 100644
--- a/ui/message_center/mojo/notification.mojom
+++ b/ui/message_center/mojo/notification.mojom
@@ -4,7 +4,7 @@
 
 module message_center.mojom;
 
-import "mojo/common/common_custom_types.mojom";
+import "mojo/common/string16.mojom";
 import "skia/public/interfaces/bitmap.mojom";
 import "url/mojo/url.mojom";
 
diff --git a/ui/message_center/notification_delegate.h b/ui/message_center/notification_delegate.h
index a493c18e..b9432fb1 100644
--- a/ui/message_center/notification_delegate.h
+++ b/ui/message_center/notification_delegate.h
@@ -15,9 +15,7 @@
 #include "ui/message_center/message_center_export.h"
 
 #if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX)
-namespace views {
-class View;
-}
+#include "ui/message_center/views/custom_notification_content_view_delegate.h"
 #endif
 
 namespace message_center {
@@ -59,7 +57,7 @@
 #if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX)
   // To be called to construct the contents view of a popup for notifications
   // whose type is NOTIFICATION_TYPE_CUSTOM.
-  virtual std::unique_ptr<views::View> CreateCustomContent();
+  virtual std::unique_ptr<CustomContent> CreateCustomContent();
 #endif
 
   // Indicates whether this notification should be displayed when there is
diff --git a/ui/message_center/notification_delegate_views.cc b/ui/message_center/notification_delegate_views.cc
index 6f993d2c..1d12a30 100644
--- a/ui/message_center/notification_delegate_views.cc
+++ b/ui/message_center/notification_delegate_views.cc
@@ -8,8 +8,8 @@
 
 namespace message_center {
 
-std::unique_ptr<views::View> NotificationDelegate::CreateCustomContent() {
-  return std::unique_ptr<views::View>();
+std::unique_ptr<CustomContent> NotificationDelegate::CreateCustomContent() {
+  return nullptr;
 }
 
 }  // namespace message_center
diff --git a/ui/message_center/views/custom_notification_content_view_delegate.cc b/ui/message_center/views/custom_notification_content_view_delegate.cc
new file mode 100644
index 0000000..a3ee7cd
--- /dev/null
+++ b/ui/message_center/views/custom_notification_content_view_delegate.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/message_center/views/custom_notification_content_view_delegate.h"
+
+#include "ui/views/view.h"
+
+namespace message_center {
+
+CustomContent::CustomContent(
+    std::unique_ptr<views::View> view,
+    std::unique_ptr<CustomNotificationContentViewDelegate> delegate)
+    : view(std::move(view)), delegate(std::move(delegate)) {}
+
+CustomContent::~CustomContent() {}
+
+}  // namespace message_center
diff --git a/ui/message_center/views/custom_notification_content_view_delegate.h b/ui/message_center/views/custom_notification_content_view_delegate.h
new file mode 100644
index 0000000..fbbcc68
--- /dev/null
+++ b/ui/message_center/views/custom_notification_content_view_delegate.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_CUSTOM_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_
+#define UI_MESSAGE_CENTER_CUSTOM_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "ui/message_center/message_center_export.h"
+
+namespace views {
+class View;
+}  // namespace views
+
+namespace message_center {
+
+// Delegate for a view that is hosted by CustomNotificationView.
+class MESSAGE_CENTER_EXPORT CustomNotificationContentViewDelegate {
+ public:
+  virtual bool IsCloseButtonFocused() const = 0;
+  virtual void RequestFocusOnCloseButton() = 0;
+  virtual bool IsPinned() const = 0;
+};
+
+// The struct to hold a view and CustomNotificationContentViewDelegate
+// associating with the view.
+struct MESSAGE_CENTER_EXPORT CustomContent {
+ public:
+  CustomContent(
+      std::unique_ptr<views::View> view,
+      std::unique_ptr<CustomNotificationContentViewDelegate> delegate);
+  ~CustomContent();
+
+  std::unique_ptr<views::View> view;
+  std::unique_ptr<CustomNotificationContentViewDelegate> delegate;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CustomContent);
+};
+
+}  // namespace message_center
+
+#endif  // UI_MESSAGE_CENTER_CUSTOM_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_
diff --git a/ui/message_center/views/custom_notification_view.cc b/ui/message_center/views/custom_notification_view.cc
index b3528679..b94e722 100644
--- a/ui/message_center/views/custom_notification_view.cc
+++ b/ui/message_center/views/custom_notification_view.cc
@@ -20,18 +20,21 @@
     : MessageView(controller, notification) {
   DCHECK_EQ(NOTIFICATION_TYPE_CUSTOM, notification.type());
 
-  contents_view_ = notification.delegate()->CreateCustomContent().release();
+  auto custom_content = notification.delegate()->CreateCustomContent();
+
+  contents_view_ = custom_content->view.release();
   DCHECK(contents_view_);
   AddChildView(contents_view_);
 
+  contents_view_delegate_ = std::move(custom_content->delegate);
+  DCHECK(contents_view_delegate_);
+
   if (contents_view_->background()) {
     background_view()->background()->SetNativeControlColor(
         contents_view_->background()->get_color());
   }
 
   AddChildView(small_image());
-
-  CreateOrUpdateCloseButtonView(notification);
 }
 
 CustomNotificationView::~CustomNotificationView() {}
@@ -44,6 +47,23 @@
   MessageView::SetDrawBackgroundAsActive(active);
 }
 
+bool CustomNotificationView::IsCloseButtonFocused() const {
+  if (!contents_view_delegate_)
+    return false;
+  return contents_view_delegate_->IsCloseButtonFocused();
+}
+
+void CustomNotificationView::RequestFocusOnCloseButton() {
+  if (contents_view_delegate_)
+    contents_view_delegate_->RequestFocusOnCloseButton();
+}
+
+bool CustomNotificationView::IsPinned() const {
+  if (!contents_view_delegate_)
+    return false;
+  return contents_view_delegate_->IsPinned();
+}
+
 gfx::Size CustomNotificationView::GetPreferredSize() const {
   const gfx::Insets insets = GetInsets();
   const int contents_width = kNotificationWidth - insets.width();
diff --git a/ui/message_center/views/custom_notification_view.h b/ui/message_center/views/custom_notification_view.h
index 8a5ab62..6a7a2a2 100644
--- a/ui/message_center/views/custom_notification_view.h
+++ b/ui/message_center/views/custom_notification_view.h
@@ -21,6 +21,9 @@
 
   // Overidden from MessageView:
   void SetDrawBackgroundAsActive(bool active) override;
+  bool IsCloseButtonFocused() const override;
+  void RequestFocusOnCloseButton() override;
+  bool IsPinned() const override;
 
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
@@ -31,6 +34,8 @@
 
   // The view for the custom content. Owned by view hierarchy.
   views::View* contents_view_ = nullptr;
+  std::unique_ptr<CustomNotificationContentViewDelegate>
+      contents_view_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(CustomNotificationView);
 };
diff --git a/ui/message_center/views/custom_notification_view_unittest.cc b/ui/message_center/views/custom_notification_view_unittest.cc
index a5d87f41..e26c4b2 100644
--- a/ui/message_center/views/custom_notification_view_unittest.cc
+++ b/ui/message_center/views/custom_notification_view_unittest.cc
@@ -66,13 +66,22 @@
   DISALLOW_COPY_AND_ASSIGN(TestCustomView);
 };
 
+class TestContentViewDelegate : public CustomNotificationContentViewDelegate {
+ public:
+  bool IsCloseButtonFocused() const override { return false; }
+  void RequestFocusOnCloseButton() override {}
+  bool IsPinned() const override { return false; }
+};
+
 class TestNotificationDelegate : public NotificationDelegate {
  public:
   TestNotificationDelegate() {}
 
   // NotificateDelegate
-  std::unique_ptr<views::View> CreateCustomContent() override {
-    return base::WrapUnique(new TestCustomView);
+  std::unique_ptr<CustomContent> CreateCustomContent() override {
+    return base::MakeUnique<CustomContent>(
+        base::MakeUnique<TestCustomView>(),
+        base::MakeUnique<TestContentViewDelegate>());
   }
 
  private:
@@ -187,9 +196,6 @@
     widget()->OnKeyEvent(&event);
   }
 
-  views::ImageButton* close_button() {
-    return notification_view_->close_button();
-  }
   TestMessageCenterController* controller() { return &controller_; }
   Notification* notification() { return notification_.get(); }
   TestCustomView* custom_view() {
@@ -210,15 +216,6 @@
   EXPECT_EQ(kBackgroundColor, GetBackgroundColor());
 }
 
-TEST_F(CustomNotificationViewTest, ClickCloseButton) {
-  widget()->Show();
-
-  gfx::Point cursor_location(1, 1);
-  views::View::ConvertPointToWidget(close_button(), &cursor_location);
-  PerformClick(cursor_location);
-  EXPECT_TRUE(controller()->IsRemoved(notification()->id()));
-}
-
 TEST_F(CustomNotificationViewTest, Events) {
   widget()->Show();
   custom_view()->RequestFocus();
diff --git a/ui/message_center/views/message_list_view.cc b/ui/message_center/views/message_list_view.cc
index a5f2a70..7439364 100644
--- a/ui/message_center/views/message_list_view.cc
+++ b/ui/message_center/views/message_list_view.cc
@@ -102,13 +102,19 @@
 
 void MessageListView::RemoveNotification(MessageView* view) {
   DCHECK_EQ(view->parent(), this);
+
+
   if (GetContentsBounds().IsEmpty()) {
     delete view;
   } else {
+    if (adding_views_.find(view) != adding_views_.end())
+      adding_views_.erase(view);
+    if (animator_.IsAnimating(view))
+      animator_.StopAnimatingView(view);
+
     if (view->layer()) {
       deleting_views_.insert(view);
     } else {
-      animator_.StopAnimatingView(view);
       delete view;
     }
     DoUpdateIfPossible();
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 4031d01a..df23b09e 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -14,7 +14,6 @@
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_style.h"
 #include "ui/message_center/views/message_center_controller.h"
-#include "ui/message_center/views/padded_button.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/background.h"
@@ -27,9 +26,6 @@
 
 namespace {
 
-constexpr int kCloseIconTopPadding = 5;
-constexpr int kCloseIconRightPadding = 5;
-
 constexpr int kShadowOffset = 1;
 constexpr int kShadowBlur = 4;
 
@@ -92,7 +88,6 @@
 void MessageView::UpdateWithNotification(const Notification& notification) {
   small_image_view_->SetImage(notification.small_image().AsImageSkia());
   display_source_ = notification.display_source();
-  CreateOrUpdateCloseButtonView(notification);
   accessible_name_ = CreateAccessibleName(notification);
 }
 
@@ -110,24 +105,6 @@
                        message_center::kShadowColor))));
 }
 
-bool MessageView::IsCloseButtonFocused() {
-  if (!close_button_)
-    return false;
-
-  views::FocusManager* focus_manager = GetFocusManager();
-  return focus_manager &&
-         focus_manager->GetFocusedView() == close_button_.get();
-}
-
-void MessageView::RequestFocusOnCloseButton() {
-  if (close_button_)
-    close_button_->RequestFocus();
-}
-
-bool MessageView::IsPinned() {
-  return !close_button_;
-}
-
 void MessageView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ui::AX_ROLE_BUTTON;
   node_data->SetName(accessible_name_);
@@ -169,7 +146,6 @@
 
 void MessageView::OnPaint(gfx::Canvas* canvas) {
   DCHECK_EQ(this, small_image_view_->parent());
-  DCHECK_EQ(this, close_button_->parent());
   SlideOutView::OnPaint(canvas);
   views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
 }
@@ -192,16 +168,6 @@
   // Background.
   background_view_->SetBoundsRect(content_bounds);
 
-  // Close button.
-  if (close_button_) {
-    gfx::Rect content_bounds = GetContentsBounds();
-    gfx::Size close_size(close_button_->GetPreferredSize());
-    gfx::Rect close_rect(content_bounds.right() - close_size.width(),
-                         content_bounds.y(), close_size.width(),
-                         close_size.height());
-    close_button_->SetBoundsRect(close_rect);
-  }
-
   gfx::Size small_image_size(small_image_view_->GetPreferredSize());
   gfx::Rect small_image_rect(small_image_size);
   small_image_rect.set_origin(gfx::Point(
@@ -246,13 +212,6 @@
   event->SetHandled();
 }
 
-void MessageView::ButtonPressed(views::Button* sender,
-                                const ui::Event& event) {
-  if (close_button_ && sender == close_button_.get()) {
-    controller()->RemoveNotification(notification_id(), true);  // By user.
-  }
-}
-
 void MessageView::OnSlideOut() {
   controller_->RemoveNotification(notification_id_, true);  // By user.
 }
@@ -264,27 +223,4 @@
   SchedulePaint();
 }
 
-void MessageView::CreateOrUpdateCloseButtonView(
-    const Notification& notification) {
-  set_slide_out_enabled(!notification.pinned());
-
-  if (!notification.pinned() && !close_button_) {
-    PaddedButton* close = new PaddedButton(this);
-    close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding);
-    close->SetNormalImage(IDR_NOTIFICATION_CLOSE);
-    close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER);
-    close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED);
-    close->set_animate_on_state_change(false);
-    close->SetAccessibleName(l10n_util::GetStringUTF16(
-        IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
-    close->SetTooltipText(l10n_util::GetStringUTF16(
-        IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP));
-    close->set_owned_by_client();
-    AddChildView(close);
-    close_button_.reset(close);
-  } else if (notification.pinned() && close_button_) {
-    close_button_.reset();
-  }
-}
-
 }  // namespace message_center
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index 9eeadbd..862f27f1 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -16,11 +16,9 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/notification.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/controls/slide_out_view.h"
 
 namespace views {
-class ImageButton;
 class ImageView;
 class Painter;
 class ScrollView;
@@ -38,8 +36,7 @@
 
 // An base class for a notification entry. Contains background and other
 // elements shared by derived notification views.
-class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView,
-                                          public views::ButtonListener {
+class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView {
  public:
   MessageView(MessageCenterController* controller,
               const Notification& notification);
@@ -54,9 +51,9 @@
   // Creates a shadow around the notification.
   void CreateShadowBorder();
 
-  bool IsCloseButtonFocused();
-  void RequestFocusOnCloseButton();
-  bool IsPinned();
+  virtual bool IsCloseButtonFocused() const = 0;
+  virtual void RequestFocusOnCloseButton() = 0;
+  virtual bool IsPinned() const = 0;
 
   // Overridden from views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
@@ -71,9 +68,6 @@
   // Overridden from ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
-  // Overridden from ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; }
   std::string notification_id() { return notification_id_; }
   NotifierId notifier_id() { return notifier_id_; }
@@ -98,7 +92,6 @@
 
   views::View* background_view() { return background_view_; }
   views::ImageView* small_image() { return small_image_view_.get(); }
-  views::ImageButton* close_button() { return close_button_.get(); }
   views::ScrollView* scroller() { return scroller_; }
   MessageCenterController* controller() { return controller_; }
 
@@ -108,7 +101,6 @@
   NotifierId notifier_id_;
   views::View* background_view_ = nullptr;  // Owned by views hierarchy.
   std::unique_ptr<views::ImageView> small_image_view_;
-  std::unique_ptr<views::ImageButton> close_button_;
   views::ScrollView* scroller_ = nullptr;
 
   base::string16 accessible_name_;
diff --git a/ui/message_center/views/notification_view.cc b/ui/message_center/views/notification_view.cc
index cee41f0..746f082 100644
--- a/ui/message_center/views/notification_view.cc
+++ b/ui/message_center/views/notification_view.cc
@@ -52,6 +52,9 @@
 // Dimensions.
 const int kProgressBarBottomPadding = 0;
 
+constexpr int kCloseIconTopPadding = 5;
+constexpr int kCloseIconRightPadding = 5;
+
 // static
 std::unique_ptr<views::Border> MakeEmptyBorder(int top,
                                                int left,
@@ -294,6 +297,16 @@
                                      settings_size.height());
   }
 
+  // Close button.
+  if (close_button_) {
+    gfx::Rect content_bounds = GetContentsBounds();
+    gfx::Size close_size(close_button_->GetPreferredSize());
+    gfx::Rect close_rect(content_bounds.right() - close_size.width(),
+                         content_bounds.y(), close_size.width(),
+                         close_size.height());
+    close_button_->SetBoundsRect(close_rect);
+  }
+
   bottom_view_->SetBounds(insets.left(), bottom_y,
                           content_width, bottom_height);
 }
@@ -321,6 +334,7 @@
   MessageView::UpdateWithNotification(notification);
 
   CreateOrUpdateViews(notification);
+  CreateOrUpdateCloseButtonView(notification);
   Layout();
   SchedulePaint();
 }
@@ -332,6 +346,13 @@
   // TODO(dewittj): Remove this hack.
   std::string id(notification_id());
 
+  if (close_button_ && sender == close_button_.get()) {
+    // Warning: This causes the NotificationView itself to be deleted, so don't
+    // do anything afterwards.
+    controller()->RemoveNotification(id, true /* by_user */);
+    return;
+  }
+
   if (sender == settings_button_view_) {
     controller()->ClickOnSettingsButton(id);
     return;
@@ -344,11 +365,24 @@
       return;
     }
   }
+}
 
-  // Let the superclass handle everything else.
-  // Warning: This may cause the NotificationView itself to be deleted,
-  // so don't do anything afterwards.
-  MessageView::ButtonPressed(sender, event);
+bool NotificationView::IsCloseButtonFocused() const {
+  if (!close_button_)
+    return false;
+
+  const views::FocusManager* focus_manager = GetFocusManager();
+  return focus_manager &&
+         focus_manager->GetFocusedView() == close_button_.get();
+}
+
+void NotificationView::RequestFocusOnCloseButton() {
+  if (close_button_)
+    close_button_->RequestFocus();
+}
+
+bool NotificationView::IsPinned() const {
+  return !close_button_;
 }
 
 void NotificationView::CreateOrUpdateTitleView(
@@ -630,6 +664,29 @@
   }
 }
 
+void NotificationView::CreateOrUpdateCloseButtonView(
+    const Notification& notification) {
+  set_slide_out_enabled(!notification.pinned());
+
+  if (!notification.pinned() && !close_button_) {
+    PaddedButton* close = new PaddedButton(this);
+    close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding);
+    close->SetNormalImage(IDR_NOTIFICATION_CLOSE);
+    close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER);
+    close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED);
+    close->set_animate_on_state_change(false);
+    close->SetAccessibleName(l10n_util::GetStringUTF16(
+        IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
+    close->SetTooltipText(l10n_util::GetStringUTF16(
+        IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP));
+    close->set_owned_by_client();
+    AddChildView(close);
+    close_button_.reset(close);
+  } else if (notification.pinned() && close_button_) {
+    close_button_.reset();
+  }
+}
+
 int NotificationView::GetMessageLineLimit(int title_lines, int width) const {
   // Image notifications require that the image must be kept flush against
   // their icons, but we can allow more text if no image.
diff --git a/ui/message_center/views/notification_view.h b/ui/message_center/views/notification_view.h
index 6d54895..6ce79c28 100644
--- a/ui/message_center/views/notification_view.h
+++ b/ui/message_center/views/notification_view.h
@@ -11,6 +11,8 @@
 #include "base/macros.h"
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/views/message_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/image_button.h"
 #include "ui/views/view_targeter_delegate.h"
 
 namespace views {
@@ -29,6 +31,7 @@
 // returned by the Create() factory method below.
 class MESSAGE_CENTER_EXPORT NotificationView
     : public MessageView,
+      public views::ButtonListener,
       public views::ViewTargeterDelegate {
  public:
   NotificationView(MessageCenterController* controller,
@@ -46,6 +49,12 @@
   // Overridden from MessageView:
   void UpdateWithNotification(const Notification& notification) override;
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+  bool IsCloseButtonFocused() const override;
+  void RequestFocusOnCloseButton() override;
+  bool IsPinned() const override;
+
+ protected:
+  views::ImageButton* close_button() { return close_button_.get(); }
 
  private:
   FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, CreateOrUpdateTest);
@@ -75,6 +84,7 @@
   void CreateOrUpdateIconView(const Notification& notification);
   void CreateOrUpdateImageView(const Notification& notification);
   void CreateOrUpdateActionButtonViews(const Notification& notification);
+  void CreateOrUpdateCloseButtonView(const Notification& notification);
 
   int GetMessageLineLimit(int title_lines, int width) const;
   int GetMessageHeight(int width, int limit) const;
@@ -102,6 +112,7 @@
   views::ProgressBar* progress_bar_view_ = nullptr;
   std::vector<NotificationButton*> action_buttons_;
   std::vector<views::View*> separators_;
+  std::unique_ptr<views::ImageButton> close_button_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationView);
 };
diff --git a/ui/native_theme/native_theme_android.h b/ui/native_theme/native_theme_android.h
index e504252..52b42e5 100644
--- a/ui/native_theme/native_theme_android.h
+++ b/ui/native_theme/native_theme_android.h
@@ -13,8 +13,6 @@
 // Android implementation of native theme support.
 class NativeThemeAndroid : public NativeThemeBase {
  public:
-  static NativeThemeAndroid* instance();
-
   // NativeThemeBase:
   gfx::Size GetPartSize(Part part,
                         State state,
@@ -22,6 +20,9 @@
   SkColor GetSystemColor(ColorId color_id) const override;
 
  protected:
+  friend class NativeTheme;
+  static NativeThemeAndroid* instance();
+
   // NativeThemeBase:
   void AdjustCheckboxRadioRectForPadding(SkRect* rect) const override;
 
diff --git a/ui/native_theme/native_theme_aura.cc b/ui/native_theme/native_theme_aura.cc
index 1d67f96..4b6742f 100644
--- a/ui/native_theme/native_theme_aura.cc
+++ b/ui/native_theme/native_theme_aura.cc
@@ -46,6 +46,9 @@
 
 }  // namespace
 
+////////////////////////////////////////////////////////////////////////////////
+// NativeTheme:
+
 // static
 NativeTheme* NativeTheme::GetInstanceForWeb() {
   return NativeThemeAura::web_instance();
@@ -58,18 +61,8 @@
 }
 #endif
 
-// static
-NativeThemeAura* NativeThemeAura::instance() {
-  CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme, (false));
-  return &s_native_theme;
-}
-
-// static
-NativeThemeAura* NativeThemeAura::web_instance() {
-  CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme_for_web,
-                         (IsOverlayScrollbarEnabled()));
-  return &s_native_theme_for_web;
-}
+////////////////////////////////////////////////////////////////////////////////
+// NativeThemeAura:
 
 NativeThemeAura::NativeThemeAura(bool use_overlay_scrollbars)
     : use_overlay_scrollbars_(use_overlay_scrollbars) {
@@ -92,6 +85,19 @@
 
 NativeThemeAura::~NativeThemeAura() {}
 
+// static
+NativeThemeAura* NativeThemeAura::instance() {
+  CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme, (false));
+  return &s_native_theme;
+}
+
+// static
+NativeThemeAura* NativeThemeAura::web_instance() {
+  CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme_for_web,
+                         (IsOverlayScrollbarEnabled()));
+  return &s_native_theme_for_web;
+}
+
 // This implementation returns hardcoded colors.
 SkColor NativeThemeAura::GetSystemColor(ColorId color_id) const {
   return GetAuraColor(color_id, this);
diff --git a/ui/native_theme/native_theme_aura.h b/ui/native_theme/native_theme_aura.h
index b8235684..9b65246 100644
--- a/ui/native_theme/native_theme_aura.h
+++ b/ui/native_theme/native_theme_aura.h
@@ -12,14 +12,16 @@
 
 // Aura implementation of native theme support.
 class NATIVE_THEME_EXPORT NativeThemeAura : public NativeThemeBase {
- public:
-  static NativeThemeAura* instance();
-  static NativeThemeAura* web_instance();
-
  protected:
+  friend class NativeTheme;
+  friend class NativeThemeAuraTest;
+
   explicit NativeThemeAura(bool use_overlay_scrollbars);
   ~NativeThemeAura() override;
 
+  static NativeThemeAura* instance();
+  static NativeThemeAura* web_instance();
+
   // Overridden from NativeThemeBase:
   SkColor GetSystemColor(ColorId color_id) const override;
   void PaintMenuPopupBackground(
diff --git a/ui/native_theme/native_theme_aura_unittest.cc b/ui/native_theme/native_theme_aura_unittest.cc
index db3c5c1..0a8abeb 100644
--- a/ui/native_theme/native_theme_aura_unittest.cc
+++ b/ui/native_theme/native_theme_aura_unittest.cc
@@ -11,6 +11,7 @@
 
 namespace ui {
 namespace {
+
 void VerifyPoint(SkPoint a, SkPoint b) {
   EXPECT_EQ(a.x(), b.x());
   EXPECT_EQ(a.y(), b.y());
@@ -22,7 +23,8 @@
   VerifyPoint(p1, actualPath.getPoint(1));
   VerifyPoint(p2, actualPath.getPoint(2));
 }
-}
+
+}  // namespace
 
 class NativeThemeAuraTest : public testing::Test {
  protected:
diff --git a/ui/native_theme/native_theme_mac.h b/ui/native_theme/native_theme_mac.h
index ec48119..8f870ea 100644
--- a/ui/native_theme/native_theme_mac.h
+++ b/ui/native_theme/native_theme_mac.h
@@ -27,8 +27,6 @@
     COUNT
   };
 
-  static NativeThemeMac* instance();
-
   // Adjusts an SkColor based on the current system control tint. For example,
   // if the current tint is "graphite", this function maps the provided value to
   // an appropriate gray.
@@ -68,6 +66,10 @@
                                         bool round_right,
                                         bool focus);
 
+ protected:
+  friend class NativeTheme;
+  static NativeThemeMac* instance();
+
  private:
   NativeThemeMac();
   ~NativeThemeMac() override;
diff --git a/ui/native_theme/native_theme_mac_unittest.cc b/ui/native_theme/native_theme_mac_unittest.cc
index 5d5f748f..37641d89 100644
--- a/ui/native_theme/native_theme_mac_unittest.cc
+++ b/ui/native_theme/native_theme_mac_unittest.cc
@@ -2,26 +2,31 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/base/material_design/material_design_controller.h"
-#include "ui/native_theme/native_theme_mac.h"
+#include "ui/native_theme/native_theme.h"
 
 #include "base/mac/mac_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/material_design/material_design_controller.h"
 
 namespace ui {
 
 class NativeThemeMacTest : public testing::Test {
  public:
+  NativeThemeMacTest() {}
+
   static void SetUpTestCase() {
     MaterialDesignController::Initialize();
   }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeThemeMacTest);
 };
 
 // Test to ensure any system colors that are looked up by name exist on all Mac
 // platforms Chrome supports, and that their colorspace and component count is
 // sane.
 TEST_F(NativeThemeMacTest, SystemColorsExist) {
-  NativeTheme* native_theme = NativeThemeMac::instance();
+  NativeTheme* native_theme = NativeTheme::GetInstanceForNativeUi();
   ASSERT_TRUE(native_theme);
   for (int i = 0; i < NativeTheme::kColorId_NumColors; ++i) {
     // While 0 is a valid color, no system color should be fully transparent.
@@ -36,7 +41,7 @@
 // Spot-check some system colours that can't be changed through System
 // Preferences.
 TEST_F(NativeThemeMacTest, SystemColorSpotChecks) {
-  NativeTheme* native_theme = NativeThemeMac::instance();
+  NativeTheme* native_theme = NativeTheme::GetInstanceForNativeUi();
   const SkColor kWindowColorCatsMavericks = SkColorSetARGB(255, 232, 232, 232);
   const SkColor kWindowColorYosemite = SkColorSetARGB(255, 236, 236, 236);
   SkColor dialogColor =
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 52acd96..e811036b 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -154,20 +154,14 @@
   return NativeThemeWin::instance();
 }
 
-bool NativeThemeWin::IsThemingActive() const {
-  return is_theme_active_ && is_theme_active_();
+// static
+bool NativeThemeWin::IsUsingHighContrastTheme() {
+  return instance()->IsUsingHighContrastThemeInternal();
 }
 
-bool NativeThemeWin::IsUsingHighContrastTheme() const {
-  if (is_using_high_contrast_valid_)
-    return is_using_high_contrast_;
-  HIGHCONTRAST result;
-  result.cbSize = sizeof(HIGHCONTRAST);
-  is_using_high_contrast_ =
-      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
-      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
-  is_using_high_contrast_valid_ = true;
-  return is_using_high_contrast_;
+// static
+void NativeThemeWin::CloseHandles() {
+  instance()->CloseHandlesInternal();
 }
 
 HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
@@ -209,18 +203,6 @@
     set_theme_properties_(0);
 }
 
-void NativeThemeWin::CloseHandles() const {
-  if (!close_theme_)
-    return;
-
-  for (int i = 0; i < LAST; ++i) {
-    if (theme_handles_[i]) {
-      close_theme_(theme_handles_[i]);
-      theme_handles_[i] = NULL;
-    }
-  }
-}
-
 bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
   return !theme_dll_ || !GetThemeHandle(name);
 }
@@ -319,7 +301,6 @@
       open_theme_(NULL),
       close_theme_(NULL),
       set_theme_properties_(NULL),
-      is_theme_active_(NULL),
       get_theme_int_(NULL),
       theme_dll_(LoadLibrary(L"uxtheme.dll")),
       color_change_listener_(this),
@@ -342,8 +323,6 @@
         GetProcAddress(theme_dll_, "CloseThemeData"));
     set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
         GetProcAddress(theme_dll_, "SetThemeAppProperties"));
-    is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
-        GetProcAddress(theme_dll_, "IsThemeActive"));
     get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
         GetProcAddress(theme_dll_, "GetThemeInt"));
   }
@@ -362,6 +341,30 @@
   }
 }
 
+bool NativeThemeWin::IsUsingHighContrastThemeInternal() {
+  if (is_using_high_contrast_valid_)
+    return is_using_high_contrast_;
+  HIGHCONTRAST result;
+  result.cbSize = sizeof(HIGHCONTRAST);
+  is_using_high_contrast_ =
+      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
+      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
+  is_using_high_contrast_valid_ = true;
+  return is_using_high_contrast_;
+}
+
+void NativeThemeWin::CloseHandlesInternal() {
+  if (!close_theme_)
+    return;
+
+  for (int i = 0; i < LAST; ++i) {
+    if (theme_handles_[i]) {
+      close_theme_(theme_handles_[i]);
+      theme_handles_[i] = nullptr;
+    }
+  }
+}
+
 void NativeThemeWin::OnSysColorChange() {
   UpdateSystemColors();
   is_using_high_contrast_valid_ = false;
diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h
index fa2c82ce..84a20c4b 100644
--- a/ui/native_theme/native_theme_win.h
+++ b/ui/native_theme/native_theme_win.h
@@ -52,10 +52,12 @@
     LAST
   };
 
-  bool IsThemingActive() const;
-
   // Returns true if a high contrast theme is being used.
-  bool IsUsingHighContrastTheme() const;
+  static bool IsUsingHighContrastTheme();
+
+  // Closes cached theme handles so we can unload the DLL or update our UI
+  // for a theme change.
+  static void CloseHandles();
 
   HRESULT GetThemeColor(ThemeName theme,
                         int part_id,
@@ -84,16 +86,9 @@
   // consistent visual results.
   void DisableTheming() const;
 
-  // Closes cached theme handles so we can unload the DLL or update our UI
-  // for a theme change.
-  void CloseHandles() const;
-
   // Returns true if classic theme is in use.
   bool IsClassicTheme(ThemeName name) const;
 
-  // Gets our singleton instance.
-  static NativeThemeWin* instance();
-
   HRESULT PaintTextField(HDC hdc,
                          int part_id,
                          int state_id,
@@ -115,10 +110,17 @@
   SkColor GetSystemColor(ColorId color_id) const override;
 
  protected:
+  friend class NativeTheme;
+  // Gets our singleton instance.
+  static NativeThemeWin* instance();
+
   NativeThemeWin();
   ~NativeThemeWin() override;
 
  private:
+  bool IsUsingHighContrastThemeInternal();
+  void CloseHandlesInternal();
+
   // gfx::SysColorChangeListener implementation:
   void OnSysColorChange() override;
 
@@ -353,7 +355,6 @@
   OpenThemeDataPtr open_theme_;
   CloseThemeDataPtr close_theme_;
   SetThemeAppPropertiesPtr set_theme_properties_;
-  IsThemeActivePtr is_theme_active_;
   GetThemeIntPtr get_theme_int_;
 
   // Handle to uxtheme.dll.
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc
index d63b305..7abf320 100644
--- a/ui/ozone/common/gl_ozone_egl.cc
+++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -41,7 +41,7 @@
 }
 
 void GLOzoneEGL::ShutdownGL() {
-  gl::GLSurfaceEGL::ResetForTesting();
+  gl::GLSurfaceEGL::ShutdownOneOff();
   gl::ClearBindingsGL();
   gl::ClearBindingsEGL();
 }
diff --git a/ui/ozone/platform/cast/ozone_platform_cast.cc b/ui/ozone/platform/cast/ozone_platform_cast.cc
index 50e2215..ac9e8c92 100644
--- a/ui/ozone/platform/cast/ozone_platform_cast.cc
+++ b/ui/ozone/platform/cast/ozone_platform_cast.cc
@@ -43,6 +43,15 @@
 
   // OzonePlatform implementation:
   SurfaceFactoryOzone* GetSurfaceFactoryOzone() override {
+    if (!surface_factory_) {
+      // The browser process is trying to create a surface (only the GPU
+      // process should try to do that) for falling back on software
+      // rendering because it failed to create a channel to the GPU process.
+      // Returning a null pointer will crash via a null-pointer dereference,
+      // so instead perform a controlled crash.
+      LOG(FATAL) << "Unable to create a GPU graphics context, and Cast doesn't "
+                    "support software compositing.";
+    }
     return surface_factory_.get();
   }
   OverlayManagerOzone* GetOverlayManager() override {
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
index a031b80..f30e950 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
@@ -19,7 +19,11 @@
     : drm_(drm), bo_(bo) {
   if (flags & GBM_BO_USE_SCANOUT) {
     DCHECK(bo_);
-    framebuffer_pixel_format_ = format;
+    // The framebuffer format might be different than the format:
+    // drm supports 24 bit color depth and formats with alpha will
+    // be converted to one without it.
+    framebuffer_pixel_format_ =
+        GetFourCCFormatForFramebuffer(GetBufferFormatFromFourCCFormat(format));
 
     // TODO(dcastagna): Add multi-planar support.
     uint32_t handles[4] = {0};
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 83fff46..b787dbc 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -878,7 +878,7 @@
 }
 
 void BridgedNativeWidget::OnSystemControlTintChanged() {
-  ui::NativeThemeMac::instance()->NotifyObservers();
+  ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers();
 }
 
 void BridgedNativeWidget::OnBackingPropertiesChanged() {
diff --git a/ui/views/controls/menu/menu_config_win.cc b/ui/views/controls/menu/menu_config_win.cc
index cde8d9f..34d29d7 100644
--- a/ui/views/controls/menu/menu_config_win.cc
+++ b/ui/views/controls/menu/menu_config_win.cc
@@ -16,7 +16,6 @@
 #include "ui/native_theme/native_theme_win.h"
 
 using ui::NativeTheme;
-using ui::NativeThemeWin;
 
 namespace views {
 
@@ -32,7 +31,7 @@
     font_list = gfx::FontList(gfx::Font(new_font.get()));
   }
   NativeTheme::ExtraParams extra;
-  gfx::Size arrow_size = NativeThemeWin::instance()->GetPartSize(
+  gfx::Size arrow_size = NativeTheme::GetInstanceForNativeUi()->GetPartSize(
       NativeTheme::kMenuPopupArrow, NativeTheme::kNormal, extra);
   if (!arrow_size.IsEmpty()) {
     arrow_width = arrow_size.width();
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index d26ff66..95dd4f9 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -426,8 +426,7 @@
     state_.hot_button = hot_button_;
     hot_button_ = nullptr;
     // We're already showing, push the current state.
-    menu_stack_.push_back(
-        std::make_pair(state_, make_linked_ptr(pressed_lock_.release())));
+    menu_stack_.push_back(std::make_pair(state_, std::move(pressed_lock_)));
 
     // The context menu should be owned by the same parent.
     DCHECK_EQ(owner_, parent);
@@ -856,7 +855,7 @@
     // removed while a menu is up.
     if (details.child == hot_button_) {
       hot_button_ = nullptr;
-      for (auto nested_state : menu_stack_) {
+      for (auto&& nested_state : menu_stack_) {
         State& state = nested_state.first;
         if (details.child == state.hot_button)
           state.hot_button = nullptr;
@@ -1563,9 +1562,8 @@
 }
 
 void MenuController::CloseAllNestedMenus() {
-  for (std::list<NestedState>::iterator i = menu_stack_.begin();
-       i != menu_stack_.end(); ++i) {
-    State& state = i->first;
+  for (auto&& nested_menu : menu_stack_) {
+    State& state = nested_menu.first;
     MenuItemView* last_item = state.item;
     for (MenuItemView* item = last_item; item;
          item = item->GetParentMenuItem()) {
@@ -2614,7 +2612,7 @@
   }
 #endif
 
-  linked_ptr<MenuButton::PressedLock> nested_pressed_lock;
+  std::unique_ptr<MenuButton::PressedLock> nested_pressed_lock;
   bool nested_menu = !menu_stack_.empty();
   if (nested_menu) {
     DCHECK(!menu_stack_.empty());
@@ -2623,7 +2621,7 @@
     state_ = menu_stack_.back().first;
     pending_state_ = menu_stack_.back().first;
     hot_button_ = state_.hot_button;
-    nested_pressed_lock = menu_stack_.back().second;
+    nested_pressed_lock = std::move(menu_stack_.back().second);
     menu_stack_.pop_back();
     // Even though the menus are nested, there may not be nested delegates.
     if (delegate_stack_.size() > 1) {
@@ -2664,7 +2662,7 @@
 
   // Reset our pressed lock and hot-tracked state to the previous state's, if
   // they were active. The lock handles the case if the button was destroyed.
-  pressed_lock_.reset(nested_pressed_lock.release());
+  pressed_lock_ = std::move(nested_pressed_lock);
   if (hot_button_)
     hot_button_->SetHotTracked(true);
 
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index edf195a..30b9889 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -14,7 +14,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/linked_ptr.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "ui/events/event.h"
@@ -593,7 +592,8 @@
   // If not empty, it means we're nested. When Run is invoked from within
   // Run, the current state (state_) is pushed onto menu_stack_. This allows
   // MenuController to restore the state when the nested run returns.
-  typedef std::pair<State, linked_ptr<MenuButton::PressedLock> > NestedState;
+  using NestedState =
+      std::pair<State, std::unique_ptr<MenuButton::PressedLock>>;
   std::list<NestedState> menu_stack_;
 
   // When Run is invoked during an active Run, it may be called from a separate
diff --git a/ui/views/controls/menu/menu_separator.h b/ui/views/controls/menu/menu_separator.h
index 1287dfc..7e013ff 100644
--- a/ui/views/controls/menu/menu_separator.h
+++ b/ui/views/controls/menu/menu_separator.h
@@ -24,8 +24,6 @@
   // Gets the bounds where the separator should be painted.
   gfx::Rect GetPaintBounds();
 
-  void OnPaintAura(gfx::Canvas* canvas);
-
   // The type of the separator.
   const ui::MenuSeparatorType type_;
 
diff --git a/ui/views/controls/menu/menu_separator_views.cc b/ui/views/controls/menu/menu_separator_views.cc
index 6baae92..86f794a 100644
--- a/ui/views/controls/menu/menu_separator_views.cc
+++ b/ui/views/controls/menu/menu_separator_views.cc
@@ -14,7 +14,9 @@
 
 #if !defined(OS_WIN)
 void MenuSeparator::OnPaint(gfx::Canvas* canvas) {
-  OnPaintAura(canvas);
+  canvas->FillRect(GetPaintBounds(),
+                   GetNativeTheme()->GetSystemColor(
+                       ui::NativeTheme::kColorId_MenuSeparatorColor));
 }
 #endif
 
@@ -62,10 +64,4 @@
   return paint_rect;
 }
 
-void MenuSeparator::OnPaintAura(gfx::Canvas* canvas) {
-  canvas->FillRect(GetPaintBounds(),
-                   GetNativeTheme()->GetSystemColor(
-                       ui::NativeTheme::kColorId_MenuSeparatorColor));
-}
-
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_separator_win.cc b/ui/views/controls/menu/menu_separator_win.cc
index 2ecabdb..4fbe20aa 100644
--- a/ui/views/controls/menu/menu_separator_win.cc
+++ b/ui/views/controls/menu/menu_separator_win.cc
@@ -19,11 +19,6 @@
 
 void MenuSeparator::OnPaint(gfx::Canvas* canvas) {
   ui::NativeTheme* native_theme = GetNativeTheme();
-  if (native_theme == ui::NativeThemeAura::instance()) {
-    OnPaintAura(canvas);
-    return;
-  }
-
   gfx::Rect separator_bounds = GetPaintBounds();
 
   // Hack to get the separator to display correctly on Windows where we may
diff --git a/ui/views/controls/prefix_selector.cc b/ui/views/controls/prefix_selector.cc
index b21ff34..f9fae71 100644
--- a/ui/views/controls/prefix_selector.cc
+++ b/ui/views/controls/prefix_selector.cc
@@ -137,8 +137,7 @@
 void PrefixSelector::ExtendSelectionAndDelete(size_t before, size_t after) {
 }
 
-void PrefixSelector::EnsureCaretInRect(const gfx::Rect& rect) {
-}
+void PrefixSelector::EnsureCaretNotInRect(const gfx::Rect& rect) {}
 
 bool PrefixSelector::IsTextEditCommandEnabled(
     ui::TextEditCommand command) const {
diff --git a/ui/views/controls/prefix_selector.h b/ui/views/controls/prefix_selector.h
index 76f6141..090c2a2 100644
--- a/ui/views/controls/prefix_selector.h
+++ b/ui/views/controls/prefix_selector.h
@@ -55,7 +55,7 @@
   bool ChangeTextDirectionAndLayoutAlignment(
       base::i18n::TextDirection direction) override;
   void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretInRect(const gfx::Rect& rect) override;
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override;
 
   bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
   void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
diff --git a/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/ui/views/controls/scrollbar/overlay_scroll_bar.cc
index a340361..358a097 100644
--- a/ui/views/controls/scrollbar/overlay_scroll_bar.cc
+++ b/ui/views/controls/scrollbar/overlay_scroll_bar.cc
@@ -136,8 +136,28 @@
   return kThumbThickness;
 }
 
+void OverlayScrollBar::Layout() {
+  gfx::Rect thumb_bounds = GetTrackBounds();
+  BaseScrollBarThumb* thumb = GetThumb();
+  if (IsHorizontal()) {
+    thumb_bounds.set_x(thumb->x());
+    thumb_bounds.set_width(thumb->width());
+  } else {
+    thumb_bounds.set_y(thumb->y());
+    thumb_bounds.set_height(thumb->height());
+  }
+  thumb->SetBoundsRect(thumb_bounds);
+}
+
+bool OverlayScrollBar::CanAcceptEvent(const ui::Event& event) {
+  return layer()->opacity() > 0 && BaseScrollBar::CanAcceptEvent(event);
+}
+
 void OverlayScrollBar::OnMouseEntered(const ui::MouseEvent& event) {
-  Show();
+  // Note that events are only accepted when the scrollbar is already visible
+  // (due to a change in the scroll value). Don't let the scrollbar vanish from
+  // under the mouse pointer.
+  hide_timer_.Stop();
 }
 
 void OverlayScrollBar::OnMouseExited(const ui::MouseEvent& event) {
@@ -163,17 +183,4 @@
       base::Bind(&OverlayScrollBar::Hide, base::Unretained(this)));
 }
 
-void OverlayScrollBar::Layout() {
-  gfx::Rect thumb_bounds = GetTrackBounds();
-  BaseScrollBarThumb* thumb = GetThumb();
-  if (IsHorizontal()) {
-    thumb_bounds.set_x(thumb->x());
-    thumb_bounds.set_width(thumb->width());
-  } else {
-    thumb_bounds.set_y(thumb->y());
-    thumb_bounds.set_height(thumb->height());
-  }
-  thumb->SetBoundsRect(thumb_bounds);
-}
-
 }  // namespace views
diff --git a/ui/views/controls/scrollbar/overlay_scroll_bar.h b/ui/views/controls/scrollbar/overlay_scroll_bar.h
index 98c7561d..4771abb 100644
--- a/ui/views/controls/scrollbar/overlay_scroll_bar.h
+++ b/ui/views/controls/scrollbar/overlay_scroll_bar.h
@@ -27,6 +27,7 @@
 
   // View overrides:
   void Layout() override;
+  bool CanAcceptEvent(const ui::Event& event) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
 
diff --git a/ui/views/controls/styled_label_unittest.cc b/ui/views/controls/styled_label_unittest.cc
index c12a08d3..38881026 100644
--- a/ui/views/controls/styled_label_unittest.cc
+++ b/ui/views/controls/styled_label_unittest.cc
@@ -257,13 +257,7 @@
       static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
 }
 
-// Fails on Mac, but only on 10.10. See http://crbug.com/622983.
-#if defined(OS_MACOSX)
-#define MAYBE_StyledRangeBold DISABLED_StyledRangeBold
-#else
-#define MAYBE_StyledRangeBold StyledRangeBold
-#endif
-TEST_F(StyledLabelTest, MAYBE_StyledRangeBold) {
+TEST_F(StyledLabelTest, StyledRangeBold) {
   const std::string bold_text(
       "This is a block of text whose style will be set to BOLD in the test");
   const std::string text(" normal text");
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 87de224..2bb3e82 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -1440,7 +1440,7 @@
     DeleteRange(range);
 }
 
-void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {}
+void Textfield::EnsureCaretNotInRect(const gfx::Rect& rect) {}
 
 bool Textfield::IsTextEditCommandEnabled(ui::TextEditCommand command) const {
   base::string16 result;
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index cb99ddf3..bcc3bbc 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -320,7 +320,7 @@
   bool ChangeTextDirectionAndLayoutAlignment(
       base::i18n::TextDirection direction) override;
   void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretInRect(const gfx::Rect& rect) override;
+  void EnsureCaretNotInRect(const gfx::Rect& rect) override;
   bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
   void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
 
diff --git a/ui/views/mus/mus_client.cc b/ui/views/mus/mus_client.cc
index 7d6a534..93b437e 100644
--- a/ui/views/mus/mus_client.cc
+++ b/ui/views/mus/mus_client.cc
@@ -89,12 +89,12 @@
       init_params.mus_properties;
 
   // Widget::InitParams::Type matches ui::mojom::WindowType.
-  properties[ui::mojom::WindowManager::kWindowType_Property] =
+  properties[ui::mojom::WindowManager::kWindowType_InitProperty] =
       mojo::ConvertTo<std::vector<uint8_t>>(
           static_cast<int32_t>(init_params.type));
 
   if (!init_params.bounds.IsEmpty()) {
-    properties[ui::mojom::WindowManager::kInitialBounds_Property] =
+    properties[ui::mojom::WindowManager::kBounds_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(init_params.bounds);
   }
 
diff --git a/ui/views/mus/native_widget_mus.cc b/ui/views/mus/native_widget_mus.cc
index bb65f74..7e92c5b6 100644
--- a/ui/views/mus/native_widget_mus.cc
+++ b/ui/views/mus/native_widget_mus.cc
@@ -39,7 +39,7 @@
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/path.h"
-#include "ui/native_theme/native_theme_aura.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #include "ui/views/corewm/tooltip.h"
 #include "ui/views/corewm/tooltip_aura.h"
@@ -659,7 +659,7 @@
   properties->insert(init_params.mus_properties.begin(),
                      init_params.mus_properties.end());
   if (!init_params.bounds.IsEmpty()) {
-    (*properties)[ui::mojom::WindowManager::kUserSetBounds_Property] =
+    (*properties)[ui::mojom::WindowManager::kBounds_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(init_params.bounds);
   }
   if (!init_params.name.empty()) {
@@ -670,9 +670,9 @@
       mojo::ConvertTo<std::vector<uint8_t>>(
           static_cast<PrimitiveType>(init_params.keep_on_top));
 
-  (*properties)[ui::mojom::WindowManager::kWindowType_Property] =
-      mojo::ConvertTo<std::vector<uint8_t>>(static_cast<PrimitiveType>(
-          mojo::ConvertTo<ui::mojom::WindowType>(init_params.type)));
+  (*properties)[ui::mojom::WindowManager::kWindowType_InitProperty] =
+      mojo::ConvertTo<std::vector<uint8_t>>(
+          static_cast<int32_t>(init_params.type));
 
   if (!Widget::RequiresNonClientView(init_params.type))
     return;
@@ -1314,10 +1314,6 @@
   NOTIMPLEMENTED();
 }
 
-ui::NativeTheme* NativeWidgetMus::GetNativeTheme() const {
-  return ui::NativeThemeAura::instance();
-}
-
 bool NativeWidgetMus::IsTranslucentWindowOpacitySupported() const {
   NOTIMPLEMENTED();
   return true;
diff --git a/ui/views/mus/native_widget_mus.h b/ui/views/mus/native_widget_mus.h
index 27eb1e6..30d70e6 100644
--- a/ui/views/mus/native_widget_mus.h
+++ b/ui/views/mus/native_widget_mus.h
@@ -191,7 +191,6 @@
   void SetVisibilityAnimationDuration(const base::TimeDelta& duration) override;
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
-  ui::NativeTheme* GetNativeTheme() const override;
   bool IsTranslucentWindowOpacitySupported() const override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
diff --git a/ui/views/mus/pointer_watcher_event_router2.h b/ui/views/mus/pointer_watcher_event_router2.h
index b7577a3..5a963b8e 100644
--- a/ui/views/mus/pointer_watcher_event_router2.h
+++ b/ui/views/mus/pointer_watcher_event_router2.h
@@ -14,9 +14,6 @@
 
 namespace aura {
 class WindowTreeClient;
-namespace client {
-class CaptureClient;
-}
 }
 
 namespace ui {
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index 79c8d9d..946c208 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -131,6 +131,11 @@
   return kRelatedButtonHSpacing;
 }
 
+gfx::Insets ViewsDelegate::GetDialogFrameViewInsets() {
+  return gfx::Insets(kPanelVertMargin, kButtonHEdgeMarginNew, 0,
+                     kButtonHEdgeMarginNew);
+}
+
 ViewsDelegate::ViewsDelegate()
     : views_tsc_factory_(new ViewsTouchEditingControllerFactory) {
   DCHECK(!views_delegate);
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 5eb80096..0e0da34 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -202,6 +202,9 @@
   // dialog layout.
   virtual int GetDialogRelatedButtonHorizontalSpacing();
 
+  // Returns the insets that should be applied around a dialog's frame view.
+  virtual gfx::Insets GetDialogFrameViewInsets();
+
  protected:
   ViewsDelegate();
 
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 e50108f..b3ec277 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -26,7 +26,6 @@
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size_conversions.h"
-#include "ui/native_theme/native_theme.h"
 #include "ui/views/corewm/tooltip.h"
 #include "ui/views/corewm/tooltip_controller.h"
 #include "ui/views/drag_utils.h"
@@ -940,10 +939,6 @@
   wm::SetWindowVisibilityAnimationTransition(content_window_, wm_transition);
 }
 
-ui::NativeTheme* DesktopNativeWidgetAura::GetNativeTheme() const {
-  return DesktopWindowTreeHost::GetNativeTheme(content_window_);
-}
-
 bool DesktopNativeWidgetAura::IsTranslucentWindowOpacitySupported() const {
   return content_window_ &&
       desktop_window_tree_host_->IsTranslucentWindowOpacitySupported();
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 c12b6ff..81f575a 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -177,7 +177,6 @@
   void SetVisibilityAnimationDuration(const base::TimeDelta& duration) override;
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
-  ui::NativeTheme* GetNativeTheme() const override;
   bool IsTranslucentWindowOpacitySupported() const override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
index 6528cf5..48461a5 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
@@ -26,10 +26,6 @@
 class Rect;
 }
 
-namespace ui {
-class NativeTheme;
-}
-
 namespace views {
 namespace corewm {
 class Tooltip;
@@ -50,9 +46,6 @@
       internal::NativeWidgetDelegate* native_widget_delegate,
       DesktopNativeWidgetAura* desktop_native_widget_aura);
 
-  // Return the NativeTheme to use for |window|. WARNING: |window| may be NULL.
-  static ui::NativeTheme* GetNativeTheme(aura::Window* window);
-
   // Sets up resources needed before the WindowEventDispatcher has been created.
   virtual void Init(aura::Window* content_window,
                     const Widget::InitParams& params) = 0;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_chromeos.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_chromeos.cc
index 5d3ea30..41b1987 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_chromeos.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_chromeos.cc
@@ -4,8 +4,6 @@
 
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 
-#include "ui/native_theme/native_theme_aura.h"
-
 namespace views {
 
 // static
@@ -18,9 +16,4 @@
   return nullptr;
 }
 
-// static
-ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
-  return ui::NativeThemeAura::instance();
-}
-
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc
index e67af41e..1a78a9a 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_ozone.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "ui/aura/window_tree_host.h"
-#include "ui/native_theme/native_theme_aura.h"
 #include "ui/views/widget/desktop_aura/desktop_factory_ozone.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
 
@@ -18,9 +17,4 @@
                                          desktop_native_widget_aura);
 }
 
-// static
-ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
-  return ui::NativeThemeAura::instance();
-}
-
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 2c3c254..3f9726b6 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -24,8 +24,6 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/path_win.h"
-#include "ui/native_theme/native_theme_aura.h"
-#include "ui/native_theme/native_theme_win.h"
 #include "ui/views/corewm/tooltip_win.h"
 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h"
 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
@@ -105,21 +103,6 @@
   return host ? host->window()->GetProperty(kContentWindowForRootWindow) : NULL;
 }
 
-// static
-ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
-  // Use NativeThemeWin for windows shown on the desktop, those not on the
-  // desktop come from Ash and get NativeThemeAura.
-  aura::WindowTreeHost* host = window ? window->GetHost() : NULL;
-  if (host) {
-    HWND host_hwnd = host->GetAcceleratedWidget();
-    if (host_hwnd &&
-        DesktopWindowTreeHostWin::GetContentWindowForHWND(host_hwnd)) {
-      return ui::NativeThemeWin::instance();
-    }
-  }
-  return ui::NativeThemeAura::instance();
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation:
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 8c566fa..c2c7d3ae 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -48,8 +48,6 @@
 #include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/path.h"
 #include "ui/gfx/path_x11.h"
-#include "ui/native_theme/native_theme.h"
-#include "ui/native_theme/native_theme_aura.h"
 #include "ui/views/corewm/tooltip_aura.h"
 #include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/views_delegate.h"
@@ -2348,16 +2346,4 @@
                                       desktop_native_widget_aura);
 }
 
-// static
-ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
-  const LinuxUI* linux_ui = LinuxUI::instance();
-  if (linux_ui) {
-    ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(window);
-    if (native_theme)
-      return native_theme;
-  }
-
-  return ui::NativeThemeAura::instance();
-}
-
 }  // namespace views
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 2165bc2..80be94f 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -60,6 +60,7 @@
 #endif
 
 #if defined(USE_X11) && !defined(OS_CHROMEOS)
+#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
 #endif
 
@@ -753,14 +754,6 @@
   wm::SetWindowVisibilityAnimationTransition(window_, wm_transition);
 }
 
-ui::NativeTheme* NativeWidgetAura::GetNativeTheme() const {
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
-  return DesktopWindowTreeHost::GetNativeTheme(window_);
-#else
-  return ui::NativeThemeAura::instance();
-#endif
-}
-
 bool NativeWidgetAura::IsTranslucentWindowOpacitySupported() const {
   return true;
 }
@@ -1080,6 +1073,20 @@
   return false;
 }
 
+const ui::NativeTheme* Widget::GetNativeTheme() const {
+#if defined(USE_X11) && !defined(OS_CHROMEOS)
+  const LinuxUI* linux_ui = LinuxUI::instance();
+  if (linux_ui) {
+    ui::NativeTheme* native_theme =
+        linux_ui->GetNativeTheme(native_widget_->GetNativeWindow());
+    if (native_theme)
+      return native_theme;
+  }
+#endif
+
+  return ui::NativeTheme::GetInstanceForNativeUi();
+}
+
 namespace internal {
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h
index 0f8565e..921c119 100644
--- a/ui/views/widget/native_widget_aura.h
+++ b/ui/views/widget/native_widget_aura.h
@@ -135,7 +135,6 @@
   void SetVisibilityAnimationDuration(const base::TimeDelta& duration) override;
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
-  ui::NativeTheme* GetNativeTheme() const override;
   bool IsTranslucentWindowOpacitySupported() const override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index ff62d5fc..25e52d7 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -127,7 +127,6 @@
   void SetVisibilityAnimationDuration(const base::TimeDelta& duration) override;
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
-  ui::NativeTheme* GetNativeTheme() const override;
   bool IsTranslucentWindowOpacitySupported() const override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index e81bba4..73fad04 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -599,10 +599,6 @@
   NOTIMPLEMENTED();
 }
 
-ui::NativeTheme* NativeWidgetMac::GetNativeTheme() const {
-  return ui::NativeThemeMac::instance();
-}
-
 bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const {
   return false;
 }
@@ -640,6 +636,10 @@
   return false;
 }
 
+const ui::NativeTheme* Widget::GetNativeTheme() const {
+  return ui::NativeTheme::GetInstanceForNativeUi();
+}
+
 namespace internal {
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h
index 7ea2add..dfe37828 100644
--- a/ui/views/widget/native_widget_private.h
+++ b/ui/views/widget/native_widget_private.h
@@ -21,7 +21,6 @@
 
 namespace ui {
 class InputMethod;
-class NativeTheme;
 class OSExchangeData;
 }
 
@@ -223,7 +222,6 @@
       const base::TimeDelta& duration) = 0;
   virtual void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) = 0;
-  virtual ui::NativeTheme* GetNativeTheme() const = 0;
   virtual bool IsTranslucentWindowOpacitySupported() const = 0;
   virtual void OnSizeConstraintsChanged() = 0;
 
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index b14dce1..08578add 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -738,10 +738,6 @@
   return default_theme_provider_.get();
 }
 
-const ui::NativeTheme* Widget::GetNativeTheme() const {
-  return native_widget_->GetNativeTheme();
-}
-
 FocusManager* Widget::GetFocusManager() {
   Widget* toplevel_widget = GetTopLevelWidget();
   return toplevel_widget ? toplevel_widget->focus_manager_.get() : NULL;
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 0880a2b2..451752e 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -3862,8 +3862,20 @@
            widget_type == Widget::InitParams::TYPE_CONTROL))
         continue;
 
+#if defined(OS_MACOSX)
+      // Mac always always has a compositing window manager, but doesn't have
+      // transparent titlebars which is what ShouldWindowContentsBeTransparent()
+      // is currently used for. Asking for transparency should get it. Note that
+      // TestViewsDelegate::use_transparent_windows_ determines the result of
+      // INFER_OPACITY: assume it is false.
+      bool should_be_transparent =
+          opacity == Widget::InitParams::TRANSLUCENT_WINDOW;
+#else
+      bool should_be_transparent = widget.ShouldWindowContentsBeTransparent();
+#endif
+
       EXPECT_EQ(IsNativeWindowTransparent(widget.GetNativeWindow()),
-                widget.ShouldWindowContentsBeTransparent());
+                should_be_transparent);
 
       // When using the Mandoline UI Service, the translucency does not rely on
       // the widget type.
@@ -3899,15 +3911,7 @@
   CheckAllWidgetsForOpacity(Widget::InitParams::OPAQUE_WINDOW);
 }
 
-// Failing on Mac. http://cbrug.com/623421
-#if defined(OS_MACOSX)
-#define MAYBE_Transparency_DesktopWidgetTranslucent \
-    DISABLED_Transparency_DesktopWidgetTranslucent
-#else
-#define MAYBE_Transparency_DesktopWidgetTranslucent \
-    Transparency_DesktopWidgetTranslucent
-#endif
-TEST_F(CompositingWidgetTest, MAYBE_Transparency_DesktopWidgetTranslucent) {
+TEST_F(CompositingWidgetTest, Transparency_DesktopWidgetTranslucent) {
   CheckAllWidgetsForOpacity(Widget::InitParams::TRANSLUCENT_WINDOW);
 }
 
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index d75777d..c9eb444 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -2146,7 +2146,7 @@
 }
 
 void HWNDMessageHandler::OnThemeChanged() {
-  ui::NativeThemeWin::instance()->CloseHandles();
+  ui::NativeThemeWin::CloseHandles();
 }
 
 LRESULT HWNDMessageHandler::OnTouchEvent(UINT message,
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index aa1e002..333adf9 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -17,6 +17,7 @@
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/layout/layout_constants.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/views_delegate.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 #include "ui/views/window/dialog_client_view.h"
@@ -199,10 +200,9 @@
 NonClientFrameView* DialogDelegate::CreateDialogFrameView(
     Widget* widget,
     const gfx::Insets& content_margins) {
-  BubbleFrameView* frame =
-      new BubbleFrameView(gfx::Insets(kPanelVertMargin, kButtonHEdgeMarginNew,
-                                      0, kButtonHEdgeMarginNew),
-                          content_margins);
+  BubbleFrameView* frame = new BubbleFrameView(
+      ViewsDelegate::GetInstance()->GetDialogFrameViewInsets(),
+      content_margins);
   const BubbleBorder::Shadow kShadow = BubbleBorder::SMALL_SHADOW;
   std::unique_ptr<BubbleBorder> border(
       new BubbleBorder(BubbleBorder::FLOAT, kShadow, gfx::kPlaceholderColor));
diff --git a/ui/webui/resources/cr_elements/compiled_resources2.gyp b/ui/webui/resources/cr_elements/compiled_resources2.gyp
index 07c349a..130e9db 100644
--- a/ui/webui/resources/cr_elements/compiled_resources2.gyp
+++ b/ui/webui/resources/cr_elements/compiled_resources2.gyp
@@ -9,6 +9,7 @@
       'dependencies': [
         'cr_action_menu/compiled_resources2.gyp:*',
         'cr_dialog/compiled_resources2.gyp:*',
+        'cr_expand_button/compiled_resources2.gyp:*',
         'cr_profile_avatar_selector/compiled_resources2.gyp:*',
         'cr_slider/compiled_resources2.gyp:*',
         'network/compiled_resources2.gyp:*',
diff --git a/ui/webui/resources/cr_elements/cr_expand_button/compiled_resources2.gyp b/ui/webui/resources/cr_elements/cr_expand_button/compiled_resources2.gyp
new file mode 100644
index 0000000..45d702fe
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_expand_button/compiled_resources2.gyp
@@ -0,0 +1,13 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'targets': [
+    {
+      'target_name': 'cr_expand_button',
+      'dependencies': [
+      ],
+      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+  ],
+}
diff --git a/ui/wm/core/shadow.cc b/ui/wm/core/shadow.cc
index 7885343..8319095 100644
--- a/ui/wm/core/shadow.cc
+++ b/ui/wm/core/shadow.cc
@@ -4,11 +4,14 @@
 
 #include "ui/wm/core/shadow.h"
 
-#include "third_party/skia/include/core/SkBitmap.h"
+#include "base/lazy_instance.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/resources/grit/ui_resources.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/image/image_skia_operations.h"
+
+namespace wm {
 
 namespace {
 
@@ -16,18 +19,6 @@
 // inactive/active shadow.
 const float kInactiveShadowAnimationOpacity = 0.2f;
 
-// Shadow aperture for different styles.
-// Note that this may be greater than interior inset to allow shadows with
-// curved corners that extend inwards beyond a window's borders.
-const int kActiveInteriorAperture = 134;
-const int kInactiveInteriorAperture = 134;
-const int kSmallInteriorAperture = 9;
-
-// Interior inset for different styles.
-const int kActiveInteriorInset = 64;
-const int kInactiveInteriorInset = 64;
-const int kSmallInteriorInset = 4;
-
 // Rounded corners are overdrawn on top of the window's content layer,
 // we need to exclude them from the occlusion area.
 const int kRoundedCornerRadius = 2;
@@ -35,39 +26,50 @@
 // Duration for opacity animation in milliseconds.
 const int kShadowAnimationDurationMs = 100;
 
-int GetShadowApertureForStyle(wm::Shadow::Style style) {
-  switch (style) {
-    case wm::Shadow::STYLE_ACTIVE:
-      return kActiveInteriorAperture;
-    case wm::Shadow::STYLE_INACTIVE:
-      return kInactiveInteriorAperture;
-    case wm::Shadow::STYLE_SMALL:
-      return kSmallInteriorAperture;
-  }
-  return 0;
-}
+struct ShadowDetails {
+  // Description of the shadows.
+  gfx::ShadowValues values;
+  // Cached ninebox image based on |values|.
+  gfx::ImageSkia ninebox_image;
+};
 
-int GetInteriorInsetForStyle(wm::Shadow::Style style) {
-  switch (style) {
-    case wm::Shadow::STYLE_ACTIVE:
-      return kActiveInteriorInset;
-    case wm::Shadow::STYLE_INACTIVE:
-      return kInactiveInteriorInset;
-    case wm::Shadow::STYLE_SMALL:
-      return kSmallInteriorInset;
-  }
-  return 0;
+// Map from elevation to a cached shadow.
+using ShadowDetailsMap = std::map<int, ShadowDetails>;
+base::LazyInstance<ShadowDetailsMap> g_shadow_cache = LAZY_INSTANCE_INITIALIZER;
+
+const ShadowDetails& GetDetailsForElevation(int elevation) {
+  auto iter = g_shadow_cache.Get().find(elevation);
+  if (iter != g_shadow_cache.Get().end())
+    return iter->second;
+
+  auto insertion =
+      g_shadow_cache.Get().insert(std::make_pair(elevation, ShadowDetails()));
+  DCHECK(insertion.second);
+  ShadowDetails* shadow = &insertion.first->second;
+  // To match the CSS notion of blur (spread outside the bounding box) to the
+  // Skia notion of blur (spread outside and inside the bounding box), we have
+  // to double the designer-provided blur values.
+  const int kBlurCorrection = 2;
+  // "Key shadow": y offset is elevation and blur is twice the elevation.
+  shadow->values.emplace_back(gfx::Vector2d(0, elevation),
+                              kBlurCorrection * elevation * 2,
+                              SkColorSetA(SK_ColorBLACK, 0x3d));
+  // "Ambient shadow": no offset and blur matches the elevation.
+  shadow->values.emplace_back(gfx::Vector2d(), kBlurCorrection * elevation,
+                              SkColorSetA(SK_ColorBLACK, 0x1f));
+  // To see what this looks like for elevation 24, try this CSS:
+  //   box-shadow: 0 24px 48px rgba(0, 0, 0, .24),
+  //               0 0 24px rgba(0, 0, 0, .12);
+  shadow->ninebox_image = gfx::ImageSkiaOperations::CreateShadowNinebox(
+      shadow->values, kRoundedCornerRadius);
+  return *shadow;
 }
 
 }  // namespace
 
-namespace wm {
+Shadow::Shadow() {}
 
-Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) {
-}
-
-Shadow::~Shadow() {
-}
+Shadow::~Shadow() {}
 
 void Shadow::Init(Style style) {
   style_ = style;
@@ -149,59 +151,61 @@
 }
 
 void Shadow::UpdateImagesForStyle() {
-  ResourceBundle& res = ResourceBundle::GetSharedInstance();
-  gfx::Image image;
-  switch (style_) {
-    case STYLE_ACTIVE:
-      image = res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE);
-      break;
-    case STYLE_INACTIVE:
-      image = res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE);
-      break;
-    case STYLE_SMALL:
-      image = res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL);
-      break;
-    default:
-      NOTREACHED() << "Unhandled style " << style_;
-      break;
-  }
-
-  shadow_layer_->UpdateNinePatchLayerImage(image.AsImageSkia());
-  image_size_ = image.Size();
-  interior_inset_ = GetInteriorInsetForStyle(style_);
-
-  // Image sizes may have changed.
+  const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle());
+  shadow_layer_->UpdateNinePatchLayerImage(details.ninebox_image);
+  // The ninebox grid is defined in terms of the image size. The shadow blurs in
+  // both inward and outward directions from the edge of the contents, so the
+  // aperture goes further inside the image than the shadow margins (which
+  // represent exterior blur).
+  gfx::Rect aperture(details.ninebox_image.size());
+  gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) +
+                            gfx::Insets(kRoundedCornerRadius);
+  aperture.Inset(blur_region);
+  shadow_layer_->UpdateNinePatchLayerAperture(aperture);
   UpdateLayerBounds();
 }
 
 void Shadow::UpdateLayerBounds() {
-  // Update bounds based on content bounds and interior inset.
+  const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle());
+  // Shadow margins are negative, so this expands outwards from
+  // |content_bounds_|.
+  const gfx::Insets margins = gfx::ShadowValue::GetMargin(details.values);
   gfx::Rect layer_bounds = content_bounds_;
-  layer_bounds.Inset(-interior_inset_, -interior_inset_);
+  layer_bounds.Inset(margins);
   layer()->SetBounds(layer_bounds);
-  shadow_layer_->SetBounds(gfx::Rect(layer_bounds.size()));
+  const gfx::Rect shadow_layer_bounds(layer_bounds.size());
+  shadow_layer_->SetBounds(shadow_layer_bounds);
 
-  // Update the shadow aperture and border for style. Note that border is in
-  // layer space and it cannot exceed the bounds of the layer.
-  int aperture = GetShadowApertureForStyle(style_);
-  int aperture_x = std::min(aperture, layer_bounds.width() / 2);
-  int aperture_y = std::min(aperture, layer_bounds.height() / 2);
-  gfx::Rect aperture_rect(aperture_x, aperture_y,
-                          image_size_.width() - aperture_x * 2,
-                          image_size_.height() - aperture_y * 2);
+  // Occlude the region inside the bounding box. Occlusion uses shadow layer
+  // space. See nine_patch_layer.h for more context on what's going on here.
+  gfx::Rect occlusion_bounds = shadow_layer_bounds;
+  occlusion_bounds.Inset(-margins + gfx::Insets(kRoundedCornerRadius));
+  shadow_layer_->UpdateNinePatchOcclusion(occlusion_bounds);
 
-  shadow_layer_->UpdateNinePatchLayerAperture(aperture_rect);
+  // The border is more or less the same inset as the aperture, but can be no
+  // larger than the shadow layer. When the shadow layer is too small, shrink
+  // the dimensions proportionally.
+  gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) +
+                            gfx::Insets(kRoundedCornerRadius);
+  int border_w = std::min(blur_region.width(), shadow_layer_bounds.width());
+  int border_x = border_w * blur_region.left() / blur_region.width();
+  int border_h = std::min(blur_region.height(), shadow_layer_bounds.height());
+  int border_y = border_h * blur_region.top() / blur_region.height();
   shadow_layer_->UpdateNinePatchLayerBorder(
-      gfx::Rect(aperture_x, aperture_y, aperture_x * 2, aperture_y * 2));
+      gfx::Rect(border_x, border_y, border_w, border_h));
+}
 
-  // The content bounds in the shadow's layer space are offsetted by
-  // |interior_inset_|. The occlusion area also has to be shrunk to allow
-  // rounded corners overdrawing on top of the window's content.
-  gfx::Rect content_bounds(interior_inset_ + kRoundedCornerRadius,
-                           interior_inset_ + kRoundedCornerRadius,
-                           content_bounds_.width() - 2 * kRoundedCornerRadius,
-                           content_bounds_.height() - 2 * kRoundedCornerRadius);
-  shadow_layer_->UpdateNinePatchOcclusion(content_bounds);
+int Shadow::ElevationForStyle() {
+  switch (style_) {
+    case STYLE_ACTIVE:
+      return 24;
+    case STYLE_INACTIVE:
+      return 8;
+    case STYLE_SMALL:
+      return 6;
+  }
+  NOTREACHED();
+  return 0;
 }
 
 }  // namespace wm
diff --git a/ui/wm/core/shadow.h b/ui/wm/core/shadow.h
index 81199f84..e0b7a1db 100644
--- a/ui/wm/core/shadow.h
+++ b/ui/wm/core/shadow.h
@@ -69,8 +69,13 @@
   // |content_bounds_|.
   void UpdateLayerBounds();
 
+  // Returns the "elevation" for |style_|, which dictates the shadow's display
+  // characteristics. The elevation is proportional to the size of the blur and
+  // its offset.
+  int ElevationForStyle();
+
   // The current style, set when the transition animation starts.
-  Style style_;
+  Style style_ = STYLE_ACTIVE;
 
   // The parent layer of the shadow layer. It serves as a container accessible
   // from the outside to control the visibility of the shadow.
@@ -79,16 +84,9 @@
   // The actual shadow layer corresponding to a cc::NinePatchLayer.
   std::unique_ptr<ui::Layer> shadow_layer_;
 
-  // Size of the current shadow image.
-  gfx::Size image_size_;
-
   // Bounds of the content that the shadow encloses.
   gfx::Rect content_bounds_;
 
-  // The interior inset of the shadow images. The content bounds of the image
-  // grid should be set to |content_bounds_| inset by this amount.
-  int interior_inset_;
-
   DISALLOW_COPY_AND_ASSIGN(Shadow);
 };
 
diff --git a/ui/wm/core/shadow_unittest.cc b/ui/wm/core/shadow_unittest.cc
index b376262..7416c39 100644
--- a/ui/wm/core/shadow_unittest.cc
+++ b/ui/wm/core/shadow_unittest.cc
@@ -4,145 +4,42 @@
 
 #include "ui/wm/core/shadow.h"
 
-#include <memory>
-
 #include "base/macros.h"
-#include "base/path_service.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/aura/test/aura_test_base.h"
-#include "ui/aura/test/test_windows.h"
-#include "ui/aura/window.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_paths.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_tree_owner.h"
-#include "ui/resources/grit/ui_resources.h"
 
 namespace wm {
-
 namespace {
 
-const int kSmallBitmapSize = 129;
-const int kLargeBitmapSize = 269;
-
-// Mock for the ResourceBundle::Delegate class.
-class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate {
- public:
-  MockResourceBundleDelegate() : last_resource_id_(0) {
-    SkBitmap bitmap_small, bitmap_large;
-    bitmap_small.allocPixels(
-        SkImageInfo::MakeN32Premul(kSmallBitmapSize, kSmallBitmapSize));
-    bitmap_large.allocPixels(
-        SkImageInfo::MakeN32Premul(kLargeBitmapSize, kLargeBitmapSize));
-    image_small_ = gfx::Image::CreateFrom1xBitmap(bitmap_small);
-    image_large_ = gfx::Image::CreateFrom1xBitmap(bitmap_large);
-  }
-  ~MockResourceBundleDelegate() override {}
-
-  // ResourceBundle::Delegate:
-  base::FilePath GetPathForResourcePack(const base::FilePath& pack_path,
-                                        ui::ScaleFactor scale_factor) override {
-    return base::FilePath();
-  }
-  base::FilePath GetPathForLocalePack(const base::FilePath& pack_path,
-                                      const std::string& locale) override {
-    return base::FilePath();
-  }
-  gfx::Image GetImageNamed(int resource_id) override {
-    last_resource_id_ = resource_id;
-    switch (resource_id) {
-      case IDR_WINDOW_BUBBLE_SHADOW_SMALL:
-        return image_small_;
-      case IDR_AURA_SHADOW_ACTIVE:
-      case IDR_AURA_SHADOW_INACTIVE:
-        return image_large_;
-      default:
-        NOTREACHED();
-        return gfx::Image();
-    }
-  }
-  gfx::Image GetNativeImageNamed(int resource_id) override {
-    return gfx::Image();
-  }
-  base::RefCountedStaticMemory* LoadDataResourceBytes(
-      int resource_id,
-      ui::ScaleFactor scale_factor) override {
-    return NULL;
-  }
-  bool GetRawDataResource(int resource_id,
-                          ui::ScaleFactor scale_factor,
-                          base::StringPiece* value) override {
-    return false;
-  }
-  bool GetLocalizedString(int message_id, base::string16* value) override {
-    return false;
-  }
-
-  int last_resource_id() const { return last_resource_id_; }
-
- private:
-  gfx::Image image_small_;
-  gfx::Image image_large_;
-  int last_resource_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockResourceBundleDelegate);
-};
-
-}  // namespace
-
-class ShadowTest: public aura::test::AuraTestBase {
- public:
-  ShadowTest() {}
-  ~ShadowTest() override {}
-
-  MockResourceBundleDelegate* delegate() { return delegate_.get(); }
-
-  // aura::testAuraBase:
-  void SetUp() override {
-    aura::test::AuraTestBase::SetUp();
-    delegate_.reset(new MockResourceBundleDelegate());
-    if (ResourceBundle::HasSharedInstance())
-      ui::ResourceBundle::CleanupSharedInstance();
-    ui::ResourceBundle::InitSharedInstanceWithLocale(
-        "en-US", delegate(), ui::ResourceBundle::LOAD_COMMON_RESOURCES);
-  }
-  void TearDown() override {
-    ui::ResourceBundle::CleanupSharedInstance();
-    base::FilePath ui_test_pak_path;
-    ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
-    ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
-    aura::test::AuraTestBase::TearDown();
-  }
- private:
-  std::unique_ptr<MockResourceBundleDelegate> delegate_;
-  DISALLOW_COPY_AND_ASSIGN(ShadowTest);
-};
-
-// Test if the proper image is set for the specified style.
-TEST_F(ShadowTest, UpdateImagesForStyle) {
-  Shadow shadow;
-
-  shadow.Init(Shadow::STYLE_SMALL);
-  EXPECT_EQ(delegate()->last_resource_id(), IDR_WINDOW_BUBBLE_SHADOW_SMALL);
-  shadow.SetStyle(Shadow::STYLE_ACTIVE);
-  EXPECT_EQ(delegate()->last_resource_id(), IDR_AURA_SHADOW_ACTIVE);
-  shadow.SetStyle(Shadow::STYLE_INACTIVE);
-  EXPECT_EQ(delegate()->last_resource_id(), IDR_AURA_SHADOW_INACTIVE);
-}
+using ShadowTest = aura::test::AuraTestBase;
 
 // Test if the proper content bounds is calculated based on the current style.
 TEST_F(ShadowTest, SetContentBounds) {
+  // Verify that layer bounds are outset from content bounds.
   Shadow shadow;
+  {
+    shadow.Init(Shadow::STYLE_ACTIVE);
+    gfx::Rect content_bounds(100, 100, 300, 300);
+    shadow.SetContentBounds(content_bounds);
+    EXPECT_EQ(content_bounds, shadow.content_bounds());
+    gfx::Rect shadow_bounds(content_bounds);
+    int elevation = 24;
+    shadow_bounds.Inset(-gfx::Insets(2 * elevation) +
+                        gfx::Insets(elevation, 0, -elevation, 0));
+    EXPECT_EQ(shadow_bounds, shadow.layer()->bounds());
+  }
 
-  // Verify that layer bounds are inset from content bounds.
-  shadow.Init(Shadow::STYLE_ACTIVE);
-  gfx::Rect content_bounds(100, 100, 300, 300);
-  shadow.SetContentBounds(content_bounds);
-  EXPECT_EQ(shadow.content_bounds(), content_bounds);
-  EXPECT_EQ(shadow.layer()->bounds(), gfx::Rect(36, 36, 428, 428));
-
-  shadow.SetStyle(Shadow::STYLE_SMALL);
-  EXPECT_EQ(shadow.content_bounds(), content_bounds);
-  EXPECT_EQ(shadow.layer()->bounds(), gfx::Rect(96, 96, 308, 308));
+  {
+    shadow.SetStyle(Shadow::STYLE_SMALL);
+    gfx::Rect content_bounds(100, 100, 300, 300);
+    shadow.SetContentBounds(content_bounds);
+    EXPECT_EQ(content_bounds, shadow.content_bounds());
+    gfx::Rect shadow_bounds(content_bounds);
+    int elevation = 6;
+    shadow_bounds.Inset(-gfx::Insets(2 * elevation) +
+                        gfx::Insets(elevation, 0, -elevation, 0));
+    EXPECT_EQ(shadow_bounds, shadow.layer()->bounds());
+  }
 }
+
+}  // namespace
 }  // namespace wm